Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Performa lebih baik dengan fungsi tf.

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Di TensorFlow 2, eager execution diaktifkan secara default. Antarmuka pengguna intuitif dan fleksibel (menjalankan operasi satu kali jauh lebih mudah dan lebih cepat), tetapi hal ini dapat mengorbankan kinerja dan penerapan.

Anda dapat menggunakan tf.function untuk membuat grafik dari program Anda. Ini adalah alat transformasi yang membuat grafik aliran data bebas Python dari kode Python Anda. Ini akan membantu Anda membuat model yang berkinerja baik dan portabel, dan diperlukan untuk menggunakan SavedModel .

Panduan ini akan membantu Anda membuat konsep tentang cara kerja tf.function bawah tenda sehingga Anda dapat menggunakannya secara efektif.

Poin utama dan rekomendasi adalah:

  • Debug dalam mode bersemangat, lalu hiasi dengan @tf.function .
  • Jangan mengandalkan efek samping Python seperti mutasi objek atau penambahan daftar.
  • tf.function bekerja paling baik dengan operasi TensorFlow; Panggilan NumPy dan Python diubah menjadi konstanta.

Mendirikan

import tensorflow as tf

Tentukan fungsi pembantu untuk mendemonstrasikan jenis kesalahan yang mungkin Anda temui:

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

Dasar

Pemakaian

Function Anda tetapkan sama seperti operasi inti TensorFlow: Anda dapat menjalankannya dengan penuh semangat; Anda dapat menghitung gradien; dan seterusnya.

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

Anda dapat menggunakan Function di dalam Function lainnya.

@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 bisa lebih cepat daripada kode yang diinginkan, terutama untuk grafik dengan banyak operasi kecil. Tetapi untuk grafik dengan beberapa operasi mahal (seperti konvolusi), Anda mungkin tidak melihat banyak percepatan.

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

Menelusuri

Pengetikan dinamis Python berarti Anda dapat memanggil fungsi dengan berbagai tipe argumen, dan Python dapat melakukan sesuatu yang berbeda di setiap skenario.

Namun, untuk membuat Grafik TensorFlow, dtypes statis dan dimensi bentuk diperlukan. tf.function menjembatani celah ini dengan membungkus fungsi Python untuk membuat objek Function . Berdasarkan masukan yang diberikan, Function memilih grafik yang sesuai untuk masukan yang diberikan, menelusuri kembali fungsi Python seperlunya. Setelah Anda memahami mengapa dan kapan pelacakan terjadi, jauh lebih mudah untuk menggunakan tf.function secara efektif!

Anda bisa memanggil Function dengan argumen tipe berbeda untuk melihat perilaku polimorfik ini beraksi.

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


Perhatikan bahwa jika Anda berulang kali memanggil Function dengan tipe argumen yang sama, TensorFlow akan menggunakan kembali grafik yang dilacak sebelumnya, karena grafik yang dihasilkan akan identik.

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

Anda bisa menggunakan pretty_printed_concrete_signatures() untuk melihat semua jejak yang tersedia:

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

Sejauh ini, Anda telah melihat bahwa tf.function membuat lapisan pengiriman dinamis dalam cache di atas logika pelacakan grafik TensorFlow. Untuk lebih spesifik tentang terminologi:

  • Sebuah tf.Graph adalah tf.Graph mentah, bahasa-agnostik, portabel dari komputasi Anda.
  • ConcreteFunction adalah wrapper yang dieksekusi dengan penuh semangat di sekitar tf.Graph .
  • A Function mengelola cache dari ConcreteFunction dan memilih cache yang tepat untuk input Anda.
  • tf.function membungkus fungsi Python, mengembalikan objek Function .

Memperoleh fungsi konkret

Setiap kali suatu fungsi dilacak, fungsi konkret baru dibuat. Anda bisa langsung mendapatkan fungsi konkret, dengan menggunakan get_concrete_function .

print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.constant("a"))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))

Obtaining concrete trace
Executing traced function
tf.Tensor(b'aa', shape=(), dtype=string)
tf.Tensor(b'bb', shape=(), dtype=string)

# You can also call get_concrete_function on an InputSpec
double_strings_from_inputspec = double.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.string))
print(double_strings_from_inputspec(tf.constant("c")))
Tracing with Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'cc', shape=(), dtype=string)

