Catat tanggalnya! Google I / O mengembalikan 18-20 Mei Daftar sekarang
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 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-dasar

Pemakaian

Function Anda definisikan (misalnya dengan menerapkan @tf.function dekorator) sama seperti operasi inti TensorFlow: Anda bisa mengeksekusinya dengan penuh semangat; Anda dapat menghitung gradien; dan seterusnya.

@tf.function  # The decorator converts `add` into a `Function`.
def add(a, b):
  return a + b

add(tf.ones([2, 2]), tf.ones([2, 2]))  #  [[2., 2.], [2., 2.]]
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[2., 2.],
       [2., 2.]], dtype=float32)>
v = tf.Variable(1.0)
with tf.GradientTape() as tape:
  result = add(v, 1.0)
tape.gradient(result, v)
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>

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

Menelusuri

Bagian ini memaparkan bagaimana Function bekerja di balik terpal, termasuk detail implementasi yang dapat berubah di masa mendatang . Namun, setelah Anda memahami mengapa dan kapan pelacakan terjadi, jauh lebih mudah menggunakan tf.function secara efektif!

Apa itu "penelusuran"?

Function menjalankan program Anda dalam Grafik TensorFlow . Namun, tf.Graph tidak dapat mewakili semua hal yang Anda tulis dalam program TensorFlow yang bersemangat. Misalnya, Python mendukung polimorfisme, tetapi tf.Graph memerlukan inputnya untuk memiliki tipe data dan dimensi yang ditentukan. Atau Anda dapat melakukan tugas sampingan seperti membaca argumen baris perintah, memunculkan kesalahan, atau bekerja dengan objek Python yang lebih kompleks; tak satu pun dari hal-hal ini dapat berjalan di tf.Graph .

Function menjembatani celah ini dengan memisahkan kode Anda dalam dua tahap:

1) Pada tahap pertama, disebut sebagai " tracing ", Function membuat tf.Graph baru. Kode Python berjalan normal, tetapi semua operasi TensorFlow (seperti menambahkan dua Tensor) ditunda : mereka ditangkap oleh tf.Graph dan tidak dijalankan.

2) Pada tahap kedua, tf.Graph yang berisi segala sesuatu yang ditangguhkan pada tahap pertama dijalankan. Tahap ini jauh lebih cepat daripada tahap penelusuran.

Bergantung pada inputnya, Function tidak selalu menjalankan tahap pertama saat dipanggil. Lihat "Aturan penelusuran" di bawah untuk lebih memahami bagaimana cara membuat keputusan tersebut. Melewati tahap pertama dan hanya menjalankan tahap kedua akan memberi Anda performa tinggi TensorFlow.

Ketika Function memutuskan untuk melacak, tahapan pelacakan segera diikuti oleh tahap kedua, jadi memanggil Function membuat dan menjalankan tf.Graph . Nanti Anda akan melihat bagaimana Anda dapat menjalankan hanya tahap penelusuran dengan get_concrete_function .

Saat kami mengirimkan argumen dari tipe yang berbeda ke dalam sebuah Function , kedua tahapan tersebut dijalankan:

@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 melewati tahap pelacakan dan 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: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

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

double(a)
  Args:
    a: int32 Tensor, shape=()
  Returns:
    int32 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, tanpa bahasa, dan portabel dari komputasi TensorFlow.
  • ConcreteFunction membungkus tf.Graph .
  • A Function mengelola cache dari ConcreteFunction dan memilih cache yang tepat untuk input Anda.
  • tf.function membungkus fungsi Python, mengembalikan objek Function .
  • Tracing membuat tf.Graph dan membungkusnya dalam ConcreteFunction , juga dikenal sebagai jejak.

Aturan penelusuran

