当前位置: 首页 > web >正文

使用注意力机制的seq2seq

使用注意力机制的seq2seq

Motivation

机器翻译中,每个生成的词可能相关于源句子中不同的词
在这里插入图片描述
但是,seq2seq 模型中不能对此直接建模

加入注意力

在这里插入图片描述

编码器对每次词的输出作为key和value(它们是同样的)
解码器RNN对上一个词的输出是query
注意力的输出和下一个词的词嵌入合并进入RNN

总结

Seq2seq中通过隐状态在编码器和解码器中传递信息
注意力机制可以根据解码器RNN的输出来匹配到合适的编码器RNN的输出来更有效的传递信息

代码实现

Bahdanau 注意力

首先导入必要的环境

import torch
from torch import nn
from d2l import torch as d2l

带有注意力机制的解码器基本接口

# @save
class AttentionDecoder(d2l.Decoder):"""带有注意力机制解码器的基本接口"""def __init__(self, **kwargs):# 调用父类 d2l.Decoder 的初始化方法# **kwargs 允许传递任意数量的关键字参数super(AttentionDecoder, self).__init__(**kwargs)@propertydef attention_weights(self):  # 有没有都不重要,主要是用来画图用的# 定义一个抽象属性,用于获取注意力权重# 子类需要实现此方法以返回注意力权重raise NotImplementedError

在接下来的Seq2SeqAttentionDecoder类中实现带有Bahdanau注意力的循环神经网络解码器。首先,初始化解码器的状态,需要下面的输入:

  1. 编码器在所有时间步的最终层隐状态,将作为注意力的键和值;
  2. 上一时间步的编码器全层隐状态,将作为初始化解码器的隐状态;
  3. 编码器有效长度(排除在注意力池中填充词元)。
    在每个解码时间步骤中,解码器上一个时间步的最终层隐状态将用作查询。因此,注意力输出和输入嵌入都连结为循环神经网络解码器的输入。
class Seq2SeqAttentionDecoder(AttentionDecoder):def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,dropout=0, **kwargs):# 调用父类 AttentionDecoder 的初始化方法super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)# 定义加性注意力机制self.attention = d2l.AdditiveAttention(num_hiddens, num_hiddens,num_hiddens, dropout)# 定义嵌入层,将词元索引映射到指定维度的嵌入向量self.embedding = nn.Embedding(vocab_size, embed_size)# 定义循环神经网络(GRU),输入维度为嵌入维度+注意力上下文向量维度self.rnn = nn.GRU(embed_size + num_hiddens,num_hiddens, num_layers,dropout=dropout)# 定义全连接层,用于将GRU的输出映射到词汇表大小的向量self.dense = nn.Linear(num_hiddens, vocab_size)def init_state(self, enc_outputs, enc_valid_lens, *args):# 初始化解码器的状态# enc_outputs 是编码器的输出,包括所有时间步的隐状态和最终层隐状态# enc_valid_lens 是编码器的有效长度,用于掩码填充词元# outputs 的形状为 (batch_size, num_steps, num_hiddens)# hidden_state 的形状为 (num_layers, batch_size, num_hiddens)outputs, hidden_state = enc_outputs# 将 outputs 的时间步维度和批量维度交换,方便后续操作return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens)def forward(self, X, state):# 前向传播# X 是输入序列,形状为 (batch_size, num_steps)# state 是解码器的状态,包括编码器输出、隐状态和有效长度enc_outputs, hidden_state, enc_valid_lens = state# 将输入序列通过嵌入层,输出形状为 (num_steps, batch_size, embed_size)X = self.embedding(X).permute(1, 0, 2)outputs, self._attention_weights = [], []  # 存储输出和注意力权重for x in X:  # 遍历每个时间步的输入# query 是解码器当前时间步的隐状态,形状为 (batch_size, 1, num_hiddens)query = torch.unsqueeze(hidden_state[-1], dim=1)# 计算注意力上下文向量,形状为 (batch_size, 1, num_hiddens)context = self.attention(query, enc_outputs, enc_outputs,enc_valid_lens)# 将当前时间步的输入和上下文向量在特征维度上拼接# 拼接后的形状为 (batch_size, 1, embed_size + num_hiddens)x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1)# 将拼接后的输入传入GRU,输出形状为 (1, batch_size, num_hiddens)out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state)outputs.append(out)  # 将当前时间步的输出添加到列表中# 保存当前时间步的注意力权重self._attention_weights.append(self.attention.attention_weights)# 将所有时间步的输出拼接,并通过全连接层映射到词汇表大小# 输出形状为 (num_steps, batch_size, vocab_size)outputs = self.dense(torch.cat(outputs, dim=0))# 调整输出形状为 (batch_size, num_steps, vocab_size)return outputs.permute(1, 0, 2), [enc_outputs, hidden_state,enc_valid_lens]@propertydef attention_weights(self):# 返回注意力权重,用于可视化或其他分析return self._attention_weights

