Exercice - Créer et entraîner un réseau neuronal

Effectué

Dans cette unité, vous allez utiliser Keras pour créer et entraîner un réseau neuronal qui analyse les sentiments exprimés dans un texte. Avant de pouvoir entraîner un réseau neuronal, vous devez préparer les données qui seront utilisées pour son entraînement. Plutôt que de télécharger un jeu de données externe, vous allez utiliser le jeu de données de la classification des sentiments des critiques de films d’IMDB qui est fourni avec Keras. Le jeu de données IMDB contient 50 000 critiques de films qui ont chacune été évaluées comme positive (1) ou négative (0). Il est subdivisé en 25 000 critiques pour l’entraînement et 25 000 critiques pour les tests. Sur la base des sentiments exprimés dans ces critiques, votre réseau neuronal va analyser le texte qui lui est présenté et évaluer les sentiments.

Le jeu de données IMDB est l’un des jeux de données utiles fournis avec Keras. Pour obtenir une liste complète des jeux de données intégrés, consultez https://keras.io/datasets/.

  1. Tapez ou collez le code suivant dans la première cellule du notebook, puis cliquez sur le bouton Exécuter (ou appuyez sur Maj+Entrée) pour exécuter le code et ajouter une nouvelle cellule en dessous :

    from keras.datasets import imdb
    top_words = 10000
    (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=top_words)
    

    Ce code charge le jeu de données IMDB inclus avec Keras, puis il crée un dictionnaire qui mappe les mots contenus dans l’ensemble des 50 000 critiques à des nombres entiers indiquant la fréquence d’emploi relative des mots. Chaque mot est mappé à un entier unique. Le mot le plus fréquent reçoit le nombre 1, le deuxième le nombre 2, et ainsi de suite. load_data retourne également une paire de tuples qui contiennent les critiques de films (dans cet exemple, x_train et x_test) ainsi que les scores 1 et 0 qui classifient ces critiques comme positives ou négatives (y_train et y_test).

  2. Vérifiez que vous voyez le message « Using TensorFlow backend », qui indique que Keras utilise bien TensorFlow comme back-end.

    Loading the IMDB dataset.

    Chargement du jeu de données IMDB

    Si vous préférez que Keras utilise Microsoft Cognitive Toolkit (également appelé CNTK) comme back-end, vous pouvez le configurer en ajoutant quelques lignes de code au début du notebook. Pour obtenir un exemple, consultez CNTK and Keras in Azure Notebooks.

  3. Donc, au final, qu’est-ce que la fonction load_data a-t-elle chargé ? La variable x_train est une liste de 25 000 listes, chacune d’elles représentant une critique de film. (x_test est également une liste de 25 000 listes représentant 25 000 critiques. x_train est utilisé pour l’entraînement, tandis que x_test est utilisé à des fins de test.) Toutefois, les listes internes, celles qui représentent les critiques de films, ne contiennent pas de mots mais des nombres entiers. Elles sont décrites ainsi dans la documentation Keras :

    Keras documentation.

    La raison pour laquelle les listes internes contiennent des nombres plutôt que du texte est que l’entraînement d’un réseau neuronal s’effectue avec des nombres, pas avec du texte. Plus précisément, l’entraînement est basé sur des tenseurs. Dans ce cas, chaque critique est un tenseur à une seule dimension (comme un tableau unidimensionnel) contenant des entiers qui identifient les mots employés dans la critique. En guise d’exemple, tapez l’instruction Python suivante dans une cellule vide et exécutez-la pour voir les entiers qui représentent la première critique dans le jeu d’entraînement :

    x_train[0]
    

    Integers comprising the first review in the IMDB training set.

    Entiers représentant la première critique dans le jeu d’entraînement IMDB

    Le premier nombre dans la liste, 1, ne représente pas un mot. Il indique le début de la critique et est identique pour chaque critique dans le jeu de données. Les nombres 0 et 2 sont également réservés. Vous soustrayez 3 des autres nombres pour mapper un entier dans une critique à l’entier correspondant dans le dictionnaire. Le deuxième nombre, 14, référence le mot qui correspond au nombre 11 dans le dictionnaire, le troisième nombre représente le mot correspondant au nombre 19 dans le dictionnaire, et ainsi de suite.

  4. Vous êtes curieux de voir à quoi ressemble le dictionnaire ? Exécutez l’instruction suivante dans une nouvelle cellule du notebook :

    imdb.get_word_index()
    

    Vous voyez seulement une partie des entrées du dictionnaire, mais en tout, le dictionnaire comprend plus de 88 000 mots et les entiers associés. La sortie que vous verrez sera probablement différente de celle illustrée dans la capture d’écran, car le dictionnaire est généré à chaque nouvel appel de load_data.

    Dictionary mapping words to integers.

    Mots mappés à des entiers dans le dictionnaire

  5. Comme vous l’avez vu, chaque critique dans le jeu de données est encodée comme une collection de nombres entiers au lieu de mots. Est-il possible de contrepasser l’encodage d’une critique afin de voir le texte initial de la critique ? Entrez les instructions suivantes dans une nouvelle cellule et exécutez-les pour afficher la première critique dans x_train au format texte :

    word_dict = imdb.get_word_index()
    word_dict = { key:(value + 3) for key, value in word_dict.items() }
    word_dict[''] = 0  # Padding
    word_dict['>'] = 1 # Start
    word_dict['?'] = 2 # Unknown word
    reverse_word_dict = { value:key for key, value in word_dict.items() }
    print(' '.join(reverse_word_dict[id] for id in x_train[0]))
    

    Dans la sortie, « > » marque le début de la critique, tandis que « ? » signale les mots qui ne figurent pas parmi les 10 000 mots les plus fréquents dans le jeu de données. Ces mots « inconnus » sont mappés au nombre 2 dans la liste des entiers représentant une critique. Vous vous souvenez du paramètre num_words que vous avez passé à load_data ? C’est maintenant qu’il entre en jeu. Ce paramètre ne réduit pas la taille du dictionnaire, mais il restreint la plage d’entiers utilisés pour encoder les critiques.

    The first review in textual format.

    Première critique au format texte

  6. Les critiques sont « propres », c’est-à-dire que les lettres ont été converties en minuscules et que les caractères de ponctuation ont été supprimés. Toutefois, elles ne sont pas encore prêtes pour entraîner un réseau neuronal à analyser les sentiments exprimés dans le texte. Quand vous entraînez un réseau neuronal avec une collection de tenseurs, tous les tenseurs doivent avoir la même longueur. Or les listes représentant les critiques dans x_train et x_test sont actuellement de longueur différente.

    Heureusement, Keras inclut une fonction qui prend une liste de listes en entrée et convertit les listes internes à une longueur spécifiée, en les tronquant si nécessaire ou en les remplissant avec des zéros (0). Entrez le code suivant dans le notebook et exécutez-le pour limiter toutes les listes représentant des critiques de films dans x_train et x_test à une longueur de 500 entiers :

    from keras.preprocessing import sequence
    max_review_length = 500
    x_train = sequence.pad_sequences(x_train, maxlen=max_review_length)
    x_test = sequence.pad_sequences(x_test, maxlen=max_review_length)
    
  7. Les données d’entraînement et de test sont prêtes. Il est temps maintenant de générer le modèle ! Dans le notebook, exécutez le code suivant pour créer un réseau neuronal qui effectue l’analyse des sentiments :

    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers.embeddings import Embedding
    from keras.layers import Flatten
    
    embedding_vector_length = 32
    model = Sequential()
    model.add(Embedding(top_words, embedding_vector_length, input_length=max_review_length))
    model.add(Flatten())
    model.add(Dense(16, activation='relu'))
    model.add(Dense(16, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy'])
    print(model.summary())
    

    La sortie doit ressembler à ceci :

    Creating a neural network with Keras.

    Création d’un réseau neuronal avec Keras

    Ce code est la base à partir duquel vous construisez un réseau neuronal avec Keras. Il instancie d’abord un objet Sequential représentant un modèle « séquentiel », qui se compose d’une pile de couches de bout en bout où la sortie d’une couche constitue l’entrée de la couche suivante.

    Les instructions suivantes ajoutent des couches au modèle. La première est une couche d’incorporation, qui est essentielle dans les réseaux neuronaux qui traitent des mots. La couche d’incorporation mappe essentiellement des tableaux à plusieurs dimensions contenant des index de mots entiers en tableaux de valeurs à virgule flottante contenant moins de dimensions. Elle permet également de traiter des mots ayant des significations similaires de manière identique. Le traitement complet des incorporations de mots n’entre pas dans le cadre de cet atelier, mais vous pourrez trouver plus d’informations à ce sujet dans Why You Need to Start Using Embedding Layers. Si vous préférez une présentation plus technique, lisez Efficient Estimation of Word Representations in Vector Space. Après l’ajout de la couche d’incorporation, Flatten est appelé pour remettre en forme la sortie à passer en entrée à la couche suivante.

    Les trois couches suivantes ajoutées au modèle sont des couches denses, également appelées couches entièrement connectées. Ce sont les couches classiques qui figurent en principe dans les réseaux neuronaux. Chaque couche contient n nœuds ou neurones, et chaque neurone reçoit une entrée de chaque neurone de la couche précédente, d’où le terme de couches « entièrement connectées ». Ces couches permettent au réseau neuronal d’« apprendre » à partir des données d’entrée en devinant la sortie de manière itérative, en vérifiant les résultats et en ajustant les connexions pour affiner les résultats. Les deux premières couches denses dans ce réseau contiennent 16 neurones chacune. Ce nombre a été choisi arbitrairement. Vous pouvez essayer différentes tailles afin d’améliorer la précision du modèle. La dernière couche dense contient un seul neurone, car l’objectif final du réseau est de prédire une sortie unique, à savoir un score de sentiment compris entre 0.0 et 1.0.

    Le résultat est le réseau neuronal illustré ci-dessous. Le réseau contient une couche d’entrée, une couche de sortie et deux couches masquées (les couches denses contenant 16 neurones chacune). À titre de comparaison, certains réseaux neuronaux plus sophistiqués actuels ont plus de 100 couches. C’est le cas, par exemple, du réseau ResNet-152 de Microsoft Research, dont la précision d’identification des objets sur les photographies dépasse parfois celle d’un humain. Pour concevoir un réseau ResNet-152 avec Keras, vous auriez besoin d’entraîner tout un cluster d’ordinateurs avec GPU à partir de zéro.

    Visualizing the neural network.

    Illustration du réseau neuronal

    L’appel à la fonction compile « compile » le modèle en spécifiant différents paramètres importants à utiliser, comme l’optimiseur ainsi que les métriques d’évaluation de la précision du modèle à chaque étape de l’entraînement. L’entraînement commençant seulement après l’appel de la fonction fit du modèle, l’appel compile s’exécute généralement rapidement.

  8. Appelez maintenant la fonction fit pour entraîner le réseau neuronal :

    hist = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=5, batch_size=128)
    

    L’entraînement prend environ six minutes, ou un peu plus d’une minute par époque (epoch). epochs=5 indique à Keras de faire cinq passes vers l’avant et l’arrière dans le modèle. À chaque passe, le modèle poursuit son apprentissage à partir des données d’entraînement et mesure (« valide ») son niveau d’apprentissage à l’aide des données de test. Ensuite, il apporte des ajustements et revient en arrière pour la passe ou l’époque suivante. Cela est traduit dans la sortie de la fonction fit, qui montre la précision d’entraînement (acc) et la précision de validation (val_acc) pour chaque époque.

    batch_size=128 indique à Keras d’utiliser 128 exemples de données d’entraînement à la fois pour entraîner le réseau. Les tailles de lots plus grandes accélèrent l’entraînement (car moins de passes sont nécessaires dans chaque époque pour consommer l’intégralité des données d’entraînement). En revanche, les tailles de lots plus petites tendent à améliorer la précision. À la fin de ce labo, si vous le souhaitez, vous pourrez revenir en arrière et réentraîner le modèle en utilisant une taille de lot de 32 afin de voir l’effet éventuel de ce changement sur la précision du modèle. Avec cette taille, l’entraînement est à peu près deux fois plus long.

    Training the model.

    Entraînement du modèle

  9. Ce modèle est atypique en ce sens qu’il réussit à bien apprendre en seulement quelques époques. La précision d’entraînement s’approche vite des 100 %, tandis que la précision de validation augmente pendant une ou deux époques avant de se stabiliser. En règle générale, vous ne souhaiterez pas entraîner un modèle plus longtemps que la durée nécessaire pour stabiliser ces précisions. Le risque est le surajustement du modèle, qui donne de meilleurs résultats sur les données de test que sur les données réelles. Quand vous observez un écart croissant entre la précision d’entraînement et la précision de validation, c’est le signe d’un surajustement du modèle. Pour avoir une excellente introduction au surajustement, consultez Overfitting in Machine Learning: What It Is and How to Prevent It.

    Pour visualiser les changements dans les précisions d’entraînement et de validation au fil du processus d’entraînement, exécutez les instructions suivantes dans une nouvelle cellule du notebook :

    import seaborn as sns
    import matplotlib.pyplot as plt
    %matplotlib inline
    
    sns.set()
    acc = hist.history['acc']
    val = hist.history['val_acc']
    epochs = range(1, len(acc) + 1)
    
    plt.plot(epochs, acc, '-', label='Training accuracy')
    plt.plot(epochs, val, ':', label='Validation accuracy')
    plt.title('Training and Validation Accuracy')
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(loc='upper left')
    plt.plot()
    

    Les données sur la précision proviennent de l’objet history retourné par la fonction fit du modèle. Sur la base du graphique que vous voyez ici, vous recommanderiez d’augmenter le nombre d’époques d’entraînement, de le diminuer ou de le laisser identique ?

  10. Un autre moyen de détecter un surajustement est de comparer la perte d’entraînement et la perte de validation tout au long de l’entraînement. Les problèmes d’optimisation tels que celui-ci cherchent à minimiser une fonction de perte (loss). Vous trouverez plus d’informations ici. Si, pour une époque donnée, vous observez une perte d’entraînement beaucoup plus grande que la perte de validation, cela peut être le signe d’un surajustement. À l’étape précédente, vous avez utilisé les propriétés acc et val_acc de la propriété history de l’objet history pour tracer les précisions d’entraînement et de validation. La même propriété contient également des valeurs nommées loss et val_loss qui représentent une perte d’entraînement et une perte de validation, respectivement. Si vous vouliez tracer ces valeurs afin de créer un graphique semblable à celui qui suit, comment devriez-vous modifier le code ci-dessus ?

    Training and validation loss.

    Perte d’entraînement et de validation

    Compte tenu que l’écart entre la perte d’entraînement et la perte de validation s’accentue durant la troisième époque, que diriez-vous si quelqu’un vous suggérait d’augmenter le nombre d’époques à 10 ou 20 ?

  11. Pour finir, appelez la méthode evaluate du modèle pour déterminer avec quelle précision le modèle est capable de quantifier les sentiments exprimés dans le texte à partir des données de test dans x_test (critiques) et y_test (valeurs 0 et 1, ou « étiquettes », indiquant si les critiques sont positives ou négatives) :

    scores = model.evaluate(x_test, y_test, verbose=0)
    print("Accuracy: %.2f%%" % (scores[1] * 100))
    

    Quelle est la précision calculée de votre modèle ?

Vous avez probablement atteint une précision comprise entre 85 % et 90 %. C’est un résultat convenable si l’on tient compte du fait que vous avez créé le modèle à partir de zéro (au lieu d’avoir utilisé un réseau neuronal préentraîné) et que la durée d’entraînement était courte même sans un GPU. Il est possible d’obtenir des précisions de 95 % ou plus avec d’autres architectures de réseau neuronal, en particulier avec les réseaux de neurones récurrents qui utilisent des couches LSTM (mémoire à court et long terme ). Keras facilite la création de réseaux de ce type, mais la durée d’entraînement peut augmenter de manière exponentielle. Le modèle que vous avez créé offre un équilibre raisonnable entre la précision et la durée d’entraînement. Si vous souhaitez en savoir plus sur la création de réseaux de neurones récurrents avec Keras, consultez Understanding LSTM and its Quick Implementation in Keras for Sentiment Analysis.

Vérifiez vos connaissances

1.

Sur la base du premier graphique que vous avez créé (« Précision d’entraînement et de validation »), vous recommanderiez d’augmenter le nombre d’époques d’entraînement, de le diminuer ou de le laisser identique ?