Sebuah Function menentukan apakah akan menggunakan kembali ConcreteFunction dilacak dengan menghitung kunci cache dari input args dan kwargs. Kunci cache adalah kunci yang mengidentifikasi ConcreteFunction berdasarkan input args dan kwargs dari panggilan Function , menurut aturan berikut (yang dapat berubah):

  • Kunci yang dihasilkan untuk tf.Tensor adalah bentuk dan dtype-nya.
  • Kunci yang dihasilkan untuk tf.Variable adalah id variabel unik.
  • Kunci yang dihasilkan untuk primitif Python (seperti int , float , str ) adalah nilainya.
  • Kunci yang dihasilkan untuk dict bersarang, list s, tuple s, namedtuple s, dan attr s adalah tupel kunci daun yang diratakan (lihatnest.flatten ). (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 adalah unik untuk objek tersebut. Dengan cara ini sebuah fungsi atau metode dilacak secara independen untuk setiap instance yang dipanggil dengannya.

Mengontrol penelusuran kembali

Retracing, yaitu ketika Function Anda membuat lebih dari satu pelacakan, 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 menemukan 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 ulang 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 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 grafik tersebut.

    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

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)

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_162 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_162]

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 1683, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 1728, 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

Mendapatkan 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 antara 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.)

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 sering kali 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.928048491 0.537333608 0.319427252 0.414729953 0.138620138]
[0.729682684 0.490966946 0.308988899 0.392481416 0.137739]
[0.62287122 0.454983532 0.299516946 0.373497456 0.136874482]
[0.553123951 0.425986826 0.290870458 0.357047111 0.13602607]
[0.502857924 0.401961982 0.282935768 0.342610359 0.135193244]
[0.464361787 0.381626487 0.27562 0.329805791 0.134375557]
[0.433632493 0.364119112 0.268846452 0.318346262 0.133572534]
[0.408352554 0.348837078 0.262551099 0.308010817 0.132783771]
[0.387072921 0.335343778 0.256680071 0.298626363 0.132008836]
[0.368834078 0.32331419 0.251187652 0.290055037 0.131247327]
[0.352971435 0.312500536 0.246034727 0.282185435 0.130498841]
[0.339008093 0.302710205 0.241187632 0.274926543 0.129763052]
[0.326591551 0.293790847 0.236617178 0.26820302 0.129039586]
[0.315454811 0.285620153 0.232297987 0.261951953 0.128328085]
[0.305391371 0.278098613 0.228207797 0.256120354 0.127628237]
[0.296238661 0.27114439 0.224326983 0.250663161 0.126939729]
[0.287866682 0.264689356 0.220638305 0.245541915 0.126262262]
[0.280170113 0.25867638 0.217126325 0.240723446 0.12559554]
[0.273062497 0.253057063 0.213777393 0.236178935 0.124939285]
[0.266472191 0.247790173 0.210579231 0.231883332 0.124293216]
[0.260339141 0.242840245 0.207520843 0.227814704 0.12365707]
[0.254612684 0.238176659 0.204592302 0.223953649 0.123030603]
[0.249249727 0.23377277 0.201784685 0.220283121 0.122413576]
[0.244213238 0.229605287 0.199089885 0.216787875 0.12180575]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.23947136, 0.22565375, 0.19650048, 0.21345437, 0.12120689],
      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 penelusuran, 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. Menelusuri 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:

Sebuah loop Python 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 di 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 paling aman untuk melakukannya adalah dengantf.data.Dataset data Anda sebagaitf.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 10 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 10 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.8216245 , 0.29562855, 0.379112  , 0.49940717],
        [1.6473945 , 1.039927  , 1.3268942 , 0.5298227 ],
        [2.4393063 , 1.1283967 , 2.087479  , 1.2748951 ]],

       [[0.08016336, 0.73864746, 0.33738315, 0.4542967 ],
        [0.7459605 , 1.307698  , 1.1588445 , 0.9293362 ],
        [1.3752056 , 1.6133544 , 1.8199729 , 1.7356051 ]]], dtype=float32)>

