从零训练7B大模型:数据、预训练、对齐与部署全流程实战
2026/9/24 20:54:28 网站建设 项目流程

把一个 7B 模型从零造出来,我和一个小团队前后折腾了将近四个月。回头看,这件事真正难的不是数学和代码,而是每个环节都要在信息不完整的情况下做决策——数据投多少、词表定多大、学习率给多少、loss 不掉的时候要不要慌。这篇文章把我踩过的坑、算过的账,以及最后真正跑通的完整路径写成一份可以参考的流程,从数据处理、分词器、预训练、对齐、评测一路讲到部署。适合想从空 token 开始训一个 7B 模型、又不想只看论文空转的朋友。如果你只是打算微调现有开源模型,这篇文章也能帮你理解底座模型的训练逻辑。

1. 动手之前,先把 7B 这笔账算明白

1.1 为什么选 7B 这个规模

很多朋友上来就问“为什么不直接上 13B 或者 70B”。我的回答很简单:7B 是当前单位算力下性价比最合适的规模。它能在单张 80G 或者两张消费级显卡上做推理,量化之后一张 24G 卡就能跑,部署成本极低;同时参数规模又足够撑起复杂指令理解和多轮对话。更关键的是,开源社区围绕 7B 生态最活跃,不管是评测基准、量化工具还是推理框架,都会优先兼容这个尺寸。

从项目掌控角度,7B 的试错成本也最友好。你训练一个 7B 用的时间,是训练 13B 的一半左右,但能验证的数据工程方法、并行策略、对齐流程,几乎可以原样复用到更大的模型上。说白了,选 7B 不是妥协,是在“跑通全流程”和“控制成本”之间取的最优平衡点。

1.2 token 预算和算力怎么算

先讲一个经常会被人忽略的硬约束:训练一个模型需要多少算力,基本由参数量和训练 token 数决定。业界常用的粗略公式是总计算量 C ≈ 6 × N × D,其中 N 是参数量,D 是训练 token 数。

7B 模型如果用 5000 亿 token(500B)做预训练,总计算量大概是:

C ≈ 6 × 7e9 × 5e11 = 2.1e22 FLOPs

假设你手上是 32 张 NVIDIA H100,BF16 单卡算力约 989 TFLOPS,实际训练利用率按 40% 算,一天的可用算力就是:

32 × 989e12 × 0.4 × 86400 ≈ 1.09e21 FLOPs/天

2.1e22 除以 1.09e21,大约 19 天。考虑到 checkpoint 写入、日志、故障恢复,实际跑完大概需要 25 到 30 天。我把不同硬件规模下的预估时间整理成了一个参考表格:

硬件配置单卡算力预估有效利用率500B token 预估耗时
8 × A100 80G312 TFLOPS BF1635%约 90 天
32 × A100 80G312 TFLOPS BF1635%约 22 天
64 × A100 80G312 TFLOPS BF1638%约 10 天
32 × H100 80G989 TFLOPS BF1640%约 19 天
64 × H100 80G989 TFLOPS BF1642%约 9 天

这个表告诉我们两件事:第一,如果你只有一两台 8 卡机器,硬跑 7B 预训练时间会非常漫长;第二,token 预算直接决定成本,减少到 300B token 可以省将近一半算力,代价是模型可能欠拟合。我的建议是至少准备 64 张 A100/H100 级别的卡,再谈从零训练 7B,否则不如先用小模型验证方案。

1.3 预算里一定要给数据留出位置

算力账算完之后,大多数人会忽略另一笔账:时间账。我们项目里真正耗时最长的地方不是训练,而是数据处理和排查。从收集原始语料、写清洗脚本、跑去重、做质量过滤,到一遍遍看数据样本、调整配比,前后差不多占掉了整个项目 60% 的时间。算力预算最好也按这个比例来做,不要觉得“先拿公开数据集顶一下就行”——公开数据集的脏数据比例往往比你想象的高,后面训练出了诡异问题,你大概率会回来翻数据的账。

2. 数据工程:模型的上限在这里决定

2.1 语料来源和配比思路

大语言模型的预训练数据,核心就三类:通用文本、代码、专业内容。通用文本提供语言能力和世界知识,代码提供逻辑推理和结构化表达能力,专业内容(书籍、论文)提供深度知识。我们最后采用的配比大概是:英语网页类 45%、中文网页类 20%、代码 20%、书籍和学术文本 10%、多语言文本 5%。

