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

Giới thiệu về đồ thị và hàm

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ tay

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

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

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

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

Đồ thị là gì?

Trong ba hướng dẫn trước, bạn đã thấy TensorFlow chạy một cách háo hức . Điều này có nghĩa là các hoạt động TensorFlow được thực thi bởi Python, hoạt động theo hoạt động và trả kết quả trở lại Python. Eager TensorFlow tận dụng lợi thế của GPU, cho phép bạn đặt các biến, bộ căng và thậm chí các 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 không muốn rời khỏi Python.

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

Đồ thị là 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 lưu chuyển giữa các hoạt động. Chúng được định nghĩa trong ngữ cảnh tf.Graph . Vì các đồ thị 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à biểu đồ hai lớp đơn giản trông như thế nào khi được hiển thị trong TensorBoard.

đồ thị dòng chảy căng thẳ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ư các ứng dụng di động, thiết bị nhúng và máy chủ phụ trợ. TensorFlow sử dụng đồ thị làm định dạng cho các mô hình đã lưu khi xuất chúng từ Python.

Đồ thị 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 tensors bằng cách gấp các nút không đổi trong tính toán của bạn ("gấp liên tục") .
  • Tách các phần con của một phép tính độc lập và 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 thông thường.

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

Tóm lại, đồ thị cực kỳ hữu ích và cho phép 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 (hoặc các phép tính khác) của chúng tôi bằng Python để thuận tiện và sau đó tự động xây dựng đồ thị khi bạn cần.

Đồ thị theo dõi

Cách bạn tạo một biểu đồ trong TensorFlow là sử dụng tf.function , dưới dạng một cuộc gọi trực tiếp hoặc như một người 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 tf. functions -ized là các hàm được gọi trong Python hoạt động giống như các hàm tương đương Python của chúng. Chúng có một lớp cụ thể ( python.eager.def_function.Function ), nhưng đối với bạn, chúng hoạt động như một phiên bản không theo dõi.

tf.function theo dõi một cách đệ quy bất kỳ hàm Python nào mà 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ạn không cần phải xác định Placeholder hoặc tf.Sesssion .

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

Kiểm soát luồng và vòng lặp được chuyển đổi thành TensorFlow thông qua tf.autograph theo mặc định. Autograph sử dụng kết hợp các phương pháp, bao gồm chuẩn hóa cấu trúc vòng lặp, thao tác hủy cuộn và 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ể trực tiếp gọi chuyển đổi Autograph để xem cách Python được chuyển đổi thành hoạt động TensorFlow. Điều này hầu như không thể đọc được, nhưng bạn có thể thấy sự biế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)


Autograph 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ố lưu ý và hướng dẫn tf. Chức năng có thể giúp ích ở đây, cũng như tài liệu tham khảo đầy đủ về chữ ký

Thấy tốc độ tăng lên

Chỉ gói một chức năng sử dụng tensor trong chức năng tf.function không tự động tăng tốc mã của bạn. Đối với các chức năng nhỏ được gọi một vài lần trên một máy, chi phí gọi một đồ thị hoặc phân đoạn đồ thị có thể chi phối thời gian chạy. Ngoài ra, nếu hầu hết quá trình tính toán đã diễn ra trên một máy gia tốc, chẳng hạn như các đống phức tạp nặng nề của 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 độ đáng kể. Điều này là do đồ thị làm giảm giao tiếp giữa Python với thiết bị và thực hiện một số tăng tốc.

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

# 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: 5.302399797999897
Graph time: 2.3688509589999285

Các hàm đa hình

Khi bạn theo dõi một hàm, bạn tạo một đối tượng Function đa hình . Một hàm đa hình là một hàm có thể gọi trong Python đóng gói một số đồ thị hàm cụ thể đằng sau một API.

