BERT est un modèle de langage de pointe pour 104 langues. Tutoriel pour lancer BERT localement et sur Google Colab

image


BERT est un réseau neuronal de Google, qui a montré par une large marge des résultats de pointe sur un certain nombre de tâches. En utilisant BERT, vous pouvez créer des programmes d'IA pour traiter un langage naturel: répondre aux questions posées sous n'importe quelle forme, créer des robots de discussion, des traducteurs automatiques, analyser du texte, etc.


Google a publié des modèles BERT pré-formés, mais comme c'est généralement le cas avec le Machine Learning, ils souffrent d'un manque de documentation. Par conséquent, dans ce didacticiel, nous allons apprendre à exécuter le réseau neuronal BERT sur l'ordinateur local, ainsi que sur le GPU du serveur gratuit sur Google Colab.


Pourquoi est-ce nécessaire


Pour soumettre du texte à l'entrée d'un réseau de neurones, vous devez en quelque sorte le présenter sous forme de nombres. Il est plus facile de faire cette lettre par lettre, en appliquant une lettre à chaque entrée de réseau neuronal. Ensuite, chaque lettre sera codée avec un nombre de 0 à 32 (plus une sorte de marge pour les signes de ponctuation). C'est ce qu'on appelle le niveau des personnages.


Mais de bien meilleurs résultats sont obtenus si nous présentons des propositions non pas par une seule lettre, mais en soumettant à chaque entrée du réseau neuronal immédiatement un mot entier (ou au moins des syllabes). Ce sera déjà un niveau mot. L'option la plus simple consiste à compiler un dictionnaire avec tous les mots existants et à fournir au réseau le nombre de mots de ce dictionnaire. Par exemple, si le mot "chien" est dans ce dictionnaire à la place 1678, alors nous entrons le nombre 1678 pour l'entrée du réseau neuronal pour ce mot.


Mais ce n'est que dans le langage naturel avec le mot «chien» qu'une personne fait immédiatement apparaître beaucoup d'associations: «moelleux», «diabolique», «ami de l'homme». Est-il possible de coder d'une manière ou d'une autre cette caractéristique de notre pensée dans la présentation du réseau neuronal? Il s'avère que vous le pouvez. Pour ce faire, il suffit de réorganiser les numéros de mots pour que les mots dont la signification est proche se tiennent côte à côte. Que ce soit, par exemple, pour «chien» le nombre 1678, et pour le mot «pelucheux» le nombre 1680. Et pour le mot «théière» le nombre est 9000. Comme vous pouvez le voir, les nombres 1678 et 1680 sont beaucoup plus proches les uns des autres que le nombre 9000.


En pratique, chaque mot ne se voit pas attribuer un seul numéro, mais plusieurs - un vecteur, disons, de 32 chiffres. Et les distances sont mesurées comme les distances entre les points vers lesquels ces vecteurs pointent dans l'espace de la dimension correspondante (pour un vecteur de 32 chiffres, il s'agit d'un espace à 32 dimensions ou à 32 axes). Cela vous permet de comparer un mot à la fois avec plusieurs mots dont le sens est proche (selon l'axe à compter). De plus, des opérations arithmétiques peuvent être effectuées avec des vecteurs. Un exemple classique: si vous soustrayez le vecteur «homme» du vecteur désignant le mot «roi» et ajoutez le vecteur pour le mot «femme», vous obtenez un certain vecteur de résultat. Et il correspondra miraculeusement au mot "reine". Et en effet, "le roi est homme + femme = reine". La magie! Et ce n'est pas un exemple abstrait, mais ça arrive vraiment . Étant donné que les réseaux de neurones sont bien adaptés aux transformations mathématiques sur leurs entrées, cela fournit apparemment une efficacité si élevée de cette méthode.


