tf.function के साथ बेहतर प्रदर्शन

TensorFlow.org पर देखें Google Colab में चलाएं GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

TensorFlow 2 में, उत्सुक निष्पादन डिफ़ॉल्ट रूप से चालू होता है। उपयोगकर्ता इंटरफ़ेस सहज और लचीला है (एक बार के संचालन को चलाना बहुत आसान और तेज़ है), लेकिन यह प्रदर्शन और तैनाती की कीमत पर आ सकता है।

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

यह मार्गदर्शिका आपको यह tf.function मदद करेगी कि 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.23302616399996623
Function conv: 0.21780993200013654
Note how there's not much difference in performance for convolutions

अनुरेखण

यह खंड उजागर करता है कि Function हुड के तहत कैसे काम करता है, जिसमें कार्यान्वयन विवरण शामिल हैं जो भविष्य में बदल सकते हैं । हालाँकि, एक बार जब आप समझ जाते हैं कि ट्रेसिंग क्यों और कब होती है, तो tf.function प्रभावी ढंग से उपयोग करना बहुत आसान हो जाता है!

"ट्रेसिंग" क्या है?

एक Function आपके प्रोग्राम को TensorFlow ग्राफ़ में चलाता है। हालांकि, एक tf.Graph उन सभी चीजों का प्रतिनिधित्व नहीं कर सकता है जो आप एक उत्सुक TensorFlow प्रोग्राम में लिखेंगे। उदाहरण के लिए, पायथन बहुरूपता का समर्थन करता है, लेकिन tf.Graph को एक निर्दिष्ट डेटा प्रकार और आयाम के लिए इसके इनपुट की आवश्यकता होती है। या आप कमांड-लाइन तर्क पढ़ने, त्रुटि उठाने, या अधिक जटिल पायथन ऑब्जेक्ट के साथ काम करने जैसे साइड कार्य कर सकते हैं; इनमें से कोई भी चीज tf.Graph में नहीं चल tf.Graph

Function आपके कोड को दो चरणों में अलग करके इस अंतर को पाटता है:

1) पहले चरण में, जिसे " ट्रेसिंग " कहा जाता है, Function एक नया tf.Graph बनाता है। पायथन कोड सामान्य रूप से चलता है, लेकिन सभी TensorFlow संचालन (जैसे दो Tensors जोड़ना) स्थगित कर दिए जाते हैं: वे tf.Graph द्वारा कैप्चर किए 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 एक tf.Graph का कच्चा, भाषा-अज्ञेयवादी, पोर्टेबल प्रतिनिधित्व है।
  • एक ConcreteFunction एक tf.Graph लपेटता है।
  • एक Function ConcreteFunction s के कैश का प्रबंधन करता है और आपके इनपुट के लिए सही को चुनता है।
  • tf.function एक पायथन फ़ंक्शन को लपेटता है, एक Function ऑब्जेक्ट लौटाता है।
  • ट्रेसिंग एक tf.Graph बनाता है और इसे एक ConcreteFunction में लपेटता है, जिसे ट्रेस के रूप में भी जाना जाता है

ट्रेसिंग के नियम Rules

एक Function यह निर्धारित करता है कि किसी इनपुट के args और kwargs से कैश कुंजी की गणना करके किसी ट्रेस किए गए ConcreteFunction का पुन: उपयोग करना है या नहीं। एक कैश कुंजी एक कुंजी है जो निम्नलिखित नियमों (जो बदल सकती है) के अनुसार, Function कॉल के इनपुट आर्ग और क्वार्ग के आधार पर ConcreteFunction Function की पहचान करती है:

  • 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 उपयोग न करने की तुलना में अधिक धीरे-धीरे निष्पादित 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-14ebce7b7ee8>", 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-14ebce7b7ee8>", 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 और ग्राफ़ निर्माण को नियंत्रित करने के लिए पायथन तर्कों का उपयोग किया जाता है - उदाहरण के लिए, 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

यदि आपको रीट्रेसिंग को बाध्य करने की आवश्यकता है, तो एक नया 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")))
Tracing with Tensor("a:0", shape=(), dtype=string)
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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-e4e2860a4364>", 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 1725, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1770, in _call_with_flat_signature
    self._flat_signature_summary(), ", ".join(sorted(kwargs))))
TypeError: pow(a) got unexpected keyword arguments: b.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d163f3d206cb>", 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 योग्य आवरण है। हालांकि वास्तविक 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.function साथ सजाने से पहले आपका कोड उत्सुक मोड में त्रुटि-मुक्त निष्पादित करता है। डिबगिंग प्रक्रिया में सहायता के लिए, आप tf.config.run_functions_eagerly(True) को वैश्विक रूप से अक्षम और tf.function पुन: सक्षम करने के लिए कॉल कर सकते हैं।

केवल tf.function दिखाई देने वाली समस्याओं को ट्रैक करते tf.function , यहां कुछ युक्तियां दी गई हैं:

  • सादे पुराने पायथन print कॉल केवल ट्रेसिंग के दौरान निष्पादित होते हैं, जब आपके फ़ंक्शन का पता लगाया जाता है, तो आपको ट्रैक करने में मदद मिलती है।
  • tf.print कॉल हर बार निष्पादित होंगे, और निष्पादन के दौरान मध्यवर्ती मानों को ट्रैक करने में आपकी सहायता कर सकते हैं।
  • tf.debugging.enable_check_numerics यह पता tf.debugging.enable_check_numerics का एक आसान तरीका है कि NaN और Inf कहाँ बनाए गए हैं।
  • pdb ( पायथन डीबगर ) आपको यह समझने में मदद कर सकता है कि ट्रेसिंग के दौरान क्या हो रहा है। (चेतावनी: pdb हस्ताक्षर-बदल स्रोत कोड में आप छोड़ देंगे।)

ऑटोग्राफ रूपांतरण

ऑटोग्राफ एक पुस्तकालय है जो डिफ़ॉल्ट रूप से tf.function में 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.710546374 0.327660799 0.393230557 0.545059443 0.666661739]
[0.611019373 0.316417336 0.374141902 0.496808201 0.582779706]
[0.54484427 0.306263864 0.357609242 0.45960331 0.52468282]
[0.496646136 0.297034383 0.343106419 0.429760844 0.481306106]
[0.459475428 0.288596332 0.330247819 0.405121386 0.44728902]
[0.429656595 0.280842364 0.318743408 0.384322464 0.419668049]
[0.405034214 0.273684502 0.308370233 0.366455346 0.396650732]
[0.384248167 0.267049909 0.298953682 0.350887358 0.377079546]
[0.366391063 0.260877609 0.290354759 0.337162226 0.360168517]
[0.350830942 0.255116194 0.282461286 0.324941576 0.345362455]
[0.337112248 0.2497219 0.275181532 0.313968241 0.332256317]
[0.324896872 0.244657204 0.268439621 0.304042816 0.320546716]
[0.313927948 0.239889801 0.262172282 0.295007944 0.310001194]
[0.304006279 0.235391632 0.256326199 0.286737591 0.300438195]
[0.294974595 0.231138244 0.250856102 0.279129326 0.291713566]
[0.286706954 0.227108166 0.245723218 0.272099048 0.283711195]
[0.279101074 0.223282441 0.240894228 0.265576899 0.276336372]
[0.272072881 0.219644368 0.23634018 0.259504348 0.269510925]
[0.26555258 0.216179058 0.232035935 0.253831863 0.263169676]
[0.259481668 0.212873235 0.227959365 0.24851726 0.257257849]
[0.253810644 0.209715009 0.224091038 0.243524343 0.251728892]
[0.248497337 0.206693679 0.220413819 0.238821834 0.246543139]
[0.243505597 0.20379965 0.216912434 0.2343826 0.241666421]
[0.238804176 0.20102416 0.213573262 0.230182901 0.237069115]
[0.234365895 0.198359385 0.210384145 0.226201892 0.232725471]
[0.230167076 0.195798069 0.207334161 0.222421184 0.228612974]
[0.226186857 0.19333373 0.204413429 0.218824506 0.224711776]
[0.222406894 0.190960392 0.201613098 0.215397388 0.221004337]
[0.218810901 0.188672557 0.198925063 0.212126866 0.217475086]
[0.215384394 0.186465234 0.196342021 0.209001362 0.214110211]
[0.212114424 0.184333771 0.193857312 0.206010461 0.210897282]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.20898944, 0.18227392, 0.19146483, 0.2031447 , 0.20782518],
      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 कॉल में 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.while_loop . tf.while_loop । यदि परिवर्तित नहीं किया जाता है, तो लूप के for या while लूप को पायथन लूप के रूप में निष्पादित किया जाता है।

यह प्रतिस्थापन निम्नलिखित स्थितियों में किया जाता है:

  • for x in y : यदि y एक टेंसर है, तो tf.while_loop . tf.while_loop कनवर्ट करें। विशेष मामले में जहां y एकtf.data.Dataset ,tf.data.Dataset ops का एक संयोजन उत्पन्न होता है।
  • while <condition> : यदि <condition> एक टेंसर है, तो tf.while_loop . tf.while_loop कनवर्ट करें।

एक पायथन लूप ट्रेसिंग के दौरान निष्पादित होता है, लूप के प्रत्येक पुनरावृत्ति के लिए tf.Graph अतिरिक्त ऑप्स tf.Graph

एक TensorFlow लूप लूप के शरीर का पता लगाता है, और गतिशील रूप से चयन करता है कि निष्पादन समय पर कितने पुनरावृत्तियों को चलाना है। लूप बॉडी केवल एक बार उत्पन्न tf.Graph में दिखाई tf.Graph

ऑटोग्राफ-कन्वर्टेड for एंड while स्टेटमेंट्स पर अतिरिक्त प्रतिबंधों के लिए संदर्भ दस्तावेज़ देखें।

पायथन डेटा पर लूपिंग

tf.function भीतर Python/NumPy डेटा पर लूप करना एक आम tf.function । यह लूप ट्रेसिंग प्रक्रिया के दौरान निष्पादित होगा, लूप के प्रत्येक पुनरावृत्ति के लिए आपके मॉडल की एक प्रति tf.Graph में tf.Graph

यदि आप संपूर्ण प्रशिक्षण लूप को tf.function में tf.function , तो ऐसा करने का सबसे सुरक्षित तरीका है कि आप अपने डेटा कोtf.data.Dataset रूप में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 10 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 10 nodes in its graph

डेटासेट में Python/NumPy डेटा tf.data.Dataset.from_generator , tf.data.Dataset.from_generator बनाम tf.data.Dataset.from_tensors सावधान tf.data.Dataset.from_tensors । पहला डेटा को पायथन में रखेगा और इसे tf.py_function माध्यम से tf.py_function जिसमें प्रदर्शन प्रभाव हो सकते हैं, जबकि बाद वाला डेटा की एक प्रति को tf.constant() में एक बड़े tf.constant() नोड के रूप में बंडल करेगा, जिसमें स्मृति प्रभाव हो सकते हैं।

TFRecordDataset , CsvDataset , आदि के माध्यम से फ़ाइलों से डेटा पढ़ना डेटा का उपभोग करने का सबसे प्रभावी तरीका है, क्योंकि तब TensorFlow स्वयं पायथन को शामिल किए बिना डेटा के एसिंक्रोनस लोडिंग और प्रीफ़ेचिंग का प्रबंधन कर सकता है। अधिक जानने के लिए, tf.data देखें : TensorFlow इनपुट पाइपलाइन गाइड बनाएं

लूप में मान जमा करना

एक सामान्य पैटर्न लूप से मध्यवर्ती मूल्यों को जमा करना है। आम तौर पर, यह एक पायथन सूची में जोड़कर या एक पायथन शब्दकोश में प्रविष्टियां जोड़कर पूरा किया जाता है। हालांकि, चूंकि ये पायथन साइड इफेक्ट हैं, वे गतिशील रूप से अनियंत्रित लूप में अपेक्षित रूप से काम नहीं करेंगे। गतिशील रूप से tf.TensorArray लूप से परिणाम संचित करने के लिए 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.60458577, 0.3308612 , 0.7878152 , 0.3223114 ],
        [0.9110272 , 1.0819752 , 1.7657743 , 1.2409766 ],
        [1.7235098 , 1.5416101 , 2.2929285 , 1.9181627 ]],

       [[0.89487076, 0.22811687, 0.342862  , 0.5752872 ],
        [1.0133923 , 0.28650808, 0.9558767 , 1.0829899 ],
        [1.9280962 , 1.1437279 , 0.9857702 , 1.4834155 ]]], dtype=float32)>

सीमाओं

TensorFlow Function डिजाइन है कि आप जब एक करने के लिए एक अजगर समारोह परिवर्तित करने के बारे में पता होना चाहिए द्वारा कुछ सीमाएँ हैं Function

पायथन साइड इफेक्ट निष्पादित करना

साइड इफेक्ट, जैसे प्रिंटिंग, सूचियों में शामिल होना, और ग्लोबल्स को बदलना, Function अंदर अप्रत्याशित रूप से व्यवहार कर सकते हैं, कभी-कभी दो बार या सभी को निष्पादित नहीं करते हैं। वे केवल पहली बार तब होते हैं जब आप किसी Function को इनपुट के सेट के साथ कॉल करते हैं। बाद में, पता लगाया tf.Graph अजगर कोड को क्रियान्वित करने के बिना reexecuted है।

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

आपको Function बाहर रहने वाले सूचियों, डिक्ट्स, अन्य ऑब्जेक्ट्स जैसे कंटेनरों को बदलने से बचना चाहिए। इसके बजाय, तर्कों और TF ऑब्जेक्ट्स का उपयोग करें। उदाहरण के लिए, "लूप में मान जमा करना" अनुभाग में एक उदाहरण है कि सूची-जैसी संचालन कैसे कार्यान्वित किया जा सकता है।

यदि आप tf.Variable है, तो आप कुछ मामलों में स्थिति को कैप्चर और हेरफेर कर सकते हैं। यह कैसे Keras मॉडलों के भार एक ही करने के लिए बार-बार कॉल के साथ अपडेट किया जाता है 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

Function कॉल के बीच tf.Variables को हटाना

एक और त्रुटि जिसका आप सामना कर सकते हैं वह है कचरा-एकत्रित चर। ConcreteFunction केवल WeakRefs को उनके द्वारा बंद किए गए चर के लिए बनाए रखता है, इसलिए आपको किसी भी चर के संदर्भ को बनाए रखना होगा।

external_var = tf.Variable(3)
@tf.function
def f(x):
  return x * external_var

traced_f = f.get_concrete_function(4)
print("Calling concrete function...")
print(traced_f(4))

# The original variable object gets garbage collected, since there are no more
# references to it.
external_var = tf.Variable(4)
print()
print("Calling concrete function after garbage collecting its closed Variable...")
with assert_raises(tf.errors.FailedPreconditionError):
  traced_f(4)
Calling concrete function...
tf.Tensor(12, shape=(), dtype=int32)

Calling concrete function after garbage collecting its closed Variable...
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.FailedPreconditionError'>:
Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-9a93d2e07632>", line 16, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError:  Could not find variable _AnonymousVar3. This could mean that the variable has been deleted. In TF1, it can also mean the variable is uninitialized. Debug info: container=localhost, status=Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-1-9a93d2e07632>:4) ]] [Op:__inference_f_782]

Function call stack:
f

ज्ञात पहलु