(Perubahan berikut tersedia di TensorFlow nightly, dan akan tersedia di TensorFlow 2.3.)

Mencetak ConcreteFunction menampilkan ringkasan argumen inputnya (dengan tipe) dan tipe outputnya.

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

Anda juga bisa langsung mengambil tanda tangan fungsi konkret.

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

Menggunakan jejak konkret dengan jenis yang tidak kompatibel akan menimbulkan kesalahan

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]

Anda mungkin memperhatikan bahwa argumen Python diberi perlakuan khusus dalam tanda tangan input fungsi konkret. Sebelum TensorFlow 2.3, argumen Python hanya dihapus dari tanda tangan fungsi konkret. Dimulai dengan TensorFlow 2.3, argumen Python tetap ada dalam tanda tangan, tetapi dibatasi untuk mengambil nilai yang ditetapkan selama pelacakan.

@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

Memperoleh grafik

Setiap fungsi konkret adalah pembungkus yang dapat dipanggil di sekitar tf.Graph . Meskipun mengambil objek tf.Graph sebenarnya bukanlah sesuatu yang biasanya perlu Anda lakukan, Anda dapat memperolehnya dengan mudah dari fungsi konkret apa pun.

graph = double_strings.graph
for node in graph.as_graph_def().node:
  print(f'{node.input} -> {node.name}')

[] -> a
['a', 'a'] -> add
['add'] -> Identity

Debugging

Secara umum, kode debugging lebih mudah dalam mode eager daripada di dalam tf.function . Anda harus memastikan bahwa kode Anda dijalankan tanpa kesalahan dalam mode bersemangat sebelum mendekorasi dengan tf.function . Untuk membantu dalam proses debugging, Anda dapat memanggil tf.config.run_functions_eagerly(True) untuk menonaktifkan dan mengaktifkan kembali tf.function secara global.

Saat melacak masalah yang hanya muncul dalam tf.function , berikut beberapa tip:

  • Panggilan print Python lama yang biasa hanya dijalankan selama pelacakan, membantu Anda melacak ketika fungsi Anda dilacak (ulang).
  • Panggilan tf.print akan dijalankan setiap saat, dan dapat membantu Anda melacak nilai perantara selama eksekusi.
  • tf.debugging.enable_check_numerics adalah cara mudah untuk melacak di mana NaN dan Inf dibuat.
  • pdb dapat membantu Anda memahami apa yang terjadi selama penelusuran. (Peringatan: PDB akan membawa Anda ke kode sumber yang diubah AutoGraph.)

Menelusuri semantik

Aturan kunci cache

Function menentukan apakah akan menggunakan kembali fungsi konkret yang dilacak dengan menghitung kunci cache dari input args dan kwargs.

  • Kunci yang dihasilkan untuk argumen tf.Tensor adalah bentuk dan dtype-nya.
  • Mulai TensorFlow 2.3, kunci yang dihasilkan untuk argumen tf.Variable adalah id() -nya id() .
  • Kunci yang dihasilkan untuk primitif Python adalah nilainya. Kunci yang dihasilkan untuk dict bersarang, list s, tuple s, namedtuple s, dan attr s adalah tupel yang diratakan. (Sebagai hasil dari perataan ini, memanggil fungsi konkret dengan struktur bersarang yang berbeda dari yang digunakan selama penelusuran akan menghasilkan TypeError).
  • Untuk semua jenis Python lainnya, kuncinya didasarkan pada object id() sehingga metode dilacak secara independen untuk setiap instance kelas.

Mengontrol penelusuran kembali

Retracing membantu memastikan bahwa TensorFlow menghasilkan grafik yang benar untuk setiap set input. Namun, penelusuran adalah operasi yang mahal! Jika Function Anda menelusuri kembali grafik baru untuk setiap panggilan, Anda akan mendapati bahwa kode Anda dijalankan lebih lambat daripada jika Anda tidak menggunakan tf.function .

Untuk mengontrol perilaku pelacakan, Anda dapat menggunakan teknik berikut:

  • Tentukan input_signature di tf.function untuk membatasi penelusuran.
