ChatBot: Le guide complet pour créer un chatbot avec Deep Learning From Scratch | par Matthew Evan Taruno

ChatBot: Le guide complet pour créer un chatbot avec Deep Learning From Scratch | par Matthew Evan Taruno

J’ai également essayé des techniques d’intégration au niveau des mots comme gloVe, mais pour cette étape de génération de données, nous voulons quelque chose au niveau du document car nous essayons de comparer entre les énoncés, pas entre les mots d’un énoncé.

Classification de l’intention

Mon cahier de classification d’intention est ici.

Avec nos données étiquetées, nous pouvons enfin passer à la partie amusante – en fait, classer les intentions ! Je vous recommande de ne pas passer trop de temps à essayer d’obtenir les données parfaites au préalable. Essayez d’arriver à cette étape à un rythme raisonnablement rapide afin que vous puissiez d’abord obtenir un produit minimum viable. L’idée est d’obtenir d’abord un résultat à utiliser comme référence afin que nous puissions ensuite améliorer itérativement les données.

Il existe de nombreuses façons de faire une classification d’intention, Rasa NLU par exemple vous permet d’utiliser de nombreux modèles différents tels que des machines à vecteurs de support (SVM), mais ici, je vais montrer comment le faire avec un réseau de neurones avec une architecture LSTM bidirectionnelle.

Nous essayons de mapper un énoncé utilisateur (qui n’est qu’une séquence de jetons) à l’une des N intentions que nous spécifions. Les données avec lesquelles nous commençons doivent simplement avoir un énoncé et une étiquette de vérité terrain d’intention en tant que colonnes. L’ordre de ce processus est le suivant (pour la mise en œuvre, consultez le cahier de classification des intentions disponible sur mon Github) :

  1. Train Test Split (devrait toujours allez-y en premier, mon mentor a percé ce point dans ma tête)
  2. Tokenizer Keras
  3. Libellé Encoder la variable cible (intents)
  4. Initialiser la matrice d’intégration (j’ai utilisé les intégrations gloVe car elles avaient une variante spéciale formée sur les données Twitter)
  5. Initialiser l’architecture du modèle
  6. Initialiser les rappels de modèle (techniques pour traiter le surapprentissage)
  7. Ajustez le modèle et enregistrez-le
  8. Chargez le modèle et enregistrez la sortie (je recommande un dictionnaire)

Certaines choses à garder à l’esprit pour le couche d’incorporation (étape 4):

  • Ces incorporations pré-entraînées sont essentiellement la façon dont vous convertissez le texte qui entre dans le modèle en une représentation numérique. Lorsque vous comparez les similitudes de cosinus de votre représentation numérique de différents documents, ils doivent avoir des distances significatives entre eux (la similitude de cosinus entre « roi » et « homme » doit être plus proche que « roi » et « femmes » par exemple).
  • Il est vraiment important que vous choisissiez les bons intégrations pré-entraînées qui conviennent au domaine de votre . Si vous avez un conversationnel basé sur Twitter, vous ne voulez probablement pas que les intégrations soient formées sur Wikipedia.
  • Je vous recommande également de voir si tout le vocabulaire que vous souhaitez couvrir se trouve dans votre fichier d’intégration pré-entraîné. J’ai vérifié si mes intégrations Twitter de gloVe couvraient des mots spécifiques à Apple comme « macbook pro » par exemple, et heureusement, c’est le cas.
Vérifier si le mot « macbook » est dans mes incrustations de gant avec grep (il s’avère que c’est le cas !). Image de l’auteur.

