本頁面由 Cloud Translation API 翻譯而成。
Switch to English

簡介圖表和功能

查看上TensorFlow.org 在谷歌Colab運行 GitHub上查看源代碼 下載筆記本

簡介圖和tf.function

本指南去TensorFlow和Keras的表面之下,看TensorFlow是如何工作的。如果你不是想立刻上手Keras,請參閱我們的Keras導遊的集合

在本指南中,你會看到TensorFlow如何讓你做你的代碼簡單修改得到的圖表,以及它們是如何存儲和代表,以及如何使用它們來加快和出口的模式的核心。

這是一個短的形式引入;一個完整的介紹了這些概念,看到tf.function指南

什麼是圖表?

在過去三年導遊,你已經看到TensorFlow 急切地運行。這意味著TensorFlow操作由Python中,通過操作操作執行,並返回結果返回到Python。渴望TensorFlow採用GPU的優勢,讓您的地方變量,張量,並在GPU和熱塑性聚氨酯甚至操作。這也很容易調試。

對於一些用戶來說,你可能永遠需要或想要離開的Python。

但是,運行TensorFlow運算由-OP在Python防止加速度的宿主否則可用。如果你可以提取Python的張量計算,你可以讓他們成為圖形

圖形是包含了一組數據結構tf.Operation對象,其表示計算的單元;和tf.Tensor對象,其表示數據的操作之間流動的單位。他們是在一個定義tf.Graph環境。由於這些圖是數據結構,它們可以被保存,運行,並恢復所有沒有原始Python代碼。

這是TensorBoard可視化時,一個簡單的兩層圖形看起來像什麼。

兩層tensorflow圖表

圖的好處

隨著圖形,你有具有很大的靈活性。您可以在沒有一個Python解釋器,如移動應用,嵌入式設備和後端服務器的環境中使用您的TensorFlow圖。 TensorFlow使用圖形作為保存的模型的格式,當它導出他們的Python。

圖表也容易優化,使編譯器進行轉換,如:

  • 在你的計算折疊常數節點(“常量折疊”)靜態推斷張量的值。
  • 的計算的程序是獨立於和線程或設備之間將它們分割分開的子部分。
  • 通過消除常見的子表達式簡化算術運算。

有一個完整的優化系統, 夾鉗 ,執行這個和其他的加速。

總之,圖形是非常有用的,讓你的TensorFlow跑得並行運行,並有效地在多台設備上運行。

但是,你還是要在Python來定義我們的機器學習模型(或其他計算)為方便起見,當你需要的時候再自動構造圖。

跟踪圖

您在TensorFlow創建圖形的方法是使用tf.function ,無論是作為一個直接調用或作為裝飾。

 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功能的Python可調用該工作方式相同的Python其等同物。他們有一個特別的類( python.eager.def_function.Function ),但你他們的行為就像非追踪版本。

tf.function遞歸跟踪任何Python函數調用。

 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)

如果你已經使用TensorFlow 1.x中,你會發現,在任何時候都沒有,你需要定義一個Placeholdertf.Sesssion

流量控制和副作用

流量控制和循環是通過轉換為TensorFlow tf.autograph默認。簽名使用的方法的組合,包括標準化的循環結構,展開和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.]

您可以直接調用簽名轉換,看看Python是如何轉化成TensorFlow歡聲笑語。這是居多,不可讀的,但你可以看到的轉變。

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


簽名會自動轉換if-then從句,循環, breakreturncontinue等。

在大多數情況下,簽名會工作,沒有特殊的考慮。不過,也有一些注意事項,以及tf.function指南可以幫助在這裡,還有完整的簽名參考

眼看著加快

只是包裹在一個用張量函數tf.function不會自動加速你的代碼。對於小的函數調用在單個機器上幾次,調用圖或圖表片段可能會主導運行時的開銷。另外,如果多數計算已經加速器,如GPU重卷積的堆棧發生,圖形加速也不會很大。

對於複雜的計算,圖形可以提供signficiant加速。這是因為曲線降低的Python到設備通信,並且執行一些加速比。

此代碼倍一些小緻密層幾運行。

 # 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

多態函數

當您跟踪功能,您可以創建一個Function對象是多態的 。多態函數是一個Python可調用封裝了若干具體功能的圖形背後一個API。

您可以使用此Function在所有不同類型的dtypes和形狀。每次有新的說法簽名調用它的時候,原來的功能得到重新追踪與新的論據。該Function然後存儲tf.Graph對應於一個該跟踪concrete_function 。如果該功能已經被追踪與那樣的說法,你只是讓你預先追踪圖。

從概念上講,則:

  • tf.Graph是描述計算原始的,便攜式數據結構
  • Function是一個高速緩存,追查,過ConcreteFunctions調度
  • 一個ConcreteFunction是圍繞一個圖表,讓你從Python的執行圖形的渴望兼容包裝

檢查多態函數

您可以檢查a_function ,這是調用的結果tf.function的Python函數my_function 。在這個例子中,調用a_function在三個不同的具體三種功能類型的參數結果。

 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)

在這個例子中,你看到的很遠堆中。除非你專門管理跟踪,你通常不會需要調用具體功能直接在這裡如圖所示。

恢復到執行心切

你可能會發現自己看著長的堆棧跟踪,特別是那些指tf.Graphwith tf.Graph().as_default()這意味著你可能在一個圖形上下文中運行。在TensorFlow使用圖形上下文的核心功能,例如Keras的model.fit()

它往往是調試急於執行要容易得多。堆棧跟踪應該是比較短,容易理解。

在圖中,使調試棘手的情況下,你可以恢復使用急於執行調試。

這裡有方法可以確保你急切地運行:

  • 直接調用模式和圖層作為可調用

  • 當使用Keras編譯/配合,在編譯時利用model.compile(run_eagerly=True)

  • 通過設置全局執行模式tf.config.run_functions_eagerly(True)

使用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)
 

首先,編譯模型,而不急於。需要注意的是該模型不跟踪;儘管它的名字, compile只設置了損失函數,優化等訓練參數。

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

現在,呼叫fit ,看到該功能被跟踪(兩次),然後急切效果不會再運行。

 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>

如果您在躍躍欲試,甚至一個單一的時代,但是,你可以看到兩次渴望副作用。

 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>

使用run_functions_eagerly

您也可以全局設置一切急切地運行。請注意,這只是工作,如果你再跟踪;追踪功能將保持跟踪和運行為圖形。

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

跟踪和性能

跟踪成本一定的開銷。儘管追踪小功能是快速,大的模型可以明顯的掛鐘時間追查。這種投資通常會很快支付了性能提升回來,但要知道,任何大型模型訓練前幾個時期可以慢,由於跟踪是很重要的。

無論多麼大的模型,要避免頻繁地跟踪。這將tf.function指南的部分討論如何設置輸入規格和使用參數張,以避免追溯。如果你發現你正在表現異常差,這是很好的檢查,看看是否你折回意外。

您可以添加一個熱心,唯一的副作用(如打印一個Python參數),所以你可以看到正在跟踪的功能時。在這裡,你看到額外折回,因為新的Python參數始終觸發折回。

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

下一步

您可以同時在閱讀更深入的討論tf.function API參考頁,並在引導