TensorFlow, Keras et deep learning sans doctorat

Dans cet atelier de programmation, vous allez apprendre à créer et à entraîner un réseau de neurones capable de reconnaître des chiffres manuscrits. À mesure que vous améliorez votre réseau de neurones pour obtenir une précision de 99 %, vous découvrirez également les outils professionnels que les professionnels du deep learning utilisent pour entraîner leurs modèles efficacement.

Cet atelier de programmation utilise l'ensemble de données MNIST, une collection de 60 000 chiffres étiquetés qui ont permis aux générations de PhD d'être utilisées pendant près de 20 ans. Résolvez moins de 100 lignes de code Python / TensorFlow.

Points abordés

  • Qu'est-ce qu'un réseau de neurones et comment l'entraîner
  • Créer un réseau de neurones de couche unique à l'aide de tf.keras
  • Ajouter d'autres calques
  • Configurer un planning de taux d'apprentissage
  • Comment créer des réseaux de neurones convolutifs
  • Comment utiliser les techniques de régularisation: abandon, normalisation par lot
  • Qu'est-ce que le surapprentissage

Ce dont vous avez besoin

Un navigateur. Cet atelier peut être exécuté entièrement avec Google Colaboratory.

Commentaires

N'hésitez pas à nous contacter si vous constatez un problème dans cet atelier ou si vous pensez qu'il devrait être amélioré. Nous traitons les commentaires via des problèmes GitHub [lien vers le commentaire].

Cet atelier utilise Google Colaboratory et ne nécessite aucune configuration de votre part. Vous pouvez l'exécuter sur un Chromebook. Veuillez ouvrir le fichier ci-dessous et exécuter les cellules pour vous familiariser avec les notebooks Colab.

Welcome to Colab.ipynb

Instructions supplémentaires ci-dessous:

Sélectionner un backend de GPU

Dans le menu Colab, sélectionnez Runtime > Change runtime type (Environnement d'exécution ; modifier le type d'exécution), puis sélectionnez "GPU". La connexion à l'environnement d'exécution s'effectue automatiquement à la première exécution. Vous pouvez également utiliser le bouton "&&t" (Connecter) en haut à droite.

Exécution de notebook

Exécutez les cellules une par une en cliquant sur celle-ci et en appuyant sur Maj+ENTRÉE. Vous pouvez également exécuter l'ensemble du notebook avec Runtime > Run all (Exécuter et exécuter tout).

Sommaire

Tous les notebooks disposent d'une table des matières. Vous pouvez l'ouvrir en cliquant sur la flèche noire à gauche.

Cellules masquées

Certaines cellules ne contiennent que leur titre. Il s'agit d'une fonctionnalité de notebook spécifique à Colab. Vous pouvez double-cliquer dessus pour voir le code qu'il contient, mais il n'est généralement pas très intéressant. Fonctions d'assistance ou de visualisation en général. Vous devrez quand même exécuter ces cellules pour que les fonctions qu'elles contiennent soient définies.

Nous allons commencer par regarder un train de neurones. Veuillez ouvrir le notebook ci-dessous et exécuter toutes les cellules. Ne prêtez pas encore attention au code. Nous commencerons à l'expliquer ultérieurement.

keras_01_mnist.ipynb

Lorsque vous exécutez le notebook, concentrez-vous sur les visualisations. Pour en savoir plus, consultez la section ci-dessous.

Données d'entraînement

Nous disposons d'un ensemble de données composé de chiffres manuscrits, qui indiquent ce que chaque image représente (autrement dit, un nombre compris entre 0 et 9). Dans ce notebook, un extrait s'affiche:

Le réseau de neurones que nous allons créer classe les chiffres manuscrits dans ses 10 classes (0, 9). en fonction des paramètres internes dont la classification doit être correcte. Cette valeur est obtenue via un processus d'entraînement qui nécessite un "ensemble de données étiqueté" à l'aide d'images et de réponses correctes.

Comment savoir si le réseau de neurones entraîné est performant ou non ? L'utilisation de l'ensemble de données d'entraînement pour tester le réseau serait une tricherie. Il a déjà vu cet ensemble de données à plusieurs reprises pendant l'entraînement et est très certainement très performant. Nous avons besoin d'un autre ensemble de données étiqueté, que nous n'avons jamais vu pendant l'entraînement, pour évaluer les performances réelles du réseau. C'est ce qu'on appelle un ensemble de données de validation.

Entraînement

À mesure que l'entraînement progresse, un lot de données d'entraînement est mis à jour à la fois, et le modèle identifie de mieux en mieux les chiffres manuscrits. Vous pouvez le voir sur le graphique d'entraînement:

À droite, la précision n'est que le pourcentage de chiffres correctement identifiés. Tout cela s'améliore en même temps que l'entraînement.

À gauche, on peut voir le symbole ". Pour guider l'entraînement, nous allons définir une fonction de perte, qui représente à quel point le système ne reconnaît pas les chiffres, et essayer de la réduire. La perte diminue à la fois sur l'entraînement et sur les données de validation à mesure de l'avancement de l'entraînement. Cela signifie que le réseau de neurones est en train d'apprendre.

L'axe X représente le nombre d'époques ou d'itérations dans l'ensemble de données complet.

Prédictions

Une fois le modèle entraîné, nous pouvons l'utiliser pour reconnaître des chiffres manuscrits. La visualisation suivante montre ses performances sur quelques chiffres affichés à partir de polices locales (première ligne), puis sur les 10 000 chiffres de l'ensemble de données de validation. La classe prédite s'affiche sous chaque chiffre, en rouge si elle est incorrecte.

