หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

ความรู้เบื้องต้นเกี่ยวกับกราฟและฟังก์ชั่น

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

รู้เบื้องต้นเกี่ยวกับกราฟและ tf.function

คู่มือนี้อยู่ใต้พื้นผิวของ TensorFlow และ Keras เพื่อดูว่า TensorFlow ทำงานอย่างไร หากคุณต้องการเริ่มต้นกับ Keras แทนโปรดดู ชุดคู่มือ Keras ของ เรา

ในคู่มือนี้คุณจะเห็นหลักของวิธีที่ TensorFlow อนุญาตให้คุณทำการเปลี่ยนแปลงอย่างง่าย ๆ ในโค้ดของคุณเพื่อรับกราฟและวิธีการจัดเก็บและแสดงผลและวิธีการที่พวกเขาใช้เพื่อเร่งและส่งออกแบบจำลองของคุณ

นี่คือการแนะนำแบบสั้น สำหรับการแนะนำแนวคิดเหล่านี้ทั้งหมดให้ดู ที่คำแนะนำ tf.function

กราฟคืออะไร?

ในสามแนวทางก่อนหน้านี้คุณได้เห็น TensorFlow ทำงาน อย่างกระตือรือร้น ซึ่งหมายความว่าการดำเนินการ TensorFlow ถูกดำเนินการโดย Python การดำเนินการโดยการดำเนินการและส่งคืนผลลัพธ์กลับสู่ Python Eager TensorFlow ใช้ประโยชน์จาก GPU ช่วยให้คุณวางตัวแปรเทนเซอร์และแม้แต่การทำงานกับ GPU และ TPU นอกจากนี้ยังง่ายต่อการตรวจแก้จุดบกพร่อง

สำหรับผู้ใช้บางคนคุณอาจไม่ต้องการหรือไม่ต้องการออกจาก Python

อย่างไรก็ตามการเรียกใช้ TensorFlow op-by-op ใน Python ป้องกันโฮสต์ของการเร่งความเร็วเป็นอย่างอื่น หากคุณสามารถแยกการคำนวณเมตริกซ์จาก Python คุณสามารถทำให้พวกเขาเป็น กราฟ

กราฟเป็นโครงสร้างข้อมูลที่มีชุดของวัตถุ tf.Operation ซึ่งเป็นหน่วยของการคำนวณ และ tf.Tensor objects ซึ่งเป็นหน่วยของข้อมูลที่ไหลระหว่างการดำเนินการ พวกเขาถูกกำหนดในบริบท tf.Graph เนื่องจากกราฟเหล่านี้เป็นโครงสร้างข้อมูลจึงสามารถบันทึกเรียกใช้และเรียกคืนทั้งหมดได้โดยไม่ต้องใช้รหัส Python ดั้งเดิม

นี่คือลักษณะกราฟสองชั้นที่เรียบง่ายเมื่อมองเห็นใน TensorBoard

กราฟแรงดึงสองชั้น

ประโยชน์ของกราฟ

ด้วยกราฟคุณมีความยืดหยุ่นอย่างมาก คุณสามารถใช้กราฟ TensorFlow ในสภาพแวดล้อมที่ไม่มีตัวแปล Python เช่นแอปพลิเคชันมือถืออุปกรณ์ฝังตัวและเซิร์ฟเวอร์ด้านหลัง TensorFlow ใช้กราฟเป็นรูปแบบสำหรับโมเดลที่บันทึกเมื่อส่งออกจาก Python

กราฟยังได้รับการปรับให้เหมาะสมอย่างง่ายดายทำให้คอมไพเลอร์ทำการแปลงเช่น:

  • อนุมานค่าของเทนเซอร์แบบคงที่โดยการพับโหนดคงที่ในการคำนวณของคุณ ("การพับคงที่")
  • แยกส่วนย่อยของการคำนวณที่เป็นอิสระและแยกพวกเขาระหว่างหัวข้อหรืออุปกรณ์
  • ลดความซับซ้อนของการดำเนินการทางคณิตศาสตร์โดยกำจัดนิพจน์ย่อยทั่วไป

Grappler มี ระบบปรับให้เหมาะสมทั้งหมดเพื่อทำการเร่งความเร็วและอื่น ๆ

