ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูแหล่งที่มาบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค |
ภาพรวม
คู่มือนี้กล่าวถึง TensorFlow และ Keras เพื่อสาธิตวิธีการทำงานของ TensorFlow หากคุณต้องการเริ่มต้นใช้งาน Keras ทันที ให้ตรวจสอบ ชุดคู่มือ Keras
ในคู่มือนี้ คุณจะได้เรียนรู้วิธีที่ TensorFlow อนุญาตให้คุณทำการเปลี่ยนแปลงง่ายๆ ในโค้ดของคุณเพื่อรับกราฟ วิธีการจัดเก็บและแสดงกราฟ และวิธีที่คุณจะใช้เพื่อเร่งโมเดลของคุณ
นี่คือภาพรวมภาพรวมที่ครอบคลุมวิธีที่ tf.function
ช่วยให้คุณเปลี่ยนจากการดำเนินการอย่างกระตือรือร้นเป็นการเรียกใช้กราฟได้ สำหรับข้อมูลจำเพาะที่สมบูรณ์ยิ่งขึ้นของ tf.function
ให้ไปที่ คู่มือ tf.function
กราฟคืออะไร?
ในคู่มือสามข้อก่อนหน้านี้ คุณใช้งาน TensorFlow อย่างกระตือรือร้น ซึ่งหมายความว่าการดำเนินการของ TensorFlow ดำเนินการโดย Python การดำเนินการโดยการดำเนินการ และส่งคืนผลลัพธ์กลับไปยัง Python
ในขณะที่การดำเนินการอย่างกระตือรือร้นนั้นมีข้อดีที่เป็นเอกลักษณ์หลายประการ การดำเนินการด้วยกราฟช่วยให้พกพาออกนอก Python และมีแนวโน้มที่จะให้ประสิทธิภาพที่ดีกว่า การดำเนินการกราฟ หมายความว่าการคำนวณเทนเซอร์จะดำเนินการเป็น กราฟ TensorFlow ซึ่งบางครั้งเรียกว่า tf.Graph
หรือเพียงแค่ "กราฟ"
กราฟคือโครงสร้างข้อมูลที่มีชุดของออบเจกต์ tf.Operation
ซึ่งแสดงถึงหน่วยของการคำนวณ และวัตถุ tf.Tensor
ซึ่งเป็นตัวแทนของหน่วยข้อมูลที่ไหลระหว่างการดำเนินการ มีการกำหนดไว้ในบริบท tf.Graph
เนื่องจากกราฟเหล่านี้เป็นโครงสร้างข้อมูล จึงสามารถบันทึก เรียกใช้ และกู้คืนได้ทั้งหมดโดยไม่ต้องใช้โค้ด Python ดั้งเดิม
นี่คือสิ่งที่กราฟ TensorFlow แสดงถึงโครงข่ายประสาทเทียมสองชั้น เมื่อแสดงเป็นภาพใน TensorBoard
ประโยชน์ของกราฟ
ด้วยกราฟ คุณจะมีความยืดหยุ่นอย่างมาก คุณสามารถใช้กราฟ TensorFlow ในสภาพแวดล้อมที่ไม่มีล่าม Python เช่น แอปพลิเคชันมือถือ อุปกรณ์ฝังตัว และเซิร์ฟเวอร์แบ็กเอนด์ TensorFlow ใช้กราฟเป็นรูปแบบสำหรับ โมเดลที่บันทึกไว้ เมื่อส่งออกจาก Python
กราฟยังได้รับการปรับให้เหมาะสมอย่างง่ายดาย ทำให้คอมไพเลอร์ทำการแปลงเช่น:
- อนุมานค่าเทนเซอร์แบบคงที่โดยการพับโหนดคงที่ในการคำนวณของคุณ ("การพับคงที่")
- แยกส่วนย่อยของการคำนวณที่เป็นอิสระและแยกส่วนย่อยระหว่างเธรดหรืออุปกรณ์
- ลดความซับซ้อนของการดำเนินการเลขคณิตโดยกำจัดนิพจน์ย่อยทั่วไป
มีระบบการเพิ่มประสิทธิภาพทั้งหมด Grappler เพื่อดำเนินการนี้และการเร่งความเร็วอื่นๆ
กล่าวโดยย่อ กราฟมีประโยชน์อย่างยิ่ง และให้ TensorFlow ของคุณทำงาน ได้อย่างรวดเร็ว ทำงาน พร้อมกัน และทำงานอย่างมีประสิทธิภาพ บนอุปกรณ์หลายเครื่อง
อย่างไรก็ตาม คุณยังต้องการกำหนดโมเดลการเรียนรู้ของเครื่อง (หรือการคำนวณอื่นๆ) ใน Python เพื่อความสะดวก จากนั้นจึงสร้างกราฟโดยอัตโนมัติเมื่อคุณต้องการ
ติดตั้ง
import tensorflow as tf
import timeit
from datetime import datetime
การใช้ประโยชน์จากกราฟ
คุณสร้างและเรียกใช้กราฟใน TensorFlow โดยใช้ tf.function
ไม่ว่าจะเป็นการโทรโดยตรงหรือเป็นมัณฑนากร tf.function
ใช้ฟังก์ชันปกติเป็นอินพุตและส่งกลับ Function
Function
คือ Python callable ที่สร้างกราฟ TensorFlow จากฟังก์ชัน Python คุณใช้ Function
ในลักษณะเดียวกับที่เทียบเท่ากับ Python
# Define a Python function.
def a_regular_function(x, y, b):
x = tf.matmul(x, y)
x = x + b
return x
# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)
# Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)
orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)
ภายนอก Function
ดูเหมือนเป็นฟังก์ชันปกติที่คุณเขียนโดยใช้การดำเนินการ TensorFlow ข้างล่าง นี้ แตกต่างกัน มาก Function
สรุป tf.Graph
หลายรายการหลัง API หนึ่ง รายการ นั่นคือวิธีที่ Function
สามารถให้ ประโยชน์ของการประมวลผลกราฟ แก่คุณ เช่น ความเร็วและความสามารถในการปรับใช้
tf.function
ใช้กับฟังก์ชัน และฟังก์ชันอื่น ๆ ทั้งหมดที่เรียกใช้ :
def inner_function(x, y, b):
x = tf.matmul(x, y)
x = x + b
return x
# Use the decorator to make `outer_function` a `Function`.
@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.Session
ในเวลาไม่นาน
การแปลงฟังก์ชัน Python เป็นกราฟ
ฟังก์ชันใดๆ ที่คุณเขียนด้วย TensorFlow จะประกอบด้วยการดำเนินการ TF ในตัวและตรรกะ Python เช่น if-then
clauses, loops, break
, return
, continue
และอีกมากมาย แม้ว่าการทำงานของ TensorFlow จะถูกจับโดย tf.Graph
อย่างง่ายดาย ตรรกะเฉพาะของ Python จำเป็นต้องผ่านขั้นตอนพิเศษเพื่อที่จะได้เป็นส่วนหนึ่งของกราฟ tf.function
ใช้ไลบรารีชื่อ AutoGraph ( tf.autograph
) เพื่อแปลงโค้ด Python เป็นโค้ดที่สร้างกราฟ
def simple_relu(x):
if tf.greater(x, 0):
return x
else:
return 0
# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)
print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1 Second branch, with graph: 0
แม้ว่าคุณจะไม่จำเป็นต้องดูกราฟโดยตรง แต่คุณสามารถตรวจสอบผลลัพธ์เพื่อตรวจสอบผลลัพธ์ที่แน่นอนได้ สิ่งเหล่านี้ไม่ง่ายที่จะอ่าน ดังนั้นไม่ต้องดูอย่างระมัดระวัง!
# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x): with ag__.FunctionScope('simple_relu', '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 (do_return, retval_) def set_state(vars_): nonlocal retval_, do_return (do_return, retval_) = vars_ def if_body(): nonlocal retval_, do_return try: do_return = True retval_ = ag__.ld(x) except: do_return = False raise def else_body(): nonlocal retval_, do_return try: do_return = True retval_ = 0 except: do_return = False raise ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2) return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node { name: "x" op: "Placeholder" attr { key: "_user_specified_name" value { s: "x" } } attr { key: "dtype" value { type: DT_INT32 } } attr { key: "shape" value { shape { } } } } node { name: "Greater/y" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node { name: "Greater" op: "Greater" input: "x" input: "Greater/y" attr { key: "T" value { type: DT_INT32 } } } node { name: "cond" op: "StatelessIf" input: "Greater" input: "x" attr { key: "Tcond" value { type: DT_BOOL } } attr { key: "Tin" value { list { type: DT_INT32 } } } attr { key: "Tout" value { list { type: DT_BOOL type: DT_INT32 } } } attr { key: "_lower_using_switch_merge" value { b: true } } attr { key: "_read_only_resource_inputs" value { list { } } } attr { key: "else_branch" value { func { name: "cond_false_34" } } } attr { key: "output_shapes" value { list { shape { } shape { } } } } attr { key: "then_branch" value { func { name: "cond_true_33" } } } } node { name: "cond/Identity" op: "Identity" input: "cond" attr { key: "T" value { type: DT_BOOL } } } node { name: "cond/Identity_1" op: "Identity" input: "cond:1" attr { key: "T" value { type: DT_INT32 } } } node { name: "Identity" op: "Identity" input: "cond/Identity_1" attr { key: "T" value { type: DT_INT32 } } } library { function { signature { name: "cond_false_34" input_arg { name: "cond_placeholder" type: DT_INT32 } output_arg { name: "cond_identity" type: DT_BOOL } output_arg { name: "cond_identity_1" type: DT_INT32 } } node_def { name: "cond/Const" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Const_1" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Const_2" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node_def { name: "cond/Const_3" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Identity" op: "Identity" input: "cond/Const_3:output:0" attr { key: "T" value { type: DT_BOOL } } } node_def { name: "cond/Const_4" op: "Const" attr { key: "dtype" value { type: DT_INT32 } } attr { key: "value" value { tensor { dtype: DT_INT32 tensor_shape { } int_val: 0 } } } } node_def { name: "cond/Identity_1" op: "Identity" input: "cond/Const_4:output:0" attr { key: "T" value { type: DT_INT32 } } } ret { key: "cond_identity" value: "cond/Identity:output:0" } ret { key: "cond_identity_1" value: "cond/Identity_1:output:0" } attr { key: "_construction_context" value { s: "kEagerRuntime" } } arg_attr { key: 0 value { attr { key: "_output_shapes" value { list { shape { } } } } } } } function { signature { name: "cond_true_33" input_arg { name: "cond_identity_1_x" type: DT_INT32 } output_arg { name: "cond_identity" type: DT_BOOL } output_arg { name: "cond_identity_1" type: DT_INT32 } } node_def { name: "cond/Const" op: "Const" attr { key: "dtype" value { type: DT_BOOL } } attr { key: "value" value { tensor { dtype: DT_BOOL tensor_shape { } bool_val: true } } } } node_def { name: "cond/Identity" op: "Identity" input: "cond/Const:output:0" attr { key: "T" value { type: DT_BOOL } } } node_def { name: "cond/Identity_1" op: "Identity" input: "cond_identity_1_x" attr { key: "T" value { type: DT_INT32 } } } ret { key: "cond_identity" value: "cond/Identity:output:0" } ret { key: "cond_identity_1" value: "cond/Identity_1:output:0" } attr { key: "_construction_context" value { s: "kEagerRuntime" } } arg_attr { key: 0 value { attr { key: "_output_shapes" value { list { shape { } } } } } } } } versions { producer: 898 min_consumer: 12 }
โดยส่วนใหญ่ tf.function
จะทำงานโดยไม่มีการพิจารณาเป็นพิเศษ อย่างไรก็ตาม มีข้อควรระวังบางประการ และ คู่มือ tf.function สามารถช่วยได้ที่นี่ เช่นเดียวกับการ อ้างอิง AutoGraph ฉบับสมบูรณ์
Polymorphism: หนึ่ง Function
, กราฟจำนวนมาก
tf.Graph
เชี่ยวชาญเฉพาะกับอินพุตประเภทใดประเภทหนึ่ง (เช่น เทนเซอร์ที่มี dtype
เฉพาะหรืออ็อบเจ็กต์ที่มี id()
เดียวกัน)
ทุกครั้งที่คุณเรียกใช้ Function
ด้วย dtypes
และรูปร่างใหม่ในอาร์กิวเมนต์ Function
จะสร้าง tf.Graph
ใหม่สำหรับอาร์กิวเมนต์ใหม่ dtypes
และรูปร่างของอินพุตของ tf.Graph
เรียกว่า ลายเซ็นอินพุต หรือเพียงแค่ ลายเซ็น
Function
จะเก็บ tf.Graph
ที่สอดคล้องกับลายเซ็นนั้นใน ConcreteFunction
ConcreteFunction
เป็นเครื่องห่อหุ้มรอบ tf.Graph
@tf.function
def my_relu(x):
return tf.maximum(0., x)
# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32) tf.Tensor([1. 0.], shape=(2,), dtype=float32) tf.Tensor([3. 0.], shape=(2,), dtype=float32)
หากมีการเรียกใช้ Function
ด้วยลายเซ็นนั้นแล้ว Function
จะไม่สร้าง tf.Graph
ใหม่
# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32) tf.Tensor([0. 1.], shape=(2,), dtype=float32)
เนื่องจากได้รับการสนับสนุนโดยกราฟหลายตัว Function
จึงเป็นแบบ polymorphic ซึ่งช่วยให้สามารถรองรับประเภทอินพุตได้มากกว่าที่ tf.Graph
ตัวเดียวสามารถแสดงได้ เช่นเดียวกับการเพิ่มประสิทธิภาพแต่ละ tf.Graph
เพื่อประสิทธิภาพที่ดีขึ้น
# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x) Args: x: float32 Tensor, shape=() Returns: float32 Tensor, shape=() my_relu(x=[1, -1]) Returns: float32 Tensor, shape=(2,) my_relu(x) Args: x: float32 Tensor, shape=(2,) Returns: float32 Tensor, shape=(2,)
ใช้ tf.function
จนถึงตอนนี้ คุณได้เรียนรู้วิธีแปลงฟังก์ชัน Python เป็นกราฟได้ง่ายๆ โดยใช้ tf.function
เป็นมัณฑนากรหรือ wrapper แต่ในทางปฏิบัติ การให้ tf.function
ทำงานอย่างถูกต้องอาจเป็นเรื่องยาก! ในส่วนต่อไปนี้ คุณจะได้เรียนรู้วิธีทำให้โค้ดของคุณทำงานตามที่คาดไว้ด้วย tf.function
ได้
การดำเนินการกราฟเทียบกับการดำเนินการที่กระตือรือร้น
รหัสใน Function
สามารถดำเนินการได้ทั้งแบบกระตือรือร้นและเป็นกราฟ โดยค่าเริ่มต้น Function
จะรันโค้ดเป็นกราฟ:
@tf.function
def get_MSE(y_true, y_pred):
sq_diff = tf.pow(y_true - y_pred, 2)
return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 0 4 4 7], shape=(5,), dtype=int32) tf.Tensor([3 6 3 0 6], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
เพื่อตรวจสอบว่ากราฟของ Function
ของคุณใช้การคำนวณแบบเดียวกับฟังก์ชัน Python ที่เทียบเท่ากัน คุณสามารถทำให้มันทำงานได้อย่างรวดเร็วด้วย tf.config.run_functions_eagerly(True)
นี่คือสวิตช์ที่ ปิดความสามารถของ Function
ในการสร้างและเรียกใช้กราฟ แทนที่จะเรียกใช้โค้ดตามปกติ
tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)
อย่างไรก็ตาม Function
สามารถทำงานแตกต่างกันภายใต้กราฟและการดำเนินการอย่างกระตือรือร้น ฟังก์ชัน print
Python เป็นตัวอย่างหนึ่งของความแตกต่างระหว่างสองโหมดนี้ มาดูกันว่าจะเกิดอะไรขึ้นเมื่อคุณแทรกคำสั่ง print
ลงในฟังก์ชันของคุณและเรียกใช้ซ้ำๆ
@tf.function
def get_MSE(y_true, y_pred):
print("Calculating MSE!")
sq_diff = tf.pow(y_true - y_pred, 2)
return tf.reduce_mean(sq_diff)
สังเกตสิ่งที่พิมพ์:
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
ผลลัพธ์น่าประหลาดใจไหม? get_MSE
พิมพ์เพียงครั้งเดียว แม้ว่าจะมีการเรียก สาม ครั้ง
เพื่ออธิบาย คำสั่ง print
จะดำเนินการเมื่อ Function
รันโค้ดต้นฉบับเพื่อสร้างกราฟในกระบวนการที่เรียกว่า "tracing" การติดตามจับการดำเนินการ TensorFlow ลงในกราฟ และ print
จะไม่ถูกบันทึกในกราฟ จากนั้น กราฟนั้นจะถูกดำเนินการสำหรับการโทรทั้งสามครั้ง โดยที่ไม่ต้องเรียกใช้โค้ด Python อีก เลย
ในการตรวจสอบสุขภาพจิต ให้ปิดการดำเนินการกราฟเพื่อเปรียบเทียบ:
# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE! Calculating MSE! Calculating MSE!
tf.config.run_functions_eagerly(False)
print
เป็น ผลข้างเคียงของ Python และมีความแตกต่างอื่นๆ ที่คุณควรทราบเมื่อแปลงฟังก์ชันเป็น Function
เรียนรู้เพิ่มเติมในส่วน ข้อจำกัด ของคู่มือประสิทธิภาพที่ ดีขึ้นด้วย tf.function
การดำเนินการไม่เข้มงวด
การดำเนินการกราฟจะดำเนินการเฉพาะการดำเนินการที่จำเป็นในการสร้างผลกระทบที่สังเกตได้ ซึ่งรวมถึง:
- ค่าส่งคืนของฟังก์ชัน
- เอกสารผลข้างเคียงที่รู้จักกันดีเช่น:
- การดำเนินการอินพุต/เอาต์พุต เช่น
tf.print
- การดำเนินการดีบัก เช่น ฟังก์ชันยืนยันใน
tf.debugging
- การกลายพันธุ์ของ
tf.Variable
- การดำเนินการอินพุต/เอาต์พุต เช่น
ลักษณะการทำงานนี้มักจะเรียกว่า "การดำเนินการที่ไม่เข้มงวด" และแตกต่างจากการดำเนินการที่กระตือรือร้น ซึ่งดำเนินการตามขั้นตอนของการดำเนินการของโปรแกรมทั้งหมด จำเป็นหรือไม่
โดยเฉพาะอย่างยิ่ง การตรวจสอบข้อผิดพลาดรันไทม์ไม่นับเป็นผลที่สังเกตได้ หากการดำเนินการถูกข้ามเนื่องจากไม่จำเป็น จะไม่สามารถทำให้เกิดข้อผิดพลาดรันไทม์ได้
ในตัวอย่างต่อไปนี้ การดำเนินการ tf.gather
ที่ "ไม่จำเป็น" จะถูกข้ามระหว่างการใช้กราฟ ดังนั้นข้อผิดพลาดรันไทม์ InvalidArgumentError
จะไม่เพิ่มขึ้น เนื่องจากจะเป็นการดำเนินการที่กระตือรือร้น อย่าพึ่งพาข้อผิดพลาดที่เกิดขึ้นขณะเรียกใช้กราฟ
def unused_return_eager(x):
# Get index 1 will fail when `len(x) == 1`
tf.gather(x, [1]) # unused
return x
try:
print(unused_return_eager(tf.constant([0.0])))
except tf.errors.InvalidArgumentError as e:
# All operations are run during eager execution so an error is raised.
print(f'{type(e).__name__}: {e}')
tf.Tensor([0.], shape=(1,), dtype=float32)
@tf.function
def unused_return_graph(x):
tf.gather(x, [1]) # unused
return x
# Only needed operations are run during graph exection. The error is not raised.
print(unused_return_graph(tf.constant([0.0])))
tf.Tensor([0.], shape=(1,), dtype=float32)
tf.function
แนวปฏิบัติที่ดีที่สุด
อาจต้องใช้เวลาสักระยะในการทำความคุ้นเคยกับการทำงานของ Function
ในการเริ่มต้นอย่างรวดเร็ว ผู้ใช้ครั้งแรกควรลองเล่นด้วยการตกแต่งฟังก์ชันของเล่นด้วย @tf.function
เพื่อรับประสบการณ์ในการเปลี่ยนจากความกระตือรือร้นไปสู่การสร้างกราฟ
การออกแบบสำหรับ tf.function
อาจเป็นทางออกที่ดีที่สุดสำหรับการเขียนโปรแกรม TensorFlow ที่เข้ากันได้กับกราฟ นี่คือเคล็ดลับบางประการ:
- สลับไปมาระหว่างความกระตือรือร้นและการใช้กราฟตั้งแต่เนิ่นๆ และบ่อยครั้งด้วย
tf.config.run_functions_eagerly
เพื่อระบุว่า/ เมื่อทั้งสองโหมดต่างกัน - สร้าง
tf.Variable
นอกฟังก์ชัน Python และแก้ไขที่ด้านใน เช่นเดียวกับวัตถุที่ใช้tf.Variable
เช่นkeras.layers
,keras.Model
s และtf.optimizers
- หลีกเลี่ยงการเขียนฟังก์ชันที่ ขึ้นอยู่กับตัวแปร Python ภายนอก ยกเว้น
tf.Variable
s และ Keras - ต้องการเขียนฟังก์ชันที่รับเทนเซอร์และประเภท TensorFlow อื่นๆ เป็นอินพุต คุณสามารถส่งผ่านวัตถุประเภทอื่นได้ แต่ ระวัง !
- รวมการคำนวณให้มากที่สุดภายใต้
tf.function
เพื่อเพิ่มประสิทธิภาพสูงสุด ตัวอย่างเช่น ตกแต่งขั้นตอนการฝึกทั้งหมดหรือรอบการฝึกทั้งหมด
เห็นความเร็วขึ้น
tf.function
มักจะช่วยปรับปรุงประสิทธิภาพของโค้ดของคุณ แต่จำนวนการเพิ่มความเร็วนั้นขึ้นอยู่กับประเภทของการคำนวณที่คุณรัน การคำนวณขนาดเล็กสามารถถูกครอบงำโดยค่าใช้จ่ายของการเรียกกราฟ คุณสามารถวัดความแตกต่างของประสิทธิภาพได้ดังนี้:
x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)
def power(x, y):
result = tf.eye(10, dtype=tf.dtypes.int32)
for _ in range(y):
result = tf.matmul(x, result)
return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 2.5637862179974036
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6832536700021592
โดยทั่วไป tf.function
จะใช้เพื่อเพิ่มความเร็วของลูปการฝึก และคุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับมันใน การเขียนลูปการฝึกตั้งแต่ต้น ด้วย Keras
ประสิทธิภาพและการแลกเปลี่ยน
กราฟสามารถเร่งความเร็วโค้ดของคุณได้ แต่กระบวนการสร้างมันมีค่าใช้จ่ายบางอย่าง สำหรับบางฟังก์ชัน การสร้างกราฟจะใช้เวลามากกว่าการดำเนินการของกราฟ การลงทุนนี้มักจะจ่ายคืนอย่างรวดเร็วด้วยการเพิ่มประสิทธิภาพของการดำเนินการที่ตามมา แต่สิ่งสำคัญคือต้องตระหนักว่า สองสามขั้นตอนแรกของการฝึกโมเดลขนาดใหญ่อาจช้าลงเนื่องจากการติดตาม
ไม่ว่าโมเดลของคุณจะใหญ่แค่ไหน คุณต้องการหลีกเลี่ยงการติดตามบ่อยๆ คู่มือ tf.function
จะอธิบายวิธีตั้งค่าข้อมูลจำเพาะอินพุตและใช้อาร์กิวเมนต์เทนเซอร์ เพื่อหลีกเลี่ยงการย้อนกลับ หากคุณพบว่าประสิทธิภาพการทำงานต่ำผิดปกติ เป็นความคิดที่ดีที่จะตรวจสอบว่าคุณกำลังย้อนกลับโดยไม่ได้ตั้งใจหรือไม่
การติดตาม Function
คือเมื่อใด
เมื่อต้องการค้นหาว่า Function
ของคุณกำลังติดตามเมื่อใด ให้เพิ่มคำสั่ง print
ลงในโค้ด ตามหลักการทั่วไป Function
จะดำเนินการ print
คำสั่งทุกครั้งที่ติดตาม
@tf.function
def a_function_with_python_side_effect(x):
print("Tracing!") # An eager-only side effect.
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)))
Tracing! tf.Tensor(6, shape=(), dtype=int32) tf.Tensor(11, shape=(), dtype=int32)
# 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) Tracing! tf.Tensor(11, shape=(), dtype=int32)
อาร์กิวเมนต์ Python ใหม่จะทริกเกอร์การสร้างกราฟใหม่เสมอ ดังนั้นจึงมีการติดตามเพิ่มเติม
ขั้นตอนถัดไป
คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับ tf.function
ได้ในหน้าอ้างอิง API และโดยปฏิบัติตามคำแนะนำ ประสิทธิภาพที่ ดีขึ้นด้วย tf.function