Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Pengantar grafik dan fungsi

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

Pengantar Grafik dan tf.function

Panduan ini berada di bawah permukaan TensorFlow dan Keras untuk melihat bagaimana TensorFlow bekerja. Jika Anda ingin segera memulai dengan Keras, silakan lihat koleksi panduan Keras kami .

Dalam panduan ini Anda akan melihat inti bagaimana TensorFlow memungkinkan Anda untuk membuat perubahan sederhana pada kode Anda untuk mendapatkan grafik, dan bagaimana mereka disimpan dan diwakili, dan bagaimana Anda dapat menggunakannya untuk mempercepat dan mengekspor model Anda.

Ini adalah pengantar bentuk pendek; untuk pengantar penuh konsep-konsep ini, lihat panduan tf.function .

Apa itu grafik?

Dalam tiga panduan sebelumnya, Anda telah melihat TensorFlow berjalan dengan penuh semangat . Ini berarti operasi TensorFlow dijalankan oleh Python, operasi demi operasi, dan mengembalikan hasilnya ke Python. Eager TensorFlow memanfaatkan GPU, memungkinkan Anda menempatkan variabel, tensor, dan bahkan operasi pada GPU dan TPU. Juga mudah untuk di-debug.

Untuk beberapa pengguna, Anda mungkin tidak perlu atau ingin meninggalkan Python.

Namun, menjalankan TensorFlow op-by-op dengan Python mencegah sejumlah akselerasi yang tersedia. Jika Anda bisa mengekstraksi perhitungan tensor dari Python, Anda bisa membuatnya menjadi 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 semua tanpa kode Python asli.

Ini adalah apa yang tampak seperti grafik dua lapis ketika divisualisasikan di TensorBoard.

grafik tensorflow dua lapisan

Manfaat grafik

Dengan grafik, Anda memiliki banyak fleksibilitas. Anda dapat menggunakan grafik TensorFlow Anda di lingkungan yang tidak memiliki juru bahasa Python, seperti aplikasi seluler, perangkat yang disematkan, dan server backend. TensorFlow menggunakan grafik sebagai format untuk model yang disimpan ketika mengekspornya dari Python.

Grafik juga mudah dioptimalkan, memungkinkan kompiler melakukan transformasi seperti:

  • Secara statis simpulkan nilai tensor dengan melipat node konstan dalam perhitungan Anda ("konstanta lipat") .
  • Pisahkan sub-bagian dari perhitungan yang independen dan membaginya di antara utas atau perangkat.
  • Sederhanakan operasi aritmatika dengan menghilangkan sub-ekspresi umum.

Ada seluruh sistem optimisasi, Grappler , untuk melakukan ini dan speedup lainnya.

Singkatnya, grafik sangat berguna dan biarkan TensorFlow Anda berjalan cepat , berjalan secara paralel , dan berjalan secara efisien di beberapa perangkat .

Namun, Anda masih ingin mendefinisikan model pembelajaran mesin kami (atau perhitungan lain) dengan Python untuk kenyamanan, dan kemudian secara otomatis membuat grafik saat Anda membutuhkannya.

Menelusuri grafik

Cara Anda membuat grafik di TensorFlow adalah dengan menggunakan tf.function , baik sebagai panggilan langsung atau sebagai dekorator.

 import tensorflow as tf
import timeit
from datetime import datetime
 
 # Define a Python function
