TensorFlow 1.x बनाम TensorFlow 2 - व्यवहार और API

TensorFlow.org पर देखें Google Colab में चलाएं गिटहब पर देखें नोटबुक डाउनलोड करें

हुड के तहत, TensorFlow 2 TF1.x से मौलिक रूप से भिन्न प्रोग्रामिंग प्रतिमान का अनुसरण करता है।

यह मार्गदर्शिका व्यवहार और एपीआई के संदर्भ में TF1.x और TF2 के बीच मूलभूत अंतरों का वर्णन करती है, और ये सभी आपकी माइग्रेशन यात्रा से कैसे संबंधित हैं।

प्रमुख परिवर्तनों का उच्च-स्तरीय सारांश

मूल रूप से, TF1.x और TF2 निष्पादन (TF2 में उत्सुक), चर, नियंत्रण प्रवाह, टेंसर आकार और टेंसर समानता तुलनाओं के आसपास रनटाइम व्यवहार के एक अलग सेट का उपयोग करते हैं। TF2 संगत होने के लिए, आपका कोड TF2 व्यवहारों के पूर्ण सेट के साथ संगत होना चाहिए। माइग्रेशन के दौरान, आप इनमें से अधिकांश व्यवहारों को tf.compat.v1.enable_* या tf.compat.v1.disable_* API के माध्यम से व्यक्तिगत रूप से सक्षम या अक्षम कर सकते हैं। एक अपवाद संग्रह को हटाना है, जो उत्सुक निष्पादन को सक्षम/अक्षम करने का एक साइड इफेक्ट है।

उच्च स्तर पर, TensorFlow 2:

  • अनावश्यक एपीआई हटा देता है।
  • एपीआई को अधिक सुसंगत बनाता है - उदाहरण के लिए, एकीकृत आरएनएन और एकीकृत अनुकूलक
  • सत्रों में कार्यों को प्राथमिकता देता है और tf.function के साथ डिफ़ॉल्ट रूप से सक्षम tf.function निष्पादन के साथ पायथन रनटाइम के साथ बेहतर एकीकृत करता है जो ग्राफ़ और संकलन के लिए स्वचालित नियंत्रण निर्भरता प्रदान करता है।
  • वैश्विक ग्राफ संग्रह को हटा देता है।
  • ReferenceVariables पर ResourceVariables का उपयोग करके परिवर्तनीय समवर्ती शब्दार्थ को बदल देता है।
  • फ़ंक्शन-आधारित और अलग-अलग नियंत्रण प्रवाह का समर्थन करता है (नियंत्रण प्रवाह v2)।
  • TensorShape API को tf.compat.v1.Dimension ऑब्जेक्ट्स के बजाय int s को होल्ड करने के लिए सरल बनाता है।
  • टेंसर समानता यांत्रिकी को अद्यतन करता है। TF1.x में == ऑपरेटर टेंसर और वेरिएबल पर ऑब्जेक्ट संदर्भ समानता के लिए जाँच करता है। TF2 में यह मूल्य समानता की जाँच करता है। इसके अतिरिक्त, टेंसर/चर अब धोने योग्य नहीं हैं, लेकिन यदि आप उन्हें सेट में या ताना कुंजियों के रूप में उपयोग करने की आवश्यकता है, तो आप dict var.ref() के माध्यम से उनके लिए हैशबल ऑब्जेक्ट संदर्भ प्राप्त कर सकते हैं।

नीचे दिए गए अनुभाग TF1.x और TF2 के बीच अंतर पर कुछ और संदर्भ प्रदान करते हैं। TF2 के पीछे की डिज़ाइन प्रक्रिया के बारे में अधिक जानने के लिए RFC और डिज़ाइन दस्तावेज़ पढ़ें।

एपीआई सफाई

कई API या तो चले गए हैं या TF2 में चले गए हैं। कुछ बड़े बदलावों में tf.app , tf.flags , और tf.logging को अब ओपन-सोर्स absl-py के पक्ष में हटाना, tf.contrib में रहने वाली परियोजनाओं को फिर से घर देना, और मुख्य tf.* नेमस्पेस को साफ करना शामिल है। कम इस्तेमाल किए गए कार्यों को tf.math जैसे उप-पैकेजों में ले जाना। कुछ API को उनके TF2 समकक्षों - tf.summary , tf.keras.metrics , और tf.keras.optimizers से बदल दिया गया है।

tf.compat.v1 : विरासत और संगतता API समापन बिंदु

tf.compat और tf.compat.v1 नामस्थान के अंतर्गत प्रतीकों को TF2 API नहीं माना जाता है। ये नाम स्थान संगतता प्रतीकों के मिश्रण के साथ-साथ TF 1.x से विरासती API समापन बिंदुओं को उजागर करते हैं। इनका उद्देश्य TF1.x से TF2 में माइग्रेशन में सहायता करना है। हालांकि, चूंकि इनमें से कोई भी compat.v1 API मुहावरेदार TF2 API नहीं है, इसलिए उनका उपयोग बिल्कुल नया TF2 कोड लिखने के लिए न करें।

व्यक्तिगत tf.compat.v1 प्रतीक TF2 संगत हो सकते हैं क्योंकि वे TF2 व्यवहार सक्षम होने पर भी काम करना जारी रखते हैं (जैसे tf.compat.v1.losses.mean_squared_error ), जबकि अन्य TF2 के साथ असंगत हैं (जैसे tf.compat.v1.metrics.accuracy )। कई compat.v1 प्रतीकों (हालांकि सभी नहीं) में उनके दस्तावेज़ीकरण में समर्पित माइग्रेशन जानकारी होती है जो TF2 व्यवहारों के साथ उनकी संगतता की डिग्री के साथ-साथ उन्हें TF2 API में माइग्रेट करने के तरीके के बारे में बताती है।

TF2 अपग्रेड स्क्रिप्ट कई compat.v1 API प्रतीकों को समतुल्य TF2 API में मैप कर सकती है, जहां वे उपनाम हैं या समान तर्क हैं लेकिन एक अलग क्रम के साथ। आप TF1.x API का स्वचालित रूप से नाम बदलने के लिए अपग्रेड स्क्रिप्ट का भी उपयोग कर सकते हैं।

झूठे दोस्त एपीआई

