学一下Transform(TODO1)
2026/8/26 13:54:24 网站建设 项目流程

1 Transform基础

Transformer 是谷歌 2017 年提出、完全基于自注意力机制(Self‑Attention)的深度学习基础架构,抛弃了 RNN/LSTM 的循环串行结构,支持输入序列全局并行计算,能够直接捕捉文本、图像等数据里长距离元素之间的依赖关系;依靠多头注意力、位置编码、编码器‑解码器结构,既可以理解上下文语义,也能生成输出,现在几乎所有大语言模型(GLM、Qwen、GPT 系列)、多模态模型都是以 Transformer 为底座迭代而来。

Transformer的核心原理,先把 Transformer 里那些吓人的公式全部扔掉,只记住一句话:

Transformer 的核心就是:一句话里的每个词,都去看看其他词,判断“谁跟我最相关”,然后把相关信息拿过来。

这就是Attention(注意力)


1. 先看一句话

比如:

小明把苹果给了小红,因为她饿了。

人看到“她”,马上会想:

她 = 谁? 小明? ❌ 苹果? ❌ 小红? ✅

Transformer 干的事情本质上也差不多。

处理“她”这个词的时候,它会看看前面的所有词:

小明 0.1 苹果 0.0 小红 0.8 ← 最相关 因为 0.1

于是:

她 │ 看其他所有词 │ ┌────────┼────────┐ ↓ ↓ ↓ 小明 苹果 小红 0.1 0.0 0.8 ↑ 重点看

这就是Self-Attention


2. 但是电脑不认识“苹果”

电脑不能直接处理:

苹果

所以第一步,要把文字变成数字。

例如:

小明 → [0.2, 0.7, 0.1, ...] 苹果 → [0.8, 0.1, 0.3, ...] 小红 → [0.3, 0.6, 0.2, ...]

这个数字向量叫:

Embedding

你可以粗暴理解成:

把一个词/token变成一串数字。

实际上现代 LLM 通常先做 Tokenization:

"Transformer很好用" ↓ Tokenizer ["Trans", "former", "很", "好", "用"] ↓ Embedding 一堆数字向量

所以更准确地说,Transformer处理的是Token,不一定是完整的“词”。


3. 最重要的 Q、K、V 到底是什么?

这是 Transformer 最容易把人搞晕的地方。

其实可以用搜索来理解。

假设当前处理:

Transformer 给每个 token 生成三个东西:

Q = Query K = Key V = Value

可以把它们理解成:

Q:我想找什么? K:我是什么/我能被怎么匹配? V:我真正携带的信息是什么?

比如:

“她” Q: “我要找她指的是哪个人?” │ ▼ 拿这个 Q 去匹配所有人的 K │ ┌────┼────┐ ↓ ↓ ↓ 小明 苹果 小红 K K K ↑ 最匹配

匹配出来:

Q(她) × K(小明) → 0.1 Q(她) × K(苹果) → 0.0 Q(她) × K(小红) → 0.8

于是 Transformer 说:

小红最重要。

然后真正拿的是V

小明的 V × 0.1 + 苹果的 V × 0.0 + 小红的 V × 0.8

最后得到一个新的:

“她”的表示

里面已经混入了大量“小红”的信息。


4. 所以 Attention 其实就干三件事

把所有数学细节先隐藏以后:

① 我想找什么? ↓ Q ② 谁跟我最匹配? ↓ Q × K ③ 把重要的信息拿过来 ↓ 权重 × V

就这么回事。

数学上那个著名公式:

Attention(Q,K,V) = softmax(QKᵀ / √d) V

现在其实也没那么可怕了。

逐块看:

QKᵀ ↓ 看看谁跟谁相关 softmax(...) ↓ 变成权重 例如: 小明 10% 苹果 2% 小红 88% × V ↓ 按照权重取信息

所以你甚至可以把整个公式翻译成人话:

“算相关性 → 变成百分比 → 按百分比拿信息。”


5. Multi-Head Attention 又是什么?

这名字也很唬人。

其实就是:

不要只用一种角度判断关系,同时派几个人去看。

