当前位置:   article > 正文

机器翻译——英译中_基于bert的中英文翻译模型

基于bert的中英文翻译模型

1. 前言

本文使用飞桨(PaddlePaddle)训练机器翻译模型,实现将英文翻译成中文的神经网络翻译机。
本人全部文章请参见:博客文章导航目录
本文归属于:自然语言处理系列
本系列实践代码请参见:我的GitHub
前文:BERT与ERNIE

2. 训练机器翻译模型

本文使用Sequence-to-Sequence模型原理一文中所述Seq2Seq模型实现机器翻译。其中RNN使用长短期记忆网络(LSTM)原理与实战一文中所述LSTM模型结构,同时结合注意力机制(Attention):Seq2Seq模型的改进一文中所述注意力机制改进Seq2Seq模型,提升机器翻译效果。
本文代码部分参考了PaddlePaddle官方应用实践教程,对代码结构进行了修改,并调整了部分处理逻辑,使得该深度学习实践与本系列讲解的方法原理一致。

2.1 数据处理

本文讲解的机器翻译实践使用http://www.manythings.org/anki/提供的中英文句子对作为训练数据集。对于英文,先将其全部变成小写,然后仅保留英文单词及句子末尾标点符号。对于中文,直接按字进行切分,不采用分词操作。同时设定MAX_LEN = 20,得到一个包含29077个中英句子对的训练数据集。
对数据集进行进一步处理,生成中英文词表。在英文句子后添加<eos>符号,在中文句子前后分别添加<bos><eos>符号,
并使用<pad>符号将短句子填充成统一长度。最后根据中文句子创建Decoder每个时刻标签对应的句子,并将训练句子转换成词的ID构成的序列。具体代码如下:

# -*- coding: utf-8 -*-
# @Time    : 2021/8/1 19:16
# @Author  : He Ruizhi
# @File    : machine_translation.py
# @Software: PyCharm

import paddle
import paddle.nn.functional as F
import re
import numpy as np
import time
import warnings
warnings.filterwarnings('ignore')
print(paddle.__version__)  # 2.1.0

# 设置训练句子最大长度,用于筛选数据集中的部分数据
MAX_LEN = 20


def create_dataset(file_path):
    """
    构建机器翻译训练数据集

    :param file_path: 训练数据路径
    :return:
    train_en_sents:由数字ID组成的英文句子
    train_cn_sents:由数字ID组成的中文句子
    train_cn_label_sents:由数字ID组成的中文词汇标签
    en_vocab:英文词表
    cn_vocab:中文词表
    """
    with open(file_path, 'rt', encoding='utf-8') as f:
        lines = f.read().strip().split('\n')
    # 设置正则匹配模板,用于从英文句子中提取单词
    words_re = re.compile(r'\w+')

    # 将训练数据文件中的中文和英文句子全部提取出来
    pairs = []
    for line in lines:
        en_sent, cn_sent, _ = line.split('\t')
        pairs.append((words_re.findall(en_sent.lower())+[en_sent[-1]], list(cn_sent)))

    # 从原始训练数据中筛选出一部分数据用来训练模型
    # 实际训练神经网络翻译机时数据量肯定是越多越好,不过本文只选取长度小于10的句子
    filtered_pairs = []
    for pair in pairs:
        if len(pair[0]) < MAX_LEN and len(pair[1]) < MAX_LEN:
            filtered_pairs.append(pair)

    # 创建中英文词表,将中文和因为句子转换成词的ID构成的序列
    # 此外须在词表中添加三个特殊词:<pad>用来对短句子进行填充;<bos>表示解码时的起始符号;<eos>表示解码时的终止符号
    # 在实际任务中,一般还会需要指定<unk>符号表示在词表中未出现过的词,并在构造训练集时有意识地添加<unk>符号,使模型能够处理相应情况
    en_vocab = {}
    cn_vocab = {}
    en_vocab['<pad>'], en_vocab['<bos>'], en_vocab['<eos>'] = 0, 1, 2
    cn_vocab['<pad>'], cn_vocab['<bos>'], cn_vocab['<eos>'] = 0, 1, 2
    en_idx, cn_idx = 3, 3
    for en, cn in filtered_pairs:
        for w in en:
            if w not in en_vocab:
                en_vocab[w] = en_idx
                en_idx += 1
        for w in cn:
            if w not in cn_vocab:
                cn_vocab[w] = cn_idx
                cn_idx += 1

    # 使用<pad>符号将短句子填充成长度一致的句子,便于使用批量数据训练模型
    # 同时根据词表,创建一份实际的用于训练的用numpy array组织起来的数据集
    padded_en_sents = []
    padded_cn_sents = []
    # 训练过程中的预测的目标,即每个中文的当前词去预测下一个词是什么词
    padded_cn_label_sents = []
    for en, cn in filtered_pairs:
        padded_en_sent = en + ['<eos>'] + ['<pad>'] * (MAX_LEN - len(en))
        padded_cn_sent = ['<bos>'] + cn + ['<eos>'] + ['<pad>'] * (MAX_LEN - len(cn))
        padded_cn_label_sent = cn + ['<eos>'] + ['<pad>'] * (MAX_LEN - len(cn) + 1)

        # 根据词表,将相应的单词转换成数字ID
        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])

    # 将训练数据用numpy array组织起来
    train_en_sents = np.array(padded_en_sents, dtype='int64')
    train_cn_sents = np.array(padded_cn_sents, dtype='int64')
    train_cn_label_sents = np.array(padded_cn_label_sents, dtype='int64')

    return train_en_sents, train_cn_sents, train_cn_label_sents, en_vocab, cn_vocab
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