语料来源方面,网上有大量开源数据集可用,比如常见的 C4、RedPajama,中文场景还有社区整理好的各类清洗语料。除了使用开源数据集,我们还补充了一部分合规渠道获取的网页数据。这里要明确一点:配比不是抄来的,是试出来的。刚开始可以参照社区的常见配比起跑,但一定要在训练中途观察不同 domain 的 loss 表现,再回头调整配比。比如我们第一版代码配比只有 10%,训练时发现代码相关评测分数明显偏低,就把代码比例提到 20% 后才改善。

2.2 清洗流水线的完整步骤

数据清洗没有魔法,就是一堆规则和工具堆出来的流水线。我按实际执行顺序列一下:

  1. HTML 解析和正文抽取,去掉标签、脚本、导航栏等噪音。
  2. 规则过滤:按文本长度、句长、字符重复率、特殊符号比例过滤。比如过短的文本、纯 URL 列表、乱码文本直接丢弃。
  3. 语言识别,把中英文本分开统计,方便后续配比。
  4. 去重:使用 MinHash 做近似去重,同时配合字符级去重处理完全重复的样本。
  5. 质量过滤:用已有的小模型给数据打分,筛掉低质量片段。
  6. PII(个人隐私信息)过滤,至少把明显的邮箱、电话、身份证号等内容抹掉。

每一步都有坑。MinHash 去重如果阈值设得太高,会把大量正常文本当重复去掉,表现为训练语料“越洗越干净”但多样性骤降;阈值设得太低,重复文本又清理不干净。我们试下来对中文网页数据,MinHash 相似度阈值设在 0.7 到 0.8 之间比较合理。

2.3 数据质量打分:决定上限的关键一步

我们一开始也抱着“数据越多越好”的想法,堆了几百 GB 网页数据进去,结果小规模测试模型训练出来的效果很一般。后来做了个简单实验:用同一个模型分别在随机抽取的 5B token 和经过质量过滤的 5B token 上各训练一个 1B 模型,后者的下游评测分数明显更高。这说明质量过滤不是可有可无的优化,而是决定模型上限的关键步骤。

具体做法也不复杂:用一个现成的中等规模模型(不需要很大,1B 左右足够)对每条数据算困惑度或者打质量分,然后按分数截断或重采样。也可以用更轻量的规则先粗筛,再用模型打分做精筛。我们会给每类数据设定不同的分数阈值,比如代码数据阈值可以放低一点,因为代码片段本身简洁,不能拿网页文本的标准去卡。

2.4 训练过程验证数据配比

数据配比合不合理,不是“感觉”出来的,而是在训练中看出来的。我们会在训练日志里额外记录不同 domain 的交叉熵。如果你的英文网页数据学习得很好、中文数据迟迟下不去,说明中文语料比例太低或者质量太差。这时候不是加算力硬跑,而是回数据管线里补数据。

还有一个经验:永远不要只盯全局 loss。全局 loss 下降正常,不代表每个 domain 都在学习。我遇到过某次训练全局 loss 一路下降,但代码评测分数纹丝不动,最后查出来是代码数据占比被清洗管线误伤了一大半,清洗规则里有一个过度激进的符号过滤,把代码里的特殊字符全部打散了。

3. 分词器与模型架构:动手前的三个关键选择

3.1 自己训分词器还是复用现成的

分词器是第一个需要决策的点。直接用社区里现成的 tokenizer 可以省事,但如果你的语料分布和它训练时的分布差得比较远,就会出现 token 碎片化严重、训练效率低的问题。我们最后决定自己训,基于 SentencePiece 用 BPE 算法训练了一个 50k 词表的分词器。

训练词表的数据配比,应该和预训练数据配比保持一致。如果你主要做中文场景,中文语料一定要占足够比例,否则中文字会被切得特别碎。50k 词表是 7B 模型比较常见的选择,太小会让序列变长、训练和推理效率都下降;太大则 embedding 参数占的比重太大,浪费模型容量。举个例子,50k 词表每个 token 的 embedding 维度如果是 4096,embedding 矩阵就是 50k × 4096 ≈ 2 亿参数,对 7B 模型来说占比还好;如果词表放大到 100k,这一块会明显变大。

3.2 架构参数定多大:一个可参考的配置

7B 模型架构建议直接采用当前被验证过的主流设计:decoder-only、Pre-Norm、RoPE 位置编码、RMSNorm、SwiGLU 激活函数、GQA 注意力。下面是可以直接用的参考配置:

  • 层数:32
  • hidden size:4096
  • FFN 中间维度:11008
  • 注意力头数:32
  • KV 头数(GQA):8
  • 头维度:128
  • 上下文长度:4096
  • 词表大小:50000
  • 激活函数:SwiGLU
  • 位置编码:RoPE

