Ce travail tente de reproduire les résultats de A Neural Conversational Model (alias le chatbot de Google). Il utilise un RNN (modèle seq2seq) pour les prédictions de phrases. Cela se fait en utilisant Python et TensorFlow.
La partie chargement du corpus du programme est inspirée du Torch neuralconvo de macournoyer.
Pour l'instant, DeepQA prend en charge le corpus de dialogue suivant :
--corpus opensubs
.--corpus scotus
. Voir les instructions d'installation.--corpus ubuntu
. Voir les instructions d'installation.Pour accélérer la formation, il est également possible d'utiliser des intégrations de mots pré-entraînées (grâce à Eschnou). Plus d'informations ici.
Le programme nécessite les dépendances suivantes (faciles à installer avec pip : pip3 install -r requirements.txt
) :
Vous devrez peut-être également télécharger des données supplémentaires pour faire fonctionner nltk.
python3 -m nltk.downloader punkt
L'ensemble de données Cornell est déjà inclus. Pour les autres ensembles de données, consultez les fichiers Lisez-moi dans leurs dossiers respectifs (à l'intérieur data/
).
L'interface Web nécessite quelques packages supplémentaires :
Une installation Docker est également disponible. Des instructions plus détaillées ici.
Pour entraîner le modèle, exécutez simplement main.py
. Une fois formé, vous pouvez tester les résultats avec main.py --test
(résultats générés dans 'save/model/samples_predictions.txt') ou main.py --test interactive
(plus amusant).
Voici quelques drapeaux qui pourraient être utiles. Pour plus d'aide et d'options, utilisez python main.py -h
:
--modelTag
: permet de donner un nom au modèle courant pour les différencier lors des tests/entraînements.--keepAll
: utilisez ce flag lors de l'entraînement si lors des tests, vous souhaitez voir les prédictions à différentes étapes (il peut être intéressant de voir le programme changer de nom et d'âge au fur et à mesure de l'entraînement). Attention : Cela peut rapidement prendre beaucoup d'espace de stockage si vous n'augmentez pas l'option --saveEvery
.--filterVocab 20
ou --vocabularySize 30000
: Limite la taille du vocabulaire et optimise les performances et l'utilisation de la mémoire. Remplacez les mots utilisés moins de 20 fois par le jeton
et définissez une taille maximale de vocabulaire.--verbose
: lors du test, imprimera les phrases au fur et à mesure qu'elles sont calculées.--playDataset
: affiche quelques échantillons de dialogue de l'ensemble de données (peut être utilisé conjointement avec --createDataset
si c'est la seule action que vous souhaitez effectuer). Pour visualiser le graphique de calcul et le coût avec TensorBoard, exécutez simplement tensorboard --logdir save/
.
Par défaut, l'architecture réseau est un encodeur/décodeur standard avec deux couches LSTM (taille cachée de 256) et une taille d'intégration pour le vocabulaire de 32. Le réseau est formé à l'aide d'ADAM. La longueur maximale des phrases est fixée à 10 mots, mais peut être augmentée.
Une fois formé, il est possible de discuter avec lui en utilisant une interface plus conviviale. Le serveur examinera le modèle copié dans save/model-server/model.ckpt
. La première fois que vous souhaiterez l'utiliser, vous devrez le configurer avec :
export CHATBOT_SECRET_KEY= " my-secret-key "
cd chatbot_website/
python manage.py makemigrations
python manage.py migrate
Ensuite, pour lancer le serveur localement, utilisez les commandes suivantes :
cd chatbot_website/
redis-server & # Launch Redis in background
python manage.py runserver
Après le lancement, l'interface devrait être disponible sur http://localhost:8000/. Si vous souhaitez déployer le programme sur un serveur, utilisez plutôt python manage.py runserver 0.0.0.0
. Plus d'informations ici.
Étonnamment, il est possible d'obtenir des résultats après seulement 1 ou 2 heures d'entraînement (sur une GeForce GT 740M), en réduisant drastiquement la séquence de saisie à 5 mots et de sortie à 3 (plus les jetons go
et eos
) et en utilisant un petit taille d'intégration (quelque chose comme 25). Depuis, j'ai modifié le code et maintenant la longueur de sortie doit correspondre à celle d'entrée mais vous pouvez toujours reproduire les résultats originaux, en utilisant cette version. Bien sûr, le réseau ne sera pas vraiment bavard :
Q: Hi
A: Hi.
Q: What is your name ?
A: Laura.
Q: What does that mean ?
A: I dunno.
Q: How old are you ?
A: thirty-five.
Q: Will Google hire me ?
A: No.
Q: Tell me the alphabet
A: Fuck you.
Q: That's not nice
A: Yeah.
Voici un cas où cela échoue :
Q: Who is Laura ?
A: My brother.
Q: Say 'goodbye'
A: Alright.
Q: What is cooking ?
A: A channel.
Q: Can you say no ?
A: No.
Q: Two plus two
A: Manny...
Avec des phrases plus longues, le réseau est beaucoup plus lent à s’entraîner. Après 250 000 itérations avec maxLength=10, le réseau donne encore principalement des réponses évasives (en répondant à la plupart des questions ouvertes par « je ne sais pas ») mais il semble avoir intégré une sémantique de base :
Q: When are the fireworks ?
A: Tomorrow.
Q: What color ?
A: Gray.
Q: Which apple ?
A: That's the first one.
Q: What time is it ?
A: Eleven o'clock.
Q: Who is Skywalker ?
A: She's a freelance bio-exorcist.
Q: Who is John ?
A: The old man.
Q: Who is Jenny ?
A: The man who killed herself.
Q: When were you born ?
A: Soon.
Comme le montrent les dernières questions, le réseau fait encore une certaine confusion entre les genres et le présent/futur. J'ai aussi essayé quelques questions philosophiques plus profondes avec plus ou moins de succès.
Q: What is the purpose of being intelligent ?
A: The man are obsolete.
Q: What is immoral ?
A: The truth.
Q: What is morality ?
A: The worst.
Q: What is the purpose of existence ?
A: It 's private.
Q: What happens if machines can think ?
A: I think I'll just lie with you.
Le modèle que j'ai formé est assez limité par la taille d'intégration que j'ai mise et par sa taille, ainsi que par la taille du corpus de formation. Son « vecteur de pensée » est également probablement trop petit pour répondre au type de question suivant :
Q: My name is David. What is my name ?
A: Debbie.
Q: My name is John. What is my name ?
A: Carl Smith.
Q: How many legs does a cat have ?
A: Four.
Q: How many legs does a spider have ?
A: Four.
Q: Do you prefer football or soccer ?
A: Whiskey.
Il semble également être surajusté, car parfois il extrait simplement des phrases de son ensemble de formation sans rapport avec la question. L'ajout de quelques abandons devrait réduire le problème.
Q: I play tennis. What do I play ?
A: I'm going to get laid.
Q: What color is the sky ?
A: One of the deputies is everywhere.
Attention : les liens ne fonctionnent plus.
Vous pouvez trouver un modèle pré-entraîné ici , formé du corpus par défaut. Pour l'utiliser :
DeepQA/save/
save/model-pretrainedv2/dataset-cornell-old-lenght10-filter0-vocabSize0.pkl
vers data/samples/
../main.py --modelTag pretrainedv2 --test interactive
. Grâce à Nicholas C., voici (original) quelques modèles pré-entraînés supplémentaires (compatibles avec TF 1.2) pour divers ensembles de données. Le dossier contient également l'ensemble de données prétraité pour Cornell, OpenSubtitles, Ubuntu et Scotus (à déplacer dans data/samples/
). Celles-ci sont obligatoires si vous ne souhaitez pas traiter les ensembles de données vous-même.
Si vous disposez d'un GPU haut de gamme, n'hésitez pas à jouer avec les hyper-paramètres/corpus pour entraîner un meilleur modèle. D'après mes expériences, il semble que le taux d'apprentissage et le taux d'abandon aient le plus d'impact sur les résultats. Aussi si vous souhaitez partager vos modèles, n'hésitez pas à me contacter et je l'ajouterai ici.
En plus d’essayer un modèle plus grand/plus profond, de nombreuses petites améliorations pourraient être testées. N'hésitez pas à envoyer une pull request si vous en implémentez une. Voici quelques idées :
loop_function
de tf.nn.seq2seq.rnn_decoder
, cela ne devrait pas être trop difficile à ajouter. Après cela, il devrait être possible de jouer avec la température SoftMax pour obtenir des prédictions plus conservatrices ou exotiques.embedding_rnn_seq2seq
par embedding_attention_seq2seq
sur model.py
.Q:Sentence 1. Sentence 2. => A:Sentence X. Sentence Y.
nous pourrions générer 3 nouveaux échantillons : Q:Sentence 1. Sentence 2. => A:Sentence X.
, Q:Sentence 2. => A:Sentence X. Sentence Y.
et Q:Sentence 2. => A:Sentence X.
. Attention : d'autres combinaisons comme Q:Sentence 1. => A:Sentence X.
ne fonctionneront pas car cela briserait la transition 2 => X
qui relie la question à la réponse)
et
pourraient être ajoutés afin que l'encodeur sache quand l'interlocuteur change. Je ne suis pas sûr cependant que le simple modèle seq2seq serait suffisant pour capturer les dépendances à long terme entre les phrases. L’ajout d’un système de seau pour regrouper des longueurs d’entrée similaires pourrait grandement améliorer la vitesse d’entraînement.