Comme vous pouvez le constater, ce modèle initial n'est pas très efficace, mais il reconnaît certains chiffres. Sa justesse de validation finale est d'environ 90 %, ce qui n'est pas si mauvais pour le modèle simpliste initial que nous commençons,mais cela signifie tout de même qu'il manque 1 000 chiffres de validation sur les 10 000. C'est beaucoup plus visible, c'est pourquoi toutes les réponses semblent incorrectes (rouge).

Tensors

Les données sont stockées dans des matrices. Une image en nuances de gris de 28 x 28 pixels s'intègre dans une matrice à deux dimensions. Pour une image en couleurs, nous avons besoin de plus de dimensions. Il y a trois valeurs de couleur par pixel (rouge, vert, bleu). Vous aurez donc besoin d'un tableau en trois dimensions avec les dimensions [28, 28, 3]. De plus, pour stocker un lot de 128 images en couleur, un tableau à quatre dimensions est nécessaire avec les dimensions [128, 28, 28, 3].

Ces tableaux multidimensionnels sont appelés Tensors, et la liste de leurs dimensions est leur formes.

En résumé

Si vous connaissez déjà tous les termes du paragraphe gras, vous pouvez passer à l'exercice suivant. Si vous débutez dans le domaine du deep learning, bienvenue. Nous vous invitons à poursuivre votre lecture.

sorcière.png

Pour les modèles créés comme séquence de couches, Keras propose l'API Sequential. Par exemple, un classificateur d'images utilisant trois calques denses peut être écrit en Keras de la manière suivante:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[28, 28, 1]),
    tf.keras.layers.Dense(200, activation="relu"),
    tf.keras.layers.Dense(60, activation="relu"),
    tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

Une seule couche dense

Les chiffres écrits à la main dans l'ensemble de données MNIST sont des images en nuances de gris de 28 x 28 pixels. L'approche la plus simple pour la classification consiste à utiliser 28 x 28=784 pixels comme entrées pour un réseau de neurones à une couche.

Capture d'écran 26/07/2016 à 12/32/24.png

Chaque &neuron d'un réseau de neurones effectue une somme pondérée de l'ensemble de ses entrées, ajoute une constante appelée "biais", puis alimente le résultat par une fonction d'activation non linéaire. Les paramètres &weights et biais sont des paramètres qui seront déterminés au cours de l'entraînement. Elles sont d'abord initialisées avec des valeurs aléatoires.

L'image ci-dessus représente un réseau de neurones à 1 couche avec 10 neurones de sortie, car nous voulons classer les chiffres en 10 classes (0 à 9).

Avec une multiplication matricielle

Voici comment une couche de réseau de neurones traitant une collection d'images peut être représentée par une multiplication matricielle:

matmul.gif

À l'aide de la première colonne de pondérations de la matrice de pondération W, nous calculons la somme pondérée de tous les pixels de la première image. Cette somme correspond au premier neurone. À l'aide de la deuxième colonne de pondérations, nous faisons la même chose pour le deuxième neurone, et ainsi de suite jusqu'au 10e neurone. Nous pouvons ensuite répéter l'opération pour les 99 images restantes. Si nous appelons X la matrice contenant nos 100 images, toutes les sommes pondérées pour nos 10 neurons, calculées sur 100 images, sont simplement X,W, une multiplication matricielle.

Chaque neurone doit maintenant ajouter son biais (une constante). Puisque nous avons 10 neurons, nous avons 10 constantes de biais. Nous appelons ce vecteur de 10 valeurs b. Il doit être ajouté à chaque ligne de la matrice calculée précédemment. À l'aide d'un peu de magie, ce qu'on appelle "diffusion". Nous allons l'écrire avec un signe plus.

Nous appliquons enfin une fonction d'activation, par exemple &softtsoftmax (décrite ci-dessous), et nous obtenons la formule décrivant un réseau de neurones à une couche, appliquée à 100 images:

Capture d'écran 26/07/2016 à 16.02.36.png

Dans Keras

Avec les bibliothèques de réseaux de neurones de haut niveau telles que Keras, nous n'avons pas besoin d'appliquer cette formule. Cependant, il est important de comprendre que la couche de réseau de neurones n'est qu'un ensemble de multiplications et d'ajouts. Dans Keras, une couche dense se présente comme suit:

tf.keras.layers.Dense(10, activation='softmax')

Profond

Il est simple d'enchaîner les couches de réseau de neurones. La première couche calcule les sommes pondérées des pixels. Les couches suivantes calculent les sommes pondérées des sorties des couches précédentes.

La seule différence réside dans le choix de la fonction d'activation, à l'exception du nombre de neurones.

Fonctions d'activation: relu, softmax et sigmoid

En règle générale, vous devez utiliser la fonction d'activation "lu" pour toutes les couches, sauf la dernière. La dernière couche d'un classificateur utiliserait l'activation &softmax.

Là encore, un "neurone" calcule une somme pondérée de toutes ses entrées, ajoute une valeur appelée "biais" et transmet le résultat via la fonction d'activation.

La fonction d'activation la plus utilisée est "RELU" pour l'unité linéaire rectifiée. Cette fonction est très simple, comme vous pouvez le constater sur le graphique ci-dessus.

La fonction d'activation traditionnelle des réseaux de neurones était "sigmoid"mais celle-ci rencontrait des propriétés de convergence plus nombreuses et mieux maintenant.

Activation softmax pour la classification