TF2 tf नेमस्पेस ( compat.v1 के तहत नहीं) में "झूठे-मित्र" प्रतीकों का एक सेट पाया गया है जो वास्तव में TF2 व्यवहारों को अंडर-द-हूड को अनदेखा करता है, और/या TF2 व्यवहारों के पूर्ण सेट के साथ पूरी तरह से संगत नहीं है। जैसे, इन एपीआई के TF2 कोड के साथ दुर्व्यवहार करने की संभावना है, संभावित रूप से मूक तरीके से।

  • tf.estimator.* : अनुमानक हुड के नीचे ग्राफ़ और सत्र बनाते हैं और उनका उपयोग करते हैं। जैसे, इन्हें TF2-संगत नहीं माना जाना चाहिए। यदि आपका कोड अनुमानक चला रहा है, तो यह TF2 व्यवहारों का उपयोग नहीं कर रहा है।
  • keras.Model.model_to_estimator(...) : यह हुड के नीचे एक अनुमानक बनाता है, जैसा कि ऊपर बताया गया है TF2-संगत नहीं है।
  • tf.Graph().as_default() : यह TF1.x ग्राफ़ व्यवहार में प्रवेश करता है और मानक TF2-संगत tf.function व्यवहारों का पालन नहीं करता है। इस तरह से ग्राफ़ में प्रवेश करने वाला कोड आमतौर पर उन्हें सत्रों के माध्यम से चलाएगा, और इसे TF2-संगत नहीं माना जाना चाहिए।
  • tf.feature_column.* फीचर कॉलम एपीआई आमतौर पर TF1-स्टाइल tf.compat.v1.get_variable वैरिएबल क्रिएशन पर भरोसा करते हैं और मानते हैं कि बनाए गए वेरिएबल्स को ग्लोबल कलेक्शन के जरिए एक्सेस किया जाएगा। चूंकि TF2 संग्रह का समर्थन नहीं करता है, TF2 व्यवहार सक्षम होने पर उन्हें चलाने पर API ठीक से काम नहीं कर सकते हैं।

अन्य एपीआई परिवर्तन

  • TF2 डिवाइस प्लेसमेंट एल्गोरिदम में महत्वपूर्ण सुधार पेश करता है जो tf.colocate_with अनावश्यक के उपयोग को प्रस्तुत करता है। यदि इसे हटाने से प्रदर्शन में गिरावट आती है तो कृपया एक बग दर्ज करें

  • tf.v1.ConfigProto के सभी उपयोगों को tf.v1.ConfigProto से समकक्ष कार्यों से tf.config

उत्सुक निष्पादन

TF1.x के लिए आवश्यक है कि आप tf.* API कॉल करके एक अमूर्त सिंटैक्स ट्री (ग्राफ़) को मैन्युअल रूप से एक साथ सिलाई करें और फिर एक session.run कॉल के लिए आउटपुट टेंसर और इनपुट टेंसर के एक सेट को पास करके एब्सट्रैक्ट सिंटैक्स ट्री को मैन्युअल रूप से संकलित करें। TF2 उत्सुकता से निष्पादित करता है (जैसे पायथन सामान्य रूप से करता है) और ग्राफ़ और सत्र को कार्यान्वयन विवरण की तरह महसूस कराता है।

उत्सुक निष्पादन का एक उल्लेखनीय उपोत्पाद यह है कि tf.control_dependencies की अब आवश्यकता नहीं है, क्योंकि कोड की सभी पंक्तियाँ क्रम में निष्पादित होती हैं (एक tf.function के भीतर, साइड इफेक्ट वाला कोड लिखे गए क्रम में निष्पादित होता है)।

कोई और वैश्विक नहीं

TF1.x निहित वैश्विक नामस्थानों और संग्रहों पर बहुत अधिक निर्भर करता है। जब आप tf.Variable को कॉल करते हैं, तो इसे डिफ़ॉल्ट ग्राफ़ में एक संग्रह में डाल दिया जाएगा, और यह वहीं रहेगा, भले ही आपने इसे इंगित करने वाले पायथन चर का ट्रैक खो दिया हो। फिर आप उस tf.Variable को पुनर्प्राप्त कर सकते हैं, लेकिन केवल तभी जब आप उस नाम को जानते हों जिसके साथ इसे बनाया गया था। यह करना मुश्किल था यदि आप चर के निर्माण के नियंत्रण में नहीं थे। नतीजतन, सभी प्रकार के तंत्र आपके चर को फिर से खोजने में मदद करने के लिए और उपयोगकर्ता द्वारा बनाए गए चर खोजने के लिए चौखटे के लिए प्रयास करने के लिए बढ़े। इनमें से कुछ में शामिल हैं: वैरिएबल स्कोप्स, ग्लोबल कलेक्शन्स, हेल्पर मेथड्स जैसे tf.get_global_step और tf.global_variables_initializer , ऑप्टिमाइज़र सभी ट्रेनेबल वेरिएबल्स पर परोक्ष रूप से ग्रैडिएंट्स की गणना करते हैं, और इसी तरह। TF2 डिफ़ॉल्ट तंत्र के पक्ष में इन सभी तंत्रों ( Variables 2.0 RFC ) को हटा देता है - आप अपने चर का ट्रैक रखते हैं। यदि आप tf.Variable का ट्रैक खो देते हैं, तो यह कचरा एकत्र हो जाता है।

चर को ट्रैक करने की आवश्यकता कुछ अतिरिक्त काम पैदा करती है, लेकिन मॉडलिंग शिम और व्यवहार जैसे tf.Module s और tf.keras.layers.Layer s में निहित ऑब्जेक्ट-ओरिएंटेड वैरिएबल संग्रह जैसे टूल के साथ, बोझ कम से कम होता है।

कार्य, सत्र नहीं

एक session.run कॉल लगभग एक फ़ंक्शन कॉल की तरह है: आप इनपुट और फ़ंक्शन को कॉल करने के लिए निर्दिष्ट करते हैं, और आप आउटपुट का एक सेट वापस प्राप्त करते हैं। TF2 में, आप JIT संकलन के लिए इसे चिह्नित करने के लिए tf.function का उपयोग करके एक पायथन फ़ंक्शन को सजा सकते हैं ताकि TensorFlow इसे एकल ग्राफ़ ( फ़ंक्शंस 2.0 RFC ) के रूप में चलाए। यह तंत्र TF2 को ग्राफ़ मोड के सभी लाभ प्राप्त करने की अनुमति देता है:

  • प्रदर्शन: फ़ंक्शन को अनुकूलित किया जा सकता है (नोड प्रूनिंग, कर्नेल फ़्यूज़न, आदि)
  • पोर्टेबिलिटी: फ़ंक्शन को निर्यात / पुन: आयात किया जा सकता है ( SavedModel 2.0 RFC ), जिससे आप मॉड्यूलर TensorFlow फ़ंक्शन का पुन: उपयोग और साझा कर सकते हैं।
