इस पेज का अनुवाद Cloud Translation API से किया गया है.
Switch to English

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
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.0026629049998518894
Function conv: 0.0034744160000172997
Note how there's not much difference in performance for convolutions

ट्रेसिंग

पायथन के गतिशील टाइपिंग का मतलब है कि आप विभिन्न प्रकार के तर्क के साथ फ़ंक्शन कह सकते हैं, और पायथन प्रत्येक परिदृश्य में कुछ अलग कर सकता है।

फिर भी, TensorFlow Graph बनाने के लिए, स्थिर dtypes और आकार आयामों की आवश्यकता होती है। Function ऑब्जेक्ट बनाने के लिए पायथन फ़ंक्शन को लपेटकर इस अंतराल को tf.function पुल करता है। दिए गए इनपुट्स के आधार पर, Function दिए गए इनपुट्स के लिए उपयुक्त ग्राफ का चयन करता है, जो आवश्यक के रूप में पायथन फ़ंक्शन को फिर से शुरू करता है। एक बार जब आप समझ जाते हैं कि क्यों और कब ट्रेसिंग होती है, तो प्रभावी रूप से tf.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)

(निम्नलिखित परिवर्तन रात में TensorFlow में उपलब्ध है, और TensorFlow 2.3 में उपलब्ध होगा।)

उपलब्ध सभी निशानों को देखने के लिए आप pretty_printed_concrete_signatures() का उपयोग कर सकते हैं:

print(double.pretty_printed_concrete_signatures())
double(a)
  Args:
    a: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

double(a)
  Args:
    a: int32 Tensor, shape=()
  Returns:
    int32 Tensor, shape=()

double(a)
  Args:
    a: string Tensor, shape=()
  Returns:
    string Tensor, shape=()

अब तक, आपने देखा है कि tf.function TensorFlow के ग्राफ ट्रेसिंग लॉजिक के ऊपर एक कैश्ड, डायनामिक डिस्पैच लेयर बनाता है। शब्दावली के बारे में अधिक विशिष्ट होना:

  • एक tf.Graph आपके गणना के कच्चे, भाषा-अज्ञेय, पोर्टेबल प्रतिनिधित्व है।
  • एक ConcreteFunction के चारों ओर एक एक उत्सुकता-क्रियान्वित आवरण है tf.Graph
  • एक Function की एक कैश का प्रबंधन करता है ConcreteFunction और आपके इनपुट के लिए सही चुनता है।
  • tf.function एक पायथन फ़ंक्शन को लपेटता है, जो एक Function ऑब्जेक्ट देता है।

ठोस कार्यों को प्राप्त करना

जब भी किसी फ़ंक्शन का पता लगाया जाता है, एक नया ठोस फ़ंक्शन बनाया जाता है। आप 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)

(निम्नलिखित परिवर्तन रात में TensorFlow में उपलब्ध है, और TensorFlow 2.3 में उपलब्ध होगा।)

एक 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-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-15-e4e2860a4364>", line 2, in <module>
    double_strings(tf.constant(1))
tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_168 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_168]

आप देख सकते हैं कि कंक्रीट फ़ंक्शन के इनपुट हस्ताक्षर में पायथन के तर्क को विशेष उपचार दिया गया है। TensorFlow 2.3 से पहले, पायथन तर्क को केवल ठोस फ़ंक्शन के हस्ताक्षर से हटा दिया गया था। 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.6/site-packages/tensorflow/python/eager/function.py", line 1669, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 1714, 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-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-17-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 , यहां कुछ सुझाव दिए गए हैं:

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

शब्दार्थ का पता लगाना

कैश प्रमुख नियम

एक Function यह निर्धारित करता है कि किसी इनपुट की आर्ग और काग्रेस से कैश कुंजी की गणना करके एक ट्रेस किए गए कंक्रीट फ़ंक्शन का पुन: उपयोग किया जाए या नहीं।

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

नियंत्रण से पीछे हटना

Retracing यह सुनिश्चित करने में मदद करता है कि TensorFlow इनपुट्स के प्रत्येक सेट के लिए सही ग्राफ़ बनाता है। हालाँकि, ट्रेसिंग एक महंगा ऑपरेशन है! यदि आपका Function हर कॉल के लिए एक नया ग्राफ़ tf.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])))
# We 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]]))

# We 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-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-19-20f544b8adbf>", 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-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-19-20f544b8adbf>", 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 या num_layers=10 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