Cette approche est appelée Embeddings. Tous les packages d'apprentissage automatique (TensorFlow, PyTorch) permettent à la première couche du réseau de neurones de mettre une couche spéciale de couche d'intégration, qui le fait automatiquement. C'est-à-dire qu'à l'entrée du réseau de neurones, nous donnons le numéro de mot habituel dans le dictionnaire, et Embedding Layer, auto-apprentissage, traduit chaque mot en un vecteur de la longueur spécifiée, disons 32 chiffres.


Mais ils ont rapidement réalisé qu'il était beaucoup plus rentable de pré-former une telle représentation vectorielle des mots sur un énorme corpus de textes, par exemple, sur l'ensemble de Wikipédia, et d'utiliser des vecteurs de mots prêts à l'emploi dans des réseaux de neurones spécifiques plutôt que de les former à chaque fois.


Il existe plusieurs façons de représenter les mots comme vecteurs; ils ont progressivement évolué: word2vec, GloVe, Elmo.


À l'été 2018, OpenAI a remarqué que si vous pré-entraînez un réseau de neurones sur l'architecture Transformer sur de gros volumes de texte, alors de manière inattendue et par une grande marge, il donne d'excellents résultats dans de nombreux types de tâches de traitement du langage naturel. En fait, un tel réseau de neurones à sa sortie crée des représentations vectorielles pour les mots, et même des phrases entières. Et en accrochant au-dessus d'un tel modèle de langage un petit bloc d'une paire de couches supplémentaires de neurones, vous pouvez entraîner ce réseau neuronal pour toutes les tâches.


BERT de Google est un réseau GPA avancé d'OpenAI (bidirectionnel au lieu d'unidirectionnel, etc.), également basé sur l'architecture Transformer. À l'heure actuelle, BERT est à la pointe de la technologie sur presque tous les benchmarks NLP populaires.


Comment l'ont-ils fait


L'idée derrière BERT est très simple: alimentons le réseau neuronal avec des phrases dans lesquelles nous remplaçons 15% des mots par [MASQUE] et entraînons le réseau neuronal à prédire ces mots masqués.


Par exemple, si nous envoyons la phrase «Je suis venu à [MASQUE] et j'ai acheté [MASQUE]» à l'entrée du réseau neuronal, il devrait afficher les mots «stocker» et «lait» à la sortie. Ceci est un exemple simplifié de la page officielle du BERT; sur des phrases plus longues, la gamme d'options possibles devient plus petite et la réponse du réseau neuronal est sans ambiguïté.


Et pour que le réseau neuronal apprenne à comprendre la relation entre différentes phrases, nous allons également l'entraîner à prédire si la deuxième phrase est une continuation logique de la première. Ou est-ce une phrase aléatoire qui n'a rien à voir avec la première.


Donc, pour deux phrases: "Je suis allé au magasin." et "Et acheté du lait là-bas.", le réseau neuronal devrait répondre que c'est logique. Et si la deuxième phrase est "Crucian sky Pluto", alors je dois répondre que cette proposition n'est pas liée à la première. Nous allons jouer avec ces deux modes BERT ci-dessous.


Ayant ainsi formé le réseau neuronal sur le corpus de textes de Wikipédia et la collection de livres BookCorpus pendant 4 jours à 16 TPU, nous avons obtenu le BERT.


Installation et configuration


Remarque : dans cette section, nous allons lancer et jouer avec BERT sur l'ordinateur local. Pour exécuter ce réseau neuronal sur un GPU local, vous aurez besoin d'un NVidia GTX 970 avec 4 Go de mémoire vidéo ou plus. Si vous souhaitez simplement exécuter BERT dans un navigateur (vous n'avez même pas besoin d'un GPU sur votre ordinateur pour cela), accédez à la section Google Colab.


Installez d'abord TensorFlow si vous ne l'avez pas déjà, en suivant les instructions de https://www.tensorflow.org/install . Pour prendre en charge le GPU, vous devez d'abord installer CUDA Toolkit 9.0, puis cuDNN SDK 7.2, puis TensorFlow lui-même avec prise en charge GPU:


pip install tensorflow-gpu 

Fondamentalement, cela suffit pour exécuter BERT. Mais il n'y a aucune instruction en tant que telle, vous pouvez la composer vous-même en triant les sources dans le fichier run_classifier.py (la situation habituelle dans Machine Learning est lorsque vous devez aller dans les sources au lieu de la documentation). Mais nous allons le faire plus facilement et utiliser le shell Keras BERT (il peut également être utile pour affiner le réseau plus tard, car il fournit une interface Keras pratique).


Pour ce faire, installez Keras lui-même:


 pip install keras 

Et après Keras BERT:


 pip install keras-bert 

Nous aurons également besoin du fichier tokenization.py du github original BERT. Soit cliquez sur le bouton Raw et enregistrez-le dans le dossier avec le futur script, ou téléchargez le référentiel entier et récupérez le fichier à partir de là, ou prenez une copie du référentiel avec ce code https://github.com/blade1780/bert .


Il est maintenant temps de télécharger le réseau neuronal pré-formé. Il existe plusieurs options pour BERT, qui sont toutes répertoriées sur la page officielle github.com/google-research/bert . Nous prendrons le multilingue universel "BERT-Base, Multilingual Cased", pour 104 langues. Téléchargez le fichier multi_cased_L-12_H-768_A-12.zip (632 Mo) et décompressez-le dans le dossier avec le futur script.


Tout est prêt, créez le fichier BERT.py, puis il y aura un peu de code.


Importer les bibliothèques requises et définir les chemins


 # coding: utf-8 import sys import codecs import numpy as np from keras_bert import load_trained_model_from_checkpoint import tokenization # ,     BERT folder = 'multi_cased_L-12_H-768_A-12' config_path = folder+'/bert_config.json' checkpoint_path = folder+'/bert_model.ckpt' vocab_path = folder+'/vocab.txt' 

Puisque nous devrons traduire des lignes de texte ordinaires dans un format spécial de jetons, nous créerons un objet spécial pour cela. Faites attention à do_lower_case = False, car nous utilisons le modèle Cased BERT, qui est sensible à la casse.


 tokenizer = tokenization.FullTokenizer(vocab_file=vocab_path, do_lower_case=False) 

Modèle de chargement


 model = load_trained_model_from_checkpoint(config_path, checkpoint_path, training=True) model.summary() 

BERT peut fonctionner en deux modes: deviner les mots manqués dans la phrase, ou deviner si la deuxième phrase est logique après la première. Nous ferons les deux options.


Pour le premier mode, vous devez soumettre une phrase au format:


 [CLS]    [MASK]   [MASK]. [SEP] 

Le réseau neuronal devrait renvoyer une phrase complète avec les mots remplis à la place des masques: "Je suis venu au magasin et j'ai acheté du lait."


Pour le deuxième mode, les deux phrases séparées par un séparateur doivent être introduites à l'entrée du réseau neuronal:


 [CLS]    . [SEP]   . [SEP] 

Le réseau neuronal doit répondre si la deuxième phrase est une continuation logique de la première. Ou est-ce une phrase aléatoire qui n'a rien à voir avec la première.


Pour que BERT fonctionne, vous devez préparer trois vecteurs, chacun avec une longueur de 512 nombres: token_input, seg_input et mask_input.


Token_input stockera notre code source traduit en jetons à l'aide de tokenizer. La phrase sous forme d'index dans le dictionnaire sera au début de ce vecteur et le reste sera rempli de zéros.


Dans mask_input, nous devons mettre 1 pour toutes les positions où se trouve le masque [MASK] et remplir le reste avec des zéros.


Dans seg_input, nous devons désigner la première phrase (y compris le séparateur CLS et SEP de départ) comme 0, la deuxième phrase (y compris le SEP final) comme 1, et remplir le reste à la fin du vecteur avec des zéros.


