Bu sayfa, Cloud Translation API ile çevrilmiştir.
Switch to English

Tf.function ile daha iyi performans

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

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 daha hızlıdır), ancak bu performans ve devreye alınabilirlik pahasına olabilir.

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

Bu kılavuz, tf.function başlık altında nasıl çalıştığını kavramsallaştırmanıza yardımcı olacak, böylece onu etkili bir şekilde kullanabilirsiniz.

Ana çıkarımlar ve öneriler şunlardır:

  • İstekli modda hata ayıklayın, ardından @tf.function ile @tf.function .
  • Nesne mutasyonu veya liste ekleri gibi Python yan etkilerine güvenmeyin.
  • tf.function en iyi tf.function ç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 , tıpkı çekirdek bir TensorFlow işlemi gibidir: Hevesle çalıştırabilirsiniz; degradeleri hesaplayabilirsiniz; ve bunun gibi.

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

Diğer Function içindeki Function 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]))
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
       [3., 3.],
       [3., 3.]], dtype=float32)>

Function , özellikle çok sayıda 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 (evrişimler 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")
Eager conv: 0.002407395000091128
Function conv: 0.004000883000117028
Note how there's not much difference in performance for convolutions

İzleme

Python'un dinamik yazımı, fonksiyonları çeşitli argüman türleriyle çağırabileceğiniz anlamına gelir ve Python her senaryoda farklı bir şey yapabilir.

Yine de, bir TensorFlow Grafiği oluşturmak için statik dtypes ve şekil boyutları gereklidir. tf.function bir Function nesnesi oluşturmak için bir Python işlevini sarmalayarak bu boşluğu kapatır. Verilen girdilere bağlı olarak, Function , Python işlevini gerektiği gibi yeniden izleyerek, verilen girdiler için uygun grafiği seçer. tf.function neden ve ne zaman gerçekleştiğini anladıktan sonra, tf.function etkili bir şekilde kullanmak çok daha kolaydır!

Bu polimorfik davranışı eylem halinde görmek için farklı türde argümanlar içeren bir Function çağırabilirsiniz.

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


Aynı bağımsız değişken türüne sahip bir Function tekrar tekrar çağırırsanız, TensorFlow'un, oluşturulan grafik aynı olacağından daha önce izlenen bir grafiği yeniden kullanacağını unutmayın.

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

Tüm mevcut izleri görmek için pretty_printed_concrete_signatures() kullanabilirsiniz:

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

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

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

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

  • Bir tf.Graph , hesaplamanızın ham, dilden bağımsız, taşınabilir temsilidir.
  • ConcreteFunction , bir tf.Graph etrafında hevesle çalışan bir sarmalayıcıdır.
  • Bir Function , bir ConcreteFunction önbelleğini yönetir ve girdileriniz için doğru olanı seçer.
  • tf.function bir Python işlevini tf.function bir Function nesnesi döndürür.

Somut işlevler elde etmek

Bir işlev her izlendiğinde, yeni bir somut işlev oluşturulur. get_concrete_function işlevini 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)

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

(Aşağıdaki değişiklik TensorFlow'da her gece mevcuttur ve TensorFlow 2.3'te kullanıma sunulacaktır.)

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)
ConcreteFunction double(a)
  Args:
    a: string Tensor, shape=()
  Returns:
    string Tensor, shape=()

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

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

Uyumsuz türlere sahip somut bir iz kullanmak bir hataya neden olur

with assert_raises(tf.errors.InvalidArgumentError):
  double_strings(tf.constant(1))
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>:

Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-e4e2860a4364>", line 2, in <module>
    double_strings(tf.constant(1))
tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_168 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_168]

Python argümanlarına somut bir fonksiyonun girdi imzasında özel muamele gördüğünü fark edebilirsiniz. TensorFlow 2.3'ten önce, Python argümanları somut fonksiyonun imzasından basitçe kaldırılıyordu. TensorFlow 2.3 ile başlayarak, Python argümanları imzada kalır, ancak izleme sırasında ayarlanan değeri almak için 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>

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-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d163f3d206cb>", line 4, in <module>
    square(tf.constant(10.0), b=3)
TypeError: ConcreteFunction pow(a, b) was constructed with int value 2 in b, but was called with int value 3

Grafikler elde etmek