这个配置基本对标 Llama-2 7B,只是词表按自己的分词器做了调整。GQA 是必须加的,它能显著减少推理时 KV cache 的显存占用,对后续部署帮助很大。SwiGLU 和 RoPE 已经是现代模型的标配,没必要在这上面搞创新,稳定复现主流方案比炫技重要得多。

3.3 并行训练方案选型

7B 模型的并行方案不需要太复杂。单机 8 卡可以用 DeepSpeed ZeRO-3 或者 PyTorch FSDP,多机环境下推荐用 Megatron 或者成熟的训练框架。我们的实际做法是:32 卡环境下用 DeepSpeed ZeRO-3 + FlashAttention 2,同时开启激活检查点(activation checkpointing)来压显存。

FlashAttention 2 是必选项,它不只省显存,还能显著提升训练速度。激活检查点就是“用计算换显存”的经典方案,开启后能把激活值显存占用降一个数量级,代价是大概 20% 到 30% 的速度损失。对于 7B + 4096 序列长度,开启激活检查点后单卡显存占用可以控制在 35GB 左右。还有一个建议:优先使用 BF16 而不是 FP16 训练。BF16 的动态范围更大,不容易出现梯度溢出,省掉了很多排查 NaN 的时间。

4. 预训练跑起来的那些天:超参、监控与异常处置

4.1 先用一个 1B 探路模型验证方案

正式开跑 7B 之前,我们花了将近一周时间训练了一个 1B 参数的探路模型,训练数据约 20B token。这个探路模型成本低、迭代快,足以验证数据管线是否正常、超参是否合理、loss 下降曲线是否符合预期。

探路模型跑完后,我们做了几件事:看全局 loss 有没有平稳下降、grad norm 是否稳定、不同 domain 的 loss 表现是否合理、生成几个样本文本看语言是否通顺。只有当探路模型的结果符合预期时,才把同样的配置放大到 7B。这一步能避免你在 7B 上跑了几天之后发现数据有问题,浪费巨额算力。

4.2 超参配置的经验值

7B 模型预训练超参,社区里有一套经过大量验证的配置,可以直接作为起点:

  • 峰值学习率:3e-4,配合 warmup 约 1% 到 2% 的训练步数。
  • 学习率调度:cosine decay,最低衰减到峰值学习率的 10%。
  • Batch size:每步约 100 万到 200 万 token。以 4096 序列长度为例,如果每个序列 4096 token,一个 batch 256 个序列就是约 100 万 token。
  • AdamW:beta1 = 0.9,beta2 = 0.95,weight decay = 0.1。
  • 梯度裁剪:max grad norm 设为 1.0。

为什么峰值 lr 用 3e-4 而不是更高?7B 规模的训练稳定性比收敛速度优先级高。lr 太大,前期容易出现 loss spike 甚至发散;lr 太小,收敛太慢浪费算力。cosine 衰减到峰值 lr 的 10% 是兼顾收敛和稳定的常用选择。warmup 的作用是让模型在一个比较平滑的起点上开始更新,避免一开始大步走导致梯度异常。

4.3 训练监控和 checkpoint 管理

训练期间的监控信息,我建议至少记录这六项:step、全局 loss、每个 domain 的 loss、grad norm、学习率、实际吞吐(tokens/second)。这些日志不仅要写,还要定期画图看。grad norm 是判断训练稳定性的重要指标,正常应该在某个范围内波动,如果出现数量级的飙升,八成是数据问题或者超参问题。

checkpoint 策略同样重要。我们每 500 步保存一次主 checkpoint,每 100 步保存一个临时 checkpoint,至少保留最近三个。断点续训不只是加载模型权重,dataloader 的位置和随机数生成器的状态也要考虑进去,否则可能造成某些数据被重复训练。使用 DeepSpeed 的话,记得用--include之类的参数保证恢复时各进程和保存时的 rank 对应关系一致。

4.4 训练异常的真实案例:loss spike、grad norm 爆炸和 NaN

预训练过程中一定会遇到异常,重点讲三个。

第一个是 loss spike。某天凌晨训练跑着跑着,loss 从 2.1 突然跳到 3.8,grad norm 也急剧上升。排查后确认是当批次数据里混入了一段异常文本,过滤管线漏掉了一个分类。处理方式是调低学习率重启,同时修正过滤规则。这个案例说明,遇到 spike 先别急着调超参,花时间查数据往往能找到根因。

第二个是 grad norm 持续飙升。前几百步模型还没进入稳定状态时,grad norm 偏高是正常的,但如果 warmup 结束后还在持续爬升,很可能是数据分布不均或者 batch size 太小。我们把 batch size 翻倍后,问题得到明显缓解。

