tf.function ile daha iyi performans

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyinNot defterini indir

TensorFlow 2'de istekli yürütme varsayılan olarak açıktır. Kullanıcı arayüzü sezgisel ve esnektir (tek seferlik işlemleri yürütmek çok daha kolay ve hızlıdır), ancak bu performans ve konuşlandırılabilirlik pahasına olabilir.

Programlarınızdan grafikler oluşturmak için tf.function işlevini kullanabilirsiniz. Python kodunuzdan Python'dan bağımsız veri akışı grafikleri oluşturan bir dönüştürme aracıdır. Bu, performanslı ve taşınabilir modeller oluşturmanıza yardımcı olur ve SavedModel kullanılması gerekir.

Bu kılavuz, etkin bir şekilde kullanabilmeniz için tf.function kaputun altında nasıl çalıştığını kavramsallaştırmanıza yardımcı olacaktır.

Başlıca çıkarımlar ve öneriler şunlardır:

  • İstekli modda hata ayıklayın, ardından @tf.function ile süsleyin.
  • Nesne mutasyonu veya liste ekleme gibi Python yan etkilerine güvenmeyin.
  • tf.function en iyi TensorFlow operasyonlarında çalışır; NumPy ve Python çağrıları sabitlere dönüştürülür.

Kurmak

import tensorflow as tf

Karşılaşabileceğiniz hata türlerini göstermek için bir yardımcı işlev tanımlayın:

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

temel bilgiler

kullanım

Tanımladığınız bir Function (örneğin @tf.function dekoratörünü uygulayarak) tıpkı bir çekirdek TensorFlow işlemi gibidir: Onu hevesle çalıştırabilirsiniz; gradyanları hesaplayabilirsiniz; ve benzeri.

@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)>
yer tutucu4 l10n-yer
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 içinde kullanabilirsiniz.

@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]))
tutucu7 l10n-yer
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
       [3., 3.],
       [3., 3.]], dtype=float32)>

Function s, özellikle birçok küçük işlem içeren grafikler için istekli koddan daha hızlı olabilir. Ancak birkaç pahalı işlem içeren grafikler için (kıvrımlar gibi), çok fazla hızlanma görmeyebilirsiniz.

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")
tutucu9 l10n-yer
Eager conv: 0.006058974999177735
Function conv: 0.005791576000774512
Note how there's not much difference in performance for convolutions

İzleme

Bu bölüm , gelecekte değişebilecek uygulama ayrıntıları da dahil olmak üzere, Function başlık altında nasıl çalıştığını ortaya koymaktadır. Ancak, izlemenin neden ve ne zaman gerçekleştiğini anladıktan sonra, tf.function etkili bir şekilde kullanmak çok daha kolay!

"İzlemek" nedir?

Bir Function , programınızı bir TensorFlow Graph'ta çalıştırır. Ancak, bir tf.Graph , hevesli bir TensorFlow programında yazacağınız her şeyi temsil edemez. Örneğin, Python polimorfizmi destekler, ancak tf.Graph , girdilerinin belirli bir veri tipine ve boyutuna sahip olmasını gerektirir. Veya komut satırı argümanlarını okumak, bir hatayı yükseltmek veya daha karmaşık bir Python nesnesi ile çalışmak gibi yan görevler gerçekleştirebilirsiniz; bunların hiçbiri bir tf.Graph içinde çalıştırılamaz.

Function , kodunuzu iki aşamada ayırarak bu boşluğu doldurur:

1) " Tracing " olarak adlandırılan ilk aşamada, Function yeni bir tf.Graph oluşturur. Python kodu normal çalışır, ancak tüm TensorFlow işlemleri (iki Tensör eklemek gibi) ertelenir : bunlar tf.Graph tarafından yakalanır ve çalıştırılmaz.

2) İkinci aşamada, ilk aşamada ertelenen her şeyi içeren bir tf.Graph çalıştırılır. Bu aşama, izleme aşamasından çok daha hızlıdır.

Girişlerine bağlı olarak, Function çağrıldığında her zaman ilk aşamayı çalıştırmaz. Bu belirlemeyi nasıl yaptığını daha iyi anlamak için aşağıdaki "İzleme kuralları"na bakın. İlk aşamayı atlamak ve yalnızca ikinci aşamayı yürütmek size TensorFlow'un yüksek performansını verir.

Function izlemeye karar verdiğinde, izleme aşamasını hemen ikinci aşama izler, bu nedenle Function çağırmak tf.Graph hem oluşturur hem de çalıştırır. Daha sonra get_concrete_function ile sadece izleme aşamasını nasıl çalıştırabileceğinizi göreceksiniz.

Bir Function içine farklı türdeki argümanları ilettiğinizde, her iki aşama da çalıştırılır:

@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()
tutucu11 l10n-yer
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)

Aynı argüman tipine sahip bir Function tekrar tekrar çağırırsanız, oluşturulan grafik aynı olacağından TensorFlow'un izleme aşamasını atlayacağını ve önceden izlenen bir grafiği yeniden kullanacağını unutmayın.

# This doesn't print 'Tracing with ...'
print(double(tf.constant("b")))
tutucu13 l10n-yer
tf.Tensor(b'bb', shape=(), dtype=string)

Mevcut tüm izleri görmek için pretty_printed_concrete_signatures() kullanabilirsiniz:

print(double.pretty_printed_concrete_signatures())
tutucu15 l10n-yer
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=()

Şimdiye kadar, tf.function işlevinin tf.function grafik izleme mantığı üzerinde önbelleğe alınmış, dinamik bir gönderme katmanı oluşturduğunu gördünüz. Terminoloji hakkında daha spesifik olmak gerekirse:

  • Bir tf.Graph , bir TensorFlow hesaplamasının ham, dilden bağımsız, taşınabilir temsilidir.
  • Bir ConcreteFunction , bir tf.Graph sarar.
  • Bir Function , ConcreteFunction s önbelleğini yönetir ve girdileriniz için doğru olanı seçer.
  • tf.function Bir Python işlevini sarar ve bir Function nesnesi döndürür.
  • İzleme , bir tf.Graph oluşturur ve onu iz olarak da bilinen bir ConcreteFunction 'a sarar.

izleme kuralları

Bir Function , bir girdinin argümanlarından ve kwarg'larından bir önbellek anahtarı hesaplayarak izlenen bir ConcreteFunction yeniden kullanılıp kullanılmayacağını belirler. Önbellek anahtarı , aşağıdaki kurallara (değişebilir) göre, Function çağrısının giriş argümanlarına ve ConcreteFunction dayalı olarak bir SomutFonksiyon tanımlayan bir anahtardır:

  • Bir tf.Tensor için oluşturulan anahtar, şekli ve tipidir.
  • Bir tf.Variable için oluşturulan anahtar, benzersiz bir değişken kimliğidir.
  • Bir Python ilkel öğesi ( int , float , str gibi) için oluşturulan anahtar, değeridir.
  • Yuvalanmış dict s, list s, tuple s, namedtuple s ve attr s için oluşturulan anahtar, yaprak anahtarların düzleştirilmiş demetidir (bkz. nest.flatten ). (Bu düzleştirmenin bir sonucu olarak, izleme sırasında kullanılandan farklı bir yuvalama yapısına sahip somut bir işlevi çağırmak TypeError ile sonuçlanacaktır).
  • Diğer tüm Python türleri için anahtar, nesneye özeldir. Bu şekilde bir işlev veya yöntem, çağrıldığı her örnek için bağımsız olarak izlenir.

Geri izlemeyi kontrol etme

Function birden fazla iz oluşturduğu geri izleme, TensorFlow'un her giriş kümesi için doğru grafikler oluşturmasını sağlamaya yardımcı olur. Ancak, izleme pahalı bir işlemdir! Function her arama için yeni bir grafiğin izini sürerse, kodunuzun tf.function kullanmadığınıza göre daha yavaş yürütüldüğünü göreceksiniz.

İzleme davranışını kontrol etmek için aşağıdaki teknikleri kullanabilirsiniz:

  • İzlemeyi sınırlamak için input_signature içinde tf.function belirtin.
@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]))
tutucu17 l10n-yer
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([4 1], shape=(2,), dtype=int32)
Caught expected exception 
  <class 'ValueError'>:
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/1851403433.py", line 9, in <module>
    next_collatz(tf.constant([[1, 2], [3, 4]]))
ValueError: Python inputs incompatible with input_signature:
  inputs: (
    tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32))
  input_signature: (
    TensorSpec(shape=(None,), dtype=tf.int32, name=None)).
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/1851403433.py", line 13, in <module>
    next_collatz(tf.constant([1.0, 2.0]))
ValueError: Python inputs incompatible with input_signature:
  inputs: (
    tf.Tensor([1. 2.], shape=(2,), dtype=float32))
  input_signature: (
    TensorSpec(shape=(None,), dtype=tf.int32, name=None)).
  • İzleme yeniden kullanımında esnekliğe izin vermek için tf.TensorSpec bir [Yok] boyutu belirtin.

    TensorFlow, tensörleri şekillerine göre eşleştirdiğinden, joker karakter olarak None boyutunun kullanılması, Function s öğesinin değişken boyutlu girdiler için izleri yeniden kullanmasına olanak tanır. Farklı uzunluktaki dizileriniz veya her grup için farklı boyutlardaki resimleriniz varsa, değişken boyutlu girdiler oluşabilir (örneğin, Transformer ve Deep Dream eğitimlerine bakın).

@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])))
tutucu19 l10n-yer
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)
  • Geri izlemeyi azaltmak için Python bağımsız değişkenlerini Tensörlere yayınlayın.

    Genellikle, Python bağımsız değişkenleri hiperparametreleri ve grafik yapılarını kontrol etmek için kullanılır - örneğin, num_layers=10 veya training=True veya nonlinearity='relu' . Bu nedenle, Python argümanı değişirse, grafiği tekrar izlemeniz mantıklı olur.

    Ancak, grafik yapısını kontrol etmek için bir Python argümanının kullanılmaması mümkündür. Bu durumlarda, Python değerindeki bir değişiklik gereksiz yere yeniden izlemeyi tetikleyebilir. Örneğin, AutoGraph'ın dinamik olarak açacağı bu eğitim döngüsünü ele alalım. Birden çok izlemeye rağmen, oluşturulan grafik aslında aynıdır, bu nedenle yeniden izleme gereksizdir.

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))
tutucu21 l10n-yer
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