# TF1.x
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TF2
outputs = f(input)

Python और TensorFlow कोड को स्वतंत्र रूप से प्रतिच्छेद करने की शक्ति के साथ, आप Python की अभिव्यंजना का लाभ उठा सकते हैं। हालाँकि, पोर्टेबल TensorFlow एक पायथन दुभाषिया के बिना संदर्भों में निष्पादित होता है, जैसे कि मोबाइल, C++ और जावास्क्रिप्ट। tf.function जोड़ते समय अपने कोड को फिर से लिखने से बचने में मदद करने के लिए, ऑटोग्राफ का उपयोग पायथन के एक सबसेट को उनके TensorFlow समकक्षों में बदलने के लिए करें:

  • for / while -> tf.while_loop ( break और continue समर्थित हैं)
  • if -> tf.cond
  • for _ in dataset dataset.reduce

ऑटोग्राफ नियंत्रण प्रवाह के मनमाने नेस्टिंग का समर्थन करता है, जिससे अनुक्रम मॉडल, सुदृढीकरण सीखने, कस्टम प्रशिक्षण लूप, और बहुत कुछ जैसे कई जटिल एमएल कार्यक्रमों को प्रदर्शन और संक्षिप्त रूप से लागू करना संभव हो जाता है।

TF 2.x व्यवहार परिवर्तन के अनुकूल होना

TF2 में आपका माइग्रेशन केवल तभी पूर्ण होता है जब आप TF2 व्यवहारों के पूर्ण सेट में माइग्रेट कर लेते हैं। व्यवहार के पूरे सेट को tf.compat.v1.enable_v2_behaviors और tf.compat.v1.disable_v2_behaviors के माध्यम से सक्षम या अक्षम किया जा सकता है। नीचे दिए गए अनुभाग प्रत्येक प्रमुख व्यवहार परिवर्तन पर विस्तार से चर्चा करते हैं।

tf.function s . का उपयोग करना

माइग्रेशन के दौरान आपके कार्यक्रमों में सबसे बड़ा परिवर्तन ग्राफ़ और सत्रों से उत्सुक निष्पादन और tf.function में मूलभूत प्रोग्रामिंग मॉडल प्रतिमान बदलाव से आने की संभावना है। उत्सुक निष्पादन के साथ असंगत APIs और उनके साथ संगत APIs में tf.function से आगे बढ़ने के बारे में अधिक जानने के लिए TF2 माइग्रेशन मार्गदर्शिका देखें।

नीचे कुछ सामान्य प्रोग्राम पैटर्न दिए गए हैं जो किसी एक एपीआई से बंधे नहीं हैं जो tf.Graph s और tf.compat.v1.Session s से tf.function s के साथ उत्सुक निष्पादन के लिए स्विच करते समय समस्याएं पैदा कर सकते हैं।

पैटर्न 1: पायथन ऑब्जेक्ट मैनिपुलेशन और वेरिएबल क्रिएशन जिसे केवल एक बार करने का इरादा है, कई बार रन करें

TF1.x प्रोग्राम में जो ग्राफ़ और सेशन पर निर्भर करते हैं, आमतौर पर यह अपेक्षा होती है कि आपके प्रोग्राम में सभी Python लॉजिक केवल एक बार चलेंगे। हालांकि, उत्सुक निष्पादन और tf.function के साथ यह अपेक्षा करना उचित है कि आपका पायथन तर्क कम से कम एक बार चलाया जाएगा, लेकिन संभवतः अधिक बार (या तो कई बार उत्सुकता से, या कई बार अलग-अलग tf.function निशान)। कभी-कभी, tf.function एक ही इनपुट पर दो बार ट्रेस भी करता है, जिससे अप्रत्याशित व्यवहार होता है (उदाहरण 1 और 2 देखें)। अधिक विवरण के लिए tf.function गाइड देखें।

उदाहरण 1: परिवर्तनीय निर्माण

नीचे दिए गए उदाहरण पर विचार करें, जहां फ़ंक्शन कॉल किए जाने पर एक चर बनाता है:

def f():
  v = tf.Variable(1.0)
  return v

with tf.Graph().as_default():
  with tf.compat.v1.Session() as sess:
    res = f()
    sess.run(tf.compat.v1.global_variables_initializer())
    sess.run(res)

हालांकि, tf.function के साथ परिवर्तनशील निर्माण वाले उपरोक्त फ़ंक्शन को भोलेपन से लपेटने की अनुमति नहीं है। tf.function केवल पहली कॉल पर सिंगलटन वेरिएबल क्रिएशन का समर्थन करता है। इसे लागू करने के लिए, जब tf.function पहली कॉल में चर निर्माण का पता लगाता है, तो यह फिर से ट्रेस करने का प्रयास करेगा और दूसरे ट्रेस में चर निर्माण होने पर एक त्रुटि उत्पन्न करेगा।

@tf.function
def f():
  print("trace") # This will print twice because the python body is run twice
  v = tf.Variable(1.0)
  return v

try:
  f()
except ValueError as e:
  print(e)

वर्कअराउंड पहली कॉल में बनाए जाने के बाद वेरिएबल को कैशिंग और पुन: उपयोग कर रहा है।

class Model(tf.Module):
  def __init__(self):
    self.v = None

  @tf.function
  def __call__(self):
    print("trace") # This will print twice because the python body is run twice
    if self.v is None:
      self.v = tf.Variable(0)
    return self.v

m = Model()
m()

उदाहरण 2: tf. tf.function के कारण आउट-ऑफ़-स्कोप टेंसर