第三个是 NaN。用 BF16 后 NaN 的情况少了很多,但还是会遇到。我遇到过一次 NaN 是自定义 kernel 在某个极端 input shape 下出了问题,换了版本之后就好了。处理 NaN 最忌讳的是“拍脑袋调超参”,正确的思路是定位到出故障的 step,把该 step 的输入 dump 出来复现,逐层检查。

5. 从“会续写”到“会对话”:SFT 和 DPO 的实操细节

5.1 SFT 数据怎么准备

预训练完成的模型本质上只会续写,你需要通过有监督微调(SFT)让它学会“提问-回答”的模式。SFT 数据的核心不是量大,而是质量。我们最终使用的指令数据量在 5 万条左右,已经是够用的水平。在准备阶段,我建议直接把数据组织成统一的对话格式,比如尽量规范的system+user+assistant三段结构。

数据内容要覆盖这些类型:通用问答、写作、代码、数学、逻辑推理、多轮对话、角色扮演、结构化输出。每类数据都要有足够的多样性。我们踩过最大的坑是数据格式不统一,有些样本有 system 提示,有些没有,训练完之后模型在对话中会偶尔忽略 system 指令。后面花了不少时间把所有数据统一成同一种模板,再训练模型输出稳定了很多。

5.2 SFT 训练参数与 loss mask

SFT 阶段,我们选择全参数微调而不是 LoRA,两者效果差距在 7B 这个规模上能明显感知。全参微调的显存压力和训练时间可控,别在这上面省功夫。

超参方面,峰值学习率可以设为 1e-5 到 2e-5,训练 3 个 epoch。关键点在于 loss 计算时要做 mask:只计算 assistant 回复部分(以及部分场景下的 system 部分)的交叉熵,prompt 部分的 loss 全部置为 0。如果不做 mask,模型会把学习重点放在“预测用户说过的内容”上,而不是真正学习如何回复。我们最开始图省事没做 mask,结果模型生成的回复越来越像复读机,后面加了 mask 才正常。

使用 packing 将多条短样本拼接成一个训练序列时,要额外小心。不同样本之间的拼接处会发生跨样本 attention,这会污染训练目标。解决方法是加载数据时按字段长度排序,尽量让同一条数据足够长,或者在做 attention mask 时把跨样本位置置为不可见。

5.3 DPO 对齐:比 RLHF 省钱的选择

SFT 之后,我们希望模型更符合人类的偏好偏好,比如更愿意承认不知道、更少输出有害内容。完整走 RLHF 需要一个训练好的 reward model,成本和复杂度都比较高,我们用的是 DPO(Direct Preference Optimization),通过偏好对数据直接对齐,不需要单独训练奖励模型。

DPO 的原理简单说就是:给定同一个 prompt 的 chosen 回复和 rejected 回复,训练时增大 chosen 的概率、降低 rejected 的概率,同时用 reference model 的 KL 散度约束,防止模型偏离 SFT 版本太远。我们用了约 2 万条偏好对数据,beta 参数设为 0.1,训练 1 个 epoch。

一个很重要的经验:DPO 训练集里 chosen 和 rejected 的质量差距要足够明显且真实。如果两条回复只是措辞差异,没有本质上的质量差别,模型很难学到有效信号,甚至会变傻。我们迭代了几轮偏好数据,最终效果明显提升。

6. 评测不只看跑分:建立自己的评测闭环

6.1 跑分基准的选择与陷阱

业内常见的评测集,英文通用能力看 MMLU,中文场景看 C-Eval,数学看 GSM8K,代码看 HumanEval。我们在这几项上的最终成绩,和同规模开源模型对比属于中上水平,但这里必须泼一盆冷水:跑分不能完全代表实际体验。

举个实际例子,GSM8K 分数高不代表模型真的会做数学题,可能只是背下了类似题目的解法;HumanEval 分数高不代表能写好业务代码。更现实的问题是,跑分只是静态快照,而且基准集容易被“刷”。我们自己规定,每次调参都先跑固定的评测集,但最终是否上线,以内部评测和人工盲测为准。

6.2 自建评测集和人工盲测

我们花了一个星期建了一套内部评测集,大概 300 道题,覆盖通用问答、中文写作、代码生成、逻辑推理、数学计算、多轮对话、指令遵循。这些题的答案不需要官方人工标注,可以定评分维度:正确性、完整性、格式规范性、是否有幻觉、是否答非所问。