पायथन साइड इफेक्ट

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

अंगूठे का सामान्य नियम केवल अपने निशान को ख़राब करने के लिए पायथन साइड इफेक्ट्स का उपयोग करना है। अन्यथा, TensorFlow tf.Variable.assign , tf.print , और tf.summary जैसे ऑप्स आपके कोड को पता लगाने और प्रत्येक कॉल के लिए 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 अंदर कई अप्रत्याशित चीजें हो सकती हैं।

एक उदाहरण देने के लिए, पुनरावृत्ति की स्थिति को आगे बढ़ाना एक पायथन साइड इफेक्ट है और इसलिए केवल अनुरेखण के दौरान होता है।

external_var = tf.Variable(0)
@tf.function
def buggy_consume_next(iterator):
  external_var.assign_add(next(iterator))
  tf.print("Value of external_var:", external_var)

iterator = iter([0, 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 of external_var: 0
Value of external_var: 0
Value of external_var: 0

कुछ पुनरावृत्ति निर्माण AutoGraph के माध्यम से समर्थित हैं। अवलोकन के लिए AutoGraph ट्रांसफ़ॉर्मेशन पर अनुभाग देखें।

यदि आप Function प्रत्येक मंगलाचरण के दौरान पायथन कोड निष्पादित करना चाहते हैं, तो tf.py_function एक निकास हैच है। tf.py_function की खामी यह है कि यह पोर्टेबल या विशेष रूप से tf.py_function नहीं है, और न ही यह वितरित (मल्टी-जीपीयू, टीपीयू) सेटअप में अच्छी तरह से काम करता है। इसके अलावा, चूंकि tf.py_function को tf.py_function में वायर्ड किया जाना है, इसलिए यह सभी इनपुट / आउटपुट को tf.py_function है।

API जैसे tf.gather , tf.stack और tf.TensorArray आपको देशी टेंसरफ्लो में आम लूपिंग पैटर्न को लागू करने में मदद कर सकते हैं।

external_list = []

def side_effect(x):
  print('Python side effect')
  external_list.append(x)

@tf.function
def f(x):
  tf.py_function(side_effect, inp=[x], Tout=[])

f(1)
f(1)
f(1)
# The list append happens all three times!
assert len(external_list) == 3
# The list contains tf.constant(1), not 1, because py_function casts everything to tensors.
assert external_list[0].numpy() == 1

Python side effect
Python side effect
Python side effect

चर

किसी फ़ंक्शन में एक नया tf.Variable बनाते समय आप एक त्रुटि का सामना कर सकते हैं। यह त्रुटि बार-बार कॉल पर व्यवहार के विचलन के खिलाफ गार्ड होती है: उत्सुक मोड में, एक फ़ंक्शन प्रत्येक कॉल के साथ एक नया चर बनाता है, लेकिन Function , ट्रेस पुन: उपयोग के कारण एक नया चर नहीं बनाया जा सकता है।

@tf.function
def f(x):
  v = tf.Variable(1.0)
  v.assign_add(x)
  return v

with assert_raises(ValueError):
  f(1.0)
Caught expected exception 
  <class 'ValueError'>:

Traceback (most recent call last):
  File "<ipython-input-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-26-73e410646579>", line 8, in <module>
    f(1.0)
ValueError: in user code:

    <ipython-input-26-73e410646579>:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:262 __call__  **
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:256 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/def_function.py:702 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)

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

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))

del external_var
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-3-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-28-304a18524b57>", line 14, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found.
  (0) Failed precondition:  Error while reading resource variable _AnonymousVar4 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar4/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-28-304a18524b57>:4) ]]
  (1) Failed precondition:  Error while reading resource variable _AnonymousVar4 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar4/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-28-304a18524b57>:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_514]

Function call stack:
f -> f


AutoGraph रूपांतरण

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

TensorFlow जैसे tf.cond और tf.while_loop का काम जारी है, लेकिन पायथन में लिखे जाने पर नियंत्रण प्रवाह अक्सर लिखना और समझना आसान होता है।