def function_to_get_faster(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Create a `Function` object that contains a graph
a_function_that_uses_a_graph = tf.function(function_to_get_faster)

# Make some tensors
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

# It just works!
a_function_that_uses_a_graph(x1, y1, b1).numpy()
 
array([[12.]], dtype=float32)

tf.function -ized fungsi yang Python callables yang bekerja sama dengan Python setara mereka. Mereka memiliki kelas tertentu ( python.eager.def_function.Function ), tetapi bagi Anda mereka bertindak seperti versi yang tidak dilacak.

tf.function secara rekursif melacak setiap fungsi Python yang dipanggilnya.

 def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator
@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 menentukan Placeholder atau tf.Sesssion .

Kontrol aliran dan efek samping

Kontrol aliran dan loop dikonversi ke TensorFlow melalui tf.autograph secara default. Autograph menggunakan kombinasi metode, termasuk standardisasi loop konstruksi, membuka gulungan, dan manipulasi AST .

 def my_function(x):
  if tf.reduce_sum(x) <= 1:
    return x * x
  else:
    return x-1

a_function = tf.function(my_function)

print("First branch, with graph:", a_function(tf.constant(1.0)).numpy())
print("Second branch, with graph:", a_function(tf.constant([5.0, 5.0])).numpy())
 
First branch, with graph: 1.0
Second branch, with graph: [4. 4.]

Anda dapat langsung memanggil konversi Autograph untuk melihat bagaimana Python dikonversi menjadi op TensorFlow. Ini, sebagian besar, tidak dapat dibaca, tetapi Anda dapat melihat transformasi.

 # Don't read the output too carefully.
print(tf.autograph.to_code(my_function))
 
def tf__my_function(x):
    with ag__.FunctionScope('my_function', '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 (retval_, do_return)

        def set_state(vars_):
            nonlocal retval_, do_return
            (retval_, do_return) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) * ag__.ld(x))
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) - 1)
            except:
                do_return = False
                raise
        ag__.if_stmt((ag__.converted_call(ag__.ld(tf).reduce_sum, (ag__.ld(x),), None, fscope) <= 1), if_body, else_body, get_state, set_state, ('retval_', 'do_return'), 2)
        return fscope.ret(retval_, do_return)


Tanda tangan secara otomatis mengkonversi if-then klausa, loop, break , return , continue , dan banyak lagi.

Sebagian besar waktu, Autograph akan bekerja tanpa pertimbangan khusus. Namun, ada beberapa peringatan, dan panduan fungsi ini dapat membantu di sini, serta referensi tanda tangan lengkap

Melihat kecepatannya

Hanya dengan membungkus fungsi yang menggunakan tensor di tf.function tidak secara otomatis mempercepat kode Anda. Untuk fungsi-fungsi kecil yang disebut beberapa kali pada mesin tunggal, overhead memanggil grafik atau fragmen grafik dapat mendominasi runtime. Juga, jika sebagian besar perhitungan sudah terjadi pada akselerator, seperti tumpukan konvolusi berat GPU, percepatan grafik tidak akan besar.

Untuk perhitungan yang rumit, grafik dapat memberikan peningkatan kecepatan yang signifikan. Ini karena grafik mengurangi komunikasi Python ke perangkat, dan melakukan beberapa percepatan.

Kode ini dikali beberapa kali berjalan pada beberapa lapisan padat kecil.

 # Create an oveerride model to classify pictures
class SequentialModel(tf.keras.Model):
  def __init__(self, **kwargs):
    super(SequentialModel, self).__init__(**kwargs)
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return x

input_data = tf.random.uniform([60, 28, 28])

eager_model = SequentialModel()
graph_model = tf.function(eager_model)

print("Eager time:", timeit.timeit(lambda: eager_model(input_data), number=10000))
print("Graph time:", timeit.timeit(lambda: graph_model(input_data), number=10000))

 
Eager time: 4.9249971129993355
Graph time: 2.026840765000088

Fungsi polimorfik

Ketika Anda melacak suatu fungsi, Anda membuat objek Function bersifat polimorfik . Fungsi polimorfik adalah Python callable yang merangkum beberapa grafik fungsi konkret di belakang satu API.

Anda dapat menggunakan Function ini pada semua jenis dtypes dan bentuk yang berbeda. Setiap kali Anda memohonnya dengan tanda tangan argumen baru, fungsi asli akan ditelusuri kembali dengan argumen baru. Function kemudian menyimpan tf.Graph sesuai dengan jejak itu di fungsi concrete_function . Jika fungsi telah dilacak dengan argumen semacam itu, Anda cukup mendapatkan grafik pra-penelusuran.

Secara konseptual, maka:

  • tf.Graph adalah struktur data portabel dan mentah yang menggambarkan perhitungan
  • Function adalah caching, tracing, dispatcher di atas ConcreteFunctions
  • A ConcreteFunction adalah pembungkus yang kompatibel dengan semangat di sekitar grafik yang memungkinkan Anda mengeksekusi grafik dari Python

