Lihat di TensorFlow.org | Jalankan di Google Colab | Lihat sumber di GitHub | Unduh buku catatan |
Ringkasan
Panduan ini membahas di bawah permukaan TensorFlow dan Keras untuk menunjukkan cara kerja TensorFlow. Jika Anda ingin segera memulai Keras, lihat koleksi panduan Keras .
Dalam panduan ini, Anda akan mempelajari bagaimana TensorFlow memungkinkan Anda membuat perubahan sederhana pada kode Anda untuk mendapatkan grafik, bagaimana grafik disimpan dan direpresentasikan, dan bagaimana Anda dapat menggunakannya untuk mempercepat model Anda.
Ini adalah gambaran besar gambaran yang mencakup bagaimana tf.function
memungkinkan Anda untuk beralih dari eksekusi bersemangat ke eksekusi grafik. Untuk spesifikasi tf.function
yang lebih lengkap, buka panduan tf.function
.
Apa itu grafik?
Dalam tiga panduan sebelumnya, Anda menjalankan TensorFlow dengan penuh semangat . Ini berarti operasi TensorFlow dijalankan oleh Python, operasi demi operasi, dan mengembalikan hasil kembali ke Python.
Sementara eksekusi bersemangat memiliki beberapa keunggulan unik, eksekusi grafik memungkinkan portabilitas di luar Python dan cenderung menawarkan kinerja yang lebih baik. Eksekusi grafik berarti bahwa perhitungan tensor dijalankan sebagai grafik TensorFlow , terkadang disebut sebagai tf.Graph
atau hanya "grafik".
Grafik adalah struktur data yang berisi sekumpulan objek tf.Operation
, yang mewakili unit komputasi; dan objek tf.Tensor
, yang mewakili unit data yang mengalir di antara operasi. Mereka didefinisikan dalam konteks tf.Graph
. Karena grafik ini adalah struktur data, mereka dapat disimpan, dijalankan, dan dipulihkan semuanya tanpa kode Python asli.
Ini adalah tampilan grafik TensorFlow yang mewakili jaringan saraf dua lapis saat divisualisasikan di TensorBoard.
Manfaat grafik
Dengan grafik, Anda memiliki banyak fleksibilitas. Anda dapat menggunakan grafik TensorFlow di lingkungan yang tidak memiliki penerjemah Python, seperti aplikasi seluler, perangkat yang disematkan, dan server backend. TensorFlow menggunakan grafik sebagai format untuk model yang disimpan saat mengekspornya dari Python.
Grafik juga mudah dioptimalkan, memungkinkan kompiler untuk melakukan transformasi seperti:
- Simpulkan nilai tensor secara statis dengan melipat simpul konstan dalam komputasi Anda ("pelipatan konstan") .
- Pisahkan sub-bagian dari komputasi yang independen dan pisahkan di antara utas atau perangkat.
- Sederhanakan operasi aritmatika dengan menghilangkan subekspresi umum.
Ada seluruh sistem pengoptimalan, Grappler , untuk melakukan ini dan percepatan lainnya.
Singkatnya, grafik sangat berguna dan membuat TensorFlow Anda berjalan cepat , berjalan secara paralel , dan berjalan secara efisien di beberapa perangkat .
Namun, Anda masih ingin mendefinisikan model pembelajaran mesin Anda (atau perhitungan lainnya) dengan Python untuk kenyamanan, dan kemudian secara otomatis membuat grafik saat Anda membutuhkannya.
Mempersiapkan
import tensorflow as tf
import timeit
from datetime import datetime
Memanfaatkan grafik
Anda membuat dan menjalankan grafik di TensorFlow menggunakan tf.function
, baik sebagai panggilan langsung atau sebagai dekorator. tf.function
mengambil fungsi reguler sebagai input dan mengembalikan Function
. Function
adalah panggilan Python yang membangun grafik TensorFlow dari fungsi Python. Anda menggunakan Function
dengan cara yang sama seperti padanan Python-nya.
# Define a Python function.
def a_regular_function(x, y, b):
x = tf.matmul(x, y)
x = x + b
return x
# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)
# Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)
orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)
Di luar, Function
terlihat seperti fungsi biasa yang Anda tulis menggunakan operasi TensorFlow. Namun, di bawahnya sangat berbeda . Sebuah Function
merangkum beberapa tf.Graph
di belakang satu API . Begitulah Function
dapat memberi Anda manfaat dari eksekusi grafik , seperti kecepatan dan kemampuan penerapan.
tf.function
berlaku untuk suatu fungsi dan semua fungsi lain yang dipanggilnya :
def inner_function(x, y, b):
x = tf.matmul(x, y)
x = x + b
return x
# Use the decorator to make `outer_function` a `Function`.
@tf.function
def outer_function(x):
y = tf.constant([[2.0], [3.0]])
b = tf.constant(4.0)
return inner_function(x, y, b)
# Note that the callable will create a graph that
# includes `inner_function` as well as `outer_function`.
outer_function(tf.constant([[1.0, 2.0]])).numpy()
array([[12.]], dtype=float32)
Jika Anda telah menggunakan TensorFlow 1.x, Anda akan melihat bahwa Anda tidak perlu mendefinisikan Placeholder
atau tf.Session
.
Mengubah fungsi Python menjadi grafik
Setiap fungsi yang Anda tulis dengan TensorFlow akan berisi campuran operasi TF bawaan dan logika Python, seperti klausa if-then
, loop, break
, return
, continue
, dan banyak lagi. Meskipun operasi TensorFlow mudah ditangkap oleh tf.Graph
, logika khusus Python perlu menjalani langkah ekstra untuk menjadi bagian dari grafik. tf.function
menggunakan perpustakaan yang disebut AutoGraph ( tf.autograph
) untuk mengubah kode Python menjadi kode penghasil grafik.
def simple_relu(x):
if tf.greater(x, 0):
return x
else:
return 0
# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)
print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1 Second branch, with graph: 0
Meskipun sepertinya Anda tidak perlu melihat grafik secara langsung, Anda dapat memeriksa output untuk memeriksa hasil yang tepat. Ini tidak mudah dibaca, jadi tidak perlu melihat terlalu hati-hati!
# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x): with ag__.FunctionScope('simple_relu', '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 (do_return, retval_) def set_state(vars_): nonlocal retval_, do_return (do_return, retval_) = vars_ def if_body(): nonlocal retval_, do_return try: do_return = True retval_ = ag__.ld(x) except: do_return = False raise def else_body(): nonlocal retval_, do_return try: do_return = True retval_ = 0 except: do_return = False raise ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2) return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node { name: "x" op: "Placeholder" attr { key: "_user_specified_name" value { s: "x" } } attr { key: "dtype" value { type: DT_INT32 } } attr { key: "shape" value { shape { } } } } node { name: "Greater/y" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node { name: "Greater" op: "Greater" input: "x" input: "Greater/y" attr { key: "T" value { type: DT_INT32 } } } node { name: "cond" op: "StatelessIf" input: "Greater" input: "x" attr { key: "Tcond" value { type: DT_BOOL } } attr { key: "Tin" value { list { type: DT_INT32 } } } attr { key: "Tout" value { list { type: DT_BOOL type: DT_INT32 } } } attr { key: "_lower_using_switch_merge" value { b: true } } attr { key: "_read_only_resource_inputs" value { list { } } } attr { key: "else_branch" value { func { name: "cond_false_34" } } } attr { key: "output_shapes" value { list { shape { } shape { } } } } attr { key: "then_branch" value { func { name: "cond_true_33" } } } } node { name: "cond/Identity" op: "Identity" input: "cond" attr { key: "T" value { type: DT_BOOL } } } node { name: "cond/Identity_1" op: "Identity" input: "cond:1" attr { key: "T" value { type: DT_INT32 } } } node { name: "Identity" op: "Identity" input: "cond/Identity_1" attr { key: "T" value { type: DT_INT32 } } } library { function { signature { name: "cond_false_34" input_arg { name: "cond_placeholder" type: DT_INT32 } output_arg { name: "cond_identity" type: DT_BOOL } output_arg { name: "cond_identity_1" type: DT_INT32 } } node_def { name: "cond/Const" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Const_1" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Const_2" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node_def { name: "cond/Const_3" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Identity" op: "Identity" input: "cond/Const_3:output:0" attr { key: "T" value { type: DT_BOOL } } } node_def { name: "cond/Const_4" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node_def { name: "cond/Identity_1" op: "Identity" input: "cond/Const_4:output:0" attr { key: "T" value { type: DT_INT32 } } } ret { key: "cond_identity" value: "cond/Identity:output:0" } ret { key: "cond_identity_1" value: "cond/Identity_1:output:0" } attr { key: "_construction_context" value { s: "kEagerRuntime" } } arg_attr { key: 0 value { attr { key: "_output_shapes" value { list { shape { } } } } } } } function { signature { name: "cond_true_33" input_arg { name: "cond_identity_1_x" type: DT_INT32 } output_arg { name: "cond_identity" type: DT_BOOL } output_arg { name: "cond_identity_1" type: DT_INT32 } } node_def { name: "cond/Const" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Identity" op: "Identity" input: "cond/Const:output:0" attr { key: "T" value { type: DT_BOOL } } } node_def { name: "cond/Identity_1" op: "Identity" input: "cond_identity_1_x" attr { key: "T" value { type: DT_INT32 } } } ret { key: "cond_identity" value: "cond/Identity:output:0" } ret { key: "cond_identity_1" value: "cond/Identity_1:output:0" } attr { key: "_construction_context" value { s: "kEagerRuntime" } } arg_attr { key: 0 value { attr { key: "_output_shapes" value { list { shape { } } } } } } } } versions { producer: 898 min_consumer: 12 }
Sebagian besar waktu, tf.function
akan bekerja tanpa pertimbangan khusus. Namun, ada beberapa peringatan, dan panduan tf.function dapat membantu di sini, serta referensi AutoGraph lengkap
Polimorfisme: satu Function
, banyak grafik
Sebuah tf.Graph
dikhususkan untuk tipe input tertentu (misalnya, tensor dengan tipe dtype
tertentu atau objek dengan id()
yang sama).
Setiap kali Anda memanggil Function
dengan dtypes
dan bentuk baru dalam argumennya, Function
membuat tf.Graph
baru untuk argumen baru. Tipe dan bentuk tf.Graph
dtypes
sebagai tanda tangan input atau hanya tanda tangan .
Function
menyimpan tf.Graph
yang sesuai dengan tanda tangan itu dalam ConcreteFunction
. ConcreteFunction
adalah pembungkus di sekitar tf.Graph
.
@tf.function
def my_relu(x):
return tf.maximum(0., x)
# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32) tf.Tensor([1. 0.], shape=(2,), dtype=float32) tf.Tensor([3. 0.], shape=(2,), dtype=float32)
Jika Function
telah dipanggil dengan tanda tangan itu, Function
tidak membuat tf.Graph
baru.
# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32) tf.Tensor([0. 1.], shape=(2,), dtype=float32)
Karena didukung oleh banyak grafik, Function
bersifat polimorfik . Itu memungkinkannya untuk mendukung lebih banyak jenis input daripada yang dapat diwakili oleh satu tf.Graph
, serta untuk mengoptimalkan setiap tf.Graph
untuk kinerja yang lebih baik.
# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x) Args: x: float32 Tensor, shape=() Returns: float32 Tensor, shape=() my_relu(x=[1, -1]) Returns: float32 Tensor, shape=(2,) my_relu(x) Args: x: float32 Tensor, shape=(2,) Returns: float32 Tensor, shape=(2,)
Menggunakan tf.function
Sejauh ini, Anda telah mempelajari cara mengubah fungsi Python menjadi grafik hanya dengan menggunakan tf.function
sebagai dekorator atau pembungkus. Namun dalam praktiknya, membuat tf.function
berfungsi dengan benar bisa jadi rumit! Di bagian berikut, Anda akan mempelajari cara membuat kode Anda berfungsi seperti yang diharapkan dengan tf.function
.
Eksekusi grafik vs. eksekusi bersemangat
Kode dalam suatu Function
dapat dieksekusi baik dengan penuh semangat maupun sebagai grafik. Secara default, Function
mengeksekusi kodenya sebagai grafik:
@tf.function
def get_MSE(y_true, y_pred):
sq_diff = tf.pow(y_true - y_pred, 2)
return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 0 4 4 7], shape=(5,), dtype=int32) tf.Tensor([3 6 3 0 6], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
Untuk memverifikasi bahwa grafik Function
Anda melakukan perhitungan yang sama dengan fungsi Python yang setara, Anda dapat membuatnya dieksekusi dengan bersemangat dengan tf.config.run_functions_eagerly(True)
. Ini adalah sakelar yang mematikan kemampuan Function
untuk membuat dan menjalankan grafik , alih-alih mengeksekusi kode secara normal.
tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)
Namun, Function
dapat berperilaku berbeda di bawah grafik dan eksekusi yang bersemangat. Fungsi print
Python adalah salah satu contoh perbedaan kedua mode ini. Mari kita periksa apa yang terjadi ketika Anda menyisipkan pernyataan print
ke fungsi Anda dan memanggilnya berulang kali.
@tf.function
def get_MSE(y_true, y_pred):
print("Calculating MSE!")
sq_diff = tf.pow(y_true - y_pred, 2)
return tf.reduce_mean(sq_diff)
Perhatikan apa yang dicetak:
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Apakah outputnya mengejutkan? get_MSE
hanya dicetak sekali meskipun dipanggil tiga kali.
Untuk menjelaskan, pernyataan print
dieksekusi ketika Function
menjalankan kode asli untuk membuat grafik dalam proses yang dikenal sebagai "tracing" . Pelacakan menangkap operasi TensorFlow ke dalam grafik, dan print
tidak direkam dalam grafik. Grafik itu kemudian dieksekusi untuk ketiga panggilan tanpa pernah menjalankan kode Python lagi .
Sebagai pemeriksaan kewarasan, mari matikan eksekusi grafik untuk membandingkan:
# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE! Calculating MSE! Calculating MSE!
tf.config.run_functions_eagerly(False)
print
adalah efek samping Python , dan ada perbedaan lain yang harus Anda ketahui saat mengubah fungsi menjadi Function
. Pelajari lebih lanjut di bagian Batasan kinerja yang lebih baik dengan panduan tf.function .
Eksekusi tidak ketat
Eksekusi grafik hanya mengeksekusi operasi yang diperlukan untuk menghasilkan efek yang dapat diamati, yang meliputi:
- Nilai balik dari fungsi
- Efek samping terkenal yang terdokumentasi seperti:
- Operasi input/output, seperti
tf.print
- Operasi debug, seperti fungsi penegasan di
tf.debugging
- Mutasi
tf.Variable
- Operasi input/output, seperti
Perilaku ini biasanya dikenal sebagai "Eksekusi non-ketat", dan berbeda dari eksekusi bersemangat, yang melangkah melalui semua operasi program, diperlukan atau tidak.
Secara khusus, pemeriksaan kesalahan runtime tidak dihitung sebagai efek yang dapat diamati. Jika operasi dilewati karena tidak perlu, itu tidak dapat meningkatkan kesalahan runtime.
Dalam contoh berikut, operasi "tidak perlu" tf.gather
dilewati selama eksekusi grafik, sehingga kesalahan runtime InvalidArgumentError
tidak dimunculkan seperti pada eksekusi bersemangat. Jangan mengandalkan kesalahan yang muncul saat menjalankan grafik.
def unused_return_eager(x):
# Get index 1 will fail when `len(x) == 1`
tf.gather(x, [1]) # unused
return x
try:
print(unused_return_eager(tf.constant([0.0])))
except tf.errors.InvalidArgumentError as e:
# All operations are run during eager execution so an error is raised.
print(f'{type(e).__name__}: {e}')
tf.Tensor([0.], shape=(1,), dtype=float32)
@tf.function
def unused_return_graph(x):
tf.gather(x, [1]) # unused
return x
# Only needed operations are run during graph exection. The error is not raised.
print(unused_return_graph(tf.constant([0.0])))
tf.Tensor([0.], shape=(1,), dtype=float32)
praktik terbaik tf.function
Mungkin perlu beberapa waktu untuk membiasakan diri dengan perilaku Function
. Untuk memulai dengan cepat, pengguna pertama kali harus bermain-main dengan fungsi mainan dekorasi dengan @tf.function
untuk mendapatkan pengalaman beralih dari bersemangat ke eksekusi grafik.
Mendesain untuk tf.function
mungkin merupakan pilihan terbaik Anda untuk menulis program TensorFlow yang kompatibel dengan grafik. Berikut beberapa tipsnya:
- Beralih antara bersemangat dan eksekusi grafik lebih awal dan sering dengan
tf.config.run_functions_eagerly
untuk menentukan jika/ ketika dua mode berbeda. - Buat
tf.Variable
s di luar fungsi Python dan ubah di dalam. Hal yang sama berlaku untuk objek yang menggunakantf.Variable
, sepertikeras.layers
,keras.Model
s dantf.optimizers
. - Hindari menulis fungsi yang bergantung pada variabel Python luar , tidak termasuk
tf.Variable
s dan Keras. - Lebih suka menulis fungsi yang menggunakan tensor dan jenis TensorFlow lainnya sebagai input. Anda dapat memasukkan jenis objek lain tetapi hati-hati !
- Sertakan komputasi sebanyak mungkin di bawah
tf.function
untuk memaksimalkan perolehan kinerja. Misalnya, hiasi seluruh langkah pelatihan atau seluruh putaran pelatihan.
Melihat kecepatannya
tf.function
biasanya meningkatkan kinerja kode Anda, tetapi jumlah percepatan tergantung pada jenis komputasi yang Anda jalankan. Komputasi kecil dapat didominasi oleh overhead pemanggilan graf. Anda dapat mengukur perbedaan kinerja seperti:
x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)
def power(x, y):
result = tf.eye(10, dtype=tf.dtypes.int32)
for _ in range(y):
result = tf.matmul(x, result)
return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 2.5637862179974036
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6832536700021592
tf.function
biasanya digunakan untuk mempercepat loop pelatihan, dan Anda dapat mempelajarinya lebih lanjut di Menulis loop pelatihan dari awal dengan Keras.
Performa dan trade-off
Grafik dapat mempercepat kode Anda, tetapi proses pembuatannya memerlukan biaya tambahan. Untuk beberapa fungsi, pembuatan grafik membutuhkan waktu lebih lama daripada eksekusi grafik. Investasi ini biasanya cepat terbayar kembali dengan peningkatan kinerja eksekusi berikutnya, tetapi penting untuk diperhatikan bahwa beberapa langkah pertama dari setiap pelatihan model besar bisa lebih lambat karena pelacakan.
Tidak peduli seberapa besar model Anda, Anda ingin menghindari tracing sesering mungkin. Panduan tf.function
membahas cara menyetel spesifikasi input dan menggunakan argumen tensor untuk menghindari penelusuran ulang. Jika Anda menemukan Anda mendapatkan kinerja yang sangat buruk, ada baiknya untuk memeriksa apakah Anda menelusuri kembali secara tidak sengaja.
Kapan Pelacakan Function
?
Untuk mengetahui kapan Function
Anda melacak, tambahkan pernyataan print
ke kodenya. Sebagai aturan praktis, Function
akan mengeksekusi pernyataan print
setiap kali dilacak.
@tf.function
def a_function_with_python_side_effect(x):
print("Tracing!") # An eager-only side effect.
return x * x + tf.constant(2)
# This is traced the first time.
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect.
print(a_function_with_python_side_effect(tf.constant(3)))
Tracing! tf.Tensor(6, shape=(), dtype=int32) tf.Tensor(11, shape=(), dtype=int32)
# This retraces each time the Python argument changes,
# as a Python argument could be an epoch count or other
# hyperparameter.
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))
Tracing! tf.Tensor(6, shape=(), dtype=int32) Tracing! tf.Tensor(11, shape=(), dtype=int32)
Argumen Python baru selalu memicu pembuatan grafik baru, karenanya penelusuran ekstra.
Langkah selanjutnya
Anda dapat mempelajari lebih lanjut tentang tf.function
di halaman referensi API dan dengan mengikuti kinerja yang lebih baik dengan panduan tf.function
.