2.2 网络构建

本文讲解的机器翻译实践采用Encoder-AttentionDecoder架构,Encoder采用LSTM模型,Decoder采用Attention+LSTM模型。虽然在注意力机制(Attention):Seq2Seq模型的改进一文中为了讲解简便,使用的是SimpleRNN+Attention,而不是LSTM+Attention。但是使用不同的RNN结构,本质均为Decoder RNN初始状态等于Encoder RNN最后时刻状态,Decoder RNN t t t时刻输入为 x t ′ x_t^\prime xt和Context Vector c t − 1 c_{t-1} ct1拼接而成的向量。

2.2.1 构建Encoder

在Encoder部分,输入英文句子,通过查找完Embedding之后接一个LSTM的方式构建一个对英文句子编码的网络。具体代码如下:

class Encoder(paddle.nn.Layer):
    """Seq2Seq模型Encoder,采用LSTM结构"""
    def __init__(self, en_vocab_size, en_embedding_dim, lstm_hidden_size, lstm_num_layers):
        super(Encoder, self).__init__()
        self.emb = paddle.nn.Embedding(num_embeddings=en_vocab_size, embedding_dim=en_embedding_dim)
        self.lstm = paddle.nn.LSTM(input_size=en_embedding_dim, hidden_size=lstm_hidden_size,
                                   num_layers=lstm_num_layers)

    def forward(self, x):
        x = self.emb(x)
        x, (h, c) = self.lstm(x)
        return x, h, c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.2.2 构建AttentionDecoder

在Decoder部分,通过一个带有Attention的LSTM来完成解码。
Decoder的forward函数每次调用只让LSTM往前计算一次,即对只生成当前时刻的输出,而不是生成整个输出序列。整体的recurrent部分,是在训练循环内完成的。
本文讲解的机器翻译实践Attention中权重计算采用注意力机制(Attention):Seq2Seq模型的改进一文3.2部分所述方法一。即将Decoder t t t时刻输出状态向量 s t s_t st分别与Encoder各个时刻状态向量 h 1 h_1 h1 h m h_m hm拼接,依次通过一个tanh函数激活的全连接层,和一个线性加和层(Linear Layer)。
具体代码如下:

class AttentionDecoder(paddle.nn.Layer):
    """Seq2Seq模型解码器,采用带有注意力机制的LSTM结构"""
    def __init__(self, cn_vocab_size, cn_embedding_dim, lstm_hidden_size, v_dim):
        super(AttentionDecoder, self).__init__()
        self.emb = paddle.nn.Embedding(num_embeddings=cn_vocab_size, embedding_dim=cn_embedding_dim)
        # lstm层输入为x'_t和Context Vector拼接而成的向量,Context Vector的维度与lstm_hidden_size一致
        self.lstm = paddle.nn.LSTM(input_size=cn_embedding_dim + lstm_hidden_size,
                                   hidden_size=lstm_hidden_size)

        # 用于计算Attention权重
        self.attention_linear1 = paddle.nn.Linear(lstm_hidden_size * 2, v_dim)
        self.attention_linear2 = paddle.nn.Linear(v_dim, 1)

        # 用于根据lstm状态计算输出
        self.out_linear = paddle.nn.Linear(lstm_hidden_size, cn_vocab_size)

    # forward函数每次往前计算一次。整体的recurrent部分,是在训练循环内完成的。
    def forward(self, x, previous_hidden, previous_cell, encoder_outputs):
        x = self.emb(x)

        # 对previous_hidden进行数据重排
        hidden_transpose = paddle.transpose(previous_hidden, [1, 0, 2])

        # attention输入:Decoder当前状态和Encoder所有状态
        # 总共需计算encoder_outputs.shape[1]个权重(这是因为会在输入句子后面加上一个<eos>符号)
        attention_inputs = paddle.concat(
            (encoder_outputs, paddle.tile(hidden_transpose, repeat_times=[1, encoder_outputs.shape[1], 1])), axis=-1
        )

        attention_hidden = self.attention_linear1(attention_inputs)
        attention_hidden = F.tanh(attention_hidden)
        attention_logits = self.attention_linear2(attention_hidden)
        attention_logits = paddle.squeeze(attention_logits)

        # 计算得到所有encoder_outputs.shape[1]个权重
        attention_weights = F.softmax(attention_logits)
        attention_weights = paddle.expand_as(paddle.unsqueeze(attention_weights, -1),
                                             encoder_outputs)

        # 计算Context Vector
        context_vector = paddle.multiply(encoder_outputs, attention_weights)
        context_vector = paddle.sum(context_vector, 1)
        context_vector = paddle.unsqueeze(context_vector, 1)

        lstm_input = paddle.concat((x, context_vector), axis=-1)

        x, (hidden, cell) = self.lstm(lstm_input, (previous_hidden, previous_cell))

        output = self.out_linear(hidden)
        output = paddle.squeeze(output, axis=0)
        return output, (hidden, cell)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

2.3 训练模型

定义trian函数,创建EncoderAttentionDecoder对象,创建Adam优化器,设定学习率和优化参数。
在每个epoch内,随机打乱训练数据,在每个batch内,通过多次调用atten_decoder,实现解码时的recurrent循环。在每次解码下一个词时,给定了训练数据当中的真实词作为了预测下一个词时的输入。
具体代码如下:

