TensorFlow.org पर देखें | Google Colab में चलाएं | GitHub पर स्रोत देखें | नोटबुक डाउनलोड करें |
TensorFlow 2 में, उत्सुक निष्पादन डिफ़ॉल्ट रूप से चालू होता है। उपयोगकर्ता इंटरफ़ेस सहज और लचीला है (एक बार के संचालन को चलाना बहुत आसान और तेज़ है), लेकिन यह प्रदर्शन और तैनाती की कीमत पर आ सकता है।
आप अपने प्रोग्राम से ग्राफ़ बनाने के लिए tf.function
का उपयोग कर सकते हैं। यह एक परिवर्तन उपकरण है जो आपके पायथन कोड से पायथन-स्वतंत्र डेटाफ्लो ग्राफ़ बनाता है। यह आपको प्रदर्शनकारी और पोर्टेबल मॉडल बनाने में मदद करेगा, और इसके लिए SavedModel
का उपयोग करना आवश्यक है।
यह मार्गदर्शिका आपको यह समझने में मदद करेगी कि tf.function
कैसे काम करता है, ताकि आप इसका प्रभावी ढंग से उपयोग कर सकें।
मुख्य टेकअवे और सिफारिशें हैं:
- उत्सुक मोड में डीबग करें, फिर
@tf.function
से सजाएं। - ऑब्जेक्ट म्यूटेशन या लिस्ट एपेंड जैसे पायथन साइड इफेक्ट्स पर भरोसा न करें।
-
tf.function
TensorFlow ops के साथ सबसे अच्छा काम करता है; NumPy और Python कॉल को स्थिरांक में बदल दिया जाता है।
सेट अप
import tensorflow as tf
आपके सामने आने वाली त्रुटियों के प्रकार को प्रदर्शित करने के लिए एक सहायक फ़ंक्शन को परिभाषित करें:
import traceback
import contextlib
# Some helper code to demonstrate the kinds of errors you might encounter.
@contextlib.contextmanager
def assert_raises(error_class):
try:
yield
except error_class as e:
print('Caught expected exception \n {}:'.format(error_class))
traceback.print_exc(limit=2)
except Exception as e:
raise e
else:
raise Exception('Expected {} to be raised but no error was raised!'.format(
error_class))
मूल बातें
प्रयोग
एक Function
जिसे आप परिभाषित करते हैं (उदाहरण के लिए @tf.function
डेकोरेटर को लागू करके) एक कोर TensorFlow ऑपरेशन की तरह है: आप इसे उत्सुकता से निष्पादित कर सकते हैं; आप ग्रेडियेंट की गणना कर सकते हैं; और इसी तरह।
@tf.function # The decorator converts `add` into a `Function`.
def add(a, b):
return a + b
add(tf.ones([2, 2]), tf.ones([2, 2])) # [[2., 2.], [2., 2.]]
<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[2., 2.], [2., 2.]], dtype=float32)>
v = tf.Variable(1.0)
with tf.GradientTape() as tape:
result = add(v, 1.0)
tape.gradient(result, v)
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>
आप अन्य Function
s के अंदर Function
s का उपयोग कर सकते हैं।
@tf.function
def dense_layer(x, w, b):
return add(tf.matmul(x, w), b)
dense_layer(tf.ones([3, 2]), tf.ones([2, 2]), tf.ones([2]))
<tf.Tensor: shape=(3, 2), dtype=float32, numpy= array([[3., 3.], [3., 3.], [3., 3.]], dtype=float32)>
Function
s उत्सुक कोड से तेज़ हो सकते हैं, खासकर कई छोटे ऑप्स वाले ग्राफ़ के लिए। लेकिन कुछ महंगे ऑप्स (जैसे कनवल्शन) वाले ग्राफ़ के लिए, आपको अधिक गति दिखाई नहीं दे सकती है।
import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)
@tf.function
def conv_fn(image):
return conv_layer(image)
image = tf.zeros([1, 200, 200, 100])
# Warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")
Eager conv: 0.006058974999177735 Function conv: 0.005791576000774512 Note how there's not much difference in performance for convolutions
अनुरेखण
यह खंड उजागर करता है कि Function
हुड के तहत कैसे काम करता है, जिसमें कार्यान्वयन विवरण शामिल हैं जो भविष्य में बदल सकते हैं । हालांकि, एक बार जब आप समझ जाते हैं कि ट्रेसिंग क्यों और कब होती है, तो tf.function
का प्रभावी ढंग से उपयोग करना बहुत आसान हो जाता है!
"ट्रेसिंग" क्या है?
एक Function
आपके प्रोग्राम को TensorFlow ग्राफ़ में चलाता है। हालांकि, एक tf.Graph
उन सभी चीजों का प्रतिनिधित्व नहीं कर सकता है जो आप एक उत्सुक TensorFlow प्रोग्राम में लिखेंगे। उदाहरण के लिए, पायथन बहुरूपता का समर्थन करता है, लेकिन tf.Graph
को एक निर्दिष्ट डेटा प्रकार और आयाम के लिए इसके इनपुट की आवश्यकता होती है। या आप कमांड-लाइन तर्क पढ़ने, त्रुटि उठाने, या अधिक जटिल पायथन ऑब्जेक्ट के साथ काम करने जैसे साइड कार्य कर सकते हैं; इनमें से कोई भी चीज tf.Graph
में नहीं चल सकती है।
Function
आपके कोड को दो चरणों में अलग करके इस अंतर को पाटता है:
1) पहले चरण में, जिसे " ट्रेसिंग " कहा जाता है, Function
एक नया tf.Graph
बनाता है। पायथन कोड सामान्य रूप से चलता है, लेकिन सभी TensorFlow संचालन (जैसे दो Tensors जोड़ना) स्थगित कर दिए जाते हैं: वे tf.Graph
द्वारा कैप्चर किए जाते हैं और नहीं चलते हैं।
2) दूसरे चरण में, एक tf.Graph
जाता है जिसमें वह सब कुछ होता है जिसे पहले चरण में स्थगित किया गया था। यह चरण अनुरेखण चरण की तुलना में बहुत तेज है।
इसके इनपुट के आधार पर, Function
हमेशा पहले चरण को नहीं चलाएगा जब इसे कहा जाता है। यह कैसे निर्धारण करता है, इसकी बेहतर समझ प्राप्त करने के लिए नीचे "अनुरेखण के नियम" देखें। पहले चरण को छोड़ना और केवल दूसरे चरण को निष्पादित करना ही आपको TensorFlow का उच्च प्रदर्शन देता है।
जब Function
ट्रेस करने का निर्णय लेता है, तो ट्रेसिंग चरण तुरंत दूसरे चरण के बाद आता है, इसलिए Function
को कॉल करना दोनों tf.Graph
बनाता है और चलाता है। बाद में आप देखेंगे कि आप get_concrete_function
के साथ केवल ट्रेसिंग चरण कैसे चला सकते हैं।
जब आप विभिन्न प्रकार के तर्कों को Function
में पास करते हैं, तो दोनों चरण चलाए जाते हैं:
@tf.function
def double(a):
print("Tracing with", a)
return a + a
print(double(tf.constant(1)))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant("a")))
print()
Tracing with Tensor("a:0", shape=(), dtype=int32) tf.Tensor(2, shape=(), dtype=int32) Tracing with Tensor("a:0", shape=(), dtype=float32) tf.Tensor(2.2, shape=(), dtype=float32) Tracing with Tensor("a:0", shape=(), dtype=string) tf.Tensor(b'aa', shape=(), dtype=string)
ध्यान दें कि यदि आप एक ही तर्क प्रकार के साथ एक Function
को बार-बार कॉल करते हैं, तो TensorFlow ट्रेसिंग चरण को छोड़ देगा और पहले से ट्रेस किए गए ग्राफ़ का पुन: उपयोग करेगा, क्योंकि उत्पन्न ग्राफ़ समान होगा।
# This doesn't print 'Tracing with ...'
print(double(tf.constant("b")))
tf.Tensor(b'bb', shape=(), dtype=string)
सभी उपलब्ध निशान देखने के लिए आप pretty_printed_concrete_signatures()
का उपयोग कर सकते हैं:
print(double.pretty_printed_concrete_signatures())
double(a) Args: a: int32 Tensor, shape=() Returns: int32 Tensor, shape=() double(a) Args: a: float32 Tensor, shape=() Returns: float32 Tensor, shape=() double(a) Args: a: string Tensor, shape=() Returns: string Tensor, shape=()
अब तक, आपने देखा है कि tf.function
TensorFlow के ग्राफ़ ट्रेसिंग लॉजिक पर एक कैश्ड, डायनेमिक डिस्पैच लेयर बनाता है। शब्दावली के बारे में अधिक विशिष्ट होने के लिए:
- एक
tf.Graph
एक TensorFlow गणना का कच्चा, भाषा-अज्ञेय, पोर्टेबल प्रतिनिधित्व है। - एक
ConcreteFunction
एकtf.Graph
लपेटता है। - एक
Function
ConcreteFunction
s के कैश का प्रबंधन करता है और आपके इनपुट के लिए सही को चुनता है। -
tf.function
एक पायथन फ़ंक्शन को लपेटता है, एकFunction
ऑब्जेक्ट लौटाता है। - ट्रेसिंग एक
tf.Graph
बनाता है और इसेConcreteFunction
में लपेटता है, जिसे ट्रेस के रूप में भी जाना जाता है।
ट्रेसिंग के नियम
एक Function
यह निर्धारित करता है कि किसी इनपुट के args और kwargs से कैश कुंजी की गणना करके किसी ट्रेस किए गए ConcreteFunction
का पुन: उपयोग करना है या नहीं। एक कैश कुंजी एक कुंजी है जो निम्नलिखित नियमों (जो बदल सकती है) के अनुसार Function
कॉल के इनपुट आर्ग और क्वार्ग के आधार पर ConcreteFunction
फ़ंक्शन की पहचान करती है:
-
tf.Tensor
के लिए उत्पन्न कुंजी इसका आकार और प्रकार है। - एक
tf.Variable
के लिए उत्पन्न कुंजी एक अद्वितीय चर आईडी है। - पायथन आदिम (जैसे
int
,float
,str
) के लिए उत्पन्न कुंजी इसका मान है। - नेस्टेड
dict
s,list
s,tuple
s,namedtuple
s, औरattr
s के लिए उत्पन्न कुंजी लीफ-कीज़ का चपटा टपल है (देखेंnest.flatten
)। (इस चपटेपन के परिणामस्वरूप, ट्रेसिंग के दौरान उपयोग किए जाने वाले की तुलना में एक अलग नेस्टिंग संरचना के साथ एक ठोस फ़ंक्शन को कॉल करने के परिणामस्वरूप एक TypeError होगा)। - अन्य सभी पायथन प्रकारों के लिए कुंजी वस्तु के लिए अद्वितीय है। इस तरह एक फ़ंक्शन या विधि को प्रत्येक उदाहरण के लिए स्वतंत्र रूप से पता लगाया जाता है जिसे इसे कहा जाता है।
रिट्रेसिंग को नियंत्रित करना
रिट्रेसिंग, जो तब होता है जब आपका Function
एक से अधिक ट्रेस बनाता है, यह सुनिश्चित करने में मदद करता है कि TensorFlow इनपुट के प्रत्येक सेट के लिए सही ग्राफ़ उत्पन्न करता है। हालाँकि, ट्रेसिंग एक महंगा ऑपरेशन है! यदि आपका Function
प्रत्येक कॉल के लिए एक नया ग्राफ़ पुनः ट्रेस करता है, तो आप पाएंगे कि आपका कोड tf.function
का उपयोग न करने की तुलना में अधिक धीरे-धीरे निष्पादित होता है।
अनुरेखण व्यवहार को नियंत्रित करने के लिए, आप निम्न तकनीकों का उपयोग कर सकते हैं:
- ट्रेसिंग को सीमित करने के लिए
input_signature
मेंtf.function
निर्दिष्ट करें।
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def next_collatz(x):
print("Tracing with", x)
return tf.where(x % 2 == 0, x // 2, 3 * x + 1)
print(next_collatz(tf.constant([1, 2])))
# You specified a 1-D tensor in the input signature, so this should fail.
with assert_raises(ValueError):
next_collatz(tf.constant([[1, 2], [3, 4]]))
# You specified an int32 dtype in the input signature, so this should fail.
with assert_raises(ValueError):
next_collatz(tf.constant([1.0, 2.0]))
Tracing with Tensor("x:0", shape=(None,), dtype=int32) tf.Tensor([4 1], shape=(2,), dtype=int32) Caught expected exception <class 'ValueError'>: Caught expected exception <class 'ValueError'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/1851403433.py", line 9, in <module> next_collatz(tf.constant([[1, 2], [3, 4]])) ValueError: Python inputs incompatible with input_signature: inputs: ( tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32)) input_signature: ( TensorSpec(shape=(None,), dtype=tf.int32, name=None)). Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/1851403433.py", line 13, in <module> next_collatz(tf.constant([1.0, 2.0])) ValueError: Python inputs incompatible with input_signature: inputs: ( tf.Tensor([1. 2.], shape=(2,), dtype=float32)) input_signature: ( TensorSpec(shape=(None,), dtype=tf.int32, name=None)).
ट्रेस पुन: उपयोग में लचीलेपन की अनुमति देने के लिए
tf.TensorSpec
में एक [कोई नहीं] आयाम निर्दिष्ट करें।चूंकि TensorFlow अपने आकार के आधार पर टेंसर से मेल खाता है, वाइल्डकार्ड के रूप में
None
आयाम का उपयोग करने सेFunction
s को भिन्न-भिन्न आकार के इनपुट के लिए ट्रेस का पुन: उपयोग करने की अनुमति मिल जाएगी। यदि आपके पास अलग-अलग लंबाई के अनुक्रम हैं, या प्रत्येक बैच के लिए अलग-अलग आकार की छवियां हैं, तो भिन्न-आकार का इनपुट हो सकता है (उदाहरण के लिए ट्रांसफॉर्मर और डीप ड्रीम ट्यूटोरियल देखें)।
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def g(x):
print('Tracing with', x)
return x
# No retrace!
print(g(tf.constant([1, 2, 3])))
print(g(tf.constant([1, 2, 3, 4, 5])))
Tracing with Tensor("x:0", shape=(None,), dtype=int32) tf.Tensor([1 2 3], shape=(3,), dtype=int32) tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
रिट्रेसिंग को कम करने के लिए टेन्सर्स को पायथन तर्क कास्ट करें।
अक्सर, हाइपरपैरामीटर और ग्राफ़ निर्माण को नियंत्रित करने के लिए पायथन तर्कों का उपयोग किया जाता है - उदाहरण के लिए,
num_layers=10
याtraining=True
याnonlinearity='relu'
। इसलिए, यदि पायथन तर्क बदलता है, तो यह समझ में आता है कि आपको ग्राफ़ को फिर से ट्रेस करना होगा।हालांकि, यह संभव है कि ग्राफ़ निर्माण को नियंत्रित करने के लिए पायथन तर्क का उपयोग नहीं किया जा रहा हो। इन मामलों में, पायथन मूल्य में बदलाव अनावश्यक रीट्रेसिंग को ट्रिगर कर सकता है। उदाहरण के लिए, इस प्रशिक्षण लूप को लें, जिसे ऑटोग्राफ गतिशील रूप से अनियंत्रित करेगा। कई निशानों के बावजूद, उत्पन्न ग्राफ़ वास्तव में समान है, इसलिए पुन: अनुरेखण अनावश्यक है।
def train_one_step():
pass
@tf.function
def train(num_steps):
print("Tracing with num_steps = ", num_steps)
tf.print("Executing with num_steps = ", num_steps)
for _ in tf.range(num_steps):
train_one_step()
print("Retracing occurs for different Python arguments.")
train(num_steps=10)
train(num_steps=20)
print()
print("Traces are reused for Tensor arguments.")
train(num_steps=tf.constant(10))
train(num_steps=tf.constant(20))
Retracing occurs for different Python arguments. Tracing with num_steps = 10 Executing with num_steps = 10 Tracing with num_steps = 20 Executing with num_steps = 20 Traces are reused for Tensor arguments. Tracing with num_steps = Tensor("num_steps:0", shape=(), dtype=int32) Executing with num_steps = 10 Executing with num_steps = 20प्लेसहोल्डर22
यदि आपको रीट्रेसिंग को बाध्य करने की आवश्यकता है, तो एक नया Function
बनाएं। अलग-अलग Function
ऑब्जेक्ट्स की गारंटी है कि वे निशान साझा न करें।
def f():
print('Tracing!')
tf.print('Executing')
tf.function(f)()
tf.function(f)()
Tracing! Executing Tracing! Executing
ठोस कार्य प्राप्त करना
हर बार जब किसी फ़ंक्शन का पता लगाया जाता है, तो एक नया ठोस फ़ंक्शन बनाया जाता है। आप get_concrete_function
का उपयोग करके सीधे एक ठोस फ़ंक्शन प्राप्त कर सकते हैं।
print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.constant("a"))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))
Obtaining concrete trace Executing traced function tf.Tensor(b'aa', shape=(), dtype=string) tf.Tensor(b'bb', shape=(), dtype=string)
# You can also call get_concrete_function on an InputSpec
double_strings_from_inputspec = double.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.string))
print(double_strings_from_inputspec(tf.constant("c")))
tf.Tensor(b'cc', shape=(), dtype=string)
ConcreteFunction
को प्रिंट करना इसके इनपुट तर्कों (प्रकारों के साथ) और इसके आउटपुट प्रकार का सारांश प्रदर्शित करता है।
print(double_strings)
ConcreteFunction double(a) Args: a: string Tensor, shape=() Returns: string Tensor, shape=()
आप सीधे एक ठोस फ़ंक्शन के हस्ताक्षर को पुनः प्राप्त कर सकते हैं।
print(double_strings.structured_input_signature)
print(double_strings.structured_outputs)
((TensorSpec(shape=(), dtype=tf.string, name='a'),), {}) Tensor("Identity:0", shape=(), dtype=string)
असंगत प्रकारों के साथ एक ठोस ट्रेस का उपयोग करने से त्रुटि होगी
with assert_raises(tf.errors.InvalidArgumentError):
double_strings(tf.constant(1))
Caught expected exception <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/3196284684.py", line 2, in <module> double_strings(tf.constant(1)) tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_162 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_162]
आप देख सकते हैं कि एक ठोस फ़ंक्शन के इनपुट हस्ताक्षर में पायथन तर्कों को विशेष उपचार दिया जाता है। TensorFlow 2.3 से पहले, Python तर्कों को केवल ठोस फ़ंक्शन के हस्ताक्षर से हटा दिया गया था। TensorFlow 2.3 से शुरू होकर, पायथन तर्क हस्ताक्षर में बने रहते हैं, लेकिन ट्रेसिंग के दौरान निर्धारित मान लेने के लिए विवश हैं।
@tf.function
def pow(a, b):
return a ** b
square = pow.get_concrete_function(a=tf.TensorSpec(None, tf.float32), b=2)
print(square)
ConcreteFunction pow(a, b=2) Args: a: float32 Tensor, shape=<unknown> Returns: float32 Tensor, shape=<unknown>
assert square(tf.constant(10.0)) == 100
with assert_raises(TypeError):
square(tf.constant(10.0), b=3)
Caught expected exception <class 'TypeError'>: Traceback (most recent call last): File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1721, in _call_impl cancellation_manager) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1765, in _call_with_flat_signature raise TypeError(f"{self._flat_signature_summary()} got unexpected " TypeError: pow(a) got unexpected keyword arguments: b. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/2310937119.py", line 4, in <module> square(tf.constant(10.0), b=3) TypeError: ConcreteFunction pow(a, b) was constructed with int value 2 in b, but was called with int value 3.
ग्राफ प्राप्त करना
प्रत्येक ठोस कार्य tf.Graph
के चारों ओर एक कॉल करने योग्य आवरण है। हालांकि वास्तविक tf.Graph
ऑब्जेक्ट को पुनर्प्राप्त करना ऐसा कुछ नहीं है जिसे आपको सामान्य रूप से करने की आवश्यकता होगी, आप इसे किसी भी ठोस फ़ंक्शन से आसानी से प्राप्त कर सकते हैं।
graph = double_strings.graph
for node in graph.as_graph_def().node:
print(f'{node.input} -> {node.name}')
[] -> a ['a', 'a'] -> add ['add'] -> Identity
डिबगिंग
सामान्य तौर पर, डिबगिंग कोड tf.function
के अंदर की तुलना में उत्सुक मोड में आसान होता है। आपको यह सुनिश्चित करना चाहिए कि tf.function
के साथ सजाने से पहले आपका कोड उत्सुक मोड में त्रुटि-मुक्त निष्पादित करता है। डिबगिंग प्रक्रिया में सहायता के लिए, आप tf.config.run_functions_eagerly(True)
को वैश्विक रूप से अक्षम और tf.function
को पुन: सक्षम करने के लिए कॉल कर सकते हैं।
केवल tf.function
में दिखाई देने वाली समस्याओं को ट्रैक करते समय, यहां कुछ युक्तियां दी गई हैं:
- सादे पुराने पायथन
print
कॉल केवल ट्रेसिंग के दौरान निष्पादित होते हैं, जब आपके फ़ंक्शन का पता लगाया जाता है, तो आपको ट्रैक करने में मदद मिलती है। -
tf.print
कॉल हर बार निष्पादित होंगे, और निष्पादन के दौरान मध्यवर्ती मानों को ट्रैक करने में आपकी सहायता कर सकते हैं। -
tf.debugging.enable_check_numerics
यह पता लगाने का एक आसान तरीका है कि NaN और Inf कहाँ बनाए गए हैं। -
pdb
( पायथन डीबगर ) आपको यह समझने में मदद कर सकता है कि ट्रेसिंग के दौरान क्या हो रहा है। (चेतावनी:pdb
आपको ऑटोग्राफ-रूपांतरित स्रोत कोड में छोड़ देगा।)
ऑटोग्राफ रूपांतरण
ऑटोग्राफ एक पुस्तकालय है जो डिफ़ॉल्ट रूप से tf.function
में चालू होता है, और पायथन उत्सुक कोड के एक सबसेट को ग्राफ-संगत TensorFlow ops में बदल देता है। इसमें नियंत्रण प्रवाह शामिल है जैसे if
, for
, while
।
TensorFlow ops जैसे tf.cond
और tf.while_loop
काम करना जारी रखते हैं, लेकिन नियंत्रण प्रवाह अक्सर पायथन में लिखे जाने पर लिखना और समझना आसान होता है।
# A simple loop
@tf.function
def f(x):
while tf.reduce_sum(x) > 1:
tf.print(x)
x = tf.tanh(x)
return x
f(tf.random.uniform([5]))
[0.666458249 0.713946581 0.723879576 0.330758929 0.184087753] [0.582645297 0.613145649 0.619306684 0.319202513 0.182036072] [0.524585426 0.546337605 0.550645113 0.308785647 0.18005164] [0.481231302 0.497770309 0.501003504 0.299331933 0.178130865] [0.447229207 0.460361809 0.462906033 0.290701121 0.176270396] [0.419618756 0.430379033 0.432449728 0.282779962 0.174467146] [0.396609187 0.405638 0.407366514 0.275476 0.172718227] [0.377043903 0.384762734 0.386234313 0.268712848 0.17102097] [0.360137492 0.366836458 0.368109286 0.262426734 0.169372901] [0.345335096 0.351221472 0.352336824 0.256563932 0.167771652] [0.332231969 0.337458342 0.338446289 0.251078814 0.166215062] [0.320524871 0.325206399 0.326089561 0.24593246 0.164701089] [0.309981436 0.314206958 0.31500268 0.241091311 0.163227797] [0.300420195 0.304259449 0.304981351 0.236526251 0.161793426] [0.291697085 0.295205742 0.295864582 0.232211992 0.160396278] [0.283696055 0.286919087 0.287523568 0.228126258 0.159034774] [0.276322395 0.279296666 0.27985391 0.224249557 0.157707423] [0.269497961 0.272254 0.272769839 0.220564634 0.15641281] [0.263157606 0.265720904 0.266200244 0.21705614 0.155149609] [0.257246554 0.259638608 0.260085613 0.213710397 0.153916568] [0.251718313 0.25395745 0.254375577 0.210515186 0.152712509] [0.246533215 0.248635098 0.249027327 0.207459539 0.151536316] [0.241657034 0.243635193 0.244004101 0.204533577 0.15038693] [0.237060249 0.238926381 0.239274174 0.201728329 0.149263337] [0.232717097 0.234481394 0.234810054 0.199035719 0.148164615] [0.228605017 0.230276451 0.230587661 0.196448416 0.147089839] [0.224704206 0.226290658 0.22658591 0.193959698 0.14603813] [0.220997125 0.222505584 0.222786173 0.191563457 0.145008713] <tf.Tensor: shape=(5,), dtype=float32, numpy= array([0.21746822, 0.21890487, 0.21917202, 0.18925412, 0.14400077], dtype=float32)>
यदि आप उत्सुक हैं तो आप कोड ऑटोग्राफ जेनरेट का निरीक्षण कर सकते हैं।
print(tf.autograph.to_code(f.python_function))
def tf__f(x): with ag__.FunctionScope('f', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope: do_return = False retval_ = ag__.UndefinedReturnValue() def get_state(): return (x,) def set_state(vars_): nonlocal x (x,) = vars_ def loop_body(): nonlocal x ag__.converted_call(ag__.ld(tf).print, (ag__.ld(x),), None, fscope) x = ag__.converted_call(ag__.ld(tf).tanh, (ag__.ld(x),), None, fscope) def loop_test(): return (ag__.converted_call(ag__.ld(tf).reduce_sum, (ag__.ld(x),), None, fscope) > 1) ag__.while_stmt(loop_test, loop_body, get_state, set_state, ('x',), {}) try: do_return = True retval_ = ag__.ld(x) except: do_return = False raise return fscope.ret(retval_, do_return)
सशर्त,
AutoGraph कुछ if <condition>
कथनों को समतुल्य tf.cond
कॉल में रूपांतरित कर देगा। यह प्रतिस्थापन तब किया जाता है जब <condition>
एक टेंसर हो। अन्यथा, if
कथन को पायथन सशर्त के रूप में निष्पादित किया जाता है।
एक पायथन सशर्त अनुरेखण के दौरान निष्पादित होता है, इसलिए सशर्त की एक शाखा को ग्राफ में जोड़ा जाएगा। ऑटोग्राफ के बिना, डेटा-निर्भर नियंत्रण प्रवाह होने पर यह ट्रेस किया गया ग्राफ वैकल्पिक शाखा लेने में असमर्थ होगा।
tf.cond
में सशर्त की दोनों शाखाओं का पता लगाता है और जोड़ता है, गतिशील रूप से निष्पादन समय पर एक शाखा का चयन करता है। अनुरेखण के अनपेक्षित दुष्प्रभाव हो सकते हैं; अधिक जानकारी के लिए ऑटोग्राफ ट्रेसिंग प्रभाव देखें।
@tf.function
def fizzbuzz(n):
for i in tf.range(1, n + 1):
print('Tracing for loop')
if i % 15 == 0:
print('Tracing fizzbuzz branch')
tf.print('fizzbuzz')
elif i % 3 == 0:
print('Tracing fizz branch')
tf.print('fizz')
elif i % 5 == 0:
print('Tracing buzz branch')
tf.print('buzz')
else:
print('Tracing default branch')
tf.print(i)
fizzbuzz(tf.constant(5))
fizzbuzz(tf.constant(20))
Tracing for loop Tracing fizzbuzz branch Tracing fizz branch Tracing buzz branch Tracing default branch 1 2 fizz 4 buzz 1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz
ऑटोग्राफ-कन्वर्टेड इफ स्टेटमेंट पर अतिरिक्त प्रतिबंधों के लिए संदर्भ दस्तावेज देखें।
छोरों
AutoGraph कुछ for
और while
स्टेटमेंट को समतुल्य TensorFlow लूपिंग ऑप्स में बदल देगा, जैसे tf. tf.while_loop
। यदि परिवर्तित नहीं किया जाता है, तो लूप के for
या while
लूप को पायथन लूप के रूप में निष्पादित किया जाता है।
यह प्रतिस्थापन निम्नलिखित स्थितियों में किया जाता है:
-
for x in y
: यदिy
एक टेंसर है, तो tf.tf.while_loop
में कनवर्ट करें। विशेष मामले में जहांy
एकtf.data.Dataset
है,tf.data.Dataset
ops का एक संयोजन उत्पन्न होता है। -
while <condition>
: यदि<condition>
एक टेंसर है, तो tf.tf.while_loop
में कनवर्ट करें।
एक पायथन लूप ट्रेसिंग के दौरान निष्पादित होता है, लूप के प्रत्येक पुनरावृत्ति के लिए tf.Graph
में अतिरिक्त ऑप्स जोड़ता है।
एक TensorFlow लूप लूप के शरीर का पता लगाता है, और गतिशील रूप से चयन करता है कि निष्पादन समय पर कितने पुनरावृत्तियों को चलाना है। लूप बॉडी केवल एक बार उत्पन्न tf.Graph
में दिखाई देती है।
ऑटोग्राफ-कन्वर्टेड for
एंड while
स्टेटमेंट्स पर अतिरिक्त प्रतिबंधों के लिए संदर्भ दस्तावेज देखें।
पायथन डेटा पर लूपिंग
tf.function
के भीतर Python/NumPy डेटा पर लूप करना एक आम समस्या है। यह लूप ट्रेसिंग प्रक्रिया के दौरान निष्पादित होगा, लूप के प्रत्येक पुनरावृत्ति के लिए आपके मॉडल की एक प्रति tf.Graph
में जोड़ देगा।
यदि आप संपूर्ण प्रशिक्षण लूप को tf.function
में लपेटना चाहते हैं, तो ऐसा करने का सबसे सुरक्षित तरीका है कि आप अपने डेटा को tf.data.Dataset
के रूप में लपेटें ताकि ऑटोग्राफ गतिशील रूप से प्रशिक्षण लूप को अनियंत्रित कर सके।
def measure_graph_size(f, *args):
g = f.get_concrete_function(*args).graph
print("{}({}) contains {} nodes in its graph".format(
f.__name__, ', '.join(map(str, args)), len(g.as_graph_def().node)))
@tf.function
def train(dataset):
loss = tf.constant(0)
for x, y in dataset:
loss += tf.abs(y - x) # Some dummy computation.
return loss
small_data = [(1, 1)] * 3
big_data = [(1, 1)] * 10
measure_graph_size(train, small_data)
measure_graph_size(train, big_data)
measure_graph_size(train, tf.data.Dataset.from_generator(
lambda: small_data, (tf.int32, tf.int32)))
measure_graph_size(train, tf.data.Dataset.from_generator(
lambda: big_data, (tf.int32, tf.int32)))
train([(1, 1), (1, 1), (1, 1)]) contains 11 nodes in its graph train([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) contains 32 nodes in its graph train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph
डेटासेट में Python/NumPy डेटा लपेटते समय, tf.data.Dataset.from_generator
बनाम tf.data.Dataset.from_tensors
से सावधान रहें। पहला डेटा को पायथन में रखेगा और इसे tf.py_function
के माध्यम से लाएगा जिसमें प्रदर्शन प्रभाव हो सकते हैं, जबकि बाद वाला डेटा की एक प्रति को ग्राफ़ में एक बड़े tf.constant()
नोड के रूप में बंडल करेगा, जिसमें स्मृति प्रभाव हो सकते हैं।
TFRecordDataset
, CsvDataset
, आदि के माध्यम से फ़ाइलों से डेटा पढ़ना डेटा का उपभोग करने का सबसे प्रभावी तरीका है, क्योंकि तब TensorFlow स्वयं पायथन को शामिल किए बिना डेटा के एसिंक्रोनस लोडिंग और प्रीफ़ेचिंग का प्रबंधन कर सकता है। अधिक जानने के लिए, tf.data
देखें: TensorFlow इनपुट पाइपलाइन गाइड बनाएं।
लूप में मान जमा करना
एक सामान्य पैटर्न लूप से मध्यवर्ती मूल्यों को जमा करना है। आम तौर पर, यह एक पायथन सूची में जोड़कर या एक पायथन शब्दकोश में प्रविष्टियां जोड़कर पूरा किया जाता है। हालांकि, चूंकि ये पायथन साइड इफेक्ट हैं, वे गतिशील रूप से अनियंत्रित लूप में अपेक्षा के अनुरूप काम नहीं करेंगे। गतिशील रूप से अनियंत्रित लूप से परिणाम संचित करने के लिए tf.TensorArray
का उपयोग करें।
batch_size = 2
seq_len = 3
feature_size = 4
def rnn_step(inp, state):
return inp + state
@tf.function
def dynamic_rnn(rnn_step, input_data, initial_state):
# [batch, time, features] -> [time, batch, features]
input_data = tf.transpose(input_data, [1, 0, 2])
max_seq_len = input_data.shape[0]
states = tf.TensorArray(tf.float32, size=max_seq_len)
state = initial_state
for i in tf.range(max_seq_len):
state = rnn_step(input_data[i], state)
states = states.write(i, state)
return tf.transpose(states.stack(), [1, 0, 2])
dynamic_rnn(rnn_step,
tf.random.uniform([batch_size, seq_len, feature_size]),
tf.zeros([batch_size, feature_size]))
<tf.Tensor: shape=(2, 3, 4), dtype=float32, numpy= array([[[0.06309307, 0.9938811 , 0.90789986, 0.42136216], [0.44997275, 1.9107027 , 1.0716251 , 0.717237 ], [0.6026064 , 2.1622117 , 1.4164022 , 1.4153863 ]], [[0.04946005, 0.69127274, 0.56848884, 0.22406638], [0.8148316 , 1.0278493 , 0.6207781 , 1.1935129 ], [0.9178308 , 1.320889 , 0.989761 , 2.0120025 ]]], dtype=float32)>
सीमाओं
TensorFlow Function
की डिज़ाइन द्वारा कुछ सीमाएँ हैं जिन्हें आपको Python फ़ंक्शन को Function
में कनवर्ट करते समय पता होना चाहिए।
पायथन साइड इफेक्ट निष्पादित करना
साइड इफेक्ट, जैसे प्रिंटिंग, सूचियों में शामिल होना, और ग्लोबल्स को बदलना, Function
के अंदर अप्रत्याशित रूप से व्यवहार कर सकते हैं, कभी-कभी दो बार या सभी को निष्पादित नहीं करते हैं। वे केवल पहली बार होते हैं जब आप इनपुट के एक सेट के साथ Function
को कॉल करते हैं। बाद में, ट्रेस किए गए tf.Graph
को फिर से निष्पादित किया जाता है, बिना पायथन कोड को निष्पादित किए।
अंगूठे का सामान्य नियम अपने तर्क में पायथन के दुष्प्रभावों पर भरोसा करने से बचना है और केवल अपने निशान को डीबग करने के लिए उनका उपयोग करना है। अन्यथा, TensorFlow API जैसे tf.data
, tf.print
, tf.summary
, tf.Variable.assign
, और tf.TensorArray
यह सुनिश्चित करने का सबसे अच्छा तरीका है कि आपका कोड प्रत्येक कॉल के साथ TensorFlow रनटाइम द्वारा निष्पादित किया जाएगा।
@tf.function
def f(x):
print("Traced with", x)
tf.print("Executed with", x)
f(1)
f(1)
f(2)
Traced with 1 Executed with 1 Executed with 1 Traced with 2 Executed with 2
यदि आप किसी Function
के प्रत्येक आह्वान के दौरान पायथन कोड निष्पादित करना चाहते हैं, tf.py_function
एक निकास हैच है। tf.py_function
की कमी यह है कि यह पोर्टेबल या विशेष रूप से निष्पादक नहीं है, सहेजे गए मॉडल के साथ सहेजा नहीं जा सकता है, और वितरित (मल्टी-जीपीयू, टीपीयू) सेटअप में अच्छी तरह से काम नहीं करता है। साथ ही, चूंकि tf.py_function
को ग्राफ़ में तार-तार करना होता है, यह सभी इनपुट/आउटपुट को टेंसर में डाल देता है।
पायथन वैश्विक और मुक्त चर बदलना
पायथन ग्लोबल और फ्री वेरिएबल्स को बदलना एक पायथन साइड इफेक्ट के रूप में गिना जाता है, इसलिए यह केवल ट्रेसिंग के दौरान होता है।
external_list = []
@tf.function
def side_effect(x):
print('Python side effect')
external_list.append(x)
side_effect(1)
side_effect(1)
side_effect(1)
# The list append only happened once!
assert len(external_list) == 1
Python side effect
कभी-कभी अप्रत्याशित व्यवहार नोटिस करना बहुत कठिन होता है। नीचे दिए गए उदाहरण में, counter
का उद्देश्य एक चर के वेतन वृद्धि की रक्षा करना है। हालाँकि, क्योंकि यह एक अजगर पूर्णांक है और TensorFlow ऑब्जेक्ट नहीं है, इसका मान पहले ट्रेस के दौरान कैप्चर किया जाता है। जब tf.function
का उपयोग किया जाता है, तो अंतर्निहित ग्राफ़ में assign_add
बिना शर्त के रिकॉर्ड किया जाएगा। इसलिए हर बार tf.function
को कॉल करने पर v
1 से बढ़ जाएगा। यह समस्या उन उपयोगकर्ताओं के बीच आम है जो tf.function
डेकोरेटर्स का उपयोग करके अपने Grpah-mode Tensorflow कोड को Tensorflow 2 में माइग्रेट करने का प्रयास करते हैं, जब पायथन साइड-इफेक्ट्स (उदाहरण में counter
) का उपयोग यह निर्धारित करने के लिए किया जाता है कि कौन से ऑप्स को चलाना है (उदाहरण में assign_add
) ) आमतौर पर, उपयोगकर्ताओं को इसका एहसास केवल संदिग्ध संख्यात्मक परिणाम, या अपेक्षा से काफी कम प्रदर्शन देखने के बाद होता है (उदाहरण के लिए यदि संरक्षित ऑपरेशन बहुत महंगा है)।
class Model(tf.Module):
def __init__(self):
self.v = tf.Variable(0)
self.counter = 0
@tf.function
def __call__(self):
if self.counter == 0:
# A python side-effect
self.counter += 1
self.v.assign_add(1)
return self.v
m = Model()
for n in range(3):
print(m().numpy()) # prints 1, 2, 3
1 2 3
अपेक्षित व्यवहार को प्राप्त करने के लिए एक वैकल्पिक हल tf.init_scope
का उपयोग करके फंक्शन ग्राफ के बाहर के कार्यों को उठाना है। यह सुनिश्चित करता है कि चर वृद्धि केवल एक बार ट्रेसिंग समय के दौरान की जाती है। यह ध्यान दिया जाना चाहिए कि init_scope
के अन्य दुष्प्रभाव हैं जिनमें क्लियर कंट्रोल फ्लो और ग्रेडिएंट टेप शामिल हैं। कभी-कभी वास्तविक प्रबंधन के लिए init_scope
का उपयोग बहुत जटिल हो सकता है।
class Model(tf.Module):
def __init__(self):
self.v = tf.Variable(0)
self.counter = 0
@tf.function
def __call__(self):
if self.counter == 0:
# Lifts ops out of function-building graphs
with tf.init_scope():
self.counter += 1
self.v.assign_add(1)
return self.v
m = Model()
for n in range(3):
print(m().numpy()) # prints 1, 1, 1
1 1 1
संक्षेप में, अंगूठे के एक नियम के रूप में, आपको Function
के बाहर रहने वाली सूचियों जैसे पूर्णांक या कंटेनर जैसी पायथन वस्तुओं को बदलने से बचना चाहिए। इसके बजाय, तर्कों और TF ऑब्जेक्ट्स का उपयोग करें। उदाहरण के लिए, "लूप में मान जमा करना" अनुभाग में एक उदाहरण है कि सूची-जैसी संचालन कैसे कार्यान्वित किया जा सकता है।
यदि आप tf.Variable
है, तो आप कुछ मामलों में स्थिति को कैप्चर और हेरफेर कर सकते हैं। इस प्रकार केरस मॉडल के वजन को उसी ConcreteFunction
में बार-बार कॉल के साथ अपडेट किया जाता है।
पायथन इटरेटर और जनरेटर का उपयोग करना
कई पायथन विशेषताएं, जैसे जनरेटर और इटरेटर, राज्य का ट्रैक रखने के लिए पायथन रनटाइम पर भरोसा करते हैं। सामान्य तौर पर, जबकि ये निर्माण उत्सुक मोड में अपेक्षित रूप से काम करते हैं, वे पायथन साइड इफेक्ट्स के उदाहरण हैं और इसलिए केवल ट्रेसिंग के दौरान होते हैं।
@tf.function
def buggy_consume_next(iterator):
tf.print("Value:", next(iterator))
iterator = iter([1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Value: 1 Value: 1 Value: 1
ठीक वैसे ही जैसे TensorFlow में सूची निर्माण के लिए एक विशेष tf.TensorArray
है, इसके पास पुनरावृत्ति निर्माण के लिए एक विशेष tf.data.Iterator
है। एक सिंहावलोकन के लिए ऑटोग्राफ रूपांतरण पर अनुभाग देखें। साथ ही, tf.data
API जनरेटर पैटर्न को लागू करने में मदद कर सकता है:
@tf.function
def good_consume_next(iterator):
# This is ok, iterator is a tf.data.Iterator
tf.print("Value:", next(iterator))
ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
iterator = iter(ds)
good_consume_next(iterator)
good_consume_next(iterator)
good_consume_next(iterator)
Value: 1 Value: 2 Value: 3
tf.function के सभी आउटपुट रिटर्न वैल्यू होने चाहिए
tf.Variable
s के अपवाद के साथ, एक tf.function को अपने सभी आउटपुट वापस करने होंगे। रिटर्न वैल्यू से गुजरे बिना किसी फ़ंक्शन से किसी भी टेंसर को सीधे एक्सेस करने का प्रयास "लीक" का कारण बनता है।
उदाहरण के लिए, पायथन ग्लोबल x
के माध्यम से टेंसर a
को "लीक" करने वाला फ़ंक्शन:
x = None
@tf.function
def leaky_function(a):
global x
x = a + 1 # Bad - leaks local tensor
return a + 2
correct_a = leaky_function(tf.constant(1))
print(correct_a.numpy()) # Good - value obtained from function's returns
try:
x.numpy() # Bad - tensor leaked from inside the function, cannot be used here
except AttributeError as expected:
print(expected)
3 'Tensor' object has no attribute 'numpy'
यह सच है भले ही लीक किया गया मान भी लौटाया गया हो:
@tf.function
def leaky_function(a):
global x
x = a + 1 # Bad - leaks local tensor
return x # Good - uses local tensor
correct_a = leaky_function(tf.constant(1))
print(correct_a.numpy()) # Good - value obtained from function's returns
try:
x.numpy() # Bad - tensor leaked from inside the function, cannot be used here
except AttributeError as expected:
print(expected)
@tf.function
def captures_leaked_tensor(b):
b += x # Bad - `x` is leaked from `leaky_function`
return b
with assert_raises(TypeError):
captures_leaked_tensor(tf.constant(2))
2 'Tensor' object has no attribute 'numpy' Caught expected exception <class 'TypeError'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/566849597.py", line 21, in <module> captures_leaked_tensor(tf.constant(2)) TypeError: Originated from a graph execution error. The graph execution error is detected at a node built at (most recent call last): >>> File /usr/lib/python3.7/runpy.py, line 193, in _run_module_as_main >>> File /usr/lib/python3.7/runpy.py, line 85, in _run_code >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py, line 16, in <module> >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/traitlets/config/application.py, line 846, in launch_instance >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelapp.py, line 677, in start >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tornado/platform/asyncio.py, line 199, in start >>> File /usr/lib/python3.7/asyncio/base_events.py, line 534, in run_forever >>> File /usr/lib/python3.7/asyncio/base_events.py, line 1771, in _run_once >>> File /usr/lib/python3.7/asyncio/events.py, line 88, in _run >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 457, in dispatch_queue >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 446, in process_one >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 353, in dispatch_shell >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 648, in execute_request >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/ipkernel.py, line 353, in do_execute >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/zmqshell.py, line 533, in run_cell >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 2902, in run_cell >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 2947, in _run_cell >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/async_helpers.py, line 68, in _pseudo_sync_runner >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3173, in run_cell_async >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3364, in run_ast_nodes >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3444, in run_code >>> File /tmp/ipykernel_26244/566849597.py, line 7, in <module> >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 910, in __call__ >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 958, in _call >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 781, in _initialize >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3157, in _get_concrete_function_internal_garbage_collected >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3557, in _maybe_define_function >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3402, in _create_graph_function >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 1143, in func_graph_from_py_func >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 672, in wrapped_fn >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 1125, in autograph_handler >>> File /tmp/ipykernel_26244/566849597.py, line 4, in leaky_function >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py, line 1383, in binary_op_wrapper >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py, line 1096, in op_dispatch_handler >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py, line 1737, in _add_dispatch >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/gen_math_ops.py, line 476, in add_v2 >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/op_def_library.py, line 746, in _apply_op_helper >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 691, in _create_op_internal >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/ops.py, line 3705, in _create_op_internal >>> File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/ops.py, line 2101, in __init__ Error detected in node 'add' defined at: File "/tmp/ipykernel_26244/566849597.py", line 4, in leaky_function TypeError: tf.Graph captured an external symbolic tensor. The symbolic tensor 'add:0' created by node 'add' is captured by the tf.Graph being executed as an input. But a tf.Graph is not allowed to take symbolic tensors from another graph as its inputs. Make sure all captured inputs of the executing tf.Graph are not symbolic tensors. Use return values, explicit Python locals or TensorFlow collections to access it. Please see https://www.tensorflow.org/guide/function#all_outputs_of_a_tffunction_must_be_return_values for more information.
आमतौर पर, इस तरह के लीक तब होते हैं जब आप पायथन स्टेटमेंट या डेटा स्ट्रक्चर का उपयोग करते हैं। अप्राप्य टेंसरों को लीक करने के अलावा, इस तरह के बयान भी गलत होने की संभावना है क्योंकि वे पायथन साइड इफेक्ट के रूप में गिने जाते हैं, और प्रत्येक फ़ंक्शन कॉल पर निष्पादित होने की गारंटी नहीं है।
स्थानीय टेंसर को लीक करने के सामान्य तरीकों में बाहरी पायथन संग्रह या किसी वस्तु को बदलना भी शामिल है:
class MyClass:
def __init__(self):
self.field = None
external_list = []
external_object = MyClass()
def leaky_function():
a = tf.constant(1)
external_list.append(a) # Bad - leaks tensor
external_object.field = a # Bad - leaks tensor
पुनरावर्ती tf.functions समर्थित नहीं हैं
रिकर्सिव Function
s समर्थित नहीं हैं और अनंत लूप का कारण बन सकते हैं। उदाहरण के लिए,
@tf.function
def recursive_fn(n):
if n > 0:
return recursive_fn(n - 1)
else:
return 1
with assert_raises(Exception):
recursive_fn(tf.constant(5)) # Bad - maximum recursion error.
Caught expected exception <class 'Exception'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/2233998312.py", line 9, in <module> recursive_fn(tf.constant(5)) # Bad - maximum recursion error. tensorflow.python.autograph.impl.api.StagingError: in user code: File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn * return recursive_fn(n - 1) File "/tmp/ipykernel_26244/2233998312.py", line 3, in recursive_fn * if n > 0: File "/usr/lib/python3.7/abc.py", line 139, in __instancecheck__ return _abc_instancecheck(cls, instance) RecursionError: maximum recursion depth exceeded while calling a Python object
यहां तक कि अगर एक पुनरावर्ती Function
काम करता प्रतीत होता है, तो भी पायथन फ़ंक्शन का कई बार पता लगाया जाएगा और इसका प्रदर्शन प्रभाव हो सकता है। उदाहरण के लिए,
@tf.function
def recursive_fn(n):
if n > 0:
print('tracing')
return recursive_fn(n - 1)
else:
return 1
recursive_fn(5) # Warning - multiple tracings
tracing tracing tracing tracing tracing <tf.Tensor: shape=(), dtype=int32, numpy=1>
ज्ञात पहलु
यदि आपका Function
सही ढंग से मूल्यांकन नहीं कर रहा है, तो त्रुटि को इन ज्ञात मुद्दों द्वारा समझाया जा सकता है जिन्हें भविष्य में ठीक करने की योजना है।
पायथन वैश्विक और मुक्त चर के आधार पर
पायथन तर्क के नए मान के साथ बुलाए जाने पर फ़ंक्शन एक नया ConcreteFunction
Function
बनाता है। हालांकि, यह ऐसा नहीं करता है कि पाइथन बंद करने, ग्लोबल्स, या उस Function
के गैर-स्थानीय लोगों के लिए। यदि Function
में कॉल के बीच उनका मान बदल जाता है, तो Function
अभी भी उन मानों का उपयोग करेगा जो उनके पास थे जब इसका पता लगाया गया था। यह इस बात से अलग है कि नियमित पायथन कैसे काम करता है।
इस कारण से, आपको एक कार्यात्मक प्रोग्रामिंग शैली का पालन करना चाहिए जो बाहरी नामों को बंद करने के बजाय तर्कों का उपयोग करती है।
@tf.function
def buggy_add():
return 1 + foo
@tf.function
def recommended_add(foo):
return 1 + foo
foo = 1
print("Buggy:", buggy_add())
print("Correct:", recommended_add(foo))
Buggy: tf.Tensor(2, shape=(), dtype=int32) Correct: tf.Tensor(2, shape=(), dtype=int32)
print("Updating the value of `foo` to 100!")
foo = 100
print("Buggy:", buggy_add()) # Did not change!
print("Correct:", recommended_add(foo))
Updating the value of `foo` to 100! Buggy: tf.Tensor(2, shape=(), dtype=int32) Correct: tf.Tensor(101, shape=(), dtype=int32)
वैश्विक मान को अपडेट करने का दूसरा तरीका है, इसे tf.Variable
बनाना और इसके बजाय Variable.assign
पद्धति का उपयोग करना।
@tf.function
def variable_add():
return 1 + foo
foo = tf.Variable(1)
print("Variable:", variable_add())
Variable: tf.Tensor(2, shape=(), dtype=int32)
print("Updating the value of `foo` to 100!")
foo.assign(100)
print("Variable:", variable_add())
Updating the value of `foo` to 100! Variable: tf.Tensor(101, shape=(), dtype=int32)
पायथन वस्तुओं के आधार पर
पायथन ऑब्जेक्ट्स को tf.function
में तर्क के रूप में पारित करने की सिफारिश में कई ज्ञात मुद्दे हैं, जिन्हें भविष्य में तय किए जाने की उम्मीद है। सामान्य तौर पर, आप लगातार ट्रेसिंग पर भरोसा कर सकते हैं यदि आप एक पायथन आदिम या tf.nest
-संगत संरचना का उपयोग तर्क के रूप में करते हैं या किसी ऑब्जेक्ट के किसी भिन्न उदाहरण में Function
में पास करते हैं। हालांकि, जब आप एक ही ऑब्जेक्ट को पास करते हैं और केवल इसकी विशेषताओं को बदलते हैं , तो Function
एक नया ट्रेस नहीं बनाएगा।
class SimpleModel(tf.Module):
def __init__(self):
# These values are *not* tf.Variables.
self.bias = 0.
self.weight = 2.
@tf.function
def evaluate(model, x):
return model.weight * x + model.bias
simple_model = SimpleModel()
x = tf.constant(10.)
print(evaluate(simple_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
simple_model.bias += 5.0
print(evaluate(simple_model, x)) # Didn't change :(
Adding bias! tf.Tensor(20.0, shape=(), dtype=float32)
मॉडल के अपडेटेड इंस्टेंस का मूल्यांकन करने के लिए समान Function
का उपयोग करना छोटा होगा क्योंकि अपडेट किए गए मॉडल में मूल मॉडल के समान कैशे कुंजी है।
इस कारण से, आपको परिवर्तनशील वस्तु विशेषताओं के आधार पर बचने या नई वस्तुओं को बनाने से बचने के लिए अपना Function
लिखने की अनुशंसा की जाती है।
यदि यह संभव नहीं है, तो हर बार जब आप अपने ऑब्जेक्ट को रीट्रेसिंग के लिए संशोधित करते हैं तो नया Function
बनाना एक कामकाज है:
def evaluate(model, x):
return model.weight * x + model.bias
new_model = SimpleModel()
evaluate_no_bias = tf.function(evaluate).get_concrete_function(new_model, x)
# Don't pass in `new_model`, `Function` already captured its state during tracing.
print(evaluate_no_bias(x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
new_model.bias += 5.0
# Create new Function and ConcreteFunction since you modified new_model.
evaluate_with_bias = tf.function(evaluate).get_concrete_function(new_model, x)
print(evaluate_with_bias(x)) # Don't pass in `new_model`.
Adding bias! tf.Tensor(25.0, shape=(), dtype=float32)
चूंकि रिट्रेसिंग महंगा हो सकता है , आप tf.Variable
s को ऑब्जेक्ट एट्रिब्यूट्स के रूप में उपयोग कर सकते हैं, जिसे बिना रिट्रेस की आवश्यकता के समान प्रभाव के लिए उत्परिवर्तित (लेकिन बदला नहीं, सावधान!) किया जा सकता है।
class BetterModel:
def __init__(self):
self.bias = tf.Variable(0.)
self.weight = tf.Variable(2.)
@tf.function
def evaluate(model, x):
return model.weight * x + model.bias
better_model = BetterModel()
print(evaluate(better_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
better_model.bias.assign_add(5.0) # Note: instead of better_model.bias += 5
print(evaluate(better_model, x)) # This works!
Adding bias! tf.Tensor(25.0, shape=(), dtype=float32)
tf.Variables बनाना
Function
केवल सिंगलटन tf.Variable
का समर्थन करता है जिसे पहली कॉल पर एक बार बनाया जाता है, और बाद के फ़ंक्शन कॉल में पुन: उपयोग किया जाता है। नीचे दिया गया कोड स्निपेट प्रत्येक फ़ंक्शन कॉल में एक नया tf.Variable
, जिसके परिणामस्वरूप ValueError
अपवाद होगा।
उदाहरण:
@tf.function
def f(x):
v = tf.Variable(1.0)
return v
with assert_raises(ValueError):
f(1.0)
Caught expected exception <class 'ValueError'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/3018268426.py", line 7, in <module> f(1.0) ValueError: in user code: File "/tmp/ipykernel_26244/3018268426.py", line 3, in f * v = tf.Variable(1.0) ValueError: tf.function only supports singleton tf.Variables created on the first call. Make sure the tf.Variable is only created once or created outside tf.function. See https://www.tensorflow.org/guide/function#creating_tfvariables for more information.
इस सीमा के आसपास काम करने के लिए उपयोग किया जाने वाला एक सामान्य पैटर्न एक पायथन कोई नहीं मूल्य के साथ शुरू करना है, फिर सशर्त रूप से tf.Variable
यदि मान कोई नहीं है:
class Count(tf.Module):
def __init__(self):
self.count = None
@tf.function
def __call__(self):
if self.count is None:
self.count = tf.Variable(0)
return self.count.assign_add(1)
c = Count()
print(c())
print(c())
tf.Tensor(1, shape=(), dtype=int32) tf.Tensor(2, shape=(), dtype=int32)
एकाधिक केरा अनुकूलक के साथ प्रयोग करना
आप ValueError: tf.function only supports singleton tf.Variables created on the first call.
tf.function
के साथ एक से अधिक केरस अनुकूलक का उपयोग करते समय। यह त्रुटि इसलिए होती है क्योंकि ऑप्टिमाइज़र आंतरिक रूप से tf.Variables
बनाते हैं जब वे पहली बार ग्रेडिएंट लागू करते हैं।
opt1 = tf.keras.optimizers.Adam(learning_rate = 1e-2)
opt2 = tf.keras.optimizers.Adam(learning_rate = 1e-3)
@tf.function
def train_step(w, x, y, optimizer):
with tf.GradientTape() as tape:
L = tf.reduce_sum(tf.square(w*x - y))
gradients = tape.gradient(L, [w])
optimizer.apply_gradients(zip(gradients, [w]))
w = tf.Variable(2.)
x = tf.constant([-1.])
y = tf.constant([2.])
train_step(w, x, y, opt1)
print("Calling `train_step` with different optimizer...")
with assert_raises(ValueError):
train_step(w, x, y, opt2)
Calling `train_step` with different optimizer... Caught expected exception <class 'ValueError'>: Traceback (most recent call last): File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises yield File "/tmp/ipykernel_26244/3167358578.py", line 18, in <module> train_step(w, x, y, opt2) ValueError: in user code: File "/tmp/ipykernel_26244/3167358578.py", line 9, in train_step * optimizer.apply_gradients(zip(gradients, [w])) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 639, in apply_gradients ** self._create_all_weights(var_list) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 828, in _create_all_weights _ = self.iterations File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 835, in __getattribute__ return super(OptimizerV2, self).__getattribute__(name) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 995, in iterations aggregation=tf.VariableAggregation.ONLY_FIRST_REPLICA) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 1202, in add_weight aggregation=aggregation) File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer_utils.py", line 129, in make_variable shape=variable_shape if variable_shape else None) ValueError: tf.function only supports singleton tf.Variables created on the first call. Make sure the tf.Variable is only created once or created outside tf.function. See https://www.tensorflow.org/guide/function#creating_tfvariables for more information.
यदि आपको प्रशिक्षण के दौरान ऑप्टिमाइज़र को बदलने की आवश्यकता है, तो प्रत्येक ऑप्टिमाइज़र के लिए एक नया Function
बनाने के लिए एक समाधान है, सीधे ConcreteFunction
को कॉल करना।
opt1 = tf.keras.optimizers.Adam(learning_rate = 1e-2)
opt2 = tf.keras.optimizers.Adam(learning_rate = 1e-3)
# Not a tf.function.
def train_step(w, x, y, optimizer):
with tf.GradientTape() as tape:
L = tf.reduce_sum(tf.square(w*x - y))
gradients = tape.gradient(L, [w])
optimizer.apply_gradients(zip(gradients, [w]))
w = tf.Variable(2.)
x = tf.constant([-1.])
y = tf.constant([2.])
# Make a new Function and ConcreteFunction for each optimizer.
train_step_1 = tf.function(train_step).get_concrete_function(w, x, y, opt1)
train_step_2 = tf.function(train_step).get_concrete_function(w, x, y, opt2)
for i in range(10):
if i % 2 == 0:
train_step_1(w, x, y) # `opt1` is not used as a parameter.
else:
train_step_2(w, x, y) # `opt2` is not used as a parameter.
कई केरस मॉडल के साथ प्रयोग करना
आप ValueError: tf.function only supports singleton tf.Variables created on the first call.
एक ही Function
में विभिन्न मॉडल इंस्टेंस पास करते समय।
यह त्रुटि इसलिए होती है क्योंकि केरस मॉडल (जिनके इनपुट आकार परिभाषित नहीं होते हैं ) और केरस परतें tf.Variables
बनाते हैं जब उन्हें पहली बार बुलाया जाता है। हो सकता है कि आप उन वेरिएबल्स को Function
के अंदर इनिशियलाइज़ करने का प्रयास कर रहे हों, जिन्हें पहले ही कॉल किया जा चुका है। इस त्रुटि से बचने के लिए, मॉडल को प्रशिक्षित करने से पहले सभी भारों को प्रारंभ करने के लिए model.build(input_shape)
को कॉल करने का प्रयास करें।
अग्रिम पठन
किसी Function
को निर्यात और लोड करने के तरीके के बारे में जानने के लिए, सहेजे गए मॉडल मार्गदर्शिका देखें। ट्रेसिंग के बाद किए जाने वाले ग्राफ़ ऑप्टिमाइज़ेशन के बारे में अधिक जानने के लिए, ग्रेप्लर गाइड देखें। अपनी डेटा पाइपलाइन को अनुकूलित करने और अपने मॉडल को प्रोफाइल करने का तरीका जानने के लिए, प्रोफाइलर गाइड देखें।