机器翻译:引入注意力机制的Encoder-Decoder深度神经网络训练实战中英文互译(完结篇)


前言

注意力机制是一种在深度学习中广泛使用的技术,它可以有效地处理输入序列和输出序列之间的对应关系。其中,Encoder-Decoder是一种常见的注意力机制,它主要用于序列到序列的任务,例如机器翻译、语音识别等。在Encoder-Decoder中,输入序列首先通过一个编码器进行编码,然后再通过一个解码器进行解码,最终生成输出序列。在这个过程中,注意力机制可以帮助模型更好地理解输入序列中的重要信息,并在生成输出序列的过程中更加准确地对其进行利用。由于其出色的性能和广泛的适用性,注意力机制的Encoder-Decoder已经成为自然语言处理和语音识别等领域中的重要技术之一。


要做一个中英互译的神经网络训练,我们需要有:
预:可供训练的数据集 数据集我已经上传数据集(免费)
1.数据的预处理
2.模型配置
3.模型训练
整个过程是在kaggle上进行的

一、数据的预处理

数据的预处理和有关数据集的简介,我上篇文章已经讲解,有需要的直接可以查阅机器翻译 深度学习预处理实战(中英文互译)一
在这里我直接附上代码:

import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from sklearn.model_selection import train_test_split
import unicodedata
import re
import numpy as np
import os
import io
import time
import pandas as pd
import numpy as np
import jieba
jieba.initialize() # 手动初始化jieba资源,提高分词效率。
jieba.enable_paddle() # 启动paddle模式。 0.40版之后开始支持,早期版本不支持

# 判断是否包含中文
def is_chinese(string):
  """
  检查整个字符串是否包含中文
  :param string: 需要检查的字符串
  :return: bool
  """
  for ch in string:
    if u'\u4e00' <= ch <= u'\u9fa5':
      return True
  return False

# 中英文预处理
def preprocess_sentence(w):
  if not is_chinese(w):
    w = w.lower()

    # 除了 (a-z, A-Z, ".", "?", "!", ","),将所有字符替换为空格
    w = re.sub(r"[^a-zA-Z?.!,]+", " ", w)
    w = w.rstrip().strip()

    # 给句子加上开始和结束标记,以便模型知道每个句子开始和结束的位置
    w = '<start> ' + w + ' <end>'
  else:
    w = re.sub(r"[^\u4e00-\u9fa5,。?!]+", "", w)
    w = w.strip()
    # seg_list = jieba.cut(w,use_paddle=True) # 使用paddle模式分词
    # w= ' '.join(list(seg_list))
    w = 分字(w)
    # 给句子加上开始和结束标记,以便模型知道每个句子开始和结束的位置
    w = '<start> ' + w + ' <end>'
  return w

def 分字(str):
  line = str.strip()
  pattern = re.compile('[^\u4e00-\u9fa5,。?!]')
  en = ' '.join(pattern.split(line)).strip()
  result=''
  for character in en:
    result+=character+' '
  return result.strip()

# 调用预处理方法,并返回这样格式的句子对:[english, chinese]
def create_dataset(path, num_examples):
  lines = io.open(path, encoding='UTF-8').read().strip().split('\n')
  word_pairs = [[preprocess_sentence(w) for w in l.split('\t')[0:2][::-1]] for l in lines[:num_examples]]
  return zip(*word_pairs)

# 判断词序列长度
def max_length(tensor):
  return max(len(t) for t in tensor)

# 词符化
def tokenize(lang):
  lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(filters='')
  lang_tokenizer.fit_on_texts(lang)
  tensor = lang_tokenizer.texts_to_sequences(lang)
  tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor,padding='post')
  return tensor, lang_tokenizer

# 创建清理过的输入输出对
def load_dataset(path, num_examples=None):
  chs, en = create_dataset(path, num_examples)
  input_tensor, inp_lang_tokenizer = tokenize(en)
  target_tensor, targ_lang_tokenizer = tokenize(chs)
  return input_tensor, target_tensor, inp_lang_tokenizer, targ_lang_tokenizer

# 格式化显示字典内容
def convert(lang, tensor):
  for t in tensor:
    if t!=0:
      print("%d ----> %s" % (t, lang.index_word[t]))

