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

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

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

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

इस गाइड आप की अवधारणा कैसे मदद मिलेगी tf.function अंतर्गत कार्य करता है, ताकि आप इसे प्रभावी ढंग से इस्तेमाल कर सकते हैं।

मुख्य टेकअवे और सिफारिशें हैं:

  • उत्सुक मोड में डिबग, तो साथ सजाने @tf.function
  • ऑब्जेक्ट म्यूटेशन या लिस्ट एपेंड जैसे पायथन साइड इफेक्ट्स पर भरोसा न करें।
  • tf.function TensorFlow ऑप्स के साथ सबसे अच्छा काम करता है; 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 रों अंदर अन्य Function रों।

@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 रों विशेष रूप से कई छोटे ऑप्स साथ रेखांकन के लिए, उत्सुक कोड की तुलना में तेजी हो सकता है। लेकिन कुछ महंगे ऑप्स (जैसे कनवल्शन) वाले ग्राफ़ के लिए, आपको अधिक गति दिखाई नहीं दे सकती है।

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.00443888600000264
Function conv: 0.004833394000002045
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 और आपके इनपुट के लिए सही चुनता है।
  • tf.function एक अजगर समारोह लपेटता, एक लौटने Function वस्तु।
  • ट्रेसिंग एक बनाता है tf.Graph और एक में यह लपेटता ConcreteFunction , यह भी एक का पता लगाने के रूप में जाना।

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

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

  • एक के लिए उत्पन्न कुंजी tf.Tensor अपने आकार और dtype है।
  • एक के लिए उत्पन्न कुंजी tf.Variable एक अनूठा चर आईडी है।
  • एक अजगर आदिम (जैसे के लिए उत्पन्न कुंजी int , float , str ) अपने मूल्य है।
  • के लिए नेस्टेड जनरेट की गई कुंजी dict रों, list है, tuple रों, namedtuple है, और attr रों पत्ती-कुंजी (देखें की चपटी टपल है nest.flatten )। (इस चपटेपन के परिणामस्वरूप, ट्रेसिंग के दौरान उपयोग किए जाने वाले की तुलना में एक अलग नेस्टिंग संरचना के साथ एक ठोस फ़ंक्शन को कॉल करने के परिणामस्वरूप एक TypeError होगा)।
  • अन्य सभी पायथन प्रकारों के लिए कुंजी वस्तु के लिए अद्वितीय है। इस तरह एक फ़ंक्शन या विधि को प्रत्येक उदाहरण के लिए स्वतंत्र रूप से पता लगाया जाता है जिसे इसे कहा जाता है।

रिट्रेसिंग को नियंत्रित करना

Retracing, जो तब होता है जब आपके 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_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/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_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/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 एक का उपयोग कर उनके आकार के आधार पर tensors से मेल खाता है, None वाइल्डकार्ड के रूप में आयाम की अनुमति देगा Function variably आकार इनपुट के लिए पुन: उपयोग के निशान को रों। यदि आप प्रत्येक बैच (देखें के लिए अलग अलग लंबाई के दृश्यों, या विभिन्न आकार के चित्र हैं variably आकार इनपुट हो सकता है ट्रांसफार्मर और दीप ड्रीम उदाहरण के लिए ट्यूटोरियल)।

@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)
  • रिट्रेसिंग को कम करने के लिए टेन्सर्स को पायथन तर्क कास्ट करें।

    उदाहरण के लिए, - अक्सर, अजगर तर्क नियंत्रण hyperparameters और ग्राफ निर्माण करने के लिए उपयोग किया जाता है 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

आप बल retracing की जरूरत है, एक नया बनाने के 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 "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/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 1766, 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 "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/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 नीचे ट्रैक करने के लिए जहां Nans और Inf बनाई गई हैं एक आसान तरीका है।
  • pdb ( अजगर डिबगर ) मदद कर सकते हैं समझते हैं कि क्या पता लगाने के दौरान हो रहा है। (चेतावनी: pdb हस्ताक्षर-बदल स्रोत कोड में आप छोड़ देंगे।)

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

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