人工盲测是不可替代的环节。每次对比 SFT 版本和 DPO 版本,或者对比两个训练参数版本,都用 A/B 盲测的方式让三个人独立打分,然后统计胜率。盲测结果经常会和跑分不一致:某个版本跑分高,但真人觉得它回答冗长、喜欢堆套话。这时候我们选择相信人工评判,毕竟最终用户是真人而不是 benchmark。

6.3 bad case 驱动的迭代闭环

评测的主要产出不是分数,而是 bad case。我们每周开一次 bad case 复盘会,从自然语言生成、测试集、线上小流量数据三个渠道收集问题,归类整理,然后进入迭代流程。

如果模型在某个知识领域的回答经常出错,优先补充该领域的 SFT 数据;如果是逻辑推理能力不足,可能要在预训练阶段补充代码和数学语料,或者调大 SFT 中推理类数据的比例;如果是格式问题(该用 JSON 输出时输出成了纯文本),回到 SFT 数据里补一批结构化输出样本即可。这套闭环是模型质量持续提升的主要驱动力。

7. 部署阶段的量化与显存账

7.1 7B 模型吃多少显存

7B 参数用 FP16 存权重,大概是 14GB 显存。但实际推理不能只看权重,KV cache 才是大头。KV cache 显存公式:

KV cache = 2 × 层数 × KV 头数 × 头维度 × 序列长度 × batch 数 × 2 字节

按我们前面的架构配置算,层数 32、KV 头数 8、头维度 128、序列长度 4096、batch 为 1,显存约 536MB。看起来不大,但 batch 到 64 之后就超过 34GB 了。所以部署时如果追求高并发,KV cache 的显存优化非常关键,这也是架构选型时要选 GQA 的原因——8 个 KV 头比 32 个 KV 头省了四倍的 KV cache 显存。

7.2 量化的实际效果

我们试过 AWQ 和 GPTQ 两种 4bit 量化方案,权重显存从 14GB 降到约 4GB,同时评测分数下降控制在 1 到 2 个点以内。量化后单张 24G 显卡可以跑 4096 上下文的各种业务场景,部署门槛大大降低。这里提一个建议:量化一定要在 SFT + DPO 之后做,不要在预训练 checkpoint 上做。量化会带来精度损失,如果直接量化一个还没对齐的模型,后续对齐阶段的错误会放大。

7.3 推理框架选择

推理框架我们主要评估了 vLLM 和 llama.cpp。在线服务场景用 vLLM,它的 continuous batching 能显著提升吞吐,高并发下效果很明显。本地或边缘部署用 llama.cpp,量化支持好,CPU 和 GPU 都能跑。如果要用多卡并行推理,vLLM 或者 TensorRT-LLM 都有完善的 tensor parallel 支持。

有一个部署细节特别容易踩:推理时的 chat template 必须和 SFT 训练时的模板完全一致。如果训练时用了一套模板,部署时框架又套了另一套,模型会表现得很”傻”,还以为是模型没训好。

8. 重来一次我会做什么调整

8.1 整体成本和大体时间回顾

整个项目走下来,算力成本大头在预训练,SFT 和 DPO 全量微调的成本其实很小。如果让我重新做一遍,我大概率会把预训练 token 预算从 500B 压到 400B 左右,省下来的算力用于更充分的数据清洗和更多的 SFT/DPO 迭代。对 7B 这个规模来说,边际收益最高的不是多训那 100B token,而是把数据的质量做得更极致,把对齐做得更充分。

8.2 值得复用的资产清单

这次项目沉淀下来的几类资产非常值钱:训练好的分词器、数据处理流水线、探路模型脚本、训练配置、内部评测集、bad case 库。这些资产在新项目里可以反复使用。尤其是内部评测集和 bad case 库,它们才是真正帮你做决策的依据。如果后面想继续做 3B 模型或者 13B 模型,不需要从零开始造轮子。

8.3 给准备动手的人的三条建议

如果你真的准备从零造一个 7B 模型,我最想说的是三条。第一,先用小模型把数据和超参验证完,再启动大规模训练。第二,固定数据基线,不要边训练边改数据配比,否则你永远不知道是哪个改动带来了提升。第三,遇到问题先查数据,再查代码,最后才怀疑超参。绝大多数预训练异常,根源都在数据管线上。

最后分享一个我的个人体会:从零训一个 7B 模型,最大的价值不只是得到一个能用的模型,而是你把整个大模型系统工程从头到尾走了一遍,之后无论是微调、RAG 还是模型部署,你对很多问题的判断会比以前稳得多。如果你想入局大模型这条赛道,这条路虽然贵,但值得走一次。

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

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

立即咨询