जैसा कि उदाहरण 1 में दिखाया गया है, tf.function जब पहली कॉल में वेरिएबल क्रिएशन का पता लगाता है तो वह फिर से ट्रेस हो जाएगा। यह अतिरिक्त भ्रम पैदा कर सकता है, क्योंकि दो अनुरेखण दो रेखांकन बनाएंगे। जब रिट्रेसिंग से दूसरा ग्राफ़ पहली ट्रेसिंग के दौरान उत्पन्न ग्राफ़ से एक टेंसर तक पहुँचने का प्रयास करता है, तो Tensorflow यह शिकायत करते हुए एक त्रुटि उत्पन्न करेगा कि Tensor गुंजाइश से बाहर है। परिदृश्य को प्रदर्शित करने के लिए, नीचे दिया गया कोड पहले tf.function कॉल पर एक डेटासेट बनाता है। यह उम्मीद के मुताबिक चलेगा।

class Model(tf.Module):
  def __init__(self):
    self.dataset = None

  @tf.function
  def __call__(self):
    print("trace") # This will print once: only traced once
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    it = iter(self.dataset)
    return next(it)

m = Model()
m()

हालांकि, अगर हम पहले tf.function कॉल पर एक वैरिएबल बनाने का भी प्रयास करते हैं, तो कोड एक त्रुटि उत्पन्न करेगा जिसमें शिकायत की जाएगी कि डेटासेट दायरे से बाहर है। ऐसा इसलिए है क्योंकि डेटासेट पहले ग्राफ़ में है, जबकि दूसरा ग्राफ़ भी इसे एक्सेस करने का प्रयास कर रहा है।

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  @tf.function
  def __call__(self):
    print("trace") # This will print twice because the python body is run twice
    if self.v is None:
      self.v = tf.Variable(0)
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
try:
  m()
except TypeError as e:
  print(e) # <tf.Tensor ...> is out of scope and cannot be used here.

सबसे सीधा समाधान यह सुनिश्चित करना है कि चर निर्माण और डेटासेट निर्माण दोनों tf.funciton कॉल के बाहर हैं। उदाहरण के लिए:

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  def initialize(self):
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])
    if self.v is None:
      self.v = tf.Variable(0)

  @tf.function
  def __call__(self):
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
m.initialize()
m()

हालांकि, कभी-कभी tf.function (जैसे कुछ TF keras अनुकूलक में स्लॉट चर) में चर बनाने से बचा नहीं जा सकता है। फिर भी, हम बस डेटासेट निर्माण को tf.function कॉल के बाहर ले जा सकते हैं। इसका कारण यह है कि हम इस पर भरोसा कर सकते हैं क्योंकि tf.function डेटासेट को एक निहित इनपुट के रूप में प्राप्त करेगा और दोनों ग्राफ़ इसे ठीक से एक्सेस कर सकते हैं।

class Model(tf.Module):
  def __init__(self):
    self.v = None
    self.dataset = None

  def initialize(self):
    if self.dataset is None:
      self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])

  @tf.function
  def __call__(self):
    if self.v is None:
      self.v = tf.Variable(0)
    it = iter(self.dataset)
    return [self.v, next(it)]

m = Model()
m.initialize()
m()

उदाहरण 3: तानाशाही उपयोग के कारण अनपेक्षित Tensorflow वस्तु पुन: निर्माण

tf.function को अजगर के साइड इफेक्ट के लिए बहुत खराब समर्थन है जैसे कि किसी सूची में शामिल होना, या किसी शब्दकोश में जांच/जोड़ना। अधिक विवरण "tf.function के साथ बेहतर प्रदर्शन" में हैं। नीचे दिए गए उदाहरण में, कोड डेटासेट और इटरेटर को कैश करने के लिए शब्दकोशों का उपयोग करता है। उसी कुंजी के लिए, मॉडल के लिए प्रत्येक कॉल डेटासेट के समान पुनरावर्तक को वापस कर देगा।

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  def __call__(self, key):
    if key not in self.datasets:
      self.datasets[key] = tf.compat.v1.data.Dataset.from_tensor_slices([1, 2, 3])
      self.iterators[key] = self.datasets[key].make_initializable_iterator()
    return self.iterators[key]

with tf.Graph().as_default():
  with tf.compat.v1.Session() as sess:
    m = Model()
    it = m('a')
    sess.run(it.initializer)
    for _ in range(3):
      print(sess.run(it.get_next())) # prints 1, 2, 3

हालांकि, ऊपर दिया गया पैटर्न tf.function में अपेक्षित रूप से काम नहीं करेगा। ट्रेसिंग के दौरान, tf.function शब्दकोशों में जोड़ने के पायथन साइड इफेक्ट को अनदेखा कर देगा। इसके बजाय, यह केवल एक नए डेटासेट और इटरेटर के निर्माण को याद रखता है। नतीजतन, मॉडल के लिए प्रत्येक कॉल हमेशा एक नया इटरेटर लौटाएगा। इस मुद्दे को नोटिस करना मुश्किल है जब तक कि संख्यात्मक परिणाम या प्रदर्शन पर्याप्त रूप से महत्वपूर्ण न हों। इसलिए, हम उपयोगकर्ताओं को सलाह देते हैं कि tf.function को अजगर कोड पर भोलेपन से लपेटने से पहले कोड के बारे में सावधानी से सोचें।

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  @tf.function
  def __call__(self, key):
    if key not in self.datasets:
      self.datasets[key] = tf.data.Dataset.from_tensor_slices([1, 2, 3])
      self.iterators[key] = iter(self.datasets[key])
    return self.iterators[key]

m = Model()
for _ in range(3):
  print(next(m('a'))) # prints 1, 1, 1

हम अपेक्षित व्यवहार को प्राप्त करने के लिए, ग्राफ़ के बाहर डेटासेट और इटरेटर निर्माण को उठाने के लिए tf.init_scope का उपयोग कर सकते हैं:

class Model(tf.Module):
  def __init__(self):
    self.datasets = {}
    self.iterators = {}

  @tf.function
  def __call__(self, key):
    if key not in self.datasets:
      # Lifts ops out of function-building graphs
      with tf.init_scope():
        self.datasets[key] = tf.data.Dataset.from_tensor_slices([1, 2, 3])
        self.iterators[key] = iter(self.datasets[key])
    return self.iterators[key]

m = Model()
for _ in range(3):
  print(next(m('a'))) # prints 1, 2, 3

अंगूठे का सामान्य नियम अपने तर्क में पायथन के दुष्प्रभावों पर भरोसा करने से बचना है और केवल अपने निशान को डीबग करने के लिए उनका उपयोग करना है।

उदाहरण 4: वैश्विक पायथन सूची में हेरफेर करना

