序列到序列模型与注意力机制 - Transformer基础 | 自在学序列到序列模型与注意力机制
2016年,Google翻译切换到神经机器翻译系统,翻译质量大幅提升。2017年,“Attention is All You Need”论文提出Transformer,彻底改变了NLP领域。这些突破的核心是注意力机制——让模型能够聚焦于输入的重要部分,而不是平等对待所有信息。
注意力机制是近年来深度学习最重要的创新之一。它不仅用于翻译,还是BERT、GPT等大语言模型的基础。理解注意力机制,是理解现代AI系统的关键。

Seq2Seq模型:从序列到序列
机器翻译是典型的序列到序列(Sequence-to-Sequence)任务:输入是源语言句子,输出是目标语言句子,两者长度通常不同。
编码器-解码器架构:
编码器处理输入序列,将信息压缩到一个固定维度的向量(上下文向量):
ht=LSTM(xt,ht−1)
c=hT(最后时刻的隐藏状态)
解码器基于上下文向量生成输出序列:
st=LSTM(yt−1,st−1,
yt=softmax(Wsst)
class Seq2SeqModel(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size, embedding_dim, hidden_dim):
super().__init__()
self.encoder_embedding = nn.Embedding(src_vocab_size, embedding_dim)
self.encoder_lstm = nn.LSTM(embedding_dim, hidden_dim, batch_first=True)
self.decoder_embedding = nn.Embedding(tgt_vocab_size, embedding_dim)
self.decoder_lstm = nn.LSTM(embedding_dim, hidden_dim, batch_first=
问题:将整个输入句子压缩到一个固定维度的向量 c,信息瓶颈。长句子的信息会丢失。
注意力机制:动态聚焦

标准Seq2Seq的问题是:解码器只能看到编码器最后的隐藏状态 hT。翻译“我爱深度学习”到英文时,生成“love”应该更多关注“爱”,生成“learning”应该更多关注“学习”。但标准Seq2Seq平等对待所有源词。
注意力机制让解码器能动态地关注不同的源词。解码器在时刻 t 不只看固定的 c,而是计算一个动态的上下文向量 ct,它是所有编码器隐藏状态的加权和:
ct=i=1∑Txα
权重 αt,i 表示在生成第 t 个输出词时,应该关注第 i 个输入词多少。这些权重通过一个注意力网络计算:
et,i=a(st−1,hi)
αt,i=∑j=1
这里 a 可以是一个小的神经网络(如单层全连接),或者简单的点积 st−1Thi。
class AttentionDecoder(nn.Module):
def __init__(self, embedding_dim, hidden_dim, output_dim):
super().__init__()
self.embedding = nn.Embedding(output_dim, embedding_dim)
self.lstm = nn.LSTMCell(embedding_dim + hidden_dim, hidden_dim)
self.attention = nn.Linear(hidden_dim * 2, 1)
self.output_layer = nn.Linear(hidden_dim, output_dim)
注意力的直观理解:
翻译“我爱深度学习”→“I love deep learning”时:
- 生成“I”:90%注意力在“我”,10%在其他词
- 生成“love”:85%注意力在“爱”
- 生成“deep”:80%注意力在“深度”
- 生成“learning”:90%注意力在“学习”
注意力权重可视化能清楚地展示模型在“看”哪里,这也提供了可解释性。
Beam Search:寻找最优序列
解码时,我们需要找到概率最大的输出序列。贪婪解码每步选择概率最大的词,但可能错过全局最优。集束搜索(Beam Search)是一个平衡的方案。
维护 B 个最可能的候选序列(B 是集束宽度,如3或10):
- 第一步:选概率最高的 B 个词作为候选开头
- 第二步:对每个候选,扩展所有可能的第二个词,得到 B×V 个序列,保留概率最高的 B 个
- 重复直到所有候选都生成结束标记
def beam_search_decode(model, src, beam_width=3, max_length=50):
"""
集束搜索解码
"""
# 编码
context = model.encode(src)
# 初始化:起始标记
sequences = [[BOS_token]]
scores = [0.0]
for _ in range(max_length):
all_candidates = []
B=1 就是贪婪搜索,B 越大越可能找到全局最优,但计算量是 O(B) 倍。实践中 B=3 到 B=10 通常够用。
注意力机制如此有效,引发了一个疑问:既然注意力能捕捉序列依赖,我们还需要RNN吗?
2017年,Google的“Attention is All You Need”论文给出答案:不需要。Transformer完全抛弃了RNN,只用注意力机制。
自注意力(Self-Attention)让序列中的每个词关注其他所有词,捕捉任意距离的依赖。更重要的是,自注意力可以并行计算——不像RNN必须顺序处理,训练速度快了几十倍。
Transformer的成功催生了BERT(2018)、GPT-2/3(2019/2020)、GPT-4(2023)等大语言模型。2026年的今天,几乎所有SOTA的NLP模型都基于Transformer,RNN已经很少用于新项目。
Flash Attention等优化:
标准自注意力的复杂度是 O(n2)(n 是序列长度),限制了能处理的最大长度。2022年提出的Flash Attention通过GPU内存层级优化,将速度提升3倍,内存占用降低10倍,让处理数万token的序列成为可能。
# Transformer的自注意力(简化版)
def self_attention(Q, K, V):
"""
Q, K, V: (batch, seq_len, d_model)
"""
d_k = Q.shape[-1]
# 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / np.sqrt(d_k)
# Softmax得到注意力权重
attention_weights = F.softmax(scores, dim=-1)
# 加权和
多模态应用:
注意力机制不限于文本。2020年后,视觉Transformer(ViT)将注意力应用到图像,CLIP将图像和文本通过注意力对齐,DALL-E用注意力生成图像。注意力成为深度学习的通用机制,跨越视觉、语言、音频等模态。
从基础到前沿
我们的深度学习之旅至此阶段性告一段落。我们系统学习了神经网络的基本单元、前向与反向传播、梯度下降等核心理论,掌握了正则化、优化算法、批归一化等实用训练技巧,了解了错误分析、迁移学习、端到端学习等工程策略,深入探索了卷积网络及其在计算机视觉中的成功应用,并理解了序列建模领域的关键技术——如RNN、词嵌入和注意力机制。
通过这些理论与方法的融合,我们已经全面构建起深度学习的知识体系。
但深度学习远未停止发展。2026年的今天:
- 大语言模型:ChatGPT、Claude、GPT-5等能力持续提升
- 多模态模型:CLIP、Flamingo等统一处理图像和文本
- 高效训练:Flash Attention、混合精度、模型并行让训练更快
- 边缘部署:模型压缩、量化让AI能在手机和IoT设备运行
技术在快速演进,但核心原理保持稳定。掌握了这门课程的内容,你已经具备了深度学习的坚实基础。无论未来出现什么新模型,理解它们的原理、调试训练过程、优化性能,都离不开这些基础知识。
接下来我们建议你:
- 实践项目:从Kaggle选一个竞赛题目,完整实现一遍——数据处理、模型训练、超参数调优、结果分析。
- 阅读论文:从经典论文开始(AlexNet、ResNet、Transformer),理解它们的创新点和局限。
- 参与社区:GitHub上的开源项目、论文复现、技术博客,都是学习的好途径。
- 持续学习:深度学习每年都有新突破。关注顶会(NeurIPS、ICML、CVPR),保持对前沿的了解。
深度学习的魅力在于,它仍在快速发展。你学到的不仅是现有的技术,更是一种思维方式——如何将问题形式化、如何设计模型、如何通过数据驱动的实验改进。这些能力在AI的下一个十年仍将重要。
祝你在深度学习的道路上不断探索、不断进步!
c
)
True
)
self.output_layer = nn.Linear(hidden_dim, tgt_vocab_size)
def forward(self, src, tgt):
# 编码
src_embedded = self.encoder_embedding(src)
_, (h_n, c_n) = self.encoder_lstm(src_embedded)
# 解码
tgt_embedded = self.decoder_embedding(tgt)
decoder_output, _ = self.decoder_lstm(tgt_embedded, (h_n, c_n))
# 输出层
output = self.output_layer(decoder_output)
return output
t,i
hi
(对齐分数)
Tx
exp
(
et,j
)
exp(et,i)
(softmax归一化)
def forward(self, prev_word, prev_hidden, encoder_outputs):
"""
prev_word: 前一个词,shape (batch,)
prev_hidden: 前一时刻隐藏状态,shape (batch, hidden_dim)
encoder_outputs: 所有编码器隐藏状态,shape (batch, src_len, hidden_dim)
"""
batch_size = encoder_outputs.shape[0]
src_len = encoder_outputs.shape[1]
# 计算注意力权重
# 将prev_hidden扩展为(batch, src_len, hidden_dim)
prev_hidden_expanded = prev_hidden.unsqueeze(1).expand(-1, src_len, -1)
# 拼接并通过注意力网络
combined = torch.cat([encoder_outputs, prev_hidden_expanded], dim=2)
attention_scores = self.attention(combined).squeeze(2) # (batch, src_len)
attention_weights = F.softmax(attention_scores, dim=1) # (batch, src_len)
# 计算上下文向量
context = torch.bmm(attention_weights.unsqueeze(1), encoder_outputs).squeeze(1) # (batch, hidden_dim)
# 嵌入当前输入词
embedded = self.embedding(prev_word) # (batch, emb_dim)
# LSTM输入是[embedded, context]拼接
lstm_input = torch.cat([embedded, context], dim=1)
hidden_new, cell_new = self.lstm(lstm_input, (prev_hidden, prev_cell))
# 输出
output = self.output_layer(hidden_new)
return output, hidden_new, cell_new, attention_weights
# 扩展每个候选序列
for i in range(len(sequences)):
seq = sequences[i]
score = scores[i]
if seq[-1] == EOS_token: # 已结束的序列
all_candidates.append((score, seq))
continue
# 预测下一个词的概率
probs = model.decode_step(seq, context)
# 取top-K个词
top_k_probs, top_k_words = torch.topk(probs, beam_width)
for j in range(beam_width):
candidate_seq = seq + [top_k_words[j].item()]
candidate_score = score + np.log(top_k_probs[j].item())
all_candidates.append((candidate_score, candidate_seq))
# 按分数排序,保留top-B
ordered = sorted(all_candidates, key=lambda x: x[0], reverse=True)
sequences = [seq for _, seq in ordered[:beam_width]]
scores = [score for score, _ in ordered[:beam_width]]
# 如果所有序列都结束了,提前停止
if all(seq[-1] == EOS_token for seq in sequences):
break
# 返回分数最高的序列
return sequences[0]
output = torch.matmul(attention_weights, V)
return output, attention_weights