Trang này được dịch bởi Cloud Translation API.
Switch to English

Giới thiệu về đồ thị và chức năng

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải vở

Giới thiệu về đồ thị và tf.function

Hướng dẫn này đi bên dưới bề mặt của TensorFlow và Keras để xem cách thức hoạt động của TensorFlow. Thay vào đó, nếu bạn muốn bắt đầu ngay với Keras, vui lòng xem bộ sưu tập các hướng dẫn về Keras của chúng tôi .

Trong hướng dẫn này, bạn sẽ thấy cốt lõi của cách thức TensorFlow cho phép bạn thực hiện các thay đổi đơn giản cho mã của mình để lấy biểu đồ và cách chúng được lưu trữ và đại diện cũng như cách bạn có thể sử dụng chúng để tăng tốc và xuất mô hình của mình.

Đây là một giới thiệu dạng ngắn; để biết giới thiệu đầy đủ về các khái niệm này, hãy xem hướng dẫn tf.function .

Đồ thị là gì?

Trong ba hướng dẫn trước, bạn đã thấy TensorFlow chạy háo hức . Điều này có nghĩa là các hoạt động của TensorFlow được thực thi bởi Python, hoạt động bằng thao tác và trả lại kết quả cho Python. Eager TensorFlow tận dụng lợi thế của GPU, cho phép bạn đặt các biến, tenxơ và thậm chí hoạt động trên GPU và TPU. Nó cũng dễ dàng để gỡ lỗi.

Đối với một số người dùng, bạn có thể không bao giờ cần hoặc muốn rời khỏi Python.

Tuy nhiên, việc chạy TensorFlow op-by-op trong Python sẽ ngăn chặn một loạt các khả năng tăng tốc nếu không. Nếu bạn có thể trích xuất các tính toán tenor từ Python, bạn có thể biến chúng thành một biểu đồ .

Đồ thị là các cấu trúc dữ liệu chứa một tập hợp các đối tượng tf.Operation , đại diện cho các đơn vị tính toán; và các đối tượng tf.Tensor , đại diện cho các đơn vị dữ liệu chảy giữa các hoạt động. Chúng được định nghĩa trong bối cảnh tf.Graph . Vì các biểu đồ này là cấu trúc dữ liệu, chúng có thể được lưu, chạy và khôi phục tất cả mà không cần mã Python gốc.

Đây là những gì một biểu đồ hai lớp đơn giản trông giống như khi được hiển thị trong TensorBoard.

đồ thị kéo căng hai lớp

Lợi ích của đồ thị

Với một biểu đồ, bạn có rất nhiều tính linh hoạt. Bạn có thể sử dụng biểu đồ TensorFlow của mình trong các môi trường không có trình thông dịch Python, như ứng dụng di động, thiết bị nhúng và máy chủ phụ trợ. TensorFlow sử dụng biểu đồ làm định dạng cho các mô hình đã lưu khi xuất chúng từ Python.

Các biểu đồ cũng dễ dàng được tối ưu hóa, cho phép trình biên dịch thực hiện các phép biến đổi như:

  • Suy ra tĩnh giá trị của tenxơ bằng cách gấp các nút không đổi trong tính toán của bạn ("gấp không đổi") .
  • Các phần phụ riêng biệt của một tính toán độc lập và phân chia chúng giữa các luồng hoặc thiết bị.
  • Đơn giản hóa các phép toán số học bằng cách loại bỏ các biểu thức con phổ biến.

Có toàn bộ hệ thống tối ưu hóa, Grappler , để thực hiện điều này và các tăng tốc khác.

Nói tóm lại, đồ thị cực kỳ hữu ích và để cho TensorFlow của bạn chạy nhanh , chạy song song và chạy hiệu quả trên nhiều thiết bị .

Tuy nhiên, bạn vẫn muốn xác định các mô hình học máy của chúng tôi (hoặc các tính toán khác) trong Python để thuận tiện và sau đó tự động xây dựng biểu đồ khi bạn cần chúng.