if __name__=="__main__":
  num_examples = 100
  # 读取英中互译文件
  path_to_file = '/kaggle/input/tansantcmn/cmn.txt'
  input_tensor, target_tensor, inp_lang_tokenizer, targ_lang_tokenizer = load_dataset(path_to_file, num_examples)

  # 计算最大长度
  max_length_input, max_length_target = max_length(input_tensor), max_length(target_tensor)

  # 将数据集拆分为训练和验证集
  input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)

  # 显示长度
  print("Max length of input:", max_length_input)
  print("Max length of target:", max_length_target)

  # 显示词典长度
  print("Input vocabulary size:", len(inp_lang_tokenizer.word_index))
  print("Target vocabulary size:", len(targ_lang_tokenizer.word_index))

  # 显示样例
  print("Example input sequence:")
  convert(inp_lang_tokenizer, input_tensor_train[0])
  print("Actual input sentence:")
  print(' '.join([inp_lang_tokenizer.index_word[i] for i in input_tensor_train[0] if i not in [0]]))
  print("\nExample target sequence:")
  convert(targ_lang_tokenizer, target_tensor_train[0])
  print("Actual target sentence:")
  print(' '.join([targ_lang_tokenizer.index_word[i] for i in target_tensor_train[0] if i not in [0]]))
print(len(input_tensor_train), len(target_tensor_train), len(input_tensor_val), len(target_tensor_val))

# 显示编码后的源语言(中文)张量数据集示例
print('经过编码后的源语言(中文)张量数据集示例:')
print(input_tensor[:3])

先看运行结果
在这里插入图片描述
我们从数据集中随机抽取了一句话来进行翻译(grab tom.),可以看到翻译的结果(抓住汤姆)。此时效果还是不错的
我们这里是英译中的翻译,如果小伙伴想进行中译英,可以在(# 中英文预处理)函数中将not is_chinese(w):中的not去掉。
接下来我们来尝试使用注意力机制来进行深度学习训练

二、模型配置

要进行深度学习的训练,我们首先需要搭建模型
实现了一个基于注意力机制的Encoder-Decoder模型,
这里主要分为三个部分:
Encoder、
BahdanauAttention
Decoder。

Encoder部分
Encoder是一个继承自tf.keras.Model的类,用于对输入序列进行编码。其中,输入序列通过Embedding层进行嵌入后,再通过GRU层进行编码,最终输出编码器的编码结果和隐藏状态。

class Encoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
        super(Encoder, self).__init__()
        self.batch_sz = batch_sz
        self.enc_units = enc_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.enc_units,return_sequences=True,return_state=True,recurrent_initializer='glorot_uniform')
    def call(self, x, hidden):
        x = self.embedding(x)
        output, state = self.gru(x, initial_state = hidden)
        return output, state
    def initialize_hidden_state(self):
        return tf.zeros((self.batch_sz, self.enc_units))

BahdanauAttention
BahdanauAttention是一个继承自tf.keras.layers.Layer的类,用于计算注意力向量和注意力权重。其中,通过Dense层将解码器的隐状态和编码器的编码结果映射到同一维度,然后通过tanh函数和Dense层计算分数,再通过softmax函数计算注意力权重。最后,将注意力权重乘以编码器的编码结果得到注意力向量。

class BahdanauAttention(tf.keras.layers.Layer):
    def __init__(self, units):
        super(BahdanauAttention, self).__init__()
        self.W1 = tf.keras.layers.Dense(units)
        self.W2 = tf.keras.layers.Dense(units)
        self.V = tf.keras.layers.Dense(1)
    def call(self, query, values):
        # query为上次的GRU隐藏层,values为编码器的编码结果enc_output,隐藏层的形状 == (批大小,隐藏层大小)
        # hidden_with_time_axis 的形状 == (批大小,1,隐藏层大小)
        # 这样做是为了执行加法以计算分数  
        hidden_with_time_axis = tf.expand_dims(query, 1)
        # 分数的形状 == (批大小,最大长度,1)
        # 我们在最后一个轴上得到 1, 因为我们把分数应用于 self.V
        # 在应用 self.V 之前,张量的形状是(批大小,最大长度,单位)
        score = self.V(tf.nn.tanh(self.W1(values) + self.W2(hidden_with_time_axis)))
        # 注意力权重 (attention_weights) 的形状 == (批大小,最大长度,1)
        attention_weights = tf.nn.softmax(score, axis=1)
        # 上下文向量 (context_vector) 求和之后的形状 == (批大小,隐藏层大小)
        context_vector = attention_weights * values  # 使用注意力权重*编码器输出作为返回值,将来会作为解码器的输入
        context_vector = tf.reduce_sum(context_vector, axis=1)
        return context_vector, attention_weights