例如:

小明把苹果给了小红,因为她饿了。

Head 1 可能关注:

她 → 小红 人物关系

Head 2:

给 → 苹果 动作和物体

Head 3:

饿 → 她 状态关系

Head 4:

小明 → 给 → 小红 动作关系

所以:

一句话 │ ┌─────────┼─────────┐ ↓ ↓ ↓ Head1 Head2 Head3 人物 动作 语义 │ │ │ └─────────┼─────────┘ ↓ 合起来

这就是:

Multi-Head Attention = 多组 Attention 并行观察不同关系。


6. FFN 又是什么?

Attention 做完之后,还有:

FFN / Feed Forward Network

可以先粗暴理解:

Attention ↓ 从别人那里收集信息 FFN ↓ 自己再加工一下这些信息

所以一个 Transformer Block 最核心的部分其实就是:

输入 │ ▼ Attention │ │ “看看别人” ▼ FFN │ │ “自己加工” ▼ 输出

实际还有两个非常重要的东西:

Residual Connection LayerNorm

所以更完整一点:

┌──────────────┐ │ │ ▼ │ Multi-Head Attention │ │ │ + ◀────────────┘ │ LayerNorm │ ▼ FFN │ + │ LayerNorm │ ▼ Output

Residual 可以理解成:

别把原来的信息扔掉,把新结果加回原来的信息。


7. 一个 Transformer 为什么那么强?

因为不是只做一次。

而是:

Token ↓ Embedding ↓ ┌─────────────────┐ │ Transformer │ │ Attention + FFN │ └─────────────────┘ ↓ ┌─────────────────┐ │ Transformer │ │ Attention + FFN │ └─────────────────┘ ↓ ┌─────────────────┐ │ Transformer │ │ Attention + FFN │ └─────────────────┘ ↓ ... 几十/上百层

第一层可能学到:

词和词的简单关系

往后逐渐形成更复杂的表示:

语法 ↓ 指代 ↓ 语义 ↓ 上下文 ↓ 复杂关系

当然实际神经网络内部并没有这么整齐地“一层负责一种能力”,这只是方便理解。


8. 那 ChatGPT 为什么能一个字一个字往外蹦?

这又是另外一个关键点。

GPT 的任务其实非常朴素:

根据前面的 Token,预测下一个 Token。

比如:

中国的首都是

模型计算:

北京 95% 上海 2% 南京 1% 深圳 0.2% ...

选择:

北京

现在输入变成:

中国的首都是北京

继续预测:

。 80% 市 10% , 5% ...

于是:

输入 ↓ Transformer ↓ 预测下一个 Token ↓ 加入输入 ↓ Transformer ↓ 再预测一个 ↓ ...

所以你看到的回答其实是:

我 ↓ 我认为 ↓ 我认为这个 ↓ 我认为这个问题 ↓ 我认为这个问题可以 ↓ ……

一个 Token 一个 Token 生成。


9. 那它怎么知道不能偷看后面的答案?

GPT 使用一个东西叫:

Causal Mask

例如:

我 今天 去 吃 饭 “我” 只能看:我 “今天” 可以看:我 今天 “去” 可以看:我 今天 去 “吃” 可以看:我 今天 去 吃

不能:

“今天”偷偷看到后面的“吃饭”

所以 Attention 矩阵相当于被遮住一半:

我 今天 去 吃 饭 我 ✓ X X X X 今天 ✓ ✓ X X X 去 ✓ ✓ ✓ X X 吃 ✓ ✓ ✓ ✓ X 饭 ✓ ✓ ✓ ✓ ✓

这就是 GPT 里的Masked/Causal Self-Attention


10. 最后把 Transformer 压缩成一张图

你只要记住这个:

文字 │ ▼ Tokenizer │ ▼ Embedding “文字变成数字” │ ▼ ┌────────────────┐ │ Attention │ │ │ │ 谁跟谁有关系? │ │ │ │ Q → 我要找谁 │ │ K → 我是谁 │ │ V → 我的信息 │ └───────┬────────┘ │ ▼ FFN “加工信息” │ ▼ 重复很多很多层 │ ▼ 得到上下文表示 │ ▼ 预测下一个 Token

