Documents | Exemples
Sonnet est une bibliothèque construite sur TensorFlow 2 conçue pour fournir des abstractions simples et composables pour la recherche en apprentissage automatique.
Sonnet a été conçu et construit par des chercheurs de DeepMind. Il peut être utilisé pour construire des réseaux de neurones à de nombreuses fins différentes (apprentissage non supervisé, apprentissage par renforcement, ...). Nous trouvons que c’est une abstraction réussie pour notre organisation, vous pourriez aussi le faire !
Plus précisément, Sonnet propose un modèle de programmation simple mais puissant centré autour d'un seul concept : snt.Module
. Les modules peuvent contenir des références à des paramètres, à d'autres modules et méthodes qui appliquent une fonction à l'entrée utilisateur. Sonnet est livré avec de nombreux modules prédéfinis (par exemple snt.Linear
, snt.Conv2D
, snt.BatchNorm
) et certains réseaux de modules prédéfinis (par exemple snt.nets.MLP
), mais les utilisateurs sont également encouragés à créer leurs propres modules.
Contrairement à de nombreux frameworks, Sonnet est extrêmement neutre sur la manière dont vous utiliserez vos modules. Les modules sont conçus pour être autonomes et entièrement découplés les uns des autres. Sonnet n'est pas livré avec un cadre de formation et les utilisateurs sont encouragés à créer le leur ou à adopter ceux construits par d'autres.
Sonnet est également conçu pour être simple à comprendre, notre code est (espérons-le !) clair et ciblé. Lorsque nous avons choisi des valeurs par défaut (par exemple, des valeurs par défaut pour les valeurs initiales des paramètres), nous essayons d'expliquer pourquoi.
Le moyen le plus simple d'essayer Sonnet est d'utiliser Google Colab qui propose un bloc-notes Python gratuit connecté à un GPU ou à un TPU.
snt.distribute
Pour commencer, installez TensorFlow 2.0 et Sonnet 2 :
$ pip install tensorflow tensorflow-probability
$ pip install dm-sonnet
Vous pouvez exécuter la commande suivante pour vérifier que les éléments sont correctement installés :
import tensorflow as tf
import sonnet as snt
print ( "TensorFlow version {}" . format ( tf . __version__ ))
print ( "Sonnet version {}" . format ( snt . __version__ ))
Sonnet est livré avec un certain nombre de modules intégrés que vous pouvez utiliser de manière simple. Par exemple, pour définir un MLP, nous pouvons utiliser le module snt.Sequential
pour appeler une séquence de modules, en passant la sortie d'un module donné comme entrée du module suivant. Nous pouvons utiliser snt.Linear
et tf.nn.relu
pour définir réellement notre calcul :
mlp = snt . Sequential ([
snt . Linear ( 1024 ),
tf . nn . relu ,
snt . Linear ( 10 ),
])
Pour utiliser notre module, nous devons "l'appeler". Le module Sequential
(et la plupart des modules) définissent une méthode __call__
qui signifie que vous pouvez les appeler par leur nom :
logits = mlp ( tf . random . normal ([ batch_size , input_size ]))
Il est également très courant de demander tous les paramètres de votre module. La plupart des modules de Sonnet créent leurs paramètres la première fois qu'ils sont appelés avec une entrée (puisque dans la plupart des cas, la forme des paramètres est fonction de l'entrée). Les modules Sonnet fournissent deux propriétés pour accéder aux paramètres.
La propriété variables
renvoie tous les tf.Variable
référencés par le module donné :
all_variables = mlp . variables
Il convient de noter que les tf.Variable
ne sont pas uniquement utilisés pour les paramètres de votre modèle. Par exemple, ils sont utilisés pour conserver l'état dans les métriques utilisées dans snt.BatchNorm
. Dans la plupart des cas, les utilisateurs récupèrent les variables du module pour les transmettre à un optimiseur pour les mettre à jour. Dans ce cas, les variables non entraînables ne doivent généralement pas figurer dans cette liste car elles sont mises à jour via un mécanisme différent. TensorFlow dispose d'un mécanisme intégré pour marquer les variables comme « entraînables » (paramètres de votre modèle) ou non entraînables (autres variables). Sonnet fournit un mécanisme pour rassembler toutes les variables entraînables de votre module, ce qui est probablement ce que vous souhaitez transmettre à un optimiseur :
model_parameters = mlp . trainable_variables
Sonnet encourage fortement les utilisateurs à sous-classer snt.Module
pour définir leurs propres modules. Commençons par créer un simple calque Linear
appelé 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
Utiliser ce module est trivial :
mod = MyLinear ( 32 )
mod ( tf . ones ([ batch_size , input_size ]))
En sous-classant snt.Module
vous obtenez gratuitement de nombreuses propriétés intéressantes. Par exemple une implémentation par défaut de __repr__
qui affiche les arguments du constructeur (très utile pour le débogage et l'introspection) :
>> > print ( repr ( mod ))
MyLinear ( output_size = 10 )
Vous obtenez également les propriétés variables
et trainable_variables
:
>> > mod . variables
( < tf . Variable 'my_linear/b:0' shape = ( 10 ,) ...) > ,
< tf . Variable 'my_linear/w:0' shape = ( 1 , 10 ) ...) > )
Vous remarquerez peut-être le préfixe my_linear
sur les variables ci-dessus. En effet, les modules Sonnet entrent également dans la portée du nom du module chaque fois que des méthodes sont appelées. En entrant la portée du nom du module, nous fournissons un graphique beaucoup plus utile à consommer par des outils comme TensorBoard (par exemple, toutes les opérations qui se produisent dans my_linear seront dans un groupe appelé my_linear).
De plus, votre module prendra désormais en charge les points de contrôle TensorFlow et le modèle enregistré, qui sont des fonctionnalités avancées abordées plus tard.
Sonnet prend en charge plusieurs formats de sérialisation. Le format le plus simple que nous prenons en charge est pickle
de Python, et tous les modules intégrés sont testés pour s'assurer qu'ils peuvent être enregistrés/chargés via pickle dans le même processus Python. En général, nous déconseillons l'utilisation de pickle, car il n'est pas bien pris en charge par de nombreuses parties de TensorFlow et, d'après notre expérience, peut être assez fragile.
Référence : https://www.tensorflow.org/alpha/guide/checkpoints
Les points de contrôle TensorFlow peuvent être utilisés pour enregistrer périodiquement la valeur des paramètres pendant l'entraînement. Cela peut être utile pour sauvegarder la progression de la formation au cas où votre programme planterait ou serait arrêté. Sonnet est conçu pour fonctionner proprement avec les points de contrôle 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 )
Référence : https://www.tensorflow.org/alpha/guide/saved_model
Les modèles enregistrés TensorFlow peuvent être utilisés pour enregistrer une copie de votre réseau découplée de la source Python correspondante. Ceci est activé en enregistrant un graphique TensorFlow décrivant le calcul et un point de contrôle contenant la valeur des poids.
La première chose à faire pour créer un modèle enregistré est de créer un snt.Module
que vous souhaitez enregistrer :
my_module = snt . nets . MLP ([ 1024 , 1024 , 10 ])
my_module ( tf . ones ([ 1 , input_size ]))
Ensuite, nous devons créer un autre module décrivant les parties spécifiques de notre modèle que nous souhaitons exporter. Nous vous conseillons de procéder ainsi (plutôt que de modifier le modèle d'origine sur place) afin d'avoir un contrôle précis sur ce qui est réellement exporté. Ceci est généralement important pour éviter de créer des modèles enregistrés très volumineux, et de telle sorte que vous ne partagiez que les parties de votre modèle que vous souhaitez (par exemple, vous souhaitez uniquement partager le générateur pour un GAN mais garder le discriminateur privé).
@ 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" )
Nous avons maintenant un modèle enregistré dans le dossier /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
Le chargement de ce modèle est simple et peut être effectué sur une autre machine sans aucun code Python qui a construit le modèle enregistré :
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
Notez que l'objet chargé n'est pas un module Sonnet, c'est un objet conteneur qui possède les méthodes spécifiques (par exemple inference
) et les propriétés (par exemple all_variables
) que nous avons ajoutées dans le bloc précédent.
Exemple : https://github.com/deepmind/sonnet/blob/v2/examples/distributed_cifar10.ipynb
Sonnet prend en charge la formation distribuée à l'aide de stratégies de distribution TensorFlow personnalisées.
Une différence clé entre Sonnet et la formation distribuée utilisant tf.keras
est que les modules et les optimiseurs Sonnet ne se comportent pas différemment lorsqu'ils sont exécutés dans le cadre de stratégies de distribution (par exemple, nous ne faisons pas la moyenne de vos gradients ni ne synchronisons vos statistiques de normes de lots). Nous pensons que les utilisateurs devraient avoir le contrôle total de ces aspects de leur formation et ne devraient pas être intégrés à la bibliothèque. Le compromis ici est que vous devez implémenter ces fonctionnalités dans votre script de formation (il ne s'agit généralement que de 2 lignes de code pour réduire toutes vos dégradés avant d'appliquer votre optimiseur) ou échanger des modules explicitement compatibles avec la distribution (par exemple snt.distribute.CrossReplicaBatchNorm
).
Notre exemple Cifar-10 distribué explique comment effectuer une formation multi-GPU avec Sonnet.