मैं इस ठीक उसी मुद्दे में भाग गया, और आदमी यह एक खरगोश का छेद था। मेरा समाधान यहां पोस्ट करना चाहता था क्योंकि यह किसी को काम का एक दिन बचा सकता है:
TensorFlow थ्रेड-विशिष्ट डेटा संरचनाएं
जब आप model.predict
को कॉल करते हैं तो TensorFlow में दो प्रमुख डेटा संरचनाएं होती हैं जो परदे के पीछे काम करती हैं। (या keras.models.load_model
, या keras.backend.clear_session
, या बहुत अधिक कोई अन्य फ़ंक्शन जो TensorFlow बैकएंड के साथ इंटरैक्ट करता है):
- एक TensorFlow ग्राफ़, जो आपके Keras मॉडल की संरचना को दर्शाता है
- एक TensorFlow सत्र, जो आपके वर्तमान ग्राफ़ और TensorFlow रनटाइम के बीच संबंध है
कुछ खुदाई के बिना दस्तावेज़ों में स्पष्ट रूप से स्पष्ट नहीं है कि सत्र और ग्राफ़ दोनों वर्तमान थ्रेड के गुण हैं . यहाँ और यहाँ API दस्तावेज़ देखें।
विभिन्न थ्रेड में TensorFlow मॉडल का उपयोग करना
अपने मॉडल को एक बार लोड करना और फिर .predict()
. पर कॉल करना स्वाभाविक है उस पर कई बार बाद में:
from keras.models import load_model
MY_MODEL = load_model('path/to/model/file')
def some_worker_function(inputs):
return MY_MODEL.predict(inputs)
सेलेरी जैसे वेबसर्वर या वर्कर पूल के संदर्भ में, इसका मतलब यह है कि जब आप load_model
वाले मॉड्यूल को आयात करते हैं तो आप मॉडल को लोड करेंगे। लाइन, फिर एक अलग थ्रेड some_worker_function
निष्पादित करेगा , केरस मॉडल वाले ग्लोबल वेरिएबल पर प्रेडिक्ट चलाना। हालांकि, एक अलग थ्रेड में लोड किए गए मॉडल पर भविष्यवाणी चलाने की कोशिश करने से "टेंसर इस ग्राफ का तत्व नहीं है" त्रुटियां उत्पन्न करता है। इस विषय पर कई SO पोस्ट के लिए धन्यवाद, जैसे कि ValueError:Tensor Tensor(...) इस ग्राफ का एक तत्व नहीं है। वैश्विक चर केरस मॉडल का उपयोग करते समय। इसे काम करने के लिए, आपको उपयोग किए गए TensorFlow ग्राफ़ पर लटकने की आवश्यकता है-- जैसा कि हमने पहले देखा, ग्राफ़ वर्तमान थ्रेड की एक संपत्ति है। अपडेट किया गया कोड इस तरह दिखता है:
from keras.models import load_model
import tensorflow as tf
MY_MODEL = load_model('path/to/model/file')
MY_GRAPH = tf.get_default_graph()
def some_worker_function(inputs):
with MY_GRAPH.as_default():
return MY_MODEL.predict(inputs)
यहाँ कुछ आश्चर्यजनक मोड़ है:उपरोक्त कोड पर्याप्त है यदि आप Thread
का उपयोग कर रहे हैं s, लेकिन यदि आप Process
का उपयोग कर रहे हैं तो अनिश्चित काल के लिए हैंग हो जाता है तों. और डिफ़ॉल्ट रूप से, सेलेरी अपने सभी वर्कर पूल को प्रबंधित करने के लिए प्रक्रियाओं का उपयोग करती है। तो इस समय, चीज़ें अभी भी हैं अजवाइन पर काम नहीं कर रहा।
यह केवल Thread
पर ही क्यों काम करता है एस?
पायथन में, Thread
s समान वैश्विक निष्पादन संदर्भ को मूल प्रक्रिया के रूप में साझा करते हैं। पायथन _थ्रेड डॉक्स से:
यह मॉड्यूल कई थ्रेड्स (जिसे लाइट-वेट प्रोसेस या टास्क भी कहा जाता है) के साथ काम करने के लिए निम्न-स्तरीय प्रिमिटिव प्रदान करता है - अपने वैश्विक डेटा स्थान को साझा करने वाले नियंत्रण के कई थ्रेड्स।
चूंकि धागे वास्तविक अलग प्रक्रियाएं नहीं हैं, वे एक ही पायथन दुभाषिया का उपयोग करते हैं और इस प्रकार कुख्यात ग्लोबल इंटरपीटर लॉक (जीआईएल) के अधीन हैं। शायद इस जांच के लिए अधिक महत्वपूर्ण, वे साझा करें माता-पिता के साथ वैश्विक डेटा स्थान।
इसके विपरीत, Process
ये वास्तविक हैं कार्यक्रम द्वारा उत्पन्न नई प्रक्रियाएँ। इसका मतलब है:
- नया पायथन दुभाषिया उदाहरण (और कोई GIL नहीं)
- वैश्विक पता स्थान डुप्लिकेट है
यहां अंतर नोट करें। जबकि Thread
s के पास एक साझा एकल वैश्विक सत्र चर तक पहुंच है (आंतरिक रूप से tensorflow_backend
में संग्रहीत है) केरस का मॉड्यूल), Process
es में सत्र चर के डुप्लीकेट हैं।
इस मुद्दे के बारे में मेरी सबसे अच्छी समझ यह है कि सत्र चर को क्लाइंट (प्रक्रिया) और TensorFlow रनटाइम के बीच एक अद्वितीय कनेक्शन का प्रतिनिधित्व करने वाला माना जाता है, लेकिन फोर्किंग प्रक्रिया में डुप्लिकेट होने के कारण, यह कनेक्शन जानकारी ठीक से समायोजित नहीं होती है। यह किसी भिन्न प्रक्रिया में बनाए गए सत्र का उपयोग करने का प्रयास करते समय TensorFlow को लटका देता है। अगर किसी के पास अधिक जानकारी है कि यह TensorFlow में हुड के तहत कैसे काम कर रहा है, तो मुझे यह सुनना अच्छा लगेगा!
समाधान / समाधान
मैं अजवाइन को समायोजित करने के साथ गया ताकि यह Thread
. का उपयोग करे Process
. के बजाय s पूलिंग के लिए है। इस दृष्टिकोण के कुछ नुकसान हैं (उपरोक्त जीआईएल टिप्पणी देखें), लेकिन यह हमें मॉडल को केवल एक बार लोड करने की अनुमति देता है। हम वास्तव में सीपीयू बाध्य नहीं हैं क्योंकि टेंसरफ्लो रनटाइम सभी सीपीयू कोर को अधिकतम करता है (यह जीआईएल को दूर कर सकता है क्योंकि यह पायथन में नहीं लिखा गया है)। थ्रेड-आधारित पूलिंग करने के लिए आपको सेलेरी को एक अलग पुस्तकालय के साथ आपूर्ति करनी होगी; दस्तावेज़ दो विकल्प सुझाते हैं:gevent
या eventlet
. फिर आप अपने द्वारा चुनी गई लाइब्रेरी को --pool
. के माध्यम से वर्कर में पास करते हैं कमांड लाइन तर्क।
वैकल्पिक रूप से, ऐसा लगता है (जैसा कि आपको पहले ही @ pX0r पता चल गया है) कि अन्य केरस बैकएंड जैसे थीनो में यह समस्या नहीं है। यह समझ में आता है, क्योंकि ये मुद्दे TensorFlow कार्यान्वयन विवरण से संबंधित हैं। मैंने व्यक्तिगत रूप से अभी तक थीनो की कोशिश नहीं की है, इसलिए आपका माइलेज भिन्न हो सकता है।
मुझे पता है कि यह प्रश्न कुछ समय पहले पोस्ट किया गया था, लेकिन समस्या अभी भी बनी हुई है, इसलिए उम्मीद है कि इससे किसी को मदद मिलेगी!