python - 使用 tf.function 时,Tensorflow 逐元素梯度变慢
问题描述
我需要计算一个元素梯度作为神经网络的输入。我决定使用 atf.data.Dataset
来存储输入数据。预处理数据和计算梯度很昂贵,我想分批进行,然后存储数据。
我简化了使用形状的函数(batch_size, x, y)
,我想分别计算每个的梯度y
。
使用tf.GradientTape
它看起来如下:
import tensoflow as tf
# @tf.function
def test(inp):
with tf.GradientTape(persistent=True) as tape:
tape.watch(inp)
out = inp**2
out = tf.unstack(out, axis=-1)
grad = []
for x in out:
grad.append(tape.gradient(x, inp))
del tape
return tf.stack(grad, axis=-1)
inp = tf.random.normal((32, 100, 50))
test(inp)
这段代码需要 ~76 ms
在没有和3.1 s
有tf.function
装饰器的情况下执行。不幸的是,在使用它时会发生同样的减速,tf.data.Dataset.map
我假设将它转换为tf.function
我尝试tf.batch_jacobian
改用它,它不会因tf.function
减速而受到影响,但会计算出更多的梯度,我必须减少它们。执行大约需要 15 秒。
@tf.function
def test(inp):
with tf.GradientTape() as tape:
tape.watch(inp)
out = inp**2
grad = tape.batch_jacobian(out, inp)
return tf.math.reduce_sum(grad, axis=3)
x = test(inp)
对于更大的数据集和更多的资源繁重的计算,我试图避免这种减速,但我还没有找到解决方案,我也不明白,为什么它的计算速度要慢得多。有没有办法重塑数据并使用雅可比方法或其他东西,可以克服这个问题?
解决方案
因此,让我们用 IPython 的%timeit
. 我定义了两个函数,一个带有tf.function
装饰器,一个没有:
import tensorflow as tf
def test_no_tracing(inp):
with tf.GradientTape(persistent=True) as tape:
tape.watch(inp)
out = inp**2
out = tf.unstack(out, axis=-1)
grad = []
for x in out:
grad.append(tape.gradient(x, inp))
del tape
return tf.stack(grad, axis=-1)
@tf.function
def test_tracing(inp):
print("Tracing")
with tf.GradientTape(persistent=True) as tape:
tape.watch(inp)
out = inp**2
out = tf.unstack(out, axis=-1)
grad = []
for x in out:
grad.append(tape.gradient(x, inp))
del tape
return tf.stack(grad, axis=-1)
inp = tf.random.normal((32, 100, 50))
让我们看看结果:
使用tf.function
装饰器:
In [2]: %timeit test_tracing(inp)
Tracing
2021-01-22 15:22:15.003262: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2021-01-22 15:22:15.076448: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2599990000 Hz
10.3 ms ± 579 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
并且没有:
In [3]: %timeit test_no_tracing(inp)
71.7 ms ± 1.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
用修饰的函数tf.function
大约快 7 倍。如果只运行一次函数,它看起来会更慢,因为修饰函数具有跟踪的开销,将代码转换为图形。一旦完成跟踪,代码就会快得多。
这可以通过仅运行该函数一次来验证,此时尚未对其进行跟踪。我们可以通过告诉%timeit
只做一次循环和一次重复来做到这一点:
In [2]: %timeit -r 1 -n 1 test_tracing(inp)
Tracing
2021-01-22 15:29:47.189850: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2021-01-22 15:29:47.284413: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2599990000 Hz
4.97 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
在这里,时间要长得多,更接近您在问题中报告的内容。但是一旦完成,跟踪函数就会快很多!让我们再来一次:
In [3]: %timeit -r 1 -n 1 test_tracing(inp)
29.1 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
tf.function
您可以在指南中阅读有关如何获得更好性能的更多信息:使用 tf.function获得更好的性能
推荐阅读
- html - 保持按钮固定,但在体内
- c - 使用设备配置服务时出现错误和段错误
- django - 基于对象状态的自定义错误消息
- c# - 在 C# 中使用 Datagridview 的 CRUD
- android - 使用来自另一个 Fragment 的方法/函数
- android - 从我的库插件android项目中获取maven url的过程是什么
- php - 如何修复 aloha/twilio 警告
- php - 在 Eloquent 视图中使用控制器会出错:htmlspecialchars() 期望参数 1 是字符串,给定对象
- jquery - 了解此逻辑中对匿名函数的需求
- jquery - 如何删除注销按钮或链接上的启动画面?