التوثيق | أمثلة
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 تنسيقات التسلسل المتعددة. إن أبسط تنسيق ندعمه هو Python's pickle
، ويتم اختبار جميع الوحدات المضمنة للتأكد من إمكانية حفظها/تحميلها عبر 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 والمحسنات لا تتصرف بشكل مختلف عند تشغيلها بموجب استراتيجيات التوزيع (على سبيل المثال، لا نقوم بحساب متوسط التدرجات الخاصة بك أو مزامنة إحصائيات معيار الدفعة الخاصة بك). نحن نؤمن بأن المستخدمين يجب أن يتمتعوا بالسيطرة الكاملة على هذه الجوانب من تدريبهم ولا ينبغي أن يتم تخزينهم في المكتبة. تتمثل المقايضة هنا في أنك تحتاج إلى تنفيذ هذه الميزات في البرنامج النصي للتدريب الخاص بك (عادةً ما يكون هذا مجرد سطرين من التعليمات البرمجية لتقليل التدرجات اللونية الخاصة بك قبل تطبيق المحسن الخاص بك) أو تبديل الوحدات النمطية التي تدرك التوزيع بشكل صريح (على سبيل المثال snt.distribute.CrossReplicaBatchNorm
).
يشرح مثال Cifar-10 الموزع الخاص بنا إجراء تدريب على وحدات معالجة الرسومات المتعددة باستخدام Sonnet.