Geri izlemeyi zorlamanız gerekirse, yeni bir Function oluşturun. Separate Function nesnelerinin izleri paylaşmamaları garanti edilir.

def f():
  print('Tracing!')
  tf.print('Executing')

tf.function(f)()
tf.function(f)()
tutucu23 l10n-yer
Tracing!
Executing
Tracing!
Executing

Somut fonksiyonların elde edilmesi

Her fonksiyon izlendiğinde, yeni bir somut fonksiyon yaratılır. get_concrete_function kullanarak doğrudan somut bir işlev elde edebilirsiniz.

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)
yer tutucu26 l10n-yer
# You can also call get_concrete_function on an InputSpec
double_strings_from_inputspec = double.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.string))
print(double_strings_from_inputspec(tf.constant("c")))
tf.Tensor(b'cc', shape=(), dtype=string)

Bir ConcreteFunction yazdırmak, girdi bağımsız değişkenlerinin (türlerle birlikte) ve çıktı türünün bir özetini görüntüler.

print(double_strings)
tutucu29 l10n-yer
ConcreteFunction double(a)
  Args:
    a: string Tensor, shape=()
  Returns:
    string Tensor, shape=()

Ayrıca, somut bir işlevin imzasını doğrudan alabilirsiniz.

print(double_strings.structured_input_signature)
print(double_strings.structured_outputs)
tutucu31 l10n-yer
((TensorSpec(shape=(), dtype=tf.string, name='a'),), {})
Tensor("Identity:0", shape=(), dtype=string)

Uyumsuz türlerle somut bir iz kullanmak hata verir

with assert_raises(tf.errors.InvalidArgumentError):
  double_strings(tf.constant(1))
tutucu33 l10n-yer
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/3196284684.py", line 2, in <module>
    double_strings(tf.constant(1))
tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_162 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_162]

Somut bir işlevin giriş imzasında Python argümanlarına özel muamele yapıldığını fark edebilirsiniz. TensorFlow 2.3'ten önce, Python argümanları somut fonksiyonun imzasından basitçe kaldırıldı. TensorFlow 2.3 ile başlayarak, Python bağımsız değişkenleri imzada kalır, ancak izleme sırasında ayarlanan değeri almakla sınırlandırılır.

@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>
yer tutucu36 l10n-yer
assert square(tf.constant(10.0)) == 100

with assert_raises(TypeError):
  square(tf.constant(10.0), b=3)
Caught expected exception 
  <class 'TypeError'>:
Traceback (most recent call last):
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1721, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1765, in _call_with_flat_signature
    raise TypeError(f"{self._flat_signature_summary()} got unexpected "
TypeError: pow(a) got unexpected keyword arguments: b.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/2310937119.py", line 4, in <module>
    square(tf.constant(10.0), b=3)
TypeError: ConcreteFunction pow(a, b) was constructed with int value 2 in b, but was called with int value 3.

Grafiklerin elde edilmesi

Her somut işlev, bir tf.Graph çevresinde çağrılabilir bir sarmalayıcıdır. Gerçek tf.Graph nesnesini almak normalde yapmanız gereken bir şey olmasa da, herhangi bir somut işlevden kolayca elde edebilirsiniz.

graph = double_strings.graph
for node in graph.as_graph_def().node:
  print(f'{node.input} -> {node.name}')
tutucu39 l10n-yer
[] -> a
['a', 'a'] -> add
['add'] -> Identity

hata ayıklama

Genel olarak, hevesli modda kodun hata ayıklaması, tf.function içindekinden daha kolaydır. tf.function ile süslemeden önce kodunuzun istekli modda hatasız yürütülmesini sağlamalısınız. Hata ayıklama sürecine yardımcı olmak için, tf.function öğesini genel olarak devre dışı bırakmak ve yeniden etkinleştirmek için tf.function tf.config.run_functions_eagerly(True) öğesini çağırabilirsiniz.

Yalnızca tf.function içinde görünen sorunları takip ederken, işte bazı ipuçları:

  • Düz eski Python print çağrıları yalnızca izleme sırasında yürütülür ve işleviniz (yeniden) izlendiğinde izlemenize yardımcı olur.
  • tf.print çağrıları her seferinde yürütülür ve yürütme sırasında ara değerleri izlemenize yardımcı olabilir.
  • tf.debugging.enable_check_numerics , NaN'lerin ve Inf'nin nerede oluşturulduğunu izlemenin kolay bir yoludur.
  • pdb ( Python hata ayıklayıcı ), izleme sırasında neler olduğunu anlamanıza yardımcı olabilir. (Uyarı: pdb sizi AutoGraph ile dönüştürülmüş kaynak koduna bırakacaktır.)

Otomatik Grafik dönüşümleri

AutoGraph, tf.function varsayılan olarak açık olan ve Python istekli kodunun bir alt kümesini grafik uyumlu TensorFlow işlemlerine dönüştüren bir kitaplıktır. Bu, if , for , while gibi kontrol akışını içerir.

tf.cond ve tf.while_loop gibi tf.cond operasyonları çalışmaya devam eder, ancak Python'da yazıldığında kontrol akışını yazmak ve anlamak genellikle daha kolaydır.

# 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]))
tutucu41 l10n-yer
[0.666458249 0.713946581 0.723879576 0.330758929 0.184087753]
[0.582645297 0.613145649 0.619306684 0.319202513 0.182036072]
[0.524585426 0.546337605 0.550645113 0.308785647 0.18005164]
[0.481231302 0.497770309 0.501003504 0.299331933 0.178130865]
[0.447229207 0.460361809 0.462906033 0.290701121 0.176270396]
[0.419618756 0.430379033 0.432449728 0.282779962 0.174467146]
[0.396609187 0.405638 0.407366514 0.275476 0.172718227]
[0.377043903 0.384762734 0.386234313 0.268712848 0.17102097]
[0.360137492 0.366836458 0.368109286 0.262426734 0.169372901]
[0.345335096 0.351221472 0.352336824 0.256563932 0.167771652]
[0.332231969 0.337458342 0.338446289 0.251078814 0.166215062]
[0.320524871 0.325206399 0.326089561 0.24593246 0.164701089]
[0.309981436 0.314206958 0.31500268 0.241091311 0.163227797]
[0.300420195 0.304259449 0.304981351 0.236526251 0.161793426]
[0.291697085 0.295205742 0.295864582 0.232211992 0.160396278]
[0.283696055 0.286919087 0.287523568 0.228126258 0.159034774]
[0.276322395 0.279296666 0.27985391 0.224249557 0.157707423]
[0.269497961 0.272254 0.272769839 0.220564634 0.15641281]
[0.263157606 0.265720904 0.266200244 0.21705614 0.155149609]
[0.257246554 0.259638608 0.260085613 0.213710397 0.153916568]
[0.251718313 0.25395745 0.254375577 0.210515186 0.152712509]
[0.246533215 0.248635098 0.249027327 0.207459539 0.151536316]
[0.241657034 0.243635193 0.244004101 0.204533577 0.15038693]
[0.237060249 0.238926381 0.239274174 0.201728329 0.149263337]
[0.232717097 0.234481394 0.234810054 0.199035719 0.148164615]
[0.228605017 0.230276451 0.230587661 0.196448416 0.147089839]
[0.224704206 0.226290658 0.22658591 0.193959698 0.14603813]
[0.220997125 0.222505584 0.222786173 0.191563457 0.145008713]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.21746822, 0.21890487, 0.21917202, 0.18925412, 0.14400077],
      dtype=float32)>

Merak ediyorsanız, imzanın oluşturduğu kodu inceleyebilirsiniz.

print(tf.autograph.to_code(f.python_function))
tutucu43 l10n-yer
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)

Şartlılar

AutoGraph, bazı if <condition> ifadelerini eşdeğer tf.cond çağrılarına dönüştürür. Bu değiştirme, <condition> bir Tensör ise yapılır. Aksi takdirde, if ifadesi Python koşullu olarak yürütülür.

İzleme sırasında bir Python koşulu yürütülür, bu nedenle koşulun tam olarak bir dalı grafiğe eklenir. AutoGraph olmadan, veriye bağlı kontrol akışı varsa, bu izlenen grafik alternatif dalı alamaz.

tf.cond , yürütme zamanında dinamik olarak bir dal seçerek koşulun her iki dalını da izler ve grafiğe ekler. İzlemenin istenmeyen yan etkileri olabilir; Daha fazla bilgi için AutoGraph izleme efektlerine bakın.

@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))
tutucu45 l10n-yer
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 ile dönüştürülmüş if ifadeleriyle ilgili ek kısıtlamalar için başvuru belgelerine bakın.

döngüler

AutoGraph, bazı for ve while ifadelerini tf.while_loop gibi eşdeğer tf.while_loop döngü işlemlerine dönüştürür. Dönüştürülmezse, for veya while döngüsü bir Python döngüsü olarak yürütülür.

Bu ikame aşağıdaki durumlarda yapılır:

  • y'deki for x in y : y bir Tensör ise, tf.while_loop dönüştürün. y bir tf.data.Dataset olduğu özel durumda, tf.data.Dataset işlemlerinin bir kombinasyonu oluşturulur.
  • while <condition> : <condition> bir Tensör ise, tf.while_loop dönüştürün.

İzleme sırasında bir Python döngüsü yürütülür ve döngünün her yinelemesi için tf.Graph ek işlemler eklenir.

Bir TensorFlow döngüsü, döngünün gövdesini izler ve yürütme zamanında kaç yinelemenin çalıştırılacağını dinamik olarak seçer. Döngü gövdesi, oluşturulan tf.Graph yalnızca bir kez görünür.

AutoGraph ile dönüştürülmüş for ve while ifadelerine ilişkin ek kısıtlamalar için başvuru belgelerine bakın.

Python verileri üzerinde döngü

Yaygın bir tuzak, bir tf.function içindeki Python/NumPy verileri üzerinde döngü oluşturmaktır. Bu döngü, izleme işlemi sırasında yürütülecek ve döngünün her yinelemesi için modelinizin bir kopyasını tf.Graph .