Certaines choses à garder à l’esprit pour le architecture de modèle (étape 5) :

  • Assurez-vous que la couche de sortie est softmax (si vous souhaitez effectuer une classification multi-étiquettes, utilisez sigmoïde).
  • Assurez-vous que la couche de sortie a une dimensionnalité identique au nombre d’intentions que vous souhaitez classer, sinon vous rencontrerez des problèmes de forme.
  • Si vous n’encodez pas d’étiquette, votre utilisation de model.predict() peut être inexact car ce dictionnaire final dans lequel vous affichez où les clés sont les intentions et les valeurs sont les probabilités de l’énoncé étant que l’intention ne serait pas mappée correctement.
  • Lorsque vous déployez votre bot, vous ne devez pas réexécuter le modèle. Au lieu de cela, j’ai écrit un script qui commence par lire le fichier de modèle enregistré et fait les prédictions à partir de là.

Résultats

Les résultats sont prometteurs. La perte converge vers un niveau bas et la précision de mon modèle sur des données invisibles est de 87% !

Image de l’auteur.
Image de l’auteur.

Si vous visualisez le résultat de la classification d’intention, voici à quoi cela ressemble pour l’énoncé « ma batterie sur mon iphone a cessé de fonctionner ! » :

Image de l’auteur.

Prévenir le surapprentissage

Afin d’éviter le surajustement de mon modèle, il existe également d’autres paramètres que je définis sous la forme de rappels Keras :

  • Planification du taux d’apprentissage – Ralentissement du taux d’apprentissage après avoir dépassé un certain nombre d’époque
  • Arrêt précoce — Arrêt prématuré de l’entraînement une fois que la perte de validation (ou tout autre paramètre de votre choix) atteint un certain seuil

Et enfin, après avoir exécuté le modèle, je l’ai enregistré dans un fichier h5 afin de pouvoir l’initialiser plus tard sans réentraîner mon modèle à l’aide de Model Checkpoint. Le code est ci-dessous :

# Initializing checkpoint settings to view progress and save model
filename = 'models/intent_classification_b.h5'

# Learning rate scheduling
# This function keeps the initial learning rate for the first ten epochs
# and decreases it exponentially after that.
def scheduler(epoch, lr):
if epoch return lr
else:
return lr * tf.math.exp(-0.1)

lr_sched_checkpoint = tf.keras.callbacks.LearningRateScheduler(scheduler)

# Early stopping
early_stopping = tf.keras.callbacks.EarlyStopping(
monitor='val_loss', min_delta=0, patience=3, verbose=0, mode='auto',
baseline=None, restore_best_weights=True
)

# This saves the best model
checkpoint = ModelCheckpoint(filename, monitor='val_loss', verbose=1,
save_best_only=True, mode='min')

# The model you get at the end of it is after 100 epochs, but that might not have been
# the weights most associated with validation accuracy

# Only save the weights when you model has the lowest val loss. Early stopping

# Fitting model with all the callbacks above
hist = model.fit(padded_X_train, y_train, epochs = 20, batch_size = 32,
validation_data = (padded_X_val, y_val),
callbacks = [checkpoint, lr_sched_checkpoint, early_stopping])

Extraction d’entité

Voici mon cahier complet sur l’extraction d’entités.

Pour EVE bot, l’objectif est d’extraire des mots-clés spécifiques à Apple qui correspondent à la catégorie du matériel ou de l’application. Comme pour la classification des intentions, il existe de nombreuses façons de le faire – chacune a ses avantages en fonction du contexte. Rasa NLU utilise un modèle de champ aléatoire conditionnel (CRF), mais pour cela, j’utiliserai l’implémentation de spaCy de la descente de gradient stochastique (SGD).

La première étape consiste à créer un dictionnaire qui stocke les catégories d’entités que vous pensez pertinentes pour votre . Ensuite, vous voyez si spaCy les a par défaut. Plus probablement qu’improbable, ils ne le feront pas. Donc, dans ce cas, vous devrez former votre propre modèle personnalisé de reconnaissance d’entité nommée (NER) spaCy. Pour les produits Apple, il est logique que les entités soient le matériel et l’application que le client utilise. Vous souhaitez répondre aux clients qui posent des questions sur un iPhone différemment des clients qui posent des questions sur leur Macbook Pro.