La dernière couche de notre réseau de neurones compte 10 neurons, car nous voulons classer les chiffres manuscrits en 10 classes (0 à 9). Il doit générer 10 chiffres compris entre 0 et 1, qui représentent la probabilité que ce chiffre soit de 0, de 1, de 2, etc. À cette fin, nous allons utiliser une fonction d'activation appelée "softmax".

L'application de softmax sur un vecteur s'effectue en prenant l' exponentielle de chaque élément, puis en normalisant le vecteur, généralement en le divisant par son, &l1&quo; norm (c'est-à-dire la somme des valeurs absolues), de sorte que les valeurs normalisées soient égales à 1 et peuvent être interprétées comme des probabilités.

Le résultat de la dernière couche est parfois appelé "logits". Si ce vecteur est L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9], alors:

Perte d'entropie croisée

Maintenant que notre réseau de neurones produit des prédictions à partir d'images d'entrée, nous devons mesurer leur qualité, c'est-à-dire la distance entre ce que le réseau nous indique et les bonnes réponses, souvent appelées « libellés ». N'oubliez pas que les étiquettes de toutes les images de l'ensemble de données sont correctes.

N'importe quelle distance conviendra, mais pour les problèmes de classification, la distance cross-entropie est la plus efficace. Il s'agit de l'erreur ou de la fonction suivante:

Descente de gradient

"entraînement" consiste à utiliser des images et des étiquettes d'entraînement pour ajuster les pondérations et les biais afin de réduire la fonction d'entropie croisée. Voici comment procéder.

L'entropie croisée est une fonction de pondérations, des biais, des pixels de l'image d'entraînement et de sa classe connue.

Si nous calculons les dérivées partielles de l'entropie croisée par rapport à l'ensemble des pondérations et de tous les biais, nous obtenons un "gradient" calculé pour une image, une étiquette et une valeur actuelle de pondérations et de biais. N'oubliez pas que nous pouvons avoir des millions de pondérations et de biais, donc calculer le gradient s'apparente à beaucoup de travail. Heureusement, TensorFlow le fait pour nous. La propriété mathématique d'un dégradé pointe vers le haut. Comme nous voulons nous rendre là où l'entropie croisée est faible, nous allons dans la direction opposée. Nous mettons à jour les pondérations et les biais selon une fraction du dégradé. Nous effectuons ensuite la même opération à l'aide des lots suivants d'images et d'étiquettes d'entraînement, en boucle d'entraînement. Heureusement, le phénomène d'entropie croisée est minime, mais rien ne garantit que ce minimum sera unique.

descente de gradient.png

Mini-lots et dynamique

Vous pouvez calculer votre dégradé sur une seule image d'exemple et mettre immédiatement à jour les pondérations et les biais. Toutefois, sur un lot de 128 images, par exemple, un dégradé représente mieux les contraintes imposées par différents exemples d'images et est donc susceptible de converger plus rapidement vers la solution. La taille du mini-lot est un paramètre ajustable.

Cette technique, parfois appelée "descente de gradient stochastique", présente un autre avantage plus pragmatique: l'utilisation de lots implique également de travailler avec des matrices plus grandes. Elles sont généralement plus faciles à optimiser sur les GPU et les TPU.

La convergence peut quand même être légèrement chaotique et peut même s'arrêter si le vecteur de dégradé est à zéro. Cela signifie-t-il que nous avons trouvé un minimum ? Non. Un composant de dégradé peut être nul ou minimal. Avec un vecteur de dégradé avec des millions d'éléments, s'il s'agit de zéros, la probabilité que tous les zéros correspondent à un minimum et aucun d'entre eux à un point maximal est faible. Dans un espace où il y a de nombreuses dimensions, les points selliers sont assez courants et nous ne voulons pas nous en arrêter.

Illustration: point sellier. Le dégradé est égal à 0, mais pas dans toutes les directions. (Attribution d'image Wikimedia: By Nicoguaro - Own work, CC BY 3.0)

La solution consiste à ajouter de la dynamique à l'algorithme d'optimisation pour qu'il puisse naviguer au-delà des points selliers sans s'arrêter.

Glossaire

batch ou mini-batch: l'entraînement est toujours effectué sur des lots de données d'entraînement et d'étiquettes. Cela contribue à la convergence de l'algorithme. La dimension "lots" est généralement la première dimension des Tensors de données. Par exemple, un Tensor de forme [100, 192, 192, 3] contient 100 images de 192 x 192 pixels avec trois valeurs par pixel (RVB).

perte entropique : fonction de perte spéciale souvent utilisée dans les classificateurs.

couche dense : couche de neurones dont les neurones sont connectés à tous les neurones de la couche précédente.

caractéristiques : les entrées d'un réseau de neurones sont parfois appelées "caractéristiques". L'art de déterminer quelles parties d'un ensemble de données (ou combinaison de parties) pour alimenter un réseau de neurones est appelée "extraction de caractéristiques"

labels : autre nom pour les "classes" ou les réponses correctes dans un problème de classification supervisé

taux d'apprentissage: fraction du dégradé par lequel les pondérations et les biais sont mis à jour à chaque itération de la boucle d'entraînement.

logits: les sorties d'une couche de neurones avant l'application de la fonction d'activation sont appelées "logits". Ce terme est issu de la "fonction logistique", qui était la fonction d'activation la plus populaire. Les résultats de Neuron avant la fonction logistique ont été raccourcis en "logits".

loss : fonction d'erreur comparant les résultats du réseau de neurones aux bonnes réponses

neurone: calcule la somme pondérée de ses entrées, ajoute un biais et alimente le résultat via une fonction d'activation.

Encodage one-hot: la classe 3 sur 5 est encodée sous forme de vecteur de 5 éléments, tous les zéros, sauf le 3e, qui est égal à 1.

relu : unité linéaire corrigée. Fonction d'activation populaire pour les neurones.

sigmoïde : une autre fonction d'activation qui était autrefois populaire et est toujours utile dans des cas spéciaux.

softmax : fonction d'activation spéciale qui agit sur un vecteur, augmente la différence entre le plus grand composant et tous les autres, et normalise le vecteur avec une somme de 1 pour qu'il puisse être interprété comme un vecteur de probabilités. Utilisé comme dernière étape dans les classificateurs.

Tensor: un Tensor de type matrice, mais avec un nombre arbitraire de dimensions Un Tensor unidimensionnel est un vecteur. Un Tensor à deux dimensions est une matrice. Vous avez ensuite des Tensors avec 3, 4, 5 dimensions ou plus.

Revenez au notebook de l'étude et cette fois, lisons le code.

keras_01_mnist.ipynb

Passons en revue toutes les cellules de ce notebook.

Paramètres de la cellule

La taille du lot, le nombre d'époques d'entraînement et l'emplacement des fichiers de données sont définis ici. Les fichiers de données sont hébergés dans un bucket Google Cloud Storage (GCS). C'est pourquoi leur adresse commence par gs://.

Cellules ;importations &

Toutes les bibliothèques Python nécessaires sont importées ici, y compris TensorFlow et matplotlib pour les visualisations.

Cellule utilitaire de visualisation [RUN ME]"

Cette cellule contient un code de visualisation peu intéressant. Il est réduit par défaut, mais vous pouvez l'ouvrir et observer le code au moment voulu en double-cliquant dessus.

Cellule :tf.data.Dataset : analyse des fichiers, et préparation des ensembles de données d'entraînement et de validation"

Cette cellule a utilisé l'API tf.data.Dataset pour charger l'ensemble de données MNIST à partir des fichiers de données. Il n'est pas nécessaire de passer trop de temps sur cette cellule. Si vous êtes intéressé par l'API tf.data.Dataset, voici un tutoriel qui explique son fonctionnement.Pipelines de données à vitesse rapide. Pour le moment, les bases sont les suivantes:

Les images et les libellés (réponses correctes) de l'ensemble de données MNIST sont stockés dans des enregistrements de longueur fixe dans quatre fichiers. Les fichiers peuvent être chargés avec la fonction d'enregistrement fixe dédié :

imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)

Nous disposons maintenant d'un ensemble de données d'octets d'image. Elles doivent être décodées en images. Pour cela, nous définissons une fonction. L'image n'est pas compressée et n'a donc pas besoin de décoder quoi que ce soit (decode_raw n'a rien à faire). L'image est ensuite convertie en valeurs à virgule flottante comprises entre 0 et 1. Nous pourrions le remodeler en tant qu'image 2D, mais nous le conservons en tant que tableau plat de pixels de 28 x 28, car c'est ce que prévoit notre couche dense initiale.

def read_image(tf_bytestring):
    image = tf.decode_raw(tf_bytestring, tf.uint8)
    image = tf.cast(image, tf.float32)/256.0
    image = tf.reshape(image, [28*28])
    return image

Nous appliquons cette fonction à l'ensemble de données à l'aide de .map et obtenons un ensemble de données d'images:

imagedataset = imagedataset.map(read_image, num_parallel_calls=16)

Nous procédons de la même manière pour la lecture et le décodage des étiquettes, et nous .zip les images et les étiquettes ensemble:

dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))

Nous disposons désormais d'un ensemble de données par paires (image, label). C'est ce que attend notre modèle. Nous ne sommes pas encore tout à fait prêts à l'utiliser dans la fonction d'entraînement:

dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)

L'API tf.data.Dataset dispose de toute la fonction utilitaire nécessaire pour préparer les ensembles de données:

.cache met en cache l'ensemble de données dans la RAM. Il s'agit d'un tout petit ensemble de données qui fonctionnera donc. .shuffle le brasse avec un tampon de 5 000 éléments. Il est important de bien brasser les données d'entraînement. .repeat fait une boucle sur l'ensemble de données. Nous allons l'entraîner plusieurs fois (à plusieurs époques). .batch réunit plusieurs images et libellés dans une mini-nache. Enfin, .prefetch peut utiliser le processeur pour préparer le lot suivant pendant l'entraînement du lot actuel sur le GPU.

L'ensemble de données de validation est préparé de la même manière. Nous sommes maintenant prêts à définir un modèle et à utiliser cet ensemble de données pour l'entraîner.

Modèle de réseau mobile Keras

Tous nos modèles sont des séquences directes de calques. Nous pouvons donc utiliser le style tf.keras.Sequential pour les créer. Dans un premier temps, il s'agit d'une seule couche dense. Il y a 10 neurons, car nous classons des chiffres manuscrits en 10 classes. Elle utilise l'activation &softt, car il s'agit de la dernière couche d'un classificateur.

Un modèle Keras doit également connaître la forme de ses entrées. Vous pouvez utiliser tf.keras.layers.Input pour le définir. Ici, les vecteurs d'entrée sont des vecteurs plats de valeurs en pixels de longueur 28*28.

model = tf.keras.Sequential(
  [
    tf.keras.layers.Input(shape=(28*28,)),
    tf.keras.layers.Dense(10, activation='softmax')
  ])

model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# print model layers
model.summary()

# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)

La configuration du modèle s'effectue dans Keras avec la fonction model.compile. Nous utilisons ici l'optimiseur de base 'sgd' (descente de gradient stochastique). Un modèle de classification nécessite une fonction de perte d'entropie croisée appelée 'categorical_crossentropy' dans Keras. Enfin, nous demandons au modèle de calculer la métrique 'accuracy', qui est le pourcentage d'images correctement classées.

Keras propose l'utilitaire très utile model.summary() qui affiche les détails du modèle que vous avez créé. Votre formateur a ajouté l'utilitaire PlotTraining (défini dans la cellule "Utilitaires de visualisation" de la cellule) qui permet d'afficher diverses courbes d'entraînement pendant l'entraînement.

Cellule : entraînez et validez le modèle.

C'est là que l'entraînement se produit, en appelant model.fit et en transmettant les ensembles de données d'entraînement et de validation. Par défaut, Keras effectue une série de validation à la fin de chaque époque.

model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
          validation_data=validation_dataset, validation_steps=1,
          callbacks=[plot_training])

Dans Keras, il est possible d'ajouter des comportements personnalisés pendant l'entraînement à l'aide de rappels. C'est ainsi que le graphique d'entraînement a été mis à jour de façon dynamique pour cet atelier.

Cellule "Visualiser les prédictions"

Une fois le modèle entraîné, nous pouvons en obtenir des prédictions en appelant model.predict():

probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)

Ici, nous avons préparé un ensemble de chiffres imprimés à partir de polices locales. N'oubliez pas que le réseau de neurones affiche un vecteur de 10 probabilité à partir de son &maxt;softmax. Pour obtenir l'étiquette, nous devons déterminer la probabilité la plus élevée. np.argmax de la bibliothèque numpy effectue cette opération.

Pour comprendre pourquoi le paramètre axis=1 est nécessaire, n'oubliez pas que nous avons traité un lot de 128 images. Le modèle renvoie donc 128 vecteurs de probabilités. La forme du Tensor de sortie est [128, 10]. Nous calculons l'argmax pour les 10 probabilités renvoyées pour chaque image, donc axis=1 (le premier axe étant 0).

Ce modèle simple reconnaît déjà 90% des chiffres. Vous n'allez pas mal, mais vous allez maintenant vous améliorer de manière significative.

Godeep.png

Pour améliorer la précision de la reconnaissance, nous allons ajouter d'autres couches au réseau de neurones.

Capture d'écran 27/07/2016 à 15.36.55.png

Nous conservons softmax comme fonction d'activation sur la dernière couche, car c'est la méthode la plus efficace pour la classification. Cependant, sur les couches intermédiaires, nous utiliserons la fonction d'activation la plus classique: le sigmoïde:

Par exemple, le modèle peut se présenter comme suit (n'oubliez pas les virgules : tf.keras.Sequential utilise une liste de calques séparés par une virgule) :

model = tf.keras.Sequential(
  [
      tf.keras.layers.Input(shape=(28*28,)),
      tf.keras.layers.Dense(200, activation='sigmoid'),
      tf.keras.layers.Dense(60, activation='sigmoid'),
      tf.keras.layers.Dense(10, activation='softmax')
  ])

Examinez le "Résumé" de votre modèle. Elle comporte désormais au moins 10 fois plus de paramètres. Le résultat devrait être 10 fois plus élevé ! Mais pour une raison quelconque, ce n'est pas ...

La perte semble également être passée à travers le toit. Un problème est survenu.

Vous venez de tester les réseaux de neurones. Il s'agissait des personnes qui les créaient dans les années 80 et 90. Il n'est pas étonnant que le projet ait été fondé sur l'idée de l'IA hivernale. En effet, à mesure que vous ajoutez des couches, les réseaux de neurones ont de plus en plus de difficultés à converger.

Il s'avère que les réseaux de neurones profonds comportant de nombreuses couches (20, 50, voire 100 aujourd'hui) peuvent très bien fonctionner, à la différence de quelques tours de mathématiques mathématiques qui provoquent la convergence. La découverte de ces astuces simples est l'une des raisons qui expliquent la renaissance du deep learning en 2010.

Activer le RELU

Fichier rel.png

La fonction d'activation des sigmoïdes est relativement problématique sur les réseaux profonds. Il écrase toutes les valeurs comprises entre 0 et 1. Lorsque cela se produit à plusieurs reprises, les sorties de neurones et leurs gradients peuvent disparaître complètement. Il a été mentionné pour des raisons historiques, mais les réseaux modernes utilisent le format RELU (Rectified Linear Unit) semblable à celui-ci:

En revanche, le chiffre a la valeur de 1, au moins sur le côté droit. Avec l'activation du RELU, même si les gradients provenant de certains neurones peuvent être nuls, d'autres donnent toujours un gradient non égal à zéro, et l'entraînement peut continuer à un rythme soutenu.

Optimisez votre expérience

Dans les espaces de très grande dimension, comme ici, nous avons un ordre de 10 000 poids et biais, et les points de selle sont fréquents. Il s'agit des points qui ne sont pas des minimums locaux, mais où le dégradé est égal à zéro et l'optimiseur de descente de gradient reste bloqué ici. TensorFlow dispose d'un large éventail d'optimiseurs disponibles, y compris certains qui fonctionnent avec une certaine inertie et qui traversent en toute sécurité les points obsolètes.

Initialisations aléatoires

L'art d'initialisation des pondérations avant la formation constitue un domaine de recherche en soi, avec de nombreux articles publiés à ce sujet. Pour consulter la liste de tous les initialisateurs disponibles dans Keras, cliquez ici. Heureusement, Keras fait le bon choix par défaut et utilise l'initialiseur 'glorot_uniform' qui est parfaitement adapté dans presque tous les cas.

Vous n'avez rien à faire, car Keras fait déjà ce qu'il faut.

NaN ???

La formule d'entropie croisée implique un logarithme, et "log(0)" n'est pas un nombre (NaN), un plantage numérique si vous préférez. L'entrée pour l'entropie croisée peut-elle être égale à 0 ? L'entrée provient de softmax, une fonction exponentielle et une valeur exponentielle n'est jamais nulle. Nous sommes donc en sécurité.

Et là vous vous dites : "ah bon ?" Dans le beau monde des mathématiques, nous pourrions en utiliser une. Mais dans le monde de l'informatique, l'exp(-150), représenté au format float32, est de zéro à sa valeur et les plantages d'entropie croisée s'affichent.

Heureusement, vous n'avez rien d'autre à faire ici, car Keras se charge de ce calcul et calcule le softmax suivi de l'entropie croisée avec une attention particulière afin de garantir la stabilité numérique et d'éviter les NaN redoutés.

Vous avez réussi ?

Vous devez à présent obtenir un taux de précision de 97 %. Dans cet atelier, l'objectif est nettement supérieur à 99 %.

Si vous êtes bloqué, voici la solution à ce stade:

keras_02_mnist_dense.ipynb

Peut-être pouvons-nous essayer de nous entraîner plus vite ? Le taux d'apprentissage par défaut dans l'optimiseur Adam est de 0,001. Essayez d'augmenter ce nombre.

Accélérer l'exécution ne semble pas être d'une grande aide, et vous vous demandez pourquoi ?

Les courbes d'entraînement sont très bruyantes et regardent les deux courbes de validation: elles sautent vers le haut et vers le bas. Nous accélérons donc. Nous pourrions revenir à notre vitesse précédente, mais il y a un meilleur moyen.

ralenti.png

La bonne solution consiste à démarrer rapidement et à augmenter le taux d'apprentissage de manière exponentielle. Dans Keras, vous pouvez le faire à l'aide du rappel tf.keras.callbacks.LearningRateScheduler.

Code utile pour le copier-coller:

# lr decay function
def lr_decay(epoch):
  return 0.01 * math.pow(0.6, epoch)

# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)

# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)

N'oubliez pas d'utiliser le lr_decay_callback que vous avez créé. Ajoutez-le à la liste des rappels dans model.fit :

model.fit(...,  callbacks=[plot_training, lr_decay_callback])

L'impact de cette légère modification est spectaculaire. Vous constatez que la majeure partie du bruit a disparu et que la précision du test est maintenant supérieure à 98% de manière durable.

Le modèle semble maintenant converger correctement. Essayons d'aller encore plus loin.

Est-ce que cela vous permet ?

Non, la précision est toujours bloquée à 98 %, et la perte de validation a été constatée. Ça monte ! L'algorithme d'apprentissage ne traite que les données d'entraînement et optimise les pertes d'entraînement en conséquence. Les données de validation n'apparaissent jamais. Il n'est donc pas surprenant qu'au bout d'un certain temps, son travail n'ait plus d'effet sur la perte de validation, qui entraîne une interruption et parfois un rebond.

Cela n'affecte pas immédiatement les capacités de reconnaissance réelles de votre modèle, mais cela vous empêchera d'exécuter de nombreuses itérations et est généralement le signe que l'entraînement n'a plus d'effet positif.

dropout.png

Cet écart est généralement appelé "surapprentissage" et, lorsque vous le voyez, vous pouvez essayer d'appliquer une technique de régularisation appelée "abandon". La technique d'abandon utilise des neurones aléatoires à chaque itération de l'entraînement.

Cela a-t-il fonctionné ?

Le bruit réapparaît (sans surprise). La perte de validation ne semble plus se peser, mais elle est globalement plus élevée que sans abandon. La justesse de la validation a baissé. Ce résultat est plutôt décevant.

L'abandon semble être la solution correcte, ou le surapprentissage est un concept plus complexe, et certaines de ses causes ne peuvent pas être résolues à l'aide d'un abandon.

Qu'est-ce que le surapprentissage ? Le surapprentissage se produit lorsqu'un réseau de neurones apprend "mauvais" et non pour les exemples d'entraînement mais pas pour les données réelles. Certaines techniques de régularisation, comme l'abandon, peuvent l'obliger à améliorer son apprentissage, mais le surapprentissage a également des racines plus profondes.

surapprentissage.png

Le surapprentissage de base se produit lorsqu'un réseau de neurones a trop de liberté pour résoudre le problème. Imaginez que nous disposons d'un tel nombre de neurones que le réseau peut stocker toutes nos images d'entraînement, puis qu'il les reconnaît par correspondance de modèles. car cela échouerait complètement des données réelles. Un réseau de neurones doit être soumis à des contraintes qui obligent à généraliser ce qu'il apprend pendant l'entraînement.

Si vous avez très peu de données d'entraînement, même un petit réseau peut les apprendre par cœur, et vous constaterez un surapprentissage. De manière générale, vous avez besoin de beaucoup de données pour entraîner les réseaux de neurones.

Enfin, si vous avez tout fait dans le livre, testé différentes tailles de réseau pour vous assurer que les niveaux de liberté sont limités, appliqués à l'abandon et entraînés sur de nombreuses données, il est possible qu'il reste bloqué à un niveau de performances que vous ne pourriez pas améliorer. Votre réseau de neurones, dans sa forme actuelle, n'est donc pas en mesure d'extraire plus d'informations à partir de vos données, comme dans le cas présent.

