ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Transformer零基础精讲:原理、手写实现与训练全攻略

Transformer零基础精讲:原理、手写实现与训练全攻略 这篇东西我早就该写了。做了这么多年的AI大模型开发几乎每天都要和Transformer打交道——你要去读论文也好要用大模型做应用也好甚至是本地部署一个开源模型底层全是这个结构。很多人一听到“Transformer给零基础讲明白”就觉得不现实其实它真没有你想的那么玄乎。你把它拆成几个零件一个一个看明白就会发现它本质就是一套“让机器学会关注重点”的加工流水线。这篇文章我不拽术语尽量用大白话把它的原理、结构、手写实现、训练方式都铺开讲一遍适合刚入门、想转行、或者只是想搞懂“大模型到底是怎么干活”的人。1. 为什么大模型开发绕不开 Transformer1.1 Transformer 到底是什么简单说Transformer 是一种用来处理序列数据的神经网络结构。这里的“序列”可不是只有文本一段语音、一帧帧的视频、一长串传感器数值都可以看作序列。它的出现最早是为了解决机器翻译问题比如把英文句子翻译成中文。但是它的厉害之处在于它能够通过一种叫自注意力机制的东西让序列里的每一个元素都去观察序列里的其他元素并判断“谁和我最有关系”。这种机制天然适合处理上下文信息非常重的任务所以后来几乎横扫了自然语言处理、语音识别、计算机视觉等多个领域。你如果接触过 AI 大模型相关的开发应该已经听过 GPT 系列、BERT或者像 LLaMA 这样的开源模型。这些模型的底层统一都是 Transformer 的某种变体。换句话说学大模型开发如果不把 Transformer 摸透后面做微调、做推理优化、做 Agent 应用都会觉得缺了一块最重要的根基。1.2 为什么它能在众多结构里胜出在 Transformer 以前大家处理文本序列主流方案是 RNN 和 LSTM。这两种结构像是“流水线上一个一个挨着看零件”你必须等前一个零件处理完才能处理下一个速度慢而且句子一长前面的信息早就“忘”得差不多了。Transformer 的做法完全不同它相当于把所有零件一次性平铺在大桌上让每个零件都能同时看到桌上所有其他零件再用一个叫“注意力分数”的指标告诉你在当前这一拍里你最该关注的是谁。这个设计带来的好处非常直接第一训练速度快因为可以并行计算不再需要一个词一个词往后推第二长距离依赖能力强哪怕两个词隔了很远的距离模型依然能直接建模它们的关系。今天大模型动辄几百亿甚至上千亿参数能在大规模数据上高效训练靠的就是 Transformer 并行和长程建模这两个核心优势。1.3 从一个“智能翻译现场”理解核心流程为了让你更有体感我用一个翻译场景来演示。假设机器要翻译“I love you”它先把每个单词切成标记然后把这些标记变成数字向量。接下来Transformer 会分别对 I、love、you 这三个词做自注意力计算其中“love”这个词在计算时会去和 I、you 比较相关程度最终会发现 I 和 love 关系强、love 和 you 关系强于是模型就学会了“I 是动作的发出者you 是动作的承受者”这种隐含关系。整个过程里“注意力”就是模型分配给各个词的一组权重。这套机制里面没有任何人告诉模型语法规则它就是靠海量文本里的统计规律自己摸索出“哪个词和哪个词经常会一起出现、谁在修饰谁、谁在承接谁”。这就是为什么大模型能做翻译、写文章、写代码因为它本质上是在操作一套非常庞大的、从数据里学出来的上下文关联网络。你可以把练习资料、开源代码、部署工具全找齐但如果不亲自把 Transformer 跑一遍你终究会觉得自己的知识是虚的。2. Transformer 核心组件拆解2.1 输入要先变“数字”Tokenization 与 EmbeddingTransformer 不能直接读原始文字它只认数字。所以第一步是做分词和嵌入。分词就是把一句话拆成模型词典里有的小片段这个小片段叫 Token。比如“我爱写代码”可能会被切成“我”“爱”“写”“代码”也可能会用更细的 BPE 算法切出子词。关键在于模型里会有一个词表每个 Token 对应一个唯一 ID。拿到 ID 之后模型会做一步叫 Embedding 的映射把 ID 变成一个一维向量比如 512 维、768 维或者 4096 维。这个向量不是随机的而是模型在训练中慢慢学出来的。刚开始“猫”和“狗”对应的向量可能差别很大但训练久了之后它们在向量空间中会自然聚到“宠物”这个语义区域附近。你可以简单理解成词嵌入是把人类的词语搬进了一个高维坐标系意思相近的词坐标也相近。这个坐标系就是模型理解和推理的“内部语言”。实际操作中Embedding 层通常就是一个查询表输入 Token ID输出对应的一排浮点数。很多大模型的参数量之所以大很大一部分原因就是这个词表查表层非常大。比如一个词表有 5 万个 Token每个 Token 映射成 4096 维向量那光这一个层就有 5 万乘 4096约 2 亿个参数。所以你别小看这一步它既是模型理解语言的入口也是显存占用的大头。2.2 位置编码让模型知道谁先谁后自注意力机制是并行计算的它没有天然的顺序概念。你把“我爱你”和“你爱我”里的词打乱顺序模型看到的依然是同样的词集合。但人类的语言顺序往往决定语义。为了解决这个问题Transformer 在输入里加入了位置编码。最经典的方案是使用正弦和余弦函数生成一组固定的位置向量。比如第 1 个位置加一组数第 2 个位置加另一组数这些数之间有明确的周期规律模型一眼就能区分出“哦这个词离我远还是近”。后来又出现了很多变体比如可学习位置编码干脆把位置向量也当成一组参数一起训练再比如旋转位置编码它通过旋转坐标的方式把相对位置信息编码进注意力计算里。你如果去看 GPT 系列或者 LLaMA 的实现会发现现代模型几乎都在用旋转位置编码。这里给你一个理解上的抓手位置编码不是要让模型记住“第 5 个位置长什么样”而是要让模型能够感知到“相对距离”。比如“张三打了李四”和“李四打了张三”两者的词完全一样但位置信息不同模型就是靠位置编码来区分谁是主语、谁是宾语。没有这一步Transformer 就真的只是一堆词的乱炖。固定位置编码优点是稳定、无需训练适合初期方案。可学习位置编码更灵活但需要更多训练数据来稳定。旋转位置编码现代大模型主流能天然支持任意长度的相对距离感知。2.3 自注意力机制每个词都要查“上下文词典”自注意力是 Transformer 的心脏。它的思路可以用一句话概括每一个词都要根据上下文里所有词的信息重新梳理自己的表示。具体怎么做的呢假设你有一个句子每个词已经有一串向量。模型给每个词准备了三份“副本”一个叫 Query查询一个叫 Key键一个叫 Value值。你可以把 Query 理解成“我现在想问的问题”Key 是“我身上贴的标签”Value 是“我能提供的内容”。每个词拿着自己的 Query去和所有词的 Key 做匹配得到一个分数表示“我有多需要你”。这些分数经过 Softmax 归一化之后变成一组权重最后用这些权重去加权求和所有词的 Value得到这个词的新表示。这样一说你可能觉得有点抽象我拿现实类比一下你在图书馆里想研究“AI 大模型如何部署”你就是一个 Query书架上的书有各自的分类标签那是 Key每本书的实际内容就是 Value。你会先根据标签快速筛选相关的书再重点翻阅那些和你问题最匹配的书最后提出、整合出一份属于你自己的综述。注意力机制就是让每个词都自动完成一轮这样的“文献综述”。2.4 多头注意力团队协作各看各的刚才说的自注意力如果把所有逻辑都放在一个空间里做很容易出现“每个词都只关注自己”或者“关注得太泛”的问题。因此 Transformer 引入了多头注意力。所谓多头就是把整个向量的维度切成多份每一份做一次独立的注意力计算最后再把多份结果拼起来。比如一个有 12 个头的大模型可能其中几个头负责学习语法关系几个头负责学习指代关系还有几个头负责捕捉“谁和谁经常出现在同一语境”这种语义关系。它们并行工作互不干扰最后把各自学到的信息合在一起形成一个更丰富、更立体的表示。你可以把它想象成一个公司里的多个项目小组有的小组研究市场有的小组研究技术有的小组研究成本最后大家把报告汇总给 CEO 做决策。在代码里做多头注意力重点在于维度的切分和变换。一般把d_model分成num_heads份每份维度是d_k d_model / num_heads。运算时要把张量形状从(batch_size, seq_len, d_model)变成(batch_size, num_heads, seq_len, d_k)再做注意力计算最后再拼接回来。这个 reshape 的过程看着不起眼但很多人都在这上面出过错尤其是最后拼接时忘记用contiguous()很容易导致运行时报错。2.5 前馈网络、层归一化与残差连接自注意力负责的是“词与词之间的交流”但光有交流还不够模型还需要对每个词的信息进行更深层的加工。因此在每个 Transformer 块里自注意力之后会接一个前馈网络。它其实就是两个全连接层中间夹一个激活函数一般是 ReLU 或者 GELU。这一步相当于“每个词单独做一次非线性变换”把注意力提取到的信息再消化一遍、提炼一遍。同时Transformer 结构里还非常依赖两样“稳定器”残差连接和层归一化。残差连接的意思很简单就是让输出等于“当前模块的结果 原始输入”。这样做有一个直接的好处梯度可以从最后一层直接传回第一层避免深层网络训练时梯度消失。层归一化则是对每个样本的所有特征做标准化把数据拉回均值为 0、方差为 1 的分布这样可以让训练过程更稳定不容易出现数值爆炸。你如果去读常见开源代码会发现大多数模型用的是“先归一化再子层”的排列叫 Pre-LN而不是经典论文里的 Post-LN。这里补充一个我实际测试过的结论Pre-LN 在训练初期更稳定允许你用更大的学习率但可能略微损失一些最终效果Post-LN 训练时更容易崩不过如果调好了最终收敛结果通常更优。现代大模型几乎清一色选 Pre-LN毕竟稳定压倒一切。2.6 编码器与解码器的差别经典 Transformer 论文里结构分成了编码器和解码器两半。编码器负责把输入文本“读”成一堆上下文向量解码器负责根据这些向量“写”出输出文本。两个结构非常相似都包含多头自注意力、前馈网络和归一化但有一个关键区别解码器里多了一层“交叉注意力”用来关注编码器输出的信息同时解码器的自注意力会被一个掩码限制只能看到当前位置之前的内容不能“偷看”未来的词。这个区别直接演化出了当今两个重要分支。BERT 用的是纯编码器架构擅长理解类任务比如分类、命名实体识别因为它能同时看到句子前后的信息。GPT 用的是纯解码器架构擅长生成类任务因为它训练时就是“根据前文预测后文”。后来大家发现只要缩放够大的数据和参数纯解码器在“理解”上也不差于是现代大模型基本都一水儿用解码器。但这不代表你不需要了解编码器很多落地场景里像向量检索、语义匹配、非结构化信息抽取用 BERT 这类编码器模型反而更轻便更高效。3. 手写一个迷你 Transformer3.1 先搭一个最简单可运行的代码骨架能把原理讲清楚还差最后一步亲手写一遍。我以下给出一版极简 Transformer 块代码。它不追求最佳性能只用来帮助你建立直觉。import torch import torch.nn as nn import math class SelfAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() self.num_heads num_heads self.d_model d_model self.d_k d_model // num_heads 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, maskNone): batch, seq_len, _ x.size() Q self.W_q(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) K self.W_k(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) V self.W_v(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) 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) out torch.matmul(attn, V) out out.transpose(1, 2).contiguous().view(batch, seq_len, self.d_model) return self.W_o(out) class TransformerBlock(nn.Module): def __init__(self, d_model, num_heads, d_ff, dropout0.1): super().__init__() self.attn SelfAttention(d_model, num_heads) self.norm1 nn.LayerNorm(d_model) self.norm2 nn.LayerNorm(d_model) self.ffn nn.Sequential( nn.Linear(d_model, d_ff), nn.ReLU(), nn.Linear(d_ff, d_model) ) self.dropout nn.Dropout(dropout) def forward(self, x, maskNone): x x self.dropout(self.attn(self.norm1(x), mask)) x x self.dropout(self.ffn(self.norm2(x))) return x这段代码里SelfAttention 类的forward流程就是我在前面讲过的 Q、K、V 三步先做线性映射再切成多头然后计算注意力分数最后加权求和并输出。TransformerBlock 里用的是 Pre-LN也就是先归一化再进入注意力或前馈模块最后通过残差相加回来。整个结构干净简单我建议你把它复制下来再结合一个具体任务去改。3.2 关键参数怎么定从 d_model 到 num_heads写代码之前你一定会遇到参数定义的问题。我以自己的使用经验给你一个参考d_model向量维度。小实验用 64 或 128普通任务用 768大模型动辄 4096 甚至以上。不是越大越好它要和数据量、算力匹配。num_heads注意力头数。常见有 8、12、16。需要保证d_model % num_heads 0否则没法均分。d_ff前馈网络中间层维度。经典论文里通常是d_model * 4。比如d_model512时d_ff2048。num_layersTransformer 块堆叠层数。小模型 2 到 4 层大模型几十层甚至上百层。dropout丢弃率。一般在 0.1 附近。数据量很小时可以调低防止模型过度随机丢弃信息。这几个参数之间其实是联动的。比如你把d_model调大那么注意力头和前馈维度通常也要跟着调大训练所需的数据量和显存也会同步上涨。很多初学者喜欢盲目叠参数结果模型在普通数据集上严重过拟合反而效果不如小模型。3.3 用迷你 Transformer 做一个“字符级文本生成”实验代码骨架有了怎么验证它真能工作我建议你做一个非常经典的小实验给模型一段文本让它预测下一个字符。数据可以直接用你手头的一份英文小说或者中文语料预处理到字符级别就行。我把训练主循环的简化版写在这里。import torch.optim as optim def train(model, data, seq_len64, epochs5, lr1e-3): optimizer optim.AdamW(model.parameters(), lrlr) loss_fn nn.CrossEntropyLoss() model.train() for epoch in range(epochs): total_loss 0 for i in range(0, len(data) - seq_len - 1, seq_len): src torch.tensor(data[i:iseq_len]).unsqueeze(0) tgt torch.tensor(data[i1:iseq_len1]).unsqueeze(0) logits model(src) # (batch, seq_len, vocab_size) loss loss_fn(logits.view(-1, logits.size(-1)), tgt.view(-1)) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() print(fepoch {epoch 1}, loss {total_loss:.4f})这个实验里模型每一刻都在做同样一件事看着前 64 个字符预测第 65 个字符。训练结束后你可以输入一个种子文本比如“The meaning of”然后反复采样下一个字符拼回输入里模型就能生成一段看起来有模有样的英语句子。这个过程非常能建立直觉大模型能长篇大论写文章本质上就是把这个“预测下一个字符”的任务放大到海量数据、超大参数、超长上下文上并没有神奇的秘密。我在实际做这个实验时遇到过一个小问题训练 loss 下降得很慢。后来发现是学习率设得太小同时没有做学习率 warmup。Transformer 对学习率其实非常敏感初期梯度方差大突然给一个大的学习率很容易让 loss 震荡甚至爆炸。规范做法是先让学习率在线性上升几百步再按余弦或倒数方式逐步衰减。这个小技巧等你真的去训练更大模型时会更深有体会。4. 训练与推理模型是怎么“学”起来的4.1 预训练与微调大模型开发的常见分工Transformer 本身只是一个结构要让结构具备能力必须经过训练。大模型开发里的训练一般分成两个阶段预训练和微调。预训练阶段在海量无标注文本上做自监督学习核心目标是让模型学会语言的通识规律。比如 GPT 系列会随机“盖住”一段文本的后半部分让模型根据前文预测后文。这个阶段极其消耗算力但产出的模型已经具备很强的语言生成和知识记忆能力。微调阶段则是在预训练模型基础上用带标注的少量数据把模型往特定任务上引导。比如你想做一个法律问答机器人那就准备一批法律问题和标准回答让模型根据用户问题生成答案。微调的显存和算力需求远小于预训练很多个人开发者都能在单卡或者几张小卡上完成。实际开发中如果你觉得自己从头训练一个大模型不现实那很正常行业里的共识就是“别人负责预训练你负责微调和应用”。这里我想多说一句关于“全栈”理解的重要性。现在有一个很流行的词叫 AI 大模型全栈知识库里面会涵盖从数据清洗、词表构建、预训练、指令微调、强化学习、推理加速到部署上线的全部内容。你没必要每个环节都亲自动手做一遍但一定要知道它们各自解决什么问题。否则你很容易陷入“只知道调库、不知道原理”的尴尬局面遇到问题也不知道该往哪个方向排查。4.2 输入输出与损失函数模型到底在优化什么Transformer 的输入输出形式非常统一。输入通常是一个 Token ID 序列输出是对应每个位置的“下一个 Token”的概率分布。你可以把模型的输出看成一个巨大的 Excel 表表头是词表里所有 Token每一行是对应输入位置的预测概率。训练时会有一个正确答案也就是真实的 Token ID然后用交叉熵损失函数计算预测分布和正确答案之间的差距反向传播去更新所有参数。这个统一的建模方式非常优雅。你训练“翻译”、训练“写诗”、训练“写代码”本质上全部都是“给定前文预测后文”。区别只在于数据格式。比如做问答你把用户问题和标准答案拼成一段文本模型只需要学会从问题部分跳到答案部分。所以很多人说大模型是“文本生成模型”其实逻辑上没错。它并不真的理解世界它只是在海量数据中学会了“在给定语境下最可能出现的下一个片段是什么”。4.3 推理时的温度与采样为什么同一个模型回答每次不同模型训练完成后进入推理阶段。此时模型已经不再更新参数而是根据输入逐步生成 Token。在每个生成步骤模型会给出所有候选 Token 的概率但怎么选决定了最终结果风格。这里最常调节的参数就是温度。温度大于 1 会让概率分布更平缓输出更随机、更多样温度小于 1 会让概率更集中在最高分的 Token 附近输出更确定、更保守。如果你在实际开发大模型应用时发现用户觉得回答“太平淡”或者“太跳脱”第一件该检查的事就是温度参数。代码里往往是这样用的先把模型输出的 logits 除以温度再做 Softmax最后按概率采样。比如temperature0.8通常能兼顾稳定性和创造力temperature1.5适合头脑风暴但容易出现事实错误。4.4 本地部署配置参考一个值得收藏的经验表现在很多人想做 AI 大模型本地部署其实说到底就是把自己微调好或者下载来的模型权重跑在本地 GPU 上提供服务。我整理了一个常见配置参考方便你有初步印象。模型规模参数量建议显存可运行场景小模型1B 以下4GB 到 8GB文本分类、简单生成中型模型7B 到 13B16GB 到 24GB通用对话、代码辅助大模型30B 到 70B40GB 到 80GB 或量化高质量生成、复杂推理超大模型100B 以上多卡并行研究、云服务这里的显存估算主要基于权重精度为 FP16。如果显存不够可以用 4bit 或 8bit 量化显存需求会明显下降但模型质量会有轻微损失。实际部署时我建议先看两点第一你手头 GPU 的显存上限第二模型是否支持加载时做设备映射。很多现代推理框架已经支持自动切分你不需要自己去手工写分布式逻辑但要大概知道这个流程是怎么工作的。5. Transformer 家族与大模型学习路线5.1 BERT 与 GPT两条主路线的区别Transformer 诞生之后整个领域迅速分成了两大门派。一派以 BERT 为代表使用编码器结构在文本里随机遮住一些词让模型通过上下文去猜被遮住的词学到的能力偏理解。另一派以 GPT 为代表使用解码器结构让模型从左往右预测下一个词学到的能力偏生成。理解这两条路线对你选择技术方案非常重要。如果业务需求是“抽取出这段合同里的关键日期、金额、当事人”BERT 类模型往往更轻、更快、更准。如果业务需求是“给我写一封周报邮件、做一份会议总结”那就该考虑 GPT 类模型。在实际项目里很多人一上来就想用大模型做所有事但其实能用小型编码器模型解决的简单任务完全没必要消耗昂贵的生成式算力。5.2 从文本到图像Vision Transformer 与 Swin TransformerTransformer 能处理文本自然也能处理图像。Vision Transformer简称 ViT的做法是把一张图片切成固定大小的 patch比如 16×16 像素一个小块然后把这些小块当作“图像里的单词”输入标准的 Transformer 结构。这听起来很直接但早期的 ViT 需要大量数据才能训好所以在小数据集上经常打不过卷积神经网络。后来出现的 Swin Transformer 在 ViT 基础上引入了分层结构和移动窗口注意力。它不像 ViT 那样一开始就在全局做注意力而是在不同层级的窗口内部做注意力然后通过窗口移动来建立跨窗口的联系。这样既降低了计算量又能逐步扩大感受野。所以 Swin Transformer 很快成为视觉领域里非常有代表性的 Transformer 结构广泛应用于图像分类、目标检测和语义分割。你在看一些较新的多模态大模型时也经常会看到它们用类似思路把图像变成视觉 Token再和文本一起送入 Transformer。5.3 零基础完整学习路线与资料推荐如果你真的是零基础直接啃论文会非常痛苦。我的建议是走“现象驱动”的路线先玩起来再拆开看。第一步先找一个在线大模型聊天产品去用体会生成式 AI 的能力边界第二步安装一个开源模型到本地跑几个脚本搞清楚模型的输入输出格式第三步把注意力机制和 Transformer 结构用网上现成的可视化交互工具过一遍第四步再回头去读经典论文原文你就会有“原来这段讲的是这么回事”的感觉。资料方面经典的《Attention Is All You Need》是必读论文但建议在你已经理解结构之后再读。代码层面建议找一个没有过度封装的 PyTorch 实现逐行调试跑通。学习资料非常多但真正稀缺的是你自己动手跑通一个完整项目。比如把本文里的迷你 Transformer 扩展成一个能写点短文的模型这一步带来的成长远超你看几十篇解析文章。AI 大模型学习路线不需要一开始铺得太大把 Transformer 吃透后面的路会越走越顺。6. 常见问题与排查技巧实录6.1 训练不收敛或 loss 一直震荡遇到这种情况我建议从上往下依次排查。第一检查数据有没有做归一化特殊字符、空行、异常 Token 都可能让模型困惑第二检查学习率是否过大建议给 Transformer 做 warmup比如前 500 步线性升到目标学习率第三检查损失函数是否和模型输出对齐比如输出的 logits 最后几维和标签索引是否一一对应第四检查数值稳定性LayerNorm 之后一般不会出太大问题但如果你手动实现注意力时没有做缩放很容易出现 logits 过大导致 Softmax 饱和。6.2 显存不足显存不够是大模型开发里最常见的瓶颈。第一招是减小 batch size但这可能影响稳定性需要配合调整学习率第二招是打开梯度累积相当于攒几个小 batch 再更新一次参数第三招是启用混合精度训练把部分张量用 FP16 存储和计算显存能省将近一半第四招是检查输入序列长度Transformer 的时间和显存占用是随序列长度平方增长的一个长序列往往能顶几十个短序列先把最大长度限制住通常能解决很多问题。6.3 生成结果重复啰嗦这是解码器模型常见的高级现象。如果你在解码时不加限制模型经常会进入“重复循环”。解法很多对重复 n-gram 做惩罚调整采样策略比如使用 top-k 或 top-p 采样也可以直接把重复惩罚系数设高一点。还有一个容易忽略的点训练阶段如果数据里大量存在模板化内容生成时就特别容易出现套话。这个只能靠数据清洗和后期过滤来解决。6.4 几个容易踩的坑千万别把注意力掩码用错。训练解码器时必须用因果掩码否则模型会偷看未来词训练指标很好看生成时一塌糊涂。多头注意力拼接后一定要contiguous()否则某些 PyTorch 算子会报错或者产生隐性性能问题。位置编码要和 Embedding 相加而不是拼接维度会完全对不上。搭建模型时先跑一个极小样本的过拟合实验比如 batch 设为 1、训练步数覆盖几百步如果 loss 能降到接近零说明结构和前向传播没问题再上全量数据。此外还有一点虽然现在的深度学习框架封装程度很高你很难在训练时真正碰到梯度爆炸导致数值异常但在自己手写模型时建议经常打印每一层的输出分布特别是注意力的权重分布。如果发现所有注意力权重都很接近均匀分布说明模型没有学到真正的关注点可能是初始化不当、学习率过低或数据量太少。这种时候先不要盲目加深模型先把一个小规模实验训好再扩大规模。最后再分享一个小技巧。我在自己写 Transformer 代码的时候一定会先画一张输入张量的形状变化图每一步把 batch size、序列长度、特征维度、注意力头数都记下来。很多莫名其妙的问题都是维度在某个 reshape 或 transpose 时不小心搞错了。把这个习惯保持住等你以后去看大模型源码比如 LLaMA、Mistral 这些开源实现也会轻松很多。其实 Transformer 本身并不难难的是在庞大的工程链条里保持清晰的结构观。你只要把每个零件的职责边界搞清楚再复杂的大模型也不过是层层叠叠的重复堆叠罢了。
返回列表