transformer你不学?09|完整 Transformer:从零手搓迷你版 transformer你不学09完整 Transformer从零手搓迷你版作 者吴佳浩Alben微某信公某众号全栈架构师笔记导读第 2 篇 到 第 8 篇 我们拆完了每一块零件分词、嵌入、位置、注意力、掩码、残差。本篇把它们全部拼起来——一个 2 万参数的迷你 Transformer能训练、能生成。大模型的一切收敛到这一个类里。本篇你会学到什么 完整 Decoder-only 架构的数据流nn.TransformerEncoder的工业用法掩码传递 训练循环 自回归生成的端到端串接。一、为什么要学这个前面的零件都单独验证过了但拼装时有两个新的工程问题组件接口对齐词嵌入输出(B,T,C)、位置编码输出(T,C)、掩码是(T,T)——三者如何相加、如何传入注意力层训练与生成两种模式的切换训练一次前向算全部位置的 loss并行生成逐 token 循环串行同一套权重两种用法。本篇的 Mini Transformer 用 PyTorch 内置的TransformerEncoderLayer工业级实现内部就是 第 6 篇第 7 篇 的组合我们负责架构层拼装——这也是读真实开源代码nanoGPT/minGPT的视角。二、核心理论Decoder-only 数据流input_ids (B,T) → tok_emb(idx) pos_emb(pos) # 语义 位置 → N × EncoderLayer(causal_mask) # 注意力带因果掩码 Decoder 能力 → LayerNorm # 最终归一化 → Linear(d_model, vocab) # 投影回词表 → logits (B,T,vocab) # 每个位置预测下一个词两个关键认知Decoder-only 就是用掩码注意力改造的 Encoder——GPT 的Decoder没有论文里的交叉注意力结构上更接近 Encoder 因果掩码语言建模的输出复用输入logits 在每个位置预测下一个词训练目标logits[:, :-1]vslabels[:, 1:]的错位对齐——一个 batch 里同时学 T 个监督信号。三、流程图训练: 错位对齐算 loss生成: 取最后位置input_ids B x T词嵌入 Embedding位置索引 arange T位置嵌入 Embedding相加: 语义位置N x TransformerEncoderLayer 因果掩码LayerNormLinear → 词表投影logits B x T x vocab反向传播argmax/采样 → 拼接 → 循环四、真实代码完整模型# 迷你 Transformer第 2 篇~第 8 篇 全部零件的拼装importtorchimporttorch.nnasnnimporttorch.nn.functionalasFimportmath torch.manual_seed(42)classMiniTransformer(nn.Module):def__init__(self,vocab50,d_model32,n_heads4,n_layers2,max_len32):super().__init__()# 零件1词嵌入第 3 篇self.tok_embnn.Embedding(vocab,d_model)# 零件2可学习位置编码第 4 篇GPT 风格self.pos_embnn.Embedding(max_len,d_model)# 零件3N 层标准块第 6 篇第 7 篇 的工业实现layernn.TransformerEncoderLayer(d_modeld_model,nheadn_heads,dim_feedforward64,batch_firstTrue,# 输入 (B,T,C)norm_firstTrue,# Pre-LN现代主流)self.encodernn.TransformerEncoder(layer,num_layersn_layers)# 零件4最终归一化 词表投影头self.lnnn.LayerNorm(d_model)self.headnn.Linear(d_model,vocab)defforward(self,idx):B,Tidx.shape postorch.arange(T,deviceidx.device)# (T,)xself.tok_emb(idx)self.pos_emb(pos)# 语义位置 (B,T,C)causaltorch.triu(torch.ones(T,T,dtypetorch.bool),1)# 因果掩码 (第 8 篇)xself.encoder(x,maskcausal)# 带掩码注意力returnself.head(self.ln(x))# (B,T,vocab)mMiniTransformer()idxtorch.randint(0,50,(2,10))logitsm(idx)print(flogits shape:{logits.shape})print(f参数量:{sum(p.numel()forpinm.parameters()):,})输出结果怎么看真实运行输出logits shape: torch.Size([2, 10, 50]) 参数量: 21,426(2, 10, 50)每个位置都有对 50 个词的预测分布——语言建模的输出形态 2.1 万参数 vs GPT-3 的 1750 亿结构完全同构只差规模。这是手搓迷你版的意义——架构认知不受参数量门槛限制。五、真实代码训练 生成端到端# 训练一个真实任务学一串循环序列的规律# 接上沿用上一块的 MiniTransformer 类定义importtorch torch.manual_seed(42)mMiniTransformer(vocab8,d_model32,n_heads4,n_layers2)opttorch.optim.AdamW(m.parameters(),lr3e-3)# 构造训练数据形如 0123456701234567... 的循环序列datatorch.tensor([list(range(8))*20])# (1, 160)# 训练错位对齐——input 是前 T 个label 是后移一位forstepinrange(300):starttorch.randint(0,152,(1,)).item()chunkdata[:,start:start16]# 随机取 16 长度片段x,ychunk[:,:-1],chunk[:,1:]# 输入截掉末位标签截掉首位logitsm(x)lossF.cross_entropy(logits.reshape(-1,8),y.reshape(-1))opt.zero_grad();loss.backward();opt.step()ifstep%1000:print(fstep{step}: loss {loss.item():.4f})# 生成自回归续写m.eval()ctxtorch.tensor([[0,1,2]])# 给 3 个起始 tokenwithtorch.no_grad():for_inrange(13):logitsm(ctx)ctxtorch.cat([ctx,logits[:,-1].argmax(-1,keepdimTrue)],dim1)print(f模型续写:{ctx.tolist()})输出结果怎么看真实运行输出step 0: loss 2.1297 step 100: loss 0.0183 step 200: loss 0.0059 模型续写: [[0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7]] loss 从 2.13≈ln82.079均匀瞎猜降到 0.0059模型学会了循环规律——这是端到端训练真实生效的铁证 续写完美复现3,4,5,6,7,0,1,2...训练时它只见过错位对齐的片段生成时靠因果掩码逐词外推——训练与生成的模式一致性得到了行为级验证 这就是所有大语言模型的缩影换更大的 vocab、更多的层、更丰富的数据结构一步没变。六、常见错误与排查掩码 dtype 错nn.TransformerEncoder的 mask 接受 bool 或 float但 bool 的 True 语义是屏蔽与我们 第 8 篇 一致float 版本-inf同效别混用两套约定错位对齐搞反y chunk[:, 1:]是标签后移写成y chunk[:, :-1]模型学的是复述自己loss 降到 0 但生成是复读机生成时忘了 eval()dropout 开着导致生成随机漂移——虽然本例没 dropout真实模型必有位置表越界max_len32时喂 40 长度序列直接 IndexError——生成循环必须检查ctx.shape[1] max_len。七、练习题把n_layers从 2 加到 4对比 loss 收敛速度体会深度带来的容量增益把训练数据换成 “0101…” 交替序列训练后让模型从 1 开始续写观察它学到什么给MiniTransformer加温度采样第 12 篇 预告对比贪心和 temperature1.5 生成序列的差异。一句话总结词嵌入位置编码进 N 层带因果掩码的标准块出来对每个位置预测下一个词——21000 个参数复现了 GPT 的全部架构原理规模是工程问题结构你已经全部掌握。下一篇transformer你不学10HuggingFace 生态加载预训练模型。