Her somut işlev, bir tf.Graph etrafında ç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}')
[] -> a
['a', 'a'] -> add
['add'] -> Identity

Hata ayıklama

Genel olarak, hata ayıklama kodu istekli modda tf.function içinden daha tf.function . tf.function ile dekorasyon yapmadan önce kodunuzun istekli modda hatasız çalıştığından emin tf.function . Hata ayıklama sürecine yardımcı olmak için, tf.config.run_functions_eagerly(True) işlevini global olarak devre dışı bırakmak ve yeniden etkinleştirmek için tf.config.run_functions_eagerly(True) işlevini tf.function .

Yalnızca tf.function görünen sorunları takip tf.function , 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 , tf.debugging.enable_check_numerics nerede oluşturulduğunu tf.debugging.enable_check_numerics kolay bir yoludur.
  • pdb , izleme sırasında neler olduğunu anlamanıza yardımcı olabilir. (Uyarı: PDB sizi AutoGraph'a dönüştürülmüş kaynak koduna bırakacaktır.)

Anlambilim izleme

Önbellek anahtarı kuralları

Bir Function , bir girdinin bağımsız değişkenlerinden ve anahtar sözcüklerinden bir önbellek anahtarı hesaplayarak izlenen somut bir işlevin yeniden kullanılıp kullanılmayacağını belirler.

  • Bir tf.Tensor argümanı için üretilen anahtar, şekli ve dtype'idir.
  • TensorFlow 2.3'ten başlayarak, bir tf.Variable bağımsız değişkeni için oluşturulan anahtar, id() .
  • Bir Python ilkeli için üretilen anahtar, değeridir. İç içe geçmiş dict s, list s, tuple s, namedtuple s ve attr s için üretilen anahtar düzleştirilmiş namedtuple . (Bu düzleştirmenin bir sonucu olarak, izleme sırasında kullanılandan farklı bir iç içe geçme yapısına sahip somut bir işlevi çağırmak TypeError ile sonuçlanacaktır).
  • Diğer tüm Python türleri için, anahtarlar nesne id() dayanır, böylece yöntemler bir sınıfın her örneği için bağımsız olarak izlenir.

Yeniden izlemeyi kontrol etme

Yeniden izleme, TensorFlow'un her girdi kümesi için doğru grafikler oluşturmasına yardımcı olur. Ancak, izleme pahalı bir işlemdir! Function her çağrı için yeni bir grafiği yeniden tf.function kullanmadığınız tf.function göre daha yavaş tf.function .

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

  • input_signature sınırlandırmak için input_signature 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])))
# 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-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-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-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-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))

  • İz yeniden kullanımında esneklik sağlamak için tf.TensorSpec içinde bir [Yok] boyutu belirtin.

    TensorFlow tensörleri şekillerine göre eşleştirdiğinden, joker karakter olarak None boyutunun kullanılması, Function ın değişken boyutlu girdiler için izleri yeniden kullanmasına izin verir. Her grup için farklı uzunlukta dizileriniz veya farklı boyutlarda görüntüleriniz varsa değişken boyutlu girdi 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])))
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)

  • Yeniden izlemeyi azaltmak için Python argümanlarını Tensörlere aktarın.

    Python argümanları genellikle hiperparametreleri ve grafik yapılarını kontrol etmek için kullanılır - örneğin, num_layers=10 veya training=True veya nonlinearity='relu' . Yani Python argümanı değişirse, grafiği yeniden izlemeniz mantıklı olur.

    Bununla birlikte, grafik yapısını kontrol etmek için bir Python argümanı kullanılmıyor olabilir. Bu durumlarda, Python değerindeki bir değişiklik gereksiz yeniden izlemeyi tetikleyebilir. Örneğin, AutoGraph'ın dinamik olarak açacağı bu eğitim döngüsünü ele alalım. Birden fazla 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))
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

Yeniden izlemeyi zorlamanız gerekirse yeni bir Function . Ayrı Function nesnelerinin izleri paylaşmamaları garanti edilir.

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

tf.function(f)()
tf.function(f)()
Tracing!
Executing
Tracing!
Executing

Python yan etkileri

Yazdırma, listelere ekleme ve globalleri değiştirme gibi Python yan etkileri, yalnızca bir dizi girdiyle bir Function ilk çağırdığınızda gerçekleşir. Daha sonra, izlenen tf.Graph , Python kodu çalıştırılmadan yeniden çalıştırılır.