निम्नलिखित TF1.x कोड हानियों की एक वैश्विक सूची का उपयोग करता है जिसका उपयोग यह केवल वर्तमान प्रशिक्षण चरण द्वारा उत्पन्न हानियों की सूची को बनाए रखने के लिए करता है। ध्यान दें कि सूची में नुकसान जोड़ने वाले पायथन तर्क को केवल एक बार बुलाया जाएगा, भले ही सत्र कितने प्रशिक्षण चरणों के लिए चलाया जाए।

all_losses = []

class Model():
  def __call__(...):
    ...
    all_losses.append(regularization_loss)
    all_losses.append(label_loss_a)
    all_losses.append(label_loss_b)
    ...

g = tf.Graph()
with g.as_default():
  ...
  # initialize all objects
  model = Model()
  optimizer = ...
  ...
  # train step
  model(...)
  total_loss = tf.reduce_sum(all_losses)
  optimizer.minimize(total_loss)
  ...
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)  

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

all_losses = []

class Model():
  def __call__(...):
    ...
    all_losses.append(regularization_loss)
    all_losses.append(label_loss_a)
    all_losses.append(label_loss_b)
    ...

# initialize all objects
model = Model()
optimizer = ...

def train_step(...)
  ...
  model(...)
  total_loss = tf.reduce_sum(all_losses) # global list is never cleared,
  # Accidentally accumulates sum loss across all training steps
  optimizer.minimize(total_loss)
  ...

पैटर्न 2: TF1.x में प्रत्येक चरण की पुनर्गणना के लिए एक प्रतीकात्मक टेंसर गलती से प्रारंभिक मान के साथ कैश किया जाता है जब उत्सुकता पर स्विच किया जाता है।

यह पैटर्न आमतौर पर आपके कोड को tf.functions के बाहर उत्सुकता से निष्पादित करते समय चुपचाप दुर्व्यवहार करने का कारण बनता है, लेकिन यदि प्रारंभिक मान कैशिंग tf.function InaccessibleTensorError । हालांकि, ध्यान रखें कि उपरोक्त पैटर्न 1 से बचने के लिए आप अक्सर अनजाने में अपने कोड को इस तरह से संरचित करेंगे कि यह प्रारंभिक मूल्य कैशिंग किसी भी tf.function के बाहर होगा जो एक त्रुटि उत्पन्न करने में सक्षम होगा। इसलिए, यदि आप जानते हैं कि आपका कार्यक्रम इस पैटर्न के लिए अतिसंवेदनशील हो सकता है, तो अतिरिक्त सावधानी बरतें।

इस पैटर्न का सामान्य समाधान कोड को पुनर्गठित करना है या यदि आवश्यक हो तो यह सुनिश्चित करने के लिए कि प्रत्येक बार गलती से कैश किए जाने के बजाय मूल्य को फिर से गणना की जाती है, पायथन कॉलेबल्स का उपयोग करें।

उदाहरण 1: सीखने की दर/हाइपरपैरामीटर/आदि। अनुसूचियां जो वैश्विक कदम पर निर्भर करती हैं

निम्नलिखित कोड स्निपेट में, उम्मीद यह है कि हर बार सत्र चलने पर सबसे हालिया global_step मान पढ़ा जाएगा और एक नई सीखने की दर की गणना की जाएगी।

g = tf.Graph()
with g.as_default():
  ...
  global_step = tf.Variable(0)
  learning_rate = 1.0 / global_step
  opt = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)
  ...
  global_step.assign_add(1)
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)

हालांकि, उत्सुकता पर स्विच करने का प्रयास करते समय, सीखने की दर के साथ समाप्त होने से सावधान रहें, केवल एक बार गणना की जा रही है, फिर से पुन: उपयोग किया जाता है, बजाय इच्छित शेड्यूल का पालन करने के:

global_step = tf.Variable(0)
learning_rate = 1.0 / global_step # Wrong! Only computed once!
opt = tf.keras.optimizers.SGD(learning_rate)

def train_step(...):
  ...
  opt.apply_gradients(...)
  global_step.assign_add(1)
  ...

क्योंकि यह विशिष्ट उदाहरण एक सामान्य पैटर्न है और ऑप्टिमाइज़र को प्रत्येक प्रशिक्षण चरण के बजाय केवल एक बार प्रारंभ किया जाना चाहिए, TF2 ऑप्टिमाइज़र tf.keras.optimizers.schedules.LearningRateSchedule शेड्यूल या पायथन कॉलेबल्स को सीखने की दर और अन्य हाइपरपैरामीटर के तर्क के रूप में समर्थन करते हैं।

उदाहरण 2: ऑब्जेक्ट एट्रिब्यूट्स के रूप में असाइन किए गए प्रतीकात्मक रैंडम नंबर इनिशियलाइज़ेशन फिर पॉइंटर के माध्यम से पुन: उपयोग किए जाते हैं, जब उत्सुकता से स्विच करते समय गलती से कैश हो जाते हैं

निम्नलिखित NoiseAdder मॉड्यूल पर विचार करें:

class NoiseAdder(tf.Module):
  def __init__(shape, mean):
    self.noise_distribution = tf.random.normal(shape=shape, mean=mean)
    self.trainable_scale = tf.Variable(1.0, trainable=True)

  def add_noise(input):
    return (self.noise_distribution + input) * self.trainable_scale

TF1.x में निम्नानुसार इसका उपयोग करना सत्र चलने पर हर बार एक नए यादृच्छिक शोर टेंसर की गणना करेगा:

g = tf.Graph()
with g.as_default():
  ...
  # initialize all variable-containing objects
  noise_adder = NoiseAdder(shape, mean)
  ...
  # computation pass
  x_with_noise = noise_adder.add_noise(x)
  ...
...
sess = tf.compat.v1.Session(graph=g)
sess.run(...)

हालाँकि, TF2 में शुरुआत में noise_adder को इनिशियलाइज़ करने से noise_distribution की गणना केवल एक बार की जाएगी और सभी प्रशिक्षण चरणों के लिए फ़्रीज़ हो जाएगी:

...
# initialize all variable-containing objects
noise_adder = NoiseAdder(shape, mean) # Freezes `self.noise_distribution`!
...
# computation pass
x_with_noise = noise_adder.add_noise(x)
...

इसे ठीक करने के लिए, हर बार एक ही टेंसर ऑब्जेक्ट को संदर्भित करने के बजाय, हर बार एक नए रैंडम टेंसर की आवश्यकता होने पर NoiseAdder को कॉल करने के लिए tf.random.normal को रिफैक्टर करें।

