【开源项目】Excel手撕AI算法深入理解(二):Transformer
本文图片出自https://github.com/ImagineAILab/ai-by-hand-excel.git
一、Transformer
Transformer 是近年来深度学习领域最重要的突破之一,彻底改变了自然语言处理(NLP)和其他序列建模任务的范式。
FFN由两层全连接层 + ReLU激活
1. Transformer 的核心思想
核心目标:解决传统RNN/LSTM的长程依赖和并行化训练问题。
关键创新:
-
自注意力机制(Self-Attention):直接建模序列中任意两个元素的关系,无论距离多远。
-
全并行化架构:摒弃递归,完全基于矩阵运算,极大加速训练。
-
位置编码(Positional Encoding):注入序列位置信息,弥补无递归的缺陷。
2. Transformer 的架构分解
Transformer 由 编码器(Encoder) 和 解码器(Decoder) 堆叠而成,以下是其核心组件:
2.1 输入表示
-
词嵌入(Word Embedding):将词映射为稠密向量(如
d_model=512
)。 -
位置编码(Positional Encoding):
-
pos
:词的位置,i
:维度索引。 -
通过正弦/余弦函数编码位置,使模型能学习相对位置关系。
-
2.2 编码器层(Encoder Layer)
每层包含两个子模块:
-
多头自注意力(Multi-Head Self-Attention)
-
将输入拆分为
h
个头(如h=8
),分别计算注意力后拼接。 -
公式(单头):
-
Q
(Query),K
(Key),V
(Value) 由输入线性变换得到。 -
√d_k
缩放防止点积过大导致梯度消失。
-
-
-
前馈神经网络(Feed-Forward Network, FFN)
(1)FFN 的结构与尺寸
Transformer中的FFN通常由两个全连接层(Linear Layer)组成,结构如下:
关键点:
第一层将维度从 d_model
扩展到更大的 d_ff
(如512→2048)。
第二层将维度从 d_ff
压缩回 d_model
(2048→512)。
ReLU激活函数仅作用于第一层的输出。
为什么这样设计?
(1)扩展维度(512 → 2048)
-
增强表达能力:在高维空间(如2048)进行非线性变换(ReLU),可以学习更复杂的特征组合。
-
类比“宽网络”:类似于计算机视觉中先用
1x1
卷积扩展通道数,再压缩回去(如ResNet的Bottleneck)。
(2)压缩回原始维度(2048 → 512)
-
保持一致性:确保FFN的输出与输入维度相同,便于残差连接(
x + FFN(x)
)。 -
控制参数量:若直接保持高维(如2048),后续层的计算量会爆炸式增长。
(3)为什么是4倍扩展?
原始论文(《Attention Is All You Need》)中采用modeldff=4×dmodel(如512→2048),这是经验性选择:
-
平衡模型容量和计算效率。
-
实验表明,扩展倍数小于4可能导致性能下降,大于4则收益递减。
2.3 解码器层(Decoder Layer)
比编码器多一个 掩码多头注意力(Masked Multi-Head Attention):
-
掩码机制:防止解码时看到未来信息(训练时用三角矩阵掩码)。
-
编码器-解码器注意力:解码器的
Q
来自上一输出,K/V
来自编码器输出。
3. 关键数学细节
3.1 自注意力的计算步骤
-
线性变换生成
Q
,K
,V
: -
计算注意力分数并缩放:
-
加权求和:
3.2 多头注意力的实现
-
将
Q/K/V
拆分为h
个头(维度d_k = d_model / h
),分别计算后拼接:其中,
head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)
。
4. 代码实现(PyTorch 简化版)
import torch
import torch.nn as nn
import math
class MultiHeadAttention(nn.Module):
def __init__(self, d_model=512, h=8):
super().__init__()
self.d_k = d_model // h
self.h = h
self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)
self.W_O = nn.Linear(d_model, d_model)
def forward(self, x, mask=None):
# x: [batch_size, seq_len, d_model]
Q = self.W_Q(x) # [batch_size, seq_len, d_model]
K = self.W_K(x)
V = self.W_V(x)
# Split into heads
Q = Q.view(-1, Q.size(1), self.h, self.d_k) # [batch_size, seq_len, h, d_k]
K = K.view(-1, K.size(1), self.h, self.d_k)
V = V.view(-1, V.size(1), self.h, self.d_k)
# Scaled dot-product attention
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn = torch.softmax(scores, dim=-1)
output = torch.matmul(attn, V) # [batch_size, seq_len, h, d_k]
# Concatenate and project
output = output.transpose(1, 2).contiguous().view(-1, output.size(1), self.h * self.d_k)
return self.W_O(output)
class TransformerLayer(nn.Module):
def __init__(self, d_model=512, h=8, d_ff=2048):
super().__init__()
self.self_attn = MultiHeadAttention(d_model, h)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.ReLU(),
nn.Linear(d_ff, d_model)
)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
def forward(self, x, mask=None):
# Self-attention + residual
attn_output = self.self_attn(x, mask)
x = self.norm1(x + attn_output)
# FFN + residual
ffn_output = self.ffn(x)
return self.norm2(x + ffn_output)
5. Transformer 的变体与改进
-
BERT:仅用编码器,通过掩码语言模型预训练。
-
GPT:仅用解码器,自回归生成文本。
-
Efficient Transformers:
-
Sparse Attention(如 Longformer)降低计算复杂度。
-
Performer 用线性近似替代 Softmax。
-
6. 为什么 Transformer 如此强大?
-
全局依赖性建模:自注意力直接捕获任意距离的关系。
-
并行化优势:适合 GPU 加速,训练速度远超 RNN。
-
可扩展性:通过堆叠层数(如 GPT-3 有 96 层)处理复杂任务。
二、Transformer-Full-Stack全栈架构
1. 输入处理
-
Token Embedding
-
输入:
[batch_size, seq_len]
(词ID序列) -
输出:
[batch_size, seq_len, d_model]
(如512维) -
作用:将离散词ID映射为连续向量。
-
-
Positional Encoding
-
公式:
PE(pos,2i)=sin(pos/10000^(2i/d_model))
-
输出:与词嵌入相加,维度不变。
-
作用:为模型提供序列位置信息。
-
2. 编码器(Encoder)
-
Multi-Head Attention
-
输入:
Q, K, V
(均来自上一层输出) -
过程:
-
拆分为
h
个头(如8头 → 每个头维度d_k = d_model/h = 64
)。 -
计算缩放点积注意力:
softmax(QK^T/√d_k)V
。 -
拼接多头输出并通过
W_O
投影。
-
-
输出维度:与输入一致
[batch_size, seq_len, d_model]
。
-
-
Feed-Forward Network (FFN)
-
结构:
Linear(d_model→d_ff) → ReLU → Linear(d_ff→d_model)
-
典型尺寸:
d_ff = 4*d_model
(如2048)。
-
在Transformer中,Add & Norm(残差连接与层归一化)是每个子层(如自注意力、前馈神经网络)后的标准操作,其核心目的是稳定深层网络的训练并加速收敛。
1. Add & Norm 的组成
-
Add(残差连接):
将子层(如Self-Attention或FFN)的输入x 和输出 Sublayer(x) 直接相加: -
Norm(层归一化):
对相加后的结果进行层归一化(Layer Normalization):
3. 解码器(Decoder)
-
Masked Multi-Head Attention
-
区别:在Softmax前添加下三角掩码(
mask=-inf
),防止看到未来信息。
-
-
Encoder-Decoder Attention
-
Q
:来自解码器上一层的输出。 -
K, V
:来自编码器的最终输出。
-
解码器使用了中文输入的英文表示,再走一遍刚刚编码器的流程
编码器的最终输出会作为Key(K)和Value(V)传递给解码器的“编码器-解码器注意力”层
在Transformer的解码器中,编码器的最终输出会作为Key(K)和Value(V)传递给解码器的“编码器-解码器注意力”层(即第二个多头注意力子层),而Query(Q)来自解码器自身的当前状态。这种设计是为了实现跨序列的信息融合,让解码器能够动态关注编码器输出的相关部分。以下是详细解释:
1. 核心目的:信息桥接
编码器的输出(通常称为“记忆”)包含了输入序列的全局编码信息(如源语言的语义特征)。解码器需要根据这些信息生成目标序列(如翻译结果)。通过将编码器输出作为K和V,解码器可以:
-
动态检索输入序列的关键内容(通过Q与K的匹配)。
-
避免重复编码(解码器无需重新学习输入序列的表示)。
2. 为什么这样设计?
(1)解耦生成与检索
-
解码器自身(通过Q)专注于已生成序列的状态管理。
-
编码器输出(K/V)专注于提供输入序列的全局信息库。
-
类比:就像人类翻译时,先理解原文(编码器),再根据已翻译的部分(Q)查找原文中需要的信息(K/V)。
(2)处理不等长序列
-
输入(源序列)和输出(目标序列)长度通常不同(如翻译任务)。通过分离Q和K/V,模型可以灵活处理这种差异。
(3)避免信息冗余
-
如果解码器自行重新编码输入序列,会导致参数浪费和训练低效。直接复用编码器输出更高效。
这种设计是Transformer能够高效处理序列到序列任务的核心机制之一!
3. 典型应用场景
-
机器翻译:解码器生成目标语言时,动态参考编码器对源语言的编码。
-
文本摘要:解码器生成摘要时,关注输入文章的关键部分。
-
语音识别:解码器输出文本时,对齐音频编码特征。
4. 输出生成
-
Linear + Softmax
-
线性层:将
d_model
维映射到词表大小vocab_size
。 -
Softmax:生成每个位置的词概率分布。
-