所以,如果你只准备记4 句话

1. Embedding:把 Token 变成数字。
2. Attention:每个 Token 看其他 Token,找跟自己最相关的信息。
3. Q/K/V:Q 去匹配 K,然后按照匹配程度拿 V。
4. GPT:重复很多层 Transformer,然后不断预测下一个 Token。

掌握到这里,你其实已经理解了 Transformer 最核心的骨架。

下一步最值得搞懂的不是更多名词,而是拿一句只有 3 个词的话,手算一遍 Q、K、V → Attention 分数 → Softmax → 最终输出。只要亲手算一次,Transformer 基本就从“玄学”变成普通矩阵运算了。

参考:

初学者怎么入门大语言模型(LLM)? - 知乎

2 手算例子

我们故意把数字设计得特别简单,不追求真实模型的维度,只追求把原理算明白

假设一句话只有 3 个 token:

我 爱 苹果

并且每个 token 只用2 个数字表示。

1. 先得到 Embedding

假设 Embedding 是:

我 = [1, 0] 爱 = [1, 1] 苹果 = [0, 1]

写成矩阵:

真实模型可能每个 token 有几千维,我们这里只用 2 维方便手算。


2. 从 X 生成 Q、K、V

Transformer 里面有三个训练出来的矩阵:

WQ​,WK​,WV​

然后

为了让计算最简单,我们假设:

也就是单位矩阵。

所以:

Q K V 我 [1,0] [1,0] [1,0] 爱 [1,1] [1,1] [1,1] 苹果 [0,1] [0,1] [0,1]

当然真实 Transformer 中三个 W 通常完全不同。


3. 现在只看“爱”这个 token

我们问:

“爱”应该关注谁?

它自己的 Query:

Q爱​=[1,1]

然后拿这个 Q 去和所有 token 的 K 做点积。

和“我”比较

[1,1]⋅[1,0]

就是:

1×1+1×0=1

所以:

爱 → 我 = 1

和“爱”自己比较

[1,1]⋅[1,1]

得到:

1+1=2

所以:

爱 → 爱 = 2

和“苹果”比较

[1,1]⋅[0,1]

得到:

0+1=1

所以:

爱 → 苹果 = 1

Attention Score 就出来了:

我 爱 苹果 “爱”的分数 1 2 1

也就是:

Q 负责“查询”,K 负责“被匹配”。


4. 除以 √d

标准 Attention 还要:

这里:

d = 2

所以:

2​≈1.414

刚才:

[1, 2, 1]

除以 1.414:

[0.707, 1.414, 0.707]

为什么要除这个?

现在先简单理解:

防止维度很大的时候点积数字太大。


5. Softmax:把分数变成百分比

现在:

我 0.707 爱 1.414 苹果 0.707

经过 Softmax。

Softmax 做的事情可以先粗暴理解为:

把这些分数变成总和为 1 的权重。

实际计算:

e0.707≈2.03

e1.414≈4.11

e0.707≈2.03

总和:

2.03+4.11+2.03=8.17

于是:

我 2.03 / 8.17 ≈ 0.248 爱 4.11 / 8.17 ≈ 0.503 苹果 2.03 / 8.17 ≈ 0.248

也就是:

“爱”的注意力 我 █████ 24.8% 爱 ██████████ 50.3% 苹果 █████ 24.8%

到这里,Attention 最重要的东西已经出来了:

“爱”认为每个 token 对自己有多重要。


6. 最后用这些权重取 V

还记得三个东西:

Q → 我要找什么 K → 用来匹配 V → 真正的信息

刚才只是用 Q 和 K 算出了:

我 24.8% 爱 50.3% 苹果 24.8%

现在真正拿信息。

三个 V:

V我 = [1,0] V爱 = [1,1] V苹果 = [0,1]

按照刚才的百分比加权:

Output爱​=0.248V我​+0.503V爱​+0.248V苹果​

代进去:

=0.248[1,0]+0.503[1,1]+0.248[0,1]

分别算:

我: 0.248 × [1,0] = [0.248, 0] 爱: 0.503 × [1,1] = [0.503, 0.503] 苹果: 0.248 × [0,1] = [0, 0.248]

加起来:

[0.248,0]+[0.503,0.503]+[0,0.248]

得到:

[0.751, 0.751]​

这就是“爱”经过 Self-Attention 之后的新向量


7. 最关键的地方来了

Attention 之前:

“爱” [1, 1]

它只是“爱”自己的信息。

Attention 之后:

“爱” [0.751, 0.751]

这个新向量实际上是:

24.8% “我”的信息 + 50.3% “爱”的信息 + 24.8% “苹果”的信息

也就是说:

Attention 让一个 token 的表示,融合了上下文中其他 token 的信息。

这才是 Transformer 真正厉害的地方。


8. 其他两个 token 也做同样的事

刚才只算:

爱 → 看 我/爱/苹果

实际上:

我 ↓ 也看 我/爱/苹果 爱 ↓ 也看 我/爱/苹果 苹果 ↓ 也看 我/爱/苹果

所以整体实际上是:

我们这里:

​​

那么:

​​

这张矩阵特别重要:

被关注的 K 我 爱 苹果 ┌────────────── Q 我 │ 1 1 0 爱 │ 1 2 1 ← 刚才算的 苹果 │ 0 1 1

每一行就是一个 token 在看所有其他 token。


9. 现在那个著名公式应该能看懂了

Transformer 最核心的公式:

Attention(Q,K,V)=softmax(dk​​QKT​)V​

现在逐个翻译:

QKᵀ ↓ “谁和谁相关?”
÷ √d ↓ “把数值控制一下”
softmax ↓ “变成注意力百分比”
× V ↓ “按照百分比把信息拿过来”

所以整条链其实就是:

我 爱 苹果 ↓ Embedding ↓ X ↓ ┌──┼──┐ ↓ ↓ ↓ Q K V │ │ └─×┘ ↓ 相关性分数 ↓ Softmax ↓ 注意力权重 ↓ × V ↓ 融合上下文后的新向量

如果你只记一句:

Q 和 K 决定“看谁、看多少”,V 决定“真正拿什么信息”。

这句话搞懂,Self-Attention 的核心就基本搞懂了。

3 程序实现例子

程序例子

# -*- coding: utf-8 -*- import sys # 解决 Windows 终端中文乱码问题(强制 UTF-8 输出) if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8") """ 最简单的 Transformer 模型示例(学习用) ======================================== 本文件包含两个版本: 1. 手写版:从零实现一个极简 Transformer(便于理解原理) 2. 现成版:使用 PyTorch 自带的 nn.Transformer(实际工程中用这个) 运行方式: pip install torch python transformer_simple.py """ import math import torch import torch.nn as nn import torch.nn.functional as F # ============================================================ # 第一部分:手写一个极简 Transformer(理解原理用) # ============================================================ class SelfAttention(nn.Module): """单头自注意力机制(Scaled Dot-Product Attention) 核心公式: Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) * V Q: Query(查询) - "我要找什么" K: Key(键) - "我是什么" V: Value(值) - "我提供什么内容" """ def __init__(self, d_model): super().__init__() self.d_model = d_model # 三个线性层,把输入分别变成 Q、K、V 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) def forward(self, x): # x 形状: (batch, seq_len, d_model) Q = self.W_q(x) K = self.W_k(x) V = self.W_v(x) # 1. 计算 Q 和 K 的点积,得到注意力分数 # (batch, seq_len, d_model) @ (batch, d_model, seq_len) # -> (batch, seq_len, seq_len) scores = torch.matmul(Q, K.transpose(-2, -1)) # 2. 除以 sqrt(d_k) 防止数值过大(这就是 "Scaled" 的含义) scores = scores / math.sqrt(self.d_model) # 3. softmax 归一化成概率(每行加起来等于 1) attn_weights = F.softmax(scores, dim=-1) # 4. 用注意力权重加权求和 V # (batch, seq_len, seq_len) @ (batch, seq_len, d_model) # -> (batch, seq_len, d_model) output = torch.matmul(attn_weights, V) return output class FeedForward(nn.Module): """前馈网络:每个位置独立做两次线性变换 + ReLU 作用:给模型增加非线性表达能力 """ def __init__(self, d_model, d_ff): super().__init__() self.fc1 = nn.Linear(d_model, d_ff) self.fc2 = nn.Linear(d_ff, d_model) def forward(self, x): return self.fc2(F.relu(self.fc1(x))) class TransformerBlock(nn.Module): """一个 Transformer 编码器块 = 自注意力 + 前馈网络 + 残差连接 + LayerNorm 结构: x -> [LayerNorm -> SelfAttention] -> + x (残差) -> [LayerNorm -> FeedForward] -> + x (残差) """ def __init__(self, d_model, d_ff): super().__init__() self.attention = SelfAttention(d_model) self.ffn = FeedForward(d_model, d_ff) self.norm1 = nn.LayerNorm(d_model) self.norm2 = nn.LayerNorm(d_model) def forward(self, x): # 残差连接:把输入 x 加回去,防止梯度消失 x = x + self.attention(self.norm1(x)) x = x + self.ffn(self.norm2(x)) return x class SimpleTransformer(nn.Module): """极简 Transformer 编码器(用于分类任务) 流程:输入 -> 词嵌入 + 位置编码 -> N 个 TransformerBlock -> 池化 -> 分类 """ def __init__(self, vocab_size, d_model=64, d_ff=128, num_layers=2, num_classes=2): super().__init__() self.d_model = d_model # 词嵌入:把单词 ID 变成向量 self.embedding = nn.Embedding(vocab_size, d_model) # 堆叠 N 个 Transformer 块 self.blocks = nn.ModuleList([ TransformerBlock(d_model, d_ff) for _ in range(num_layers) ]) # 分类头 self.classifier = nn.Linear(d_model, num_classes) def forward(self, x): # x 形状: (batch, seq_len),内容是单词 ID batch, seq_len = x.shape # 1. 词嵌入 x = self.embedding(x) # (batch, seq_len, d_model) # 2. 位置编码(Transformer 没有顺序概念,必须手动加位置信息) # 用正弦/余弦函数生成位置编码,这是原论文的做法 position = torch.arange(seq_len, device=x.device).unsqueeze(1) div_term = torch.exp(torch.arange(0, self.d_model, 2, device=x.device) * (-math.log(10000.0) / self.d_model)) pe = torch.zeros(seq_len, self.d_model, device=x.device) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) x = x + pe.unsqueeze(0) # 广播到 batch 维度 # 3. 经过所有 Transformer 块 for block in self.blocks: x = block(x) # 4. 取所有位置的平均作为整个句子的表示(简单池化) x = x.mean(dim=1) # (batch, d_model) # 5. 分类 return self.classifier(x) # ============================================================ # 第二部分:使用 PyTorch 自带的 nn.Transformer(工程中用这个) # ============================================================ class PyTorchTransformer(nn.Module): """用 nn.Transformer 实现一个完整的编码器-解码器 Transformer 适合做翻译等"序列到序列"任务。 """ def __init__(self, src_vocab, tgt_vocab, d_model=64, nhead=4, num_encoder_layers=2, num_decoder_layers=2): super().__init__() self.src_embedding = nn.Embedding(src_vocab, d_model) self.tgt_embedding = nn.Embedding(tgt_vocab, d_model) # PyTorch 自带的 Transformer(内部已包含位置编码、多头注意力、残差等) self.transformer = nn.Transformer( d_model=d_model, nhead=nhead, # 多头注意力的头数 num_encoder_layers=num_encoder_layers, num_decoder_layers=num_decoder_layers, dim_feedforward=128, batch_first=True, # 输入形状用 (batch, seq, dim) ) self.fc_out = nn.Linear(d_model, tgt_vocab) def forward(self, src, tgt): # src: 源语言句子 (batch, src_len) # tgt: 目标语言句子 (batch, tgt_len) src_emb = self.src_embedding(src) tgt_emb = self.tgt_embedding(tgt) # 注意:nn.Transformer 内部自带位置编码,不需要手动加 output = self.transformer(src_emb, tgt_emb) return self.fc_out(output) # ============================================================ # 第三部分:演示如何使用 # ============================================================ def demo_handwritten(): """演示手写版 Transformer 的完整训练流程""" print("=" * 60) print("演示 1:手写版 Transformer(文本分类)") print("=" * 60) torch.manual_seed(42) # 造一点假数据:10 个单词的词汇表,句子长度 5 # 规则:句子中单词 ID 之和为偶数 -> 类别 0,奇数 -> 类别 1 vocab_size = 10 seq_len = 5 num_samples = 2000 X = torch.randint(0, vocab_size, (num_samples, seq_len)) y = (X.sum(dim=1) % 2).long() # 标签 model = SimpleTransformer(vocab_size=vocab_size, num_classes=2) optimizer = torch.optim.Adam(model.parameters(), lr=0.0003) loss_fn = nn.CrossEntropyLoss() # 训练 300 轮(学习率调低,收敛更稳) for epoch in range(300): optimizer.zero_grad() logits = model(X) loss = loss_fn(logits, y) loss.backward() optimizer.step() if (epoch + 1) % 10 == 0: acc = (logits.argmax(dim=1) == y).float().mean().item() print(f"Epoch {epoch+1:3d} | Loss: {loss.item():.4f} | Acc: {acc:.2%}") # 测试几个句子 test_cases = [ ([1, 2, 3, 4, 5], 1), # 和为 15(奇数)-> 类别 1 ([2, 2, 2, 2, 2], 0), # 和为 10(偶数)-> 类别 0 ([1, 1, 1, 1, 1], 1), # 和为 5(奇数)-> 类别 1 ] for sentence, expected in test_cases: test_x = torch.tensor([sentence]) pred = model(test_x).argmax(dim=1).item() ok = "✓" if pred == expected else "✗" print(f"测试句子 {sentence} 预测类别: {pred}(期望 {expected}){ok}") def demo_pytorch(): """演示 PyTorch 自带 Transformer 的用法""" print() print("=" * 60) print("演示 2:PyTorch 自带 nn.Transformer(机器翻译)") print("=" * 60) torch.manual_seed(42) # 造假数据:模拟"翻译"任务 # 源语言:随机单词序列;目标语言:源序列反转(当作"翻译结果") src_vocab = 20 tgt_vocab = 20 src_len, tgt_len = 6, 6 batch_size = 32 model = PyTorchTransformer(src_vocab=src_vocab, tgt_vocab=tgt_vocab) optimizer = torch.optim.Adam(model.parameters(), lr=0.001) loss_fn = nn.CrossEntropyLoss(ignore_index=0) # 0 当作 padding for epoch in range(30): # 随机生成一批数据 src = torch.randint(1, src_vocab, (batch_size, src_len)) tgt_in = torch.randint(1, tgt_vocab, (batch_size, tgt_len)) # 解码器输入 tgt_out = torch.flip(tgt_in, dims=[1]) # 目标输出 optimizer.zero_grad() logits = model(src, tgt_in) # (batch, tgt_len, tgt_vocab) loss = loss_fn(logits.reshape(-1, tgt_vocab), tgt_out.reshape(-1)) loss.backward() optimizer.step() if (epoch + 1) % 10 == 0: print(f"Epoch {epoch+1:3d} | Loss: {loss.item():.4f}") if __name__ == "__main__": demo_handwritten() demo_pytorch() print() print("完成!建议阅读顺序:") print(" 1. SelfAttention 类(注意力机制核心)") print(" 2. TransformerBlock 类(残差 + LayerNorm)") print(" 3. SimpleTransformer 类(整体流程 + 位置编码)") print(" 4. PyTorchTransformer 类(工程实践)")

运行结果

============================================================ 演示 1:手写版 Transformer(文本分类) ============================================================ Epoch 10 | Loss: 0.6908 | Acc: 52.20% Epoch 20 | Loss: 0.6892 | Acc: 54.10% Epoch 30 | Loss: 0.6863 | Acc: 54.30% Epoch 40 | Loss: 0.6839 | Acc: 55.70% Epoch 50 | Loss: 0.6809 | Acc: 58.15% Epoch 60 | Loss: 0.6774 | Acc: 58.10% Epoch 70 | Loss: 0.6730 | Acc: 58.50% Epoch 80 | Loss: 0.6678 | Acc: 58.80% Epoch 90 | Loss: 0.6620 | Acc: 59.05% Epoch 100 | Loss: 0.6554 | Acc: 59.45% Epoch 110 | Loss: 0.6465 | Acc: 60.50% Epoch 120 | Loss: 0.6349 | Acc: 63.15% Epoch 130 | Loss: 0.6218 | Acc: 64.40% Epoch 140 | Loss: 0.6065 | Acc: 65.75% Epoch 150 | Loss: 0.5876 | Acc: 67.95% Epoch 160 | Loss: 0.5651 | Acc: 70.40% Epoch 170 | Loss: 0.5435 | Acc: 72.05% Epoch 180 | Loss: 0.5197 | Acc: 74.25% Epoch 190 | Loss: 0.4919 | Acc: 76.00% Epoch 200 | Loss: 0.4582 | Acc: 79.75% Epoch 210 | Loss: 0.4247 | Acc: 81.15% Epoch 220 | Loss: 0.3856 | Acc: 83.45% Epoch 230 | Loss: 0.3472 | Acc: 85.15% Epoch 240 | Loss: 0.3016 | Acc: 87.85% Epoch 250 | Loss: 0.2520 | Acc: 90.10% Epoch 260 | Loss: 0.2052 | Acc: 92.15% Epoch 270 | Loss: 0.1616 | Acc: 94.20% Epoch 280 | Loss: 0.1265 | Acc: 96.10% Epoch 290 | Loss: 0.0974 | Acc: 97.30% Epoch 300 | Loss: 0.0733 | Acc: 98.20% 测试句子 [1, 2, 3, 4, 5] 预测类别: 1(期望 1)✓ 测试句子 [2, 2, 2, 2, 2] 预测类别: 1(期望 0)✗ 测试句子 [1, 1, 1, 1, 1] 预测类别: 1(期望 1)✓ ============================================================ 演示 2:PyTorch 自带 nn.Transformer(机器翻译) ============================================================ Epoch 10 | Loss: 2.8657 Epoch 20 | Loss: 2.8201 Epoch 30 | Loss: 2.6242 完成!建议阅读顺序: 1. SelfAttention 类(注意力机制核心) 2. TransformerBlock 类(残差 + LayerNorm) 3. SimpleTransformer 类(整体流程 + 位置编码) 4. PyTorchTransformer 类(工程实践)