class NoiseAdder(tf.Module):
  def __init__(shape, mean):
    self.noise_distribution = lambda: tf.random.normal(shape=shape, mean=mean)
    self.trainable_scale = tf.Variable(1.0, trainable=True)

  def add_noise(input):
    return (self.noise_distribution() + input) * self.trainable_scale

पैटर्न 3: TF1.x कोड सीधे नाम से टेंसर पर निर्भर करता है और देखता है

TF1.x कोड परीक्षणों के लिए यह जाँचना आम बात है कि ग्राफ़ में कौन से टेंसर या ऑपरेशन मौजूद हैं। कुछ दुर्लभ मामलों में, मॉडलिंग कोड नाम से इन लुकअप पर भी निर्भर करेगा।

tf.function के बाहर उत्सुकतापूर्वक निष्पादित करते समय टेंसर नाम उत्पन्न नहीं होते हैं, इसलिए tf.function के सभी उपयोग tf.Tensor.name के अंदर होने tf.function । ध्यान रखें कि वास्तविक जेनरेट किए गए नाम TF1.x और TF2 के बीच समान tf.function के भीतर भी भिन्न होने की संभावना है, और API गारंटी TF संस्करणों में जेनरेट किए गए नामों की स्थिरता सुनिश्चित नहीं करती है।

पैटर्न 4: TF1.x सत्र चुनिंदा रूप से जेनरेट किए गए ग्राफ़ का केवल एक भाग चलाता है

TF1.x में, आप एक ग्राफ का निर्माण कर सकते हैं और फिर केवल चुनिंदा इनपुट और आउटपुट का एक सेट चुनकर सत्र के साथ इसका केवल एक सबसेट चलाना चुन सकते हैं, जिसके लिए ग्राफ़ में हर ऑप को चलाने की आवश्यकता नहीं होती है।

उदाहरण के लिए, आपके पास एक ही ग्राफ़ के अंदर एक जनरेटर और एक विवेचक दोनों हो सकते हैं, और अलग-अलग tf.compat.v1.Session.run कॉल का उपयोग केवल विवेचक को प्रशिक्षण देने या केवल जनरेटर को प्रशिक्षित करने के बीच वैकल्पिक करने के लिए कर सकते हैं।

TF2 में, tf.function और उत्सुक निष्पादन में स्वत: नियंत्रण निर्भरता के कारण, tf.function ट्रेस की कोई चयनात्मक छंटाई नहीं होती है। सभी परिवर्तनशील अद्यतनों वाला एक पूर्ण ग्राफ चलाया जाएगा, भले ही, उदाहरण के लिए, केवल विवेचक या जनरेटर का आउटपुट tf.function से आउटपुट हो।

इसलिए, आपको या तो प्रोग्राम के विभिन्न भागों वाले एकाधिक tf.function s का उपयोग करना होगा, या tf.function के लिए एक सशर्त तर्क का उपयोग करना होगा ताकि आप केवल उन चीज़ों को निष्पादित कर सकें जिन्हें आप वास्तव में चलाना चाहते हैं।

संग्रह हटाना

जब उत्सुक निष्पादन सक्षम होता है, तो ग्राफ़ संग्रह-संबंधित compat.v1 API (जिसमें tf.compat.v1.trainable_variables जैसे हुड के तहत संग्रह को पढ़ना या लिखना शामिल है) अब उपलब्ध नहीं हैं। कुछ ValueError s बढ़ा सकते हैं, जबकि अन्य चुपचाप खाली सूचियाँ लौटा सकते हैं।

TF1.x में संग्रह का सबसे मानक उपयोग प्रारंभकर्ताओं, वैश्विक चरण, भार, नियमितीकरण हानियों, मॉडल आउटपुट हानियों, और चर अद्यतनों को बनाए रखने के लिए है जिन्हें चलाने की आवश्यकता है जैसे कि BatchNormalization परतों से।

इनमें से प्रत्येक मानक उपयोग को संभालने के लिए:

  1. प्रारंभकर्ता - अनदेखा करें। उत्सुक निष्पादन सक्षम होने के साथ मैन्युअल चर आरंभीकरण की आवश्यकता नहीं है।
  2. वैश्विक चरण - माइग्रेशन निर्देशों के लिए tf.compat.v1.train.get_or_create_global_step का दस्तावेज़ देखें।
  3. वज़न - अपने मॉडल को tf.Module s/ tf.keras.layers.Layer s/ tf.keras.Model s के लिए मॉडल मैपिंग गाइड में मार्गदर्शन का पालन करके मैप करें और फिर उनके संबंधित वजन-ट्रैकिंग तंत्र जैसे tf.module.trainable_variables
  4. नियमितीकरण हानियाँ - अपने मॉडल को tf.Module s/ tf.keras.layers.Layer s/ tf.keras.Model s के लिए मॉडल मैपिंग गाइड में मार्गदर्शन का पालन करके मैप करें और फिर tf.keras.losses का उपयोग करें। वैकल्पिक रूप से, आप अपने नियमितीकरण के नुकसान को मैन्युअल रूप से भी ट्रैक कर सकते हैं।
  5. मॉडल आउटपुट लॉस - tf.keras.Model लॉस मैनेजमेंट मैकेनिज्म का उपयोग करें या संग्रह का उपयोग किए बिना अपने नुकसान को अलग से ट्रैक करें।
  6. वजन अद्यतन - इस संग्रह पर ध्यान न दें। उत्सुक निष्पादन और tf.function (ऑटोग्राफ और ऑटो-कंट्रोल-निर्भरता के साथ) का अर्थ है कि सभी परिवर्तनीय अपडेट स्वचालित रूप से चलेंगे। इसलिए, आपको अंत में सभी वज़न अपडेट को स्पष्ट रूप से चलाने की ज़रूरत नहीं होगी, लेकिन ध्यान दें कि इसका मतलब है कि वज़न अपडेट आपके TF1.x कोड की तुलना में एक अलग समय पर हो सकता है, इस पर निर्भर करता है कि आप नियंत्रण निर्भरताओं का उपयोग कैसे कर रहे थे।
  7. सारांश - माइग्रेट सारांश API मार्गदर्शिका देखें.