जैसे TensorFlow ऑप्स 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.134340048 0.12084043 0.899008512 0.317973018 0.984562874]
[0.133537665 0.120255642 0.715814829 0.307672977 0.755034447]
[0.132749513 0.119679272 0.614310205 0.298318624 0.638142884]
[0.131975174 0.119111128 0.547154069 0.289773166 0.56363374]
[0.131214246 0.118551008 0.498384237 0.281926 0.510668516]
[0.130466327 0.117998719 0.46084547 0.274686694 0.470465899]
[0.129731074 0.117454082 0.43077302 0.267980397 0.438575685]
[0.129008129 0.116916925 0.405967116 0.261744559 0.412463129]
[0.12829715 0.116387077 0.385043025 0.255926549 0.390562057]
[0.127597824 0.115864381 0.367079 0.250481546 0.371844649]
[0.126909807 0.115348652 0.351434082 0.245371237 0.355604142]
[0.126232818 0.11483977 0.337646723 0.240562648 0.341336131]
[0.125566557 0.114337578 0.325374842 0.236027122 0.328669697]
[0.124910742 0.113841921 0.314358741 0.231739685 0.317324936]
[0.124265119 0.113352649 0.304397196 0.227678493 0.30708608]
[0.123629414 0.112869643 0.295331478 0.223824292 0.297783881]
[0.123003371 0.112392753 0.287034482 0.220160022 0.289283246]
[0.122386754 0.111921862 0.279403061 0.216670573 0.281474978]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.12177932, 0.11145685, 0.2723525 , 0.21334243, 0.27426964],
      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)

सशर्त,

हस्ताक्षर कुछ परिवर्तित कर देंगे 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

देखें संदर्भ दस्तावेज़ पर अतिरिक्त प्रतिबंध के लिए बयान करता है, तो हस्ताक्षर-बदल दिया।

छोरों

हस्ताक्षर कुछ परिवर्तित कर देंगे for और while बराबर TensorFlow में बयान ऑप्स पाशन, जैसे tf.while_loop । अगर परिवर्तित नहीं किया, for या while पाश एक अजगर लूप के रूप में क्रियान्वित किया जाता है।

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

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

ट्रेसिंग के दौरान एक अजगर पाश कार्यान्वित, करने के लिए अतिरिक्त ऑप्स जोड़ने tf.Graph पाश के हर यात्रा के लिए।

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

देखें संदर्भ दस्तावेज़ पर हस्ताक्षर-परिवर्तित अतिरिक्त प्रतिबंध के लिए for और while बयान।

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

एक आम ख़तरा एक के भीतर अजगर / NumPy डेटा पर पाश करने के लिए है tf.function । इस पाश का पता लगाने की प्रक्रिया के दौरान निष्पादित करने के लिए अपने मॉडल की एक प्रति को जोड़ने जाएगा 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

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

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

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

एक सामान्य पैटर्न एक लूप से मध्यवर्ती मूल्यों को जमा करना है। आम तौर पर, यह एक पायथन सूची में जोड़कर या एक पायथन शब्दकोश में प्रविष्टियां जोड़कर पूरा किया जाता है। हालांकि, चूंकि ये पायथन साइड इफेक्ट हैं, वे गतिशील रूप से अनियंत्रित लूप में अपेक्षित रूप से काम नहीं करेंगे। उपयोग tf.TensorArray एक गतिशील unrolled पाश से परिणाम जमा करने के लिए।

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.50723314, 0.05389965, 0.6574272 , 0.7971941 ],
        [0.76342154, 0.67873085, 0.6722325 , 0.80833924],
        [1.4959786 , 0.92963636, 1.181517  , 0.82988703]],

       [[0.05639362, 0.30149877, 0.5931294 , 0.58158445],
        [0.7223501 , 0.7357049 , 1.5900992 , 1.5196564 ],
        [1.2542304 , 1.443726  , 1.6683336 , 2.2088552 ]]], dtype=float32)>

सीमाओं

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

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

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

अंगूठे का सामान्य नियम अपने तर्क में पायथन के दुष्प्रभावों पर भरोसा करने से बचना है और केवल अपने निशान को डीबग करने के लिए उनका उपयोग करना है। अन्यथा, जैसे TensorFlow एपीआई 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 यह पोर्टेबल नहीं या विशेष रूप से performant, SavedModel साथ सहेजा नहीं जा सकता है, और वितरित (बहु GPU, TPU) सेटअप में अच्छी तरह से काम नहीं करता है। इसके अतिरिक्त, क्योंकि tf.py_function ग्राफ में तार हो गया है, यह सब आदानों / आउटपुट tensors के लिए डाले।

पायथन वैश्विक और मुक्त चर बदलना

बदल रहा है अजगर वैश्विक और मुक्त चर एक अजगर पक्ष प्रभाव के रूप में गिना जाता है, तो यह केवल पता लगाने के दौरान होती है।

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

आप सूचियों, dicts, अन्य वस्तुओं है कि बाहर रहते हैं जैसे कंटेनर परिवर्तनशील से बचना चाहिए 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 एपीआई मदद कर सकते हैं जनरेटर पैटर्न को लागू:

@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.Variables हटाया जा रहा है Function कॉल

एक और त्रुटि जो आपको मिल सकती है वह है कचरा-एकत्रित चर। 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 "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3862898592.py", line 16, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found.
  (0) Failed precondition:  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 tmp/ipykernel_8296/3862898592.py:4) ]]
  (1) Failed precondition:  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 tmp/ipykernel_8296/3862898592.py:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_772]

