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

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

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

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

python大神给出的解决方案

更新-1/15/2020:小批量的当前最佳实践应该是直接将输入输入模型-即preds = model(x),如果层在训练/推理时表现不同,则model(x, training=False)。每次最新提交时,此时间现在为documented。

我还没有对这些基准进行基准测试,但是根据Git discussion,也值得尝试predict_on_batch()-特别是在TF 2.1中进行了改进。

最终罪魁祸首:self._experimental_run_tf_function = True。是experimental。但这实际上并不坏。

对于任何TensorFlow开发人员而言:清理代码。一团糟。而且它违反了重要的编码惯例,例如一个功能只能做一件事; _process_inputs比“过程输入”做更多的事情,与_standardize_user_data相同。 “我付的钱不够”-但是,您确实要付出,要花更多的时间来理解自己的东西,并且用户要在您的“问题”页面中填充错误,而这些错误更容易用更清晰的代码解决。

简介:使用compile()仅慢一点。

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

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

我该怎么办?

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

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

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

细节:

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

(为简便起见,FLAG == self.experimental_run_tf_function

默认情况下,ModelFLAG=False实例化。 compile()将其设置为True
predict()涉及获取预测函数,func = self._select_training_loop(x)
在没有传递给predictcompile的任何特殊kwarg的情况下,所有其他标志如下:

(A)FLAG==True-> func = training_v2.Loop()
(B)FLAG==False-> func = training_arrays.ArrayLikeTrainingLoop()

从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 code和here

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

(A):distribution function和here

(B):distribution function (different)和here

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

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

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

中型:10次迭代

(间接)对compile()的影响的文档:source

与其他TensorFlow操作不同,我们不转换python
张量的数字输入。此外,为每个生成新图
不同的python数值,例如调用g(2)g(3)
生成两个新图

function为每个唯一的输入集实例化一个单独的图
形状和数据类型。例如,将产生以下代码片段
在三个不同的图形中进行跟踪,因为每个输入都有不同的
形状

一个tf.function对象可能需要映射到多个计算图
在引擎盖下。这应该仅在性能方面可见(跟踪图具有
非零的计算和内存成本),但不应影响正确性
该程序的

反例:

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

Python pytz时区函数返回的时区为9分钟 - python

由于某些原因,我无法从以下代码中找出原因:>>> from pytz import timezone >>> timezone('America/Chicago') 我得到:<DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD…

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

用大写字母拆分字符串,但忽略AAA Python Regex - python

我的正则表达式:vendor = "MyNameIsJoe. I'mWorkerInAAAinc." ven = re.split(r'(?<=[a-z])[A-Z]|[A-Z](?=[a-z])', vendor) 以大写字母分割字符串,例如:'我的名字是乔。 I'mWorkerInAAAinc”变成…

Python:同时在for循环中添加到列表列表 - python

我想用for循环外的0索引值创建一个新列表,然后使用for循环添加到相同的列表。我的玩具示例是:import random data = ['t1', 't2', 't3'] masterlist = [['col1', 'animal1', 'an…

Python:来自dict系列的Pandas数据框 - python

我有一个熊猫数据框:type(original) pandas.core.frame.DataFrame 其中包括系列对象original['user']:type(original['user']) pandas.core.series.Series original['user']指向许多命令:…