神经机器翻译必读NLP教程:了解谷歌翻译的核心

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

如果你用一个人听得懂的语言与他交流,他会记在脑子里;如果你用他自己的语言与他交流,他会记在心里。

——纳尔逊 · 曼德拉

语言的魅力可以超越国界和文化。对个人来说,除了母语之外再学习一门语言无疑是一种很大的优势,但是掌握双语或者多语的道路是非常漫长的,毕竟语言之间许多细微的差别很难区分。但是,通过在线翻译服务,事情开始变得简单——这就离不开自然语言处理(NLP)技术了。

在本文中,我们将介绍使用Keras构建德语—英语翻译模型的步骤以及机器翻译系统的历史(本文假设你对RNN,LSTM和Keras较为熟悉)。

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

目 录

1. 机器翻译的历史回顾

2. 理解问题陈述

3. 序列预测导论

4. 使用Keras在Python中进行应用

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

1. 机器翻译的历史回顾

机器翻译(MT)的研究工作早在1950年就开始了,主要是在美国。这些早期系统依赖于庞大的双语词典、手工编码规则以及基于自然语言的通用原则。

1954年,IBM首次举办了机器翻译的公开演示。该系统的词汇量相当小,只有250个单词,只能将49个精心挑选的俄语句子翻译成英语。这个数字现在看来微不足道,但该系统被广泛认为是机器翻译进程中的一个重要里程碑。

随后,两大学派产生:

· 经验性的试错法,使用统计方法。

· 涉及基础语言研究的理论方法。

1964年,美国政府成立了自动语言处理咨询委员会(ALPAC),以评估机器翻译的进展。ALPAC在1966年11月发表了关于机器翻译状态的报告。以下是该报告的主要亮点:

· 它对机器翻译的可行性提出了严重质疑,并将其称为无可救药。

· 不鼓励资助机器翻译研究。

· 对于在该领域工作的研究人员来说,这份报告令人沮丧的。

· 大多数人离开了这个领域并开始了新的职业生涯。

这份报告推出之后,该领域停滞了很长时间。最后,1981年,加拿大部署了一个名为“METEO系统”的新系统,用于将法语发布的天气预报翻译成英语。这是一个非常成功的项目,一直运作到2001年。

世界上第一个网络翻译工具Babel Fish于1997年由AltaVista搜索引擎发布。

然后是我们现在熟悉的突破性技术——谷歌翻译。从那以后,它改变了我们用不同语言工作(甚至学习)的方式。

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

2. 理解问题陈述

让我们回到介绍部分中所讲的学习德语。但是,这次我们要让机器完成这项任务——目标是使用神经机器翻译系统将德语句子转换为英语句子。

我们会使用来自http://www.manythings.org/anki/的德语—英语语句数据。

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

3. 序列预测导论

Seq2seq模型被用于很多自然语言处理任务,例如文本总结、语音识别、DNA序列模型等等。我们的目标是将给定的句子从一种语言翻译成另一种语言。这里,输入和输出都是句子。换句话说,这些句子是进出模型的一系列单词。这是Seq2seq建模的基本思想,其中输入和输出都是序列。

典型的seq2seq模型有2个主要组件——编码器和解码器。这两个部分基本上是两个不同的递归神经网络(RNN)模型,组合成一个巨大的网络。

下面是一些重要的seq2seq建模用例(当然除了机器翻译):

· 语音识别

· 命名实体/主题提取以从文本正文中标识主要主题

· 关系分类,用于标记上述步骤中各种实体之间的关系

· Chatbot技能,具有会话和与客户互动的能力

· 文本摘要以生成大量文本的简明摘要

· 问答系统

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

4. 使用Keras在Python中进行应用

是时候开始实践了!没有比亲眼看到结果更好的学习体验了。我们将启动Python环境并直接开展测试。

输入需要的库

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)

读取数据到IDE

我们的数据是英语—德语句子对的文本文件。首先,使用下面定义的函数读取文件。

# 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

定义另一个函数将文本拆分为由’\ n’分隔的英—德对,然后将这些对分别分为英语句子和德语句子。

# 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)

实际数据包含超过150,000个句子对。但是,仅使用前50,000个句子对来减少模型的训练时间。可以根据系统的计算能力更改此数字。