Tüm eğitim döngüsünü tf.function içine sarmak istiyorsanız, bunu yapmanın en güvenli yolu, AutoGraph'ın eğitim döngüsünü dinamik olarak açması için verilerinizi bir tf.data.Dataset olarak sarmaktır.

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)))
tutucu47 l10n-yer
train([(1, 1), (1, 1), (1, 1)]) contains 11 nodes in its graph
train([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) contains 32 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph

Python/NumPy verilerini bir Veri Kümesine sararken, tf.data.Dataset.from_generator ile tf.data.Dataset.from_tensors arasında dikkatli olun. İlki, verileri Python'da tutacak ve performans etkileri olabilecek tf.py_function aracılığıyla getirecek, ikincisi ise verilerin bir kopyasını grafikte bellek etkileri olabilecek büyük bir tf.constant() düğümü olarak paketleyecektir.

Dosyalardan TFRecordDataset , CsvDataset vb. aracılığıyla veri okumak, veri tüketmenin en etkili yoludur, çünkü bu durumda TensorFlow, Python'u dahil etmek zorunda kalmadan verilerin eşzamansız yüklenmesini ve önceden getirilmesini yönetebilir. Daha fazla bilgi edinmek için tf.data : TensorFlow giriş işlem hatları oluşturma kılavuzuna bakın.

Bir döngüde değerlerin toplanması

Yaygın bir kalıp, bir döngüden ara değerleri toplamaktır. Normalde bu, bir Python listesine ekleyerek veya bir Python sözlüğüne girdiler ekleyerek gerçekleştirilir. Ancak bunlar Python yan etkileri olduğundan dinamik olarak açılmamış bir döngüde beklendiği gibi çalışmayacaktır. Dinamik olarak açılmamış bir döngüden sonuçları toplamak için tf.TensorArray kullanın.

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]))
tutucu49 l10n-yer
<tf.Tensor: shape=(2, 3, 4), dtype=float32, numpy=
array([[[0.06309307, 0.9938811 , 0.90789986, 0.42136216],
        [0.44997275, 1.9107027 , 1.0716251 , 0.717237  ],
        [0.6026064 , 2.1622117 , 1.4164022 , 1.4153863 ]],

       [[0.04946005, 0.69127274, 0.56848884, 0.22406638],
        [0.8148316 , 1.0278493 , 0.6207781 , 1.1935129 ],
        [0.9178308 , 1.320889  , 0.989761  , 2.0120025 ]]], dtype=float32)>

sınırlamalar

TensorFlow Function , bir Python işlevini bir Function dönüştürürken bilmeniz gereken tasarım gereği birkaç sınırlamaya sahiptir.

Python yan etkilerini yürütme

Yazdırma, listelere ekleme ve globalleri değiştirme gibi yan etkiler, bir Function içinde beklenmedik şekilde davranabilir, bazen iki kez yürütülür veya tümü yürütülmez. Yalnızca bir dizi girdiye sahip bir Function ilk kez çağırdığınızda gerçekleşirler. Daha sonra izlenen tf.Graph , Python kodu çalıştırılmadan yeniden çalıştırılır.

Genel kural, mantığınızdaki Python yan etkilerine güvenmekten kaçınmak ve bunları yalnızca izlemelerinizde hata ayıklamak için kullanmaktır. Aksi takdirde, tf.data , tf.print , tf.summary , tf.Variable.assign ve tf.TensorArray gibi tf.data API'leri, kodunuzun her çağrıda TensorFlow çalışma zamanı tarafından yürütülmesini sağlamanın en iyi yoludur.

@tf.function
def f(x):
  print("Traced with", x)
  tf.print("Executed with", x)

f(1)
f(1)
f(2)
tutucu51 l10n-yer
Traced with 1
Executed with 1
Executed with 1
Traced with 2
Executed with 2

Bir Function öğesinin her çağrılması sırasında Python kodunu yürütmek isterseniz, tf.py_function bir çıkış taramasıdır. tf.py_function öğesinin dezavantajı, taşınabilir olmaması veya özellikle performanslı olmaması, SavedModel ile kaydedilememesi ve dağıtılmış (çoklu GPU, TPU) kurulumlarda iyi çalışmamasıdır. Ayrıca, tf.py_function bağlı olması gerektiğinden, tüm girdileri/çıktıları tensörlere gönderir.

Python global ve serbest değişkenlerini değiştirme

Python genel ve serbest değişkenlerini değiştirmek, Python yan etkisi olarak sayılır, bu nedenle yalnızca izleme sırasında olur.

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
tutucu53 l10n-yer
Python side effect

Bazen beklenmedik davranışları fark etmek çok zordur. Aşağıdaki örnekte, counter bir değişkenin artışını koruması amaçlanmıştır. Ancak bir python tamsayı olduğu ve bir TensorFlow nesnesi olmadığı için değeri ilk izleme sırasında yakalanır. tf.function kullanıldığında, assign_add , temel alınan grafikte koşulsuz olarak kaydedilecektir. Bu nedenle v , tf.function her çağrıldığında 1 artacaktır. Bu sorun, hangi işlemlerin çalıştırılacağını belirlemek için python yan etkileri (örnekteki counter ) kullanıldığında (örnekte assign_add ) Grpah modu Tensorflow kodunu tf.function 2'ye geçirmeye çalışan kullanıcılar arasında yaygındır. ). Genellikle, kullanıcılar bunu ancak şüpheli sayısal sonuçlar gördükten veya beklenenden önemli ölçüde daha düşük performans gördükten sonra fark ederler (örneğin, korunan işlem çok maliyetliyse).

class Model(tf.Module):
  def __init__(self):
    self.v = tf.Variable(0)
    self.counter = 0

  @tf.function
  def __call__(self):
    if self.counter == 0:
      # A python side-effect
      self.counter += 1
      self.v.assign_add(1)

    return self.v