Function call stack:
f -> f

tf.function के सभी आउटपुट रिटर्न वैल्यू होने चाहिए

के अपवाद के साथ tf.Variable रों, एक tf.function अपने सभी outputs लौट जाना चाहिए। रिटर्न वैल्यू से गुजरे बिना किसी फ़ंक्शन से किसी भी टेंसर को सीधे एक्सेस करने का प्रयास "लीक" का कारण बनता है।

उदाहरण के लिए, "लीक" टेन्सर नीचे समारोह a के माध्यम से अजगर वैश्विक x :

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
with assert_raises(AttributeError):
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
print(x)
3
Caught expected exception 
  <class 'AttributeError'>:
Tensor("add:0", shape=(), dtype=int32)
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/1216349822.py", line 13, in <module>
    x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
AttributeError: '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
with assert_raises(AttributeError):
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
print(x)
2
Caught expected exception 
  <class 'AttributeError'>:
Tensor("add:0", shape=(), dtype=int32)
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3201904755.py", line 11, in <module>
    x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
AttributeError: 'Tensor' object has no attribute 'numpy'

आप का सामना कर सकते TypeError: tf.Graph captured an external symbolic tensor. जब आप किसी अन्य tf.Graph में लीक प्रतीकात्मक टेंसर का उपयोग करते हैं। उदाहरण के लिए,

@tf.function
def captures_leaked_tensor(b):
  b += x  # Bad - `x` is leaked from `leaky_function`
  return b

# `x` leaked from `leaky_function` is captured by `captures_leaked_tensor`.
with assert_raises(TypeError):
  captures_leaked_tensor(tf.constant(2))

concrete_func = captures_leaked_tensor.get_concrete_function(tf.constant(2))
print(concrete_func.captured_inputs)
assert x is concrete_func.captured_inputs[0]
Caught expected exception 
  <class 'TypeError'>:
[<tf.Tensor 'add:0' shape=() dtype=int32>]
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/2761600771.py", line 8, in <module>
    captures_leaked_tensor(tf.constant(2))
TypeError: An op outside of the function building code is being passed
a "Graph" tensor. It is possible to have Graph tensors
leak out of the function building context by including a
tf.init_scope in your function building code.
For example, the following function will fail:
  @tf.function
  def has_init_scope():
    my_constant = tf.constant(1.)
    with tf.init_scope():
      added = my_constant * 2
The graph tensor has name: add:0

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

स्थानीय टेंसर को लीक करने के सामान्य तरीकों में बाहरी पायथन संग्रह, या एक वस्तु को बदलना भी शामिल है:

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

ज्ञात पहलु

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

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

Function एक नया बनाता है ConcreteFunction जब एक अजगर तर्क का एक नया मान के साथ कहा जाता है। हालांकि, यह है कि ऐसा नहीं करता है अजगर बंद, वैश्विक, या उस की nonlocals के लिए 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 हर बार जब आप बल retracing करने के लिए अपने ऑब्जेक्ट को संशोधित रहा है:

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)

के रूप में retracing महंगा हो सकता है , तो आप उपयोग कर सकते हैं tf.Variable एक retrace की जरूरत के बिना वस्तु गुण है, जो उत्परिवर्तित जा सकता है (लेकिन नहीं बदला, सावधान!) एक समान प्रभाव के लिए के रूप में।

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.चर बनाना

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_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3018268426.py", line 7, in <module>
    f(1.0)
ValueError: in user code:

    /tmp/ipykernel_8296/3018268426.py:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:268 __call__  **
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:262 _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:765 invalid_creator_scope
        "tf.function-decorated function tried to create "

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

एक आम इस सीमा के आसपास काम करने के लिए इस्तेमाल पैटर्न एक अजगर कोई भी मूल्य के साथ शुरू करते हैं, तो सशर्त बनाने के लिए है 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. जब एक साथ एक से अधिक Keras अनुकूलक का उपयोग कर 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_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3167358578.py", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    /tmp/ipykernel_8296/3167358578.py:9 train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:628 apply_gradients  **
        self._create_all_weights(var_list)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:813 _create_all_weights
        _ = self.iterations
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:820 __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:980 iterations
        aggregation=tf.VariableAggregation.ONLY_FIRST_REPLICA)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:1186 add_weight
        aggregation=aggregation)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/training/tracking/base.py:818 _add_variable_with_custom_getter
        **kwargs_for_getter)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer_utils.py:129 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:266 __call__
        return cls._variable_v1_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:227 _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:765 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 only supports singleton tf.Variables created on the first call. जब एक ही करने के लिए विभिन्न मॉडल उदाहरणों गुजर Function

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

आगे की पढाई

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