Genel kural, izlerinizde hata ayıklamak için yalnızca Python yan etkilerini kullanmaktır. Aksi takdirde, tf.Variable.assign, tf.print ve tf.summary gibi tf.Variable.assign tf.print , kodunuzun her çağrıda TensorFlow çalışma zamanı tarafından izlenmesini ve 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)
Traced with 1
Executed with 1
Executed with 1
Traced with 2
Executed with 2

Üreteçler ve yineleyiciler gibi birçok Python özelliği, durumu takip etmek için Python çalışma zamanına güvenir. Genel olarak, bu yapılar istekli modda beklendiği gibi çalışırken, bir Function içinde pek çok beklenmedik şey olabilir.

Bir örnek vermek gerekirse, ilerleyen yineleyici durumu bir Python yan etkisidir ve bu nedenle yalnızca izleme sırasında gerçekleşir.

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

Bazı yineleme yapıları AutoGraph aracılığıyla desteklenir. Genel bakış için AutoGraph Dönüşümleri bölümüne bakın.

Bir her çağrı sırasında Python kodu çalıştırmak istiyorsanız Function , tf.py_function bir çıkış kapak olduğunu. tf.py_function dezavantajı, taşınabilir olmaması veya özellikle performans göstermesi ve dağıtılmış (çoklu GPU, TPU) kurulumlarda iyi çalışmamasıdır. Ayrıca, tf.py_function grafiğe bağlanması gerektiğinden, tüm girdileri / çıktıları tensörlere çevirir.

tf.gather , tf.stack ve tf.TensorArray gibi tf.gather , yerel TensorFlow'da ortak döngü kalıpları uygulamanıza yardımcı olabilir.

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

Değişkenler

Bir fonksiyonda yeni bir tf.Variable oluştururken bir hatayla karşılaşabilirsiniz. Bu hata, tekrarlanan aramalarda davranış farklılaşmasına karşı koruma sağlar: İstekli modda, bir işlev her aramada yeni bir değişken oluşturur, ancak bir Function , izlemenin yeniden kullanılması nedeniyle yeni bir değişken oluşturulamayabilir.

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

    <ipython-input-1-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.


Bu değişkenler yalnızca işlev ilk kez çalıştırıldığında oluşturulduğu sürece Function içinde değişkenler oluşturabilirsiniz.

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)

Karşılaşabileceğiniz başka bir hata, çöpte toplanan bir değişkendir. Normal Python işlevlerinden farklı olarak, somut işlevler yalnızca kapattıkları değişkenlere yönelik WeakRef'leri tutar, bu nedenle herhangi bir değişkene yönelik bir referans tutmanız gerekir.

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-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-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-1-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-1-304a18524b57>:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_514]

Function call stack:
f -> f


Otomatik Grafik Dönüşümleri

AutoGraph tf fonksiyonunda varsayılan olarak tf.function 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 işlemleri çalışmaya devam eder, ancak kontrol akışının Python'da yazıldığında yazılması ve anlaşılması genellikle daha kolaydır.

# 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.224704742 0.895507693 0.0398198366 0.98112452 0.278468847]
[0.220997646 0.71410346 0.0397988036 0.753552318 0.271487355]
[0.217468739 0.61324358 0.0397778042 0.637263417 0.265008271]
[0.214104146 0.546406269 0.0397568382 0.563033342 0.258973926]
[0.210891485 0.497821957 0.0397359058 0.510224521 0.253335565]
[0.207819641 0.460402519 0.0397150069 0.470120102 0.248051569]
[0.204878598 0.430412233 0.0396941416 0.438296348 0.243086234]
[0.202059314 0.405665785 0.039673306 0.412231296 0.2384087]
[0.199353606 0.384786367 0.039652504 0.39036563 0.23399213]
[0.196754038 0.366856933 0.0396317355 0.371675402 0.229813099]
[0.194253832 0.351239443 0.039611 0.355456293 0.225851]
[0.191846803 0.337474287 0.0395902954 0.341205537 0.222087651]
[0.189527303 0.325220674 0.0395696238 0.3285532 0.218506947]
[0.187290132 0.314219803 0.0395489857 0.317220151 0.215094551]
[0.185130537 0.304271102 0.0395283774 0.30699119 0.211837649]
[0.183044136 0.295216352 0.0395078026 0.297697395 0.208724767]
[0.181026861 0.286928833 0.0394872613 0.289204 0.205745578]