{'hardware': ['macbook pro',
'iphone',
'iphones',
'mac',
'ipad',
'watch',
'TV',
'airpods'],
'apps': ['app store',
'garageband',
'books',
'calendar',
'podcasts',
'notes',
'icloud',
'music',
'messages',
'facetime',
'catalina',
'maverick']}

Une fois que vous avez stocké les mots-clés d’entité dans le dictionnaire, vous devriez également avoir un ensemble de données qui utilise essentiellement ces mots-clés dans une phrase. Heureusement pour moi, j’ai déjà un grand ensemble de données Twitter de Kaggle que j’utilise. Si vous alimentez ces exemples et spécifiez quels mots sont les mots-clés de l’entité, vous avez essentiellement un ensemble de données étiqueté, et spaCy peut apprendre le contexte à partir duquel ces mots sont utilisés dans une phrase.

Afin d’étiqueter votre ensemble de données, vous devez convertir vos données au format spaCy. Ceci est un exemple de ce à quoi mes données d’entraînement devraient ressembler pour pouvoir être introduites dans spaCy pour entraîner votre modèle NER personnalisé à l’aide de la descente de gradient stochastique (SGD). Nous fabriquons un offset et utilisons le PhraseMatcher de spaCy, le tout dans le but de faciliter sa conversion dans ce format.

TRAIN_DATA = [
('what is the price of polo?', {'entities': [(21, 25, 'PrdName')]}),
('what is the price of ball?', {'entities': [(21, 25, 'PrdName')]}),
('what is the price of jegging?', {'entities': [(21, 28, 'PrdName')]}),
('what is the price of t-shirt?', {'entities': [(21, 28, 'PrdName')]})
]

Offset

# Utility function - converts the output of the PhraseMatcher to something usable in training

def offsetter(lbl, doc, matchitem):
''' Converts word position to string position '''
one = len(str(doc[0:matchitem[1]]))
subdoc = doc[matchitem[1]:matchitem[2]]
two = one + len(str(subdoc))

# This function was misaligned by a factor of one character, not sure why, but this is my solution
if one != 0:
one += 1
two += 1
return (one, two, lbl)

# Example
# offsetter(‘HARDWARE’, nlp(‘hmm macbooks are great’),(2271554079456360229, 1, 2)) -> (4, 12, ‘HARDWARE’)

J’ai utilisé cette fonction dans ma fonction plus générale pour « spaCify » une ligne, une fonction qui prend en entrée les données brutes de la ligne et les convertit en une version balisée que spaCy peut lire. J’ai dû modifier le positionnement de l’index pour le décaler de un indice au début, je ne sais pas pourquoi mais cela a bien fonctionné.

Ensuite, j’ai aussi fait une fonction train_spacy pour l’alimenter dans spaCy, qui utilise le nlp.update méthode pour entraîner mon modèle NER. Il l’entraîne pour le nombre arbitraire de 20 époques, où à chaque époque les exemples d’entraînement sont préalablement mélangés. Essayez de ne pas choisir un nombre d’époques trop élevé, sinon le modèle pourrait commencer à « oublier » les modèles qu’il a déjà appris aux étapes précédentes. Étant donné que vous minimisez la perte avec la descente de gradient stochastique, vous pouvez visualiser votre perte au fil des époques.

Perte du modèle d’entité matérielle à chaque itération. Image de l’auteur.
Perte du modèle d’entité d’application à chaque itération. Image de l’auteur.

Je n’ai pas trouvé de moyen de combiner tous les différents modèles que j’ai entraînés dans un seul objet spaCy pipe, j’ai donc eu deux modèles distincts sérialisés dans deux fichiers pickle. Encore une fois, voici les visualisations d’affichage que j’ai présentées ci-dessus – elles ont réussi à étiqueter macbook pro et garageband dans ses compartiments d’entité corrects.