BERT n'utilise pas un dictionnaire de mots entiers, mais plutôt des syllabes les plus courantes. Bien qu'il ait aussi des mots entiers. Vous pouvez ouvrir le fichier vocab.txt dans le réseau neuronal téléchargé et voir quels mots le réseau neuronal utilise à son entrée. Il y a des mots entiers comme la France. Mais la plupart des mots russes doivent être décomposés en syllabes. Ainsi, le mot «est venu» doit être décomposé en «avec» et «## est allé». Pour vous aider à convertir des lignes de texte régulières au format requis par BERT, nous utilisons le module tokenization.py.


Mode 1: Prédiction des mots fermés par jeton [MASQUE] dans une phrase


La phrase d'entrée qui est envoyée à l'entrée du réseau neuronal


 sentence = '   [MASK]   [MASK].' print(sentence) 

Convertissez-le en jetons. Le problème est que le tokenizer ne peut pas traiter les marques de service comme [CLS] et [MASK], bien que vocab.txt les ait dans le dictionnaire. Par conséquent, nous devrons rompre manuellement notre ligne avec les marqueurs [MASK] et en extraire des morceaux de texte brut afin de le convertir en jetons BERT en utilisant le tokenizer. Ajoutez également [CLS] au début et [SEP] à la fin de la phrase.


 sentence = sentence.replace(' [MASK] ','[MASK]'); sentence = sentence.replace('[MASK] ','[MASK]'); sentence = sentence.replace(' [MASK]','[MASK]') #    sentence = sentence.split('[MASK]') #     tokens = ['[CLS]'] #      [CLS] #        tokenizer.tokenize(),    [MASK] for i in range(len(sentence)): if i == 0: tokens = tokens + tokenizer.tokenize(sentence[i]) else: tokens = tokens + ['[MASK]'] + tokenizer.tokenize(sentence[i]) tokens = tokens + ['[SEP]'] #      [SEP] 

Les jetons ont maintenant des jetons qui sont garantis pour être convertis en index dans le dictionnaire. Faisons-le:


 token_input = tokenizer.convert_tokens_to_ids(tokens) 

Maintenant, dans token_input, il y a une série de nombres (numéros de mots dans le dictionnaire vocab.txt) qui doivent être introduits dans l'entrée du réseau neuronal. Il ne reste plus qu'à étendre ce vecteur à une longueur de 512 éléments. La construction Python [0] * length crée un tableau de longueur de longueur, rempli de zéros. Ajoutez-le simplement à nos jetons, qui en python combinent deux tableaux en un.


 token_input = token_input + [0] * (512 - len(token_input)) 

Créez maintenant un masque de 512 de longueur, en mettant 1 partout, où le nombre 103 apparaît dans les jetons (qui correspond au marqueur [MASK] dans le dictionnaire vocab.txt), et remplissez le reste avec 0:


 mask_input = [0]*512 for i in range(len(mask_input)): if token_input[i] == 103: mask_input[i] = 1 

Pour le premier mode de fonctionnement BERT, seg_input doit être complètement rempli de zéros:


 seg_input = [0]*512 

La dernière étape, vous devez convertir les tableaux python en tableaux numpy avec une forme (1 512), pour lesquels nous les avons mis dans un sous-tableau []:


 token_input = np.asarray([token_input]) mask_input = np.asarray([mask_input]) seg_input = np.asarray([seg_input]) 

OK, c'est fait. Maintenant, exécutez la prédiction du réseau neuronal!


 predicts = model.predict([token_input, seg_input, mask_input])[0] predicts = np.argmax(predicts, axis=-1) predicts = predicts[0][:len(tokens)] #   ,    ,        

Formatez maintenant le résultat des jetons en une chaîne séparée par des espaces


 out = [] #   out     [MASK],    1  mask_input for i in range(len(mask_input[0])): if mask_input[0][i] == 1: # [0][i], ..   batch   (1,512),       out.append(predicts[i]) out = tokenizer.convert_ids_to_tokens(out) #     out = ' '.join(out) #       out = tokenization.printable_text(out) #    out = out.replace(' ##','') #   : " ##" -> "" 