अधिक जटिल संग्रह उपयोग (जैसे कि कस्टम संग्रह का उपयोग करना) के लिए आपको अपने कोड को रीफैक्टर करने की आवश्यकता हो सकती है या तो अपने स्वयं के वैश्विक स्टोर बनाए रखने के लिए, या इसे वैश्विक स्टोर पर बिल्कुल भी निर्भर न करने के लिए।

ReferenceVariables चर के बजाय ResourceVariables चर

ResourceVariables में ReferenceVariables की तुलना में अधिक मजबूत पठन-लेखन स्थिरता गारंटी है। यह शब्दार्थ के बारे में अधिक अनुमानित, आसान-से-कारण की ओर जाता है कि आप अपने चर का उपयोग करते समय पिछले लेखन के परिणाम का निरीक्षण करेंगे या नहीं। इस परिवर्तन से मौजूदा कोड में त्रुटियां होने या चुपचाप टूटने की संभावना नहीं है।

हालांकि, यह संभव नहीं है, हालांकि यह संभावना नहीं है कि ये मजबूत स्थिरता गारंटी आपके विशिष्ट प्रोग्राम के मेमोरी उपयोग को बढ़ा सकती है। यदि आपको ऐसा लगता है तो कृपया समस्या दर्ज करें। इसके अतिरिक्त, यदि आपके पास वेरिएबल रीड्स के अनुरूप ग्राफ़ में ऑपरेटर नामों के विरुद्ध सटीक स्ट्रिंग तुलनाओं पर निर्भर यूनिट परीक्षण हैं, तो ध्यान रखें कि संसाधन चर सक्षम करने से इन ऑपरेटरों का नाम थोड़ा बदल सकता है।

अपने कोड पर इस व्यवहार परिवर्तन के प्रभाव को अलग करने के लिए, यदि उत्सुक निष्पादन अक्षम है तो आप इस व्यवहार परिवर्तन को वैश्विक रूप से अक्षम या सक्षम करने के लिए tf.compat.v1.disable_resource_variables() और tf.compat.v1.enable_resource_variables() का उपयोग कर सकते हैं। यदि उत्सुक निष्पादन सक्षम है तो ResourceVariables हमेशा उपयोग किए जाएंगे।

नियंत्रण प्रवाह v2

TF1.x में, नियंत्रण प्रवाह ऑप्स जैसे tf.cond और tf.while_loop इनलाइन निम्न-स्तरीय ऑप्स जैसे Switch , Merge आदि। TF2 बेहतर कार्यात्मक नियंत्रण प्रवाह ऑप्स प्रदान करता है जो प्रत्येक शाखा और समर्थन के लिए अलग tf.function . फ़ंक्शन ट्रेस के साथ कार्यान्वित किए जाते हैं। उच्च-क्रम विभेदन।

अपने कोड पर इस व्यवहार परिवर्तन के प्रभाव को अलग करने के लिए, यदि उत्सुक निष्पादन अक्षम है तो आप इस व्यवहार परिवर्तन को वैश्विक रूप से अक्षम या सक्षम करने के लिए tf.compat.v1.disable_control_flow_v2() और tf.compat.v1.enable_control_flow_v2() का उपयोग कर सकते हैं। हालाँकि, आप केवल नियंत्रण प्रवाह v2 को अक्षम कर सकते हैं यदि उत्सुक निष्पादन भी अक्षम है। यदि यह सक्षम है, तो नियंत्रण प्रवाह v2 हमेशा उपयोग किया जाएगा।

यह व्यवहार परिवर्तन नाटकीय रूप से उत्पन्न TF प्रोग्रामों की संरचना को बदल सकता है जो नियंत्रण प्रवाह का उपयोग करते हैं, क्योंकि उनमें एक फ्लैट ग्राफ़ के बजाय कई नेस्टेड फ़ंक्शन ट्रेस होंगे। इसलिए, कोई भी कोड जो उत्पादित ट्रेस के सटीक शब्दार्थ पर अत्यधिक निर्भर है, उसे कुछ संशोधन की आवश्यकता हो सकती है। यह भी शामिल है:

  • ऑपरेटर और टेंसर नामों पर निर्भर कोड
  • कोड उस शाखा के बाहर से एक TensorFlow नियंत्रण प्रवाह शाखा के भीतर बनाए गए टेंसर का संदर्भ देता है। यह एक InaccessibleTensorError उत्पन्न करने की संभावना है

इस व्यवहार परिवर्तन का उद्देश्य प्रदर्शन तटस्थ से सकारात्मक होना है, लेकिन यदि आप किसी ऐसे मुद्दे पर चलते हैं जहां नियंत्रण प्रवाह v2 आपके लिए TF1.x नियंत्रण प्रवाह से भी बदतर प्रदर्शन करता है तो कृपया पुनरुत्पादन चरणों के साथ एक समस्या दर्ज करें।

TensorShape API व्यवहार में परिवर्तन

TensorShape वर्ग को tf.compat.v1.Dimension वस्तुओं के बजाय int s धारण करने के लिए सरल बनाया गया था। तो int प्राप्त करने के लिए .value को कॉल करने की कोई आवश्यकता नहीं है।

व्यक्तिगत tf.compat.v1.Dimension ऑब्जेक्ट अभी भी tf.TensorShape.dims से पहुंच योग्य हैं।

अपने कोड पर इस व्यवहार परिवर्तन के प्रभाव को अलग करने के लिए, आप इस व्यवहार परिवर्तन को वैश्विक रूप से अक्षम या सक्षम करने के लिए tf.compat.v1.disable_v2_tensorshape() और tf.compat.v1.enable_v2_tensorshape() का उपयोग कर सकते हैं।

निम्नलिखित TF1.x और TF2 के बीच अंतर प्रदर्शित करता है।

import tensorflow as tf
# Create a shape and choose an index
i = 0
shape = tf.TensorShape([16, None, 256])
shape
TensorShape([16, None, 256])

यदि आपके पास यह TF1.x में था:

value = shape[i].value

फिर इसे TF2 में करें:

value = shape[i]
value
16

यदि आपके पास यह TF1.x में था:

for dim in shape:
    value = dim.value
    print(value)

फिर, इसे TF2 में करें:

for value in shape:
  print(value)
16
None
256

यदि आपके पास यह TF1.x (या किसी अन्य आयाम विधि का उपयोग) में था:

dim = shape[i]
dim.assert_is_compatible_with(other_dim)

