第三章 线性代数--综合实例
第11节 聚焦注意力:解析GPT等大模型中的注意力机制
在人工智能的众多技术中,注意力机制(Attention Mechanism)无疑是推动大规模模型如GPT(Generative Pre-trained Transformer)取得突破性进展的关键因素之一。本节将通过五个实际应用案例,深入解析注意力机制在不同AI任务中的应用与实现,涵盖案例描述、分析、算法步骤以及配套的Python代码示例。
案例一:机器翻译中的自注意力机制
1. 案例描述
机器翻译是自然语言处理(NLP)中的核心任务之一,旨在将一种语言的文本自动翻译为另一种语言。传统的序列到序列(Seq2Seq)模型在翻译过程中容易遇到长距离依赖问题,难以有效捕捉源语言与目标语言之间复杂的对应关系。自注意力机制的引入显著提升了翻译质量,使得模型能够在翻译过程中更好地理解上下文。
2. 案例分析
自注意力机制允许模型在处理每个词时,关注输入序列中所有位置的词,从而捕捉全局依赖关系。以Transformer模型为代表的架构,通过多头自注意力(Multi-Head Self-Attention)实现了这一点,极大地提高了翻译的准确性和效率。
3. 案例算法步骤
- 输入编码:将源语言句子转换为词向量表示。
- 位置编码:加入位置信息以保留词序。
- 多头自注意力计算:
- 计算查询(Q)、键(K)、值(V)向量。
- 计算注意力权重:
Attention(Q, K, V) = softmax(QK^T / √d_k) V
- 多头并行计算并拼接结果。
- 前馈神经网络:对注意力输出进行非线性变换。
- 输出生成:通过解码器生成目标语言句子。
4. 案例对应Python代码及详解
以下示例展示了如何使用PyTorch实现一个简化的自注意力机制,用于机器翻译任务。
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert (
self.head_dim * heads == embed_size
), "Embedding size needs to be divisible by heads"
# 定义线性变换层
self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, queries, mask):
N, seq_length, embed_size = queries.shape
# 将嵌入分成多头
values = values.reshape(N, seq_length, self.heads, self.head_dim)
keys = keys.reshape(N, seq_length, self.heads, self.head_dim)
queries = queries.reshape(N, seq_length, self.heads, self.head_dim)
# 线性变换
values = self.values(values)
keys = self.keys(keys)
queries = self.queries(queries)
# 计算注意力分数
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
attention = torch.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, seq_length, self.heads * self.head_dim
)
# 最终线性变换
out = self.fc_out(out)
return out
# 示例用法
embed_size = 256
heads = 8
self_attention = SelfAttention(embed_size, heads)
# 假设输入序列长度为10,批大小为2
queries = torch.rand((2, 10, embed_size))
keys = torch.rand((2, 10, embed_size))
values = torch.rand((2, 10, embed_size))
mask = None # 这里不使用掩码
output = self_attention(values, keys, queries, mask)
print(output.shape) # 输出: torch.Size([2, 10, 256])
代码详解:
-
SelfAttention类:实现了多头自注意力机制。
__init__
方法中定义了线性变换层,用于生成查询、键、值向量。forward
方法中:- 将输入的嵌入向量分成多个头。
- 对每个头进行线性变换。
- 计算注意力权重,并应用于值向量。
- 将所有头的输出拼接,并通过最终的线性层生成输出。
-
示例用法:创建一个
SelfAttention
实例,并对随机生成的查询、键、值进行前向传播,输出结果的形状为[批大小, 序列长度, 嵌入维度]
。
案例二:文本摘要中的注意力机制
1. 案例描述
文本摘要任务旨在从长文本中提取关键信息,生成简明扼要的摘要。注意力机制在文本摘要中帮助模型聚焦于输入文本的关键部分,提高摘要的质量和相关性。
2. 案例分析
在文本摘要中,注意力机制使得解码器能够在生成每个摘要词时,动态地关注源文本的不同部分。这种动态聚焦能力有效地捕捉了源文本中的关键信息,从而生成连贯且信息丰富的摘要。
3. 案例算法步骤
- 输入编码:将源文本转换为词向量,并通过编码器生成上下文表示。
- 位置编码:为词向量加入位置信息。
- 注意力机制:
- 解码器在生成每个词时,计算对编码器输出的注意力权重。
- 根据注意力权重加权汇总编码器输出,作为当前生成词的上下文向量。
- 词生成:通过解码器的前馈网络和softmax层生成摘要词。
4. 案例对应Python代码及详解
以下示例展示了如何使用PyTorch实现一个简化的注意力机制,用于文本摘要任务。
import torch
import torch.nn as nn
import torch.nn.functional as F
class Attention(nn.Module):
def __init__(self, encoder_hidden_dim, decoder_hidden_dim):
super(Attention, self).__init__()
self.attn = nn.Linear((encoder_hidden_dim + decoder_hidden_dim), decoder_hidden_dim)
self.v = nn.Linear(decoder_hidden_dim, 1, bias=False)
def forward(self, hidden, encoder_outputs, mask):
# hidden: [batch_size, decoder_hidden_dim]
# encoder_outputs: [batch_size, src_len, encoder_hidden_dim]
batch_size = encoder_outputs.shape[0]
src_len = encoder_outputs.shape[1]
# 重复隐藏状态以匹配源序列长度
hidden = hidden.unsqueeze(1).repeat(1, src_len, 1) # [batch_size, src_len, decoder_hidden_dim]
# 连接隐藏状态与编码器输出
energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim=2))) # [batch_size, src_len, decoder_hidden_dim]
# 计算注意力得分
attention = self.v(energy).squeeze(2) # [batch_size, src_len]
# 应用掩码
if mask is not None:
attention = attention.masked_fill(mask == 0, -1e10)
return F.softmax(attention, dim=1) # [batch_size, src_len]
# 示例用法
batch_size = 2
src_len = 10
encoder_hidden_dim = 256
decoder_hidden_dim = 256
attention = Attention(encoder_hidden_dim, decoder_hidden_dim)
hidden = torch.rand((batch_size, decoder_hidden_dim))
encoder_outputs = torch.rand((batch_size, src_len, encoder_hidden_dim))
mask = torch.ones((batch_size, src_len)) # 假设没有填充
attn_weights = attention(hidden, encoder_outputs, mask)
print(attn_weights.shape) # 输出: torch.Size([2, 10])
代码详解:
-
Attention类:实现了一个基于加性注意力的机制。
__init__
方法中定义了用于计算注意力得分的线性层。forward
方法中:- 将解码器的隐藏状态与编码器的输出进行拼接。
- 通过非线性激活函数(tanh)生成能量分数。
- 计算注意力权重,并应用掩码防止关注填充部分。
-
示例用法:创建一个
Attention
实例,并对随机生成的隐藏状态和编码器输出进行前向传播,输出注意力权重的形状为[批大小, 源序列长度]
。
案例三:问答系统中的双向注意力
1. 案例描述
问答系统旨在根据用户提出的问题,从给定的文本中找到准确的答案。双向注意力机制(Bi-Attention)在此任务中起到了关键作用,通过同时关注问题和上下文,提高了系统的理解和回答能力。
2. 案例分析
双向注意力机制使得模型能够在问题和上下文之间建立相互关联,增强了对相关信息的捕捉能力。在问答系统中,模型需要理解问题的意图,并在上下文中找到对应的答案,双向注意力提供了有效的方式来实现这一目标。
3. 案例算法步骤
- 输入编码:将问题和上下文分别编码为词向量表示。
- 位置编码:为问题和上下文词向量加入位置信息。
- 双向注意力计算:
- 问题对上下文的注意力:计算上下文中每个词对问题的关注程度。
- 上下文对问题的注意力:计算问题中每个词对上下文的关注程度。
- 特征融合:结合双向注意力的输出,生成更丰富的特征表示。
- 答案预测:通过前馈网络和softmax层预测答案的位置或内容。
4. 案例对应Python代码及详解
以下示例展示了如何使用PyTorch实现一个简化的双向注意力机制,用于问答系统任务。
import torch
import torch.nn as nn
import torch.nn.functional as F
class BiAttention(nn.Module):
def __init__(self, hidden_dim):
super(BiAttention, self).__init__()
self.hidden_dim = hidden_dim
def forward(self, context, question, mask_context=None, mask_question=None):
# context: [batch_size, context_len, hidden_dim]
# question: [batch_size, question_len, hidden_dim]
# 计算相似度矩阵
similarity = torch.bmm(context, question.transpose(1, 2)) # [batch_size, context_len, question_len]
# 问题对上下文的注意力
if mask_question is not None:
similarity = similarity.masked_fill(mask_question.unsqueeze(1).expand(-1, context.size(1), -1) == 0, -1e10)
attention_context_to_question = F.softmax(similarity, dim=2) # [batch_size, context_len, question_len]
# 上下文对问题的注意力
similarity_transposed = similarity.transpose(1, 2) # [batch_size, question_len, context_len]
if mask_context is not None:
similarity_transposed = similarity_transposed.masked_fill(mask_context.unsqueeze(1).expand(-1, question.size(1), -1) == 0, -1e10)
attention_question_to_context = F.softmax(similarity_transposed, dim=2) # [batch_size, question_len, context_len]
# 加权求和
attended_question = torch.bmm(attention_context_to_question, question) # [batch_size, context_len, hidden_dim]
attended_context = torch.bmm(attention_question_to_context, context) # [batch_size, question_len, hidden_dim]
return attended_question, attended_context
# 示例用法
batch_size = 2
context_len = 15
question_len = 5
hidden_dim = 256
bi_attention = BiAttention(hidden_dim)
context = torch.rand((batch_size, context_len, hidden_dim))
question = torch.rand((batch_size, question_len, hidden_dim))
mask_context = torch.ones((batch_size, context_len))
mask_question = torch.ones((batch_size, question_len))
attended_question, attended_context = bi_attention(context, question, mask_context, mask_question)
print(attended_question.shape) # 输出: torch.Size([2, 15, 256])
print(attended_context.shape) # 输出: torch.Size([2, 5, 256])
代码详解:
-
BiAttention类:实现了双向注意力机制。
forward
方法中:- 通过批量矩阵乘法计算上下文与问题之间的相似度矩阵。
- 计算问题对上下文的注意力权重,并进行加权求和生成
attended_question
。 - 计算上下文对问题的注意力权重,并进行加权求和生成
attended_context
。
-
示例用法:创建一个
BiAttention
实例,并对随机生成的上下文和问题进行前向传播,输出attended_question
和attended_context
的形状分别为[批大小, 上下文长度, 隐藏维度]
和[批大小, 问题长度, 隐藏维度]
。
案例四:情感分析中的注意力机制
1. 案例描述
情感分析旨在识别文本中表达的情感倾向,如积极、消极或中立。引入注意力机制后,模型能够重点关注对情感分类最具影响力的词语,从而提升分类的准确性。
2. 案例分析
在情感分析任务中,并非所有词语对情感判断都有同等的重要性。注意力机制帮助模型自动识别并聚焦于关键情感词,如“优秀”、“糟糕”等,提高了模型对情感的敏感度和判别能力。
3. 案例算法步骤
- 输入编码:将输入文本转换为词向量表示。
- 位置编码:为词向量加入位置信息。
- 注意力机制:
- 计算每个词对整体情感的注意力权重。
- 根据注意力权重加权汇总词向量,生成全局情感表示。
- 分类层:通过全连接层和softmax进行情感分类。
4. 案例对应Python代码及详解
以下示例展示了如何使用PyTorch实现一个简化的注意力机制,用于情感分析任务。
import torch
import torch.nn as nn
import torch.nn.functional as F
class SentimentAttention(nn.Module):
def __init__(self, embed_size, hidden_dim, num_classes):
super(SentimentAttention, self).__init__()
self.embedding = nn.Embedding(num_embeddings=5000, embedding_dim=embed_size)
self.lstm = nn.LSTM(embed_size, hidden_dim, batch_first=True, bidirectional=True)
self.attention = nn.Linear(hidden_dim * 2, 1)
self.fc = nn.Linear(hidden_dim * 2, num_classes)
def forward(self, x, mask=None):
# x: [batch_size, seq_len]
embedded = self.embedding(x) # [batch_size, seq_len, embed_size]
lstm_out, _ = self.lstm(embedded) # [batch_size, seq_len, hidden_dim*2]
# 计算注意力权重
attn_weights = self.attention(lstm_out).squeeze(2) # [batch_size, seq_len]
if mask is not None:
attn_weights = attn_weights.masked_fill(mask == 0, -1e10)
attn_weights = F.softmax(attn_weights, dim=1) # [batch_size, seq_len]
# 加权求和
weighted = torch.bmm(attn_weights.unsqueeze(1), lstm_out).squeeze(1) # [batch_size, hidden_dim*2]
# 分类
out = self.fc(weighted) # [batch_size, num_classes]
return out
# 示例用法
batch_size = 2
seq_len = 10
embed_size = 128
hidden_dim = 64
num_classes = 3 # 积极、消极、中立
model = SentimentAttention(embed_size, hidden_dim, num_classes)
input_ids = torch.randint(0, 5000, (batch_size, seq_len)) # 随机生成输入
mask = torch.ones((batch_size, seq_len)) # 假设没有填充
logits = model(input_ids, mask)
print(logits.shape) # 输出: torch.Size([2, 3])
代码详解:
-
SentimentAttention类:实现了一个基于注意力机制的情感分析模型。
__init__
方法中定义了嵌入层、双向LSTM、注意力层和全连接分类层。forward
方法中:- 将输入序列转换为嵌入向量,并通过双向LSTM提取上下文特征。
- 计算每个时间步的注意力权重,并进行加权求和生成全局表示。
- 通过全连接层和softmax进行情感分类。
-
示例用法:创建一个
SentimentAttention
实例,并对随机生成的输入进行前向传播,输出情感分类的logits,其形状为[批大小, 类别数]
。
案例五:文本生成中的多头注意力机制
1. 案例描述
文本生成任务旨在根据给定的上下文生成连贯且符合语法的文本,如对话系统、自动写作等。多头注意力机制在文本生成中发挥了重要作用,使模型能够从多个子空间同时关注不同的信息,提高生成文本的多样性和质量。
2. 案例分析
多头注意力机制通过并行计算多个注意力头,每个头学习不同的表示,从而捕捉到不同层次和不同类型的依赖关系。在文本生成中,这种机制使得模型能够更全面地理解上下文,提高生成文本的相关性和流畅度。
3. 案例算法步骤
- 输入编码:将上下文文本转换为词向量表示。
- 位置编码:为词向量加入位置信息。
- 多头注意力机制:
- 生成多个注意力头,分别计算查询、键、值向量。
- 计算每个头的注意力输出,并将其拼接。
- 前馈神经网络:对多头注意力的输出进行非线性变换。
- 文本生成:通过解码器逐词生成文本。
4. 案例对应Python代码及详解
以下示例展示了如何使用PyTorch实现一个简化的多头注意力机制,用于文本生成任务。
import torch
import torch.nn as nn
import torch.nn.functional as F
class MultiHeadAttention(nn.Module):
def __init__(self, embed_size, heads):
super(MultiHeadAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert (
self.head_dim * heads == embed_size
), "Embedding size must be divisible by heads"
# 定义线性变换层
self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, queries, mask=None):
N, seq_length, embed_size = queries.shape
# 分头
values = values.reshape(N, seq_length, self.heads, self.head_dim)
keys = keys.reshape(N, seq_length, self.heads, self.head_dim)
queries = queries.reshape(N, seq_length, self.heads, self.head_dim)
# 线性变换
values = self.values(values)
keys = self.keys(keys)
queries = self.queries(queries)
# 计算注意力分数
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
attention = torch.softmax(energy / (self.head_dim ** 0.5), dim=3)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, seq_length, self.heads * self.head_dim
)
# 最终线性变换
out = self.fc_out(out)
return out
# 示例用法
embed_size = 256
heads = 8
multi_head_attention = MultiHeadAttention(embed_size, heads)
# 假设输入序列长度为10,批大小为2
queries = torch.rand((2, 10, embed_size))
keys = torch.rand((2, 10, embed_size))
values = torch.rand((2, 10, embed_size))
mask = None # 这里不使用掩码
output = multi_head_attention(values, keys, queries, mask)
print(output.shape) # 输出: torch.Size([2, 10, 256])
代码详解:
-
MultiHeadAttention类:实现了多头注意力机制。
__init__
方法中定义了用于生成查询、键、值向量的线性层,以及最终的线性输出层。forward
方法中:- 将输入的嵌入向量分成多个头。
- 对每个头进行线性变换。
- 计算注意力权重,并应用于值向量。
- 将所有头的输出拼接,并通过最终的线性层生成输出。
-
示例用法:创建一个
MultiHeadAttention
实例,并对随机生成的查询、键、值进行前向传播,输出结果的形状为[批大小, 序列长度, 嵌入维度]
。
总结
通过以上五个案例,我们深入探讨了注意力机制在不同AI应用中的具体实现和作用。无论是机器翻译、文本摘要、问答系统、情感分析还是文本生成,注意力机制都展示了其强大的能力,帮助模型更好地理解和处理复杂的语言信息。理解和掌握这些注意力机制,不仅有助于提升模型的性能,也为进一步的创新提供了坚实的基础。
标签:dim,AI,self,torch,python,hidden,注意力,size From: https://blog.csdn.net/l35633/article/details/145087832