กล่าวโดยย่อคือกราฟมีประโยชน์อย่างยิ่งและช่วยให้ 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 ฟังก์ชั่น callables งูหลาม ที่ทำงานเช่นเดียวกับงูหลามเทียบเท่าของพวกเขา พวกเขามีคลาสเฉพาะ ( 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 คุณจะสังเกตเห็นว่าในเวลาไม่นานคุณจำเป็นต้องกำหนดตัว Placeholder หรือ tf.Sesssion

การควบคุมการไหลและผลข้างเคียง

การควบคุมการไหลและลูปจะถูกแปลงเป็น TensorFlow ผ่านทาง tf.autograph โดยค่าเริ่มต้น 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.]

คุณสามารถเรียกการแปลง Autograph โดยตรงเพื่อดูว่า 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 , ลูป, break , return , continue และอื่น ๆ โดยอัตโนมัติ

ส่วนใหญ่แล้ว Autograph จะทำงานโดยไม่มีการพิจารณาเป็นพิเศษ อย่างไรก็ตามมีคำเตือนอยู่บ้างและ คำแนะนำ tf.function สามารถช่วยได้ที่นี่เช่นเดียวกับการ อ้างอิงลายเซ็นที่สมบูรณ์

เห็นความเร็วขึ้น

เพียงแค่ห่อฟังก์ชั่นการใช้เมตริกซ์ใน tf.function ไม่ได้ทำให้โค้ดของคุณเร็วขึ้นโดยอัตโนมัติ สำหรับฟังก์ชั่นขนาดเล็กที่เรียกว่าสองสามครั้งในเครื่องเดียวค่าใช้จ่ายในการเรียกกราฟหรือส่วนของกราฟอาจครอบงำรันไทม์ นอกจากนี้หากการคำนวณส่วนใหญ่เกิดขึ้นกับเครื่องเร่งความเร็วเช่นการซ้อนของ GPU ที่ซับซ้อนการเพิ่มความเร็วกราฟจะไม่ใหญ่

สำหรับการคำนวณที่ซับซ้อนกราฟสามารถให้การเร่งความเร็วแบบ signficiant นี่เป็นเพราะกราฟลดการสื่อสารของ Python-to-device และดำเนินการเร่งความเร็วบางอย่าง

รหัสนี้ใช้เวลาสองสามรันบนเลเยอร์หนาแน่นเล็ก ๆ

 # 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 วัตถุเป็น polymorphic ฟังก์ชั่น polymorphic คือ Python callable ที่ห่อหุ้มกราฟฟังก์ชั่นคอนกรีตจำนวนมากที่อยู่ด้านหลังหนึ่ง API

คุณสามารถใช้ Function ได้ที่แตกต่างกันของ dtypes และรูปร่าง ทุกครั้งที่คุณเรียกใช้ด้วยลายเซ็นอาร์กิวเมนต์ใหม่ฟังก์ชันดั้งเดิมจะได้รับการสืบค้นกลับด้วยอาร์กิวเมนต์ใหม่ Function จะเก็บ tf.Graph สอดคล้องกับการติดตามนั้นใน concrete_function หากฟังก์ชันได้รับการติดตามด้วยอาร์กิวเมนต์ชนิดนั้นคุณก็จะได้กราฟที่ได้รับการตรวจสอบล่วงหน้า

ตามแนวคิดแล้ว:

  • tf.Graph เป็นโครงสร้างข้อมูลแบบพกพาที่อธิบายการคำนวณ
  • Function คือการแคชการติดตามการมอบหมายงานมากกว่า ConcreteFunctions
  • ConcreteFunction เป็นกระดาษห่อหุ้มที่เข้ากันได้กระตือรือร้นรอบกราฟที่ช่วยให้คุณดำเนินการกราฟจาก Python

การตรวจสอบฟังก์ชัน polymorphic

คุณสามารถตรวจสอบ 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.Graph หรือ with tf.Graph().as_default() ซึ่งหมายความว่าคุณมีแนวโน้มที่จะทำงานในบริบทกราฟ ฟังก์ชั่นหลักใน TensorFlow ใช้บริบทกราฟเช่น Keras's model.fit()

การดีบักการดำเนินการกระตือรือร้นมักจะง่ายกว่ามาก การติดตามสแต็กควรค่อนข้างสั้นและง่ายต่อการเข้าใจ

ในสถานการณ์ที่กราฟทำการดีบักหากินคุณสามารถย้อนกลับไปใช้การดำเนินการกระตือรือร้นที่จะดีบั๊ก

นี่คือวิธีที่คุณสามารถมั่นใจได้ว่าคุณกำลังวิ่งอย่างกระตือรือร้น:

  • โทรรุ่นและเลเยอร์โดยตรงเป็น callables

  • เมื่อใช้การคอมไพล์ Keras / fit ณ เวลารวบรวมใช้ 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 และที่ คำแนะนำ