热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【自然语言处理(NLP)】基于序列到序列的中英机器翻译

【自然语言处理(NLP)】基于序列到序列的中-英机器翻译作者简介:在校大学生一枚,华为云享专家,阿里云专家博主




【自然语言处理(NLP)】基于序列到序列的中-英机器翻译

在这里插入图片描述





作者简介:在校大学生一枚,华为云享专家,阿里云专家博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC)志愿者,以及编程爱好者,期待和大家一起学习,一起进步~
.
博客主页:ぃ灵彧が的学习日志
.
本文专栏:人工智能
.
专栏寄语:若你决定灿烂,山无遮,海无拦
.
在这里插入图片描述




文章目录


  • 【自然语言处理(NLP)】基于序列到序列的中-英机器翻译
  • 前言
    • (一)、任务描述
    • (二)、环境配置

  • 一、数据准备
    • (一)、数据集下载
    • (二)、构建双语句对的数据结构
    • (三)、创建词表
    • (四)、创建padding过的数据集

  • 二、网络构建
    • (一)、Encoder部分
    • (二)、Decoder部分

  • 三、模型训练
  • 四、使用模型进行机器翻译
  • 总结





前言

(一)、任务描述

本示例教程介绍如何使用飞桨完成一个机器翻译任务。

我们将会使用飞桨提供的LSTM的API,组建一个sequence to sequence with attention的机器翻译的模型,并在示例的数据集上完成从英文翻译成中文的机器翻译。




(二)、环境配置

本示例基于飞桨开源框架2.0版本。

import paddle
import paddle.nn.functional as F
import re
import numpy as np
print(paddle.__version__)
# cpu/gpu环境选择,在 paddle.set_device() 输入对应运行设备。
# device = paddle.set_device('gpu')

输出结果如下图1所示:
在这里插入图片描述




一、数据准备


(一)、数据集下载

我们将使用 http://www.manythings.org/anki/ 提供的中英文的英汉句对作为数据集,来完成本任务。该数据集含有23610个中英文双语的句对。

!wget -c https://www.manythings.org/anki/cmn-eng.zip && unzip cmn-eng.zip

!wc -l cmn.txt

输出结果如下图2所示:

在这里插入图片描述




(二)、构建双语句对的数据结构

接下来我们通过处理下载下来的双语句对的文本文件,将双语句对读入到python的数据结构中。这里做了如下的处理。


  • 对于英文,会把全部英文都变成小写,并只保留英文的单词。
  • 对于中文,为了简便起见,未做分词,按照字做了切分。
  • 为了后续的程序运行的更快,我们通过限制句子长度,和只保留部分英文单词开头的句子的方式,得到了一个较小的数据集。这样得到了一个有5508个句对的数据集。


MAX_LEN = 10

