Keras的预测时间不一致


17

我试图估计我的keras模型的预测时间,并意识到一些奇怪的事情。除了正常情况下相当快外,模型偶尔还需要很长时间才能得出预测。不仅如此,这些时间还增加了模型运行的时间。我添加了一个最小的工作示例来重现该错误。

import time
import numpy as np
from sklearn.datasets import make_classification
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten

# Make a dummy classification problem
X, y = make_classification()

# Make a dummy model
model = Sequential()
model.add(Dense(10, activation='relu',name='input',input_shape=(X.shape[1],)))
model.add(Dense(2, activation='softmax',name='predictions'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

model.fit(X, y, verbose=0, batch_size=20, epochs=100)

for i in range(1000):
    # Pick a random sample
    sample = np.expand_dims(X[np.random.randint(99), :], axis=0)
    # Record the prediction time 10x and then take the average
    start = time.time()
    for j in range(10):
        y_pred = model.predict_classes(sample)
    end = time.time()
    print('%d, %0.7f' % (i, (end-start)/10))

时间不取决于样本(它是随机抽取的)。如果重复测试,则预测所需时间更长的for循环中的索引将再次(几乎)相同。

在此处输入图片说明

我在用着:

tensorflow 2.0.0
python 3.7.4

对于我的应用程序,我需要保证在一定时间内执行。但是考虑到该行为,这是不可能的。怎么了?是Keras中的错误还是tensorflow后端中的错误?

编辑: predict_on_batch显示相同的行为,但是更稀疏: 在此处输入图片说明

y_pred = model(sample, training=False).numpy() 也显示了一些严重的离群值,但是它们并没有增加。 在此处输入图片说明

编辑2:我降级到最新的tensorflow 1版本(1.15)。不仅不再存在这个问题,而且“正常”的预测时间也大大缩短了!我认为这两个峰值没有问题,因为当我重复测试时它们并没有出现(至少不是在相同的指数处并且线性增加),并且百分比不如第一个图大。 在此处输入图片说明

因此,我们可以得出结论,这似乎是tensorflow 2.0固有的问题,它在其他情况下表现出与@OverLordGoldDragon提到的类似的行为。


这种行为听起来是可以预见的。...增长是线性的。如果您在时间计算中包括此行为,它将不会消失吗?---我不知道那里发生了什么...。但是,如果尝试尝试会发生什么predict_on_batch呢?
DanielMöller

另一种尝试,有什么会发生y_pred = model(sample).numpy(),并用y_pred = model(sample, training=False).numpy()
DanielMöller

我添加了我的发现。numpy版本似乎没有显示该行为。
ga97dil

但是predict_classes仍然是最快的。那又如何predict呢?
DanielMöller

1
我想这可能是某种类型的内存清理....
丹尼尔·默勒

Answers:


10

在我遇到的几个实例中,TF2通常表现出较差且类似错误的内存管理- 此处此处进行简要说明。特别是通过预测,最有效的喂养方法是model(x)直接通过-参见此处及其相关链接的讨论。

概括地说:model(x)通过其它的作用__call__方法(其从继承base_layer.Layer),而predict()predict_classes()等经由涉及专用回路功能_select_training_loop(); 每种工具都利用了适合不同用例的不同数据预处理和后处理方法,并且model(x)在2.1版中专门针对产生最快的小模型/小批量(可能是任意大小)性能(在2.0版中仍然最快)进行了设计。

从链接的讨论中引用TensorFlow开发人员:

您可以使用模型调用而不是模型预测来预测输出,即,调用model(x)将使速度更快,因为没有“转换为数据集”部分,而且它直接调用了cached tf.function

注意:在2.1,尤其是2.2中,这应该不是什么大问题-但无论如何都要测试每种方法。我也意识到这并不能直接回答您关于时间高峰的问题;我怀疑这是关系到渴望缓存机制,但确定最可靠的方法是通过TF Profiler,这是 2.1。


更新:关于尖峰的增加,GPU可能的节流;您已经完成了大约1000次迭代,请尝试尝试10,000次-最终,增长应该停止。正如您在评论中指出的那样,这不会发生model(x);因为减少了一个GPU步骤(“转换为数据集”)就很有意义。

Update2:如果您遇到此问题,可以在这里与开发人员联系。主要是我在那里唱歌


这是为什么一个方法较慢的一个很好的答案,但是它不能解释多次运行会增加运行时间的原因。
LLSv2.0

1
@ LLSv2.0我不太确定自己,但是更新了我的答案-当我在这里
OverLordGoldDragon

1
@ ga97dil是的,那么我没有解释-尝试在Github上提问,尽管您可能会面临漫长的响应时间。
OverLordGoldDragon

1
@ ga97dil确实,TF1可以比TF2 快得多 -尽管TF 2.1对于小型模型和数据集值得尝试,因为在我的基准测试中它是训练最快的(没有做预测)。更重要的是,如果您曾经使用过TF2,我强烈建议您在Graph vs. Eager中测试可重复性。结果可以相差非常的TF 2.1。
OverLordGoldDragon

1
我已经将您的帖子添加到Git线程中,并且将我的TF2与TF1添加在一起。谢谢你通知我,这个问题在TF 1.消失
OverLordGoldDragon

2

虽然我无法解释执行时间上的不一致,但我建议您尝试将模型转换为TensorFlow Lite,以加快对单个数据记录或小批量的预测。

我对此模型进行了基准测试:

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(384, activation='elu', input_shape=(256,)),
    tf.keras.layers.Dense(384, activation='elu'),
    tf.keras.layers.Dense(256, activation='elu'),
    tf.keras.layers.Dense(128, activation='elu'),
    tf.keras.layers.Dense(32, activation='tanh')
])

