[python] 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)으로 다운 그레이드했습니다. 문제가 더 이상 존재하지 않을뿐만 아니라 “정상”예측 시간도 크게 개선되었습니다! 테스트를 반복 할 때 (적어도 같은 지수는 아니고 선형 적으로 증가 할 때) 나타나지 않았고 첫 번째 플롯에서만큼 크지 않은 두 스파이크는 문제가되지 않습니다.
여기에 이미지 설명을 입력하십시오

따라서 이것은 @OverLordGoldDragon이 언급 한 것처럼 다른 상황에서도 유사한 동작을 나타내는 tensorflow 2.0 고유의 문제인 것으로 판단 할 수 있습니다.



답변

간단한 설명 – TF2는 일반적으로 몇 가지 내가 만난 경우에 가난하고 버그 등의 메모리 관리를 전시 여기여기 . 특히 예측을 통해 가장 성능이 좋은 사료 공급 방법은 여기 를 통해 model(x)직접 참조 하고 관련 토론을 통해 이루어집니다.

요컨대 : model(x)그것의 작용을 통해 __call__(이 상속되는 메소드 base_layer.Layer반면) predict(), predict_classes()등을 통해 전용의 루프 기능을 포함한다 _select_training_loop(); 각각은 서로 다른 사용 사례에 적합한 서로 다른 데이터 전처리 및 사후 처리 방법을 사용 model(x)하며 2.1에서 가장 빠른 소형 모델 / 작은 배치 (아마 크기에 관계없이) 성능을 제공하도록 설계되었습니다 (아직도 2.0에서 가장 빠름).

연결된 토론에서 텐서 플로우 개발자 인용 :

모델 예측이 아닌 모델 호출을 사용하여 출력을 예측할 수 있습니다. 즉, model(x)“데이터 세트로의 변환”부분이 없기 때문에 호출 하면이 작업이 훨씬 빨라지고 캐시 된 직접 호출 tf.function됩니다.

참고 : 이것은 2.1, 특히 2.2에서는 문제가되지 않지만 각 방법을 테스트하십시오. 또한 이것이 시간 급상승에 대한 귀하의 질문에 직접 대답하지는 않는다는 것을 알고 있습니다. 나는 그것이 Eager 캐싱 메커니즘과 관련이 있다고 생각하지만, 결정하는 가장 확실한 방법은 via TF Profiler이며, 2.1에서 깨졌습니다 .


업데이트 : 스파이크 증가 , 가능한 GPU 조절; ~ 1000 회 반복을 수행하고 대신 10,000을 시도하십시오. 결국 증가가 멈춰야합니다. 귀하의 의견에서 언급했듯이, 이것은 model(x); GPU 단계가 하나도 덜 필요하기 때문에 의미가 있습니다 ( “데이터 세트로의 변환”).

Update2 : 이 문제에 직면하면 여기 에 개발자를 버그로 만들 수 있습니다. 그것은 주로 내가 거기 노래


답변

실행 시간의 불일치를 설명 할 수는 없지만 단일 데이터 레코드 또는 작은 배치에 대한 예측 속도를 높이기 위해 모델을 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): 18ms
  2. model(input): 1.3ms
  3. TensorFlow Lite로 변환 된 모델 : 43us

모델 변환 시간은 2 초였습니다.

아래의 클래스는 모델을 변환하고 사용하는 방법을 보여 주며 predictKeras 모델과 같은 방법을 제공합니다 . 단일 1D 입력 및 단일 1D 출력이없는 모델과 함께 사용하려면 수정해야합니다.

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 에서 확인할 수 있습니다.


답변