tensorflow实战

采用Attention机制包装的Seq2Seq模型构建英译中翻译

2018-12-31  本文已影响0人  YinliX

运行环境

python3.6.3、tensorflow1.10.0
Intel@AIDevCloud:Intel Xeon Gold 6128 processors集群

思路

根据已经给出的中文和英文词汇表以及中文和英文的训练材料,将每句话看作一个时间序列,采用Seq2Seq模型和训练材料进行训练,用attention机制进行包装,最后用一些句子来测试训练好的翻译器的效果。

代码说明

共有六个代码文件,NMTModel.py、NMTModelWithAttention.py、train.py、translate.py、makeData.py和SentenceTool.py。其中NMTModel.py和NMTModelWithAttention.py分别是不带注意力机制的和带注意力机制的Seq2Seq模型的定义,train.py是训练的程序,translate.py是用来根据训练好的模型翻译给定的文本文件的,后面两个都是工具函数,makeData.py是用来从训练材料中提取数据并进行处理的,SentenceTool.py是用来用来实现词汇表和实际语言之间的转换的工具。

源代码

NMTModel.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import tensorflow as tf

'''
定义不带注意力机制的Seq2Seq模型
编码解码都由两层的LSTM模型组成
'''

# 定义相关参数
# LSTM的隐藏层规模
HIDDEN_SIZE = 1024
# LSTM的层数
NUM_LAYERS = 2
# 英文词汇表的大小
SRC_VOCAB_SIZE = 10000
# 中文词汇表的大小
TRG_VOCAB_SIZE = 4000
# dropout层保留的概率
KEEP_PROB = 0.8
# 控制梯度膨胀的梯度大小上限
MAX_GRAD_NORM = 5
# 共享softmax层和词向量层之间的参数
SHARE_EMB_AND_SOFTMAX = True
# 句首和句末的标志
SOS_ID = 1
EOS_ID = 2

