UPDATE-1/15/2020:小批量的当前最佳实践应该是直接将输入输入模型,即preds = model(x)
,如果各层在训练/推理时表现不同,则model(x, training=False)
。每次最新提交,现在都记录在案。
我还没有对它们进行基准测试,但是根据Git的讨论,也值得尝试predict_on_batch()
-尤其是TF 2.1的改进。
最高罪犯:self._experimental_run_tf_function = True
。这是实验性的。但这实际上并不坏。
对于任何TensorFlow开发人员而言:清理代码。一团糟。而且它违反了重要的编码惯例,例如,一项功能只能做一件事情;_process_inputs
做了很多比“过程输入”,一样的_standardize_user_data
。“我付的钱不够”-但是,您确实要付出代价,要花更多的时间来理解自己的东西,并且用户要在您的“问题”页面中填充易于使用更清晰代码解决的错误。
简介:使用只会慢一点compile()
。
compile()
设置一个内部标志,该标志为分配一个不同的预测函数predict
。此函数在每次调用时构造一个新图,相对于未编译而言,它会降低速度。但是,仅当训练时间比数据处理时间短得多时,差异才明显。如果我们增加模型的大小至少中型,两人成为相等。请参阅底部的代码。
数据处理时间的这种轻微增加远远超过了放大图形功能所能弥补的。由于仅保留一个模型图更为有效,因此放弃了一个预编译。尽管如此:如果您的模型相对于数据而言很小,那么无需compile()
模型推断就可以更好。请参阅我的其他答案以找到解决方法。
我该怎么办?
比较我在底部的代码中已编译与未编译的模型性能。
- 编译速度更快:
predict
在已编译模型上运行。
- 编译较慢:
predict
在未编译的模型上运行。
是的,两者都是可能的,这取决于(1)数据大小;(2)型号尺寸;(3)硬件。底部的代码实际上表明编译的模型更快,但是10次迭代只是一个小样本。请参阅我的其他答案中的“解决方法”,以获取“操作方法”。
细节:
调试花费了一段时间,但很有趣。在下文中,我描述了我发现的主要罪魁祸首,并引用了一些相关文档,并显示了导致最终瓶颈的探查器结果。
(FLAG == self.experimental_run_tf_function
为简洁起见)
Model
默认情况下用实例化FLAG=False
。compile()
将其设置为True
。
predict()
涉及获取预测功能, func = self._select_training_loop(x)
- 没有传递任何特殊的kwarg到
predict
和compile
,所有其他标志如下:
- (A)
FLAG==True
->func = training_v2.Loop()
- (B)
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
- 从源代码docstring来看,(A)非常依赖图,使用更多的分发策略,并且ops易于创建和销毁图元素,“可能”(确实)影响性能。
真正的罪魁祸首:_process_inputs()
占运行时间的81%。它的主要成分?_create_graph_function()
,占运行时间的72%。此方法甚至不存在于(B) 。使用中型模型,然而,_process_inputs
包括运行时间的不到1% 。代码位于底部,并提供概要分析结果。
数据处理器:
(A):,<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
用于中_process_inputs()
。相关源代码
(B):numpy.ndarray
由convert_eager_tensors_to_numpy
。相关的源代码,在这里
模型执行功能(例如预测)
(A):分布函数,这里
(B):分布函数(不同),这里
PROFILER:我的另一个答案“小模型”和此答案“中等模型”中代码的结果:
小模型:1000次迭代,compile()
小模型:1000次迭代,否 compile()
中型:10次迭代
文档(间接)对以下方面的影响compile()
:来源
与其他TensorFlow操作不同,我们不会将python数值输入转换为张量。此外,针对每个不同的蟒数值产生一个新的图,例如主叫g(2)
和g(3)
将生成两个新的图
function
为每个唯一的输入形状和数据类型集实例化一个单独的图。例如,下面的代码片段将导致跟踪三个不同的图形,因为每个输入的形状都不相同
一个tf.function对象可能需要映射到后台的多个计算图。这应该仅在性能上可见(跟踪图的计算和内存成本为非零),但不应影响程序的正确性
COUNTEREXAMPLE:
from tensorflow.keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from tensorflow.keras.layers import Flatten, Dropout
from tensorflow.keras.models import Model
import numpy as np
from time import time
def timeit(func, arg, iterations):
t0 = time()
for _ in range(iterations):
func(arg)
print("%.4f sec" % (time() - t0))
batch_size = 32
batch_shape = (batch_size, 400, 16)
ipt = Input(batch_shape=batch_shape)
x = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x = LSTM(512, activation='relu', return_sequences=True)(ipt)
x = Conv1D(128, 400, 1, padding='same')(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)
out = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)
X = np.random.randn(*batch_shape)
timeit(model.predict, X, 10)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 10)
输出:
34.8542 sec
34.7435 sec