Vous vous souvenez de la façon dont nous utilisons nos images, regroupées dans un seul vecteur ? Mauvaise idée. Les chiffres écrits à la main sont constitués de formes et nous avons supprimé les informations de formes lorsque nous avons aplati les pixels. Cependant, un type de réseau de neurones peut exploiter des informations de forme: les réseaux convolutifs. Faisons le test.

Si vous êtes bloqué, voici la solution à ce stade:

keras_03_mnist_dense_lrdecay_dropout.ipynb

En résumé

Si vous connaissez déjà tous les termes du paragraphe gras, vous pouvez passer à l'exercice suivant. Si vous débutez avec des réseaux de neurones convolutifs, lisez la suite.

GIF convolutif.gif

Illustration: filtrer une image avec deux filtres successifs composés de 4 x 4 x 48 pixels apprenant.

Voici à quoi ressemble un réseau de neurones convolutif simple dans Keras:

model = tf.keras.Sequential([
    tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
    tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(10, activation='softmax')
])

Dans une couche d'un réseau convolutif, un "neurone" effectue une somme pondérée des pixels juste au-dessus, dans une petite zone de l'image. Elle ajoute un biais et alimente la somme via une fonction d'activation, comme un neurones dans une couche dense et régulière. Cette opération est ensuite répétée sur l'ensemble de l'image avec les mêmes pondérations. Rappelez-vous que dans les couches denses, chaque neurone possédait ses propres pondérations. Ici, un seul "pochage" de pondérations glisse dans l'image dans les deux sens (une &convolution). Le résultat comporte autant de valeurs que de pixels dans l'image (une marge intérieure est toutefois nécessaire au niveau des bords). Il s'agit d'une opération de filtrage. Dans l'illustration ci-dessus, on utilise un filtre de 4 x 4 x 3=48 poids.

Cependant, 48 poids ne suffit pas. Pour ajouter des degrés de liberté, nous répétons la même opération avec un nouvel ensemble de pondérations. Cela génère un nouvel ensemble de résultats de filtre. Par exemple,appelons-le "canal" de sorties par analogie avec les canaux R,G et B de l'image d'entrée.

Capture d'écran 29/07/2016 à 16.02.37.png

Au moins un ensemble de pondérations peut être ajouté comme un Tensor en ajoutant une dimension. Nous obtenons ainsi la forme générique du Tensor de pondérations pour une couche convolutive. Étant donné que le nombre de canaux d'entrée et de sortie est un paramètre, nous pouvons commencer à superposer et à associer des couches convolutives.

Illustration: un réseau de neurones convolutif transforme des cubes de données en d'autres cubes de données.

Convolutions confondues, pooling maximal

En effectuant les convolutions avec une vitesse de 2 ou 3, nous pouvons également réduire le cube de données obtenu dans ses dimensions horizontales. Pour cela, vous pouvez procéder de deux façons:

  • Convolution écartée: filtre glissant comme ci-dessus, avec foulée >1
  • Pooling max. : fenêtre coulissante appliquant l'opération MAX (généralement sur des correctifs de 2 x 2, répétée tous les 2 pixels)

Illustration: le fait de faire glisser la fenêtre de calcul de 3 pixels permet de réduire le nombre de valeurs de sortie. Les convolutions écartées ou le pooling maximal (max. dans une fenêtre 2x2 glissant par foulée de 2) permettent de réduire le cube de données dans les dimensions horizontales.

Calque finale

Après la dernière couche convolutive, les données se présentent sous la forme d'un cube. Il existe deux manières de l'insérer à travers la couche dense finale.

Le premier consiste à aplatir le cube de données dans un vecteur, puis à l'insérer dans la couche softmax. Vous pouvez même ajouter une couche dense avant la couche softmax. qui a tendance à être cher en termes de nombre de pondérations. Une couche dense à l'extrémité d'un réseau convolutif peut contenir plus de la moitié du poids de l'ensemble du réseau de neurones.

Plutôt que d'utiliser une couche dense dense, nous pouvons également scinder les données entrantes en cube, en autant de parties que nous avons de classes, calculer leur valeur moyenne et les utiliser pour alimenter une fonction d'activation softmax. De cette manière, la tête de la classification ne coûte aucune pondération. Dans Keras, il y a une couche pour cela: tf.keras.layers.GlobalAveragePooling2D().

Passez à la section suivante pour créer un réseau convolutif à résoudre.

Créons un réseau convolutif pour la reconnaissance de chiffres manuscrits. Nous allons utiliser trois couches convolutives en haut, notre couche d'apprentissage softmax traditionnelle en bas et les connecter à une couche entièrement connectée:

Notez que les deuxième et troisième couches convolutives ont une vitesse de deux, ce qui explique pourquoi elles réduisent le nombre de valeurs de sortie de 28 x 28 à 14 x 14, puis de 7 x 7.

Écrivons le code Keras.

Une attention particulière est requise avant la première couche convolutive. En effet, on prévoit un cube 3D de données, mais l'ensemble de données a été configuré jusqu'à présent pour des calques denses, et tous les pixels des images sont aplatis dans un vecteur. Nous devons les remodeler en images au format 28 x 28 x 1 (un canal pour les images en nuances de gris):

tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))

Vous pouvez utiliser cette ligne à la place de la couche tf.keras.layers.Input que vous aviez jusqu'à présent.

Dans Keras, la syntaxe d'une couche convolutive appelée "relu'-activated" est la suivante:

tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')

Dans ce cas, vous devez écrire:

tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)

Pour aplatir un cube de données dans un vecteur de sorte qu'il puisse être utilisé par une couche dense:

tf.keras.layers.Flatten()

Et pour la couche dense, la syntaxe n'a pas changé:

tf.keras.layers.Dense(200, activation='relu')

Votre modèle a-t-il atteint la barrière de la précision de 99 % ? Presque proche... mais regardez la courbe de perte de validation. Cette sonnerie sonne-t-elle ?

Examinez également les prédictions. Pour la première fois, vous devriez constater que la plupart des 10 000 chiffres de test sont désormais correctement reconnus. Il ne reste que plus de 41⁄2 lignes de détections erronées (sur 110 chiffres sur 10 000)

Si vous êtes bloqué, voici la solution à ce stade:

keras_04_mnist_convolutional.ipynb

La formation précédente présente des signes évidents de surapprentissage (mais elle est encore inexacte). Dois-je réessayer l'abandon ?

Comment s'est-il passé cette fois ?

Il semble que la fonctionnalité d'abandon s'est bien déroulée cette fois-ci. la perte de validation ne remonte plus. La justesse finale doit être bien supérieure à 99%. Félicitations !

La première fois que nous avons essayé d'appliquer l'abandon, nous pensions qu'il y avait un problème de surapprentissage, alors qu'il s'agissait en fait de l'architecture du réseau de neurones. Nous ne pouvons pas aller plus loin sans les couches convolutives, et rien ne permet de faire cela.

Cette fois-ci, il semble que le surapprentissage soit à l'origine du problème, et que l'abandon a bien permis de résoudre le problème. N'oubliez pas qu'un grand nombre d'éléments peuvent provoquer une déconnexion entre les courbes d'entraînement et de validation, qui augmentent progressivement la perte de validation. Le surapprentissage (trop de degrés de liberté, utilisés de manière incorrecte par le réseau) n'est que l'un d'entre eux. Si votre ensemble de données est trop petit ou si l'architecture de votre réseau de neurones n'est pas adaptée, vous pouvez constater un comportement similaire sur les courbes de fonction de perte, mais l'abandon ne sera pas utile.

Enfin, essayons d'ajouter la normalisation des lots.

En pratique, n'oubliez pas quelques règles:

Pour l'instant, jouez au livre et ajoutez une couche "nord" par lot à chaque couche du réseau de neurones, mais à la dernière. Ne l'ajoutez pas à la dernière couche "softmax". Cet outil ne serait pas utile.

# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),

Quel est le degré de précision ?

Avec un peu de réglage (BATCH_SIZE=64, paramètre de dépréciation du taux d'apprentissage de 0,666), le taux d'abandon sur la couche dense 0,3 et un peu de chance, vous pouvez atteindre 99,5%. Les ajustements de taux d'apprentissage et d'abandon ont été effectués en suivant les "bonnes pratiques" relatives à l'utilisation de la norme de traitement par lot:

  • La norme de traitement par lot aide les réseaux de neurones à converger et vous permet en général d'entraîner des données plus rapidement.
  • La norme de traitement par lot est un régulateur. Vous pouvez généralement réduire le volume d'abandons que vous utilisez, voire ne pas en utiliser du tout.

Le notebook du solution propose un entraînement de 99,5 % :

keras_05_mnist_batch_norm.ipynb

Vous trouverez une version du code adaptée au cloud dans le dossier mlengine sur GitHub, ainsi que des instructions pour l'exécuter sur Google Cloud AI Platform. Avant de pouvoir exécuter cette partie, vous devez créer un compte Google Cloud et activer la facturation. Les ressources nécessaires pour réaliser l'atelier devraient être inférieures à quelques dollars (en supposant qu'une heure de temps d'entraînement est nécessaire sur un GPU). Pour préparer votre compte:

  1. Créez un projet Google Cloud Platform (http://cloud.google.com/console).
  2. Activez la facturation.
  3. Installez les outils de ligne de commande GCP (SDK GCP).
  4. Créez un bucket Google Cloud Storage (à utiliser dans la région us-central1). Il sera utilisé pour entreposer le code d'entraînement et stocker le modèle entraîné.
  5. Activez les API nécessaires et demandez les quotas nécessaires (exécutez la commande d'entraînement une fois pour obtenir les messages d'erreur qui vous indiquent ce que vous devez activer).

Vous avez créé votre premier réseau de neurones et l'avez entraîné jusqu'à 99% de précision. Les techniques apprises ne sont pas spécifiques à l'ensemble de données MNIST, mais elles sont couramment utilisées pour travailler avec des réseaux de neurones. En guise de cadeau de fin d'année, voici la carte à gratter du laboratoire, en version dessins animés. Vous pouvez l'utiliser pour mémoriser ce que vous avez appris:

falaises notez tensorflow lab.png

Étapes suivantes

  • Après avoir connecté les réseaux de neurones convolutifs et connectés, examinez les réseaux de neurones récurrents.
  • Pour exécuter vos tâches d'entraînement ou d'inférence sur le cloud sur une infrastructure distribuée, Google Cloud propose AI Platform.
  • Enfin, nous apprécions vos commentaires. N'hésitez pas à nous contacter si vous constatez un problème dans cet atelier ou si vous pensez qu'il devrait être amélioré. Nous traitons les commentaires via des problèmes GitHub [lien vers le commentaire].

HR.png

Martin Görner ID small.jpg

Auteur: Martin Görner

Twitter : @martin_gorner

Images de tous les dessins animés de cet atelier: alexpokusay / 123RF stock photos