Memeriksa fungsi polimorfik

Anda dapat memeriksa a_function , yang merupakan hasil dari memanggil tf.function pada fungsi Python my_function . Dalam contoh ini, memanggil a_function dengan tiga jenis argumen menghasilkan tiga fungsi konkret yang berbeda.

 print(a_function)

print("Calling a `Function`:")
print("Int:", a_function(tf.constant(2)))
print("Float:", a_function(tf.constant(2.0)))
print("Rank-1 tensor of floats", a_function(tf.constant([2.0, 2.0, 2.0])))
 
<tensorflow.python.eager.def_function.Function object at 0x7f466417bf60>
Calling a `Function`:
Int: tf.Tensor(1, shape=(), dtype=int32)
Float: tf.Tensor(1.0, shape=(), dtype=float32)
Rank-1 tensor of floats tf.Tensor([1. 1. 1.], shape=(3,), dtype=float32)

 # Get the concrete function that works on floats
print("Inspecting concrete functions")
print("Concrete function for float:")
print(a_function.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.float32)))
print("Concrete function for tensor of floats:")
print(a_function.get_concrete_function(tf.constant([2.0, 2.0, 2.0])))

 
Inspecting concrete functions
Concrete function for float:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()
Concrete function for tensor of floats:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=(3,)
  Returns:
    float32 Tensor, shape=(3,)

 # Concrete functions are callable
# Note: You won't normally do this, but instead just call the containing `Function`
cf = a_function.get_concrete_function(tf.constant(2))
print("Directly calling a concrete function:", cf(tf.constant(2)))
 
Directly calling a concrete function: tf.Tensor(1, shape=(), dtype=int32)

Dalam contoh ini, Anda melihat cukup jauh ke dalam tumpukan. Kecuali jika Anda secara khusus mengelola pelacakan, Anda biasanya tidak perlu memanggil fungsi konkret secara langsung seperti yang ditunjukkan di sini.

Kembali ke eksekusi yang cepat

Anda mungkin menemukan diri Anda melihat jejak tumpukan panjang, khususnya yang merujuk ke tf.Graph atau with tf.Graph().as_default() . Ini berarti Anda kemungkinan berjalan dalam konteks grafik. Fungsi inti dalam TensorFlow menggunakan konteks grafik, seperti model.fit() Keras model.fit() .

Sering kali lebih mudah untuk men-debug eksekusi yang bersemangat. Jejak tumpukan harus relatif pendek dan mudah dipahami.

Dalam situasi di mana grafik membuat debugging menjadi sulit, Anda dapat kembali menggunakan eksekusi yang diinginkan untuk debug.

Berikut adalah cara-cara yang dapat Anda pastikan Anda menjalankan dengan penuh semangat:

  • Sebut model dan layer secara langsung sebagai callable

  • Saat menggunakan kompilasi / fit Keras, pada waktu kompilasi gunakan model.compile(run_eagerly=True)

  • Tetapkan mode eksekusi global melalui tf.config.run_functions_eagerly(True)

Menggunakan run_eagerly=True

 # Define an identity layer with an eager side effect
class EagerLayer(tf.keras.layers.Layer):
  def __init__(self, **kwargs):
    super(EagerLayer, self).__init__(**kwargs)
    # Do some kind of initialization here

  def call(self, inputs):
    print("\nCurrently running eagerly", str(datetime.now()))
    return inputs
 
 # Create an override model to classify pictures, adding the custom layer
class SequentialModel(tf.keras.Model):
  def __init__(self):
    super(SequentialModel, self).__init__()
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)
    self.eager = EagerLayer()

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return self.eager(x)

# Create an instance of this model
model = SequentialModel()

# Generate some nonsense pictures and labels
input_data = tf.random.uniform([60, 28, 28])
labels = tf.random.uniform([60])

loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
 

Pertama, kompilasi model tanpa keinginan. Perhatikan bahwa model tidak dilacak; terlepas dari namanya, compile hanya mengatur fungsi kerugian, optimisasi, dan parameter pelatihan lainnya.

 model.compile(run_eagerly=False, loss=loss_fn)
 