# 模型类定义
# 共定义了三个函数,一个初始函数
# 一个前向传播定义训练过程的函数
# 还有一个预测函数
class NMTModel(object):
    # 初始化模型
    def __init__(self):
        # 定义编码器和解码器的LSTM结构
        self.enc_cell = tf.nn.rnn_cell.MultiRNNCell(
            [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
        )
        self.dec_cell = tf.nn.rnn_cell.MultiRNNCell(
            [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
        )

        # 定义两种语言的词向量层
        self.src_embedding = tf.get_variable("src_emb", [SRC_VOCAB_SIZE, HIDDEN_SIZE])
        self.trg_embedding = tf.get_variable("trg_emb", [TRG_VOCAB_SIZE, HIDDEN_SIZE])

        # 如果共享参数则用词向量层的参数初始化softmax层的参数
        if SHARE_EMB_AND_SOFTMAX:
            self.softmax_weight = tf.transpose(self.trg_embedding)
        else:
            self.softmax_weight = tf.get_variable("weight", [HIDDEN_SIZE, TRG_VOCAB_SIZE])
        # 初始化softmax的bias参数
        self.softmax_bias = tf.get_variable("softmax_bias", [TRG_VOCAB_SIZE])

    # 定义模型的前向计算
    def forward(self, src_input, src_size, trg_input, trg_label, trg_size):
        # 获取batch的大小
        batch_size = tf.shape(src_input)[0]

        # 分别将输入和输出转化为词向量
        src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)
        trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)

        # 对输入和输出的词向量进行dropout
        src_emb = tf.nn.dropout(src_emb,KEEP_PROB)
        trg_emb = tf.nn.dropout(trg_emb, KEEP_PROB)

        # 用dynamic_rnn定义编码器,输出编码器的每一步输出和最后一步的隐藏状态
        with tf.variable_scope("encoder"):
            enc_outputs, enc_state = tf.nn.dynamic_rnn(self.enc_cell, src_emb, src_size, dtype=tf.float32)

        # 用dynamic_rnn定义解码器,输出解码器的每一步输出
        # 用编码器的最后一步状态来初始解码器的初始状态
        with tf.variable_scope("decoder"):
            dec_outputs, _ = tf.nn.dynamic_rnn(self.dec_cell, trg_emb, trg_size, initial_state=enc_state)

        # 计算解码器每一步的log perplexity
        output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])
        logits = tf.matmul(output, self.softmax_weight) + self.softmax_bias
        loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.reshape(trg_label,[-1]), logits=logits)

        # 计算平均损失,注意在计算的时候需要将填充的位置设定为0
        label_weights = tf.sequence_mask(trg_size, maxlen=tf.shape(trg_label)[1], dtype=tf.float32)
        label_weights = tf.reshape(label_weights, [-1])
        cost = tf.reduce_sum(loss * label_weights)
        cost_per_token = cost / tf.reduce_sum(label_weights)

        # 获取可训练的参数
        trainable_variables = tf.trainable_variables()

        # 定义反向传播操作、优化步骤和训练步骤
        grads = tf.gradients(cost / tf.to_float(batch_size), trainable_variables)
        grads, _ = tf.clip_by_global_norm(grads, MAX_GRAD_NORM)
        optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0)
        train_op = optimizer.apply_gradients(zip(grads, trainable_variables))

        # 返回平均损失和训练步骤
        return cost_per_token, train_op

    # 定义预测函数
    def inference(self, src_input):
        # 整理输入,将其转化为一个batch大小为1的batch,并转化为对应的词向量
        src_size = tf.convert_to_tensor([len(src_input)], dtype=tf.int32)
        src_input = tf.convert_to_tensor([src_input], dtype=tf.int32)
        src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)

        # 定义编码器
        with tf.variable_scope("encoder"):
            enc_outputs, enc_state = tf.nn.dynamic_rnn(self.enc_cell, src_emb, src_size, dtype=tf.float32)

        # 定义解码的最大步骤避免无限循环
        MAX_DEC_LEN = 100

        # 和训练过程不同,需要另外定义解码过程
        with tf.variable_scope("decoder/rnn/multi_rnn_cell"):
            # 初始化生成的句子,用SOS_ID开头
            init_array = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True, clear_after_read=False)
            init_array= init_array.write(0, SOS_ID)
            # 初始循环状态以及循环步数
            init_loop_var = (enc_state, init_array, 0)

            # 定义循环结束条件
            def continue_loop_condition(state, trg_ids, step):
                # 如果遍历到了句子结尾或者比定义的最大步骤大就停止循环
                return tf.reduce_all(tf.logical_and(
                    tf.not_equal(trg_ids.read(step), EOS_ID),
                    tf.less(step, MAX_DEC_LEN-1)
                ))

            # 定义循环体
            def loop_body(state,trg_ids, step):
                # 读取最后一步输出的单词并读取其对应的词向量
                trg_input = [trg_ids.read(step)]
                trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)
                # 调用dec_cell计算下一步的结果
                dec_outputs, next_state = self.dec_cell.call(state=state, inputs=trg_emb)
                # 计算结果对应的softmax层结果
                output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])
                logits = (tf.matmul(output, self.softmax_weight) + self.softmax_bias)
                # 根据logits结果求得可能性最大的单词
                next_id = tf.argmax(logits, axis=1, output_type=tf.int32)
                # 将当前计算出来的单词结果写入trg_ids
                trg_ids = trg_ids.write(step+1, next_id[0])
                return next_state, trg_ids, step+1

            # 调用tf.while_loop,返回最终结果即trg_ids
            state, trg_ids, step = tf.while_loop(continue_loop_condition, loop_body, init_loop_var)
            # 返回trg_ids里的值
            return trg_ids.stack()
NMTModelWithAttention.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import tensorflow as tf

'''
用注意力机制包装的Seq2Seq模型
'''

# 定义相关参数
# LSTM的隐藏层规模
HIDDEN_SIZE = 1024
# LSTM的层数
NUM_LAYERS = 2
# 英文词汇表的大小
SRC_VOCAB_SIZE = 10000
# 中文词汇表的大小
TRG_VOCAB_SIZE = 4000
# dropout层保留的概率
KEEP_PROB = 0.8
# 控制梯度膨胀的梯度大小上限
MAX_GRAD_NORM = 5
# 共享softmax层和词向量层之间的参数
SHARE_EMB_AND_SOFTMAX = True
# 句首和句末的标志
SOS_ID = 1
EOS_ID = 2