Đồ thị theo dõi

Cách bạn tạo một biểu đồ trong TensorFlow là sử dụng tf.function , như một cuộc gọi trực tiếp hoặc như một công cụ trang trí.

 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 là các hàm gọi Python hoạt động giống như các hàm tương đương Python của chúng. Họ có một lớp cụ thể ( python.eager.def_function.Function ), nhưng với bạn, họ hoạt động giống như phiên bản không theo dõi.

tf.function theo dõi đệ quy bất kỳ hàm Python nào nó gọi.

 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)

Nếu bạn đã sử dụng TensorFlow 1.x, bạn sẽ nhận thấy rằng bất cứ lúc nào bạn cũng cần xác định một Placeholder hoặc tf.Sesssion .

Kiểm soát dòng chảy và tác dụng phụ

Điều khiển luồng và vòng lặp được chuyển đổi thành TensorFlow qua tf.autograph theo mặc định. Autograph sử dụng kết hợp các phương thức, bao gồm chuẩn hóa các cấu trúc vòng lặp, hủy kiểm soát và thao tác 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.]

Bạn có thể gọi trực tiếp chuyển đổi Autograph để xem Python được chuyển đổi thành các opor TensorFlow như thế nào. Điều này, hầu hết, không thể đọc được, nhưng bạn có thể thấy sự chuyển đổi.

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


Chữ ký tự động chuyển đổi các mệnh đề if-then , vòng lặp, break , return , continue và hơn thế nữa.

Hầu hết thời gian, Autograph sẽ hoạt động mà không cần cân nhắc đặc biệt. Tuy nhiên, có một số cảnh báo và hướng dẫn tf.feft có thể giúp đỡ ở đây, cũng như tham khảo chữ ký đầy đủ

Thấy tăng tốc

Chỉ gói một hàm sử dụng tenxơ trong tf.function không tự động tăng tốc mã của bạn. Đối với các hàm nhỏ được gọi một vài lần trên một máy, chi phí chung của việc gọi biểu đồ hoặc đoạn biểu đồ có thể chi phối thời gian chạy. Ngoài ra, nếu hầu hết các tính toán đã xảy ra trên một máy gia tốc, chẳng hạn như các khối kết hợp nặng GPU, thì tốc độ đồ thị sẽ không lớn.

Đối với các tính toán phức tạp, đồ thị có thể cung cấp một tốc độ tăng tốc đáng kể. Điều này là do các biểu đồ làm giảm giao tiếp Python-to-device và thực hiện một số tăng tốc.

Mã này lần một vài lần chạy trên một số lớp dày đặc nhỏ.

 # 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

Hàm đa hình

Khi bạn theo dõi một chức năng, bạn tạo một đối tượng Functionđa hình . Hàm đa hình là một hàm có thể gọi được của Python, đóng gói một số biểu đồ hàm cụ thể đằng sau một API.

Bạn có thể sử dụng Function trên tất cả các loại khác nhau của dtypes và hình dạng. Mỗi khi bạn gọi nó bằng một chữ ký đối số mới, hàm ban đầu sẽ được truy tìm lại với các đối số mới. Function sau đó lưu trữ tf.Graph tương ứng với dấu vết đó trong một hàm concrete_function . Nếu hàm đã được theo dõi với loại đối số đó, bạn chỉ cần lấy biểu đồ theo dõi trước của mình.

Về mặt khái niệm, sau đó:

  • Một tf.Graph là cấu trúc dữ liệu thô, di động mô tả một tính toán
  • Function là bộ đệm, theo dõi, bộ điều phối trên ConcreteFifts
  • ConcreteFunction là một trình bao bọc tương thích háo hức xung quanh một biểu đồ cho phép bạn thực hiện biểu đồ từ Python

Kiểm tra các chức năng đa hình

Bạn có thể kiểm tra a_function , đó là kết quả của việc gọi tf.function trên hàm Python my_function . Trong ví dụ này, việc gọi a_function với ba loại đối số dẫn đến ba hàm cụ thể khác nhau.

 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)

