ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

学一下Transform(TODO1)

学一下Transform(TODO1) 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 通常先做 TokenizationTransformer很好用 ↓ 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 │ ▼ OutputResidual 可以理解成别把原来的信息扔掉把新结果加回原来的信息。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/VQ 去匹配 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、VTransformer 里面有三个训练出来的矩阵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我们问“爱”应该关注谁它自己的 QueryQ爱​[1,1]然后拿这个 Q 去和所有 token 的 K 做点积。和“我”比较[1,1]⋅[1,0]就是1×11×01所以爱 → 我 1和“爱”自己比较[1,1]⋅[1,1]得到112所以爱 → 爱 2和“苹果”比较[1,1]⋅[0,1]得到011所以爱 → 苹果 1Attention 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.03e1.414≈4.11e0.707≈2.03总和2.034.112.038.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%现在真正拿信息。三个 VV我 [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(encodingutf-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_model64, d_ff128, num_layers2, num_classes2): 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, devicex.device).unsqueeze(1) div_term torch.exp(torch.arange(0, self.d_model, 2, devicex.device) * (-math.log(10000.0) / self.d_model)) pe torch.zeros(seq_len, self.d_model, devicex.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(dim1) # (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_model64, nhead4, num_encoder_layers2, num_decoder_layers2): 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_modeld_model, nheadnhead, # 多头注意力的头数 num_encoder_layersnum_encoder_layers, num_decoder_layersnum_decoder_layers, dim_feedforward128, batch_firstTrue, # 输入形状用 (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(dim1) % 2).long() # 标签 model SimpleTransformer(vocab_sizevocab_size, num_classes2) optimizer torch.optim.Adam(model.parameters(), lr0.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(dim1) y).float().mean().item() print(fEpoch {epoch1: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(dim1).item() ok ✓ if pred expected else ✗ print(f测试句子 {sentence} 预测类别: {pred}期望 {expected}{ok}) def demo_pytorch(): 演示 PyTorch 自带 Transformer 的用法 print() print( * 60) print(演示 2PyTorch 自带 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_vocabsrc_vocab, tgt_vocabtgt_vocab) optimizer torch.optim.Adam(model.parameters(), lr0.001) loss_fn nn.CrossEntropyLoss(ignore_index0) # 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(fEpoch {epoch1: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✓ 演示 2PyTorch 自带 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 示例包含三个部分部分内容目的第一部分手写版 Transformer4 个类理解原理第二部分PyTorch 自带nn.Transformer工程实践第三部分两个演示函数看怎么用第一部分手写版理解原理1.SelfAttention—— 注意力机制核心这是整个 Transformer 的灵魂对应原论文公式Attention(Q, K, V) softmax(QKᵀ / √d_k) · VQ/K/V 的比喻代码注释里也写了QQuery 查询我要找什么KKey 键我是什么VValue 值我提供什么内容forward里的 4 步第 47-68 行三个线性层把输入分别变成 Q、K、VQ Kᵀ计算两两之间的相似度分数 → 形状(batch, seq_len, seq_len)除以√d_k防止数值过大这就是 Scaled 的含义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的完整训练循环建议阅读顺序先看SelfAttention—— 注意力是核心看懂这个就懂了一半再看TransformerBlock—— 残差 LayerNorm 的组装然后看SimpleTransformer—— 整体流程和位置编码最后看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机器翻译演示关键学习点注意力机制Q 是我要找什么K 是我是什么V 是我提供什么通过 QKᵀ 计算相似度再加权求和位置编码Transformer 没有顺序概念用正弦/余弦函数手动加位置信息残差连接 LayerNorm防止梯度消失、稳定训练手写版 vs 自带版手写版是编码器-only分类自带版是编码器解码器翻译运行方式python transformer_simple.py已安装 torch 2.13.0 CPU 版和 numpy。
返回列表