class NMTModelWithAttention(object):
    def __init__(self):

        # 构造双向循环的网络结构
        self.enc_cell_fw = tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE)
        self.enc_cell_bw = tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE)
        # 定义解码器
        self.dec_cell = tf.nn.rnn_cell.MultiRNNCell(
            [tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)]
        )

        # 定义两种语言的词向量层
        self.src_embedding = tf.get_variable("src_emb", [SRC_VOCAB_SIZE, HIDDEN_SIZE])
        self.trg_embedding = tf.get_variable("trg_emb", [TRG_VOCAB_SIZE, HIDDEN_SIZE])

        # 如果共享参数则用词向量层的参数初始化softmax层的参数
        if SHARE_EMB_AND_SOFTMAX:
            self.softmax_weight = tf.transpose(self.trg_embedding)
        else:
            self.softmax_weight = tf.get_variable("weight", [HIDDEN_SIZE, TRG_VOCAB_SIZE])
        # 初始化softmax的bias参数
        self.softmax_bias = tf.get_variable("softmax_bias", [TRG_VOCAB_SIZE])

    def forward(self, src_input, src_size, trg_input, trg_label, trg_size):
        # 获取batch的大小
        batch_size = tf.shape(src_input)[0]

        # 分别将输入和输出转化为词向量
        src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)
        trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)

        # 对输入和输出的词向量进行dropout
        src_emb = tf.nn.dropout(src_emb, KEEP_PROB)
        trg_emb = tf.nn.dropout(trg_emb, KEEP_PROB)

        # 构造编码器,使用bidirectional_dynamic_rnn构造双向循环网络
        with tf.variable_scope("encoder"):
            enc_outputs, enc_state = tf.nn.bidirectional_dynamic_rnn(
                self.enc_cell_fw, self.enc_cell_bw, src_emb, src_size, dtype=tf.float32
            )
            # 将两个LSTM的输出拼接为一个张量
            enc_outputs = tf.concat([enc_outputs[0], enc_outputs[1]], -1)

        # 构造解码器
        with tf.variable_scope("decoder"):
            # 选择注意力权重的计算模型
            attention_machanism = tf.contrib.seq2seq.BahdanauAttention(
                HIDDEN_SIZE, enc_outputs, memory_sequence_length = src_size
            )
            # 将原来的dec_cell和注意力一起封装成更高层的attention_cell
            attention_cell = tf.contrib.seq2seq.AttentionWrapper(
                self.dec_cell, attention_machanism, attention_layer_size = HIDDEN_SIZE
            )
            # 用dynamic和attention_cell来构造编码器
            dec_outputs, _ = tf.nn.dynamic_rnn(attention_cell, trg_emb, trg_size, dtype=tf.float32)

        # 计算解码器每一步的log perplexity
        output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])
        logits = tf.matmul(output, self.softmax_weight) + self.softmax_bias
        loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.reshape(trg_label, [-1]), logits=logits)

        # 计算平均损失,注意在计算的时候需要将填充的位置设定为0
        label_weights = tf.sequence_mask(trg_size, maxlen=tf.shape(trg_label)[1], dtype=tf.float32)
        label_weights = tf.reshape(label_weights, [-1])
        cost = tf.reduce_sum(loss * label_weights)
        cost_per_token = cost / tf.reduce_sum(label_weights)

        # 获取可训练的参数
        trainable_variables = tf.trainable_variables()

        # 定义反向传播操作、优化步骤和训练步骤
        grads = tf.gradients(cost / tf.to_float(batch_size), trainable_variables)
        grads, _ = tf.clip_by_global_norm(grads, MAX_GRAD_NORM)
        optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0)
        train_op = optimizer.apply_gradients(zip(grads, trainable_variables))

        # 返回平均损失和训练步骤
        return cost_per_token, train_op

    def inference(self, src_input):
        # 整理输入,将其转化为一个batch大小为1的batch,并转化为对应的词向量
        src_size = tf.convert_to_tensor([len(src_input)], dtype=tf.int32)
        src_input = tf.convert_to_tensor([src_input], dtype=tf.int32)
        src_emb = tf.nn.embedding_lookup(self.src_embedding, src_input)

        # 定义编码器
        with tf.variable_scope("encoder"):
            enc_outputs, enc_state = tf.nn.bidirectional_dynamic_rnn(
                self.enc_cell_fw, self.enc_cell_bw, src_emb, src_size, dtype=tf.float32
            )
            enc_outputs = tf.concat([enc_outputs[0], enc_outputs[1]], -1)

        # 定义解码器
        with tf.variable_scope("decoder"):
            attention_machanism = tf.contrib.seq2seq.BahdanauAttention(
                HIDDEN_SIZE, enc_outputs, memory_sequence_length = src_size
            )
            attention_cell = tf.contrib.seq2seq.AttentionWrapper(
                self.dec_cell, attention_machanism, attention_layer_size = HIDDEN_SIZE
            )

        # 定义解码的最大步骤避免无限循环
        MAX_DEC_LEN = 100

        # 另外定义解码过程
        with tf.variable_scope("decoder/rnn/attention_wrapper"):
            # 初始化生成的句子,用SOS_ID开头
            init_array = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True, clear_after_read=False)
            init_array= init_array.write(0, SOS_ID)
            # 初始循环状态以及循环步数
            init_loop_var = (attention_cell.zero_state(batch_size=1, dtype=tf.float32),init_array, 0)

            # 定义循环结束条件
            def continue_loop_condition(state, trg_ids, step):
                # 如果遍历到了句子结尾或者比定义的最大步骤大就停止循环
                return tf.reduce_all(tf.logical_and(
                    tf.not_equal(trg_ids.read(step), EOS_ID),
                    tf.less(step, MAX_DEC_LEN - 1)
                ))

            # 定义循环体
            def loop_body(state,trg_ids, step):
                # 读取最后一步输出的单词并读取其对应的词向量
                trg_input = [trg_ids.read(step)]
                trg_emb = tf.nn.embedding_lookup(self.trg_embedding, trg_input)
                # 调用包装好的attention_cell计算下一步的结果
                dec_outputs, next_state = attention_cell.call(state=state, inputs=trg_emb)
                # 计算结果对应的softmax层结果
                output = tf.reshape(dec_outputs, [-1, HIDDEN_SIZE])
                logits = (tf.matmul(output, self.softmax_weight) + self.softmax_bias)
                # 根据logits结果求得可能性最大的单词
                next_id = tf.argmax(logits, axis=1, output_type=tf.int32)
                # 将当前计算出来的单词结果写入trg_ids
                trg_ids = trg_ids.write(step + 1, next_id[0])
                return next_state, trg_ids, step + 1

            # 调用tf.while_loop,返回最终结果即trg_ids
            state, trg_ids, step = tf.while_loop(continue_loop_condition, loop_body, init_loop_var)
            # 返回trg_ids里的值
            return trg_ids.stack()
