Modèles enregistrés réutilisables

Introduction

TensorFlow Hub héberge des SavedModels pour TensorFlow 2, parmi d'autres ressources. Ces modèles peuvent être chargés dans un programme Python avec obj = hub.load(url) [ en savoir plus ]. L' obj renvoyé est le résultat de tf.saved_model.load() (voir le guide des SavedModels de TensorFlow). Cet objet peut contenir des attributs arbitraires : des `tf.functions`, des `tf.Variables` (initialisées à partir de leurs valeurs pré-entraînées), d'autres ressources et, de manière récursive, d'autres objets de ce type.

Cette page décrit une interface que l' obj chargé doit implémenter afin d'être réutilisé dans un programme Python TensorFlow. Les SavedModels conformes à cette interface sont appelés SavedModels réutilisables .

Réutiliser un obj signifie construire un modèle plus large autour de celui-ci, avec la possibilité de l'affiner. L'affinage consiste à poursuivre l'entraînement des poids de l' obj chargé dans le cadre du modèle global. La fonction de perte et l'optimiseur sont déterminés par ce dernier ; obj définit uniquement la correspondance entre les activations d'entrée et de sortie (la « propagation avant »), pouvant inclure des techniques telles que le dropout ou la normalisation par lots.

L'équipe TensorFlow Hub recommande d'implémenter l'interface Reusable SavedModel dans tous les SavedModels destinés à être réutilisés au sens décrit ci-dessus. De nombreuses utilitaires de la bibliothèque tensorflow_hub , notamment hub.KerasLayer , exigent que les SavedModels implémentent cette interface.

Relation avec SignatureDefs

Cette interface, en termes de tf.functions et autres fonctionnalités de TF2, est distincte des signatures des SavedModels, disponibles depuis TF1 et toujours utilisées dans TF2 pour l'inférence (notamment le déploiement de SavedModels sur TF Serving ou TF Lite). Les signatures d'inférence ne sont pas suffisamment expressives pour permettre un réglage fin, et tf.function offre une API Python plus naturelle et expressive pour le modèle réutilisé.

Lien avec les bibliothèques de modélisation

Un SavedModel réutilisable utilise uniquement des primitives TensorFlow 2, indépendamment de toute bibliothèque de modélisation particulière comme Keras ou Sonnet. Cela facilite la réutilisation entre différentes bibliothèques de modélisation, sans dépendance au code source du modèle original.

Une certaine adaptation sera nécessaire pour charger ou enregistrer des Reusable SavedModels dans n'importe quelle bibliothèque de modélisation. Pour Keras, le chargement est assuré par hub.KerasLayer , et la fonction d'enregistrement intégrée de Keras au format SavedModel a été repensée pour TF2 afin de fournir un sur-ensemble de cette interface (voir la RFC de mai 2019).

Relation avec les « API communes SavedModel » spécifiques à la tâche

L'interface définie sur cette page autorise un nombre quelconque d'entrées et de sorties, quel que soit leur type. Les API Common SavedModel pour TF Hub précisent cette interface générale en définissant des conventions d'utilisation pour des tâches spécifiques, facilitant ainsi l'interchangeabilité des modèles.

Définition de l'interface

Attributs

Un SavedModel réutilisable est un SavedModel TensorFlow 2 tel que obj = tf.saved_model.load(...) renvoie un objet possédant les attributs suivants

  • __call__ . Obligatoire. Une fonction tf.function implémentant le calcul du modèle (la « passe avant ») conformément aux spécifications ci-dessous.

  • variables : Une liste d'objets tf.Variable, répertoriant toutes les variables utilisées par toute invocation possible de __call__ , y compris celles qui peuvent être entraînées et celles qui ne peuvent pas l'être.

    Cette liste peut être omise si elle est vide.

  • trainable_variables : Liste d'objets tf.Variable tels que v.trainable soit vrai pour tous les éléments. Ces variables doivent être un sous-ensemble de variables . Ce sont les variables à entraîner lors de l'ajustement fin du modèle. Le créateur du SavedModel peut choisir d'omettre certaines variables initialement entraînables pour indiquer qu'elles ne doivent pas être modifiées lors de l'ajustement fin.

    Cette liste peut être omise si elle est vide, notamment si le modèle enregistré ne prend pas en charge le réglage fin.

  • regularization_losses : Liste de fonctions TensorFlow, chacune ne prenant aucun argument et renvoyant un tenseur scalaire de type float. Pour l’ajustement fin, il est conseillé à l’utilisateur de SavedModel de les inclure comme termes de régularisation supplémentaires dans la fonction de perte (dans le cas le plus simple, sans mise à l’échelle supplémentaire). Elles servent généralement à représenter les régularisations de poids. (Faute d’arguments, ces fonctions TensorFlow ne peuvent pas exprimer les régularisations d’activité.)

    Cette liste peut être omise si elle est vide, notamment si le SavedModel ne prend pas en charge le réglage fin ou ne souhaite pas prescrire de régularisation des poids.