# 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.592976809 0.128855109 0.13305068 0.379838109 0.429846764]
[0.532033205 0.128146663 0.132271096 0.362566859 0.405193239]
[0.486933738 0.127449796 0.131505072 0.347472966 0.384383708]
[0.451779395 0.126764178 0.130752221 0.334132522 0.366508394]
[0.423360586 0.126089528 0.130012169 0.322229117 0.35093388]
[0.399757773 0.125425547 0.129284561 0.311521083 0.337203503]
[0.379741699 0.124771953 0.128569037 0.301820248 0.324978501]
[0.362483114 0.124128476 0.127865285 0.292977482 0.31400153]
[0.347399354 0.123494864 0.127172977 0.284872979 0.304073036]
[0.334067136 0.12287087 0.1264918 0.277409106 0.295035541]
[0.322170526 0.122256249 0.125821471 0.270505458 0.286762893]
[0.311468184 0.12165077 0.125161693 0.264095098 0.279152662]
[0.301772147 0.121054202 0.124512196 0.258121818 0.272120655]
[0.292933524 0.120466337 0.123872712 0.252537966 0.265597]
[0.284832567 0.119886965 0.123243 0.247302905 0.259523094]
[0.277371794 0.119315878 0.122622795 0.242381677 0.253849417]

<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.27047086, 0.11875288, 0.12201188, 0.23774408, 0.24853374],
      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> एक Tensor है। अन्यथा, if बयान को पायथन सशर्त के रूप में निष्पादित किया जाता है।

एक पायथन सशर्त अनुरेखण के दौरान निष्पादित करता है, इसलिए सशर्त की एक शाखा को ग्राफ में जोड़ा जाएगा। AutoGraph के बिना, यह ट्रेस ग्राफ़ डेटा-निर्भर नियंत्रण प्रवाह होने पर वैकल्पिक शाखा लेने में असमर्थ होगा।

tf.cond पता चलता है और सशर्त की दोनों शाखाओं को tf.cond जोड़ता है, गतिशील रूप से निष्पादन समय पर एक शाखा का चयन करता है। अनुरेखण के दुष्प्रभाव हो सकते हैं; अधिक के लिए AutoGraph अनुरेखण प्रभाव देखें।

@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 । अगर परिवर्तित नहीं किया, for या while पाश एक अजगर लूप के रूप में क्रियान्वित किया जाता है।

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

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

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

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

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

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

एक सामान्य tf.function एक tf.function भीतर अजगर / Numpy डेटा पर लूप करना है। यह लूप अनुरेखण प्रक्रिया के दौरान निष्पादित करेगा, लूप के प्रत्येक पुनरावृत्ति के लिए अपने मॉडल की प्रतिलिपि tf.Graph में tf.Graph

यदि आप संपूर्ण प्रशिक्षण पाश को tf.function में tf.function , तो ऐसा करने का सबसे सुरक्षित तरीका यह है कि आप अपने डेटा को tf.data.Dataset रूप में tf.data.Dataset ताकि AutoGraph डायनामिक रूप से प्रशिक्षण लूप को अनियंत्रित कर दे।

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 8 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 8 nodes in its graph

जब डेटासेट में Python / Numpy डेटा लपेटते हैं, तो 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 / etc के माध्यम से फाइलों से डेटा पढ़ना। डेटा का उपभोग करने का सबसे प्रभावी तरीका है, क्योंकि तब TensorFlow खुद Python को शामिल किए बिना, अतुल्यकालिक लोडिंग और डेटा के प्रीफ़ेटिंग को प्रबंधित कर सकता है। अधिक जानने के लिए, tf.data मार्गदर्शिका देखें।

एक लूप में मान संचय

एक सामान्य पैटर्न एक लूप से मध्यवर्ती मूल्यों को जमा करना है। आम तौर पर, इसे पायथन सूची में जोड़कर या पायथन शब्दकोश में प्रविष्टियों को जोड़कर पूरा किया जाता है। हालाँकि, ये पाइथन दुष्प्रभाव हैं, वे गतिशील रूप से अनियंत्रित लूप में अपेक्षित रूप से काम नहीं करेंगे। गतिशील रूप से 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.3888433 , 0.2078135 , 0.3843341 , 0.5707482 ],
        [1.1050591 , 0.5968373 , 0.982028  , 0.7987355 ],
        [1.614423  , 0.8602725 , 1.4517052 , 1.6631885 ]],

       [[0.64148533, 0.67286134, 0.07972229, 0.9772469 ],
        [1.5103817 , 0.8244705 , 0.747108  , 1.7505025 ],
        [2.5020413 , 1.5157869 , 0.8045732 , 2.4830637 ]]], dtype=float32)>

आगे की पढाई

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