Transformer 全套逻辑:公式推导 + 原理解剖 + 逐行精读 - 划时代封神之作!

标签:人工智能 深度学习 Transformer 注意力机制 NLP 大模型基础


前言

在整个人类深度学习与序列建模的进化史上,Transformer 的诞生,无异于一场颠覆行业格局的技术革命 ,是真正意义上划时代的里程碑式创举

2017 年,Google Brain 团队在神作《Attention Is All You Need》中,以石破天惊之势抛出这一全新架构------直接彻底摒弃统治时代的循环神经网络 RNN 与卷积神经网络 CNN,开创性地仅依靠自注意力机制,便实现了端到端的高性能序列建模。它一剑封喉,精准击碎了传统架构两大致命顽疾:

  1. RNN 天然串行桎梏,无法并行计算,长序列处理效率低下、速度缓慢,彻底成为性能瓶颈;
  2. CNN 局部感受野局限,建模视野狭窄,长距离依赖关系捕捉乏力,难以支撑复杂场景建模。

凭借碾压前代的并行计算能力、登峰造极的长程依赖建模实力,以及简洁到极致却威力无穷的架构美学,Transformer 横空出世、一统江湖,迅速登顶成为 NLP、CV、语音、多模态等全领域的底层基石架构。如今横扫世界的 BERT、GPT、ViT、Stable Diffusion 等里程碑模型,无一不是站在它的肩膀之上。

本文将死磕原论文、逐行拆解、逐公式推导、逐模块精讲 ,从数学本质到工程实现全覆盖,硬核公式严谨推导 + 通俗白话同步拆解,理论闭环、细节拉满、逻辑通透,让你真正做到一篇吃透、终身掌握。

论文地址https://arxiv.org/abs/1706.03762


一、模型总览:Encoder-Decoder 架构

Transformer 是标准的 Seq2Seq 架构 ,由 编码器(Encoder)解码器(Decoder) 堆叠而成。


图1:Transformer模型架构

  • Encoder :将输入序列映射为上下文特征表示
    通俗理解:相当于"阅读理解",把输入句子读懂、抓住重点。
  • Decoder :基于编码器输出,自回归生成目标序列
    通俗理解:相当于"写作生成",根据理解的内容逐词写出答案。

整体结构遵循:

plaintext 复制代码
输入嵌入 + 位置编码 → N层Encoder → N层Decoder → 线性层 + Softmax → 输出序列

原论文设置:

  • 堆叠层数 N = 6 N=6 N=6
  • 模型维度 d m o d e l = 512 d_{model}=512 dmodel=512
  • 前馈网络隐层维度 d f f = 2048 d_{ff}=2048 dff=2048
  • 多头注意力头数 h = 8 h=8 h=8

二、编码器(Encoder)详细解析

编码器由 6 层完全相同的层 堆叠而成,每层包含两个子层:

  1. 多头自注意力(Multi-Head Self-Attention)
  2. 逐位置前馈网络(Position-Wise Feed-Forward Network)

每个子层都采用 残差连接 + 层归一化
LayerNorm ( x + Sublayer ( x ) ) \text{LayerNorm}(x + \text{Sublayer}(x)) LayerNorm(x+Sublayer(x))

通俗理解

残差连接防止网络太深"学不动",层归一化让训练更稳定、不震荡。


2.1 多头自注意力(Multi-Head Attention)

注意力本质是:根据 Query 与 Key 的相似度,对 Value 加权求和

通俗理解:像人看书一样,自动给重要的词分配更高权重。

2.1.1 缩放点积注意力(Scaled Dot-Product Attention)


图 2:(左)缩放点积注意力机制。(右)多头注意力机制由多个并行运行的注意力层组成。

公式(论文原式):
Attention ( Q , K , V ) = softmax ( Q K ⊤ d k ) V \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk QK⊤)V

  • Q(Query) :查询向量,表征"当前要找什么信息"
    → 通俗:我想查什么?
  • K(Key) :键向量,表征"我有什么信息"
    → 通俗:句子里有哪些内容?
  • V(Value) :值向量,表征"我实际的信息内容"
    → 通俗:真正要提取的内容。
  • √d_k :缩放因子,防止点积过大导致 Softmax 饱和梯度消失,可以看这篇文章:Softmax、Sigmoid、CrossEntropy Loss介绍
    → 通俗:避免数值太大,把 softmax"撑到没梯度"。

2.1.2 多头机制(Multi-Head)

将 Q、K、V 通过线性投影分为 h 组,并行计算注意力,再拼接输出:
MultiHead ( Q , K , V ) = Concat ( head 1 , . . . , head h ) W O \text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1,...,\text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO

其中每组注意力:
head i = Attention ( Q W i Q , K W i K , V W i V ) \text{head}_i = \text{Attention}(QW_i^Q,KW_i^K,VW_i^V) headi=Attention(QWiQ,KWiK,VWiV)

论文参数:

  • h = 8 h=8 h=8
  • d k = d v = d m o d e l / h = 64 d_k=d_v=d_{model}/h=64 dk=dv=dmodel/h=64

通俗理解

多头 = 开多个"观察视角" ,有的看语法、有的看指代、有的看长距离关系,

相当于 8 个专家同时理解一句话,比单视角更全面。


2.2 逐位置前馈网络(FFN)

注意力层后,对每个位置独立使用两层线性变换 + 激活函数:
FFN ( x ) = max ⁡ ( 0 , x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x) = \max(0, xW_1+b_1)W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2

即:
Linear → ReLU → Linear \text{Linear} \rightarrow \text{ReLU} \rightarrow \text{Linear} Linear→ReLU→Linear

通俗理解

注意力看完全局后,前馈网络对每个词单独做一次"特征升级"

不依赖其他词,独立提炼信息。


2.3 残差连接与层归一化

每个子层都遵循:
Output = LayerNorm ( x + SubLayer ( x ) ) \text{Output} = \text{LayerNorm}(x + \text{SubLayer}(x)) Output=LayerNorm(x+SubLayer(x))

作用:

  • 残差连接解决深度网络梯度消失;
    → 通俗:防止网络太深"学废了"。
  • 层归一化稳定训练。
    → 通俗:让数据分布更规矩,训练更快更稳。


图 3:在第 6 层第 5 层的编码器自注意力机制中,遵循长距离依赖关系的一个示例。许多注意力头关注动词"制作"的远距离依赖关系,从而完成短语"制作......更困难"。此处展示的注意力仅针对"制作"这个词。不同的颜色代表不同的注意力头。以彩色方式查看效果最佳。


三、解码器(Decoder)详细解析

解码器同样堆叠 6 层 ,每层包含三个子层

  1. 掩码多头自注意力(Masked Multi-Head Attention)
  2. 编码器-解码器注意力(Encoder-Decoder Attention)
  3. 逐位置前馈网络(FFN)

3.1 掩码自注意力(Masked Self-Attention)

为保证自回归生成 ,模型不能看到未来位置信息,因此在 Softmax 前将非法位置设为负无穷:
Attention ( Q , K , V ) = softmax ( Q K ⊤ d k + M ) V \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}} + M\right)V Attention(Q,K,V)=softmax(dk QK⊤+M)V

其中 M 为掩码矩阵 ,上三角为 − ∞ -∞ −∞,下三角为 0。

通俗理解

写作文时不能偷看还没写的句子,mask 就是把未来信息全部挡住,防止作弊。


3.2 编码器-解码器注意力

Q 来自解码器上一层输出,K、V 来自编码器最终输出。

作用:让生成位置对齐输入序列的相关部分(如机器翻译的词对齐)。

通俗理解

写作时随时回头看原题,保证输出和输入对应上,不跑偏。


四、位置编码(Positional Encoding)

Transformer 无循环与卷积,无法感知序列顺序,因此必须显式注入位置信息。

原论文使用正弦余弦位置编码
P E ( p o s , 2 i ) = sin ⁡ ( p o s / 10000 2 i / d m o d e l ) PE_{(pos,2i)} = \sin\left(pos \big/ 10000^{2i/d_{model}}\right) PE(pos,2i)=sin(pos/100002i/dmodel)
P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s / 10000 2 i / d m o d e l ) PE_{(pos,2i+1)} = \cos\left(pos \big/ 10000^{2i/d_{model}}\right) PE(pos,2i+1)=cos(pos/100002i/dmodel)

将位置编码直接加到词嵌入 上:
Input = Embedding + PositionalEncoding \text{Input} = \text{Embedding} + \text{PositionalEncoding} Input=Embedding+PositionalEncoding

通俗理解

Transformer 是"瞎子",看不见词的先后顺序。

位置编码给每个位置一个唯一坐标,告诉模型谁在前、谁在后。


五、嵌入与输出层

5.1 词嵌入

将词映射为 d_model 维向量,并乘以 √d_model 缩放:
Embedding × d m o d e l \text{Embedding} \times \sqrt{d_{model}} Embedding×dmodel

→ 通俗:让嵌入数值大小合适,不太小不爆炸。

5.2 输出层

解码器最终输出经过线性变换+Softmax,得到词表概率:
P ( y ) = softmax ( Linear ( DecoderOutput ) ) P(y) = \text{softmax}(\text{Linear}(\text{DecoderOutput})) P(y)=softmax(Linear(DecoderOutput))

→ 通俗:把模型输出转成"每个词概率",选概率最大的输出。


六、损失函数

