Entwicklung eines Neuronalen Übersetzungssystem
- 27 minsEntwicklung eines Neuronalen Übersetzungssystem
Credits to https://machinelearningmastery.com/
Grundsätzlich handelt dieses Skript über die Entwicklung eines Übersetzungssystem von Deutsch auf Englisch. Dazu wird auf Basis von Neural machine translation solch ein System aufgesetzt. Neuronale maschinelle Übersetzung beinhaltet die Verwendung von tiefen Neuronalen Netzen (Deep Neural Networks) für das Problem der maschinellen Übersetzung. Dazu wird hier das Deep Learning Framework Keras Framework genutzt.
Aufbau:
- Daten bereinigen und vorbereiten um ein Neuronales Übersetzungssystem zu trainieren
- Encoder-Decoder bauen
- Trainiertes Modell mit neuen Daten füttern
# Deutsch-Englisch-Übersetzungsdatensatz
Verwendet wird ein Datensatz von Deutsch zu Englisch, der als Grundlage für Lernkarten zum Sprachenlernen dient. Der Datensatz besteht aus deutschen Phrasen und deren englischen Pendants. Soll eigentlich für eine Lernkartei Software sein.
Datenset-Download: http://www.manythings.org/anki/deu-eng.zip
Inhalte: 152.820 Paare von Englisch nach Deutsch - Ein Paar pro Zeile mit einem Tab, welches die Sprache trennt.
Erste 5 Zeilen:
- Hi. Hallo!
- Hi. Grüß Gott!
- Run! Lauf!
- Wow! Potzdonner!
- Wow! Donnerwetter!
Vorhersagemodell: Folge von Wörtern in Deutsch –> Folge von Wörtern auf Englisch Ausgabesquenz
1. Texte bereinigen
Rohdaten anschauen und Beobachtungen:
- Es gibt Satzzeichen.
- Der Text enthält Groß- und Kleinbuchstaben.
- Es gibt Sonderzeichen im Deutschen.
- Es gibt doppelte Sätze in Englisch mit verschiedenen Übersetzungen in Deutsch.
- Die Datei ist nach Satzlänge mit sehr langen Sätzen gegen Ende der Datei geordnet.
Wir definieren uns erstmal die nötigen Funktionen…
import string
import re
from pickle import dump
from unicodedata import normalize
from numpy import array
# Daten einlesen
def load_doc(filename):
# nur als read only einlesen
file = open(filename, mode='rt', encoding='utf-8')
# lese kompletten text ein
text = file.read()
# schließe file
file.close()
return text
# aufsplitten in Sätze
def to_pairs(doc):
lines = doc.strip().split('\n')
pairs = [line.split('\t') for line in lines]
return pairs
Jede Zeile enthält ein einzelnes Satzpaar, zuerst Englisch und dann Deutsch, getrennt durch ein Tabulatorzeichen.
Wir müssen den geladenen Text Zeile für Zeile und dann nach Satz teilen. Die Funktion to_pairs () unten teilt den geladenen Text auf.
Wir sind jetzt bereit, jeden Satz zu säubern. Die spezifischen Reinigungsvorgänge, die wir ausführen werden, sind wie folgt:
- Entfern alle nicht druckbaren Zeichen.
- Entferne alle Interpunktionszeichen. -> Satzzeichen
- Normalisiere alle Unicode-Zeichen in ASCII (z. B. lateinische Zeichen).
- Normalisiere die Groß- / Kleinschreibung.
- Entferne alle verbleibenden Token, die nicht alphabetisch sind.
- Wir werden diese Operationen für jede Phrase für jedes Paar im geladenen Dataset durchführen.
Die folgende Funktion clean_pairs () implementiert diese Operationen.
und speichern sie am ende als neue Datei ab über die pickle API
# säubere Daten
def clean_pairs(lines):
cleaned = list()
# Vorbereitung regex für einzelne char filterung
re_print = re.compile('[^%s]' % re.escape(string.printable))
# Bereite die Übersetzungstabelle vor um die Interpunktionen (Satzzeichen ) zu entfernen.
table = str.maketrans('', '', string.punctuation)
for pair in lines:
clean_pair = list()
for line in pair:
# normalisiere unicode zeichen
line = normalize('NFD', line).encode('ascii', 'ignore')
line = line.decode('UTF-8')
# token pro Leerzeichen
line = line.split()
# in kleinbuchstaben konvertieren...
line = [word.lower() for word in line]
# entferne Satzzeichen aus jedem Token...
line = [word.translate(table) for word in line]
# entferne nicht anzeigbare zeichen aus jedem Token
line = [re_print.sub('', w) for w in line]
# entferne tokens mit zahlen
line = [word for word in line if word.isalpha()]
# speichere als string
clean_pair.append(' '.join(line))
cleaned.append(clean_pair)
return array(cleaned)
# speichere die liste von nun sauberen Daten in eine Datei, dazu nutzen wir die Pickle-AP
def save_clean_data(sentences, filename):
dump(sentences, open(filename, 'wb'))
print('Saved: %s' % filename)
Anwenden auf Datensatz:
# lade den datensatz ein
filename = 'deu-eng/deu.txt'
doc = load_doc(filename)
# aufsplitten in englisch-deutsch Paare
pairs = to_pairs(doc)
# säubere die Sätze
clean_pairs = clean_pairs(pairs)
# speichere saubere Satzpaare in Datei
save_clean_data(clean_pairs, 'english-german.pkl')
# Stichprobe ausgeben
#Aus:
for i in range(20):
print(doc[i])
# Wurde:
for i in range(50):
print('[%s] => [%s]' % (clean_pairs[i,0], clean_pairs[i,1]))
Saved: english-german.pkl
H
i
.
H
a
l
l
o
!
H
i
.
G
r
ü
ß
[hi] => [hallo]
[hi] => [gru gott]
[run] => [lauf]
[wow] => [potzdonner]
[wow] => [donnerwetter]
[fire] => [feuer]
[help] => [hilfe]
[help] => [zu hulf]
[stop] => [stopp]
[wait] => [warte]
[hello] => [hallo]
[i try] => [ich probiere es]
[i won] => [ich hab gewonnen]
[i won] => [ich habe gewonnen]
[smile] => [lacheln]
[cheers] => [zum wohl]
[freeze] => [keine bewegung]
[freeze] => [stehenbleiben]
[got it] => [verstanden]
[got it] => [einverstanden]
[he ran] => [er rannte]
[he ran] => [er lief]
[hop in] => [mach mit]
[hug me] => [druck mich]
[hug me] => [nimm mich in den arm]
[hug me] => [umarme mich]
[i fell] => [ich fiel]
[i fell] => [ich fiel hin]
[i fell] => [ich sturzte]
[i fell] => [ich bin hingefallen]
[i fell] => [ich bin gesturzt]
[i know] => [ich wei]
[i lied] => [ich habe gelogen]
[i lost] => [ich habe verloren]
[im] => [ich bin jahre alt]
[im] => [ich bin]
[im ok] => [mir gehts gut]
[im ok] => [es geht mir gut]
[no way] => [unmoglich]
[no way] => [das gibts doch nicht]
[no way] => [ausgeschlossen]
[no way] => [in keinster weise]
[really] => [wirklich]
[really] => [echt]
[really] => [im ernst]
[thanks] => [danke]
[try it] => [versuchs]
[why me] => [warum ich]
[ask tom] => [frag tom]
[ask tom] => [fragen sie tom]
2. Text aufsplitten
Die sauberen Daten enthalten etwas mehr als 150.000 Phrasenpaare und einige der Paare am Ende der Datei sind sehr lang.
Die langen Satzpaare erhöhen die komplexität des Modells
Wir werden das Problem vereinfachen, indem wir den Datensatz auf die ersten 30.000 Beispiele in der Datei reduzieren. Dies sind die kürzesten Sätze im Datensatz. Die hinteneren Sätze sind ziemlich lang, dadurch steigt die Komplexität. Wir halten sie in diesem Beispiel erstmal kurz.
Weiter werden wir dann die ersten 29.000 als Beispiele für das Training und die restlichen 1.000 Beispiele als Test für das Modell verwenden.
from pickle import load
from pickle import dump
from numpy.random import rand
from numpy.random import shuffle
# lade sauberen Datensatz ein
def load_clean_sentences(filename):
return load(open(filename, 'rb'))
# speichere die sauberen Sätze in eine Datei
def save_clean_data(sentences, filename):
dump(sentences, open(filename, 'wb'))
print('Saved: %s' % filename)
# load dataset
raw_dataset = load_clean_sentences('english-german.pkl')
# reduziere datenset größe...
n_sentences = 30000
dataset = raw_dataset[:n_sentences, :]
# zufällig shufflen...
shuffle(dataset)
# aufteilen in training/test
train, test = dataset[:29000], dataset[29000:]
# save
save_clean_data(dataset, 'english-german-both.pkl')
save_clean_data(train, 'english-german-train.pkl')
save_clean_data(test, 'english-german-test.pkl')
Saved: english-german-both.pkl
Saved: english-german-train.pkl
Saved: english-german-test.pkl
Drei Dateien wurden erstellt… Wir sind bereit unser Übersetzungsmodell zu entwickeln
Trainieren des Modells
# load datasets
dataset = load_clean_sentences('english-german-both.pkl')
train = load_clean_sentences('english-german-train.pkl')
test = load_clean_sentences('english-german-test.pkl')
Wir werden die “beide” oder die Kombination der Training- und Testdatensätze verwenden, um die maximale Länge und das Vokabular des Problems zu definieren.
Dies ist der Einfachheit halber. Alternativ könnten wir diese Eigenschaften nur aus dem Trainingsdatensatz definieren und Beispiele im Testset abschneiden, die zu lang sind oder Wörter enthalten, die nicht im Vokabular enthalten sind.
Wir können die Keras Tokenize-Klasse verwenden, um Wörter auf Ganzzahlen abzubilden, wie sie für die Modellierung benötigt werden. Wir werden separate Tokenizer für die englischen Sequenzen und die deutschen Sequenzen verwenden. Die Funktion mit dem Namen create_tokenizer () trainiert einen Tokenizer in einer Liste von Phrasen.
from keras.preprocessing.text import Tokenizer
# tokenizer fitten
def create_tokenizer(lines):
tokenizer = Tokenizer()
tokenizer.fit_on_texts(lines)
return tokenizer
Using TensorFlow backend.
In ähnlicher Weise findet die Funktion mit dem Namen max_length () die Länge der längsten Sequenz in einer Liste von Phrasen.
# maximale satzlänge*
def max_length(lines):
return max(len(line.split()) for line in lines)
Wir können diese Funktionen mit dem kombinierten Datenbestand aufrufen, um Tokenizer, Wortschatzgrößen und maximale Längen sowohl für die englischen als auch für die deutschen Phrasen vorzubereiten.
# bereite englischen tokenizer vor
eng_tokenizer = create_tokenizer(dataset[:, 0])
eng_vocab_size = len(eng_tokenizer.word_index) + 1
eng_length = max_length(dataset[:, 0])
print('Englisch Wortschatz Size: %d' % eng_vocab_size)
print('Englisch Max Länge: %d' % (eng_length))
# bereite deutschen tokenizer vor
ger_tokenizer = create_tokenizer(dataset[:, 1])
ger_vocab_size = len(ger_tokenizer.word_index) + 1
ger_length = max_length(dataset[:, 1])
print('Deutscher Wortschatz Size: %d' % ger_vocab_size)
print('Deutsch Max Länge: %d' % (ger_length))
Englisch Wortschatz Size: 4936
Englisch Max Länge: 7
Deutscher Wortschatz Size: 8087
Deutsch Max Länge: 11
Wir sind nun bereit, den Trainingsdatensatz vorzubereiten.
Jede Eingabe- und Ausgabesequenz muss in Ganzzahlen codiert und auf die maximale Phrasenlänge aufgefüllt werden. Dies liegt daran, dass wir eine Worteinbettung für die Eingabesequenzen und eine heiße Kodierung der Ausgabesequenzen verwenden werden. Die folgende Funktion namens encode_sequences () führt diese Operationen aus und gibt das Ergebnis zurück.
from keras.preprocessing.sequence import pad_sequences
# codiere und pad sequenz
def encode_sequences(tokenizer, length, lines):
# als integer codieren...
X = tokenizer.texts_to_sequences(lines)
# Pad-Sequenzen mit 0 Werten
X = pad_sequences(X, maxlen=length, padding='post')
return X
Die Ausgabesequenz muss one-Hot-codiert sein. Dies liegt daran, dass das Modell die Wahrscheinlichkeit jedes Wortes im Vokabular als Ausgabe vorhersagt.
Die Funktion encode_output () unten wird englische Ausgabesequenzen one-hot codieren.
from keras.utils import to_categorical
# one hot encode ziel satz
def encode_output(sequences, vocab_size):
ylist = list()
for sequence in sequences:
encoded = to_categorical(sequence, num_classes=vocab_size)
ylist.append(encoded)
y = array(ylist)
y = y.reshape(sequences.shape[0], sequences.shape[1], vocab_size)
return y
Wir können diese beiden Funktionen nutzen und sowohl den Trainings- als auch den Testdatensatz für das Training des Modells vorbereiten.
# prepare training data
trainX = encode_sequences(ger_tokenizer, ger_length, train[:, 1])
trainY = encode_sequences(eng_tokenizer, eng_length, train[:, 0])
trainY = encode_output(trainY, eng_vocab_size)
# prepare validation data
testX = encode_sequences(ger_tokenizer, ger_length, test[:, 1])
testY = encode_sequences(eng_tokenizer, eng_length, test[:, 0])
testY = encode_output(testY, eng_vocab_size)
Wir sind jetzt bereit, das Modell zu definieren.
Wir werden ein Encoder-Decoder-LSTM-Modell für dieses Problem verwenden. Bei dieser Architektur wird die Eingangssequenz durch ein Frontend-Modell codiert, das Encoder genannt wird, und dann Wort für Wort von einem Backend-Modell, dem Decoder, dekodiert.
Die folgende Funktion define_model () definiert das Modell und verwendet eine Reihe von Argumenten, die zur Konfiguration des Modells verwendet werden, z. B. die Größe der Eingabe- und Ausgabevokabulare, die maximale Länge von Eingabe- und Ausgabephrasen und die Anzahl der zur Konfiguration verwendeten Speichereinheiten das Model.
Das Modell wird unter Verwendung des effizienten Adam-Ansatzes zum stochastischen Gradientenabfall trainiert und minimiert die kategoriale Verlustfunktion, da wir das Vorhersageproblem als Mehrklassenklassifikation definiert haben.
Die Modellkonfiguration wurde nicht für dieses Problem optimiert. Das bedeutet, dass Sie die Möglichkeit haben, sie zu optimieren und die Fähigkeiten der Übersetzungen zu verbessern. Ich würde gerne sehen, was du dir vorstellen kannst.
from keras.models import Sequential
from keras.layers import Embedding
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed
from keras.utils.vis_utils import plot_model
# definiere Modell funktion
def define_model(src_vocab, tar_vocab, src_timesteps, tar_timesteps, n_units):
model = Sequential()
model.add(Embedding(src_vocab, n_units, input_length=src_timesteps, mask_zero=True))
model.add(LSTM(n_units))
model.add(RepeatVector(tar_timesteps))
model.add(LSTM(n_units, return_sequences=True))
model.add(TimeDistributed(Dense(tar_vocab, activation='softmax')))
return model
# definiere modell
model = define_model(ger_vocab_size, eng_vocab_size, ger_length, eng_length, 256)
model.compile(optimizer='adam', loss='categorical_crossentropy')
# summarize defined model
print(model.summary())
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, 11, 256) 2070272
_________________________________________________________________
lstm_1 (LSTM) (None, 256) 525312
_________________________________________________________________
repeat_vector_1 (RepeatVecto (None, 7, 256) 0
_________________________________________________________________
lstm_2 (LSTM) (None, 7, 256) 525312
_________________________________________________________________
time_distributed_1 (TimeDist (None, 7, 4936) 1268552
=================================================================
Total params: 4,389,448
Trainable params: 4,389,448
Non-trainable params: 0
_________________________________________________________________
None
Schließlich können wir das Modell trainieren.
Wir trainieren das Modell für 30 Epochen und eine Batch größe von 64 Exemplaren.
Wir verwenden Checkpointing, um sicherzustellen, dass jedes Mal, wenn sich die Modellfähigkeit des Testsets verbessert, das Modell in einer Datei gespeichert wird.
from keras.callbacks import ModelCheckpoint
# fit model
filename = 'model.h5'
checkpoint = ModelCheckpoint(filename, monitor='val_loss', verbose=1, save_best_only=True, mode='min')
model.fit(trainX, trainY, epochs=30, batch_size=64, validation_data=(testX, testY), callbacks=[checkpoint], verbose=2)
Train on 29000 samples, validate on 1000 samples
Epoch 1/30
Epoch 00001: val_loss improved from inf to 3.35847, saving model to model.h5
- 160s - loss: 3.7272 - val_loss: 3.3585
Epoch 2/30
Epoch 00002: val_loss improved from 3.35847 to 3.15733, saving model to model.h5
- 164s - loss: 3.2099 - val_loss: 3.1573
Epoch 3/30
Epoch 00003: val_loss improved from 3.15733 to 2.96066, saving model to model.h5
- 161s - loss: 2.9854 - val_loss: 2.9607
Epoch 4/30
Epoch 00004: val_loss improved from 2.96066 to 2.73765, saving model to model.h5
- 160s - loss: 2.7519 - val_loss: 2.7376
Epoch 5/30
Epoch 00005: val_loss improved from 2.73765 to 2.52555, saving model to model.h5
- 159s - loss: 2.5084 - val_loss: 2.5256
Epoch 6/30
Epoch 00006: val_loss improved from 2.52555 to 2.32497, saving model to model.h5
- 160s - loss: 2.2711 - val_loss: 2.3250
Epoch 7/30
Epoch 00007: val_loss improved from 2.32497 to 2.15213, saving model to model.h5
- 160s - loss: 2.0426 - val_loss: 2.1521
Epoch 8/30
Epoch 00008: val_loss improved from 2.15213 to 2.02360, saving model to model.h5
- 166s - loss: 1.8402 - val_loss: 2.0236
Epoch 9/30
Epoch 00009: val_loss improved from 2.02360 to 1.92177, saving model to model.h5
- 161s - loss: 1.6594 - val_loss: 1.9218
Epoch 10/30
Epoch 00010: val_loss improved from 1.92177 to 1.81784, saving model to model.h5
- 160s - loss: 1.4946 - val_loss: 1.8178
Epoch 11/30
Epoch 00011: val_loss improved from 1.81784 to 1.74191, saving model to model.h5
- 160s - loss: 1.3415 - val_loss: 1.7419
Epoch 12/30
Epoch 00012: val_loss improved from 1.74191 to 1.66232, saving model to model.h5
- 160s - loss: 1.2004 - val_loss: 1.6623
Epoch 13/30
Epoch 00013: val_loss improved from 1.66232 to 1.61166, saving model to model.h5
- 160s - loss: 1.0721 - val_loss: 1.6117
Epoch 14/30
Epoch 00014: val_loss improved from 1.61166 to 1.55793, saving model to model.h5
- 167s - loss: 0.9563 - val_loss: 1.5579
Epoch 15/30
Epoch 00015: val_loss improved from 1.55793 to 1.52549, saving model to model.h5
- 162s - loss: 0.8518 - val_loss: 1.5255
Epoch 16/30
Epoch 00016: val_loss improved from 1.52549 to 1.49157, saving model to model.h5
- 160s - loss: 0.7615 - val_loss: 1.4916
Epoch 17/30
Epoch 00017: val_loss improved from 1.49157 to 1.48034, saving model to model.h5
- 161s - loss: 0.6788 - val_loss: 1.4803
Epoch 18/30
Epoch 00018: val_loss improved from 1.48034 to 1.46019, saving model to model.h5
- 160s - loss: 0.6097 - val_loss: 1.4602
Epoch 19/30
Epoch 00019: val_loss did not improve
- 165s - loss: 0.5461 - val_loss: 1.4632
Epoch 20/30
Epoch 00020: val_loss did not improve
- 162s - loss: 0.4942 - val_loss: 1.4604
Epoch 21/30
Epoch 00021: val_loss improved from 1.46019 to 1.44822, saving model to model.h5
- 162s - loss: 0.4472 - val_loss: 1.4482
Epoch 22/30
Epoch 00022: val_loss did not improve
- 174s - loss: 0.4051 - val_loss: 1.4709
Epoch 23/30
Epoch 00023: val_loss did not improve
- 165s - loss: 0.3710 - val_loss: 1.4570
Epoch 24/30
Epoch 00024: val_loss did not improve
- 160s - loss: 0.3402 - val_loss: 1.4667
Epoch 25/30
Epoch 00025: val_loss did not improve
- 166s - loss: 0.3116 - val_loss: 1.4834
Epoch 26/30
Epoch 00026: val_loss did not improve
- 161s - loss: 0.2856 - val_loss: 1.4906
Epoch 27/30
Epoch 00027: val_loss did not improve
- 160s - loss: 0.2654 - val_loss: 1.5100
Epoch 28/30
Epoch 00028: val_loss did not improve
- 160s - loss: 0.2450 - val_loss: 1.5307
Epoch 29/30
Epoch 00029: val_loss did not improve
- 161s - loss: 0.2301 - val_loss: 1.5234
Epoch 30/30
Epoch 00030: val_loss did not improve
- 160s - loss: 0.2152 - val_loss: 1.5400
<keras.callbacks.History at 0x1352a19abe0>
Das Modell ist trainiert… Jede Epoche dauert etwa 3 minuten über die gpu.
Während des Laufs wird das Modell in der Datei model.h5 gespeichert und ist im nächsten Schritt für die Inferenz bereit.
Evaluieren des Übersetzungsmodell
Wir werden das Modell im Training und im Testdatensatz auswerten.
Das Modell sollte im Trainingdatensatz sehr gut funktionieren und wurde idealerweise verallgemeinert, um eine gute Leistung im Testdatensatz zu erzielen.
Idealerweise würden wir einen separaten Validierungsdatensatz verwenden, um bei der Modellauswahl während des Trainings anstelle des Testsatzes zu helfen. Sie können dies als Erweiterung versuchen.
Die sauberen Datensätze müssen wie zuvor geladen und vorbereitet werden.
# lade daten...
dataset = load_clean_sentences('english-german-both.pkl')
train = load_clean_sentences('english-german-train.pkl')
test = load_clean_sentences('english-german-test.pkl')
# vorbereitung englisch tokenzider
eng_tokenizer = create_tokenizer(dataset[:, 0])
eng_vocab_size = len(eng_tokenizer.word_index) + 1
eng_length = max_length(dataset[:, 0])
# vorbereitung deutsch tokenizer
ger_tokenizer = create_tokenizer(dataset[:, 1])
ger_vocab_size = len(ger_tokenizer.word_index) + 1
ger_length = max_length(dataset[:, 1])
# vorbereitung daten
trainX = encode_sequences(ger_tokenizer, ger_length, train[:, 1])
testX = encode_sequences(ger_tokenizer, ger_length, test[:, 1])
Die Evaluierung umfasst zwei Schritte: zuerst wird eine übersetzte Ausgabesequenz generiert und dann wird dieser Prozess für viele Eingabebeispiele wiederholt und die Fähigkeiten des Modells in mehreren Fällen zusammengefasst.
Ausgehend von der Inferenz kann das Modell die gesamte Ausgabesequenz in einer einzigen Aufnahme vorhersagen.
Dies wird eine Folge von Ganzzahlen sein, die wir im Tokenizer aufzählen und nachschlagen können, um sie auf Wörter zurückzubilden.
Die folgende Funktion namens word_for_id () führt diese umgekehrte Zuordnung durch.
# mappe integer zu einem Wort
def word_for_id(integer, tokenizer):
for word, index in tokenizer.word_index.items():
if index == integer:
return word
return None
Wir können diese Zuordnung für jede Ganzzahl in der Übersetzung durchführen und das Ergebnis als eine Folge von Wörtern zurückgeben.
Die Funktion predict_sequence () führt diese Operation für eine einzelne codierte Quellenphrase aus.
# generiert Zielsequenz
def predict_sequence(model, tokenizer, source):
prediction = model.predict(source, verbose=0)[0]
integers = [argmax(vector) for vector in prediction]
target = list()
for i in integers:
word = word_for_id(i, tokenizer)
if word is None:
break
target.append(word)
return ' '.join(target)
Als Nächstes können wir dies für jede Quellenphrase in einer Datenmenge wiederholen und das vorhergesagte Ergebnis mit der erwarteten Zielphrase in Englisch vergleichen.
Wir können einige dieser Vergleiche auf dem Bildschirm ausdrucken, um eine Vorstellung davon zu bekommen, wie das Modell in der Praxis funktioniert.
Wir werden auch die BLEU-Werte berechnen, um eine quantitative Vorstellung davon zu erhalten, wie gut das Modell funktioniert hat.
Die Funktion evaluate_model () unten implementiert dies und ruft die obige Funktion predict_sequence () für jede Phrase in einem bereitgestellten Dataset auf.
# evaluiere skill vom Modell
def evaluate_model(model, tokenizer, sources, raw_dataset):
actual, predicted = list(), list()
for i, source in enumerate(sources):
# übersetzte encodeten Satz
source = source.reshape((1, source.shape[0]))
translation = predict_sequence(model, eng_tokenizer, source)
raw_target, raw_src = raw_dataset[i]
if i < 10:
print('src=[%s], target=[%s], predicted=[%s]' % (raw_src, raw_target, translation))
actual.append(raw_target.split())
predicted.append(translation.split())
# calculate BLEU score
print('BLEU-1: %f' % corpus_bleu(actual, predicted, weights=(1.0, 0, 0, 0)))
print('BLEU-2: %f' % corpus_bleu(actual, predicted, weights=(0.5, 0.5, 0, 0)))
print('BLEU-3: %f' % corpus_bleu(actual, predicted, weights=(0.3, 0.3, 0.3, 0)))
print('BLEU-4: %f' % corpus_bleu(actual, predicted, weights=(0.25, 0.25, 0.25, 0.25)))
Wir können all dies zusammenführen und das geladene Modell sowohl in den Trainings- als auch in den Testdatensätzen auswerten.
Die vollständige Code-Liste ist unten aufgeführt.
from nltk.translate.bleu_score import corpus_bleu
from numpy import argmax
# test on some training sequences
print('train')
evaluate_model(model, eng_tokenizer, trainX, train)
# test on some test sequences
print('test')
evaluate_model(model, eng_tokenizer, testX, test)
train
src=[ich hatte wirklich angst], target=[i was really scared], predicted=[i was really scared]
src=[wunsch mir gluck], target=[please wish me luck], predicted=[please wish me luck]
src=[ich muss ihr wiedergutmachung leisten], target=[i need to repay her], predicted=[i need to repay her]
src=[die suppe ist sehr hei], target=[the soups very hot], predicted=[the soups very hot]
src=[sie wissen um unsere plane], target=[they know our plans], predicted=[they know our plans]
src=[er ist stockbetrunken], target=[hes wasted], predicted=[hes wasted]
src=[lasst es mich erneut versuchen], target=[let me try it again], predicted=[let me try again]
src=[hier ist die rechnung], target=[here is the bill], predicted=[here is the bill]
src=[tom ist gut in mathe], target=[tom is good at math], predicted=[tom is good at math]
src=[tom war unverheiratet], target=[tom wasnt married], predicted=[tom wasnt married]
C:\Users\tareq.haschemi.INTERCEPT\AppData\Local\Continuum\anaconda3\lib\site-packages\nltk\translate\bleu_score.py:490: UserWarning:
Corpus/Sentence contains 0 counts of 3-gram overlaps.
BLEU scores might be undesirable; use SmoothingFunction().
warnings.warn(_msg)
BLEU-1: 0.077892
BLEU-2: 0.001718
BLEU-3: 0.021931
BLEU-4: 0.041453
test
src=[ihr schuldet mir], target=[you owe me], predicted=[you owe me]
src=[gib mir die waffe], target=[give me the gun], predicted=[give me the box]
src=[ich meine sie nicht], target=[i dont mean you], predicted=[i dont mean you]
src=[ich bin gescheitert], target=[i have failed], predicted=[ive failed]
src=[das ist nicht wahr], target=[that is not true], predicted=[thats isnt true true]
src=[uberbring diese nachricht], target=[deliver this message], predicted=[wow the news]
src=[dieser schreibtisch ist gut], target=[this desk is good], predicted=[this story cooks well]
src=[sie konnen sich ausruhen], target=[you can rest], predicted=[you can rest]
src=[tom trug schwarze jeans], target=[tom wore black jeans], predicted=[tom took a well]
src=[ist sie nett], target=[is she nice], predicted=[is she kind]
C:\Users\tareq.haschemi.INTERCEPT\AppData\Local\Continuum\anaconda3\lib\site-packages\nltk\translate\bleu_score.py:490: UserWarning:
Corpus/Sentence contains 0 counts of 2-gram overlaps.
BLEU scores might be undesirable; use SmoothingFunction().
warnings.warn(_msg)
BLEU-1: 0.079913
BLEU-2: 0.282689
BLEU-3: 0.468582
BLEU-4: 0.531685
BLEU = The Bilingual Evaluation Understudy Score
BLEU ist eine Metrik zur Bewertung eines generierten Satzes zu einem Referenzsatz. Eine perfekte Übereinstimmung führt zu einem Ergebnis von 1,0, während eine perfekte Abweichung zu einem Ergebnis von 0,0 führt.