Sekarang, panggil fit dan lihat bahwa fungsinya dilacak (dua kali) dan kemudian efek eager tidak pernah berjalan lagi.

 model.fit(input_data, labels, epochs=3)
 
Epoch 1/3

Currently running eagerly 2020-08-04 01:22:21.848492

Currently running eagerly 2020-08-04 01:22:21.955102
2/2 [==============================] - 0s 1ms/step - loss: 1.4056
Epoch 2/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0037
Epoch 3/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0019

<tensorflow.python.keras.callbacks.History at 0x7f45f03c5630>

Jika Anda menjalankan bahkan satu zaman dalam keinginan, Anda dapat melihat efek samping keinginan dua kali.

 print("Running eagerly")
# When compiling the model, set it to run eagerly
model.compile(run_eagerly=True, loss=loss_fn)

model.fit(input_data, labels, epochs=1)

 
Running eagerly

Currently running eagerly 2020-08-04 01:22:22.152979
1/2 [==============>...............] - ETA: 0s - loss: 8.7806e-04
Currently running eagerly 2020-08-04 01:22:22.173295
2/2 [==============================] - 0s 5ms/step - loss: 4.6877e-04

<tensorflow.python.keras.callbacks.History at 0x7f45f0312940>

Menggunakan run_functions_eagerly

Anda juga dapat secara global mengatur semuanya agar berjalan dengan penuh semangat. Perhatikan bahwa ini hanya berfungsi jika Anda melacak kembali; fungsi yang dilacak akan tetap ditelusuri dan dijalankan sebagai grafik.

 # Now, globally set everything to run eagerly
tf.config.run_functions_eagerly(True)
print("Run all functions eagerly.")

# First, trace the model, triggering the side effect
polymorphic_function = tf.function(model)

# It was traced...
print(polymorphic_function.get_concrete_function(input_data))

# But when you run the function again, the side effect happens (both times).
result = polymorphic_function(input_data)
result = polymorphic_function(input_data)
 
Run all functions eagerly.

Currently running eagerly 2020-08-04 01:22:22.202726
ConcreteFunction function(self)
  Args:
    self: float32 Tensor, shape=(60, 28, 28)
  Returns:
    float32 Tensor, shape=(60, 10)

Currently running eagerly 2020-08-04 01:22:22.206521

Currently running eagerly 2020-08-04 01:22:22.207818

 # Don't forget to set it back when you are done
tf.config.experimental_run_functions_eagerly(False)

 
WARNING:tensorflow:From <ipython-input-17-782fe9ce7b18>:2: experimental_run_functions_eagerly (from tensorflow.python.eager.def_function) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.config.run_functions_eagerly` instead of the experimental version.

Penelusuran dan kinerja

Menelusuri biaya overhead. Meskipun melacak fungsi-fungsi kecil cepat, model-model besar dapat mengambil waktu jam dinding yang nyata untuk dilacak. Investasi ini biasanya cepat dibayar kembali dengan peningkatan kinerja, tetapi penting untuk menyadari bahwa beberapa zaman pertama dari setiap pelatihan model besar dapat lebih lambat karena penelusuran.

Tidak peduli seberapa besar model Anda, Anda ingin menghindari pelacakan sering. Bagian dari panduan fungsi tf. ini membahas cara mengatur spesifikasi input dan menggunakan argumen tensor untuk menghindari pelacakan ulang. Jika Anda mendapati kinerja yang luar biasa buruk, ada baiknya memeriksa apakah Anda melakukan penelusuran ulang secara tidak sengaja.

Anda dapat menambahkan efek samping yang hanya diinginkan (seperti mencetak argumen Python) sehingga Anda dapat melihat kapan fungsi tersebut dilacak. Di sini, Anda melihat pelacakan ulang ekstra karena argumen Python baru selalu memicu penelusuran ulang.

 # Use @tf.function decorator
@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!")  # This eager
  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)))

# This retraces each time the Python argument chances
# 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)
tf.Tensor(11, shape=(), dtype=int32)
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

Langkah selanjutnya

Anda dapat membaca diskusi yang lebih mendalam di halaman referensi API tf.function dan di panduan ini .