deu_eng = deu_eng[:50000,:]

文本预处理

文本处理在任何项目中都是重要的一步,尤其是在自然语言处理中。我们使用的数据往往是非结构化的,因此在跳转到模型构建部分之前,需要注意某些事项。

(a)文本清理

先来看看数据,这将有助于我们决定采用哪些预处理步骤。

deu_eng

array([[‘Hi.’, ‘Hallo!’],

[‘Hi.’, ‘Grüß Gott!’],

[‘Run!’, ‘Lauf!’],

…,

[‘Mary has very long hair.’, ‘Maria hat sehr langes Haar.’],

[“Mary is Tom’s secretary.”, ‘Maria ist Toms Sekretärin.’],

[‘Mary is a married woman.’, ‘Maria ist eine verheiratete Frau.’]],

dtype='<U380′)

摆脱标点符号,将所有文本转换为小写。

# 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]]

deu_eng

array([[‘Hi’, ‘Hallo’],

[‘Hi’, ‘Grüß Gott’],

[‘Run’, ‘Lauf’],

…,

[‘Mary has very long hair’, ‘Maria hat sehr langes Haar’],

[‘Mary is Toms secretary’, ‘Maria ist Toms Sekretärin’],

[‘Mary is a married woman’, ‘Maria ist eine verheiratete Frau’]],

dtype='<U380′)

# 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

array([[‘hi’, ‘hallo’],

[‘hi’, ‘grüß gott’],

[‘run’, ‘lauf’],

…,

[‘mary has very long hair’, ‘maria hat sehr langes haar’],

[‘mary is toms secretary’, ‘maria ist toms sekretärin’],

[‘mary is a married woman’, ‘maria ist eine verheiratete frau’]],

dtype='<U380′)

(b)序列转换的文本

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的Tokenizer()类对文本数据进行矢量化。它会将句子变成整数序列。然后可以用零填充这些序列以使所有序列具有相同的长度。

请注意,我们将为德语和英语句子准备标记器:

# 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)

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)

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

建筑模型

现在将数据分别分为训练和测试集,用于模型训练和评估。

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)

是时候编码句子了。将德语句子编码为输入序列,将英语句子编码为目标序列。对列车和测试数据集进行此操作。

# 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])

现在到了令人兴奋的部分!

首先定义Seq2Seq模型架构:

· 对于编码器,将使用嵌入层和LSTM层

· 对于解码器,将使用另一个LSTM层,然后是密集层

模型构建

# 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

在此模型中使用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’作为损失函数。因为该函数允许我们按原样使用目标序列,而不是单热编码格式。使用如此庞大的词汇表对目标序列进行单热编码可能会占用系统的整个内存。

开始训练模型了!

将培训30个时期,批量为512,验证分为20%。 80%的数据将用于培训模型,其余数据用于评估模型。你可以更改并使用这些超参数。

我们还将使用ModelCheckpoint()函数来保存具有最低验证损失的模型。

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)

让我们比较训练损失和验证损失。

plt.plot(history.history[‘loss’])

plt.plot(history.history[‘val_loss’])

plt.legend([‘train’,’validation’])

plt.show()

验证损失在20个时期后停止下降。

最后,可以加载已保存的模型并对看不见的数据进行预测 -testX。

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

将预测转换为文本(英语):

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)

Seq2Seq模型做得不错。但有几个例子,它错过了理解关键词。例如,它将“im tired of boston”翻译成“im am boston”。

这些是在自然语言处理中定期面临的挑战,但并非是不可动摇的障碍。我们可以通过使用更多的培训数据和构建更好(或更复杂)的模型来缓解这些挑战。

《神经机器翻译必读NLP教程:了解谷歌翻译的核心》
《神经机器翻译必读NLP教程:了解谷歌翻译的核心》

总结

即使使用非常简单的Seq2Seq模型,结果也非常令人鼓舞。通过在更大的数据集上使用更复杂的encoder-decoder模型,可以轻松地改善性能。

另一个实验是在包含较长句子的数据集上尝试seq2seq方法。尝试的越多,对这个庞大而复杂的空间的了解就越多。

    原文作者:读芯术
    原文地址: https://zhuanlan.zhihu.com/p/56193913
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