python - कायर मॉडल संकलन के बाद धीमी होने की भविष्यवाणी क्यों करता है?




tensorflow keras (2)

ULTIMATE CULPRIT : self._experimental_run_tf_function = True । यह experimental । लेकिन यह वास्तव में बुरा नहीं है।

किसी भी TensorFlow देव पढ़ने के लिए: अपने कोड को साफ करें । यह एक गड़बड़ है। और यह महत्वपूर्ण कोडिंग प्रथाओं का उल्लंघन करता है, जैसे कि एक फ़ंक्शन एक काम करता है ; _process_inputs "प्रोसेस इनपुट" की तुलना में बहुत अधिक करता है, जो _standardize_user_data लिए _standardize_user_data । "मैं पर्याप्त भुगतान नहीं कर रहा हूं" - लेकिन आप भुगतान करते हैं , अतिरिक्त समय में अपने सामान को समझने में खर्च करते हैं, और उपयोगकर्ताओं में आपके इश्यू पृष्ठ को कीड़े से भरना आसान होता है जो एक स्पष्ट कोड के साथ हल किया जाता है।

सारांश : यह केवल compile() साथ थोड़ा धीमा है।

compile() एक आंतरिक ध्वज सेट करता है जो भविष्यवाणी करने के लिए एक अलग भविष्यवाणी फ़ंक्शन प्रदान करता है। यह फ़ंक्शन प्रत्येक कॉल पर एक नया ग्राफ बनाता है, जो इसे अनलेडेड के सापेक्ष धीमा कर देता है। हालाँकि, अंतर केवल तब स्पष्ट होता है जब ट्रेन का समय डाटा प्रोसेसिंग समय से बहुत कम होता है । यदि हम मॉडल का आकार कम से कम मध्य आकार में बढ़ाते हैं , तो दोनों समान हो जाते हैं। सबसे नीचे कोड देखें।

डेटा प्रोसेसिंग समय में यह मामूली वृद्धि प्रवर्धित ग्राफ क्षमता द्वारा मुआवजा से अधिक है। चूँकि यह केवल एक मॉडल ग्राफ को चारों ओर रखने के लिए अधिक कुशल है, इसलिए पूर्व-संकलन को छोड़ दिया जाता है। फिर भी : यदि आपका मॉडल डेटा के सापेक्ष छोटा है, तो आप मॉडल निष्कर्ष के लिए compile() बिना बेहतर हैं। वर्कअराउंड के लिए मेरा अन्य उत्तर देखें।

मुझे क्या करना चाहिए?

मॉडल प्रदर्शन की तुलना बनाम संकलित के रूप में मैं नीचे कोड में है।

  • संकलित तेजी से होता है : संकलित मॉडल पर रन की predict
  • संकलित धीमी है : एक अनिश्चित मॉडल पर रन की predict

हां, दोनों संभव हैं, और यह (1) डेटा आकार पर निर्भर करेगा; (2) मॉडल का आकार; (३) हार्डवेयर। नीचे स्थित कोड वास्तव में संकलित मॉडल को तेजी से दिखाता है, लेकिन 10 पुनरावृत्तियों एक छोटा नमूना है। "कैसे-करें" के लिए मेरे दूसरे उत्तर में "वर्कअराउंड" देखें।

विवरण :

यह डिबग करने में थोड़ा समय लगा, लेकिन मजेदार था। नीचे मैंने अपने द्वारा खोजे गए प्रमुख दोषियों का वर्णन किया है, कुछ प्रासंगिक दस्तावेज़ीकरण का हवाला देते हैं, और प्रोफाइलर परिणाम दिखाते हैं जो अंतिम अड़चन का कारण बनता है।

( FLAG == self.experimental_run_tf_function , संक्षिप्तता के लिए)

  1. डिफ़ॉल्ट रूप से Model FLAG=False साथ FLAG=Falsecompile() इसे True सेट करता है।
  2. predict() में भविष्यवाणी कार्य प्राप्त करना शामिल है, func = self._select_training_loop(x)
  3. बिना किसी विशेष काग्रेस के predict और compile करने के लिए पारित किए गए, अन्य सभी झंडे ऐसे हैं:
    • (ए) FLAG==True -> func = training_v2.Loop()
    • (बी) FLAG==False -> func = training_arrays.ArrayLikeTrainingLoop()
  4. स्रोत कोड docstring से , (ए) भारी मात्रा में निर्भर है, अधिक वितरण रणनीति का उपयोग करता है, और ऑप्स ग्राफ तत्वों को बनाने और नष्ट करने के लिए प्रवण हैं, जो "प्रदर्शन" (कर) प्रभाव प्रदर्शन कर सकते हैं।

