人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)
Pytorch:Transformer(Encoder编码器-Decoder解码器、多头注意力机制、多头自注意力机制、掩码张量、前馈全连接层、规范化层、子层连接结构、pyitcast) part1
Pytorch:Transformer(Encoder编码器-Decoder解码器、多头注意力机制、多头自注意力机制、掩码张量、前馈全连接层、规范化层、子层连接结构、pyitcast) part2
Pytorch:解码器端的Attention注意力机制、seq2seq模型架构实现英译法任务
BahdanauAttention注意力机制、LuongAttention注意力机制
BahdanauAttention注意力机制:基于seq2seq的西班牙语到英语的机器翻译任务、解码器端的Attention注意力机制、seq2seq模型架构
图片的描述生成任务、使用迁移学习实现图片的描述生成过程、CNN编码器+RNN解码器(GRU)的模型架构、BahdanauAttention注意力机制、解码器端的Attention注意力机制
注意力机制、bmm运算
注意力机制 SENet、CBAM
机器翻译 MXNet(使用含注意力机制的编码器—解码器,即 Encoder编码器-Decoder解码器框架 + Attention注意力机制)
基于Seq2Seq的中文聊天机器人编程实践(Encoder编码器-Decoder解码器框架 + Attention注意力机制)
基于Transformer的文本情感分析编程实践(Encoder编码器-Decoder解码器框架 + Attention注意力机制 + Positional Encoding位置编码)
注意:这一文章“基于Transformer的文本情感分析编程实践(Encoder编码器-Decoder解码器框架 + Attention注意力机制 + Positional Encoding位置编码)”
该文章实现的Transformer的Model类型模型,实际是改造过的特别版的Transformer,因为Transformer的Model类型模型中只实现了Encoder编码器,
而没有对应实现的Decoder解码器,并且因为当前Transformer的Model类型模型处理的是分类任务,
所以我们此处只用了Encoder编码器来提取特征,最后通过全连接层网络来拟合分类。
tensorflow提供了两种Attention Mechanisms(注意力机制)
1.BahdanauAttention注意力机制:tfa.seq2seq.BahdanauAttention
实现BahdanauAttention风格的(additive累积)注意力机制。
2.LuongAttention注意力机制:tfa.seq2seq.LuongAttention
实现LuongAttention风格的(multiplicative乘法)注意力机制
构建BahdanauAttention注意力机制类的伪代码:
# 这里实现 BahdanauAttention注意力机制
1, score = FC(tanh(FC(EO) + FC(H)))
2, attention weights = softmax(score, axis = 1).
解释: Softmax 默认被应用于最后一个轴,但是这里我们想将它应用于第一个轴,
因为分数 (score) 的形状是 (批大小,最大长度,隐层大小),最大长度 (max_length) 是输入的长度。
因为我们想为每个输入长度分配一个权重,所以softmax应该用在这个轴上。
3, context vector = sum(attention weights * EO, axis = 1)
解释: 选择第一个轴的原因同上.
4, embedding output = 解码器输入 X 通过一个嵌入层
5, merged vector = concat(embedding output, context vector)
符号代表:
FC: 全连接层
EO: 编码器输出
H: 隐藏层状态
X: 解码器输入
#构建BahdanauAttention注意力机制类:
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)
"""
传入值:
features:编码器的输出,(64, 16, 1024) 即 (BATCH_SIZE, 输入序列最大长度句子的长度, 隐藏层中的隐藏神经元数量)
hidden:解码器的隐层输出状态,(64, 1024) 即 (batch_size, hidden_size) (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
返回值:
attention_result:(64, 1024) 即 (batch size, units) (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
attention_weights:(64, 16, 1) 即 (batch_size, sequence_length, 1) (BATCH_SIZE, 输入序列最大长度句子的长度, 1)
"""
def call(self, features, hidden):
"""
description: 具体计算函数
:param features: 编码器的输出
:param hidden: 解码器的隐层输出状态
return: 通过注意力机制处理后的结果和注意力权重attention_weights
"""
"""
1.hidden_with_time_axis = tf.expand_dims(hidden, 1)
解码器的隐层输出状态hidden,(64, 1024) 即 (batch_size, hidden_size) (BATCH_SIZE, 隐藏层中的隐藏神经元数量)。
hidden扩展一个维度从(64, 1024)变成(64, 1,1024)。
2.score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
计算注意力得分score。
features:编码器的输出,(64, 16, 1024)。
hidden_with_time_axis:解码器的隐层输出状态,(64, 1,1024)
W1和W2:Dense(隐藏层中的隐藏神经元数量1024)
tanh(W1(features) + W2(hidden_with_time_axis)):
---> tanh(W1((64, 16, 1024)) + W2((64, 1,1024)))
---> tanh((64, 16, 1024))
---> (64, 16, 1024) 即 (BATCH_SIZE, 输入序列最大长度句子的长度, 隐藏层中的隐藏神经元数量)
3.attention_weights = tf.nn.softmax(self.V(score), axis=1)
计算注意力权重attention_weights。
V:Dense(隐藏层中的隐藏神经元数量1)
softmax(V(score), axis=1)
---> softmax(V((64, 16, 1024)), axis=1)
---> softmax((64, 16, 1), axis=1)
---> (64, 16, 1) 即 (BATCH_SIZE, 输入序列最大长度句子的长度, 1)
因为注意力得分score的形状是(BATCH_SIZE, 输入序列最大长度句子的长度, 隐藏层中的隐藏神经元数量),
输入序列最大长度句子的长度(max_length)是输入的长度。
因为我们想为每个输入长度分配一个权重,所以softmax应该用在第一个轴(max_length)上axis=1,
而softmax默认被应用于最后一个轴axis=-1。
4.context_vector = tf.reduce_sum(attention_weights * features, axis=1)
获得注意力机制处理后的结果context_vector。
reduce_sum(attention_weights * features, axis=1)
---> reduce_sum((64, 16, 1) * (64, 16, 1024), axis=1)
---> reduce_sum((64, 16, 1024), axis=1)
---> (64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
"""
# 为hidden扩展一个维度(batch_size, hidden_size) --> (batch_size, 1, hidden_size)
hidden_with_time_axis = tf.expand_dims(hidden, 1)
# 根据公式计算注意力得分, 输出score的形状为: (batch_size, 16, hidden_size)
score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
# 根据公式计算注意力权重, 输出attention_weights形状为: (batch_size, 16, 1)
attention_weights = tf.nn.softmax(self.V(score), axis=1)
# 最后根据公式获得注意力机制处理后的结果context_vector
# context_vector的形状为: (batch_size, hidden_size)
context_vector = attention_weights * features
context_vector = tf.reduce_sum(context_vector, axis=1)
return context_vector, attention_weights
#调用:
# attention_layer = BahdanauAttention(1024)
# attention_result, attention_weights = attention_layer(sample_output, sample_hidden)
# print("Attention result shape: (batch size, units) {}".format(attention_result.shape)) #(64, 1024)
# print("Attention weights shape: (batch_size, sequence_length, 1) {}".format(attention_weights.shape)) #(64, 16, 1)
"""
构建RNN解码器:这里RNN是指GRU, 同时在解码器中使用注意力机制.
"""
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')
# 实例化一个Dense层作为输出层
self.fc = tf.keras.layers.Dense(vocab_size)
# 在解码器阶段我们将使用注意力机制,这里实例化注意力的类
self.attention = BahdanauAttention(self.dec_units)
"""
1.x = self.embedding(x)
输入:(64, 1) 64行1列,批量大小句子数为64,1列为该行句子的第N列的单词
输出:(64, 1, 256) (BATCH_SIZE, 输入序列最大长度句子的长度, 嵌入维度)
2.context_vector, attention_weights = self.attention(hidden, enc_output)
attention_weights注意力权重:(64, 16, 1) 即 (BATCH_SIZE, 输入序列最大长度句子的长度, 1)
context_vector注意力机制处理后的结果:(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
3.x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
tf.expand_dims(context_vector, 1):(64, 1, 1024) 即 (BATCH_SIZE, 1, 隐藏层中的隐藏神经元数量)
concat([(64, 1, 1024),(64, 1, 256)], axis=-1):1024+256=1280,最终输出 (64, 1, 1280)
4.GRU
1.tf.keras.layers.GRU(self.dec_units, return_sequences=True, return_state=True, recurrent_initializer='glorot_uniform')
return_sequences:
布尔值。是返回输出序列中的最后一个输出还是完整序列。 默认值:False。
True代表返回GRU序列模型的每个时间步的输出(每个输出做连接操作)
return_state:
布尔值。 除输出外,是否返回最后一个状态。 默认值:False。
True代表除了返回输出外,还需要返回最后一个隐层状态。
recurrent_initializer:
recurrent_kernel权重矩阵的初始化程序,用于对递归状态进行线性转换。 默认值:正交。
'glorot_uniform'即循环状态张量的初始化方式为均匀分布。
2.output, state = gru(x)
output:
(64, 1, 1024) 即 (BATCH_SIZE, 1, 隐藏层中的隐藏神经元数量)
(当前批次的样本个数, 当前样本的序列长度(单词个数), 隐藏层中神经元数量 * 1)
state:
(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
5.output = tf.reshape(output, (-1, output.shape[2]))
(-1, output.shape[2]):表示把(64, 1, 1024)转换为(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
6.x = self.fc(output)
x:(64, 4935) 即 (BATCH_SIZE, 目标序列的不重复单词的总数作为目标序列的字典大小)
"""
def call(self, x, hidden, enc_output):
"""
:param x: 每个时间步上解码器的输入
:param hidden: 每次解码器的隐层输出
:param enc_output: 编码器的输出
"""
# print("x.shape",x.shape) #(64, 1)。64行1列,批量大小句子数为64,1列为该行句子的第N列的单词
# 输入通过embedding层
x = self.embedding(x)
# print("x1.shape",x.shape) #(64, 1, 256)。(BATCH_SIZE, 输入序列最大长度句子的长度, 嵌入维度)
# 使用注意力规则计算hidden与enc_output的'相互影响程度'
context_vector, attention_weights = self.attention(enc_output, hidden)
# print("tf.expand_dims(context_vector, 1).shape",tf.expand_dims(context_vector, 1).shape) #(64, 1, 1024)
# 将这种'影响程度'与输入x拼接(这个操作也是注意力计算规则的一部分)
x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
# print("x2.shape",x.shape) #(64, 1, 1280)
# 将新的x输入到gru层中得到输出
output, state = self.gru(x)
# print("output1.shape",output.shape) #(64, 1, 1024) 即 (BATCH_SIZE, 1, 隐藏层中的隐藏神经元数量)
# print("state.shape",state.shape) #(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
# 改变输出形状使其适应全连接层的输入形式
output = tf.reshape(output, (-1, output.shape[2]))
# print("output2.shape",output.shape) #(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
# 使用全连接层作为输出层
# 输出的形状 == (批大小,vocab)
x = self.fc(output)
# print("x3.shape",x.shape) #(64, 4935) 即 (BATCH_SIZE, 目标序列的不重复单词的总数作为目标序列的字典大小)
return x, state, attention_weights
#调用:
decoder = Decoder(vocab_tar_size, embedding_dim, units, BATCH_SIZE)
# sample_decoder_output, _, _ = decoder(tf.random.uniform((64, 1)), sample_output, sample_hidden)
# print ('Decoder output shape: (batch_size, vocab size) {}'.format(sample_decoder_output.shape)) #(64, 4935)
#构建模型的编码器部分:
class Encoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
"""
:param vocab_size: 非重复的词汇总数
:param embedding_dim: 词嵌入的维度
:enc_units: 编码器中GRU层的隐含节点数
:batch_sz: 数据批次大小(每次参数更新用到的数据量)
"""
super(Encoder, self).__init__()
# 将变量传入类中
self.batch_sz = batch_sz
self.enc_units = enc_units
# 实例化embedding层
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
"""
return_sequences:
布尔值。是返回输出序列中的最后一个输出还是完整序列。 默认值:False。
True代表返回GRU序列模型的每个时间步的输出(每个输出做连接操作)
return_state:
布尔值。 除输出外,是否返回最后一个状态。 默认值:False。
True代表除了返回输出外,还需要返回最后一个隐层状态。
recurrent_initializer:
recurrent_kernel权重矩阵的初始化程序,用于对递归状态进行线性转换。 默认值:正交。
'glorot_uniform'即循环状态张量的初始化方式为均匀分布。
"""
# 实例化gru层
# return_sequences=True代表返回GRU序列模型的每个时间步的输出(每个输出做连接操作)
# return_state=True代表除了返回输出外,还需要返回最后一个隐层状态
# recurrent_initializer='glorot_uniform'即循环状态张量的初始化方式为均匀分布
self.gru = tf.keras.layers.GRU(self.enc_units,
return_sequences=True,
return_state=True,
recurrent_initializer='glorot_uniform')
def call(self, x, hidden):
# 对输入进行embedding操作
x = self.embedding(x)
"""initial_state:要传递给单元格的第一个调用的初始状态张量的列表(可选,默认为None,这将导致创建零填充的初始状态张量)。"""
# 通过gru层获得最后一个时间步的输出和隐含状态
output, state = self.gru(x, initial_state = hidden)
return output, state
def initialize_hidden_state(self):
""" (BATCH_SIZE, 隐藏层中的隐藏神经元数量) """
# gru层的隐含节点对应的参数张量以零张量初始化
return tf.zeros((self.batch_sz, self.enc_units))
#调用:
# 实例化encoder
encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)
# 样本输入
# sample_hidden = encoder.initialize_hidden_state()
# sample_output, sample_hidden = encoder(example_input_batch, sample_hidden)
# #(64, 16, 1024) 即 (BATCH_SIZE, 输入序列最大长度句子的长度, 隐藏层中的隐藏神经元数量)
# print ('Encoder output shape: (batch size, sequence length, units) {}'.format(sample_output.shape))
# #(64, 1024) 即 (BATCH_SIZE, 隐藏层中的隐藏神经元数量)
# print ('Encoder Hidden state shape: (batch size, units) {}'.format(sample_hidden.shape))