Decoder
Decoder是一个继承自tf.keras.Model的类,用于对输出序列进行解码。其中,输入序列通过Embedding层进行嵌入后,再通过GRU层进行解码。在解码器中,首先使用BahdanauAttention计算注意力向量和注意力权重,然后将解码器的输入和注意力向量拼接在一起,通过Dense层计算输出单词的概率分布,最终输出预测的概率分布、解码器的隐藏状态和注意力权重。

class Decoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
        super(Decoder, self).__init__()
        self.batch_sz = batch_sz
        self.dec_units = dec_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.dec_units,return_sequences=True,return_state=True,recurrent_initializer='glorot_uniform')
        self.fc = tf.keras.layers.Dense(vocab_size)
        # 第一步,使用注意力机制
        self.attention = BahdanauAttention(self.dec_units)
    def call(self, x, hidden, enc_output):
        # 第二步,用解码器的隐状态和编码器输出得到注意力向量和注意力权重
        # 其中(enc_output) 的形状 == (批大小,最大长度,隐藏层大小)
        context_vector, attention_weights = self.attention(hidden, enc_output)
        #  在通过嵌入层后的形状 == (批大小,1,嵌入维度)
        x = self.embedding(x)
        # 第三步,使用解码器的输入与注意力向量拼接,用于计算输出单词的概率分布
        # x 在拼接 (concatenation) 后的形状 == (批大小,1,嵌入维度 + 隐藏层大小)
        x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
        #第四步:得到解码器的隐藏状态,用于计算下一步的注意力向量
        # 将合并后的向量传送到 GRU
        output, state = self.gru(x)
        # 输出的形状 == (批大小 * 1,隐藏层大小)
        output = tf.reshape(output, (-1, output.shape[2]))
        # 输出的形状 == (批大小,词典大小)
        x = self.fc(output)
        # 第五步,把x(预测词的概率分布)state(解码器GRU的参数快照(隐状态)),attention_weights(注意力权重)返回给调用者
        return x, state, attention_weights

这里附上模型配置的完整代码

class Encoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
        super(Encoder, self).__init__()
        self.batch_sz = batch_sz
        self.enc_units = enc_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.enc_units,return_sequences=True,return_state=True,recurrent_initializer='glorot_uniform')
    def call(self, x, hidden):
        x = self.embedding(x)
        output, state = self.gru(x, initial_state = hidden)
        return output, state
    def initialize_hidden_state(self):
        return tf.zeros((self.batch_sz, self.enc_units))
class BahdanauAttention(tf.keras.layers.Layer):
    def __init__(self, units):
        super(BahdanauAttention, self).__init__()
        self.W1 = tf.keras.layers.Dense(units)
        self.W2 = tf.keras.layers.Dense(units)
        self.V = tf.keras.layers.Dense(1)
    def call(self, query, values):
        # query为上次的GRU隐藏层,values为编码器的编码结果enc_output,隐藏层的形状 == (批大小,隐藏层大小)
        # hidden_with_time_axis 的形状 == (批大小,1,隐藏层大小)
        # 这样做是为了执行加法以计算分数  
        hidden_with_time_axis = tf.expand_dims(query, 1)
        # 分数的形状 == (批大小,最大长度,1)
        # 我们在最后一个轴上得到 1, 因为我们把分数应用于 self.V
        # 在应用 self.V 之前,张量的形状是(批大小,最大长度,单位)
        score = self.V(tf.nn.tanh(self.W1(values) + self.W2(hidden_with_time_axis)))
        # 注意力权重 (attention_weights) 的形状 == (批大小,最大长度,1)
        attention_weights = tf.nn.softmax(score, axis=1)
        # 上下文向量 (context_vector) 求和之后的形状 == (批大小,隐藏层大小)
        context_vector = attention_weights * values  # 使用注意力权重*编码器输出作为返回值,将来会作为解码器的输入
        context_vector = tf.reduce_sum(context_vector, axis=1)
        return context_vector, attention_weights
class Decoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
        super(Decoder, self).__init__()
        self.batch_sz = batch_sz
        self.dec_units = dec_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.dec_units,return_sequences=True,return_state=True,recurrent_initializer='glorot_uniform')
        self.fc = tf.keras.layers.Dense(vocab_size)
        # 第一步,使用注意力机制
        self.attention = BahdanauAttention(self.dec_units)
    def call(self, x, hidden, enc_output):
        # 第二步,用解码器的隐状态和编码器输出得到注意力向量和注意力权重
        # 其中(enc_output) 的形状 == (批大小,最大长度,隐藏层大小)
        context_vector, attention_weights = self.attention(hidden, enc_output)
        #  在通过嵌入层后的形状 == (批大小,1,嵌入维度)
        x = self.embedding(x)
        # 第三步,使用解码器的输入与注意力向量拼接,用于计算输出单词的概率分布
        # x 在拼接 (concatenation) 后的形状 == (批大小,1,嵌入维度 + 隐藏层大小)
        x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
        #第四步:得到解码器的隐藏状态,用于计算下一步的注意力向量
        # 将合并后的向量传送到 GRU
        output, state = self.gru(x)
        # 输出的形状 == (批大小 * 1,隐藏层大小)
        output = tf.reshape(output, (-1, output.shape[2]))
        # 输出的形状 == (批大小,词典大小)
        x = self.fc(output)
        # 第五步,把x(预测词的概率分布)state(解码器GRU的参数快照(隐状态)),attention_weights(注意力权重)返回给调用者
        return x, state, attention_weights
num_examples = 5000
BATCH_SIZE = 64
embedding_dim = 256
units = 1024
path_to_file = '/kaggle/input/tansantcmn/cmn.txt'
input_tensor, target_tensor, inp_lang, targ_lang = load_dataset(path_to_file, num_examples)
max_length_targ, max_length_inp = max_length(target_tensor),max_length(input_tensor)
input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)
BUFFER_SIZE = len(input_tensor_train)

steps_per_epoch = len(input_tensor_train)//BATCH_SIZE

vocab_inp_size = len(inp_lang.word_index)+1
vocab_tar_size = len(targ_lang.word_index)+1
dataset = tf.data.Dataset.from_tensor_slices((input_tensor_train, target_tensor_train)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)  
encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)
example_input_batch, example_target_batch = next(iter(dataset))
# 样本输入
sample_hidden = encoder.initialize_hidden_state()
sample_output, sample_hidden = encoder(example_input_batch, sample_hidden)
print ('Encoder(编码器层) 输出层参数: (一次训练所选取的样本数, 句子长度, 神经元数量) {}'.format(sample_output.shape))
print ('Encoder(编码器层) 隐藏层参数: (一次训练所选取的样本数, 神经元数量) {}'.format(sample_hidden.shape))  
attention_layer = BahdanauAttention(10)
attention_result, attention_weights = attention_layer(sample_hidden, sample_output)
print("Attention(注意力层) 输出层参数: (一次训练所选取的样本数, 神经元数量) {}".format(attention_result.shape))
print("Attention(注意力层) 权值参数: (一次训练所选取的样本数, 句子长度, 1) {}".format(attention_weights.shape))
decoder = Decoder(vocab_tar_size, embedding_dim, units, BATCH_SIZE)
sample_decoder_output, _, _ = decoder(tf.random.uniform((64, 1)),sample_hidden, sample_output)
print ('Decoder(解码器层) 输出层参数: (一次训练所选取的样本数, 字典大小) {}'.format(sample_decoder_output.shape))

在看一下我们模型设置的参数

大家可以进行微调,本人尝试过,效果影响不大。
在这里插入图片描述

三.模型训练