m = Model()
for n in range(3):
  print(m().numpy()) # prints 1, 2, 3
tutucu55 l10n-yer
1
2
3

Beklenen davranışı elde etmek için bir geçici çözüm, işlemleri işlev grafiğinin dışına çıkarmak için tf.init_scope kullanmaktır. Bu, değişken artışının izleme süresi boyunca yalnızca bir kez yapılmasını sağlar. init_scope temizlenmiş kontrol akışı ve gradyan bandı da dahil olmak üzere başka yan etkileri olduğuna dikkat edilmelidir. Bazen init_scope kullanımı gerçekçi bir şekilde yönetilemeyecek kadar karmaşık hale gelebilir.

class Model(tf.Module):
  def __init__(self):
    self.v = tf.Variable(0)
    self.counter = 0

  @tf.function
  def __call__(self):
    if self.counter == 0:
      # Lifts ops out of function-building graphs
      with tf.init_scope():
        self.counter += 1
        self.v.assign_add(1)

    return self.v

m = Model()
for n in range(3):
  print(m().numpy()) # prints 1, 1, 1
tutucu57 l10n-yer
1
1
1

Özetle, genel bir kural olarak, Function dışında yaşayan tamsayılar veya listeler gibi kapsayıcılar gibi python nesnelerini mutasyona uğratmaktan kaçınmalısınız. Bunun yerine argümanları ve TF nesnelerini kullanın. Örneğin, "Bir döngüde değerlerin toplanması" bölümünde, liste benzeri işlemlerin nasıl uygulanabileceğine dair bir örnek vardır.

Bazı durumlarda, bir tf.Variable ise durumu yakalayabilir ve değiştirebilirsiniz. Keras modellerinin ağırlıkları, aynı ConcreteFunction tekrarlanan çağrılarla bu şekilde güncellenir.

Python yineleyicilerini ve oluşturucularını kullanma

Oluşturucular ve yineleyiciler gibi birçok Python özelliği, durumu izlemek için Python çalışma zamanına güvenir. Genel olarak, bu yapılar istekli modda beklendiği gibi çalışsa da, bunlar Python yan etkilerinin örnekleridir ve bu nedenle yalnızca izleme sırasında gerçekleşir.

@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)
tutucu59 l10n-yer
Value: 1
Value: 1
Value: 1

Tıpkı tf.TensorArray liste yapıları için özel bir tf.data.Iterator sahiptir. Genel bir bakış için AutoGraph dönüşümleri bölümüne bakın. Ayrıca, tf.data API, oluşturucu kalıplarının uygulanmasına yardımcı olabilir:

@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)
tutucu61 l10n-yer
Value: 1
Value: 2
Value: 3

Bir tf.function işlevinin tüm çıktıları, dönüş değerleri olmalıdır

tf.Variable s dışında, bir tf.fonksiyonu tüm çıktılarını döndürmelidir. Herhangi bir tensöre bir fonksiyondan dönüş değerlerinden geçmeden doğrudan erişmeye çalışmak "sızıntılara" neden olur.

Örneğin, aşağıdaki işlev tensörü a Python global x aracılığıyla "sızdırır":

x = None

@tf.function
def leaky_function(a):
  global x
  x = a + 1  # Bad - leaks local tensor
  return a + 2

correct_a = leaky_function(tf.constant(1))

print(correct_a.numpy())  # Good - value obtained from function's returns
try:
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
except AttributeError as expected:
  print(expected)
tutucu63 l10n-yer
3
'Tensor' object has no attribute 'numpy'

Bu, sızdırılan değer de döndürülse bile geçerlidir:

@tf.function
def leaky_function(a):
  global x
  x = a + 1  # Bad - leaks local tensor
  return x  # Good - uses local tensor

correct_a = leaky_function(tf.constant(1))

print(correct_a.numpy())  # Good - value obtained from function's returns
try:
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
except AttributeError as expected:
  print(expected)

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

with assert_raises(TypeError):
  captures_leaked_tensor(tf.constant(2))
tutucu65 l10n-yer
2
'Tensor' object has no attribute 'numpy'
Caught expected exception 
  <class 'TypeError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/566849597.py", line 21, in <module>
    captures_leaked_tensor(tf.constant(2))
TypeError: Originated from a graph execution error.

