API fonctionnelle Keras dans TensorFlow



Keras dispose de deux API pour construire rapidement des architectures de réseau neuronal séquentielles et fonctionnelles. Si le premier vous permet de construire uniquement des architectures séquentielles de réseaux de neurones, alors en utilisant l'API fonctionnelle, vous pouvez définir un réseau de neurones sous la forme d'un graphe acyclique dirigé arbitraire, ce qui donne beaucoup plus de possibilités pour construire des modèles complexes. Cet article est une traduction du Guide des fonctionnalités de l'API fonctionnelle du site Web TensorFlow.

Présentation


L'API fonctionnelle vous permet de créer des modèles de manière plus flexible que l'API séquentielle; elle peut traiter des modèles avec une topologie non linéaire, des modèles avec des couches communes et des modèles avec plusieurs entrées ou sorties.

Il est basé sur le fait que le modèle d'apprentissage profond est généralement un graphe acyclique dirigé (DAG) de couches

L'API fonctionnelle est un ensemble d'outils pour tracer des couches .

Considérez le modèle suivant:

(entrée: vecteur à 784 dimensions)

[Couche dense (64 éléments, activation de relu)]

[Couche dense (64 éléments, activation de relu)]

[Couche dense (10 éléments, activation de softmax)]

(sortie: distribution de probabilité sur 10 classes)
Il s'agit d'un simple graphique de 3 couches.

Pour créer ce modèle à l'aide de l'API fonctionnelle, vous devez commencer par créer un nœud d'entrée:

from tensorflow import keras inputs = keras.Input(shape=(784,)) 

Ici, nous indiquons simplement la dimension de nos données: vecteurs à 784 dimensions. Veuillez noter que la quantité de données est toujours omise, nous n'indiquons que la dimension de chaque élément. Pour saisir la taille destinée aux images `(32, 32, 3)`, nous utiliserions:

 img_inputs = keras.Input(shape=(32, 32, 3)) 

Les inputs renvoyées contiennent des informations sur la taille et le type de données que vous prévoyez de transférer vers votre modèle:

 inputs.shape 

 TensorShape([None, 784]) 

 inputs.dtype 

 tf.float32 

Vous créez un nouveau nœud dans le graphe de calques en appelant le calque sur cet objet d' inputs :

 from tensorflow.keras import layers dense = layers.Dense(64, activation='relu') x = dense(inputs) 

«Appeler un calque» revient à dessiner une flèche depuis «l'entrée» dans le calque que nous avons créé. Nous «passons» l'entrée à la couche dense et nous obtenons x .

Ajoutons quelques couches supplémentaires à notre graphique de couches:

 x = layers.Dense(64, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x) 

Nous pouvons maintenant créer un Model spécifiant ses entrées et sorties dans le graphique des couches:

 model = keras.Model(inputs=inputs, outputs=outputs) 

Regardons à nouveau le processus complet de définition du modèle:

 inputs = keras.Input(shape=(784,), name='img') x = layers.Dense(64, activation='relu')(inputs) x = layers.Dense(64, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x) model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model') 

Voyons à quoi ressemble le résumé du modèle:

 model.summary() 

 Model: "mnist_model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= img (InputLayer) [(None, 784)] 0 _________________________________________________________________ dense_3 (Dense) (None, 64) 50240 _________________________________________________________________ dense_4 (Dense) (None, 64) 4160 _________________________________________________________________ dense_5 (Dense) (None, 10) 650 ================================================================= Total params: 55,050 Trainable params: 55,050 Non-trainable params: 0 _________________________________________________________________ 

Nous pouvons également dessiner le modèle sous forme de graphique:

 keras.utils.plot_model(model, 'my_first_model.png') 

image

Et dérivez éventuellement les dimensions de l'entrée et de la sortie de chaque couche sur le graphique construit:

 keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True) 

image

Cette image et le code que nous avons écrit sont identiques. Dans la version de code, les flèches de liaison sont simplement remplacées par des opérations d'appel.

Le «graphe de couches» est une image mentale très intuitive pour le modèle d'apprentissage en profondeur, et l'API fonctionnelle est un moyen de créer des modèles qui reflètent étroitement cette image mentale.

Formation, évaluation et conclusion


Apprendre, évaluer et dériver le travail pour les modèles construits à l'aide de l'API fonctionnelle, tout comme dans les modèles séquentiels.

Envisagez une démonstration rapide.

Ici, nous chargeons l'ensemble de données d'image MNIST, le convertissons en vecteurs, entraînons le modèle sur les données (tout en surveillant la qualité du travail sur l'échantillon de test), et enfin nous évaluons notre modèle sur les données de test:

 (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() x_train = x_train.reshape(60000, 784).astype('float32') / 255 x_test = x_test.reshape(10000, 784).astype('float32') / 255 model.compile(loss='sparse_categorical_crossentropy', optimizer=keras.optimizers.RMSprop(), metrics=['accuracy']) history = model.fit(x_train, y_train, batch_size=64, epochs=5, validation_split=0.2) test_scores = model.evaluate(x_test, y_test, verbose=2) print('Test loss:', test_scores[0]) print('Test accuracy:', test_scores[1]) 

Enregistrement et sérialisation


L'enregistrement et la sérialisation des modèles créés à l'aide de l'API fonctionnelle fonctionnent exactement de la même manière que pour les modèles séquentiels.

La méthode standard pour enregistrer un modèle fonctionnel consiste à appeler model.save( ), ce qui vous permet d'enregistrer le modèle entier dans un fichier.

Vous pouvez ensuite restaurer le même modèle à partir de ce fichier, même si vous n'avez plus accès au code qui a créé le modèle.

Ce fichier comprend:

  • Architecture du modèle
  • Poids du modèle (obtenus lors de la formation)
  • Configuration de la formation du modèle (ce que vous avez réussi à compile )
  • L'optimiseur et son état, s'il l'était (cela vous permet de reprendre l'entraînement là où vous vous étiez arrêté)

 model.save('path_to_my_model.h5') del model # Recreate the exact same model purely from the file: model = keras.models.load_model('path_to_my_model.h5') 

Utilisation du même graphe de calque pour définir plusieurs modèles


Dans l'API fonctionnelle, les modèles sont créés en spécifiant les données d'entrée et de sortie dans un graphique de couches. Cela signifie qu'un graphique à couche unique peut être utilisé pour générer plusieurs modèles.

Dans l'exemple ci-dessous, nous utilisons la même pile de calques pour créer deux modèles:
un modèle de (encoder) qui convertit les images d'entrée en vecteurs 16 dimensions et un modèle de (autoencoder) bout en (autoencoder) pour la formation.

 encoder_input = keras.Input(shape=(28, 28, 1), name='img') x = layers.Conv2D(16, 3, activation='relu')(encoder_input) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.MaxPooling2D(3)(x) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.Conv2D(16, 3, activation='relu')(x) encoder_output = layers.GlobalMaxPooling2D()(x) encoder = keras.Model(encoder_input, encoder_output, name='encoder') encoder.summary() x = layers.Reshape((4, 4, 1))(encoder_output) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) x = layers.Conv2DTranspose(32, 3, activation='relu')(x) x = layers.UpSampling2D(3)(x) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x) autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder') autoencoder.summary() 

Veuillez noter que nous rendons l'architecture de décodage strictement symétrique à l'architecture de codage, de sorte que nous obtenons la dimension des données de sortie de la même manière que les données d'entrée (28, 28, 1) . La couche Conv2D est en Conv2D vers la couche Conv2D , et la couche MaxPooling2D sera le dos à la couche MaxPooling2D .

Les modèles peuvent être appelés en tant que couches


Vous pouvez utiliser n'importe quel modèle comme s'il s'agissait d'une couche, en l'appelant en Input ou en sortie d'une autre couche.