接下来有了模型,我们就可以进行训练了
简单来说训练注意力机制神经网络的步骤为:

  1. 定义编码器-解码器模型架构,其中编码器用于将输入序列编码成一个固定长度的向量,解码器用于生成输出序列。
    2.定义一个注意力机制,用于在每个时间步根据编码器的输出和解码器的当前状态,计算一个上下文向量,该向量可以提供给解码器作为额外的信息用于生成输出序列。
    3在训练过程中,对于每个时间步,首先通过编码器获得输入序列的编码结果,然后将该编码结果和解码器的当前状态作为注意力机制的输入,得到上下文向量,将上下文向量和解码器的当前输入拼接在一起,通过解码器生成输出序列的概率分布。
    4.定义损失函数,通常使用交叉熵损失函数或者自定义损失函数,用于评估模型输出和真实输出之间的差距。
    5.使用反向传播算法计算梯度,并使用优化算法更新模型参数,使得损失函数最小化。
    6.在测试过程中,对于每个输入序列,使用训练好的模型预测输出序列,得到最终的结果。
    完整代码:
num_examples = 20000
path_to_file = '/kaggle/input/tansantcmn/cmn.txt'
BATCH_SIZE = 70
input_tensor, target_tensor, inp_lang, targ_lang = load_dataset(path_to_file, num_examples)
max_length_targ, max_length_inp = max_length(target_tensor),max_length(input_tensor)
input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)
BUFFER_SIZE = len(input_tensor_train)
steps_per_epoch = len(input_tensor_train)//BATCH_SIZE
EPOCHS = 15
embedding_dim = 256
units = 1024
vocab_inp_size = len(inp_lang.word_index)+1
vocab_tar_size = len(targ_lang.word_index)+1
dataset = tf.data.Dataset.from_tensor_slices((input_tensor_train, target_tensor_train)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)  
encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)
attention_layer = BahdanauAttention(10)
decoder = Decoder(vocab_tar_size, embedding_dim, units, BATCH_SIZE)
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, reduction='none')
#定义优化器和损失函数
def loss_function(real, pred):
    mask = tf.math.logical_not(tf.math.equal(real, 0))
    loss_ = loss_object(real, pred)
    mask = tf.cast(mask, dtype=loss_.dtype)
    loss_ *= mask
    return tf.reduce_mean(loss_)
#定义保存点
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(optimizer=optimizer,
                                 encoder=encoder,
                                 decoder=decoder)
#TensorFlow 2.0引入的eager提高了代码的简洁性,但是性能略有损失。所以我们这里使用 @tf.function可以将eager模式转为高效的Graph模式,提高计算性能。
@tf.function
#定义一次训练中所要进行的工作:
def train_step(inp, targ, enc_hidden):
    #把损失初始化为0
    loss = 0
    with tf.GradientTape() as tape:
        # 把源语言(中文)输入编码器,并获得编码器的输出结果
        enc_output, enc_hidden = encoder(inp, enc_hidden)
        # 把编码器隐藏层数值,赋予解码器隐藏层,参与第一次的注意力计算
        dec_hidden = enc_hidden
        dec_input = tf.expand_dims([targ_lang.word_index['<start>']] * BATCH_SIZE, 1)
        #  循环目标句子中所有的词,将目标词作为下一个输入
        for t in range(1, targ.shape[1]):
            # 使用本单词、解码器的隐藏层、编码器的输出共同预测下一个单词。同时保留本次的解码器隐藏层,用于预测下一个词。
            predictions, dec_hidden, _ = decoder(dec_input, dec_hidden, enc_output)
            # 计算损失值,并累加。
            loss += loss_function(targ[:, t], predictions)
            # 使用teach_forcing
            # teach_forcing指的是,在训练时,每次解码器的输入并不是上次解码器的输出,而是样本目标语言对应单词。
            dec_input = tf.expand_dims(targ[:, t], 1)
        batch_loss = (loss / int(targ.shape[1]))
        # 需要训练参数是编码器的参数和解码的参数
        variables = encoder.trainable_variables + decoder.trainable_variables
        # 根据代价值计算下一次的参量值
        gradients = tape.gradient(loss, variables)
        # 将新的参量应用到模型
        optimizer.apply_gradients(zip(gradients, variables))
    return batch_loss

def evaluate(sentence):
     # 清空注意力图
    attention_plot = np.zeros((max_length_targ, max_length_inp))
    
     # 句子预处理
    sentence = preprocess_sentence(sentence)
    print(sentence)
    # 句子数字化
    inputs = [inp_lang.word_index[i] for i in sentence.split(' ')]
     # 按照最长句子长度补齐
    inputs = tf.keras.preprocessing.sequence.pad_sequences([inputs],maxlen=max_length_inp,padding='post')
    inputs = tf.convert_to_tensor(inputs)
    result = ''
    # 句子做编码
    hidden = [tf.zeros((1, units))]
    enc_out, enc_hidden = encoder(inputs, hidden)
     # 编码器隐藏层作为第一次解码器的隐藏层值
    dec_hidden = enc_hidden
    dec_input = tf.expand_dims([targ_lang.word_index['<start>']], 0)
    
    # 假设翻译结果不超过最长的样本句子
    for t in range(max_length_targ):
        
        # 逐个单词翻译
        predictions, dec_hidden, attention_weights = decoder(dec_input,dec_hidden,enc_out)
        # 存储注意力权重以便后面制图
        attention_weights = tf.reshape(attention_weights, (-1, ))
        attention_plot[t] = attention_weights.numpy()
        # 得到预测单词的字典编号
        predicted_id = tf.argmax(predictions[0]).numpy()
        # 从字典查到对应单词,用空格作分隔,与上一次结果进行拼接,组成翻译后的句子。
        result += targ_lang.index_word[predicted_id] + ' '
        # 如果是<end>表示翻译结束
        if targ_lang.index_word[predicted_id] == '<end>':
            return result, sentence, attention_plot
        # 预测的 ID 被保存起来,参与下一次预测。
        dec_input = tf.expand_dims([predicted_id], 0)
    return result, sentence, attention_plot
# 注意力权重制图函数
def plot_attention(attention, sentence, predicted_sentence):
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(1, 1, 1)
    ax.matshow(attention, cmap='viridis')
    fontdict = {'fontsize': 14}
    ax.set_xticklabels([''] + sentence, fontdict=fontdict, rotation=90)
    ax.set_yticklabels([''] + predicted_sentence, fontdict=fontdict)
    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))
    plt.show()
#翻译一个句子。
def translate(sentence):
    result, sentence, attention_plot = evaluate(sentence)
    print('Input: %s' % (sentence))
    print('Predicted translation: {}'.format(result))
    attention_plot = attention_plot[:len(result.split(' ')), :len(sentence.split(' '))]
    plot_attention(attention_plot, sentence.split(' '), result.split(' '))

if __name__=="__main__":
    # checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
    for epoch in range(EPOCHS):
        start = time.time()
        # 初始化隐藏层和损失值
        enc_hidden = encoder.initialize_hidden_state()
        total_loss = 0
        # 一个批次的训练
        for (batch, (inp, targ)) in enumerate(dataset.take(steps_per_epoch)):
#            print("targ") 演示目标值张量,暂时注释掉
#            print(targ) 演示目标值张量,暂时注释掉
            batch_loss = train_step(inp, targ, enc_hidden)
            total_loss += batch_loss
            # 每100次显示一下模型损失值
            if batch % 100 == 0:
                print('Epoch {} Batch {} Loss {:.4f}'.format(epoch + 1,batch,batch_loss.numpy()))
        # 每 2 个周期(epoch),保存(检查点)一次模型
        if (epoch + 1) % 2 == 0:
            checkpoint.save(file_prefix = checkpoint_prefix)
        # 显示每次迭代的损失值和消耗时间
        print('Epoch {} Loss {:.4f}'.format(epoch + 1,total_loss / steps_per_epoch))
        print('Time taken for 1 epoch {} sec\n'.format(time.time() - start))
    #恢复检查点目录 (checkpoint_dir) 中最新的检查点
    checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
    translate('book')

这里要说的是训练的epoch尽量在50个以上,否则效果不是很明显。
比如这里我训练了15个epoch loss=0.0928
在这里插入图片描述
可以看到把(book)翻译为了(在读书)

这个是我训练了30个epoch loss=0.02

在这里插入图片描述

同样是翻译 a book 结果居然出了繁体字 哈哈哈!


总结

当训练epoch在60个左右时,可以发现训练的效果有显著的提高,但还是会存在有测试翻译错误的现象,这里可能是因为所给的数据集只有13444条,数据太少的缘故,所以大家可以去换一个数据集。在文章中给的代码是英译中,只需要将预处理函数 if not is_chinese(w):中的not去掉,就可以为中译英了。
如果文章中有错误,欢迎读者指出共同进步。

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

入门两年的练习生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值