เอกสารประกอบ | ตัวอย่าง
Sonnet เป็นไลบรารีที่สร้างขึ้นจาก TensorFlow 2 ซึ่งได้รับการออกแบบมาเพื่อมอบนามธรรมที่เรียบง่ายและเรียบเรียงได้สำหรับการวิจัยแมชชีนเลิร์นนิง
Sonnet ได้รับการออกแบบและสร้างโดยนักวิจัยจาก DeepMind สามารถใช้เพื่อสร้างโครงข่ายประสาทเทียมเพื่อวัตถุประสงค์ที่แตกต่างกันมากมาย (การเรียนรู้แบบไม่มี/แบบมีผู้ดูแล การเรียนรู้แบบเสริมกำลัง ...) เราพบว่ามันเป็นนามธรรมที่ประสบความสำเร็จสำหรับองค์กรของเรา คุณก็เช่นกัน!
โดยเฉพาะอย่างยิ่ง Sonnet มีโมเดลการเขียนโปรแกรมที่เรียบง่ายแต่ทรงพลังโดยมีศูนย์กลางอยู่ที่แนวคิดเดียว: snt.Module
โมดูลสามารถเก็บการอ้างอิงถึงพารามิเตอร์ โมดูลอื่นๆ และวิธีการที่ใช้ฟังก์ชันบางอย่างกับอินพุตของผู้ใช้ Sonnet มาพร้อมกับโมดูลที่กำหนดไว้ล่วงหน้าจำนวนมาก (เช่น snt.Linear
, snt.Conv2D
, snt.BatchNorm
) และเครือข่ายโมดูลที่กำหนดไว้ล่วงหน้าบางส่วน (เช่น snt.nets.MLP
) แต่ผู้ใช้ยังได้รับการสนับสนุนให้สร้างโมดูลของตนเองด้วย
แตกต่างจากเฟรมเวิร์กอื่นๆ Sonnet ไม่มีความเห็นอย่างมากเกี่ยวกับ วิธีที่ คุณจะใช้โมดูลของคุณ โมดูลได้รับการออกแบบมาให้สามารถบรรจุในตัวเองและแยกออกจากกันโดยสิ้นเชิง Sonnet ไม่ได้มาพร้อมกับกรอบการฝึกอบรม และผู้ใช้ควรสร้างกรอบการทำงานของตนเองหรือปรับใช้กรอบงานที่ผู้อื่นสร้างขึ้น
Sonnet ยังได้รับการออกแบบมาให้เข้าใจง่าย โค้ดของเรา (หวังว่า!) ชัดเจนและเน้นย้ำ เมื่อเราเลือกค่าเริ่มต้น (เช่น ค่าเริ่มต้นสำหรับค่าพารามิเตอร์เริ่มต้น) เราพยายามชี้ให้เห็นว่าเพราะเหตุใด
วิธีที่ง่ายที่สุดในการลองใช้ Sonnet คือการใช้ Google Colab ซึ่งมีสมุดบันทึก Python ฟรีที่เชื่อมต่อกับ GPU หรือ TPU
snt.distribute
ในการเริ่มต้นติดตั้ง TensorFlow 2.0 และ Sonnet 2:
$ pip install tensorflow tensorflow-probability
$ pip install dm-sonnet
คุณสามารถเรียกใช้สิ่งต่อไปนี้เพื่อตรวจสอบสิ่งต่าง ๆ ที่ติดตั้งอย่างถูกต้อง:
import tensorflow as tf
import sonnet as snt
print ( "TensorFlow version {}" . format ( tf . __version__ ))
print ( "Sonnet version {}" . format ( snt . __version__ ))
Sonnet มาพร้อมกับโมดูลในตัวจำนวนหนึ่งที่คุณสามารถใช้งานได้เล็กน้อย ตัวอย่างเช่น ในการกำหนด MLP เราสามารถใช้โมดูล snt.Sequential
เพื่อเรียกลำดับของโมดูล โดยส่งเอาต์พุตของโมดูลที่กำหนดเป็นอินพุตสำหรับโมดูลถัดไป เราสามารถใช้ snt.Linear
และ tf.nn.relu
เพื่อกำหนดการคำนวณของเรา:
mlp = snt . Sequential ([
snt . Linear ( 1024 ),
tf . nn . relu ,
snt . Linear ( 10 ),
])
หากต้องการใช้โมดูลของเรา เราจำเป็นต้อง "เรียก" มัน โมดูล Sequential
(และโมดูลส่วนใหญ่) กำหนด __call__
วิธีการซึ่งหมายความว่าคุณสามารถเรียกพวกเขาตามชื่อได้:
logits = mlp ( tf . random . normal ([ batch_size , input_size ]))
เป็นเรื่องปกติมากที่จะขอพารามิเตอร์ทั้งหมดสำหรับโมดูลของคุณ โมดูลส่วนใหญ่ใน Sonnet จะสร้างพารามิเตอร์ในครั้งแรกที่ถูกเรียกพร้อมกับอินพุตบางส่วน (เนื่องจากในกรณีส่วนใหญ่ รูปร่างของพารามิเตอร์จะเป็นฟังก์ชันของอินพุต) โมดูล Sonnet มีคุณสมบัติสองประการสำหรับการเข้าถึงพารามิเตอร์
คุณสมบัติ variables
ส่งคืน tf.Variable
ทั้งหมด ที่อ้างอิงโดยโมดูลที่กำหนด:
all_variables = mlp . variables
เป็นที่น่าสังเกตว่า tf.Variable
s ไม่ได้ใช้สำหรับพารามิเตอร์ของโมเดลของคุณเท่านั้น ตัวอย่างเช่น ใช้เพื่อคงสถานะในเมตริกที่ใช้ใน snt.BatchNorm
ในกรณีส่วนใหญ่ ผู้ใช้จะดึงข้อมูลตัวแปรโมดูลเพื่อส่งต่อไปยังเครื่องมือเพิ่มประสิทธิภาพที่จะอัปเดต ในกรณีนี้ ตัวแปรที่ไม่สามารถฝึกได้โดยทั่วไปไม่ควรอยู่ในรายการนั้น เนื่องจากมีการอัปเดตผ่านกลไกอื่น TensorFlow มีกลไกในตัวเพื่อทำเครื่องหมายตัวแปรว่า "ฝึกได้" (พารามิเตอร์ของโมเดลของคุณ) เทียบกับฝึกไม่ได้ (ตัวแปรอื่นๆ) Sonnet มีกลไกในการรวบรวมตัวแปรที่สามารถฝึกได้ทั้งหมดจากโมดูลของคุณ ซึ่งอาจเป็นสิ่งที่คุณต้องการส่งผ่านไปยังเครื่องมือเพิ่มประสิทธิภาพ:
model_parameters = mlp . trainable_variables
Sonnet ขอแนะนำให้ผู้ใช้ซับคลาส snt.Module
เพื่อกำหนดโมดูลของตนเอง เริ่มต้นด้วยการสร้างเลเยอร์ Linear
ง่ายๆ ชื่อ MyLinear
:
class MyLinear ( snt . Module ):
def __init__ ( self , output_size , name = None ):
super ( MyLinear , self ). __init__ ( name = name )
self . output_size = output_size
@ snt . once
def _initialize ( self , x ):
initial_w = tf . random . normal ([ x . shape [ 1 ], self . output_size ])
self . w = tf . Variable ( initial_w , name = "w" )
self . b = tf . Variable ( tf . zeros ([ self . output_size ]), name = "b" )
def __call__ ( self , x ):
self . _initialize ( x )
return tf . matmul ( x , self . w ) + self . b
การใช้โมดูลนี้เป็นเรื่องเล็กน้อย:
mod = MyLinear ( 32 )
mod ( tf . ones ([ batch_size , input_size ]))
โดยการแบ่งคลาสย่อย snt.Module
คุณจะได้รับคุณสมบัติดีๆ มากมายฟรี ตัวอย่างเช่น การใช้งานเริ่มต้นของ __repr__
ซึ่งแสดงอาร์กิวเมนต์ตัวสร้าง (มีประโยชน์มากสำหรับการดีบักและการวิปัสสนา):
>> > print ( repr ( mod ))
MyLinear ( output_size = 10 )
คุณยังได้รับคุณสมบัติ variables
และ trainable_variables
:
>> > mod . variables
( < tf . Variable 'my_linear/b:0' shape = ( 10 ,) ...) > ,
< tf . Variable 'my_linear/w:0' shape = ( 1 , 10 ) ...) > )
คุณอาจสังเกตเห็นคำนำหน้า my_linear
ในตัวแปรด้านบน เนื่องจากโมดูล Sonnet ยังป้อนขอบเขตชื่อโมดูลทุกครั้งที่มีการเรียกใช้เมธอด การป้อนขอบเขตชื่อโมดูลทำให้เราได้กราฟที่มีประโยชน์มากขึ้นสำหรับเครื่องมืออย่าง TensorBoard ที่จะใช้งาน (เช่น การดำเนินการทั้งหมดที่เกิดขึ้นภายใน my_linear จะอยู่ในกลุ่มที่เรียกว่า my_linear)
นอกจากนี้ โมดูลของคุณจะรองรับจุดตรวจสอบ TensorFlow และโมเดลที่บันทึกไว้ ซึ่งเป็นฟีเจอร์ขั้นสูงที่จะกล่าวถึงในภายหลัง
Sonnet รองรับรูปแบบการทำให้เป็นอนุกรมหลายรูปแบบ รูปแบบที่ง่ายที่สุดที่เรารองรับคือ pickle
ของ Python และโมดูลในตัวทั้งหมดได้รับการทดสอบเพื่อให้แน่ใจว่าสามารถบันทึก/โหลดผ่าน Pickle ในกระบวนการ Python เดียวกันได้ โดยทั่วไป เราไม่สนับสนุนการใช้ผักดอง เนื่องจาก TensorFlow หลายส่วนไม่ได้รับการสนับสนุนอย่างดี และจากประสบการณ์ของเราก็ค่อนข้างเปราะบาง
อ้างอิง: https://www.tensorflow.org/alpha/guide/checkpoints
สามารถใช้จุดตรวจสอบ TensorFlow เพื่อบันทึกค่าพารามิเตอร์เป็นระยะๆ ในระหว่างการฝึก สิ่งนี้มีประโยชน์ในการบันทึกความคืบหน้าของการฝึกอบรมในกรณีที่โปรแกรมของคุณขัดข้องหรือหยุดทำงาน Sonnet ได้รับการออกแบบให้ทำงานได้อย่างหมดจดด้วยจุดตรวจ TensorFlow:
checkpoint_root = "/tmp/checkpoints"
checkpoint_name = "example"
save_prefix = os . path . join ( checkpoint_root , checkpoint_name )
my_module = create_my_sonnet_module () # Can be anything extending snt.Module.
# A `Checkpoint` object manages checkpointing of the TensorFlow state associated
# with the objects passed to it's constructor. Note that Checkpoint supports
# restore on create, meaning that the variables of `my_module` do **not** need
# to be created before you restore from a checkpoint (their value will be
# restored when they are created).
checkpoint = tf . train . Checkpoint ( module = my_module )
# Most training scripts will want to restore from a checkpoint if one exists. This
# would be the case if you interrupted your training (e.g. to use your GPU for
# something else, or in a cloud environment if your instance is preempted).
latest = tf . train . latest_checkpoint ( checkpoint_root )
if latest is not None :
checkpoint . restore ( latest )
for step_num in range ( num_steps ):
train ( my_module )
# During training we will occasionally save the values of weights. Note that
# this is a blocking call and can be slow (typically we are writing to the
# slowest storage on the machine). If you have a more reliable setup it might be
# appropriate to save less frequently.
if step_num and not step_num % 1000 :
checkpoint . save ( save_prefix )
# Make sure to save your final values!!
checkpoint . save ( save_prefix )
อ้างอิง: https://www.tensorflow.org/alpha/guide/saved_model
โมเดลที่บันทึกไว้ของ TensorFlow สามารถใช้เพื่อบันทึกสำเนาของเครือข่ายของคุณที่แยกออกจากแหล่งที่มาของ Python ซึ่งเปิดใช้งานได้โดยการบันทึกกราฟ TensorFlow ที่อธิบายการคำนวณและจุดตรวจสอบที่มีค่าของน้ำหนัก
สิ่งแรกที่ต้องทำเพื่อสร้างโมเดลที่บันทึกไว้คือการสร้าง snt.Module
ที่คุณต้องการบันทึก:
my_module = snt . nets . MLP ([ 1024 , 1024 , 10 ])
my_module ( tf . ones ([ 1 , input_size ]))
ต่อไป เราต้องสร้างอีกโมดูลหนึ่งที่อธิบายส่วนเฉพาะของโมเดลของเราที่เราต้องการส่งออก เราแนะนำให้ทำเช่นนี้ (แทนที่จะแก้ไขโมเดลดั้งเดิมแบบแทนที่) เพื่อให้คุณสามารถควบคุมสิ่งที่ส่งออกจริงได้อย่างละเอียด โดยทั่วไปสิ่งนี้เป็นสิ่งสำคัญในการหลีกเลี่ยงการสร้างโมเดลที่บันทึกไว้ขนาดใหญ่มาก และเพื่อให้คุณแชร์เฉพาะส่วนของโมเดลที่คุณต้องการ (เช่น คุณต้องการแชร์ตัวสร้างสำหรับ GAN เท่านั้น แต่เก็บตัวแบ่งแยกไว้เป็นส่วนตัว)
@ tf . function ( input_signature = [ tf . TensorSpec ([ None , input_size ])])
def inference ( x ):
return my_module ( x )
to_save = snt . Module ()
to_save . inference = inference
to_save . all_variables = list ( my_module . variables )
tf . saved_model . save ( to_save , "/tmp/example_saved_model" )
ตอนนี้เรามีโมเดลที่บันทึกไว้ในโฟลเดอร์ /tmp/example_saved_model
:
$ ls -lh /tmp/example_saved_model
total 24K
drwxrwsr-t 2 tomhennigan 154432098 4.0K Apr 28 00:14 assets
-rw-rw-r-- 1 tomhennigan 154432098 14K Apr 28 00:15 saved_model.pb
drwxrwsr-t 2 tomhennigan 154432098 4.0K Apr 28 00:15 variables
การโหลดโมเดลนี้ทำได้ง่ายและสามารถทำได้บนเครื่องอื่นโดยไม่ต้องใช้โค้ด Python ที่สร้างโมเดลที่บันทึกไว้:
loaded = tf . saved_model . load ( "/tmp/example_saved_model" )
# Use the inference method. Note this doesn't run the Python code from `to_save`
# but instead uses the TensorFlow Graph that is part of the saved model.
loaded . inference ( tf . ones ([ 1 , input_size ]))
# The all_variables property can be used to retrieve the restored variables.
assert len ( loaded . all_variables ) > 0
โปรดทราบว่าวัตถุที่โหลดไม่ใช่โมดูล Sonnet แต่เป็นวัตถุคอนเทนเนอร์ที่มีวิธีการเฉพาะ (เช่น inference
) และคุณสมบัติ (เช่น all_variables
) ที่เราเพิ่มในบล็อกก่อนหน้า
ตัวอย่าง: https://github.com/deepmind/sonnet/blob/v2/examples/distributed_cifar10.ipynb
Sonnet ได้รับการสนับสนุนสำหรับการฝึกอบรมแบบกระจายโดยใช้กลยุทธ์การกระจาย TensorFlow ที่กำหนดเอง
ข้อแตกต่างที่สำคัญระหว่าง Sonnet และการฝึกอบรมแบบกระจายโดยใช้ tf.keras
คือ โมดูล Sonnet และเครื่องมือเพิ่มประสิทธิภาพจะไม่ทำงานแตกต่างออกไปเมื่อทำงานภายใต้กลยุทธ์การกระจาย (เช่น เราไม่ได้เฉลี่ยการไล่ระดับสีของคุณหรือซิงค์สถิติบรรทัดฐานของแบทช์ของคุณ) เราเชื่อว่าผู้ใช้ควรควบคุมการฝึกอบรมในด้านต่างๆ เหล่านี้ได้อย่างเต็มที่ และไม่ควรนำพวกเขาเข้าไปในห้องสมุด ข้อดีข้อเสียคือ คุณต้องใช้คุณสมบัติเหล่านี้ในสคริปต์การฝึกของคุณ (โดยทั่วไปแล้ว นี่เป็นโค้ดเพียง 2 บรรทัดเพื่อลดการไล่ระดับสีก่อนที่จะใช้เครื่องมือเพิ่มประสิทธิภาพ) หรือสลับโมดูลที่รับรู้ถึงการกระจายอย่างชัดเจน (เช่น snt.distribute.CrossReplicaBatchNorm
)
ตัวอย่าง Cifar-10 แบบกระจายของเราอธิบายการฝึกอบรม multi-GPU กับ Sonnet