In letzter Zeit war der Fortschritt von DNNs (Deep Neural Networks) bemerkenswert und in allen Bereichen erfolgreich. Wir hören oft über die Bereiche Bildklassifizierung und Spracherkennung, und Dialogsysteme sind keine Ausnahme. Nachdem die Python-Bibliotheksumgebung erweitert wurde, möchte ich kurz den Aufbau eines interaktiven Systems mit DNNs vorstellen.
Es gibt zwei Hauptmodelle von DNNs zum Aufbau eines Dialogsystems.
Dieser Artikel befasst sich mit dem letzteren Encoder-Decoder-Modell. Dank der umfangreichen Bibliothek wie Chainer kann jeder sie implementieren, solange Daten vorhanden sind, bei denen es sich um ein Paar aus Sprache und Antwort handelt.
Die abhängigen Pakete sind unten zusammengefasst. Mit dem Befehl pip install oder conda install können Sie die Umgebung auf einmal erstellen.
seq2seq Das in diesem Artikel beschriebene Modell heißt seq2seq (Sequence to Sequence) und besteht aus zwei Arten von Netzwerken: einem Encoder-RNN (Reccurent Neural Network) für die Eingabe und einem Decoder-RNN für die Ausgabe (der RNN-Teil ist allgemein). Wird mit LSTM implementiert).
Originalarbeit: Sequenz-zu-Sequenz-Lernen mit neuronalen Netzen
Bei Anwendung auf ein Dialogsystem wird die Eingangsäußerung durch den Codierer geleitet und die Antwort darauf wird Wort für Wort mit dem Decodierer gelernt.
Dieses Mal lernen wir die Verwendung der Daten von Dialogue Failure Detection Challenge 2. Der Korpus zur Erkennung von Dialogfehlern ist ein Korpus, den jeder für jeden Zweck verwenden kann, sodass Sie ihn mit Sicherheit verwenden können.
Korpus zur Erkennung von Dialogfehlern:
URL: https://sites.google.com/site/dialoguebreakdowndetection2/downloads
Der Inhalt des Korpus besteht aus JSON-Dateien. Darüber hinaus ist ein Skript enthalten, das den in der JSON-Datei gespeicherten Dialog auf einfach zu lesende Weise ausgibt. Versuchen Sie, wie folgt auszuführen.
show_dial.py
$ python show_dial.py 1470622453.log.json
Ausführungsergebnis:
dialogue-id : 1470622453 speaker-id : DBD-01 group-id : S: Hallo. Achten Sie auf Hitzschlag. O O O O O O O O O O O O O O O O O O O O O O O O O O O O. U: Ja. Vielen Dank. Sie sollten auch vorsichtig sein. S: Sei nicht vorsichtig mit Hitzschlag? T O T X X T X X T O T T T X X O X X X X X T T O O T O. ... (weggelassen) S: Übung ist eine Abwechslung, nicht wahr? Ich fühle mich in Ordnung
Unter Verwendung des obigen Dialogfehlererkennungskorpus wird seq2seq unter Verwendung der Benutzersprache (U) als Eingabesprache zum Lernen und der Systemsprache zu diesem Zeitpunkt als Antwortsprache gelernt.
Wie in der obigen Abbildung gezeigt, wird eine Datei mit einer Eins-zu-Eins-Entsprechung zwischen Sprache (Äußerung) und Antwort (Antwort) erstellt und als Trainingsdaten angegeben. Verwenden Sie das folgende Skript, um die JSON-Datei in eine Textdatei zu konvertieren, die die Trainingsdaten enthält.
json2text.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import json
def loadingJson(dirpath, f):
fpath = dirpath + '/' + f
fj = open(fpath,'r')
json_data = json.load(fj)
fj.close()
return json_data
def output(data, mod):
for i in range(len(data['turns'])):
if mod == "U" and data['turns'][i]['speaker'] == mod:
print data['turns'][i]['utterance'].encode('utf-8')
elif mod == "S" and data['turns'][i]['speaker'] == mod and i != 0:
print data['turns'][i]['utterance'].encode('utf-8')
else:
continue
if __name__ == "__main__":
argvs = sys.argv
_usage = """--
Usage:
python json2text.py [json] [speaker]
Args:
[json]: The argument is input directory that is contained files of json that is objective to convert to sql.
[speaker]: The argument is "U" or "S" that is speaker in dialogue.
""".rstrip()
if len(argvs) < 3:
print _usage
sys.exit(0)
# one file ver
'''
fj = open(argvs[1],'r')
json_data = json.load(fj)
fj.close()
output(json_data, mod)
'''
# more than two files ver
branch = os.walk(argvs[1])
mod = argvs[2]
for dirpath, dirs, files in branch:
for f in files:
json_data = loadingJson(dirpath, f)
output(json_data, mod)
Führen Sie wie folgt aus.
json2text.py
$ python json2text.py [json] [speaker]
Durch diese Verarbeitung wurde es kurz vor den Trainingsdaten (Utterance, Response) abgeschlossen. Danach wird eine morphologische Analyse durchgeführt und separat geschrieben.
$ mecab -Owakati Utterance.txt > Utterance_wakati.txt
Bis zu diesem Punkt wurden durch die Verarbeitung Lerndaten (Äußerung, Antwort) erstellt. Als nächstes werden wir das Modell trainieren.
learning.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
class seq2seq(chainer.Chain):
def __init__(self, jv, ev, k, jvocab, evocab):
super(seq2seq, self).__init__(
embedx = L.EmbedID(jv, k),
embedy = L.EmbedID(ev, k),
H = L.LSTM(k, k),
W = L.Linear(k, ev),
)
def __call__(self, jline, eline, jvocab, evocab):
for i in range(len(jline)):
wid = jvocab[jline[i]]
x_k = self.embedx(Variable(np.array([wid], dtype=np.int32)))
h = self.H(x_k)
x_k = self.embedx(Variable(np.array([jvocab['<eos>']], dtype=np.int32)))
tx = Variable(np.array([evocab[eline[0]]], dtype=np.int32))
h = self.H(x_k)
accum_loss = F.softmax_cross_entropy(self.W(h), tx)
for i in range(len(eline)):
wid = evocab[eline[i]]
x_k = self.embedy(Variable(np.array([wid], dtype=np.int32)))
next_wid = evocab['<eos>'] if (i == len(eline) - 1) else evocab[eline[i+1]]
tx = Variable(np.array([next_wid], dtype=np.int32))
h = self.H(x_k)
loss = F.softmax_cross_entropy(self.W(h), tx)
accum_loss += loss
return accum_loss
def main(epochs, urr_file, res_file, out_path):
jvocab = {}
jlines = open(utt_file).read().split('\n')
for i in range(len(jlines)):
lt = jlines[i].split()
for w in lt:
if w not in jvocab:
jvocab[w] = len(jvocab)
jvocab['<eos>'] = len(jvocab)
jv = len(jvocab)
evocab = {}
elines = open(res_file).read().split('\n')
for i in range(len(elines)):
lt = elines[i].split()
for w in lt:
if w not in evocab:
evocab[w] = len(evocab)
ev = len(evocab)
demb = 100
model = seq2seq(jv, ev, demb, jvocab, evocab)
optimizer = optimizers.Adam()
optimizer.setup(model)
for epoch in range(epochs):
for i in range(len(jlines)-1):
jln = jlines[i].split()
jlnr = jln[::-1]
eln = elines[i].split()
model.H.reset_state()
model.zerograds()
loss = model(jlnr, eln, jvocab, evocab)
loss.backward()
loss.unchain_backward()
optimizer.update()
print i, " finished"
outfile = out_path + "/seq2seq-" + str(epoch) + ".model"
serializers.save_npz(outfile, model)
if __name__ == "__main__":
argvs = sys.argv
_usage = """--
Usage:
python learning.py [epoch] [utteranceDB] [responseDB] [save_link]
Args:
[epoch]: The argument is the number of max epochs to train models.
[utteranceDB]: The argument is input file to train model that is to convert as pre-utterance.
[responseDB]: The argument is input file to train model that is to convert as response to utterance.
[save_link]: The argument is output directory to save trained models.
""".rstrip()
if len(argvs) < 5:
print _usage
sys.exit(0)
epochs = int(argvs[1])
utt_file = argvs[2]
res_file = argvs[3]
out_path = argvs[4]
main(epochs, utt_file, res_file, out_path)
Die Ausführung ist wie folgt.
learning.py
$ python learning.py [epoch] [utternceDB] [responseDB] [savelink]
Let's Conversation! Endlich ist das Lernen vorbei. Jetzt lass uns reden!
generating.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import numpy as np
import mecab as mcb
import chainer
from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
class seq2seq(chainer.Chain):
def __init__(self, jv, ev, k, jvocab, evocab):
super(seq2seq, self).__init__(
embedx = L.EmbedID(jv, k),
embedy = L.EmbedID(ev, k),
H = L.LSTM(k, k),
W = L.Linear(k, ev),
)
def __call__(self, jline, eline, jvocab, evocab):
for i in range(len(jline)):
wid = jvocab[jline[i]]
x_k = self.embedx(Variable(np.array([wid], dtype=np.int32)))
h = self.H(x_k)
x_k = self.embedx(Variable(np.array([jvocab['<eos>']], dtype=np.int32)))
tx = Variable(np.array([evocab[eline[0]]], dtype=np.int32))
h = self.H(x_k)
accum_loss = F.softmax_cross_entropy(self.W(h), tx)
for i in range(1,len(eline)):
wid = evocab[eline[i]]
x_k = self.embedy(Variable(np.array([wid], dtype=np.int32)))
next_wid = evocab['<eos>'] if (i == len(eline) - 1) else evocab[eline[i+1]]
tx = Variable(np.array([next_wid], dtype=np.int32))
h = self.H(x_k)
loss = F.softmax_cross_entropy(self.W(h), tx)
accum_loss = loss if accum_loss is None else accum_loss + loss
return accum_loss
def mt(model, jline, id2wd, jvocab, evocab):
for i in range(len(jline)):
wid = jvocab[jline[i]]
x_k = model.embedx(Variable(np.array([wid], dtype=np.int32), volatile='on'))
h = model.H(x_k)
x_k = model.embedx(Variable(np.array([jvocab['<eos>']], dtype=np.int32), volatile='on'))
h = model.H(x_k)
wid = np.argmax(F.softmax(model.W(h)).data[0])
if wid in id2wd:
print id2wd[wid],
else:
print wid,
loop = 0
while (wid != evocab['<eos>']) and (loop <= 30):
x_k = model.embedy(Variable(np.array([wid], dtype=np.int32), volatile='on'))
h = model.H(x_k)
wid = np.argmax(F.softmax(model.W(h)).data[0])
if wid in id2wd:
print id2wd[wid],
else:
print wid,
loop += 1
print
def constructVocabs(corpus, mod):
vocab = {}
id2wd = {}
lines = open(corpus).read().split('\n')
for i in range(len(lines)):
lt = lines[i].split()
for w in lt:
if w not in vocab:
if mod == "U":
vocab[w] = len(vocab)
elif mod == "R":
id2wd[len(vocab)] = w
vocab[w] = len(vocab)
if mod == "U":
vocab['<eos>'] = len(vocab)
v = len(vocab)
return vocab, v
elif mod == "R":
id2wd[len(vocab)] = '<eos>'
vocab['<eos>'] = len(vocab)
v = len(vocab)
return vocab, v, id2wd
def main(mpath, utt_file, res_file):
jvocab, jv = constructVocabs(utt_file, mod="U")
evocab, ev, id2wd = constructVocabs(res_file, mod="R")
demb = 100
model = seq2seq(jv, ev, demb, jvocab, evocab)
serializers.load_npz(mpath, model)
while True:
utterance = raw_input()
if utterance == "exit":
print "Bye!!"
sys.exit(0)
jln = mcb.construct_BoW(utterance)
jlnr = jln[::-1]
mt(model, jlnr, id2wd, jvocab, evocab)
if __name__ == "__main__":
argvs = sys.argv
_usage = """--
Usage:
python generating.py [model] [uttranceDB] [responseDB]
Args:
[model]: The argument is seq2seq model to be trained using dialog corpus.
[utteranceDB]: The argument is utterance corpus to gain the distributed representation of words.
[responseDB]: The argument is response corpus to gain the distributed representation of words.
""".rstrip()
if len(argvs) < 4:
print _usage
sys.exit(0)
model = argvs[1]
utt_file = argvs[2]
res_file = argvs[3]
main(model, utt_file, res_file)
generating.py
$ python generating.py [model]
Hier ist das Ergebnis des eigentlichen Dialogs!
Epoch=1:
U: Hallo S: Ich war vor kurzem dort, vor kurzem.
U: Was ist dein Geschlecht? S: Nun, zu welchem gehst du? U: Hast du einen Liebhaber? S: Nun, zu welchem gehst du?
Epoch=10:
U: Hallo S: Hallo
U: Was ist dein Geschlecht? S: Ist es Wahrsagerei! Ich bin darin nicht gut. .. .. Winterkleidung ist schwer und teuer. U: Hast du einen Liebhaber? S: Gibt es etwas Neues?
Epoch=30:
U: Hallo S: Hallo, danke
U: Was ist dein Geschlecht? S: Es kann sich um ein umgekehrtes Kind handeln und das Geschlecht ist unbekannt U: Hast du einen Liebhaber? S: Vielleicht willst du einen Liebhaber
Wenn Epoche 1 ist, habe ich das Gefühl, dass ich überhaupt nichts gelernt habe, aber mit zunehmender Epoche wird die Antwort immer besser! Diesmal war es jedoch ein geschlossener Test (Eingabe der Äußerung in den Korpus), so dass es im eigentlichen Dialog noch schlimmer wird. Es scheint notwendig, die Anzahl der Trainingsdaten zur Verbesserung um etwa 3 Stellen zu erhöhen.
Dieses Mal haben wir ein Dialogsystem implementiert, das mithilfe des seq2seq-Modells Wort für Wort Äußerungen erzeugen kann. Das Problem ist, dass eine große Menge an Trainingsdaten für die Implementierung vorbereitet werden muss. Umgekehrt kann jedoch ein System erstellt werden, das auf diese Weise interagieren kann, wenn Daten vorhanden sind. Die Antwort hängt stark von der Verteilung der verwendeten Daten ab.
Wenn Sie es interessant finden, probieren Sie es bitte aus! Let's Conversation!
Recommended Posts