@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))

  • Tentukan dimensi [Tidak Ada] di tf.TensorSpec untuk memungkinkan fleksibilitas dalam penggunaan kembali jejak.

    Karena TensorFlow mencocokkan tensor berdasarkan bentuknya, menggunakan dimensi None sebagai karakter pengganti akan memungkinkan Function menggunakan kembali jejak untuk masukan berukuran bervariasi. Input berukuran bervariasi dapat terjadi jika Anda memiliki urutan dengan panjang yang berbeda, atau gambar dengan ukuran berbeda untuk setiap kelompok (Lihat tutorial Transformer dan Deep Dream misalnya).

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

  • Transmisikan argumen Python ke Tensor untuk mengurangi penelusuran ulang.

    Seringkali, argumen Python digunakan untuk mengontrol hyperparameter dan konstruksi grafik - misalnya, num_layers=10 atau training=True atau nonlinearity='relu' . Jadi jika argumen Python berubah, masuk akal jika Anda harus menelusuri kembali grafiknya.

    Namun, ada kemungkinan argumen Python tidak digunakan untuk mengontrol konstruksi grafik. Dalam kasus ini, perubahan nilai Python dapat memicu penelusuran ulang yang tidak perlu. Ambil, misalnya, loop pelatihan ini, yang AutoGraph akan dibuka gulungannya secara dinamis. Meskipun terdapat banyak jejak, grafik yang dihasilkan sebenarnya identik, jadi penelusuran ulang tidak diperlukan.

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

Jika Anda perlu melakukan penelusuran ulang paksa, buat Function baru. Objek Function terpisah dijamin tidak berbagi jejak.

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

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

Efek samping Python

Efek samping Python seperti mencetak, menambahkan ke daftar, dan mutasi global hanya terjadi saat pertama kali Anda memanggil Function dengan satu set input. Setelah itu, tf.Graph dilacak tf.Graph ulang, tanpa mengeksekusi kode Python.

Aturan umumnya adalah dengan hanya menggunakan efek samping Python untuk men-debug jejak Anda. Jika tidak, operasi TensorFlow seperti tf.Variable.assign , tf.print , dan tf.summary adalah cara terbaik untuk memastikan kode Anda akan dilacak dan dijalankan oleh runtime TensorFlow dengan setiap panggilan.

@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

Banyak fitur Python, seperti generator dan iterator, mengandalkan runtime Python untuk melacak status. Secara umum, saat konstruksi ini bekerja seperti yang diharapkan dalam mode eager, banyak hal tak terduga dapat terjadi di dalam sebuah Function .

Untuk memberikan satu contoh, memajukan status iterator adalah efek samping Python dan karena itu hanya terjadi selama penelusuran.

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

Beberapa konstruksi iterasi didukung melalui AutoGraph. Lihat bagian Transformasi AutoGraf untuk ikhtisar.

Jika Anda ingin menjalankan kode Python selama setiap pemanggilan sebuah Function , tf.py_function adalah pintu keluar. Kelemahan dari tf.py_function adalah ia tidak portabel atau sangat berkinerja, juga tidak bekerja dengan baik dalam penyiapan terdistribusi (multi-GPU, TPU). Juga, karena tf.py_function harus disambungkan ke grafik, ini mentransmisikan semua input / output ke tensor.

API seperti tf.gather , tf.stack , dan tf.TensorArray dapat membantu Anda mengimplementasikan pola perulangan umum di TensorFlow native.

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

Variabel

Anda mungkin mengalami kesalahan saat membuat tf.Variable baru dalam suatu fungsi. Kesalahan ini melindungi dari perbedaan perilaku pada panggilan berulang: Dalam mode eager, suatu fungsi membuat variabel baru dengan setiap panggilan, tetapi dalam Function , variabel baru tidak dapat dibuat karena penggunaan kembali jejak.

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


Anda dapat membuat variabel di dalam Function selama variabel tersebut hanya dibuat saat pertama kali fungsi dijalankan.

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)

Kesalahan lain yang mungkin Anda temui adalah variabel yang dikumpulkan sampah. Tidak seperti fungsi Python normal, fungsi konkret hanya mempertahankan WeakRefs ke variabel yang ditutupnya, jadi Anda harus mempertahankan referensi ke variabel apa pun.

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


Transformasi AutoGraph

AutoGraph adalah library yang aktif secara default di tf.function , dan mengubah subset kode eager Python menjadi operasi TensorFlow yang kompatibel dengan grafik. Ini termasuk aliran kontrol seperti if , for , while .