The graph execution error is detected at a node built at (most recent call last):
>>>  File /usr/lib/python3.7/runpy.py, line 193, in _run_module_as_main
>>>  File /usr/lib/python3.7/runpy.py, line 85, in _run_code
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py, line 16, in <module>
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/traitlets/config/application.py, line 846, in launch_instance
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelapp.py, line 677, in start
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tornado/platform/asyncio.py, line 199, in start
>>>  File /usr/lib/python3.7/asyncio/base_events.py, line 534, in run_forever
>>>  File /usr/lib/python3.7/asyncio/base_events.py, line 1771, in _run_once
>>>  File /usr/lib/python3.7/asyncio/events.py, line 88, in _run
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 457, in dispatch_queue
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 446, in process_one
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 353, in dispatch_shell
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/kernelbase.py, line 648, in execute_request
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/ipkernel.py, line 353, in do_execute
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel/zmqshell.py, line 533, in run_cell
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 2902, in run_cell
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 2947, in _run_cell
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/async_helpers.py, line 68, in _pseudo_sync_runner
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3173, in run_cell_async
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3364, in run_ast_nodes
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/IPython/core/interactiveshell.py, line 3444, in run_code
>>>  File /tmp/ipykernel_26244/566849597.py, line 7, in <module>
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 910, in __call__
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 958, in _call
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 781, in _initialize
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3157, in _get_concrete_function_internal_garbage_collected
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3557, in _maybe_define_function
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py, line 3402, in _create_graph_function
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 1143, in func_graph_from_py_func
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py, line 672, in wrapped_fn
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 1125, in autograph_handler
>>>  File /tmp/ipykernel_26244/566849597.py, line 4, in leaky_function
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py, line 1383, in binary_op_wrapper
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/traceback_utils.py, line 150, in error_handler
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py, line 1096, in op_dispatch_handler
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py, line 1737, in _add_dispatch
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/gen_math_ops.py, line 476, in add_v2
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/op_def_library.py, line 746, in _apply_op_helper
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/func_graph.py, line 691, in _create_op_internal
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/ops.py, line 3705, in _create_op_internal
>>>  File /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/ops.py, line 2101, in __init__

Error detected in node 'add' defined at: File "/tmp/ipykernel_26244/566849597.py", line 4, in leaky_function

TypeError: tf.Graph captured an external symbolic tensor. The symbolic tensor 'add:0' created by node 'add' is captured by the tf.Graph being executed as an input. But a tf.Graph is not allowed to take symbolic tensors from another graph as its inputs. Make sure all captured inputs of the executing tf.Graph are not symbolic tensors. Use return values, explicit Python locals or TensorFlow collections to access it. Please see https://www.tensorflow.org/guide/function#all_outputs_of_a_tffunction_must_be_return_values for more information.

Genellikle, Python deyimlerini veya veri yapılarını kullandığınızda bu tür sızıntılar meydana gelir. Erişilemeyen tensörleri sızdırmaya ek olarak, bu tür ifadeler Python yan etkileri olarak sayıldığından ve her işlev çağrısında yürütüleceği garanti edilmediğinden muhtemelen yanlıştır.

Yerel tensörleri sızdırmanın yaygın yolları, harici bir Python koleksiyonunu veya bir nesneyi mutasyona uğratmayı da içerir:

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

Özyinelemeli tf.işlevleri desteklenmez

Özyinelemeli Function desteklenmez ve sonsuz döngülere neden olabilir. Örneğin,

@tf.function
def recursive_fn(n):
  if n > 0:
    return recursive_fn(n - 1)
  else:
    return 1

with assert_raises(Exception):
  recursive_fn(tf.constant(5))  # Bad - maximum recursion error.
tutucu68 l10n-yer
Caught expected exception 
  <class 'Exception'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/2233998312.py", line 9, in <module>
    recursive_fn(tf.constant(5))  # Bad - maximum recursion error.
tensorflow.python.autograph.impl.api.StagingError: in user code:

    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 4, in recursive_fn  *
        return recursive_fn(n - 1)
    File "/tmp/ipykernel_26244/2233998312.py", line 3, in recursive_fn  *
        if n > 0:
    File "/usr/lib/python3.7/abc.py", line 139, in __instancecheck__
        return _abc_instancecheck(cls, instance)

    RecursionError: maximum recursion depth exceeded while calling a Python object

Özyinelemeli bir Function çalışıyor gibi görünse bile, python işlevi birden çok kez izlenir ve performans ima edebilir. Örneğin,

@tf.function
def recursive_fn(n):
  if n > 0:
    print('tracing')
    return recursive_fn(n - 1)
  else:
    return 1

recursive_fn(5)  # Warning - multiple tracings
tutucu70 l10n-yer
tracing
tracing
tracing
tracing
tracing
<tf.Tensor: shape=(), dtype=int32, numpy=1>

Bilinen Sorunlar

Function doğru bir şekilde değerlendirmiyorsa, hata gelecekte düzeltilmesi planlanan bu bilinen sorunlarla açıklanabilir.

Python global ve serbest değişkenlerine bağlı olarak

Function , Python bağımsız değişkeninin yeni bir değeriyle çağrıldığında yeni bir ConcreteFunction oluşturur. Ancak, Python kapanışı, globaller veya o Function yerel olmayanları için bunu yapmaz. Function Function sahip oldukları değerleri kullanmaya devam eder. Bu, normal Python işlevlerinin çalışma biçiminden farklıdır.

Bu nedenle, dış adları kapatmak yerine argümanları kullanan işlevsel bir programlama stili izlemelisiniz.

@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)
yer tutucu73 l10n-yer
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)

Genel bir değeri güncellemenin başka bir yolu, onu bir tf.Variable yapmak ve bunun yerine Variable.assign yöntemini kullanmaktır.

@tf.function
def variable_add():
  return 1 + foo

foo = tf.Variable(1)
print("Variable:", variable_add())
Variable: tf.Tensor(2, shape=(), dtype=int32)
yer tutucu77 l10n-yer
print("Updating the value of `foo` to 100!")
foo.assign(100)
print("Variable:", variable_add())
Updating the value of `foo` to 100!
Variable: tf.Tensor(101, shape=(), dtype=int32)