यदि आपका Function सही ढंग से मूल्यांकन नहीं कर रहा है, तो त्रुटि को इन ज्ञात मुद्दों द्वारा समझाया जा सकता है जिन्हें भविष्य में ठीक करने की योजना है।

पायथन वैश्विक और मुक्त चर के आधार पर

पायथन तर्क के नए मान के साथ कॉल करने पर 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.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 महंगा हो सकता है , आप tf.Variable s को ऑब्जेक्ट एट्रिब्यूट के रूप में उपयोग कर सकते हैं, जिसे tf.Variable आवश्यकता के समान प्रभाव के लिए उत्परिवर्तित (लेकिन बदला नहीं, सावधान!) किया जा सकता है।

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.Variables को नए ट्रेस में नहीं बना सकते। बाद की कॉलों में नए चर बनाने की वर्तमान में अनुमति नहीं है, लेकिन भविष्य में होगी।

उदाहरण:

@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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-8a0913e250e0>", line 7, in <module>
    f(1.0)
ValueError: in user code:

    <ipython-input-1-8a0913e250e0>:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:262 __call__  **
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:256 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py:769 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.

आप किसी Function अंदर वेरिएबल बना सकते हैं जब तक कि वे वेरिएबल केवल पहली बार फ़ंक्शन निष्पादित होने पर ही बनाए जाते हैं।

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)

एकाधिक Keras अनुकूलक के साथ प्रयोग करना

आपको ValueError: tf.function-decorated function tried to create variables on non-first call. सामना करना पड़ सकता है ValueError: tf.function-decorated function tried to create variables on non-first call. tf.function साथ एक से अधिक 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d3d3937dbf1a>", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    <ipython-input-1-d3d3937dbf1a>:9 train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:636 apply_gradients  **
        self._create_all_weights(var_list)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:821 _create_all_weights
        _ = self.iterations
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:828 __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:988 iterations
        aggregation=tf_variables.VariableAggregation.ONLY_FIRST_REPLICA)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:1194 add_weight
        aggregation=aggregation)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/training/tracking/base.py:815 _add_variable_with_custom_getter
        **kwargs_for_getter)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer_utils.py:139 make_variable
        shape=variable_shape if variable_shape else None)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:260 __call__
        return cls._variable_v1_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:221 _variable_v1_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py:769 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.

यदि आपको प्रशिक्षण के दौरान ऑप्टिमाइज़र को बदलने की आवश्यकता है, तो प्रत्येक ऑप्टिमाइज़र के लिए एक नया 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-decorated function tried to create variables on non-first call. भी सामना कर सकते हैं ValueError: tf.function-decorated function tried to create variables on non-first call. एक ही Function विभिन्न मॉडल इंस्टेंस पास करते समय।

यह त्रुटि इसलिए होती है क्योंकि केरस मॉडल (जिनके इनपुट आकार परिभाषित नहीं होते हैं ) और tf.Variables परतें tf.Variables बनाते हैं जब उन्हें पहली बार बुलाया जाता है। हो सकता है कि आप उन वेरिएबल्स को Function अंदर इनिशियलाइज़ करने का प्रयास कर रहे हों, जिन्हें पहले ही कॉल किया जा चुका है। इस त्रुटि से बचने के लिए, मॉडल को प्रशिक्षित करने से पहले सभी भारों को प्रारंभ करने के लिए model.build(input_shape) को कॉल करने का प्रयास करें।

अग्रिम पठन

किसी Function को निर्यात और लोड करने के तरीके के बारे में जानने के लिए, सहेजे गए मॉडल मार्गदर्शिका देखें । ट्रेसिंग के बाद किए जाने वाले ग्राफ़ अनुकूलन के बारे में अधिक जानने के लिए, ग्रेप्लर गाइड देखें । अपनी डेटा पाइपलाइन को अनुकूलित करने और अपने मॉडल को प्रोफाइल करने का तरीका जानने के लिए, प्रोफाइलर गाइड देखें