Trong ví dụ này, bạn đang nhìn thấy khá xa trong ngăn xếp. Trừ khi bạn đang quản lý theo dõi cụ thể, thông thường bạn sẽ không cần gọi các hàm cụ thể trực tiếp như được hiển thị ở đây.

Hoàn nguyên để thực hiện háo hức

Bạn có thể thấy mình đang nhìn vào dấu vết ngăn xếp dài, đặc biệt là những dấu vết liên quan đến tf.Graph hoặc with tf.Graph().as_default() . Điều này có nghĩa là bạn có khả năng đang chạy trong một bối cảnh đồ thị. Các hàm cốt lõi trong TensorFlow sử dụng các bối cảnh đồ thị, chẳng hạn như model.fit() của model.fit() .

Nó thường dễ dàng hơn nhiều để gỡ lỗi thực hiện háo hức. Dấu vết ngăn xếp nên tương đối ngắn và dễ hiểu.

Trong các tình huống mà biểu đồ làm cho việc gỡ lỗi trở nên khó khăn, bạn có thể quay lại sử dụng thực thi háo hức để gỡ lỗi.

Dưới đây là những cách bạn có thể chắc chắn rằng mình đang chạy háo hức:

  • Các mô hình cuộc gọi và các lớp trực tiếp như các cuộc gọi

  • Khi sử dụng biên dịch / phù hợp với Keras, tại thời điểm biên dịch, hãy sử dụng model.compile(run_eagerly=True)

  • Đặt chế độ thực thi toàn cầu thông qua tf.config.run_functions_eagerly(True)

Sử dụng 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)
 

Đầu tiên, biên dịch mô hình mà không háo hức. Lưu ý rằng mô hình không được theo dõi; mặc dù tên của nó, compile chỉ thiết lập các hàm mất, tối ưu hóa và các tham số đào tạo khác.

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

Bây giờ, gọi fit và thấy rằng chức năng được theo dõi (hai lần) và sau đó hiệu ứng háo hức không bao giờ chạy lại.

 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>

Tuy nhiên, nếu bạn chạy ngay cả một epoch trong háo hức, bạn có thể thấy tác dụng phụ háo hức hai lần.

 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>

Sử dụng run_functions_eagerly

Bạn cũng có thể thiết lập mọi thứ để chạy một cách háo hức. Lưu ý rằng điều này chỉ hoạt động nếu bạn theo dõi lại; các hàm theo dõi sẽ vẫn được theo dõi và chạy dưới dạng biểu đồ.

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

Truy tìm và thực hiện

Truy tìm chi phí một số chi phí. Mặc dù theo dõi các chức năng nhỏ là nhanh chóng, các mô hình lớn có thể mất thời gian đồng hồ treo tường đáng chú ý để theo dõi. Khoản đầu tư này thường được hoàn trả nhanh chóng với hiệu suất tăng, nhưng điều quan trọng cần biết là một vài kỷ nguyên đầu tiên của bất kỳ chương trình đào tạo mô hình lớn nào có thể chậm hơn do truy tìm.

Cho dù mô hình của bạn lớn đến đâu, bạn muốn tránh truy tìm thường xuyên. Phần này của hướng dẫn tf.feft thảo luận về cách đặt thông số kỹ thuật đầu vào và sử dụng các đối số tenor để tránh retracing. Nếu bạn thấy bạn đang có hiệu suất kém bất thường, thật tốt để kiểm tra xem bạn có vô tình truy cứu không.

Bạn có thể thêm một hiệu ứng phụ chỉ háo hức (chẳng hạn như in một đối số Python) để bạn có thể thấy khi chức năng được theo dõi. Tại đây, bạn sẽ thấy thêm retracing vì các đối số Python mới luôn kích hoạt retracing.

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

Bước tiếp theo

Bạn có thể đọc một cuộc thảo luận sâu hơn ở cả trang tham chiếu API tf.function và tại hướng dẫn .