美文网首页机器学习实战
构建你的第一个神经机器翻译器

构建你的第一个神经机器翻译器

作者: 松鼠的读书笔记 | 来源:发表于2019-02-03 22:24 被阅读133次

今天翻译一篇来自Analytics Vidhya的深度好文:A Must-Read NLP Tutorial on Neural Machine Translation – The Technique Powering Google Translate


1 问题描述

目标:德语句子翻译为英语句子

数据集:德语-英语句子对 ,下载地址:http://www.manythings.org/anki/

德语翻译英语 模型:使用seq2seq model
seq2seq model

2 Python实现

2.1 加载相关包

import string
import re
from numpy import array, argmax, random, take
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense, LSTM, Embedding, RepeatVector
from keras.preprocessing.text import Tokenizer
from keras.callbacks import ModelCheckpoint
from keras.preprocessing.sequence import pad_sequences
from keras.models import load_model
from keras import optimizers
import matplotlib.pyplot as plt
% matplotlib inline
pd.set_option('display.max_colwidth', 200)

2.2 加载数据集

# function to read raw text file
def read_text(filename):
        # open the file
        file = open(filename, mode='rt', encoding='utf-8')
        
        # read all text
        text = file.read()
        file.close()
        return text

# split a text into sentences
def to_lines(text):
      sents = text.strip().split('\n')
      sents = [i.split('\t') for i in sents]
      return sents

data = read_text("deu.txt")
deu_eng = to_lines(data)
deu_eng = array(deu_eng)

deu_eng = deu_eng[:50000,:]

数据集包含超过150,000对句子对,为了减少模型训练的时间,这次仅取前面的50,000对数据。

2.3 文本预处理

2.3.1 文本清洗

首先来看看数据长啥样。

deu_eng
原数据

下面我们去掉标点符号,并转化为小写。

# Remove punctuation
deu_eng[:,0] = [s.translate(str.maketrans('', '', string.punctuation)) for s in deu_eng[:,0]]
deu_eng[:,1] = [s.translate(str.maketrans('', '', string.punctuation)) for s in deu_eng[:,1]]

# convert text to lowercase
for i in range(len(deu_eng)):
    deu_eng[i,0] = deu_eng[i,0].lower()
    deu_eng[i,1] = deu_eng[i,1].lower()

deu_eng
清洗后数据

2.3.2 文本转序列

Seq2Seq模型要求我们把输入和输出句子转换为固定长度的整数序列。在此之前,让我们先看看句子的长度。

# empty lists
eng_l = []
deu_l = []

# populate the lists with sentence lengths
for i in deu_eng[:,0]:
      eng_l.append(len(i.split()))

for i in deu_eng[:,1]:
      deu_l.append(len(i.split()))

length_df = pd.DataFrame({'eng':eng_l, 'deu':deu_l})

length_df.hist(bins = 30)
plt.show()
句子长度直方图

从上图可以看出,德语句子的最大长度为11,英语句子的最大长度是8。接下来,我们分别对德语句子和英语句子做下面两个操作:

  • 使用 Keras’s Tokenizer() 来向量化我们的文本数据,它会把我们的句子转换为整数序列
  • 对上述序列做zero-padding,使句子具有相同的长度
# function to build a tokenizer
def tokenization(lines):
      tokenizer = Tokenizer()
      tokenizer.fit_on_texts(lines)
      return tokenizer

# prepare english tokenizer
eng_tokenizer = tokenization(deu_eng[:, 0])
eng_vocab_size = len(eng_tokenizer.word_index) + 1

eng_length = 8
print('English Vocabulary Size: %d' % eng_vocab_size)
# output: English Vocabulary Size: 6453

# prepare Deutch tokenizer
deu_tokenizer = tokenization(deu_eng[:, 1])
deu_vocab_size = len(deu_tokenizer.word_index) + 1

deu_length = 8
print('Deutch Vocabulary Size: %d' % deu_vocab_size)
# output: Deutch Vocabulary Size: 10998

# encode and pad sequences
def encode_sequences(tokenizer, length, lines):
         # integer encode sequences
         seq = tokenizer.texts_to_sequences(lines)
         # pad sequences with 0 values
         seq = pad_sequences(seq, maxlen=length, padding='post')
         return seq

2.4 建模

2.4.1 划分训练集和测试集

from sklearn.model_selection import train_test_split