La fonction __call__

Un obj SavedModel restauré possède un attribut obj.__call__ qui est une fonction tf.function restaurée et permet d'appeler obj comme suit.

Synopsis (pseudo-code) :

outputs = obj(inputs, trainable=..., **kwargs)

Arguments

Les arguments sont les suivants.

  • Il existe un argument positionnel obligatoire avec un lot d'activations d'entrée du SavedModel. Son type est l'un des suivants :

    • un seul tenseur pour une seule entrée,
    • une liste de tenseurs pour une séquence ordonnée d'entrées anonymes,
    • un dictionnaire de tenseurs indexés par un ensemble particulier de noms d'entrée.

    (Les futures versions de cette interface pourront autoriser des imbrications plus générales.) Le créateur du SavedModel choisit l'une de ces imbrications, ainsi que la forme et le type des tenseurs. Le cas échéant, certaines dimensions de la forme doivent être indéfinies (notamment la taille du lot).

  • Il peut exister un argument optionnel nommé ` training qui accepte un booléen Python, True ou False . La valeur par défaut est False . Si le modèle prend en charge le réglage fin et si son calcul diffère entre les deux (par exemple, comme dans le cas du dropout et de la normalisation par lots), cette distinction est implémentée avec cet argument. Sinon, cet argument peut être omis.

    Il n'est pas nécessaire que __call__ accepte un argument training de type tenseur. Il incombe à l'appelant d'utiliser tf.cond() si nécessaire pour la répartition des appels.

  • Le créateur du SavedModel peut choisir d'accepter davantage kwargs nommés optionnels.

    • Pour les arguments de type tenseur, le créateur du SavedModel définit leurs types et formes autorisés. tf.function accepte une valeur par défaut Python pour un argument tracé à l'aide d'une entrée tf.TensorSpec. Ces arguments permettent de personnaliser les hyperparamètres numériques utilisés dans __call__ (par exemple, le taux de dropout).

    • Pour les arguments de type Python, le créateur du SavedModel définit leurs valeurs autorisées. Ces arguments peuvent servir d'indicateurs pour effectuer des choix discrets dans la fonction tracée (mais attention à la complexité croissante des traces possibles).

La fonction __call__ restaurée doit fournir des traces pour toutes les combinaisons d'arguments autorisées. Le passage training à True ou False ne doit pas modifier la validité des arguments.

Résultat

Les outputs de l'appel à obj peuvent être

  • un seul tenseur pour une seule sortie,
  • une liste de tenseurs pour une séquence ordonnée de sorties anonymes,
  • un dictionnaire de tenseurs indexés par un ensemble particulier de noms de sortie.

(Les futures versions de cette interface pourront autoriser des imbrications plus générales.) Le type de retour peut varier en fonction des arguments nommés (kwargs) de type Python. Ceci permet l'utilisation d'options générant des sorties supplémentaires. Le créateur de SavedModel définit les types et les formes des données de sortie, ainsi que leur dépendance aux entrées.

Appelables nommées

Un modèle réutilisable (ReusedSavedModel) peut fournir plusieurs éléments de modèle, comme décrit précédemment, en les plaçant dans des sous-objets nommés, par exemple : ` obj.foo , obj.bar , etc. Chaque sous-objet possède une méthode __call__ et des attributs relatifs aux variables, etc., spécifiques à cet élément de modèle. Dans l'exemple ci-dessus, on trouverait obj.foo.__call__ , obj.foo.variables , etc.

Notez que cette interface ne couvre pas l'approche consistant à ajouter directement une fonction tf.nue sous la forme tf.foo .

Les utilisateurs de Reusable SavedModels ne sont tenus de gérer qu'un seul niveau d'imbrication ( obj.bar mais pas obj.bar.baz ). (Les futures versions de cette interface pourront autoriser une imbrication plus profonde et pourraient supprimer l'exigence que l'objet de niveau supérieur soit lui-même appelable.)

Remarques finales

Relation avec les API en cours de traitement

Ce document décrit l'interface d'une classe Python composée de primitives telles que `tf.function` et `tf.Variable`, qui sont conservées lors de la sérialisation via tf.saved_model.save() et tf.saved_model.load() . Cette interface était déjà présente dans l'objet original passé à tf.saved_model.save() . Son adaptation permet l'échange de composants de modèle entre les API de construction de modèles au sein d'un même programme TensorFlow.