Notez qu'en invoquant un modèle, vous réutilisez non seulement son architecture, vous réutilisez également ses poids. Voyons cela en action. Voici un autre exemple d'un exemple d'auto-encodeur, lorsqu'un modèle d'encodeur, un modèle de décodeur est créé, et ils sont connectés en deux appels pour obtenir un modèle d'auto-encodeur:

 encoder_input = keras.Input(shape=(28, 28, 1), name='original_img') x = layers.Conv2D(16, 3, activation='relu')(encoder_input) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.MaxPooling2D(3)(x) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.Conv2D(16, 3, activation='relu')(x) encoder_output = layers.GlobalMaxPooling2D()(x) encoder = keras.Model(encoder_input, encoder_output, name='encoder') encoder.summary() decoder_input = keras.Input(shape=(16,), name='encoded_img') x = layers.Reshape((4, 4, 1))(decoder_input) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) x = layers.Conv2DTranspose(32, 3, activation='relu')(x) x = layers.UpSampling2D(3)(x) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x) decoder = keras.Model(decoder_input, decoder_output, name='decoder') decoder.summary() autoencoder_input = keras.Input(shape=(28, 28, 1), name='img') encoded_img = encoder(autoencoder_input) decoded_img = decoder(encoded_img) autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder') autoencoder.summary() 

Comme vous pouvez le voir, un modèle peut être imbriqué: un modèle peut contenir un sous-modèle (puisque le modèle peut être considéré comme une couche).

Un cas d'utilisation courant pour les modèles d'imbrication est l' assemblage .

À titre d'exemple, voici comment combiner un ensemble de modèles en un modèle qui fait la moyenne de leurs prévisions:

 def get_model(): inputs = keras.Input(shape=(128,)) outputs = layers.Dense(1, activation='sigmoid')(inputs) return keras.Model(inputs, outputs) model1 = get_model() model2 = get_model() model3 = get_model() inputs = keras.Input(shape=(128,)) y1 = model1(inputs) y2 = model2(inputs) y3 = model3(inputs) outputs = layers.average([y1, y2, y3]) ensemble_model = keras.Model(inputs=inputs, outputs=outputs) 


Manipulation de topologies de graphes complexes


Modèles avec plusieurs entrées et sorties


L'API fonctionnelle simplifie la manipulation de plusieurs entrées et sorties. Cela ne peut pas être fait avec l'API séquentielle.

Voici un exemple simple.

Supposons que vous créez un système de classement des applications client par priorité et que vous les envoyez au bon service.

Votre modèle aura 3 entrées:

  • En-tête d'application (saisie de texte)
  • Contenu texte de l'application (saisie de texte)
  • Toutes les balises ajoutées par l'utilisateur (entrée catégorielle)

Le modèle aura 2 sorties:

  • Score de priorité entre 0 et 1 (sortie sigmoïde scalaire)
  • Le service qui doit traiter la demande (sortie softmax pour de nombreux services)

Construisons un modèle en plusieurs lignes à l'aide de l'API fonctionnelle.

 num_tags = 12 #     num_words = 10000 #         num_departments = 4 #     title_input = keras.Input(shape=(None,), name='title') #      body_input = keras.Input(shape=(None,), name='body') #      tags_input = keras.Input(shape=(num_tags,), name='tags') #    `num_tags` #      64-  title_features = layers.Embedding(num_words, 64)(title_input) #      64-  body_features = layers.Embedding(num_words, 64)(body_input) #        128-  title_features = layers.LSTM(128)(title_features) #        32-  body_features = layers.LSTM(32)(body_features) #          x = layers.concatenate([title_features, body_features, tags_input]) #         priority_pred = layers.Dense(1, activation='sigmoid', name='priority')(x) #       department_pred = layers.Dense(num_departments, activation='softmax', name='department')(x) #   ,     model = keras.Model(inputs=[title_input, body_input, tags_input], outputs=[priority_pred, department_pred]) 

Dessinons un graphe modèle:

 keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True) 



Lors de la compilation de ce modèle, nous pouvons attribuer différentes fonctions de perte à chaque sortie.

Vous pouvez même attribuer des poids différents à chaque fonction de perte pour varier leur contribution à la fonction globale de perte d'apprentissage.

 model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=['binary_crossentropy', 'categorical_crossentropy'], loss_weights=[1., 0.2]) 

Puisque nous avons donné des noms à nos couches de sortie, nous pouvons également spécifier des fonctions de perte:

 model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss={'priority': 'binary_crossentropy', 'department': 'categorical_crossentropy'}, loss_weights=[1., 0.2]) 

Nous pouvons former le modèle en passant des listes de tableaux Numpy de données d'entrée et d'étiquettes:

 import numpy as np # Dummy input data title_data = np.random.randint(num_words, size=(1280, 10)) body_data = np.random.randint(num_words, size=(1280, 100)) tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32') # Dummy target data priority_targets = np.random.random(size=(1280, 1)) dept_targets = np.random.randint(2, size=(1280, num_departments)) model.fit({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}, epochs=2, batch_size=32) 

Lorsque vous appelez fit avec un objet Dataset , soit un tuple de listes telles que ([title_data, body_data, tags_data], [priority_targets, dept_targets]) , soit un tuple de dictionnaires ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) doivent être retournés ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) .

Modèle de resnet de formation


En plus des modèles avec plusieurs entrées et sorties, l'API fonctionnelle simplifie la manipulation des topologies avec une connectivité non linéaire, c'est-à-dire des modèles dans lesquels les couches ne sont pas connectées en série. De tels modèles ne peuvent pas non plus être implémentés à l'aide de l'API séquentielle (comme son nom l'indique).

Un cas d'utilisation courant pour cela est les connexions résiduelles.

Construisons un modèle de formation ResNet pour CIFAR10 pour le démontrer.

 inputs = keras.Input(shape=(32, 32, 3), name='img') x = layers.Conv2D(32, 3, activation='relu')(inputs) x = layers.Conv2D(64, 3, activation='relu')(x) block_1_output = layers.MaxPooling2D(3)(x) x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output) x = layers.Conv2D(64, 3, activation='relu', padding='same')(x) block_2_output = layers.add([x, block_1_output]) x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output) x = layers.Conv2D(64, 3, activation='relu', padding='same')(x) block_3_output = layers.add([x, block_2_output]) x = layers.Conv2D(64, 3, activation='relu')(block_3_output) x = layers.GlobalAveragePooling2D()(x) x = layers.Dense(256, activation='relu')(x) x = layers.Dropout(0.5)(x) outputs = layers.Dense(10, activation='softmax')(x) model = keras.Model(inputs, outputs, name='toy_resnet') model.summary() 

Dessinons un graphe modèle:

 keras.utils.plot_model(model, 'mini_resnet.png', show_shapes=True) 



Et lui apprendre:

 (x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data() x_train = x_train.astype('float32') / 255. x_test = x_test.astype('float32') / 255. y_train = keras.utils.to_categorical(y_train, 10) y_test = keras.utils.to_categorical(y_test, 10) model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss='categorical_crossentropy', metrics=['acc']) model.fit(x_train, y_train, batch_size=64, epochs=1, validation_split=0.2) 

Partage de couche


Une autre bonne utilisation de l'API fonctionnelle est les modèles qui utilisent des couches communes. Les couches communes sont des instances de couches réutilisées dans le même modèle: elles étudient les entités liées à plusieurs chemins dans un graphe de couches.

Les couches communes sont souvent utilisées pour coder des données d'entrée provenant des mêmes espaces (par exemple, de deux morceaux de texte différents qui ont le même dictionnaire), car elles fournissent l'échange d'informations entre ces différentes données, ce qui permet à ces modèles d'être formés sur moins de données. Si un certain mot apparaît sur l'une des entrées, cela facilitera son traitement sur toutes les entrées qui passent par le niveau général.

Pour partager une couche dans l'API fonctionnelle, il suffit d'appeler plusieurs fois la même instance de la couche. Par exemple, ici la couche Embedding est partagée sur deux entrées de texte:

 #   1000    128-  shared_embedding = layers.Embedding(1000, 128) #     text_input_a = keras.Input(shape=(None,), dtype='int32') #     text_input_b = keras.Input(shape=(None,), dtype='int32') #           encoded_input_a = shared_embedding(text_input_a) encoded_input_b = shared_embedding(text_input_b) 

Récupération et réutilisation des nœuds dans un graphe de couches


Étant donné que le graphe de calque que vous manipulez dans l'API fonctionnelle est une structure de données statique, vous pouvez y accéder et le vérifier. C'est ainsi que nous construisons des modèles fonctionnels, par exemple, sous forme d'images.

Cela signifie également que nous pouvons accéder aux activations des couches intermédiaires («nœuds» dans le graphique) et les utiliser à d'autres endroits. C'est extrêmement utile pour extraire des traits, par exemple!

Voyons un exemple. Il s'agit d'un modèle VGG19 avec des balances pré-entraînées sur ImageNet:

 from tensorflow.keras.applications import VGG19 vgg19 = VGG19() 

Et ce sont des activations de modèle intermédiaires obtenues en interrogeant la structure des données du graphique:

 features_list = [layer.output for layer in vgg19.layers] 

Nous pouvons utiliser ces fonctionnalités pour créer un nouveau modèle d'extraction de fonctionnalités qui renvoie des valeurs d'activation de niveau intermédiaire - et nous pouvons tout faire en 3 lignes

 feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list) img = np.random.random((1, 224, 224, 3)).astype('float32') extracted_features = feat_extraction_model(img) 

Ceci est pratique lors de la mise en œuvre d'un transfert de style neuronal, comme dans d'autres cas.

Extension de l'API en écrivant des couches personnalisées


tf.keras dispose d'une large gamme de couches intégrées. Voici quelques exemples:

Couches convolutionnelles: Conv1D , Conv2D , Conv3D , Conv2DTranspose , etc.
Couches de MaxPooling1D : MaxPooling1D , MaxPooling2D , MaxPooling3D , AveragePooling1D , etc.
Couches RNN: GRU , LSTM , ConvLSTM2D , etc.
BatchNormalization , Dropout , Embedding , etc.

Si vous n'avez pas trouvé ce dont vous avez besoin, il est facile d'étendre l'API en créant votre propre couche.

Toutes les couches sous-classent la classe Layer et implémentent:

La méthode d' call qui définit les calculs effectués par la couche.
La méthode de build qui crée les pondérations de couche (notez qu'il s'agit simplement d'une convention de style; vous pouvez également créer des pondérations dans __init__ ).

Voici une implémentation simple de la couche Dense :

 class CustomDense(layers.Layer): def __init__(self, units=32): super(CustomDense, self).__init__() self.units = units def build(self, input_shape): self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer='random_normal', trainable=True) self.b = self.add_weight(shape=(self.units,), initializer='random_normal', trainable=True) def call(self, inputs): return tf.matmul(inputs, self.w) + self.b inputs = keras.Input((4,)) outputs = CustomDense(10)(inputs) model = keras.Model(inputs, outputs) 

Si vous souhaitez que votre couche personnalisée get_config en charge la sérialisation, vous devez également définir la méthode get_config qui renvoie les arguments du constructeur de l'instance de couche:

 class CustomDense(layers.Layer): def __init__(self, units=32): super(CustomDense, self).__init__() self.units = units def build(self, input_shape): self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer='random_normal', trainable=True) self.b = self.add_weight(shape=(self.units,), initializer='random_normal', trainable=True) def call(self, inputs): return tf.matmul(inputs, self.w) + self.b def get_config(self): return {'units': self.units} inputs = keras.Input((4,)) outputs = CustomDense(10)(inputs) model = keras.Model(inputs, outputs) config = model.get_config() new_model = keras.Model.from_config( config, custom_objects={'CustomDense': CustomDense}) 

Facultativement, vous pouvez également implémenter la méthode de classe from_config (cls, config) , qui est chargée de recréer l'instance de couche, compte tenu de son dictionnaire de configuration. L' from_config par défaut from_config ressemble à ceci:

 def from_config(cls, config): return cls(**config) 

Quand utiliser l'API fonctionnelle