def train(train_en_sents, train_cn_sents, train_cn_label_sents, epochs, learning_rate, batch_size,
          en_vocab_size, en_embedding_dim, lstm_hidden_size, lstm_num_layers,
          cn_vocab_size, cn_embedding_dim, v_dim):
    encoder = Encoder(en_vocab_size, en_embedding_dim, lstm_hidden_size, lstm_num_layers)
    atten_decoder = AttentionDecoder(cn_vocab_size, cn_embedding_dim, lstm_hidden_size, v_dim)
    opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=encoder.parameters()+atten_decoder.parameters())

    for epoch in range(epochs):
        print("epoch:{}".format(epoch))

        # 将训练数据集打乱
        perm = np.random.permutation(len(train_en_sents))
        train_en_sents_shuffled = train_en_sents[perm]
        train_cn_sents_shuffled = train_cn_sents[perm]
        train_cn_label_sents_shuffled = train_cn_label_sents[perm]

        for iteration in range(train_en_sents_shuffled.shape[0] // batch_size):
            # 获取一个batch的英文句子训练数据
            x_data = train_en_sents_shuffled[(batch_size * iteration):(batch_size * (iteration + 1))]
            # 将数据转换成paddle内置tensor
            sent = paddle.to_tensor(x_data)
            # 经过encoder得到对输入数据的编码
            en_repr, hidden, cell = encoder(sent)

            # 获取一个batch的对应的中文句子数据
            x_cn_data = train_cn_sents_shuffled[(batch_size * iteration):(batch_size * (iteration + 1))]
            x_cn_label_data = train_cn_label_sents_shuffled[(batch_size * iteration):(batch_size * (iteration + 1))]

            # 损失
            loss = paddle.zeros([1])

            # 解码器循环,计算总损失
            for i in range(MAX_LEN + 2):
                # 获得当前输入atten_decoder的输入元素及标签
                cn_word = paddle.to_tensor(x_cn_data[:, i:i + 1])
                cn_word_label = paddle.to_tensor(x_cn_label_data[:, i])

                logits, (hidden, cell) = atten_decoder(cn_word, hidden, cell, en_repr)
                step_loss = F.cross_entropy(logits, cn_word_label)
                loss += step_loss

            # 计算平均损失
            loss = loss / (MAX_LEN + 2)

            if iteration % 200 == 0:
                print("iter {}, loss:{}".format(iteration, loss.numpy()))

            # 后向传播
            loss.backward()
            # 参数更新
            opt.step()
            # 清除梯度
            opt.clear_grad()

    # 训练完成保存模型参数
    paddle.save(encoder.state_dict(), 'models/encoder.pdparams')
    paddle.save(atten_decoder.state_dict(), 'models/atten_decoder.pdparams')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

调用create_dataset函数生成训练数据。设置超参数epochs = 20batch_size = 64learning_rate = 0.001en_embedding_dim = 128cn_embedding_dim = 128lstm_hidden_size = 256lstm_num_layers = 1以及v_dim = 256,调用train函数开启训练。代码如下:

if __name__ == '__main__':
    train_en_sents, train_cn_sents, train_cn_label_sents, en_vocab, cn_vocab = create_dataset('datasets/cmn.txt')

    # 设置超参数
    epochs = 20
    batch_size = 64
    learning_rate = 0.001
    en_vocab_size = len(en_vocab)
    cn_vocab_size = len(cn_vocab)
    en_embedding_dim = 128
    cn_embedding_dim = 128
    lstm_hidden_size = 256
    lstm_num_layers = 1
    v_dim = 256

    start_time = time.time()
    train(train_en_sents, train_cn_sents, train_cn_label_sents, epochs, learning_rate, batch_size,
          en_vocab_size, en_embedding_dim, lstm_hidden_size, lstm_num_layers,
          cn_vocab_size, cn_embedding_dim, v_dim)
    finish_time = time.time()
    print('训练用时:{:.2f}分钟'.format((finish_time-start_time)/60.0))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

使用GPU训练模型,用时共7.59分钟。如果使用CPU,预计训练所用时间至少翻一倍。其中训练过程打印的信息如下:

2.1.0
W0803 22:08:31.521724 16684 device_context.cc:404] Please NOTE: device: 0, GPU Compute Capability: 6.1, Driver API Version: 11.2, Runtime API Version: 10.1
W0803 22:08:31.536396 16684 device_context.cc:422] device: 0, cuDNN Version: 7.6.
epoch:0
iter 0, loss:[8.066058]
iter 200, loss:[3.3313792]
epoch:1
iter 0, loss:[3.0832756]
iter 200, loss:[2.9235165]
epoch:2
iter 0, loss:[2.5026035]
iter 200, loss:[2.6165113]
epoch:3
iter 0, loss:[2.511048]
iter 200, loss:[2.173079]
epoch:4
iter 0, loss:[2.342031]
iter 200, loss:[2.179213]
epoch:5
iter 0, loss:[1.9800943]
iter 200, loss:[1.9754598]
epoch:6
iter 0, loss:[2.1824288]
iter 200, loss:[2.0068507]
epoch:7
iter 0, loss:[1.8755927]
iter 200, loss:[1.7782102]
epoch:8
iter 0, loss:[1.6492431]
iter 200, loss:[1.7164807]
epoch:9
iter 0, loss:[1.5739967]
iter 200, loss:[1.5312105]
epoch:10
iter 0, loss:[1.6726758]
iter 200, loss:[1.5393445]
epoch:11
iter 0, loss:[1.391438]
iter 200, loss:[1.3156104]
epoch:12
iter 0, loss:[1.328358]
iter 200, loss:[1.0773911]
epoch:13
iter 0, loss:[1.1689429]
iter 200, loss:[1.2644379]
epoch:14
iter 0, loss:[1.0560603]
iter 200, loss:[1.1911696]
epoch:15
iter 0, loss:[1.1562344]
iter 200, loss:[1.0733411]
epoch:16
iter 0, loss:[1.0015976]
iter 200, loss:[0.9953356]
epoch:17
iter 0, loss:[0.78453755]
iter 200, loss:[1.0062006]
epoch:18
iter 0, loss:[0.8454544]
iter 200, loss:[1.0099177]
epoch:19
iter 0, loss:[0.8389757]
iter 200, loss:[0.74120027]
训练用时:7.59分钟
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

3. 使用模型进行机器翻译

3.1 演示机器翻译效果

训练完成后,从训练集中随机抽取部分句子演示机器翻译效果。定义translate_test函数,从训练集中随机抽取部分句子,经过encoder编码,再使用atten_decoder解码,并将结果输出。具体代码如下:

def translate_test(num_of_exampels_to_evaluate, encoder, atten_decoder, en_vocab, cn_vocab):
    """
    展示机器翻译效果,用训练集中部分数据查看机器的翻译的结果

    :param num_of_exampels_to_evaluate: 指定从训练多少句子
    :param encoder: 训练好的encoder
    :param atten_decoder: 训练好的atten_decoder
    :param en_vocab: 英文词汇表
    :param cn_vocab: 中文词汇表
    :return: None
    """

    # 将模型设置为eval模式
    encoder.eval()
    atten_decoder.eval()

    # 从训练数据中随机选择部分英语句子展示翻译效果
    indices = np.random.choice(len(train_en_sents), num_of_exampels_to_evaluate, replace=False)
    x_data = train_en_sents[indices]
    sent = paddle.to_tensor(x_data)
    en_repr, hidden, cell = encoder(sent)

    # 获取随机选择到的英语句子和对应的中文翻译
    en_vocab_list = list(en_vocab)
    cn_vocab_list = list(cn_vocab)
    en_sents = []
    cn_sents = []
    for i in range(num_of_exampels_to_evaluate):
        this_en_sents = []
        this_cn_sents = []
        for en_vocab_id in train_en_sents[indices[i]]:
            # 0,1,2是三个特殊符号的ID
            if en_vocab_id not in [0, 1, 2]:
                this_en_sents.append(en_vocab_list[en_vocab_id])
        for cn_vocab_id in train_cn_sents[indices[i]]:
            if cn_vocab_id not in [0, 1, 2]:
                this_cn_sents.append(cn_vocab_list[cn_vocab_id])
        en_sents.append(this_en_sents)
        cn_sents.append(this_cn_sents)

    # Decoder解码时输入的第一个符号为<bos>
    word = np.array([[cn_vocab['<bos>']]] * num_of_exampels_to_evaluate)
    word = paddle.to_tensor(word)

    decoded_sent = []
    for i in range(MAX_LEN + 2):
        logits, (hidden, cell) = atten_decoder(word, hidden, cell, en_repr)
        word = paddle.argmax(logits, axis=1)
        decoded_sent.append(word.numpy())
        word = paddle.unsqueeze(word, axis=-1)

    results = np.stack(decoded_sent, axis=1)
    for i in range(num_of_exampels_to_evaluate):
        en_input = " ".join(en_sents[i][:-1]) + en_sents[i][-1]
        ground_truth_translate = "".join(cn_sents[i][:-1]) + cn_sents[i][-1]
        model_translate = ""
        for k in results[i]:
            w = list(cn_vocab)[k]
            if w != '<pad>' and w != '<eos>':
                model_translate += w
        print(en_input)
        print("true: {}".format(ground_truth_translate))
        print("pred: {}".format(model_translate))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

创建encoderatten_decoder对象,并加载训练好的模型参数,调用translate函数查看机器翻译效果。具体代码如下:

    # 用训练好的模型来预测
    # 首先创建encoder和atten_decoder对象,并加载训练好的参数
    encoder = Encoder(en_vocab_size, en_embedding_dim, lstm_hidden_size, lstm_num_layers)
    atten_decoder = AttentionDecoder(cn_vocab_size, cn_embedding_dim, lstm_hidden_size, v_dim)
    encoder_state_dict = paddle.load('models/encoder.pdparams')
    atten_decoder_state_dict = paddle.load('models/atten_decoder.pdparams')
    encoder.set_state_dict(encoder_state_dict)
    atten_decoder.set_state_dict(atten_decoder_state_dict)

    # 调用translate_test函数实现机器翻译——英译中
    translate_test(10, encoder, atten_decoder, en_vocab, cn_vocab)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行上述代码,打印如下结果:

one hundred years is called a century.
true: 一百年被叫做一个世纪。
pred: 一个人都在10岁了。
how long would it take?
true: 要多长时间?
pred: 要多久?
i m waiting for this store to open.
true: 我正等著這家店開門。
pred: 我在等我去那裡。
why do you need this money?
true: 你為什麼需要這筆錢?
pred: 你為什麼需要这种錢?
i m free now.
true: 我现在有空了。
pred: 我现在有点了。
he said that it was nine o clock.
true: 他说九点了。
pred: 他說他很快就了。
you ve got a lot of willpower.
true: 你的意志力很強。
pred: 你的朋友很好。
i ve got something i want to show you.
true: 我有东西想给你看看。
pred: 我有些事要我想要你。
that hurts.
true: 真疼。
pred: 真疼。
the top of mt fuji was covered with snow.
true: 富士山顶盖满了雪。
pred: 富士山被山顶盖了。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3.2 翻译自定义英文输入