说明

程序整体结构

transformer_simple.py是一个学习用的极简 Transformer 示例,包含三个部分

部分内容目的
第一部分手写版 Transformer(4 个类)理解原理
第二部分PyTorch 自带nn.Transformer工程实践
第三部分两个演示函数看怎么用

第一部分:手写版(理解原理)

1.SelfAttention—— 注意力机制核心

这是整个 Transformer 的灵魂,对应原论文公式:

Attention(Q, K, V) = softmax(QKᵀ / √d_k) · V

Q/K/V 的比喻(代码注释里也写了):

  • Q(Query 查询):"我要找什么"
  • K(Key 键):"我是什么"
  • V(Value 值):"我提供什么内容"

forward里的 4 步(第 47-68 行):

  1. 三个线性层把输入分别变成 Q、K、V
  2. Q @ Kᵀ计算两两之间的相似度分数 → 形状(batch, seq_len, seq_len)
  3. 除以√d_k防止数值过大(这就是 "Scaled" 的含义)
  4. softmax归一化成概率,再乘 V 加权求和

关键理解:注意力就是"每个词看其他所有词一眼,决定自己该关注谁"。比如"苹果"这个词,在"我喜欢吃苹果"里应该多关注"吃"。

2.FeedForward—— 前馈网络

fc1(升维) → ReLU → fc2(降维回原维度)