<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.17907499, 0.27930567, 0.03946675, 0.281402  , 0.20289075],
      dtype=float32)>

Merak ediyorsanız, ürettiği kod imzasını inceleyebilirsiniz.

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)


Ş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 bir Python koşullu olarak çalıştırılır.

Bir Python koşullu izleme sırasında çalıştırılır, bu nedenle grafiğe koşulun tam olarak bir dalı eklenir. AutoGraph olmadan, bu izlenen grafik, veriye bağlı kontrol akışı varsa alternatif dalı alamaz.

tf.cond , yürütme zamanında dinamik olarak bir dal seçerek grafiğe koşulun her iki dalını da izler ve ekler. İzlemenin istenmeyen yan etkileri olabilir; Daha fazlası 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))
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 deyimleriyle ilgili ek kısıtlamalar için referans belgelerine bakın.

Döngüler

AutoGraph, bazı for ve while ifadelerini tf.while_loop . tf.while_loop gibi eşdeğer TensorFlow döngü işlemlerine tf.while_loop . 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:

Bir Python döngüsü izleme sırasında tf.Graph ve döngünün her yinelemesi için tf.Graph ek işlemler tf.Graph .

TensorFlow döngüsü, döngünün gövdesini izler ve yürütme zamanında kaç yineleme ç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 deyimleriyle ilgili ek kısıtlamalar için referans belgelerine bakın.

Python verileri üzerinden döngü

Yaygın bir tuzak, bir tf.function içinde Python / Numpy verileri üzerinde döngü tf.function . Bu döngü izleme işlemi sırasında yürütülür 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 sarmalamak istiyorsanız, bunu yapmanın en güvenli yolu, verilerinizitf.data.Dataset olarak sarmalayarak AutoGraph'ın egzersiz döngüsünü dinamik olarak açmasını sağlamaktı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)))
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

Bir Veri Kümesinde Python / Numpy verilerini tf.data.Dataset.from_generator , tf.data.Dataset.from_generator ile tf.data.Dataset.from_tensors dikkatli tf.data.Dataset.from_tensors . İlki, verileri Python'da tutacak ve performansı etkileyebilecek tf.py_function aracılığıyla tf.py_function , ikincisi ise verilerin bir kopyasını grafikte bellek etkilerine sahip olabilecek büyük bir tf.constant() düğümü olarak tf.constant() .

TFRecordDataset / CsvDataset / etc aracılığıyla dosyalardan veri okuma. o zaman TensorFlow, Python'u dahil etmek zorunda kalmadan verilerin eşzamansız yüklenmesini ve önceden getirilmesini yönetebildiğinden, verileri tüketmenin en etkili yoludur. Daha fazla bilgi edinmek için tf.data kılavuzuna bakın .

Bir döngüde değerleri toplamak

Yaygın bir model, bir döngüden ara değerleri toplamaktır. Normalde bu, bir Python listesine eklenerek veya bir Python sözlüğüne girişler eklenerek gerçekleştirilir. Ancak bunlar Python yan etkileri olduğundan, dinamik olarak kaydırılmamış bir döngüde beklendiği gibi çalışmayacaktır. tf.TensorArray olarak tf.TensorArray sonuçları tf.TensorArray 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]))
<tf.Tensor: shape=(2, 3, 4), dtype=float32, numpy=
array([[[0.9854791 , 0.5162524 , 0.14062047, 0.04950547],
        [1.8820469 , 0.67421603, 0.40786874, 0.7679055 ],
        [2.8815444 , 1.1567757 , 1.0627073 , 0.8880433 ]],

       [[0.94119024, 0.19776726, 0.24890792, 0.4663092 ],
        [1.4591933 , 1.123581  , 0.35438073, 1.4392309 ],
        [2.0026946 , 1.9165647 , 0.37988353, 1.8128917 ]]], dtype=float32)>

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 ardışık düzeninizi nasıl optimize edeceğinizi ve modelinizin profilini nasıl optimize edeceğinizi öğrenmek için Profiler kılavuzuna bakın .