Batasan

Function TensorFlow memiliki beberapa batasan berdasarkan desain yang harus Anda waspadai saat mengonversi fungsi Python ke sebuah Function .

Menjalankan efek samping Python

Efek samping, seperti mencetak, menambahkan ke daftar, dan mutasi global, dapat berperilaku tidak terduga di dalam Function , terkadang mengeksekusi dua kali atau tidak semua. Mereka hanya terjadi saat pertama kali Anda memanggil Function dengan sekumpulan input. Setelah itu, tf.Graph dilacak tf.Graph ulang, tanpa mengeksekusi kode Python.

Aturan umumnya adalah menghindari mengandalkan efek samping Python dalam logika Anda dan hanya menggunakannya untuk men-debug jejak Anda. Jika tidak, TensorFlow API seperti tf.data , tf.print , tf.summary , tf.Variable.assign , dan tf.TensorArray adalah cara terbaik untuk memastikan kode Anda akan 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

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

Mengubah variabel global dan gratis Python

Mengubah variabel global dan gratis Python dihitung sebagai efek samping Python, jadi itu hanya terjadi selama penelusuran.

external_list = []

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

side_effect(1)
side_effect(1)
side_effect(1)
# The list append only happened once!
assert len(external_list) == 1
Python side effect

Anda harus menghindari mutasi kontainer seperti list, dicts, objek lain yang berada di luar Function . Sebagai gantinya, gunakan argumen dan objek TF. Misalnya, bagian "Mengumpulkan nilai dalam satu lingkaran" memiliki satu contoh bagaimana operasi seperti daftar dapat diimplementasikan.

Anda dapat, dalam beberapa kasus, menangkap dan memanipulasi status jika itu adalah tf.Variable . Beginilah cara bobot model Keras diperbarui dengan panggilan berulang ke ConcreteFunction sama.

Menggunakan iterator dan generator Python

Banyak fitur Python, seperti generator dan iterator, mengandalkan runtime Python untuk melacak status. Secara umum, sementara konstruksi ini bekerja seperti yang diharapkan dalam mode bersemangat, mereka adalah contoh efek samping Python dan karena itu hanya terjadi selama penelusuran.

@tf.function
def buggy_consume_next(iterator):
  tf.print("Value:", next(iterator))

iterator = iter([1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Value: 1
Value: 1
Value: 1

Sama seperti bagaimana TensorFlow telah menjadi khusus tf.TensorArray untuk daftar konstruksi, ia memiliki spesialisasi tf.data.Iterator untuk iterasi konstruksi. Lihat bagian Transformasi AutoGraf untuk ikhtisar. Selain itu, API tf.data dapat membantu mengimplementasikan pola generator:

@tf.function
def good_consume_next(iterator):
  # This is ok, iterator is a tf.data.Iterator
  tf.print("Value:", next(iterator))

ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
iterator = iter(ds)
good_consume_next(iterator)
good_consume_next(iterator)
good_consume_next(iterator)
Value: 1
Value: 2
Value: 3

Menghapus tf. Variabel di antara panggilan Function

Kesalahan lain yang mungkin Anda temui adalah variabel yang dikumpulkan sampah. ConcreteFunction 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))

# The original variable object gets garbage collected, since there are no more
# references to it.
external_var = tf.Variable(4)
print()
print("Calling concrete function after garbage collecting its closed Variable...")
with assert_raises(tf.errors.FailedPreconditionError):
  traced_f(4)
Calling concrete function...
tf.Tensor(12, shape=(), dtype=int32)

Calling concrete function after garbage collecting its closed Variable...
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.FailedPreconditionError'>:
Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-9a93d2e07632>", line 16, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found.
  (0) Failed precondition:  Error while reading resource variable _AnonymousVar3 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-1-9a93d2e07632>:4) ]]
  (1) Failed precondition:  Error while reading resource variable _AnonymousVar3 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-1-9a93d2e07632>:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_782]

