神经机器翻译机的编码实现

本节我们将亲手构建一个机器翻译程序。要实现机器翻译通常需要大规模的语料库,在此我们用了一个比较小巧、适合实验测试用的数据集——一个有135842条平行语料的法英语料库,并在实验中做了裁剪。之所以没有选用中英语料库,一是因为很难找到特别合适的平行语料库,二是因为只有在超大规模训练的条件下,才有可能达到比较好的翻译效果。我们此处以教学为主要目的,所以将在小规模语料上进行尝试。我们的编码器和解码器内部的RNN都含有两层,每层有32个神经元。在接下来的代码示范中,我们首先会引入数据并进行一些基本处理,生成训练集,然后分别定义编码器网络和解码器网络。在定义解码器网络的过程中,我们还会构建注意力网络。
首先,导入需要用到的Python程序包:

# 进行系统操作(如I/O、正则表达式)的包
from io import open
import unicodedata
import string
import re
import random

# PyTorch必备的包
import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F
import torch.utils.data as DataSet

# 绘图所用的包
import matplotlib.pyplot as plt
import numpy as np

# 判断本机是否有支持的GPU
use_cuda = torch.cuda.is_available()
# 即时绘图
%matplotlib inline

接下来,加载数据集:

# 读取平行语料库
# 英=法
lines = open('data/fra.txt', encoding = 'utf-8')
french = lines.read().strip().split('\n')
lines = open('data/eng.txt', encoding = 'utf-8')
english = lines.read().strip().split('\n')
print(len(french))
print(len(english))

# 定义两个特殊符号,分别对应句首和句尾
SOS_token = 0
EOS_token = 1


# 定义一个语言类,方便进行自动建立、词频统计等
# 在这个对象中,最重要的是两个字典:word2index和index2word
# 第一个字典是将word映射到索引,第二个是将索引映射到word
class Lang:
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: "SOS", 1: "EOS"}
        self.n_words = 2  #Count SOS and EOS

    def addSentence(self, sentence):
        # 在语言中添加一个新句子,句子是用空格隔开的一组单词
        # 将单词切分出来,分别进行处理
        for word in sentence.split(' '):
            self.addWord(word)

    def addWord(self, word):
        # 插入一个单词,如果单词已经在字典中,则更新字典中对应单词的频率
        # 同时建立反向索引,可以根据单词编号找到单词
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words += 1
        else:
            self.word2count[word] += 1


# 将Unicode编码转变为ASCII编码
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
    )

# 把输入的英文字符串转成小写
def normalizeEngString(s):
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
    return s

# 过滤输入的单词对,保证每句话的单词数不超过MAX_LENGTH
def filterPair(p):
    return len(p[0].split(' ')) < MAX_LENGTH and \
        len(p[1].split(' ')) < MAX_LENGTH

# 输入一个句子,输出一个单词对应的编码序列
def indexesFromSentence(lang, sentence):
    return [lang.word2index[word] for word in sentence.split(' ')]


# 和上面的函数功能类似,不同之处在于输出的序列等长=MAX_LENGTH
def indexFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    indexes.append(EOS_token)
    for i in range(MAX_LENGTH - len(indexes)):
        indexes.append(EOS_token)
    return(indexes)

# 从一个词对到下标
def indexFromPair(pair):
    input_variable = indexFromSentence(input_lang, pair[0])
    target_variable = indexFromSentence(output_lang, pair[1])
    return (input_variable, target_variable)

# 从一个列表到句子
def SentenceFromList(lang, lst):
    result = [lang.index2word[i] for i in lst if i != EOS_token]
    if lang.name == 'French':
        result = ' '.join(result)
    else:
        result = ' '.join(result)
    return(result)