सच्चा अपराधी : _process_inputs() , 81% रनटाइम के लिए लेखांकन। इसका प्रमुख घटक? _create_graph_function() , रनटाइम का 72% । यह विधि (B) के लिए भी मौजूद नहीं है। हालाँकि, मध्यम आकार के मॉडल का उपयोग करना, _process_inputs में रनटाइम के 1% से कम होता है । नीचे कोड, और रूपरेखा परिणाम का पालन करें।

डेटा प्रोसेसर :

(A) : _process_inputs() में प्रयुक्त <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'> प्रासंगिक स्रोत कोड

(बी) : numpy.ndarray , convert_eager_tensors_to_numpy द्वारा लौटाया गया। प्रासंगिक स्रोत कोड , और here

मॉडल एक्सेल्यूशन फंक्शन (उदाहरण के लिए)

(ए) : वितरण समारोह , और here

(बी) : वितरण समारोह (अलग) , और here

PROFILER : मेरे अन्य उत्तर में कोड के लिए परिणाम, "छोटे मॉडल", और इस उत्तर में, "मध्यम मॉडल":

छोटे मॉडल : 1000 पुनरावृत्तियों, compile()

छोटे मॉडल : 1000 पुनरावृत्तियों, कोई compile() नहीं compile()

मध्यम मॉडल : 10 पुनरावृत्तियों

compile() स्रोत पर प्रभाव (अप्रत्यक्ष रूप से)

अन्य TensorFlow संचालन के विपरीत, हम अजगर संख्यात्मक इनपुट को दहाई में नहीं बदलते हैं। इसके अलावा, प्रत्येक अलग अजगर संख्यात्मक मूल्य के लिए एक नया ग्राफ उत्पन्न होता है , उदाहरण के लिए 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

सिद्धांत रूप में, भविष्यवाणी स्थिर होनी चाहिए क्योंकि भार का एक निश्चित आकार होता है। संकलन के बाद (ऑप्टिमाइज़र हटाने की आवश्यकता के बिना) मैं अपनी गति कैसे प्राप्त करूं?

संबंधित प्रयोग देखें: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true


अद्यतन : वास्तविक उत्तर को एक अलग उत्तर के रूप में पोस्ट किया गया देखें; इस पोस्ट में पूरक जानकारी है

.compile() हानि, मैट्रिक्स, ग्रेडिएंट्स और आंशिक रूप से ऑप्टिमाइज़र और इसके वज़न सहित TF / Keras ग्राफ के अधिकांश भाग को सेट करता है - जो एक उल्लेखनीय मंदी की गारंटी देता है।

जो अप्रत्याशित है वह मंदी की सीमा है - मेरे अपने प्रयोग पर 10 गुना और predict() , जो किसी भी भार को अद्यतन नहीं करता है। TF2 के स्रोत कोड को देखते हुए, ग्राफ एलिमेंट्स कसकर इंटरकेटेड दिखाई देते हैं, जरूरी नहीं कि संसाधनों को "निष्पक्ष" आवंटित किया जाए।

एक मॉडल के लिए predict के प्रदर्शन पर डेवलपर्स द्वारा संभावित अनदेखी, क्योंकि मॉडल आमतौर पर संकलित किए जाते हैं - लेकिन व्यवहार में , यह एक अस्वीकार्य अंतर है। यह भी संभव है कि यह एक "आवश्यक बुराई" है, क्योंकि एक साधारण वर्कअराउंड है (नीचे देखें)।

यह एक पूर्ण उत्तर नहीं है, और मुझे आशा है कि कोई व्यक्ति इसे यहां प्रदान कर सकता है - यदि नहीं, तो मैं TensorFlow पर एक Github मुद्दा खोलने का सुझाव दूंगा। (ओपी ने here ;)

वर्कअराउंड : एक मॉडल को प्रशिक्षित करें, उसके वजन को बचाएं, संकलन के बिना मॉडल को फिर से बनाएं, भार को लोड करें। पूरे मॉडल को सहेजें (जैसे model.save() ), क्योंकि यह संकलित लोड करेगा - इसके बजाय model.load_weights() और model.load_weights()

वर्कअराउंड 2 : ऊपर, लेकिन load_model(path, compile=False) ; सुझाव क्रेडिट: डी। मोलर

अद्यतन : स्पष्ट करने के लिए, ऑप्टिमाइज़र पूरी तरह से compile साथ compile , जिसमें इसके weights और updates टेंसर्स शामिल हैं - यह तब होता है जब एक फिटिंग फ़ंक्शन के लिए पहला कॉल किया जाता है ( fit , train_on_batch , आदि), model._make_train_function() माध्यम से।

देखा गया व्यवहार इस प्रकार और भी विचित्र है। इससे भी बदतर, ऑप्टिमाइज़र का निर्माण किसी भी आगे की मंदी को कम नहीं करता है (नीचे देखें) - "ग्राफ आकार" का सुझाव देना यहां का मुख्य विवरण नहीं है।

संपादित करें : कुछ मॉडल पर, एक 30x मंदी । 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






jupyter-lab