测试 Bahdanau 注意力解码器

encoder = d2l.Seq2SeqEncoder(vocab_size=10, embed_size=8, num_hiddens=16,num_layers=2)
encoder.eval()
decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16,num_layers=2)
decoder.eval()
X = torch.zeros((4, 7), dtype=torch.long)  # (batch_size,num_steps)
state = decoder.init_state(encoder(X), None)
output, state = decoder(X, state)
output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape# (torch.Size([4, 7, 10]), 3, torch.Size([4, 7, 16]), 2, torch.Size([4, 16]))

训练

embed_size, num_hiddens, num_layers, dropout = 32, 32, 2, 0.1
batch_size, num_steps = 64, 10
lr, num_epochs, device = 0.005, 250, d2l.try_gpu()train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)
encoder = d2l.Seq2SeqEncoder(len(src_vocab), embed_size, num_hiddens, num_layers, dropout)
decoder = Seq2SeqAttentionDecoder(len(tgt_vocab), embed_size, num_hiddens, num_layers, dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)

在这里插入图片描述
将几个英语句子翻译成法语并计算其 BLEU 分数

engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):translation, dec_attention_weight_seq = d2l.predict_seq2seq(net, eng, src_vocab, tgt_vocab, num_steps, device, True)print(f'{eng} => {translation}, ',f'bleu {d2l.bleu(translation, fra, k=2):.3f}')

在这里插入图片描述

attention_weights = torch.cat(
[step[0][0][0] for step in dec_attention_weight_seq], 0).reshape((1, 1, -1, num_steps))

训练结束后,下面通过可视化注意力权重。发现,每个查询都会在键值对上分配不同的权重,这说明在每个解码步中,输入序列的不同部分被选择性地聚集在注意力池中。

# 加上一个包含序列结束词元
d2l.show_heatmaps(attention_weights[:, :, :, :len(engs[-1].split()) + 1].cpu(),xlabel='Key positions', ylabel='Query positions')

在这里插入图片描述

小结

  • 在预测词元时,如果不是所有输入词元都是相关的,那么具有Bahdanau注意力的循环神经网络编码器-解码器会有选择地统计输入序列的不同部分。这是通过将上下文变量视为加性注意力池化的输出来实现的。
  • 在循环神经网络编码器-解码器中,Bahdanau注意力将上一时间步的解码器隐状态视为查询,在所有时间步的编码器隐状态同时视为键和值。

QA 思考

Q1:attention在搜索的时候是在当前句子搜索,还是所有的文本搜索?
A1:在当前的句子进行搜索。

Q2:─般都是在decoder加入注意力吗,不可以在encoder加入吗?
A2:可以在 Encoder中加入注意力,bert就是只有Encoder,这样就只在Encoder中加入注意力了。

http://www.xdnf.cn/news/4029.html

相关文章:

  • Docker —— 隔离的基本操作(2)
  • ABAP 导入Excel形成内表
  • spring中的@ConfigurationProperties注解详解
  • 网星安全AWS攻防方案,重磅发布!
  • 机器学习模型训练模块技术文档
  • AVHRR中国积雪物候数据集(1980-2020年)
  • yolo 用roboflow标注的数据集本地训练 kaggle训练 comet使用 训练笔记5
  • FISCO BCOS【初体验笔记】
  • Python 闭包:函数式编程中的魔法变量容器
  • ciscn_2019_c_1
  • 普联的AC100+AP+易展路由组网方案的一些问题
  • docker介绍以及安装
  • sherpa-ncnn:Linux_x86交叉编译Linux_arm32上的sherpa-ncnn -- 语音转文本大模型
  • 蓝桥杯单片机备战笔记
  • 【中间件】brpc_基础_TimerThread
  • 五一假期作业
  • springboot单体项目的执行流程
  • LFU算法解析
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】4.5 清洗流程自动化(存储过程/定时任务)
  • 【中间件】brpc_基础_单例
  • FreeRTOS学习系列·二值信号量
  • Linux查询日志常用命令
  • 解锁现代健康密码:科学养生新主张
  • 基于PLC的换热器温度控制系统设计
  • 状态模式(State Pattern)
  • 电子商务商家后台运营专员模板
  • C++ 中二级指针的正确释放方法
  • 【KWDB 创作者计划】_Ubuntu 22.04系统KWDB数据库安装部署使用教程
  • Qt中的UIC
  • Amazon Bedrock Converse API:开启对话式AI新体验