Function call stack:
f -> f

Masalah Dikenal

Jika Function Anda tidak mengevaluasi dengan benar, kesalahan tersebut mungkin disebabkan oleh masalah umum yang direncanakan untuk diperbaiki di masa mendatang.

Bergantung pada variabel global dan gratis Python

Function membuat ConcreteFunction baru ketika dipanggil dengan nilai baru dari argumen Python. Namun, itu tidak melakukannya untuk penutupan Python, global, atau nonlokal dari Function . Jika nilainya berubah di antara panggilan ke Function , Function akan tetap menggunakan nilai yang mereka miliki saat dilacak. Ini berbeda dari cara kerja fungsi Python biasa.

Oleh karena itu, kami merekomendasikan gaya pemrograman fungsional yang menggunakan argumen daripada menutup nama luar.

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

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

foo = 1
print("Buggy:", buggy_add())
print("Correct:", recommended_add(foo))
Buggy: tf.Tensor(2, shape=(), dtype=int32)
Correct: tf.Tensor(2, shape=(), dtype=int32)
print("Updating the value of `foo` to 100!")
foo = 100
print("Buggy:", buggy_add())  # Did not change!
print("Correct:", recommended_add(foo))
Updating the value of `foo` to 100!
Buggy: tf.Tensor(2, shape=(), dtype=int32)
Correct: tf.Tensor(101, shape=(), dtype=int32)

Anda dapat menutup nama luar, selama Anda tidak memperbarui nilainya.

Bergantung pada objek Python

Rekomendasi untuk meneruskan objek Python sebagai argumen ke tf.function memiliki sejumlah masalah yang diketahui, yang diharapkan dapat diperbaiki di masa mendatang. Secara umum, Anda dapat mengandalkan tracing konsisten jika Anda menggunakan primitif atau Python tf.nest struktur -yang kompatibel sebagai argumen atau lulus dalam contoh yang berbeda dari sebuah objek menjadi Function . Namun, Function tidak akan membuat jejak baru saat Anda meneruskan objek yang sama dan hanya mengubah atributnya .

class SimpleModel(tf.Module):
  def __init__(self):
    # These values are *not* tf.Variables.
    self.bias = 0.
    self.weight = 2.

@tf.function
def evaluate(model, x):
  return model.weight * x + model.bias