train.py
# -*- coding: UTF-8 -*-
#Author:Yinli

from makeData import *
from NMTModel import NMTModel
from NMTModelWithAttention import NMTModelWithAttention

'''
训练模型
'''

# 定义训练数据文件和模型保存路径
SRC_TRAIN_DATA = "train.en"
TRG_TRAIN_DATA = "train.zh"
# model文件夹装不带注意力机制的模型
# model1文件夹装带注意力机制的模型
# CHECKPOINT_PATH = "model/seq2seq_ckpt"
CHECKPOINT_PATH = "model1/seq2seq_ckpt"

# 定义batch大小和使用训练数据的轮数
BATCH_SIZE = 100
NUM_EPOCH = 5

# 在模型上训练一轮
def run_epoch(session, cost_op, train_op, saver, step):
    while True:
        try:
            # 训练一次得到损失
            cost, _ = session.run([cost_op, train_op])
            # 每十次训练打印一下损失
            if step % 10 == 0:
                print("——————经过%d步之后,每句话的损失为: %.3f" % (step, cost))
            # 每200次训练保存一下模型
            if step % 200 == 0:
                saver.save(session, CHECKPOINT_PATH, global_step = step)
            step += 1
        # 没有数据了则退出循环
        except tf.errors.OutOfRangeError:
            break
    return step

def main():
    # 定义初始化函数
    initializer = tf.random_normal_initializer(-0.05, 0.05)

    # 定义模型
    with tf.variable_scope("nmt_model", reuse=None, initializer=initializer):
        # train_model = NMTModel
        train_model = NMTModelWithAttention()

    # 获取数据集和其iterator
    data = MakeSrcTrgDataset(SRC_TRAIN_DATA, TRG_TRAIN_DATA, BATCH_SIZE)
    iterator = data.make_initializable_iterator()
    (src, src_size), (trg_input, trg_label, trg_size) = iterator.get_next()

    # 定义前向计算图
    cost_op, train_op = train_model.forward(src, src_size, trg_input, trg_label, trg_size)

    # 训练
    saver = tf.train.Saver()
    step = 0
    with tf.Session() as sess:
        # 初始化
        tf.global_variables_initializer().run()
        for i in range(NUM_EPOCH):
            print("第%d次迭代.." % (i+1))
            sess.run(iterator.initializer)
            step = run_epoch(sess, cost_op, train_op, saver, step)