Comment déterminer quand il vaut mieux utiliser l'API fonctionnelle pour créer un nouveau modèle, ou simplement sous-classer le Model directement?

En général, l'API fonctionnelle est de plus haut niveau et facile à utiliser, elle possède un certain nombre de fonctions qui ne sont pas prises en charge par les modèles sous-classés.

Cependant, la sous-classification du modèle vous donne une grande flexibilité lors de la création de modèles qui ne sont pas facilement décrits comme un graphique acyclique dirigé de couches (par exemple, vous ne pouvez pas implémenter Tree-RNN avec l'API fonctionnelle, vous devez sous-classer le Model directement).

Points forts de l'API fonctionnelle:


Les propriétés répertoriées ci-dessous sont toutes vraies pour les modèles séquentiels (qui sont également des structures de données), mais elles sont vraies pour les modèles sous-classés (qui sont du code Python, pas des structures de données).

L'API fonctionnelle produit un code plus court.


Pas de super(MyClass, self).__init__(...) , pas d' def call(self, ...): etc.

Comparez:

 inputs = keras.Input(shape=(32,)) x = layers.Dense(64, activation='relu')(inputs) outputs = layers.Dense(10)(x) mlp = keras.Model(inputs, outputs) 

Avec version sous-classée:

 class MLP(keras.Model): def __init__(self, **kwargs): super(MLP, self).__init__(**kwargs) self.dense_1 = layers.Dense(64, activation='relu') self.dense_2 = layers.Dense(10) def call(self, inputs): x = self.dense_1(inputs) return self.dense_2(x) #   . mlp = MLP() #    . #            . _ = mlp(tf.zeros((1, 32))) 

Votre modèle est validé tel qu'il est écrit.


Dans l'API fonctionnelle, les spécifications d'entrée (forme et type) sont créées à l'avance (via `Input`), et chaque fois que vous appelez la couche, la couche vérifie que les spécifications qui lui sont transmises correspondent à ses hypothèses; si ce n'est pas le cas, vous recevrez un message d'erreur utile .

Cela garantit que tout modèle que vous créez avec l'API fonctionnelle démarre. Tout le débogage (non lié au débogage de convergence) se produira de manière statique pendant la construction du modèle, et non au moment de l'exécution. Ceci est similaire à la vérification de type dans le compilateur.

Votre modèle fonctionnel peut être représenté graphiquement, et il est également testable.


Vous pouvez dessiner le modèle sous la forme d'un graphique, et vous pouvez facilement accéder aux nœuds intermédiaires du graphique, par exemple, pour extraire et réutiliser l'activation des couches intermédiaires, comme nous l'avons vu dans l'exemple précédent:

 features_list = [layer.output for layer in vgg19.layers] feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list) 

Étant donné que le modèle fonctionnel est plus une structure de données qu'un morceau de code, il peut être sérialisé en toute sécurité et peut être enregistré sous la forme d'un fichier unique qui vous permet de recréer exactement le même modèle sans accès au code source.

Faiblesses fonctionnelles de l'API


Il ne prend pas en charge les architectures dynamiques.


L'API fonctionnelle traite les modèles en tant que couches DAG. Cela est vrai pour la plupart des architectures d'apprentissage en profondeur, mais pas pour tout le monde: par exemple, les réseaux récursifs ou les RNN arborescents ne répondent pas à cette hypothèse et ne peuvent pas être implémentés dans l'API fonctionnelle.

Parfois, il vous suffit de tout écrire à partir de zéro.


Lors de l'écriture d'architectures avancées, vous souhaiterez peut-être aller au-delà de la «définition de couches DAG»: par exemple, vous pouvez utiliser plusieurs méthodes de formation et de sortie personnalisées sur une instance de votre modèle. Cela nécessite un sous-classement.

Combiner et combiner différents styles d'API


Il est important de noter que le choix entre l'API fonctionnelle ou la sous-classification du modèle n'est pas une solution binaire qui vous limite à une catégorie de modèles.Tous les modèles de l'API tf.keras peuvent interagir les uns avec les autres, qu'il s'agisse de modèles séquentiels, de modèles fonctionnels ou de modèles / couches sous-classés écrits à partir de zéro.