L’entité matérielle « macbook pro » est taguée. D’autres matériels peuvent inclure l’iPhone et l’iPad. Image de l’auteur.
L’entité de l’application capturée « garageband » est taguée. D’autres entités d’application peuvent inclure Apple Music ou FaceTime. Image de l’auteur.

À partir des fichiers pickle que vous enregistrez, vous pouvez stocker toutes les entités extraites sous forme de liste en parcourant l’attribut ents de l’objet doc, illustré ici :

def extract_app(user_input, visualize = False):
# Loading it in
app_nlp = pickle.load(open("models/app_big_nlp.pkl", "rb"))
doc = app_nlp(user_input)

extracted_entities = []

# These are the objects you can take out
for ent in doc.ents:
extracted_entities.append((ent.text, ent.start_char, ent.end_char, ent.label_))

return extracted_entities

Par itération, je veux vraiment dire améliorer mon modèle. J’en ai fait sa propre scène parce que cela prend en fait plus de temps que prévu. J’améliore mon modèle en :

  • Choisir de meilleures intentions et entités
  • Améliorer la qualité de mes données
  • Améliorer votre architecture de modèle

Vous n’avez pas seulement à générer les données comme je l’ai fait à l’étape 2. Considérez cela comme l’une de vos boîtes à outils pour pouvoir créer votre ensemble de données parfait.

L’objectif des données que vous introduisez dans votre classificateur d’intention est que chaque intention soit large (ce qui signifie que les exemples d’intention épuisent suffisamment l’espace d’état et les mondes de ce que l’utilisateur pourrait dire) et uniques les unes des autres.

De cette façon, le réseau de neurones est capable de faire de meilleures prédictions sur les énoncés des utilisateurs qu’il n’a jamais vu auparavant. Voici comment j’ai essayé d’atteindre cet objectif.

Exemples de manuels

En plus d’utiliser la similarité Doc2Vec pour générer des exemples de formation, j’ai également ajouté manuellement des exemples. J’ai commencé avec plusieurs exemples auxquels je peux penser, puis j’ai parcouru ces mêmes exemples jusqu’à ce qu’il atteigne le seuil de 1000. Cela fait toute la différence dans la qualité de votre modèle. Si vous savez qu’un client est très susceptible d’écrire quelque chose, vous devez simplement l’ajouter aux exemples de formation.

Optimisation de l’exploration des mots-clés

Comment choisissons-nous les intentions et les exemples à inclure ? Pour vous aider à prendre une décision plus éclairée à cet égard, j’ai créé un outil d’exploration de mots clés qui vous indique combien de Tweets contiennent ce mot clé et vous donne un aperçu de ce que sont réellement ces Tweets. Ceci est utile pour explorer ce que vos clients vous demandent souvent et aussi comment y répondre, car nous avons également des données sortantes que nous pouvons consulter.

Filtrage des Tweets par mot-clé pour explorer les sujets dans mes données. Image de l’auteur.

J’ai également trouvé un moyen d’estimer la vraie distribution des intentions ou des sujets dans mes données Twitter et de la tracer. C’est assez simple. Vous commencez par vos intentions, puis vous pensez aux mots-clés qui représentent cette intention.