if __name__ == '__main__':
    main()
translate.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import tensorflow as tf
from NMTModel import NMTModel
from NMTModelWithAttention import NMTModelWithAttention
from SentenceTool import *

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

'''
用已经训练好的模型将英文文件翻译为中文并输出
'''

# 定义训练好的模型路径、中英文词汇表和要翻译的文件
# model1路径表示带有注意力机制的模型
# model路径表示不带注意力机制的模型
# CHECKPOINT_PATH = "model/seq2seq_ckpt-9000"
CHECKPOINT_PATH = "model1/seq2seq_ckpt-9000"
SRC_VOCAB = "en.vocab"
TRG_VOCAB = "zh.vocab"
SRC_DATA = "English.txt"

# 输入一句用英文的词汇表的序号的形式表示的句子,返回其对应的翻译结果
def translate(src_sentence):
    # 重置计算图
    tf.reset_default_graph()
    # 定义模型
    with tf.variable_scope("nmt_model", reuse=None):
        model = NMTModelWithAttention()
        # model = NMTModel()
    # 定义翻译过程
    output_op = model.inference(src_sentence)
    # 创建对话并得到翻译结果
    sess = tf.Session()
    saver = tf.train.Saver()
    saver.restore(sess, CHECKPOINT_PATH)
    output = sess.run(output_op)
    # 返回翻译结果
    return output

def main():

    print("————————————用了注意力机制的Seq2Seq模型————————————")
    # 将要翻译的文件转化为词汇表的序号的形式
    src_sentences = EnglishToSentence(SRC_DATA, SRC_VOCAB)
    #print(src_sentences)
    print("")
    print("—————————————————————————————————————————————————")
    trg_sentences = []
    # 逐句翻译每句话并保存
    for src_sentence in src_sentences:
        trg_sentence = translate(src_sentence)
        trg_sentences.append(trg_sentence)

    # 将以中文词汇表序号表示的句子转化成中文
    trg_sentences = SentenceToChinese(trg_sentences, TRG_VOCAB)

    #print(trg_sentences)

if __name__ == '__main__':
    main()
makeData.py
# -*- coding: UTF-8 -*-
#Author:Yinli

import tensorflow as tf

'''
从文件中读取数据并进行一系列处理并返回
'''

# 限定每个句子的最大单词数量
MAX_LEN = 50
# 句首标志
SOS_ID = 1

# 从文件路径中读取一个语言的数据
def MakeDataset(file_path):
    # 读取原始数据
    dataset = tf.data.TextLineDataset(file_path)
    # 根据空格将单词编号切分开
    dataset = dataset.map(lambda string: tf.string_split([string]).values)
    # 将单词编号转化为整数
    dataset = dataset.map(lambda string: tf.string_to_number(string,tf.int32))
    # 在每句话后加入此句子的单词数量
    dataset = dataset.map(lambda x: (x, tf.size(x)))
    return dataset

# 从源语言文件和目标语言文件中读取数据并进行填充等一系列整理工作
def MakeSrcTrgDataset(src_path, trg_path, batch_size):
    # 分别读取源语言数据和目标语言数据
    src_data = MakeDataset(src_path)
    trg_data = MakeDataset(trg_path)

    # 整合为一个数据集
    dataset = tf.data.Dataset.zip((src_data, trg_data))

    # 定义长度过滤器,过滤掉空句子和过长的句子
    def FilterLength(src_tuple, trg_tuple):
        # 将dataset的四个tuple分离出来
        ((src_input,src_len), (trg_label, trg_len)) = (src_tuple, trg_tuple)
        # 过滤并返回
        src_len_ok = tf.logical_and(tf.greater(src_len, 1), tf.less_equal(src_len, MAX_LEN))
        trg_len_ok = tf.logical_and(tf.greater(trg_len, 1), tf.less_equal(trg_len, MAX_LEN))
        return tf.logical_and(src_len_ok, trg_len_ok)

    # 使用过滤器过滤
    dataset = dataset.filter(FilterLength)

    # 生成解码器需要形式的输入和输出数据
    # 解码器输入句子trg_input需要以SOS_ID开头
    # 解码器输出句子trg_label不变
    def MakeTrgInput(src_tuple, trg_tuple):
        ((src_input, src_len), (trg_label, trg_len)) = (src_tuple, trg_tuple)
        trg_input = tf.concat([[SOS_ID], trg_label[:-1]], axis=0)
        return ((src_input, src_len), (trg_input, trg_label, trg_len))

    # 将上面的函数map到每行数据上
    dataset = dataset.map(MakeTrgInput)

    # 随机打乱
    dataset = dataset.shuffle(10000)

    # 定义输出数据的维度
    padded_shapes = (
        (tf.TensorShape([None]), tf.TensorShape([])),
        (tf.TensorShape([None]), tf.TensorShape([None]), tf.TensorShape([]))
    )
    batched_dataset = dataset.padded_batch(batch_size, padded_shapes)
    # 返回最终数据
    return batched_dataset