使用标签平滑(Label Smoothing) 交叉熵损失:
L = − ∑ y i log ⁡ p i \mathcal{L} = -\sum y_i \log p_i L=−∑yilogpi

标签平滑值 ε=0.1,提升泛化性。

→ 通俗:不让模型"死记硬背",适当模糊标签,更不容易过拟合。


七、优化器与学习率策略

采用Adam 优化器 ,并使用论文定制学习率:
lrate = d m o d e l − 0.5 ⋅ min ⁡ ( step − 0.5 , step ⋅ warmup − 1.5 ) \text{lrate} = d_{model}^{-0.5} \cdot \min(\text{step}^{-0.5},\ \text{step} \cdot \text{warmup}^{-1.5}) lrate=dmodel−0.5⋅min(step−0.5, step⋅warmup−1.5)

  • warmup_steps=4000
  • 先线性增,后反比例平方根衰减

→ 通俗:

刚开始学习率慢慢升高,避免一开始训崩;

后期慢慢降低,精细调整。


八、核心优势:为什么 Self-Attention 更强?

论文从三个维度对比:

结构 序列操作数 最大路径长度 复杂度
Self-Attention O(1) O(1) O(n²·d)
RNN O(n) O(n) O(n·d²)
CNN O(1) O(log n) O(k·n·d²)

结论:

  1. 自注意力并行度最高 ,完全可并行;
    → 通俗:RNN 排队干活,Transformer 全员一起上。
  2. 长距离依赖路径最短 ,任意位置直接相连;
    → 通俗:句子再长,任意两个词都能"直接对话"。
  3. 长序列下可通过局部注意力进一步优化。

九、实验结果(论文原版)

机器翻译任务

  • WMT 2014 英→德:BLEU 28.4(超越所有模型)
  • WMT 2014 英→法:BLEU 41.8(单模型 SOTA)

英语句法分析

在 WSJ 数据集上取得 SOTA 级别表现,证明泛化能力极强


十、核心代码(PyTorch 精简可运行版)

python 复制代码
import torch
import torch.nn as nn
import math

# ================================
# 0. 位置编码(论文标准版)
# ================================
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

    def forward(self, x):
        seq_len = x.size(1)
        return x + self.pe[:, :seq_len]  # 跟公式的「词嵌入 + 位置编码」对应

# ================================
# 1. 缩放点积注意力
# ================================
class ScaledDotProductAttention(nn.Module):
    def forward(self, q, k, v, mask=None):
        d_k = q.size(-1)
        scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)
        attn = scores.softmax(dim=-1)
        return torch.matmul(attn, v)

# ================================
# 2. 多头注意力
# ================================
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        self.d_k = d_model // n_heads
        self.n_heads = n_heads
        self.wq = nn.Linear(d_model, d_model)
        self.wk = nn.Linear(d_model, d_model)
        self.wv = nn.Linear(d_model, d_model)
        self.out = nn.Linear(d_model, d_model)
        self.attn = ScaledDotProductAttention()

    def forward(self, q, k, v, mask=None):
        B = q.size(0)
        q = self.wq(q).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
        k = self.wk(k).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
        v = self.wv(v).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
        out = self.attn(q, k, v, mask)
        out = out.transpose(1, 2).contiguous().view(B, -1, self.n_heads * self.d_k)
        return self.out(out)

# ================================
# 3. 前馈网络 FFN
# ================================
class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff, dropout=0.1):
        super().__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.linear2(self.dropout(self.relu(self.linear1(x))))

# ================================
# 4. Encoder Layer
# ================================
class EncoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
        super().__init__()
        self.attn = MultiHeadAttention(d_model, n_heads)
        self.ffn = FeedForward(d_model, d_ff, dropout)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        attn_out = self.attn(x, x, x, mask)
        x = self.norm1(x + self.dropout1(attn_out))
        ffn_out = self.ffn(x)
        x = self.norm2(x + self.dropout2(ffn_out))
        return x

# ================================
# 5. Decoder Layer
# ================================
class DecoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
        super().__init__()
        self.self_attn = MultiHeadAttention(d_model, n_heads)
        self.cross_attn = MultiHeadAttention(d_model, n_heads)
        self.ffn = FeedForward(d_model, d_ff, dropout)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)
        self.dropout3 = nn.Dropout(dropout)

    def forward(self, x, enc_out, tgt_mask=None):
        # 掩码自注意力
        attn_out = self.self_attn(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout1(attn_out))

        # 交叉注意力
        cross_out = self.cross_attn(x, enc_out, enc_out)
        x = self.norm2(x + self.dropout2(cross_out))

        # FFN
        ffn_out = self.ffn(x)
        x = self.norm3(x + self.dropout3(ffn_out))
        return x