lines = open('cmn.txt', encoding='utf-8').read().strip().split('\n')
words_re = re.compile(r'\w+')
pairs = []
for l in lines:
en_sent, cn_sent, _ = l.split('\t')
pairs.append((words_re.findall(en_sent.lower()), list(cn_sent)))
# create a smaller dataset to make the demo process faster
filtered_pairs = []
for x in pairs:
if len(x[0]) < MAX_LEN and len(x[1]) < MAX_LEN and \
x[0][0] in (&#39;i&#39;, &#39;you&#39;, &#39;he&#39;, &#39;she&#39;, &#39;we&#39;, &#39;they&#39;):
filtered_pairs.append(x)
print(len(filtered_pairs))
for x in filtered_pairs[:10]: print(x)

输出结果如下图3所示&#xff1a;

在这里插入图片描述




(三)、创建词表

接下来我们分别创建中英文的词表&#xff0c;这两份词表会用来将英文和中文的句子转换为词的ID构成的序列。词表中还加入了如下三个特殊的词&#xff1a; - : 用来对较短的句子进行填充。 - : “begin of sentence”&#xff0c; 表示句子的开始的特殊词。 - : “end of sentence”&#xff0c; 表示句子的结束的特殊词。

Note: 在实际的任务中&#xff0c;可能还需要通过&#xff08;或者&#xff09;特殊词来表示未在词表中出现的词。



en_vocab &#61; {}
cn_vocab &#61; {}
# create special token for pad, begin of sentence, end of sentence
en_vocab[&#39;&#39;], en_vocab[&#39;&#39;], en_vocab[&#39;&#39;] &#61; 0, 1, 2
cn_vocab[&#39;&#39;], cn_vocab[&#39;&#39;], cn_vocab[&#39;&#39;] &#61; 0, 1, 2
en_idx, cn_idx &#61; 3, 3
for en, cn in filtered_pairs:
for w in en:
if w not in en_vocab:
en_vocab[w] &#61; en_idx
en_idx &#43;&#61; 1
for w in cn:
if w not in cn_vocab:
cn_vocab[w] &#61; cn_idx
cn_idx &#43;&#61; 1
print(len(list(en_vocab)))
print(len(list(cn_vocab)))

输出结果如下图4所示&#xff1a;

在这里插入图片描述




(四)、创建padding过的数据集

接下来根据词表&#xff0c;我们将会创建一份实际的用于训练的用numpy array组织起来的数据集。 - 所有的句子都通过补充成为了长度相同的句子。 - 对于英文句子&#xff08;源语言&#xff09;&#xff0c;我们将其反转了过来&#xff0c;这会带来更好的翻译的效果。 - 所创建的padded_cn_label_sents是训练过程中的预测的目标&#xff0c;即&#xff0c;每个中文的当前词去预测下一个词是什么词。



padded_en_sents &#61; []
padded_cn_sents &#61; []
padded_cn_label_sents &#61; []
for en, cn in filtered_pairs:
# reverse source sentence
padded_en_sent &#61; en &#43; [&#39;&#39;] &#43; [&#39;&#39;] * (MAX_LEN - len(en))
padded_en_sent.reverse()
padded_cn_sent &#61; [&#39;&#39;] &#43; cn &#43; [&#39;&#39;] &#43; [&#39;&#39;] * (MAX_LEN - len(cn))
padded_cn_label_sent &#61; cn &#43; [&#39;&#39;] &#43; [&#39;&#39;] * (MAX_LEN - len(cn) &#43; 1)
padded_en_sents.append([en_vocab[w] for w in padded_en_sent])
padded_cn_sents.append([cn_vocab[w] for w in padded_cn_sent])
padded_cn_label_sents.append([cn_vocab[w] for w in padded_cn_label_sent])
train_en_sents &#61; np.array(padded_en_sents)
train_cn_sents &#61; np.array(padded_cn_sents)
train_cn_label_sents &#61; np.array(padded_cn_label_sents)
print(train_en_sents.shape)
print(train_cn_sents.shape)
print(train_cn_label_sents.shape)

输出结果如下图5所示&#xff1a;

在这里插入图片描述




二、网络构建

我们将会创建一个Encoder-AttentionDecoder架构的模型结构用来完成机器翻译任务。 首先我们将设置一些必要的网络结构中用到的参数。



embedding_size &#61; 128
hidden_size &#61; 256
num_encoder_lstm_layers &#61; 1
en_vocab_size &#61; len(list(en_vocab))
cn_vocab_size &#61; len(list(cn_vocab))
epochs &#61; 20
batch_size &#61; 16



(一)、Encoder部分

在编码器的部分&#xff0c;我们通过查找完Embedding之后接一个LSTM的方式构建一个对源语言编码的网络。飞桨的RNN系列的API&#xff0c;除了LSTM之外&#xff0c;还提供了SimleRNN, GRU供使用&#xff0c;同时&#xff0c;还可以使用反向RNN&#xff0c;双向RNN&#xff0c;多层RNN等形式。也可以通过dropout参数设置是否对多层RNN的中间层进行dropout处理&#xff0c;来防止过拟合。

除了使用序列到序列的RNN操作之外&#xff0c;也可以通过SimpleRNN, GRUCell, LSTMCell等API更灵活的创建单步的RNN计算&#xff0c;甚至通过继承RNNCellBase来实现自己的RNN计算单元。



# encoder: simply learn representation of source sentence
class Encoder(paddle.nn.Layer):
def __init__(self):
super(Encoder, self).__init__()
self.emb &#61; paddle.nn.Embedding(en_vocab_size, embedding_size,)
self.lstm &#61; paddle.nn.LSTM(input_size&#61;embedding_size,
hidden_size&#61;hidden_size,
num_layers&#61;num_encoder_lstm_layers)
def forward(self, x):
x &#61; self.emb(x)
x, (_, _) &#61; self.lstm(x)
return x



(二)、Decoder部分

在解码器部分&#xff0c;我们通过一个去除注意力机制的LSTM来完成解码&#xff0c;也就是一个最简单的encoder-decoder模型架构。


  • 单步的LSTM&#xff1a;在解码器的实现的部分&#xff0c;我们同样使用LSTM&#xff0c;与Encoder部分不同的是&#xff0c;下面的代码&#xff0c;每次只让LSTM往前计算一次。整体的recurrent部分&#xff0c;是在训练循环内完成的。

  • 对于第一次接触这样的网络结构来说&#xff0c;下面的代码在理解起来可能稍微有些复杂&#xff0c;你可以通过插入打印每个tensor在不同步骤时的形状的方式来更好的理解。



# only move one step of LSTM,
# the recurrent loop is implemented inside training loop
class Decoder(paddle.nn.Layer):
def __init__(self):
super(Decoder, self).__init__()
self.emb &#61; paddle.nn.Embedding(cn_vocab_size, embedding_size)
self.lstm &#61; paddle.nn.LSTM(input_size&#61;embedding_size &#43; hidden_size,
hidden_size&#61;hidden_size)
# for computing output logits
self.outlinear &#61;paddle.nn.Linear(hidden_size, cn_vocab_size)
def forward(self, x, previous_hidden, previous_cell, encoder_outputs):
x &#61; self.emb(x)
#encoder_outputs 16*11*256
context_vector &#61; paddle.sum(encoder_outputs, 1)
#context_vector 16*1*256
context_vector &#61; paddle.unsqueeze(context_vector, 1)
#lstm_input 16*1*384
lstm_input &#61; paddle.concat((x, context_vector), axis&#61;-1)

# LSTM requirement to previous hidden/state:
# (number_of_layers * direction, batch, hidden)
previous_hidden &#61; paddle.transpose(previous_hidden, [1, 0, 2])
previous_cell &#61; paddle.transpose(previous_cell, [1, 0, 2])
x, (hidden, cell) &#61; self.lstm(lstm_input, (previous_hidden, previous_cell))
# change the return to (batch, number_of_layers * direction, hidden)
hidden &#61; paddle.transpose(hidden, [1, 0, 2])
cell &#61; paddle.transpose(cell, [1, 0, 2])
output &#61; self.outlinear(hidden)
output &#61; paddle.squeeze(output)
return output, (hidden, cell)



三、模型训练

接下来我们开始训练模型。


  • 在每个epoch开始之前&#xff0c;我们对训练数据进行了随机打乱。

  • 我们通过多次调用atten_decoder&#xff0c;在这里实现了解码时的recurrent循环。

  • teacher forcing策略: 在每次解码下一个词时&#xff0c;我们给定了训练数据当中的真实词作为了预测下一个词时的输入。相应的&#xff0c;你也可以尝试用模型预测的结果作为下一个词的输入。&#xff08;或者混合使用&#xff09;



encoder &#61; Encoder()
decoder &#61; Decoder()
opt &#61; paddle.optimizer.Adam(learning_rate&#61;0.001,
parameters&#61;encoder.parameters()&#43;decoder.parameters())
for epoch in range(epochs):
print("epoch:{}".format(epoch))
# shuffle training data
perm &#61; np.random.permutation(len(train_en_sents))
train_en_sents_shuffled &#61; train_en_sents[perm]
train_cn_sents_shuffled &#61; train_cn_sents[perm]
train_cn_label_sents_shuffled &#61; train_cn_label_sents[perm]
for iteration in range(train_en_sents_shuffled.shape[0] // batch_size):
x_data &#61; train_en_sents_shuffled[(batch_size*iteration):(batch_size*(iteration&#43;1))]
sent &#61; paddle.to_tensor(x_data)
en_repr &#61; encoder(sent)
x_cn_data &#61; train_cn_sents_shuffled[(batch_size*iteration):(batch_size*(iteration&#43;1))]
x_cn_label_data &#61; train_cn_label_sents_shuffled[(batch_size*iteration):(batch_size*(iteration&#43;1))]
# shape: (batch, num_layer(&#61;1 here) * num_of_direction(&#61;1 here), hidden_size)
hidden &#61; paddle.zeros([batch_size, 1, hidden_size])
cell &#61; paddle.zeros([batch_size, 1, hidden_size])
loss &#61; paddle.zeros([1])
# the decoder recurrent loop mentioned above
for i in range(MAX_LEN &#43; 2):
cn_word &#61; paddle.to_tensor(x_cn_data[:,i:i&#43;1])
cn_word_label &#61; paddle.to_tensor(x_cn_label_data[:,i])
logits, (hidden, cell) &#61; decoder(cn_word, hidden, cell, en_repr)
step_loss &#61; F.cross_entropy(logits, cn_word_label)
loss &#43;&#61; step_loss
loss &#61; loss / (MAX_LEN &#43; 2)
if(iteration % 200 &#61;&#61; 0):
print("iter {}, loss:{}".format(iteration, loss.numpy()))
loss.backward()
opt.step()
opt.clear_grad()

输出结果如下图6所示&#xff1a;

在这里插入图片描述




四、使用模型进行机器翻译

根据你所使用的计算设备的不同&#xff0c;上面的训练过程可能需要不等的时间。&#xff08;在一台Mac笔记本上&#xff0c;大约耗时15~20分钟&#xff09; 完成上面的模型训练之后&#xff0c;我们可以得到一个能够从英文翻译成中文的机器翻译模型。接下来我们通过一个greedy search来实现使用该模型完成实际的机器翻译。&#xff08;实际的任务中&#xff0c;你可能需要用beam search算法来提升效果&#xff09;



encoder.eval()
decoder.eval()
num_of_exampels_to_evaluate &#61; 10
indices &#61; np.random.choice(len(train_en_sents), num_of_exampels_to_evaluate, replace&#61;False)
x_data &#61; train_en_sents[indices]
sent &#61; paddle.to_tensor(x_data)
en_repr &#61; encoder(sent)
word &#61; np.array(
[[cn_vocab[&#39;&#39;]]] * num_of_exampels_to_evaluate
)
word &#61; paddle.to_tensor(word)
hidden &#61; paddle.zeros([num_of_exampels_to_evaluate, 1, hidden_size])
cell &#61; paddle.zeros([num_of_exampels_to_evaluate, 1, hidden_size])
decoded_sent &#61; []
for i in range(MAX_LEN &#43; 2):
logits, (hidden, cell) &#61; decoder(word, hidden, cell, en_repr)
word &#61; paddle.argmax(logits, axis&#61;1)
decoded_sent.append(word.numpy())
word &#61; paddle.unsqueeze(word, axis&#61;-1)
results &#61; np.stack(decoded_sent, axis&#61;1)
for i in range(num_of_exampels_to_evaluate):
en_input &#61; " ".join(filtered_pairs[indices[i]][0])
ground_truth_translate &#61; "".join(filtered_pairs[indices[i]][1])
model_translate &#61; ""
for k in results[i]:
w &#61; list(cn_vocab)[k]
if w !&#61; &#39;&#39; and w !&#61; &#39;&#39;:
model_translate &#43;&#61; w
print(en_input)
print("true: {}".format(ground_truth_translate))
print("pred: {}".format(model_translate))

输出结果如下图7所示&#xff1a;

在这里插入图片描述




总结

本系列文章内容为根据清华社出版的《自然语言处理实践》所作的相关笔记和感悟&#xff0c;其中代码均为基于百度飞桨开发&#xff0c;若有任何侵权和不妥之处&#xff0c;请私信于我&#xff0c;定积极配合处理&#xff0c;看到必回&#xff01;&#xff01;&#xff01;

最后&#xff0c;引用本次活动的一句话&#xff0c;来作为文章的结语&#xff5e;(&#xffe3;▽&#xffe3;&#xff5e;)~&#xff1a;

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。

ps&#xff1a;更多精彩内容还请进入本文专栏&#xff1a;人工智能&#xff0c;进行查看&#xff0c;欢迎大家支持与指教啊&#xff5e;(&#xffe3;▽&#xffe3;&#xff5e;)~

在这里插入图片描述







推荐阅读
  • 本文讨论了在iOS平台中的Metal框架中,对于if语句中的判断条件的限制和处理方式。作者提到了在Metal shader中,判断条件不能写得太长太复杂,否则可能导致程序停留或没有响应。作者还分享了自己的经验,建议在CPU端进行处理,以避免出现问题。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
author-avatar
幸福的小兔子3
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有