SentenceTool.py
import codecs

'''
用来实现词汇表和实际语言之间的转换的工具
'''

# 将用中文词汇表序号表示的句子数组转化为中文句子
def SentenceToChinese(src_sentences, trg_vocab):

    # 读取词汇表,构造序号和实际词的映射关系
    with codecs.open(trg_vocab, "r", "utf-8") as f_vocab:
        vocab = [w.strip() for w in f_vocab.readlines()]
    id_to_word = {k:v for (k,v) in zip(range(len(vocab)), vocab)}

    # 内部函数,用来获取序号对应的单词
    def get_word(id):
        return id_to_word[id]

    trg_sentences = []
    print("翻译的结果是:")
    # 转化每个句子
    for src_sentence in src_sentences:
        current_sentence = ""
        for id in src_sentence:
            word = get_word(id)
            # 如果不是首和尾则将这个词添加到当前句子中
            if word!="<sos>" and word!="<eos>":
                current_sentence = current_sentence + get_word(id)
        # 打印查看转化结果
        print(current_sentence)
        # 将当前句子添加到要输出的数组中
        trg_sentences.append(current_sentence)
    return trg_sentences

# 将要翻译的文件逐行转化为用英文词汇表序号表示的数组
def EnglishToSentence(src_data, src_vocab):
    # 读取词汇表,构造序号和实际词的映射关系
    with codecs.open(src_vocab, "r", "utf-8") as f_vocab:
        vocab = [w.strip() for w in f_vocab.readlines()]
    word_to_id = {k: v for (k, v) in zip(vocab, range(len(vocab)))}

    # 内部函数,用来获取单词对应的序号
    # 如果不在词汇表内就定义为unknown
    def get_id(word):
        return word_to_id[word] if word in word_to_id else word_to_id["<unk>"]

    # 打开文件
    fin = codecs.open(src_data, "r", "utf-8")
    sentences = []
    print("要翻译的句子是:")
    # 遍历每个句子
    for line in fin:
        # 打印查看每个句子
        print(line,end="")
        # 分词并在末尾加上一个结束标志
        words = line.strip().split() + ["<eos>"]
        # 将每个词转化为对应的序号
        current_sentence = []
        for w in words:
            current_sentence.append(get_id(w))
        # 将当前转化后的数组保存
        sentences.append(current_sentence)
    # 返回数组
    return sentences

运行结果

带注意力的模型在Intel的云上训练结束花了近15个小时,不带注意力的模型在Intel的云上训练结束花了近8个小时,它们的训练均是复用了5次训练数据,得到的最后的模型是训练过9000次的模型,分别用这两个模型来翻译给定的一些英文句子,结果如下:

要翻译的文本文件
需要翻译的文件
不带注意力的模型
不带注意力的模型
带注意力的模型
带注意力机制的模型

结果分析

从翻译结果可以看出来,不管是带注意力机制的还是不带注意力机制的翻译器,对于短句子比如this is a test这样的句子的翻译效果还好,但如果句子结构复杂句子长度长,翻译效果就会大打折扣,因为所用的训练集不大,只有21万个句子对,而且词汇表也不大,英文1w,中文4000,模型的稳健性并不高。
对于带注意力机制的模型和不带注意力机制的模型比较而言,带注意力机制的模型显然翻译效果稍好一筹,但前者比后者的训练复杂度和训练时间都要大。

上一篇下一篇

猜你喜欢

热点阅读