# ================================
# 6. 完整 Transformer
# ================================
class Transformer(nn.Module):
    def __init__(self, d_model=512, n_heads=8, n_layers=6, d_ff=2048, vocab_size=1000):
        super().__init__()
        self.d_model = d_model
        self.embedding = nn.Embedding(vocab_size, d_model) # 1000 = 词表大小(vocab_size),你字典里一共有多少个 "词 / 符号"。512 = 模型维度(d_model),每个词要被表示成多长的向量。
        self.pos_enc = PositionalEncoding(d_model)

        # 堆叠 N 层 Encoder / Decoder
        self.encoders = nn.ModuleList([
            EncoderLayer(d_model, n_heads, d_ff) for _ in range(n_layers)
        ])
        self.decoders = nn.ModuleList([
            DecoderLayer(d_model, n_heads, d_ff) for _ in range(n_layers)
        ])

        # 输出层
        self.linear = nn.Linear(d_model, vocab_size)
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, src, tgt, src_mask=None, tgt_mask=None):
        # 1. 嵌入 + 位置编码
        src_emb = self.embedding(src) * math.sqrt(self.d_model) # 输入维度为[2,10],embedding后维度为[2,10,512]
        tgt_emb = self.embedding(tgt) * math.sqrt(self.d_model)
        src_emb = self.pos_enc(src_emb)
        tgt_emb = self.pos_enc(tgt_emb)

        # 2. Encoder 前向
        enc_out = src_emb
        for layer in self.encoders:
            enc_out = layer(enc_out, src_mask)

        # 3. Decoder 前向
        dec_out = tgt_emb
        for layer in self.decoders:
            dec_out = layer(dec_out, enc_out, tgt_mask)

        # 4. 输出预测
        output = self.linear(dec_out)
        return output

# ================================
# MAIN 函数:可直接运行
# ================================
if __name__ == '__main__':
    # 超参数(论文原版)
    d_model = 512
    n_heads = 8
    n_layers = 6
    d_ff = 2048
    vocab_size = 1000
    batch_size = 2
    src_len = 10
    tgt_len = 8

    # 随机输入(模拟token序列)
    src = torch.randint(0, vocab_size, (batch_size, src_len)) # [2,10]
    tgt = torch.randint(0, vocab_size, (batch_size, tgt_len)) # [2,8]

    # 构建模型
    model = Transformer(d_model, n_heads, n_layers, d_ff, vocab_size)

    # 前向传播
    output = model(src, tgt)

    # 输出信息
    print("输入 src shape:", src.shape)
    print("输入 tgt shape:", tgt.shape)
    print("输出 logits shape:", output.shape)
    print("\n✅ Transformer 完整运行成功!")
    print("✅ 包含:位置编码 | Encoder×6 | Decoder×6 | 掩码 | LayerNorm | 残差")

十一、总结

Transformer 的核心创新可以概括为以下五点:

  1. 完全基于自注意力机制,抛弃 RNN/CNN,实现高度并行;
  2. 多头注意力,从多个子空间捕捉依赖关系;
  3. Encoder-Decoder 架构,通用 Seq2Seq 建模;
  4. 位置编码,显式注入序列顺序信息;
  5. 残差 + 层归一化,支持深度网络稳定训练。

它不仅重新定义了序列建模,更成为整个深度学习时代的基础组件,支撑了从 NLP 到 CV、从语音到多模态、从 AIGC 到大模型的全面爆发。

可以说,Transformer 重新定义了深度学习的架构范式,其影响力仍在持续扩展。


相关推荐
GreenTea2 小时前
AI 时代,工程师的不可替代性在哪里
前端·人工智能·后端
小程故事多_802 小时前
破除迷思,Harness Engineering从来都不是时代过渡品
人工智能·架构·prompt·aigc
热爱专研AI的学妹2 小时前
Seedance 2.0(即梦 2.0)深度解析:AI 视频正式迈入导演级精准可控时代
大数据·人工智能·阿里云·音视频
Ulyanov3 小时前
用Pyglet打造AI数字猎人:从零开始的Python游戏开发与强化学习实践
开发语言·人工智能·python
lcj09246663 小时前
磁控U位管理系统与DCIM对接实现:筑牢数据中心精细化运维底座
大数据·数据库·人工智能
swipe3 小时前
用 Nest + LangChain 打造 OpenClaw 式 Agent 定时任务系统
人工智能·llm·agent
幻风_huanfeng4 小时前
人工智能之数学基础:动量梯度下降法
人工智能·机器学习·动量梯度下降法
2301_799073024 小时前
基于 Next.js + 火山引擎 AI 的电商素材智能生成工具实战——字节跳动前端训练营成果
javascript·人工智能·火山引擎
xingyuzhisuan5 小时前
租用GPU服务器进行深度学习课程教学的实验环境搭建
运维·人工智能·深度学习·gpu算力