Et sortez le résultat:


 print('Result:', out) 

Dans notre exemple, pour la phrase «Je suis venu à [MASK] et j'ai acheté [MASK]». le réseau de neurones a produit le résultat «maison» et «ça»: «Je suis venu à la maison et je l'ai acheté». Enfin, pas si mal pour la première fois. L'achat d'une maison est certainement mieux que le lait).


Autres exemples (je ne donne pas ceux qui ont échoué, il y en a beaucoup plus que ceux qui ont réussi. Dans la plupart des cas, le réseau donne une réponse vide):

La Terre est le troisième [MASQUE] du Soleil
Résultat: Star


meilleur sandwich [MASQUE] au beurre
Résultat: rencontre


après le [MASQUE] le déjeuner est censé dormir
Résultat: de ceci


s'éloigner de [MASK]
Résultat: ## oh - est-ce une sorte de malédiction? )


[MASQUE] de la porte
Résultat: voir


Avec [MASQUE] le marteau et les clous peuvent faire une armoire
Résultat: aide


Et si demain ne l'est pas? Aujourd'hui, par exemple, ce n'est pas [MASQUE]!
Résultat: sera


Comment pouvez-vous vous lasser d'ignorer [MASQUE]?
Résultat: elle


Il y a la logique de tous les jours, il y a la logique féminine, mais on ne sait rien du mâle
Résultat: philosophie


Chez les femmes, à l'âge de trente ans, une image du prince se forme, qui convient à tout [MASQUE].
Résultat: homme


Par un vote majoritaire, Blanche-Neige et les sept Nains ont voté pour [MASQUE], avec une voix contre.
Résultat: village - la première lettre est correcte


Évaluez votre ennui sur une échelle de 10 points: [MASQUE] points
Résultat: 10


Votre [MASQUE], [MASQUE] et [MASQUE]!
Résultat: aime-moi, je - non, BERT, je ne le pensais pas du tout