फिर इसे TF2 में करें:

other_dim = 16
Dimension = tf.compat.v1.Dimension

if shape.rank is None:
  dim = Dimension(None)
else:
  dim = shape.dims[i]
dim.is_compatible_with(other_dim) # or any other dimension method
True
shape = tf.TensorShape(None)

if shape:
  dim = shape.dims[i]
  dim.is_compatible_with(other_dim) # or any other dimension method

यदि रैंक ज्ञात है, तो tf.TensorShape का बूलियन मान True है, अन्यथा False है।

print(bool(tf.TensorShape([])))      # Scalar
print(bool(tf.TensorShape([0])))     # 0-length vector
print(bool(tf.TensorShape([1])))     # 1-length vector
print(bool(tf.TensorShape([None])))  # Unknown-length vector
print(bool(tf.TensorShape([1, 10, 100])))       # 3D tensor
print(bool(tf.TensorShape([None, None, None]))) # 3D tensor with no known dimensions
print()
print(bool(tf.TensorShape(None)))  # A tensor with unknown rank.
True
True
True
True
True
True

False

TensorShape परिवर्तनों के कारण संभावित त्रुटियाँ

TensorShape व्यवहार परिवर्तन आपके कोड को चुपचाप तोड़ने की संभावना नहीं है। हालाँकि, आप देख सकते हैं कि आकृति-संबंधित कोड AttributeError s को int s के रूप में उठाना शुरू कर देता है और None नहीं में वही विशेषताएँ नहीं होती हैं जो tf.compat.v1.Dimension s करते हैं। नीचे इन AttributeError s के कुछ उदाहरण दिए गए हैं:

try:
  # Create a shape and choose an index
  shape = tf.TensorShape([16, None, 256])
  value = shape[0].value
except AttributeError as e:
  # 'int' object has no attribute 'value'
  print(e)
'int' object has no attribute 'value'
try:
  # Create a shape and choose an index
  shape = tf.TensorShape([16, None, 256])
  dim = shape[1]
  other_dim = shape[2]
  dim.assert_is_compatible_with(other_dim)
except AttributeError as e:
  # 'NoneType' object has no attribute 'assert_is_compatible_with'
  print(e)
'NoneType' object has no attribute 'assert_is_compatible_with'

मूल्य द्वारा टेंसर समानता

बाइनरी == और != वेरिएबल और टेंसर पर ऑपरेटरों को TF1.x जैसे ऑब्जेक्ट संदर्भ से तुलना करने के बजाय TF2 में मान से तुलना करने के लिए बदल दिया गया था। इसके अतिरिक्त, टेंसर और वेरिएबल अब सेट या डिक्ट कीज़ में सीधे हैशेबल या प्रयोग करने योग्य नहीं हैं, क्योंकि मूल्य के आधार पर उन्हें हैश करना संभव नहीं हो सकता है। इसके बजाय, वे एक .ref() विधि का पर्दाफाश करते हैं जिसका उपयोग आप टेंसर या चर के लिए हैशबल संदर्भ प्राप्त करने के लिए कर सकते हैं।

इस व्यवहार परिवर्तन के प्रभाव को अलग करने के लिए, आप इस व्यवहार परिवर्तन को वैश्विक रूप से अक्षम या सक्षम करने के लिए tf.compat.v1.disable_tensor_equality() और tf.compat.v1.enable_tensor_equality() का उपयोग कर सकते हैं।

उदाहरण के लिए, TF1.x में, जब आप == ऑपरेटर का उपयोग करते हैं, तो समान मान वाले दो वेरिएबल झूठी वापसी करेंगे:

tf.compat.v1.disable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x == y
False

जबकि TF2 में टेंसर समानता जांच सक्षम है, x == y True लौटाएगा।

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x == y
<tf.Tensor: shape=(), dtype=bool, numpy=True>

इसलिए, TF2 में, यदि आपको ऑब्जेक्ट संदर्भ द्वारा तुलना करने की आवश्यकता है, तो सुनिश्चित करें कि उपयोग करना is और is not

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)
y = tf.Variable(0.0)

x is y
False

हैशिंग टेंसर और वैरिएबल

TF1.x व्यवहार के साथ आप डेटा संरचनाओं में सीधे चर और टेंसर जोड़ने में सक्षम होते थे, जिन्हें हैशिंग की आवश्यकता होती है, जैसे कि set और dict कुंजियाँ।

tf.compat.v1.disable_tensor_equality()
x = tf.Variable(0.0)
set([x, tf.constant(2.0)])
{<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=2.0>}

हालांकि, TF2 में टेन्सर समानता सक्षम होने के कारण, == और != ऑपरेटर सेमेन्टिक्स को मूल्य समानता जांच में बदलने के कारण टेंसर और वेरिएबल को अप्राप्य बना दिया जाता है।

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)

try:
  set([x, tf.constant(2.0)])
except TypeError as e:
  # TypeError: Variable is unhashable. Instead, use tensor.ref() as the key.
  print(e)
Variable is unhashable. Instead, use tensor.ref() as the key.

इसलिए, TF2 में यदि आपको कुंजी या set सामग्री के रूप में टेंसर या चर वस्तुओं का उपयोग करने की आवश्यकता है, तो आप एक हैशेबल संदर्भ प्राप्त करने के लिए tensor.ref() का उपयोग कर सकते हैं जिसे एक कुंजी के रूप में उपयोग किया जा सकता है:

tf.compat.v1.enable_tensor_equality()
x = tf.Variable(0.0)

tensor_set = set([x.ref(), tf.constant(2.0).ref()])
assert x.ref() in tensor_set

tensor_set
{<Reference wrapping <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>>,
 <Reference wrapping <tf.Tensor: shape=(), dtype=float32, numpy=2.0>>}

यदि आवश्यक हो, तो आप Reference.deref reference.deref() से टेंसर या वेरिएबल भी प्राप्त कर सकते हैं:

referenced_var = x.ref().deref()
assert referenced_var is x
referenced_var
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>

संसाधन और आगे पढ़ना

  • TF1.x से TF2 में माइग्रेट करने के बारे में अधिक पढ़ने के लिए TF2 में माइग्रेट करें अनुभाग पर जाएं।
  • सीधे TF2 में काम करने के लिए अपने TF1.x मॉडल को मैप करने के बारे में अधिक जानने के लिए मॉडल मैपिंग गाइड पढ़ें।