定义translate_self函数,将输入的英文句子经过encoder编码,再使用atten_decoder解码,将输入的英文句子翻译成中文。具体代码如下:

def translate_self(encoder, atten_decoder, en_vocab, cn_vocab, max_translation_length, use_pad=True):
    """根据输入的英文句子,完成机器翻译——英译中

    Tips:
        1> 当前程序并未在训练数据中添加'<unk>',当输入语料中包含不在英文词表中的词,使用'<pad>'符号进行替换

    Args:
        max_translation_length: 翻译的中文句子的最长长度,超过该长度直接截断
        use_pad: 是否将输入的长度小于MAX_LEN的英文句子使用'<pad>'字符填充
            True: 该模式会将输入的英文句子长度填充至MAX_LEN。由于训练时使用了'<pad>'字符填充,翻译效果会好很多
            False: 不对输入的英文句子进行填充。该模式更贴近实际应用场景,但是在当前数据量下,效果预计会比较差
    """
    # 将模型设置为eval模式
    encoder.eval()
    atten_decoder.eval()

    while True:
        input_en = input("Please enter the English sentence to be translated: ")
        print(f"Your English sentence: {input_en}")

        # 将英文句子转换成token
        words_re = re.compile(r'\w+')
        words_en = words_re.findall(input_en)
        if input_en[-1] in [".", "?", "!"]:
            words_en += [input_en[-1]]
        words_en += ["<eos>"]
        words_en = [word.lower() if word.lower() in en_vocab else "<pad>" for word in words_en]
        if use_pad and len(words_en) < MAX_LEN + 1:
            words_en += ["<pad>"] * (MAX_LEN - len(words_en) + 1)
        tokens_en = [[en_vocab[word] for word in words_en]]
        print(f"Sentence for translator: {' '.join(words_en)}")

        sent = paddle.to_tensor(tokens_en, dtype="int64")
        en_repr, hidden, cell = encoder(sent)
        # Decoder解码时输入的第一个符号为<bos>
        word = np.array([[cn_vocab['<bos>']]])
        word = paddle.to_tensor(word)

        print("Translated Chinese sentences: ", end="")
        for i in range(max_translation_length):
            logits, (hidden, cell) = atten_decoder(word, hidden, cell, en_repr)
            word = paddle.argmax(logits, axis=1)
            word_id = word.numpy()
            if word_id != 2:
                print(list(cn_vocab)[int(word_id)], end="")
            else:
                break
            word = paddle.unsqueeze(word, axis=-1)
        print("\n==============================================")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

调用translate_self函数,输入英文句子,查看训练好的机器翻译模型效果:

    # 调用translate_self函数,翻译自定义英文输入
    translate_self(encoder, atten_decoder, en_vocab, cn_vocab, 25, use_pad=True)
  • 1
  • 2

运行上述代码,多次输入自定义英文句子,打印结果如下:

Please enter the English sentence to be translated: How old are you?
Your English sentence: How old are you?
Sentence for translator: how old are you ? <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>
Translated Chinese sentences: 你幾歲?
==============================================
Please enter the English sentence to be translated: What's your name?
Your English sentence: What's your name?
Sentence for translator: what s your name ? <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>
Translated Chinese sentences: 你叫什么?
==============================================
Please enter the English sentence to be translated: I love you forever.
Your English sentence: I love you forever.
Sentence for translator: i love you forever . <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>
Translated Chinese sentences: 我喜欢你们的第一。
==============================================
Please enter the English sentence to be translated: I'm fine, thanks. And you?
Your English sentence: I'm fine, thanks. And you?
Sentence for translator: i m fine thanks and you ? <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>
Translated Chinese sentences: 我又你的感覺,不是嗎?
==============================================
Please enter the English sentence to be translated: Never give up!
Your English sentence: Never give up!
Sentence for translator: never give up ! <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>
Translated Chinese sentences: 别来。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

本文着重综合NLP模型原理与应用系列内容,讲述机器翻译基本原理和深度学习实践流程,离产业化机器翻译模型还比较遥远。在工业界,主流使用BERT+Transform模型,而不是文本所采用的模型。同时工业训练神经网络翻译机,训练数据量级不可同日而语。

4. 参考资料链接

  1. https://www.paddlepaddle.org.cn/documentation/docs/zh/tutorial/nlp_case/seq2seq_with_attention/seq2seq_with_attention.html
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/521774
推荐阅读
相关标签
  

闽ICP备14008679号