simple_model = SimpleModel()
x = tf.constant(10.)
print(evaluate(simple_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
simple_model.bias += 5.0
print(evaluate(simple_model, x))  # Didn't change :(
Adding bias!
tf.Tensor(20.0, shape=(), dtype=float32)

Menggunakan Function sama untuk mengevaluasi contoh model yang diperbarui akan bermasalah karena model yang diperbarui memiliki kunci cache yang sama dengan model aslinya.

Oleh karena itu, kami merekomendasikan agar Anda menulis Function Anda untuk menghindari bergantung pada atribut objek yang bisa berubah atau membuat objek baru.

Jika itu tidak memungkinkan, salah satu solusinya adalah membuat Function baru setiap kali Anda memodifikasi objek untuk memaksa penelusuran kembali:

def evaluate(model, x):
  return model.weight * x + model.bias

new_model = SimpleModel()
evaluate_no_bias = tf.function(evaluate).get_concrete_function(new_model, x)
# Don't pass in `new_model`, `Function` already captured its state during tracing.
print(evaluate_no_bias(x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
new_model.bias += 5.0
# Create new Function and ConcreteFunction since you modified new_model.
evaluate_with_bias = tf.function(evaluate).get_concrete_function(new_model, x)
print(evaluate_with_bias(x)) # Don't pass in `new_model`.
Adding bias!
tf.Tensor(25.0, shape=(), dtype=float32)

Karena penelusuran ulang bisa mahal , Anda bisa menggunakan tf.Variable s sebagai atribut objek, yang bisa dimutasi (tapi tidak diubah, hati-hati!) Untuk efek serupa tanpa perlu penelusuran ulang.

class BetterModel:

  def __init__(self):
    self.bias = tf.Variable(0.)
    self.weight = tf.Variable(2.)

@tf.function
def evaluate(model, x):
  return model.weight * x + model.bias

better_model = BetterModel()
print(evaluate(better_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
better_model.bias.assign_add(5.0)  # Note: instead of better_model.bias += 5
print(evaluate(better_model, x))  # This works!
Adding bias!
tf.Tensor(25.0, shape=(), dtype=float32)

Membuat tf.Variables

Function hanya mendukung pembuatan variabel satu kali, saat pertama kali dipanggil, dan kemudian menggunakannya kembali. Anda tidak dapat membuat tf.Variables di jejak baru. Membuat variabel baru dalam panggilan berikutnya saat ini tidak diizinkan, tetapi akan dilakukan di masa mendatang.

Contoh:

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

with assert_raises(ValueError):
  f(1.0)
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-8a0913e250e0>", line 7, in <module>
    f(1.0)
ValueError: in user code:

    <ipython-input-1-8a0913e250e0>: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:731 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)

Menggunakan dengan beberapa pengoptimal Keras

Anda mungkin menemukan ValueError: tf.function-decorated function tried to create variables on non-first call. saat menggunakan lebih dari satu pengoptimal Keras dengan fungsi tf.function . Kesalahan ini terjadi karena pengoptimal secara internal membuat tf.Variables saat menerapkan gradien untuk pertama kali.

opt1 = tf.keras.optimizers.Adam(learning_rate = 1e-2)
opt2 = tf.keras.optimizers.Adam(learning_rate = 1e-3)

@tf.function
def train_step(w, x, y, optimizer):
   with tf.GradientTape() as tape:
       L = tf.reduce_sum(tf.square(w*x - y))
   gradients = tape.gradient(L, [w])
   optimizer.apply_gradients(zip(gradients, [w]))

w = tf.Variable(2.)
x = tf.constant([-1.])
y = tf.constant([2.])

train_step(w, x, y, opt1)
print("Calling `train_step` with different optimizer...")
with assert_raises(ValueError):
  train_step(w, x, y, opt2)
Calling `train_step` with different optimizer...
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d3d3937dbf1a>", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    <ipython-input-1-d3d3937dbf1a>:9 train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:604 apply_gradients  **
        self._create_all_weights(var_list)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:781 _create_all_weights
        _ = self.iterations
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:788 __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:926 iterations
        aggregation=tf_variables.VariableAggregation.ONLY_FIRST_REPLICA)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:1132 add_weight
        aggregation=aggregation)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/base.py:810 _add_variable_with_custom_getter
        **kwargs_for_getter)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/base_layer_utils.py:142 make_variable
        shape=variable_shape if variable_shape else None)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:260 __call__
        return cls._variable_v1_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:221 _variable_v1_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:731 invalid_creator_scope
        "tf.function-decorated function tried to create "

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

Jika Anda perlu mengubah pengoptimal selama pelatihan, solusinya adalah membuat Function baru untuk setiap pengoptimal, dengan memanggil ConcreteFunction secara langsung.

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.

Menggunakan dengan beberapa model Keras

Anda mungkin juga menemukan ValueError: tf.function-decorated function tried to create variables on non-first call. saat meneruskan contoh model yang berbeda ke Function sama.

Kesalahan ini terjadi karena model Keras (yang bentuk masukannya tidak ditentukan ) dan lapisan Keras membuat tf.Variables . tf.Variables saat pertama kali dipanggil. Anda mungkin mencoba untuk menginisialisasi variabel tersebut di dalam sebuah Function , yang telah dipanggil. Untuk menghindari kesalahan ini, coba panggil model.build(input_shape) untuk menginisialisasi semua bobot sebelum melatih model.

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 .