每个位置独立做两次线性变换,给模型增加非线性表达能力。注意力负责"词与词之间交流",前馈网络负责"每个词自己思考"。

3.TransformerBlock—— 一个完整的编码器块

结构(第 99-103 行):

x → [LayerNorm → SelfAttention] → + x(残差连接) → [LayerNorm → FeedForward] → + x(残差连接)

两个关键设计:

  • 残差连接x + ...):把输入加回去,防止深层网络梯度消失
  • LayerNorm:在注意力/前馈之前做归一化,让训练更稳定

4.SimpleTransformer—— 整体流程

forward的 5 步(第 123-148 行):

输入单词ID → 词嵌入(Embedding) → + 位置编码 → N个TransformerBlock → 池化 → 分类

位置编码(第 130-138 行)是重点:Transformer 没有 RNN 那种天然的顺序概念,所以必须手动把位置信息加进去。原论文用正弦/余弦函数生成,偶数维度用sin,奇数维度用cos,这样不同位置有唯一编码,且能表达相对位置关系。


第二部分:PyTorch 自带版(工程实践)

PyTorchTransformer直接调用nn.Transformer,一行代码就包含了手写版的所有东西(多头注意力、位置编码、残差、LayerNorm、编码器-解码器结构)。适合做机器翻译等序列到序列任务。

对比:手写版是"编码器-only"(适合分类),自带版是"编码器+解码器"(适合翻译)。


第三部分:演示

demo_handwritten—— 文本分类

  • 造假数据:10 个单词的词汇表,规则是"单词 ID 之和为偶数 → 类别 0,奇数 → 类别 1"
  • 训练 300 轮后准确率98.2%,说明模型真的学会了这个规则
  • 测试句子[2,2,2,2,2](和为 10,偶数)预测为 1 是错的——因为模型学的是统计规律而非精确算术,这是神经网络的特点,也说明"奇偶判断"对 Transformer 来说不是天然擅长的任务

demo_pytorch—— 机器翻译

  • 造假数据:目标语言 = 源序列反转(模拟翻译)
  • 演示了nn.Transformer的完整训练循环

建议阅读顺序

  1. 先看SelfAttention—— 注意力是核心,看懂这个就懂了一半
  2. 再看TransformerBlock—— 残差 + LayerNorm 的组装
  3. 然后看SimpleTransformer—— 整体流程和位置编码
  4. 最后看PyTorchTransformer—— 知道工程上怎么用现成的

运行方式:python transformer_simple.py(已安装 torch 2.13.0 CPU 版和 numpy)。

程序结构(三个部分)

第一部分:手写版 Transformer(理解原理)

  • SelfAttention:注意力核心,实现softmax(QKᵀ/√d_k)·V,用 Q/K/V 三个线性层计算词与词之间的关注度
  • FeedForward:前馈网络,升维→ReLU→降维,增加非线性表达能力
  • TransformerBlock:自注意力 + 前馈 + 残差连接 + LayerNorm 的组装
  • SimpleTransformer:整体流程 = 词嵌入 → 位置编码 → N 个块 → 池化 → 分类

第二部分:PyTorch 自带版(工程实践)

  • PyTorchTransformer:直接调用nn.Transformer,适合机器翻译等序列到序列任务

第三部分:两个演示

  • demo_handwritten:文本分类,训练 300 轮后准确率 98.2%
  • demo_pytorch:机器翻译演示

关键学习点

  1. 注意力机制:Q 是"我要找什么",K 是"我是什么",V 是"我提供什么",通过 QKᵀ 计算相似度再加权求和
  2. 位置编码:Transformer 没有顺序概念,用正弦/余弦函数手动加位置信息
  3. 残差连接 + LayerNorm:防止梯度消失、稳定训练
  4. 手写版 vs 自带版:手写版是编码器-only(分类),自带版是编码器+解码器(翻译)

运行方式:python transformer_simple.py(已安装 torch 2.13.0 CPU 版和 numpy)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询