Vous pouvez saisir des phrases en anglais (et n'importe laquelle dans 104 langues, dont la liste est ici )


[MASQUE] doit continuer!
Résultat: I


Mode 2: vérification de la cohérence de deux phrases


Nous définissons deux phrases consécutives qui seront alimentées à l'entrée du réseau neuronal


 sentence_1 = '   .' sentence_2 = '  .' print(sentence_1, '->', sentence_2) 

Nous allons créer des jetons au format [CLS] phrase_1 [SEP] phrase_2 [SEP], en convertissant du texte brut en jetons à l'aide de tokenizer:


 tokens_sen_1 = tokenizer.tokenize(sentence_1) tokens_sen_2 = tokenizer.tokenize(sentence_2) tokens = ['[CLS]'] + tokens_sen_1 + ['[SEP]'] + tokens_sen_2 + ['[SEP]'] 

Nous convertissons les jetons de chaîne en indices numériques (nombres de mots dans le dictionnaire vocab.txt) et étendons le vecteur à 512:


 token_input = tokenizer.convert_tokens_to_ids(tokens) token_input = token_input + [0] * (512 - len(token_input)) 

Le masque de mot dans ce cas est complètement rempli de zéros


 mask_input = [0] * 512 

Mais le masque de proposition doit être rempli sous la deuxième phrase (y compris le SEP final) avec des unités, et tout le reste avec des zéros:


 seg_input = [0]*512 len_1 = len(tokens_sen_1) + 2 #   , +2 -   CLS   SEP for i in range(len(tokens_sen_2)+1): # +1, ..   SEP seg_input[len_1 + i] = 1 #   ,   SEP,  #   numpy   (1,) -> (1,512) token_input = np.asarray([token_input]) mask_input = np.asarray([mask_input]) seg_input = np.asarray([seg_input]) 

Nous passons les phrases à travers le réseau de neurones (cette fois le résultat est en [1], et non en [0], comme c'était le cas ci-dessus)


 predicts = model.predict([token_input, seg_input, mask_input])[1] 

Et nous dérivons la probabilité que la deuxième phrase soit un ensemble de mots normal et non aléatoire


 print('Sentence is okey:', int(round(predicts[0][0]*100)), '%') 

En deux phrases:


Je suis venu au magasin. -> Et acheté du lait.


Réponse du réseau neuronal:


La peine est bonne: 99%


Et si la deuxième phrase est "Pluton du ciel de Crucian", alors la réponse sera:


La peine est bonne: 4%


Google colab


Google fournit un GPU de serveur Tesla K80 gratuit avec 12 Go de mémoire vidéo (les TPU sont maintenant disponibles, mais leur configuration est un peu plus compliquée). Tout le code pour Colab doit être conçu comme un cahier jupyter. Pour lancer BERT dans un navigateur, il suffit d'ouvrir le lien


http://colab.research.google.com/github/blade1780/bert/blob/master/BERT.ipynb


Dans le menu Runtime , sélectionnez Exécuter tout , de sorte que pour la première fois toutes les cellules démarrent, les téléchargements de modèle et les bibliothèques nécessaires sont connectés. Acceptez de réinitialiser tout le Runtime si nécessaire.


En cas de problème ...

Assurez-vous que GPU et Python 3 sont sélectionnés dans le menu Runtime -> Change runtime type


Si le bouton de connexion n'est pas actif, cliquez dessus pour devenir connecté.


Maintenant, changez la phrase des lignes d'entrée phrase , phrase_1 et phrase_2 , et cliquez sur l'icône de lecture sur la gauche pour démarrer uniquement la cellule actuelle. L'exécution de l'intégralité du portable n'est plus nécessaire.


Vous pouvez exécuter BERT sur Google Colab même à partir d'un smartphone, mais s'il ne s'ouvre pas, vous devrez peut-être activer la case à cocher Version complète dans les paramètres de votre navigateur.


Et ensuite?


Pour former le BERT à une tâche spécifique, vous devez ajouter une ou deux couches d'un réseau Feed Forward simple au-dessus de celui-ci et ne le former que sans toucher au réseau BERT principal. Cela peut être fait sur TensorFlow nu ou via le shell Keras BERT. Cette formation supplémentaire pour un domaine spécifique se produit très rapidement et est complètement similaire au réglage fin dans les réseaux à convolution. Ainsi, pour la tâche SQuAD, vous pouvez former un réseau neuronal sur un TPU en seulement 30 minutes (contre 4 jours sur 16 TPU pour la formation du BERT lui-même).


Pour ce faire, vous devrez étudier la façon dont les dernières couches sont représentées dans BERT, ainsi que disposer d'un ensemble de données approprié. Sur la page officielle du BERT https://github.com/google-research/bert, vous trouverez plusieurs exemples de différentes tâches, ainsi que des instructions sur la façon de commencer un recyclage sur les TPU cloud. Et tout le reste devra chercher dans la source dans les fichiers run_classifier.py et extract_features.py .


PS


Le code et le bloc-notes jupyter pour Google Colab présentés ici sont hébergés dans le référentiel .


Il ne faut pas s'attendre à des miracles. Ne vous attendez pas à ce que BERT parle comme une personne. Le statut de l'état de l'art ne signifie pas du tout que les progrès de la PNL ont atteint un niveau acceptable. Cela signifie simplement que BERT est meilleur que les modèles précédents, qui étaient encore pires. Une IA conversationnelle solide est encore très loin. De plus, BERT est principalement un modèle de langage, pas un bot de chat prêt à l'emploi, il ne montre donc de bons résultats qu'après un recyclage pour une tâche spécifique.

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


All Articles