单个记录的预测时间为:

  1. model.predict(input):18毫秒
  2. model(input):1.3毫秒
  3. 转换为TensorFlow Lite的模型:43us

转换模型的时间为2秒。

下面的类显示了如何转换和使用模型,并提供了predict类似Keras模型的方法。请注意,对于不仅仅具有一个一维输入和一个一维输出的模型,需要对其进行修改。

class LiteModel:

    @classmethod
    def from_file(cls, model_path):
        return LiteModel(tf.lite.Interpreter(model_path=model_path))

    @classmethod
    def from_keras_model(cls, kmodel):
        converter = tf.lite.TFLiteConverter.from_keras_model(kmodel)
        tflite_model = converter.convert()
        return LiteModel(tf.lite.Interpreter(model_content=tflite_model))

    def __init__(self, interpreter):
        self.interpreter = interpreter
        self.interpreter.allocate_tensors()
        input_det = self.interpreter.get_input_details()[0]
        output_det = self.interpreter.get_output_details()[0]
        self.input_index = input_det["index"]
        self.output_index = output_det["index"]
        self.input_shape = input_det["shape"]
        self.output_shape = output_det["shape"]
        self.input_dtype = input_det["dtype"]
        self.output_dtype = output_det["dtype"]

    def predict(self, inp):
        inp = inp.astype(self.input_dtype)
        count = inp.shape[0]
        out = np.zeros((count, self.output_shape[1]), dtype=self.output_dtype)
        for i in range(count):
            self.interpreter.set_tensor(self.input_index, inp[i:i+1])
            self.interpreter.invoke()
            out[i] = self.interpreter.get_tensor(self.output_index)[0]
        return out

    def predict_single(self, inp):
        """ Like predict(), but only for a single record. The input data can be a Python list. """
        inp = np.array([inp], dtype=self.input_dtype)
        self.interpreter.set_tensor(self.input_index, inp)
        self.interpreter.invoke()
        out = self.interpreter.get_tensor(self.output_index)
        return out[0]

完整的基准代码和图表可以在以下位置找到:https : //medium.com/@micwurm/using-tensorflow-lite-to-speed-up-predictions-a3954886eb98


很酷,以前从未尝试过,但也许值得一试。感谢您的提示!
ga97dil
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.