# split data into train and test set
train, test = train_test_split(deu_eng, test_size=0.2, random_state = 12)

2.4.2 数据格式转换

把德语句子编码成输入序列格式,英语句子编码成输出序列格式

# prepare training data
trainX = encode_sequences(deu_tokenizer, deu_length, train[:, 1])
trainY = encode_sequences(eng_tokenizer, eng_length, train[:, 0])

# prepare validation data
testX = encode_sequences(deu_tokenizer, deu_length, test[:, 1])
testY = encode_sequences(eng_tokenizer, eng_length, test[:, 0])

2.4.3 定义Seq2Seq模型框架

接下来,就是激动人心的时刻!

  • encoder部分,我们将使用一个embedding layer和一个LSTM layer
  • decoder部分,我们使用另一个LSTM layer,然后再用一个dense layer
模型框架
# build NMT model
def define_model(in_vocab,out_vocab, in_timesteps,out_timesteps,units):
      model = Sequential()
      model.add(Embedding(in_vocab, units, input_length=in_timesteps, mask_zero=True))
      model.add(LSTM(units))
      model.add(RepeatVector(out_timesteps))
      model.add(LSTM(units, return_sequences=True))
      model.add(Dense(out_vocab, activation='softmax'))
      return model

对于RNN模型来说,RMSprop optimizer是一个不错的选择,因此我们将使用RMSprop。

# model compilation
model = define_model(deu_vocab_size, eng_vocab_size, deu_length, eng_length, 512)

rms = optimizers.RMSprop(lr=0.001)
model.compile(optimizer=rms, loss='sparse_categorical_crossentropy')

注意到,我们使用了 ‘sparse_categorical_crossentropy‘ 作为损失函数,这个函数计算预测值与真值的多类交叉熵(输入值为二值矩阵,而不是向量),适用于稀疏情况。

2.5 模型训练

训练30个epochs, batch size设为512,vadidation split 为20%。即80%的数据用来训练模型,20%的数据用于评估模型。

同时,我们还将使用ModelCheckpoint() 函数来保存最优的模型(基于early stopping)

filename = 'model.h1.24_jan_19'
checkpoint = ModelCheckpoint(filename, monitor='val_loss', verbose=1, save_best_only=True, mode='min')

# train model
history = model.fit(trainX, trainY.reshape(trainY.shape[0], trainY.shape[1], 1),
                    epochs=30, batch_size=512, validation_split = 0.2,callbacks=[checkpoint], 
                    verbose=1)

让我们来看看训练集和验证集的loss。

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.legend(['train','validation'])
plt.show()
loss

可以看到,validation loss 在20个epochs后就停止下降了。

2.6 预测

好了,现在我们已经训练好了模型,让我们用这个模型来做一下预测,看看效果如何。

model = load_model('model.h1.24_jan_19')
preds = model.predict_classes(testX.reshape((testX.shape[0],testX.shape[1])))

这个预测输出的是整数序列,我们需要把这些整数还原为其对应的英语单词。下面定义一个函数来帮我们完成这个任务:

def get_word(n, tokenizer):
      for word, index in tokenizer.word_index.items():
          if index == n:
              return word
      return None

#Convert predictions into text (English)
preds_text = []
for i in preds:
       temp = []
       for j in range(len(i)):
            t = get_word(i[j], eng_tokenizer)
            if j > 0:
                if (t == get_word(i[j-1], eng_tokenizer)) or (t == None):
                     temp.append('')
                else:
                     temp.append(t)
            else:
                   if(t == None):
                          temp.append('')
                   else:
                          temp.append(t) 

       preds_text.append(' '.join(temp))

把测试集的真实英语句子与预测句子放到一起进行对比看看:

pred_df = pd.DataFrame({'actual' : test[:,0], 'predicted' : preds_text})

# print 15 rows randomly
pred_df.sample(15)
actual vs predicted

可以看到,仅仅是这样一个简单的模型,它已经具备了翻译的能力,表现得还是不错的。不过也有一些句子它错失了关键词,比如它把“im tired of boston” 翻译为 “im am boston”。

我们可以通过使用更多的训练数据或者建立更好更复杂的模型,来优化这个翻译器。


完整代码戳这里 Github repo

都到这了,不点个赞再走么(✧◡✧)

相关文章

网友评论

    本文标题:构建你的第一个神经机器翻译器

    本文链接:https://www.haomeiwen.com/subject/xlrusqtx.html