[python] 컴파일 후 keras 모델이 왜 느리게 예측합니까?

예측 속도 keras

이론적으로, 가중치는 고정 된 크기를 가지므로 예측은 일정해야합니다. 컴파일 후 (최적화기를 제거 할 필요없이) 속도를 어떻게 회복합니까?

관련 실험 참조 : https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true



답변

업데이트 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간결함을 위해)

  1. Model기본적으로으로 인스턴스화합니다 FLAG=False. compile()로 설정합니다 True.
  2. predict() 예측 함수 획득과 관련이 있습니다. func = self._select_training_loop(x)
  3. 특별한 kwargs가 predictand로 전달되지 않으면 compile다른 모든 플래그는 다음과 같습니다.
    • (A) FLAG==True ->func = training_v2.Loop()
    • (B) FLAG==False ->func = training_arrays.ArrayLikeTrainingLoop()
  4. 에서 소스 코드를 참조 문 , (A)는 , 크게 의존 그래프 더 분배 전략을 사용하며,이 생성 및 OPS (DO) “할 수있다”그래프 소자 성능에 영향을 파괴되기 쉽다.

진정한 범인 : 런타임의 81 %를_process_inputs() 차지 합니다 . 주요 구성 요소? _create_graph_function(), 런타임의 72 % . 이 방법은도하지 않는 존재 에 대한 (B) . 그러나 중간 크기 모델을 사용하면 런타임의 1 % 미만이_process_inputs 구성 됩니다 . 하단의 코드와 프로파일 링 결과가 이어집니다.


데이터 프로세서 :

(A) : <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>에서 사용됩니다 _process_inputs(). 관련 소스 코드

(B) : numpy.ndarray에 의해 반환됩니다 convert_eager_tensors_to_numpy. 관련 소스 코드여기


모델 실행 기능 (예 : 예측)

(A) : 분포 함수여기

(B) : 분포 함수 (상이)여기


PROFILER : 다른 답변 “tiny model”및이 답변 “medium model”의 코드 결과 :

작은 모델 : 1000 회 반복compile()

작은 모델 : 1000 회 반복, 아니오 compile()

중간 모델 : 10 회 반복


영향에 대한 문서 (간접적) compile(): source

다른 TensorFlow 작업과 달리 Python 숫자 입력을 텐서로 변환하지 않습니다. 또한 각각의 고유 한 파이썬 숫자 값 에 대해 새 그래프가 생성됩니다 ( 예 : 호출 g(2)g(3)두 개의 새 그래프 생성).

function 모든 고유 한 입력 모양 및 데이터 유형 집합에 대해 별도의 그래프를 인스턴스화합니다 . 예를 들어, 다음 코드 스 니펫은 각 입력의 모양이 다르기 때문에 3 개의 고유 한 그래프가 추적됩니다.

단일 tf.function 객체는 후드 아래의 여러 계산 그래프에 매핑해야 할 수도 있습니다. 이것은 성능으로 만 볼 수 있어야하며 (추적 그래프에는 계산 및 메모리 비용이 0 이 아님) 프로그램의 정확성에 영향을 미치지 않아야합니다.


카운터 예 :

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


답변

업데이트 : 실제 답변은 별도의 답변으로 게시되어 있습니다. 이 게시물에는 보충 정보가 포함되어 있습니다


.compile() 손실, 메트릭스, 그라디언트, 부분적으로 옵티 마이저 및 가중치를 포함하여 대부분의 TF / Keras 그래프를 설정하여 현저한 속도 저하를 보장합니다.

무엇 이며 예기치 않은 것은 경기 침체의 정도입니다 – 내 자신의 실험에 10 배, 그리고에 대한 predict()어떤 가중치를 갱신하지 않는다. TF2의 소스 코드를 살펴보면 그래프 요소가 밀접하게 얽혀있는 것처럼 보이며 리소스가 반드시 “공정하게”할당되지는 않습니다.

predict모델이 일반적으로 컴파일되어 사용되기 때문에 컴파일되지 않은 모델의 성능에 대한 개발자의 간과 가능성 은 있지만 실제로 는 용납 할 수없는 차이입니다. 간단한 해결 방법이 있기 때문에 “필요한 악”일 수도 있습니다 (아래 참조).

이것은 완전한 대답이 아니며 누군가가 여기에 제공 할 수 있기를 바랍니다. 그렇지 않은 경우 TensorFlow에서 Github 문제를 여는 것이 좋습니다. (OP는; 여기 )


해결 방법 : 모델을 학습하고 가중치를 저장하고 컴파일하지 않고 모델을 다시 빌드하고 가중치를로드하십시오. 마십시오 하지 전체 모델 (예를 저장 model.save()하는 대신 사용 -이 컴파일되면로드 겠지만,) model.save_weights()model.load_weights().

해결 방법 2 : 위와 같지만 load_model(path, compile=False); 제안 신용 : D. Möller


UPDATE : 명확하게는, 최적화가되어 있지 완전히 인스턴스화 compile의 포함 weightsupdates텐서 -이 작업이 완료되는 피팅 함수에 대한 첫 번째 호출 (이루어질 때 fit, train_on_batch등)를 통해 model._make_train_function().

따라서 관찰 된 동작은 훨씬 더 이상합니다. 더 나쁜 것은 옵티 마이저를 구축해 도 더 이상 속도 저하 가 발생하지 않습니다 (아래 참조). 여기서 “그래프 크기”는 주요 설명이 아닙니다.


편집 : 일부 모델에서는 30 배 속도가 느려 집니다. 텐서 플로우, 무엇을 했습니까? 아래 예 :

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


답변