{"update":['update'], 
"battery": ['battery','power'],
"forgot_password": ['password', 'account', 'login'],
"repair": ['repair', 'fix', 'broken'],
"payment": ['payment'}
Image de l’auteur.

Je vous encourage également à examiner 2, 3 ou même 4 combinaisons de mots-clés pour voir si vos données contiennent naturellement des Tweets à plusieurs intentions à la fois. Dans l’exemple suivant, vous pouvez voir que près de 500 Tweets contiennent les mots-clés de mise à jour, de batterie et de réparation à la fois. Il est clair que dans ces Tweets, les clients cherchent à résoudre leur problème de batterie potentiellement causé par leur récente mise à jour.

Image de l’auteur.

N’oubliez pas que tout cela vise à rendre vos compartiments d’intention distincts et étendus.

Quant à ce côté développement, c’est là que vous implémentez la logique métier qui, selon vous, convient le mieux à votre contexte. J’aime utiliser des affirmations telles que « Cela a-t-il résolu votre problème » pour réaffirmer une intention.

À des fins de démonstration, j’ai utilisé Streamlit. Ce n’est pas l’endroit idéal pour le déploiement car il est difficile d’afficher l’historique des conversations de manière dynamique, mais il fait le travail. En réalité, vous déploieriez sur une plate-forme de messagerie. Par exemple, vous pouvez utiliser Flask pour déployer votre sur Facebook Messenger et d’autres plateformes. Vous pouvez également utiliser api.slack.com pour l’intégration et y créer rapidement votre application Slack.

Les interfaces conversationnelles sont un tout autre sujet qui a un potentiel énorme à mesure que nous avançons dans le futur. Et il existe de nombreux guides pour assommer votre conception UX de conception pour ces interfaces conversationnelles.

Dans cet article, je vous montre essentiellement comment générer des données, classer les intentions et extraire des entités. Ces 3 étapes sont absolument indispensables pour faire un . Cependant, il y a encore plus à faire pour rendre un entièrement fonctionnel et naturel. Cela réside principalement dans la façon dont vous mappez l’état actuel du dialogue aux actions que le est censé entreprendre – ou en bref, gestion des dialogues.

Le bot doit apprendre exactement quand exécuter des actions comme écouter et quand demander des informations essentielles si elles sont nécessaires pour répondre à une intention particulière.

En prenant un bot météo comme exemple, lorsque l’utilisateur pose des questions sur la météo, le bot a besoin de l’emplacement pour pouvoir répondre à cette question afin qu’il sache comment faire le bon appel d’API pour récupérer les informations météo. Donc, pour cette intention spécifique de récupération météorologique, il est important de sauvegarder l’emplacement dans un emplacement stocké en mémoire. Si l’utilisateur ne mentionne pas l’emplacement, le bot doit demander à l’utilisateur où se trouve l’utilisateur. Il est irréaliste et inefficace de demander au bot de faire des appels API pour la météo dans chaque ville du monde.

Je vous recommande de regarder cette vidéo et la documentation Rasa pour voir comment les modules Rasa NLU (pour Natural Language Understanding) et Rasa Core (pour Dialogue Management) sont utilisés pour créer un intelligent. Je parle beaucoup de Rasa car en dehors des techniques de génération de données, j’ai appris ma logique de à partir de leurs vidéos de masterclass et l’ai compris pour l’implémenter moi-même à l’aide de packages Python. Leur cadre offre de nombreuses possibilités de personnalisation pour utiliser différentes politiques et techniques à différentes étapes du (par exemple, utiliser ou non des LSTM ou des SVM pour la classification des intentions, jusqu’aux détails les plus granulaires sur la façon de se replier lorsque le bot n’est pas confiant. dans sa classification d’intention).

Aussi, j’aimerais utiliser un méta modèle qui contrôle mieux la gestion des dialogues de mon . Une façon intéressante est d’utiliser un réseau de neurones de transformateur pour cela (référez-vous à l’article fait par Rasa à ce sujet, ils l’ont appelé la politique de dialogue d’intégration de transformateur). Cela vous aide essentiellement à avoir des conversations plus naturelles.

Enfin, la mise à l’échelle de mon serait également importante. Cela signifie simplement étendre le domaine des intentions et des entités auxquelles mon serait en mesure de répondre afin qu’il couvre les domaines les plus importants et les cas limites. Il est utile de se rappeler que ce framework que j’ai créé est transférable à n’importe quel autre , donc j’aimerais également prendre en charge d’autres langues à l’avenir !

ChatBot: Le guide complet pour créer un chatbot avec Deep Learning From Scratch | par Matthew Evan Taruno

#guide #complet #pour #créer # #avec #Deep #Learning #Scratch #par #Matthew #Evan #Taruno