目录
一,数据预处理
1. preprocess_eng 函数
(1) 小写化和去首尾空格
(2) 标点符号与单词之间加空格
(3) 合并多个空格
(4) 替换非目标字符
(5) 去首尾空格
(6) 添加起始和竣事标志
2. preprocess_chinese 函数
(1) 小写化和去首尾空格
(2) 中文分词
(3) 词之间添加空格
(4) 添加起始和竣事标志
3. 小结
4. 注意事项
二,检查数据处理效果
三,编码器
1. __init__ 方法
(1) 参数说明
(2) 初始化模型组件
2. call 方法
(1) 输入参数
(2) 前向传播过程
(3) 返回值
3. initialize_hidden_state 方法
(1) 初始化方式
4. 编码器的作用
5. 注意事项
6. 小结
四,Attention
1. Bahdanau 注意力机制的原理
2. __init__ 方法
(1) 参数说明
(2) 初始化层
3. call 方法
(1) 输入参数
(2) 计算过程
(3) 返回值
4. 注意力机制的作用
5. 注意事项
6. 小结
五,解码器
1. __init__ 方法
(1) 参数说明
(2) 初始化模型组件
2. call 方法
(1) 输入参数
(2) 计算注意力机制
(3) 词嵌入
(4) 拼接上下文向量和词嵌入
(5) 输入 GRU
(6) 重塑输出
(7) 全连接层
(8) 返回值
3. 解码器的作用
4. 注意事项
5. 小结
利用华为云modelart作为实行环境
在北京4选一个tensorflow的就可以
进入环境,本篇重点剖析一下机器翻译(NLP)中解码器、自注意机制息争码器
参考来源:华为人才在线AI系列认证学习资料
home/user-name/work/ 的工作目录
data/目录
导入须要的库
- import re
- import os
- import io
- import time
- import jieba # 使用命令安装 !pip install jieba
- import numpy as np
- import tensorflow as tf
- import matplotlib.pyplot as plt
- import matplotlib.ticker as ticker
- from sklearn.model_selection import train_test_split
复制代码
一,数据预处理
- path_to_file = "data/cmn.txt" ## 数据集文件
- # 步骤 7 定义预处理函数
- def preprocess_eng(w):
- w = w.lower().strip()
- # 单词都小写,用空格切分
-
- # 单词和标点之间加空格
- # eg: "he is a boy." => "he is a boy ." white-spaces-keeping-punctuation # Reference:- https://stackoverflow.com/questions/3645931/python-padding-punctuation-with-
- w = re.sub(r"([?.!,])", r" \1 ", w)
-
-
- # 多个空格合并为一个
- w = re.sub(r'[" "]+', " ", w)
-
-
- # 除了(a-z, A-Z, ".", "?", "!", ",")这些字符外,全替换成空格
- w = re.sub(r"[^a-zA-Z?.!,]+", " ", w)
-
- w = w.rstrip().strip()
-
-
- # 增加开始结束标志,让模型知道何时停止预测
- w = '<start> ' + w + ' <end>'
-
- return w
- def preprocess_chinese(w):
- w = w.lower().strip()
-
- w = jieba.cut(w, cut_all=False, HMM=True)
- # HMM=True 马尔科夫链 计算sentences里每个词的发射概率、转移概率
- w = " ".join(list(w))
- # 词之间增加空格
- w = '<start> ' + w + ' <end>'
- return w
复制代码
表明:
用于预处理英文和中文文本数据。预处理是机器翻译中非常告急的步骤,它可以资助模型更好地理解和处理输入文本。
1. preprocess_eng 函数
这个函数的作用是对英文文本进行预处理,主要包括以下几个步骤:
(1) 小写化和去首尾空格
lower():将所有字符转换为小写,确保文本的同一性,制止大小写对模型的影响。
strip():去除字符串首尾的多余空格。
(2) 标点符号与单词之间加空格
- w = re.sub(r"([?.!,])", r" \1 ", w)
复制代码
利用正则表达式 re.sub,将标点符号(.、?、!、,)与单词之间添加空格。
([?.!,]):匹配标点符号。
r" \1 ":将匹配到的标点符号替换为前后各加一个空格的形式。
例如:"he is a boy." 转换为 "he is a boy ."
(3) 合并多个空格
- w = re.sub(r'[" "]+', " ", w)
复制代码
利用正则表达式将多个一连的空格替换为一个空格。
[" "]+:匹配一个或多个一连的空格。
替换为单个空格,确保文本中没有多余的空格。
(4) 替换非目标字符
- w = re.sub(r"[^a-zA-Z?.!,]+", " ", w)
复制代码
利用正则表达式将非目标字符替换为空格。
[^a-zA-Z?.!,]+:匹配不在 a-z、A-Z、.、?、!、, 范围内的字符。
替换为单个空格,确保文本中只包含目标字符。
(5) 去首尾空格
rstrip():去除字符串尾部的多余空格。
strip():再次去除字符串首尾的多余空格。
(6) 添加起始和竣事标志
- w = '<start> ' + w + ' <end>'
复制代码
在文本的开头添加 <start>,在结尾添加 <end>。
这是为了让模型知道何时开始预测和何时竣事预测,通常用于序列生成使命(如机器翻译)。
2. preprocess_chinese 函数
这个函数的作用是对中文文本进行预处理,主要包括以下几个步骤:
(1) 小写化和去首尾空格
与英文预处理雷同,将文本转换为小写并去除首尾空格。
(2) 中文分词
- w = jieba.cut(w, cut_all=False, HMM=True)
复制代码
利用 jieba 库进行中文分词。
cut_all=False:利用精确模式分词,分词结果更准确。
HMM=True:利用隐马尔可夫模型(HMM)进行分词,可以更好地处理未登录词。
(3) 词之间添加空格
将分词结果转换为一个字符串,词之间用空格分隔。
这样可以将中文文本转换为类似英文的格式,方便模型处理。
(4) 添加起始和竣事标志
- w = '<start> ' + w + ' <end>'
复制代码
与英文预处理雷同,添加起始和竣事标志。
3. 小结
这段代码实现了对英文和中文文本的预处理
- 同一文本格式:将文本转换为小写,去除多余空格,确保格式一致。
- 处理标点符号:通过在标点符号周围添加空格,让模型更好地理解句子布局。
- 分词处理:对中文文本进行分词,将中文句子转换为词序列。
- 添加起始和竣事标志:让模型明确知道何时开始和竣事预测。
4. 注意事项
- 正则表达式的利用:正则表达式是文本处理的强大工具,但需要根据具体需求调解。
- 分词工具的选择:对于中文分词,jieba 是常用的工具,但也可以选择其他分词工具(如 HanLP 或 SnowNLP)。
- 起始和竣事标志:这些标志是序列生成使命中常用的本领,但需要确保在训练和预测时一致利用。
二,检查数据处理效果
界说好了我们需要给模型传入的数据格式,检察效果:
- en_sentence = "May I borrow this book?"
- chn_sentence = "我可以借这本书吗?"
- print(preprocess_eng(en_sentence))
- print(preprocess_chinese(chn_sentence))
复制代码
每条数据的格式已经转换成<start>word word 符号<end>的格式
- # 步骤 8 加载数据集,并进行预处理操作
- '''
- 加载原始数据集
- 预处理
- 文本转 id
- padding,统一成相同的长度定义数据加载函数
- '''
- # 读取数据,每个元素的样式是 [英文, 中文]
- def create_dataset(path, num_examples=None):
- lines = open(path, encoding='UTF-8').read().strip().split('\n')
- word_pairs = [[w for w in l.split('\t')] for l in lines[:num_examples]]
- # [英文, 中文] 的词典
- word_pairs = [[preprocess_eng(w[0]), preprocess_chinese(w[1])] for w in word_pairs]
- return word_pairs
- word_pairs = create_dataset(path_to_file)
- # 展示前20 个数据
- word_pairs[:20]
复制代码
用en,chn两个变量接一下我们刚才构造的词典,并检察最后两条
- # 把中文、英文分开:
- en, chn = zip(*create_dataset(path_to_file))
- print(en[-1])
- print(chn[-1])
- # 显示数据大小
- print(len(en), len(chn))
复制代码
同一输入input的范式,同时对输入的数据进行类似‘标准化’的处理,再界说好特征数据(en)和目标数据(chn)
- def max_length(tensor):
- # 取数据中的最大文本长度,用来将所有文本统一成一致的长度,模型才能够正常训练
- return max(len(t) for t in tensor)
- def tokenize(lang):
- # 1. 分词
- # 2. 转换成id
- # 3. padding, 将每个句子统一成相同的长度,长度不足 的 后面 补0
- lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(filters='')
- # 生成 词和id 的映射词典 {word:id}
- lang_tokenizer.fit_on_texts(lang)
- # 将词转换成对应的id
- text_ids = lang_tokenizer.texts_to_sequences(lang)
- # 统一成相同的长度
- padded_text_ids = tf.keras.preprocessing.sequence.pad_sequences(text_ids, padding='post')
-
- return padded_text_ids, lang_tokenizer
-
-
- def load_dataset(path, num_examples=None):
- # 加载数据,并做预处理
- # 将中文设置为源语言,英文设置为目标语言
- targ_lang, inp_lang = zip(*create_dataset(path, num_examples))
- input_data, inp_lang_tokenizer = tokenize(inp_lang)
- target_data, targ_lang_tokenizer = tokenize(targ_lang)
-
- return input_data, target_data, inp_lang_tokenizer, targ_lang_tokenizer
复制代码
三,编码器
Encoder
- class Encoder(tf.keras.Model):
- def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
- # vacab_size=vocab_inp_size=9394, embedding_dim=256 enc_units=1024 batch_sz=64
- 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_activation='sigmoid',
- recurrent_initializer='glorot_uniform')
-
- def call(self, x, hidden):
- # x 是训练数据,shape == (batch_size,max_length) -> (64, 46)
- # embedding 后得到每个词的词向量, x shape == (batch_size, max_length, embedding_dim) -> (64, 46, 256)
- x = self.embedding(x)
-
- #在GRU 中,每一个时间步,输出层和隐藏层是相等的
- # output 是所有时间步的输出层输出 shape == (batch_size, max_length, units) -> (64, 46, 1024)
- # state 是最后一个时间步的隐藏层输出, shape == (batch_size, units) -> (64, 1024)
- output, state = self.gru(x, initial_state=hidden)
- return output, state
- def initialize_hidden_state(self):
- #初始化gru 的隐层参数, shape == (batch_size, units) -> (64,1024)
- return tf.zeros((self.batch_sz, self.enc_units))
复制代码
这个 Encoder 类是基于 TensorFlow 的 tf.keras.Model 构建的,用于机器翻译使命中的编码器部分。编码器的主要作用是将输入序列(如中文句子)转换为上下文向量(context vector),以便解码器(Decoder)能够基于这个上下文信息生成目标序列(如英文翻译)。
1. __init__ 方法
__init__ 方法是类的初始化函数,用于界说模型的布局和参数。
(1) 参数说明
- vocab_size:输入词汇表的大小。例如,如果输入是中文句子,vocab_size 表现中文词汇表的大小。
- embedding_dim:词嵌入的维度。词嵌入(Embedding)是将词汇表中的每个词映射到一个固定维度的向量空间中,以便模型能够处理。
- enc_units:GRU 单位的数目。GRU(Gated Recurrent Unit)是一种循环神经网络(RNN)的变体,用于处理序列数据。enc_units 表现 GRU 的匿伏层单位数。
- batch_sz:每批次的样本数目。在训练过程中,数据通常以批次的形式输入模型。
(2) 初始化模型组件
- 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')
复制代码
- tf.keras.layers.Embedding:词嵌入层,将输入的词汇索引转换为固定维度的词向量。
- vocab_size:词汇表大小。
- embedding_dim:词向量的维度。
- tf.keras.layers.GRU:GRU 层,用于处理序列数据。
- self.enc_units:GRU 的匿伏层单位数。
- return_sequences=True:返回所偶然间步的输出(用于后续的注意力机制)。
- return_state=True:返回最后一个时间步的匿伏状态(用于初始化解码器)。
- recurrent_initializer='glorot_uniform':GRU 的循环核初始化方式。
2. call 方法
call 方法是模型的前向传播函数,界说了输入数据怎样通过模型进行计算。
(1) 输入参数
- x:输入序列,形状为 (batch_size, max_length),例如 (64, 46)。max_length 是序列的最大长度,64 是批次大小。
- hidden:GRU 的初始匿伏状态,形状为 (batch_size, enc_units),例如 (64, 1024)。
(2) 前向传播过程
- x = self.embedding(x)
- output, state = self.gru(x, initial_state=hidden)
复制代码
- 词嵌入层:
- 输入 x 是词汇索引序列,形状为 (batch_size, max_length)。
- 通过词嵌入层后,每个词被转换为一个固定维度的向量,形状变为 (batch_size, max_length, embedding_dim)。
- GRU 层:
- 输入为嵌入后的序列 x 和初始匿伏状态 hidden。
- 输出:
- output:所偶然间步的输出,形状为 (batch_size, max_length, enc_units)。
- state:最后一个时间步的匿伏状态,形状为 (batch_size, enc_units)。
(3) 返回值
- output:GRU 的所偶然间步输出,用于后续的注意力机制。
- state:GRU 的最后一个时间步的匿伏状态,用于初始化解码器。
3. initialize_hidden_state 方法
这个方法用于初始化 GRU 的初始匿伏状态。
(1) 初始化方式
- return tf.zeros((self.batch_sz, self.enc_units))
复制代码
初始化为全零张量,形状为 (batch_size, enc_units)。
这种初始化方式简单且常见,但在某些环境下也可以尝试其他初始化方法(如随机初始化)。
4. 编码器的作用
在机器翻译使命中,编码器的作用是将输入序列(如中文句子)转换为上下文信息,以便解码器能够基于这些信息生成目标序列(如英文翻译)。
词嵌入:将输入的词汇索引转换为词向量。
序列处理:通过 GRU 处理序列数据,提取时间步的特征。
上下文向量:GRU 的最后一个时间步的匿伏状态(state)可以作为上下文向量,传递给解码器。
5. 注意事项
词嵌入层的作用:
词嵌入将离散的词汇索引转换为一连的向量空间,便于模型学习词汇之间的关系。
嵌入维度(embedding_dim)是一个超参数,需要根据使命调解。
GRU 的选择:
GRU 是一种高效的循环神经网络变体,适用于处理序列数据。
如果序列长度较长,可以思量利用 LSTM(长短期影象网络)以制止梯度消失问题。
初始匿伏状态:
在某些环境下,可以尝试利用非零初始化(如随机初始化)来观察模型性能的变化。
返回序列和状态:
return_sequences=True 和 return_state=True 是为了后续的注意力机制息争码器初始化。
6. 小结
这个 Encoder 类是一个范例的基于 GRU 的编码器,用于机器翻译使命。它通过词嵌入和 GRU 层将输入序列转换为上下文信息,为解码器提供须要的输入。编码器的设计简洁而高效,是序列到序列(Seq2Seq)模型中的关键部分。
四,Attention
- # 步骤 11 定义Attention 层
- class BahdanauAttention(tf.keras.Model):
- 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 shape == (batch_size, hidden size)
- #扩展时间维度 shape == (batch_size, 1, hidden size)
-
- #为了计算后面的 score
- hidden_with_time_axis = tf.expand_dims(query, 1)
-
- # score shape == (batch_size, max_length, 1)
- # score 维度为1 是因为应用了self.V, V 的维度是1
-
- # 应用self.V 前后的维度是 (batch_size, max_length, units) --> (batch_size, max_length, 1)
- score = self.V(tf.nn.tanh(self.W1(values) + self.W2(hidden_with_time_axis)))
-
-
- # 使用softmax 得到attention 的权重, attention_weights shape == (batch_size, max_length, 1)
- attention_weights = tf.nn.softmax(score, axis=1)
-
-
- # context_vector shape == (batch_size, max_length, hidden_size)
- context_vector = attention_weights * values
- # 相加后的attention 上下文向量的维度:shape context_vector == (batch_size, hidden_size)
- context_vector = tf.reduce_sum(context_vector, axis=1)
-
- return context_vector, attention_weights
复制代码
这个 BahdanauAttention 类实现了 Bahdanau 注意力机制(Attention Mechanism),是机器翻译使命中解码器(Decoder)的关键部分。注意力机制的作用是让解码器在生成目标序列的每个词时,能够动态地关注输入序列(编码器输出)中最告急的部分,从而提高翻译的准确性和流畅性。
1. Bahdanau 注意力机制的原理
Bahdanau 注意力机制是一种加性注意力机制,其焦颔首脑是通过学习一个权重分布(attention_weights),将编码器的输出(values)加权求和,得到一个上下文向量(context_vector)。这个上下文向量包含了当前解码步骤中最相关的输入信息,资助解码器更好地生成目标词。
具体步骤如下:
计算分数(score):基于当前解码器的匿伏状态(query)和编码器的所偶然间步输出(values),计算每个时间步的分数。
计算注意力权重(attention_weights):通过 softmax 函数将分数归一化为权重。
计算上下文向量(context_vector):将注意力权重与编码器的输出相乘并加权求和,得到上下文向量。
2. __init__ 方法
__init__ 方法用于初始化注意力机制的参数。
(1) 参数说明
units:注意力机制的匿伏单位数。这个参数决定了注意力层的内部维度。
(2) 初始化层
- self.W1 = tf.keras.layers.Dense(units)
- self.W2 = tf.keras.layers.Dense(units)
- self.V = tf.keras.layers.Dense(1)
复制代码
W1:用于对编码器的输出(values)进行变换。
W2:用于对解码器的当前匿伏状态(query)进行变换。
V:用于将加性注意力的输出压缩为一个分数(score)。
3. call 方法
call 方法界说了注意力机制的具体计算过程。
(1) 输入参数
query:解码器的当前匿伏状态,形状为 (batch_size, hidden_size)。
values:编码器的所偶然间步输出,形状为 (batch_size, max_length, hidden_size)。
(2) 计算过程
扩展时间维度
- hidden_with_time_axis = tf.expand_dims(query, 1)
复制代码
将 query 的形状从 (batch_size, hidden_size) 扩展为 (batch_size, 1, hidden_size),以便与 values 的时间维度对齐。
计算分数(score)
- score = self.V(tf.nn.tanh(self.W1(values) + self.W2(hidden_with_time_axis)))
复制代码
self.W1(values):对编码器的输出进行变换,形状为 (batch_size, max_length, units)。
self.W2(hidden_with_time_axis):对解码器的当前匿伏状态进行变换,形状为 (batch_size, 1, units)。
tf.nn.tanh(...):将两个变换后的张量相加并通过 tanh 激活函数,形状为 (batch_size, max_length, units)。
self.V(...):将激活后的张量压缩为一个分数,形状为 (batch_size, max_length, 1)。
计算注意力权重(attention_weights)
- attention_weights = tf.nn.softmax(score, axis=1)
复制代码
- 利用 softmax 函数将分数归一化为权重,形状为 (batch_size, max_length, 1)。
- 权重表现每个时间步的告急性。
计算上下文向量(context_vector)
- context_vector = attention_weights * values
- context_vector = tf.reduce_sum(context_vector, axis=1)
复制代码
将注意力权重与编码器的输出相乘,形状为 (batch_size, max_length, hidden_size)。
沿时间维度(axis=1)加权求和,得到上下文向量,形状为 (batch_size, hidden_size)。
(3) 返回值
- context_vector:上下文向量,包含了当前解码步骤中最相关的输入信息。
- attention_weights:注意力权重,表现每个时间步的告急性。
4. 注意力机制的作用
在机器翻译使命中,注意力机制的作用是让解码器在生成目标序列的每个词时,能够动态地关注输入序列中最相关的部分。具体来说:
动态关注:注意力权重是动态计算的,取决于当前解码器的状态和编码器的输出。
上下文信息:上下文向量将编码器的输出与解码器的当前状态相结合,为解码器提供更丰富的信息。
提高性能:注意力机制可以显著提高翻译的准确性和流畅性,由于它允许模型在生成每个词时只关注最相关的输入部分。
5. 注意事项
units 参数的选择:
units 是注意力机制的匿伏单位数,通常与编码器息争码器的匿伏单位数一致(如 enc_units 或 dec_units)。
如果 units 太小,可能会导致信息丢失;如果太大,可能会增加计算成本。
tf.expand_dims 的作用:
tf.expand_dims 用于扩展张量的维度,以便在计算中对齐形状。例如,将 query 的形状从 (batch_size, hidden_size) 扩展为 (batch_size, 1, hidden_size),使其能够与 values 的时间维度对齐。
softmax 的作用:
softmax 函数将分数归一化为权重,确保所有权重的和为 1。这使得注意力权重表现每个时间步的相对告急性。
tf.reduce_sum 的作用:
tf.reduce_sum 沿指定维度(时间维度)加权求和,得到上下文向量。上下文向量的形状为 (batch_size, hidden_size),表现当前解码步骤的上下文信息。
6. 小结
这个 BahdanauAttention 类实现了 Bahdanau 注意力机制,是机器翻译使命中解码器的关键部分。它通过动态计算注意力权重,将编码器的输出加权求和,得到上下文向量,从而为解码器提供最相关的输入信息。注意力机制不光提高了翻译的准确性,还使得模型能够更好地处理长序列数据。这个实现简洁而高效,是 Seq2Seq 模型中常用的注意力机制之一。
五,解码器
Decoder
- # 步骤 12 定义Decoder
- class Decoder(tf.keras.Model):
- def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
- # vocab_size=vocab_tar_size=6082, embedding_dim=256, dec_units=1024, batch_sz=64
- 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')
- # 输出的维度是目标语言词汇表的大小,返回的是softmax 概率,词汇表中每一个词的概率
- self.fc = tf.keras.layers.Dense(vocab_size)
- # attention
- self.attention = BahdanauAttention(self.dec_units)
-
-
- def call(self, x, hidden, enc_output):
- # This function outputs a result at each timestamp
- # 计算decoder 的第一个隐状态和encoder 所有输入之间的attention 权重, 得到上下文向量, context_vector
- context_vector, attention_weights = self.attention(hidden, enc_output)
-
-
- # embedding 后的维度 == (batch_size, 1, embedding_dim)
- x = self.embedding(x)
-
-
- # 把上下文向量context_vector 和 输入embedding 拼接在一起
- # context_vector shape == (batch_size, units) -> (64, 1024)
-
- # 拼接后的数据维度 == (batch_size, 1, embedding_dim + hidden_size) -> (64, 1, 1024 + 256)
- x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
-
-
- # 把拼接后的向量输入gru
- # 得到当前时间步的输出和隐状态
- (64,1024) # output shape == (batch_size, 1, units) -> (64, 1, 1024), state shape == (batch_size, units) ->
- output, state = self.gru(x)
-
-
- # output shape == (batch_size, hidden_size=1024)
- output = tf.reshape(output, (-1, output.shape[2]))
-
-
- # output shape == (batch_size, vocab) -> (64, 6082)
- x = self.fc(output)
-
- return x, state, attention_weights
复制代码
这个 Decoder 类是机器翻译使命中的解码器部分,其主要作用是基于编码器的输出和当前解码器的状态,逐步生成目标序列(如英文翻译)。解码器结合了注意力机制(BahdanauAttention),能够动态地关注输入序列中最相关的部分,从而提高翻译的质量和流畅性。
1. __init__ 方法
__init__ 方法用于初始化解码器的布局和参数。
(1) 参数说明
- vocab_size:目标语言词汇表的大小,例如英文词汇表的大小。
- embedding_dim:词嵌入的维度,用于将目标语言的词汇索引转换为词向量。
- dec_units:解码器 GRU 的匿伏单位数。
- batch_sz:每批次的样本数目。
(2) 初始化模型组件
- self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
- self.gru = tf.keras.layers.GRU(dec_units,
- return_sequences=True,
- return_state=True,
- recurrent_initializer='glorot_uniform')
- self.fc = tf.keras.layers.Dense(vocab_size)
- self.attention = BahdanauAttention(dec_units)
复制代码
tf.keras.layers.Embedding:将目标语言的词汇索引转换为词向量。
tf.keras.layers.GRU:解码器的焦点,用于处理序列数据。return_sequences=True 和 return_state=True 确保返回所偶然间步的输出和最后一个时间步的匿伏状态。
tf.keras.layers.Dense:全连接层,将 GRU 的输出映射为目标语言词汇表大小的分布(softmax 概率)。
BahdanauAttention:注意力机制模块,用于计算上下文向量。
2. call 方法
call 方法界说了解码器的前向传播过程
(1) 输入参数
- x:当前时间步的输入,通常是目标语言的词汇索引,形状为 (batch_size, 1)。
- hidden:解码器的当前匿伏状态,形状为 (batch_size, dec_units)。
- enc_output:编码器的所偶然间步输出,形状为 (batch_size, max_length, enc_units)。
(2) 计算注意力机制
- context_vector, attention_weights = self.attention(hidden, enc_output)
复制代码
- 利用 BahdanauAttention 模块计算上下文向量(context_vector)和注意力权重(attention_weights)。
- context_vector 的形状为 (batch_size, dec_units),表现当前时间步的上下文信息。
- attention_weights 的形状为 (batch_size, max_length, 1),表现每个时间步的注意力权重。
(3) 词嵌入
将输入的词汇索引转换为词向量,形状为 (batch_size, 1, embedding_dim)。
(4) 拼接上下文向量和词嵌入
- x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
复制代码
将上下文向量(context_vector)和词嵌入(x)拼接在一起。
context_vector 的形状为 (batch_size, dec_units),扩展为 (batch_size, 1, dec_units)。
拼接后的形状为 (batch_size, 1, embedding_dim + dec_units)。
(5) 输入 GRU
- output, state = self.gru(x)
复制代码
将拼接后的向量输入 GRU,得到当前时间步的输出和匿伏状态。
output 的形状为 (batch_size, 1, dec_units)。
state 的形状为 (batch_size, dec_units)。
(6) 重塑输出
- output = tf.reshape(output, (-1, output.shape[2]))
复制代码
将 output 的形状从 (batch_size, 1, dec_units) 重塑为 (batch_size, dec_units)。
(7) 全连接层
将 GRU 的输出通过全连接层,映射为目标语言词汇表大小的分布(softmax 概率)。
输出的形状为 (batch_size, vocab_size)。
(8) 返回值
- x:当前时间步的目标语言词汇表的分布(softmax 概率)。
- state:解码器的下一个匿伏状态。
- attention_weights:注意力权重,用于可视化注意力机制。
3. 解码器的作用
解码器的主要使命是基于编码器的输出和当前状态,逐步生成目标序列。具体来说:
注意力机制:通过 BahdanauAttention 动态计算上下文向量,关注输入序列中最相关的部分。
词嵌入:将目标语言的词汇索引转换为词向量。
GRU:处理序列数据,生成当前时间步的输出和匿伏状态。
全连接层:将 GRU 的输出映射为目标语言词汇表的分布,生成下一个词的概率。
4. 注意事项
输入形状:
解码器的输入 x 是目标语言的词汇索引,形状为 (batch_size, 1)。这是由于解码器是逐步生成目标序列的,每次只处理一个时间步。
hidden 是解码器的当前匿伏状态,形状为 (batch_size, dec_units)。
enc_output 是编码器的所偶然间步输出,形状为 (batch_size, max_length, enc_units)。
拼接操作:
解码器将上下文向量和词嵌入拼接在一起,形状为 (batch_size, 1, embedding_dim + dec_units)。这种拼接方式使得解码器能够同时利用编码器的上下文信息和当前输入的信息。
GRU 的输出:
GRU 的输出 output 的形状为 (batch_size, 1, dec_units),需要通过 tf.reshape 重塑为 (batch_size, dec_units),以便输入全连接层。
全连接层的输出:
全连接层的输出是目标语言词汇表的分布,形状为 (batch_size, vocab_size)。这个分布可以用于选择下一个词。
注意力权重:
注意力权重的形状为 (batch_size, max_length, 1),表现每个时间步的注意力权重。这些权重可以用于可视化注意力机制,资助理解模型的行为。
5. 小结
这个 Decoder 类是机器翻译使命中的关键部分,结合了注意力机制和 GRU,能够动态地生成目标序列。解码器通过以下步骤实现:
利用注意力机制计算上下文向量;
将上下文向量与当前输入的词嵌入拼接;
利用 GRU 处理拼接后的向量,生成当前时间步的输出和匿伏状态;
通过全连接层将输出映射为目标语言词汇表的分布。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |