1. 从零训练大模型到底在练什么很多人第一次听到“从零训练一个大模型”脑子里浮现的画面是几十号人围着一堆显卡忙活大半年。这个印象不算错但也不全对。真实情况是从零训练这四个字里“零”的定义决定了你后面所有工作的量级。如果你说的“零”是指连模型结构都自己设计、词表自己造、数据从互联网上生爬那确实是一个团队级别的工程但如果你说的“零”是指基于开源基座模型走完数据构建、继续预训练、SFT、偏好对齐、评估这一整条链路那一个人或者一个小团队完全可以在有限资源下跑通全流程。我自己走过一遍这条路踩过的坑比想象中多得多。这篇文章不打算给你画大饼也不打算只讲概念。我会把数据准备、预训练、SFT、DPO/RLHF、评估这五个阶段拆开每个阶段告诉你实际要做什么、为什么这么做、参数怎么定、哪里容易翻车。适合有一定Python和深度学习基础、想系统了解大模型训练全流程的人也适合已经在做微调但没走过完整链路的从业者。先说一个核心认知大模型训练的本质是分布对齐。你手里的数据决定了模型学到的分布你的训练目标决定了模型往哪个方向偏移。预训练是让模型学会“世界长什么样”SFT是让模型学会“人希望我怎么回答”DPO/RLHF是让模型学会“哪种回答更好”。三个阶段的目标不同数据格式不同训练策略也不同。很多人微调效果不好根本原因不是参数没调对而是数据分布和训练目标不匹配。下面这张表是我自己总结的五个阶段核心对比先建立一个全局观阶段目标数据量级典型算力关键指标数据准备构建高质量语料1B~10T tokensCPU为主去重率、困惑度预训练学习语言与世界知识10B~1T tokens多卡GPULoss、PPLSFT学会指令跟随10K~1M条单机多卡指令遵循率DPO/RLHF对齐人类偏好5K~100K对单机多卡胜率、KL散度评估验证能力边界基准集推理卡准确率、鲁棒性这张表不是让你照搬而是让你知道每个阶段大概在什么量级上工作。接下来我逐个拆解。2. 数据准备决定模型上限的隐形工程2.1 数据来源与采集策略数据是大模型的天花板这句话已经被说烂了但真正做的时候你会发现采集策略比数据量更重要。我见过太多人一上来就爬了几百G文本结果清洗完只剩不到10G能用。问题出在源头没有按目标能力反推数据需求。如果你要做的是通用中文大模型数据来源大概分这几类网页文本覆盖面广但噪声极大需要严格清洗书籍与论文质量高但版权和格式转换是问题代码仓库对逻辑推理和结构化输出有帮助百科与问答事实性强适合知识类任务对话数据直接用于SFT阶段预训练阶段少量混入即可我的经验是预训练阶段的数据配比大概是网页60%、书籍15%、代码15%、百科问答10%。这个比例不是固定的如果你的模型偏重代码能力代码比例可以拉到30%以上。但要注意代码数据过多会让模型在自然语言生成上变得“机械”输出风格偏向代码注释。采集的时候有一个容易被忽略的点时间戳和来源标记。每一条数据都要记录它从哪里来、什么时候抓的、原始URL是什么。这不是为了合规审计而是为了后续做数据消融实验。当你发现模型在某类任务上表现差时你可以快速定位是不是某批数据出了问题。2.2 清洗与去重的实操细节清洗这一步我踩过最大的坑是过度清洗。早期我用了一套非常激进的规则把包含特殊符号、长度异常、重复字符的文本全删了结果训练出来的模型在标点使用和长文本生成上表现很差。后来才明白噪声本身也是分布的一部分你要去掉的是有害噪声不是所有非常规文本。我的清洗流程大概是这样基础过滤去掉HTML标签、乱码、控制字符长度过滤保留50~100000字符的文本太短的没信息量太长的可能是拼接错误重复检测用MinHashLSH做近似去重阈值设在0.8左右质量打分用一个小模型对文本流畅度打分低于阈值的丢弃敏感内容过滤这个不用多说必须做去重这块我要多说一句。精确去重不够近似去重才是关键。网页上大量内容是转载和改写的精确哈希根本抓不到。我用的是MinHash加LSH先做shingling按n-gram切分再算Jaccard相似度。实测下来这一步能去掉30%以上的冗余数据对训练效率提升非常明显。# 近似去重核心逻辑示意 from datasketch import MinHash, MinHashLSH def deduplicate(texts, threshold0.8): lsh MinHashLSH(thresholdthreshold, num_perm128) minhashes {} for i, text in enumerate(texts): m MinHash(num_perm128) for shingle in get_shingles(text, k5): m.update(shingle.encode(utf8)) lsh.insert(fdoc_{i}, m) minhashes[fdoc_{i}] m # 后续查询重复项并移除 return list(set(texts))注意去重阈值不要设太高0.9以上会漏掉很多改写内容也不要设太低0.6以下会把正常的不同文本误删。0.75~0.85是我实测比较稳的区间。2.3 数据格式与Tokenization数据清洗完下一步是Tokenization。这里有一个关键决策用现成的tokenizer还是自己训一个。如果你是基于开源模型做继续预训练直接用原模型的tokenizer不要换。换tokenizer意味着embedding层要重新训练代价太大。如果你是从零开始那可以考虑用SentencePiece或BPE在你自己数据上训一个词表大小一般在32000~128000之间。我自己的选择是中文为主的数据词表设50000左右中英混合的设80000~100000。词表太小会导致长词被切碎序列变长训练变慢词表太大则embedding参数过多小数据集上容易过拟合。Tokenize完之后把所有数据打包成固定长度的序列。这里有个技巧不要按文档边界截断而是把所有token拼起来再按max_length切。这样能保证每个训练样本都是满的不会浪费算力。但要注意在文档之间插入EOS token让模型知道边界。# 数据打包示意 def pack_sequences(tokenized_docs, max_length2048): all_tokens [] for doc in tokenized_docs: all_tokens.extend(doc) all_tokens.append(EOS_TOKEN_ID) sequences [] for i in range(0, len(all_tokens), max_length): seq all_tokens[i:imax_length] if len(seq) max_length: sequences.append(seq) return sequences3. 预训练让模型学会世界知识3.1 模型结构选型与参数设定预训练阶段模型结构的选择基本决定了你后面的路好不好走。目前主流就三条路Decoder-only、Encoder-Decoder、Encoder-only。做生成任务选Decoder-only做理解任务Encoder-only够用Encoder-Decoder适合翻译和摘要这类序列到序列的任务。我假设你要做的是通用生成模型那就走Decoder-only路线。结构上直接用Transformer的变体但有几个参数需要你根据算力做取舍参数小规模1B以下中规模1B~7B大规模7B以上层数12~2424~3232~80隐藏维度768~15362048~40964096~8192注意力头数12~1616~3232~64词表大小32000~5000050000~8000080000~128000上下文长度1024~20482048~40964096~32768这张表是经验值不是铁律。关键原则是在算力允许范围内优先加深而不是加宽。深层模型在复杂推理上表现更好但训练难度也更大需要残差连接和归一化策略配合。还有一个容易忽略的点位置编码。绝对位置编码在长文本上外推能力差现在主流用RoPE旋转位置编码或者ALiBi。RoPE实现简单外推性也不错我推荐优先用RoPE。3.2 训练目标与Loss计算预训练的目标函数就是标准的自回归语言建模给定前n个token预测第n1个token。Loss用交叉熵公式很简单$$L -\frac{1}{N}\sum_{i1}^{N} \log P(x_i | x_{i})$$但实际训练的时候有几个细节决定成败第一Loss masking。如果你在数据里混入了padding token计算Loss的时候要把padding位置mask掉否则模型会学会预测padding浪费容量。第二梯度累积。小显存跑大模型必须用梯度累积。比如你想用batch size 512但单卡只能放8那就累积64步再更新一次。注意梯度累积等价于大batch但BatchNorm统计量不会同步更新不过Transformer一般用LayerNorm这个问题不大。第三学习率调度。预训练阶段用warmupcosine decay是最稳的。warmup步数一般设总步数的1%~5%峰值学习率在1e-4到3e-4之间。太大容易发散太小收敛慢。# 学习率调度示意 def get_lr(step, warmup_steps, total_steps, peak_lr): if step warmup_steps: return peak_lr * step / warmup_steps progress (step - warmup_steps) / (total_steps - warmup_steps) return peak_lr * 0.5 * (1 math.cos(math.pi * progress))3.3 分布式训练与显存优化预训练绕不开分布式。单卡能跑的模型规模有限7B以上的模型基本都要多卡。分布式策略有三种数据并行、张量并行、流水线并行。数据并行最简单每张卡放完整模型数据切分张量并行把矩阵乘法切到多卡流水线并行把不同层放到不同卡。我自己的经验是7B以下用数据并行ZeRO就够7B以上考虑张量并行流水线并行。ZeRO是DeepSpeed提出的显存优化技术把优化器状态、梯度、参数分片到多卡能大幅降低单卡显存占用。显存优化还有几个实用技巧混合精度训练用bf16而不是fp16bf16动态范围大不容易溢出梯度检查点用时间换显存适合深层模型Offload把优化器状态放到CPU内存进一步省显存但会慢提示混合精度训练时Loss scaling很关键。bf16一般不需要动态scalingfp16需要。如果你用fp16发现Loss变成NaN先检查scaling策略。3.4 预训练阶段的常见翻车点预训练阶段我遇到过几次比较严重的翻车这里列出来给你避坑Loss突然飙升大概率是学习率太大或者数据里有异常样本。先降学习率再检查数据。模型输出重复这是典型的“复读机”现象原因是训练不充分或者数据重复度过高。增加数据多样性延长训练时间。显存溢出先降batch size再开梯度检查点最后考虑Offload。不要一上来就改模型结构。训练速度慢检查数据加载是不是瓶颈。用prefetch和num_workers加速把数据预处理放到GPU上做。4. SFT让模型学会听人话4.1 指令数据的构建与格式预训练完的模型你问它“今天天气怎么样”它可能会续写“今天天气怎么样明天天气怎么样”。因为它只学会了预测下一个词没学会“回答问题”。SFT就是教它这个。SFT数据的格式一般是指令-输入-输出三元组。比如{ instruction: 把下面的句子翻译成英文, input: 今天天气很好, output: The weather is nice today. }但实际构建的时候格式可以更灵活。我常用的是对话格式把多轮对话拼成一个序列用特殊token分隔角色|user|今天天气怎么样|assistant|今天天气很好适合出门。|end|这种格式的好处是模型能学会多轮交互而且推理的时候直接按同样格式拼接就行。数据量方面SFT不需要太多数据但质量要求极高。我自己的经验是10000~50000条高质量指令数据就能让模型在常见任务上表现不错。关键是覆盖度任务类型要全回答风格要一致不能有错误示范。4.2 训练策略与超参选择SFT的训练策略和预训练有本质区别。预训练是从头学SFT是在已有知识上做微调。所以学习率要小预训练用1e-4SFT用1e-5到5e-5训练轮数要少1~3个epoch足够多了容易过拟合Loss只算输出部分指令部分的Loss要mask掉只计算模型生成的回答部分的Loss# SFT Loss masking示意 def compute_sft_loss(logits, labels, response_mask): shift_logits logits[..., :-1, :].contiguous() shift_labels labels[..., 1:].contiguous() shift_mask response_mask[..., 1:].contiguous() loss_fct CrossEntropyLoss(reductionnone) loss loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1)) loss loss * shift_mask.view(-1) return loss.sum() / shift_mask.sum()还有一个关键点数据配比。SFT数据里不同任务类型的比例要控制好。如果翻译数据占80%模型就会偏向翻译其他任务表现下降。我一般让通用对话占50%专项任务各占10%~20%。4.3 SFT阶段的实操心得SFT阶段有几个坑我踩过之后才明白第一不要用预训练的Loss曲线判断SFT效果。SFT的Loss降到1以下很正常但不代表模型真的好。要看生成质量不是看Loss。第二数据质量比数量重要十倍。我试过用10万条低质量数据效果不如1万条精标数据。低质量数据里的错误模式会被模型学去而且很难纠正。第三SFT之后模型会变“窄”。预训练模型知识面广SFT之后在特定任务上强了但通用能力可能下降。这是灾难性遗忘。缓解方法是混入少量预训练数据或者用LoRA做微调只更新部分参数。注意SFT阶段如果发现模型输出变得非常模板化大概率是数据里重复模式太多。检查你的数据增加多样性。5. DPO与RLHF对齐人类偏好5.1 RLHF的三步走流程RLHF基于人类反馈的强化学习是让模型输出更符合人类偏好的关键步骤。经典RLHF分三步SFT先让模型学会基本指令跟随奖励模型训练收集人类对模型输出的偏好排序训练一个奖励模型PPO强化学习用奖励模型指导SFT模型优化这个流程很有效但工程复杂度极高。PPO训练不稳定超参敏感奖励模型容易过拟合。我自己的体验是PPO调参的时间比训练模型本身还长。奖励模型的训练数据是偏好对给定同一个prompt人类标注哪个回答更好。奖励模型的目标是学会给更好的回答打高分。Loss用pairwise ranking loss$$L -\log(\sigma(r(x, y_w) - r(x, y_l)))$$其中$y_w$是更好的回答$y_l$是更差的回答。5.2 DPO更简单的替代方案DPO直接偏好优化是最近两年非常火的方法它跳过了奖励模型直接用偏好数据优化语言模型。数学上DPO证明了最优策略和奖励函数之间存在解析关系所以不需要显式训练奖励模型。DPO的Loss长这样$$L_{DPO} -\log\sigma(\beta \log\frac{\pi_\theta(y_w|x)}{\pi_{ref}(y_w|x)} - \beta \log\frac{\pi_\theta(y_l|x)}{\pi_{ref}(y_l|x)})$$其中$\pi_\theta$是当前模型$\pi_{ref}$是参考模型通常是SFT后的模型$\beta$控制偏离参考模型的程度。DPO的优势很明显训练稳定、实现简单、不需要奖励模型。我实测下来DPO在大多数场景下效果和RLHF相当但训练时间少一半以上。# DPO Loss核心实现 def dpo_loss(policy_logps, ref_logps, beta0.1): chosen_logps, rejected_logps policy_logps chosen_ref, rejected_ref ref_logps pi_logratios chosen_logps - rejected_logps ref_logratios chosen_ref - rejected_ref logits pi_logratios - ref_logratios loss -F.logsigmoid(beta * logits) return loss.mean()5.3 DPO/RLHF的数据准备与训练细节偏好数据的质量直接决定对齐效果。我构建偏好数据的时候遵循几个原则同一个prompt下的两个回答要有明显质量差异不能模棱两可偏好标注要一致不同标注者的标准要统一数据要覆盖多种任务类型不能只集中在某一类数据量方面5000~20000对偏好数据就能有不错的效果。关键是质量不是数量。训练的时候$\beta$参数很关键。$\beta$太小模型偏离参考模型太多可能输出退化$\beta$太大模型学不到新东西。我一般从0.1开始试根据KL散度调整。提示DPO训练时监控KL散度如果KL超过10说明模型偏离参考模型太远需要降低学习率或增大$\beta$。6. 评估怎么知道模型到底行不行6.1 自动评估与基准测试模型训完了怎么判断好坏自动评估是最直接的方式。常用的基准有基准考察能力适用阶段MMLU多任务知识预训练后HellaSwag常识推理预训练后GSM8K数学推理SFT后HumanEval代码生成SFT后MT-Bench多轮对话DPO后AlpacaEval指令跟随DPO后这些基准跑起来不难但不要只看分数。我见过模型在MMLU上分数很高但实际对话一塌糊涂。原因是基准测试和真实使用场景有分布差异。自动评估还有一个坑数据污染。如果你的训练数据里包含了基准测试的题目分数会虚高。做评估之前一定要用n-gram匹配检查训练数据里有没有基准题目。6.2 人工评估与A/B测试自动评估不够人工评估来凑。人工评估一般做** pairwise比较**给评估者同一个prompt下的两个模型输出让他们选哪个更好。这种方式比打分更可靠因为人类对绝对分数不敏感但对相对好坏很敏感。我做人工评估的时候会控制几个变量评估者要多样化不能全是团队成员prompt要覆盖真实场景不能全是测试集里的盲测评估者不知道哪个是哪个模型A/B测试是上线前的最后一道关。把新模型和旧模型同时部署让真实用户用看留存、满意度、任务完成率。这一步能发现很多离线评估发现不了的问题。6.3 评估阶段的避坑指南评估阶段有几个坑我踩过之后总结如下第一不要用训练Loss判断模型好坏。Loss低不代表生成质量高这是两回事。第二不要只用一个基准。不同基准考察不同能力要组合使用。第三不要忽略推理速度。模型再好推理慢也没法用。评估的时候要测TTFT首token延迟和TPOT每token延迟。第四不要忘了安全评估。模型会不会输出有害内容会不会被诱导这些都要测。7. 全流程串讲与资源规划7.1 从零到一的完整时间线把五个阶段串起来一个完整的训练流程大概是这样数据准备2~4周取决于数据源和清洗复杂度预训练1~4周取决于模型规模和算力SFT3~7天数据量小训练快DPO/RLHF3~7天和SFT差不多评估1~2周包括自动评估和人工评估总计大概2~3个月这是小团队跑通全流程的时间。如果算力充足可以压缩到1个月以内。7.2 算力与成本估算算力是硬约束。我按7B模型估算一下预训练需要约100~500 GPU天A100级别取决于数据量SFT需要约10~50 GPU天DPO需要约10~30 GPU天评估需要约5~10 GPU天如果用云算力按A100每小时2~3美元算总成本大概在2万~10万美元之间。如果用自己的卡成本主要是电费和折旧。提示小团队可以从1B~3B模型开始练手算力需求降一个数量级流程完全一样。7.3 给小团队和个人的建议如果你是一个人或者小团队我建议不要一上来就搞7B从1B开始跑通流程最重要数据质量优先宁可少而精不要多而杂SFT和DPO是性价比最高的环节预训练可以基于开源模型做继续预训练评估要贯穿始终不要等训完再评估记录每一次实验参数、数据、结果都要记不然回头根本不知道哪个配置有效我自己走过一遍之后最大的体会是大模型训练不是算法问题是工程问题。算法论文里那些公式看起来很优雅但实际跑起来80%的时间花在数据处理、显存优化、训练稳定性上。把工程细节做好比追新算法重要得多。最后分享一个小技巧训练之前先跑一个tiny版本。用1%的数据、1%的模型规模把整个流程跑一遍确认没有bug再上全量。这一步能帮你省下大量时间和算力。