Bạn có thể sử dụng Function này trên tất cả các loại dtypes và hình dạng khác nhau. Mỗi lần bạn gọi nó với một chữ ký đối số mới, hàm gốc sẽ được truy tìm lại với các đối số mới. Sau đó, Function 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 truy tìm bằng loại đối số đó, bạn chỉ cần nhận được biểu đồ được truy tìm trước của mình.

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

  • tf.Graph là cấu trúc dữ liệu thô, di động mô tả một phép tính
  • Function là một bộ nhớ đệm, truy tìm, điều phối trên ConcreteFunctions
  • ConcreteFunction là một trình bao bọc tương thích mong muốn xung quanh một biểu đồ cho phép bạn thực thi biểu đồ từ Python

Kiểm tra các hàm đ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 my_function functions trong Python. 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 0x7fe28ce18cf8>
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ý cụ thể việc theo dõi, thông thường bạn sẽ không cần gọi trực tiếp các hàm cụ thể 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 xem xét các dấu vết ngăn xếp dài, đặc biệt là các 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ó thể đang chạy trong ngữ cảnh biểu đồ. Các hàm cốt lõi trong TensorFlow sử dụng các ngữ cảnh đồ thị, chẳng hạn như model.fit() của model.fit() .

Việc gỡ lỗi thực thi háo hức thường dễ dàng hơn nhiều. Dấu vết ngăn xếp phải 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 phức tạp, bạn có thể quay lại sử dụng tính năng thực thi háo hức để gỡ lỗi.

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

  • Gọi trực tiếp các mô hình và lớp dưới dạng có thể gọi

  • Khi sử dụng Keras compile / fit, 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ục 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 cần háo hức. Lưu ý rằng mô hình không được truy nguyên; mặc dù tên của nó, compile chỉ thiết lập các hàm mất mát, tối ưu hóa và các tham số huấn luyện khác.

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

Bây giờ, hãy gọi hàm fit và thấy rằng hàm được truy tìm (hai lần) và sau đó hiệu ứng háo hức không bao giờ chạy nữa.

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

Currently running eagerly 2020-09-30 01:22:01.861126

Currently running eagerly 2020-09-30 01:22:01.982367
2/2 [==============================] - 0s 2ms/step - loss: 1.5409
Epoch 2/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0021
Epoch 3/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0013

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

Tuy nhiên, nếu bạn chạy dù chỉ một kỷ nguyên, bạn có thể thấy tác dụng phụ của sự háo hức gấp đôi.

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-09-30 01:22:02.196963
1/2 [==============>...............] - ETA: 0s - loss: 9.7674e-04
Currently running eagerly 2020-09-30 01:22:02.220018
2/2 [==============================] - 0s 6ms/step - loss: 5.2117e-04

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

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ăng hái trên toàn cầu. Đây là một công tắc bỏ qua các chức năng được truy tìm của hàm đa hình và gọi trực tiếp hàm gốc. Bạn có thể sử dụng điều này để gỡ lỗi.

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

# Create a polymorphic function
polymorphic_function = tf.function(model)

print("Tracing")
# This does, in fact, trace the function
print(polymorphic_function.get_concrete_function(input_data))

print("\nCalling twice eagerly")
# When you run the function again, you will see the side effect
# twice, as the function is running eagerly.
result = polymorphic_function(input_data)
result = polymorphic_function(input_data)
Run all functions eagerly.
Tracing

Currently running eagerly 2020-09-30 01:22:02.249703
ConcreteFunction function(self)
  Args:
    self: float32 Tensor, shape=(60, 28, 28)
  Returns:
    float32 Tensor, shape=(60, 10)

Calling twice eagerly

Currently running eagerly 2020-09-30 01:22:02.254759

Currently running eagerly 2020-09-30 01:22:02.256224

# Don't forget to set it back when you are done
tf.config.experimental_run_functions_eagerly(False)
WARNING:tensorflow:From <ipython-input-1-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.

Theo dõi và hiệu suất

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

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

Bạn có thể thêm một hiệu ứng phụ chỉ mong muốn (chẳng hạn như in một đối số Python) để bạn có thể biết khi nào hàm đang được theo dõi. Tại đây, bạn sẽ thấy thêm việc rút lại vì các đối số Python mới luôn kích hoạt quá trình rút lại.

# 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 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)
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 thảo luận chuyên sâu hơn tại cả trang tham khảo API tf.function và tại hướng dẫn .