# 计算准确率的函数
def rightness(predictions, labels):
    '''计算预测错误率的函数,其中predictions是模型给出的一组预测结果,batch_size行num_classes列的矩阵,labels是数据中的正确答案'''
    # 对于任意一行(一个样本)的输出值的第一个维度求最大,得到每一行最大元素的下标
    pred = torch.max(predictions.data, 1)[1]
    rights = pred.eq(labels.data).sum() # 将下标与labels中包含的类别进行比较,并累计正确标签的数量
    return rights, len(labels) # 返回正确的数量和这一次比较的元素数量

在这里,我们定义了一个语言类Lang,用以实现对英、法两种语言的共同处理功能,包括建立词典、编码与实际单词的转换等。另外,我们定义了两个特殊的单词“SOS”和“EOS”,分别表示句子的起始与终结。其他函数都是为了处理语言中的标点符号、过滤特殊字符等。接下来,我们便可以在硬盘上加载平行语料库了,进而训练网络,并在每一轮打印出训练结果。最终,我们会在测试集中随机选取一些句子进行翻译,测试翻译效果。之后我们将数据集[插图]切割成训练集、校验集和测试集。最后,我们建立了数据集、加载器以及采样器等处理数据的标准组件。具体代码如下所示:

# 处理数据形成训练数据
# 设置句子的最大长度
MAX_LENGTH = 10

# 对英文做标准化处理
pairs = [[normalizeEngString(fra), normalizeEngString(eng)] for fra, eng in zip(french, english)]

# 对句子对进行过滤,处理掉超过MAX_LENGTH长度的句子
input_lang = Lang('French')
output_lang = Lang('English')
pairs = [pair for pair in pairs if filterPair(pair)]
print('有效句子对:', len(pairs))

# 建立法文和英文两个字典
for pair in pairs:
    input_lang.addSentence(pair[0])
    output_lang.addSentence(pair[1])
print("总单词数:"print(input_lang.name, input_lang.n_words)
print(output_lang.name, output_lang.n_words)


# 形成训练集,首先打乱所有句子的顺序
random_idx = np.random.permutation(range(len(pairs)))
pairs = [pairs[i] for i in random_idx]

# 将语言转变为由单词的编码构成的序列
pairs = [indexFromPair(pair) for pair in pairs]

# 形成训练集、校验集和测试集
valid_size = len(pairs) // 10
if valid_size > 10000:
    valid_size = 10000
pp = pairs
pairs = pairs[: - valid_size]
valid_pairs = pp[-valid_size : -valid_size // 2]
test_pairs = pp[- valid_size // 2 :]

# 利用PyTorch的dataset和dataloader对象,将数据加载至加载器中,并自动分批

'''一批包含32条数据记录。这个数字越大,系统在训练的时候,每一个周期处理的数据就越多,处理就越快,但总的数据量会减少。'''
batch_size = 32

print('训练记录:', len(pairs))
print('校验记录:', len(valid_pairs))
print('测试记录:', len(test_pairs))

# 形成训练对列表,用于输入给train_dataset
pairs_X = [pair[0] for pair in pairs]
pairs_Y = [pair[1] for pair in pairs]
valid_X = [pair[0] for pair in valid_pairs]
valid_Y = [pair[1] for pair in valid_pairs]
test_X = [pair[0] for pair in test_pairs]
test_Y = [pair[1] for pair in test_pairs]


# 形成训练集
train_dataset = DataSet.TensorDataset(torch.LongTensor(pairs_X), torch.LongTensor(pairs_Y))
# 形成数据加载器
train_loader = DataSet.DataLoader(train_dataset, batch_size = batch_size, shuffle = True, num_workers=8)


# 校验数据
valid_dataset = DataSet.TensorDataset(torch.LongTensor(valid_X), torch.LongTensor(valid_Y))
valid_loader = DataSet.DataLoader(valid_dataset, batch_size = batch_size, shuffle = True, num_workers=8)

# 测试数据
test_dataset = DataSet.TensorDataset(torch.LongTensor(test_X), torch.LongTensor(test_Y))
test_loader = DataSet.DataLoader(test_dataset, batch_size = batch_size, shuffle = True, num_workers = 8)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值