Python nesnelerine bağlı olarak

Python nesnelerini argüman olarak tf.function , gelecekte düzeltilmesi beklenen bir dizi bilinen soruna sahiptir. Genel olarak, argüman olarak Python ilkel veya tf.nest uyumlu bir yapı kullanıyorsanız veya bir nesnenin farklı bir örneğini bir Function içine iletirseniz tutarlı izlemeye güvenebilirsiniz. Ancak, aynı nesneyi geçtiğinizde ve yalnızca niteliklerini değiştirdiğinizde Function yeni bir iz oluşturmaz.

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)
yer tutucu81 l10n-yer
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)

Güncellenen model orijinal modelle aynı önbellek anahtarına sahip olduğundan, modelin güncellenmiş örneğini değerlendirmek için aynı Function kullanmak hatalı olacaktır.

Bu nedenle, değişken nesne özniteliklerine bağlı kalmaktan kaçınmak veya yeni nesneler oluşturmak için Function yazmanız önerilir.

Bu mümkün değilse, bir geçici çözüm, nesnenizi yeniden izlemeye zorlamak için her değiştirdiğinizde yeni Function s yapmaktır:

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)
yer tutucu85 l10n-yer
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)

Geri izleme pahalı olabileceğinden, tf.Variable s'yi nesne öznitelikleri olarak kullanabilirsiniz; bu öznitelikler mutasyona uğratılabilir (ancak değiştirilemez, dikkatli olun!), benzer bir etki için bir geri izlemeye gerek yoktur.

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)
yer tutucu89 l10n-yer
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.Değişkenleri Oluşturma

Function yalnızca, ilk çağrıda bir kez oluşturulan ve sonraki işlev çağrılarında yeniden kullanılan tekil tf.Variable destekler. Aşağıdaki kod parçacığı, her işlev çağrısında yeni bir tf.Variable oluşturur ve bu da ValueError istisnasıyla sonuçlanır.

Örnek:

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

with assert_raises(ValueError):
  f(1.0)
tutucu92 l10n-yer
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/3018268426.py", line 7, in <module>
    f(1.0)
ValueError: in user code:

    File "/tmp/ipykernel_26244/3018268426.py", line 3, in f  *
        v = tf.Variable(1.0)

    ValueError: tf.function only supports singleton tf.Variables created on the first call. Make sure the tf.Variable is only created once or created outside tf.function. See https://www.tensorflow.org/guide/function#creating_tfvariables for more information.

Bu sınırlamaya geçici bir çözüm bulmak için kullanılan yaygın bir kalıp, Python Yok değeriyle başlamak ve ardından değer Yok ise koşullu olarak tf.Variable oluşturmaktır:

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())
tutucu94 l10n-yer
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)

Birden çok Keras optimize edici ile kullanma

ValueError ile karşılaşabilirsiniz ValueError: tf.function only supports singleton tf.Variables created on the first call. tf.function ile birden fazla tf.function iyileştirici kullanırken. Bu hata, optimize edicilerin degradeleri ilk kez uyguladıklarında dahili olarak tf.Variables oluşturmaları nedeniyle oluşur.

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)
tutucu96 l10n-yer
Calling `train_step` with different optimizer...
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_26244/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_26244/3167358578.py", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    File "/tmp/ipykernel_26244/3167358578.py", line 9, in train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 639, in apply_gradients  **
        self._create_all_weights(var_list)
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 828, in _create_all_weights
        _ = self.iterations
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 835, in __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 995, in iterations
        aggregation=tf.VariableAggregation.ONLY_FIRST_REPLICA)
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py", line 1202, in add_weight
        aggregation=aggregation)
    File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer_utils.py", line 129, in make_variable
        shape=variable_shape if variable_shape else None)

    ValueError: tf.function only supports singleton tf.Variables created on the first call. Make sure the tf.Variable is only created once or created outside tf.function. See https://www.tensorflow.org/guide/function#creating_tfvariables for more information.

Eğitim sırasında optimize ediciyi değiştirmeniz gerekirse, bir geçici çözüm, her optimize edici için doğrudan ConcreteFunction çağıran yeni bir Function oluşturmaktır.

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.

Birden çok Keras modeliyle kullanma

Ayrıca ValueError ile karşılaşabilirsiniz ValueError: tf.function only supports singleton tf.Variables created on the first call. farklı model örneklerini aynı Function geçirirken.

Bu hata, Keras modellerinin ( giriş şekilleri tanımlı olmayan ) ve Keras katmanlarının ilk çağrıldıklarında tf.Variables s oluşturması nedeniyle oluşur. Bu değişkenleri, önceden çağrılmış bir Function içinde başlatmaya çalışıyor olabilirsiniz. Bu hatayı önlemek için, modeli eğitmeden önce tüm ağırlıkları başlatmak için model.build(input_shape) çağırmayı deneyin.

daha fazla okuma

Bir Function nasıl dışa aktarılacağını ve yükleneceğini öğrenmek için SavedModel kılavuzuna bakın. İzlemeden sonra gerçekleştirilen grafik optimizasyonları hakkında daha fazla bilgi edinmek için Grappler kılavuzuna bakın. Veri hattınızı nasıl optimize edeceğinizi ve modelinizin profilini nasıl çıkaracağınızı öğrenmek için Profiler kılavuzuna bakın.