Vous pouvez toujours utiliser le modèle fonctionnel ou le modèle séquentiel dans le cadre du modèle / couche sous-classé:

 units = 32 timesteps = 10 input_dim = 5 # Define a Functional model inputs = keras.Input((None, units)) x = layers.GlobalAveragePooling1D()(inputs) outputs = layers.Dense(1, activation='sigmoid')(x) model = keras.Model(inputs, outputs) class CustomRNN(layers.Layer): def __init__(self): super(CustomRNN, self).__init__() self.units = units self.projection_1 = layers.Dense(units=units, activation='tanh') self.projection_2 = layers.Dense(units=units, activation='tanh') # Our previously-defined Functional model self.classifier = model def call(self, inputs): outputs = [] state = tf.zeros(shape=(inputs.shape[0], self.units)) for t in range(inputs.shape[1]): x = inputs[:, t, :] h = self.projection_1(x) y = h + self.projection_2(state) state = y outputs.append(y) features = tf.stack(outputs, axis=1) print(features.shape) return self.classifier(features) rnn_model = CustomRNN() _ = rnn_model(tf.zeros((1, timesteps, input_dim))) 

Inversement, vous pouvez utiliser n'importe quel calque ou modèle sous-classé dans l'API fonctionnelle si vous implémentez une méthode callqui correspond à l'un des modèles suivants:

call(self, inputs, **kwargs)inputsest le tenseur ou la structure de tenseur imbriquée (par exemple, la liste des tenseurs), et où **kwargssont les arguments non-tenseur (pas d'entrée) .
call(self, inputs, training=None, **kwargs)trainingest une valeur booléenne indiquant dans quel mode la couche, l'apprentissage ou la sortie doit se comporter.
call(self, inputs, mask=None, **kwargs)maskest le tenseur de masque booléen (utile pour RNN, par exemple).
call(self, inputs, training=None, mask=None, **kwargs)- bien sûr, vous pouvez avoir les deux paramètres définissant le comportement de la couche en même temps.

De plus, si vous implémentez la méthode `get_config` sur votre couche ou modèle personnalisé, les modèles fonctionnels que vous créez avec lui seront sérialisables et clonés.

Voici un petit exemple où nous utilisons des RNN personnalisés écrits à partir de zéro Modèles fonctionnels:

 units = 32 timesteps = 10 input_dim = 5 batch_size = 16 class CustomRNN(layers.Layer): def __init__(self): super(CustomRNN, self).__init__() self.units = units self.projection_1 = layers.Dense(units=units, activation='tanh') self.projection_2 = layers.Dense(units=units, activation='tanh') self.classifier = layers.Dense(1, activation='sigmoid') def call(self, inputs): outputs = [] state = tf.zeros(shape=(inputs.shape[0], self.units)) for t in range(inputs.shape[1]): x = inputs[:, t, :] h = self.projection_1(x) y = h + self.projection_2(state) state = y outputs.append(y) features = tf.stack(outputs, axis=1) return self.classifier(features) #           #  `batch_shape`,     `CustomRNN`  #    (     `state`). inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim)) x = layers.Conv1D(32, 3)(inputs) outputs = CustomRNN()(x) model = keras.Model(inputs, outputs) rnn_model = CustomRNN() _ = rnn_model(tf.zeros((1, 10, 5))) 

Ceci conclut notre guide API fonctionnel!

Vous disposez maintenant d'un puissant ensemble d'outils pour créer des modèles d'apprentissage en profondeur.

Après vérification, la traduction apparaîtra également sur Tensorflow.org. Si vous souhaitez participer à la traduction de la documentation du site Tensorflow.org en russe, veuillez nous contacter à titre personnel ou commentaires. Toutes corrections ou commentaires sont appréciés. À titre d'illustration, nous avons utilisé l'image du modèle GoogLeNet, qui est également un graphique acyclique dirigé.

Source: https://habr.com/ru/post/fr483664/


All Articles