Operasi TensorFlow seperti tf.cond dan tf.while_loop terus berfungsi, tetapi aliran kontrol seringkali lebih mudah untuk ditulis dan dipahami jika ditulis dengan Python.

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

Jika Anda penasaran, Anda dapat memeriksa kode yang dihasilkan oleh tanda tangan.

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)


Persyaratan

AutoGraph akan mengubah beberapa pernyataan if <condition> menjadi panggilan tf.cond setara. Substitusi ini dibuat jika <condition> adalah Tensor. Jika tidak, pernyataan if dijalankan sebagai kondisional Python.

Kondisional Python dieksekusi selama pelacakan, jadi tepat satu cabang kondisional akan ditambahkan ke grafik. Tanpa AutoGraph, grafik yang dilacak ini tidak akan dapat mengambil cabang alternatif jika ada aliran kontrol yang bergantung pada data.

tf.cond melacak dan menambahkan kedua cabang kondisional ke grafik, secara dinamis memilih cabang pada waktu eksekusi. Penelusuran dapat memiliki efek samping yang tidak diinginkan; lihat efek pelacakan AutoGraph untuk selengkapnya.

@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

Lihat dokumentasi referensi untuk batasan tambahan pada pernyataan if yang dikonversi AutoGraf.

Loop

AutoGraph akan mengonversi beberapa pernyataan for dan while menjadi operasi perulangan TensorFlow yang setara, seperti tf.while_loop . tf.while_loop . Jika tidak dikonversi, loop for atau while dijalankan sebagai loop Python.

Substitusi ini dibuat dalam situasi berikut:

Python loop dijalankan selama pelacakan, menambahkan operasi tambahan ke tf.Graph untuk setiap iterasi dari loop.

Loop TensorFlow melacak isi loop, dan secara dinamis memilih berapa banyak iterasi yang akan dijalankan pada waktu eksekusi. Badan loop hanya muncul sekali di tf.Graph dihasilkan.

Lihat dokumentasi referensi untuk batasan tambahan pada pernyataan AutoGraph-convert for and while .

Perulangan atas data Python

Kesalahan umum adalah melakukan loop pada data Python / Numpy dalam sebuah tf.function . Loop ini akan dijalankan selama proses penelusuran, menambahkan salinan model Anda ke tf.Graph untuk setiap iterasi loop.

Jika Anda ingin tf.function seluruh loop pelatihan dalam tf.function , cara teraman untuk melakukannya adalah dengan tf.data.Dataset data Anda sebagai tf.data.Dataset sehingga AutoGraph akan secara dinamis membuka gulungan loop pelatihan.

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

Saat menggabungkan data Python / Numpy dalam Kumpulan Data, perhatikan tf.data.Dataset.from_generator versus tf.data.Dataset.from_tensors . Yang pertama akan menyimpan data dalam Python dan mengambilnya melalui tf.py_function yang dapat memiliki implikasi kinerja, sedangkan yang kedua akan menggabungkan salinan data sebagai satu simpul tf.constant() dalam grafik, yang dapat memiliki implikasi memori.

Membaca data dari file melalui TFRecordDataset / CsvDataset / etc. adalah cara paling efektif untuk mengonsumsi data, karena TensorFlow sendiri dapat mengelola pemuatan dan pemuatan awal data secara asinkron, tanpa harus melibatkan Python. Untuk mempelajari lebih lanjut, lihat panduan tf.data .

Mengumpulkan nilai dalam satu lingkaran

Pola yang umum adalah mengakumulasi nilai antara dari sebuah loop. Biasanya, ini dilakukan dengan menambahkan ke daftar Python atau menambahkan entri ke kamus Python. Namun, karena ini adalah efek samping Python, mereka tidak akan bekerja seperti yang diharapkan dalam loop yang tidak digulung secara dinamis. Gunakan tf.TensorArray untuk mengumpulkan hasil dari loop yang tidak tf.TensorArray secara dinamis.

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

Bacaan lebih lanjut

Untuk mempelajari tentang cara mengekspor dan memuat Function , lihat panduan SavedModel . Untuk mempelajari lebih lanjut tentang pengoptimalan grafik yang dilakukan setelah pelacakan, lihat panduan Grappler . Untuk mempelajari cara mengoptimalkan pipeline data dan membuat profil model Anda, lihat panduan Profiler .