为什么keras模型预测编译后会变慢?

时间:2019-10-14 13:58:18

标签: python performance tensorflow keras jupyter-notebook

prediction speed keras

理论上,由于权重具有固定大小,因此预测应该是恒定的。编译后如何恢复速度(无需删除优化器)?

查看相关实验:https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true

2 个答案:

答案 0 :(得分:11)

最终罪犯self._experimental_run_tf_function = True。是experimental。但这实际上还不错。

向任何TensorFlow开发人员阅读:清理代码。一团糟。而且它违反了重要的编码惯例,例如一个函数做一件事_process_inputs所做的 lot 多于“过程输入”,与_standardize_user_data相同。 “我的报酬不够”-但是您要做付出的代价是,花费更多的时间来理解自己的东西,并且用户在您的“问题”页面中填入了一些错误,这些错误可以通过更清晰的代码来解决。


摘要compile()的使用仅慢了 个。

compile()设置一个内部标志,该标志为predict分配了不同的预测功能。该函数在每次调用时构造一个新图,相对于未编译,它会降低速度。但是,只有当培训时间比数据处理时间短得多时,差异才明显。如果我们增加模型大小至至少中等大小,则两者将相等。请参阅底部的代码。

数据处理时间的这种轻微增加远远超过了放大图形功能所能弥补的。由于仅保留一个模型图更为有效,因此放弃了一个预编译。 不过:如果您的模型相对于数据而言较小,那么最好不用compile()进行模型推断。请参阅我的其他答案以找到解决方法。


我应该怎么做?

比较我在底部的代码中比较编译后的模型性能与未编译的模型性能。

  • 编译速度更快:在编译的模型上运行predict
  • 编译速度较慢:在未编译的模型上运行predict

是的,两者都是可能的,这取决于(1)数据大小; (2)型号尺寸; (3)硬件。底部的代码实际上显示了 compiled 模型更快,但是10次迭代只是一个小样本。请参阅我的其他答案中的“解决方法”,以获取“操作方法”。


详细信息

调试花费了一段时间,但很有趣。在下面,我描述了我发现的关键罪魁祸首,并引用了一些相关文档,并显示了导致最终瓶颈的分析器结果。

(为简洁起见,{FLAG == self.experimental_run_tf_function

  1. Model默认情况下以FLAG=False实例化。 compile()将其设置为True
  2. predict()涉及获取预测函数func = self._select_training_loop(x)
  3. 在没有传递给predictcompile的特殊特殊情况下,所有其他标志都是这样的:
    • (A) FLAG==True-> func = training_v2.Loop()
    • (B) FLAG==False-> func = training_arrays.ArrayLikeTrainingLoop()
  4. source code 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()中。 Relevant source code

(B)numpy.ndarray,由convert_eager_tensors_to_numpy返回。 Relevant source codehere


模型执行功能(例如预测)

(A)distribution functionhere

(B)distribution function (different)here


PROFILER :我的另一个答案“小模型”和此答案“中等模型”中的代码结果:

微型模型:1000次迭代,compile()

微型模型:1000次迭代, compile()

中等模型:10次迭代


文档(间接)对compile()source

的影响
  

与其他TensorFlow操作不同,我们不转换python     张量的数字输入。此外,将为每个生成一个新图     不同的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

答案 1 :(得分:10)

更新:将实际答案发布为单独的答案;这篇文章包含补充信息


.compile()设置了大部分TF / Keras图,包括损耗,度量,梯度,以及部分优化器及其权重-这可以确保明显的减速。

出乎意料的是,放慢的程度-在我自己的实验中是10倍,对于predict()来说,它不会更新任何权重。查看TF2的源代码,图形元素似乎紧密地交织在一起,资源不一定“公平地”分配。

开发人员可能会忽略predict对于未编译模型的性能,因为通常使用模型进行编译-但是在实践中,这是无法接受的差异。因为有一个简单的解决方法,这也有可能是“必要的邪恶”(见下文)。

这不是一个完整的答案,我希望有人可以在这里提供它-否则,我建议在TensorFlow上打开Github问题。 (OP具有; here


解决方法:训练模型,保存其权重,无需编译即可重新构建模型,并加载权重。 保存整个模型(例如model.save()),因为它将加载已编译的模型-而是使用model.save_weights()model.load_weights()

解决方法2 :上面,但使用load_model(path, compile=False);建议信用:D. Möller


更新:为澄清起见,优化器未完全使用compile实例化,包括weightsupdates张量-这通过fit首次调用拟合函数(train_on_batchmodel._make_train_function()等)时完成。

因此观察到的行为更加奇怪。更糟糕的是,构建优化程序不会不会引发任何进一步的减速(请参见下文)-提示“图形大小”不是此处的主要解释。


编辑:在某些型号上,运行速度降低30倍。 TensorFlow,您做了什么。下面的示例:

from tensorflow.keras.layers import Input, Dense
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))

ipt   = Input(shape=(4,))
x     = Dense(2, activation='relu')(ipt)
out   = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)

X = np.random.randn(32,4)

timeit(model.predict, X, 1000)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 1000)
model._make_train_function()  # build optimizer
timeit(model.predict, X, 1000)

输出

0.9891 sec
29.785 sec
29.521 sec