在很多人眼里,大语言模型的可扩展性评估就是“把模型做大,看看效果涨不涨”,但真正动手做过一轮评估之后你就会发现,这中间写满了坑。模型参数量翻倍,损失曲线确实在降,但训练成本翻了三倍,下游任务指标又只涨了零点几个点;同一个评估方案,换一批数据、换一种并行策略,得到的结果规律就完全变了。这篇文章我想从实际做评估的角度,把大语言模型可扩展性评估这件事拆开来讲清楚:它到底在衡量什么、关键指标怎么设计、实验怎么做完一轮能拿到可信的结论,以及那些文档里不会写的排除故障经验。适合正在做预训练、模型选型或基础设施规划的团队参考,也适合想系统理解大模型缩放规律的研究者快速建立框架。
1. 可扩展性评估到底在评估什么
1.1 一个规模问题引发的连锁反应
先做一个思想实验:你手上有一个1亿参数的小模型,训练数据用了5GB,效果不错。现在你想知道,如果模型扩到10亿参数、数据扩到50GB,效果能好多少,训练要多久,硬件要几卡。这些问题听起来简单,但直接拿一个10亿模型去训练是不可行的,因为你不能确定结果的提升是来自模型规模、数据规模还是优化器参数的运气。所以需要一种方法,在真正烧钱之前,先推算出规模变化的收益曲线。
这就是可扩展性评估的用处。它本质上回答的是一个预测性问题:给定模型结构、数据分布和计算资源,当规模(模型参数、数据token数、计算量FLOPs)放大时,模型性能(损失、下游能力)会以什么规律变化。如果变化规律是幂律,那我们可以用几个小规模实验,外推算出大规模训练的目标损失和资源预算。如果变化规律不是幂律,那你就要小心了,要么架构存在瓶颈,要么数据分布有问题。
这里有一个关键认知:可扩展性评估不是事后统计,而是事前的工程决策工具。所以评估的核心是“外推”和“可信度”,而不是简单地记录几个点。
1.2 可扩展性评估的三个核心维度
我习惯把评估拆成三个维度,分别是模型维度、数据维度和计算维度。三者互相耦合,不能单独抽出来看。
模型维度指的是参数量、层数、头数、宽度深度比例以及结构变体。最常见的是让模型尺寸按某种倍数递增,比如从30M、60M、120M、240M一路涨到10B,比较不同尺寸下的最终损失。这里必须保证所有尺寸用相同的训练步数或相同的计算预算,否则比较失真。
数据维度是训练数据的数量与质量。大语言模型的可扩展性研究里,数据规模通常用token数衡量。很多团队在评估时只关注模型大小,却忘了同时缩放数据量。经典结论是,模型参数量与训练token数应该一起增长,模型扩大10倍,数据最好也扩大约10倍,但现实中总是受数据收集能力和成本限制。评估时要明确定义数据是固定还是随模型缩放,这是结论差异最大的一处。
计算维度则是总FLOPs、训练时长、硬件配置和并行策略。计算预算决定了你能跑到多大尺度。可扩展性评估的一个常见目标是找到“最优分配”——在给定FLOPs预算下,模型参数和数据分别应该是多少。这需要你在多个“模型尺寸-数据量”组合下做矩阵实验,而不是一条线上的几个点。
1.3 为什么不能只看损失曲线
一个常见误区是拿训练loss曲线直接判断可扩展性。loss在变小,听起来是好事,但loss下降的速率和幅度受很多非规模因素影响:学习率调度、batch size、梯度累积步数、优化器参数、数据顺序。如果你在改变模型尺寸的同时改了这些配置,你看到的loss差异就混淆了因素。
打个比方,你测试一辆车的油耗和速度关系,但每测一次都换不同的轮胎气压和空调档位,最后得到的曲线自然无法真实反映排量与油耗的关系。可扩展性评估要严格控制变量,只让目标规模维度变化,其他超参要么固定,要么按照已有规则进行补偿。比如batch size随模型尺寸同步放大时,学习率也需要按比例调整,否则小模型和大模型处于不同优化状态。
所以我的建议是:可扩展性评估至少需要三层输出。第一层是训练损失随计算量或参数量变化的曲线;第二层是下游任务或评估集上的指标变化;第三层是资源效率曲线,比如每单位loss下降消耗的FLOPs和显存。只报告第一层的文章,很多是被其蒙蔽的。
2. 评估指标体系与实验设计
2.1 关键指标:训练侧与推理侧
把大语言模型可扩展性评估落到具体执行上,首先要定指标。训练侧最核心的是交叉熵损失(cross-entropy loss)和困惑度(perplexity)。这两者是模型对下一个词预测置信度的度量。loss越低,模型对训练数据的拟合越好。但注意,两者在数值上高度相关,不必重复统计。
更重要的指标是“loss vs. 计算量”曲线。我们可以把计算量定义为训练过程中消耗的总FLOPs,这是一个标准化的横轴,可以跨不同模型尺寸进行公平比较。因为从直觉上讲,你花了多少算力才把loss降到某个水平,才是可扩展性的真实含义。很多研究用“每token FLOPs × token数”做计算量,如果要细算:总FLOPs ≈ 6 × 参数量 × 训练token数(仅前向反向的标准估算)。这个公式是业界常用的近似值,实际操作中还要考虑激活重计算和并行通信开销,但作为评估基准足够稳定。
推理侧指标也很重要。扩展模型后若表现变好,却慢得没法上线,这可不算成功。推理侧需要记录首token延迟、生成吞吐量(tokens/s)、显存峰值占用、批处理规模等。可扩展性评估经常被想做训练的人的预训练问题误导。实际上,推理扩展性直接决定你这模型能不能用、成本能不能承受。尤其是当模型尺寸突破给定显存后,可能需要张量并行、流水线并行或量化,这些都会改变推理性能和显存曲线。评估要区分“裸模型推理性能”和“经过工程优化后的推理性能”,否则不同团队结论无法对比。
下游任务指标也不能省。损失下降不一定意味着能力均衡增长。有的模型损失降了,但数学推理能力没涨,反而在一些鲁棒性测试上退步。所以评估集合应该覆盖语言理解、生成、代码、推理、事实性等不同能力维度。每个任务选2到3个代表性指标即可,不要追求跑几十个任务,时间和算力成本太高。
2.2 设计多尺度实验的基本法则
设计一个可扩展性评估实验,本质上就是确定一组从小到大、覆盖一个数量级的模型规模点。我常用的法则如下。
首先确定最小基线和最大参考线。最小模型通常在20M到50M参数之间,小到单卡几小时能跑完;最大模型取决于你愿意为评估付出的预算,一般比目标生产模型小一倍到一个数量级,比如目标模型是10B,评估的最大尺寸可以是1B或2B。过大的评估模型会拖慢迭代,失去评估的意义。
然后确定中间尺寸。中间尺寸建议按对数均匀分布,比如30M、60M、120M、240M、480M、1B。这样在log-log坐标下点间距均匀,后续拟合幂律曲线时更稳定。如果某个区间你的架构特性突变(比如是否使用稀疏注意力、是否使用MoE),那就要在那个突变点附近加密采样,否则拟合出来的曲线会掩盖结构差异。
训练步数或token数怎么定?这是最容易出错的点。常见做法是固定总计算预算,让大模型训练更多token,小模型训练较少token,这在可扩展性研究中叫作“compute-optimal”框架。但要研究“模型大小对性能的影响”,则要固定token数,让不同尺寸模型处理相同的数据量。两种做法回答的问题完全不同,要看你评估的目标是什么。如果是为预训练方案做预算,推荐用fixed compute;如果是为了验证模型结构调整,推荐用fixed data。
还有一条容易被忽视:每个尺寸至少要重复两次不同随机种子。小规模训练成本低,可以多做几个seed去除噪声;大规模训练跑不了太多seed,但至少要有一次独立重复。否则你拟合出来的曲线可能只是噪声的趋势。
2.3 数据与算力配置的归一化方法
不同尺寸的模型,使用的数据批次大小、序列长度、总token数都不同。如果直接拿loss对比,相当于拿苹果和橘子比。必须做归一化。
数据层面,推荐以token为口径而不是以文本段落为口径。因为模型处理的是token序列,段落长度差异会导致每batch的有效信息量不同。所有实验应当使用相同的tokenizer,并统计处理过的token总数。这个总数决定了模型“看到”了多少数据。
算力层面,建议把所有成本折算成统一尺度。训练消耗的总FLOPs是最常用的归一化标准。可参考公式:每token训练的计算成本约为6N,N是参数量,也就是训练一个token需要约6N次浮点运算。如果用了MoE结构,实际计算量可能小于6N,这时候要根据激活参数量计算。在评估报告中,要显式写出计算公式和估算口径,否则别人无法复现你的成本对比。
很多团队喜欢用“GPU小时”做对比,我建议把GPU小时拆成“单卡算力×时间”再统一换算。不同型号GPU的峰值算力不一样,统一为FLOPs或Token-per-second才科学。还要考虑通信开销和IO瓶颈,实测的FLOPs利用率可能只有理论值的40%到70%,这个利用率在模型尺寸变化时通常也会变,应该在记录中单独列出。
3. 实操:从零搭建一次可扩展性评估
3.1 环境准备与基线选择
在我看来,新建可扩展性评估工程最好的起点不是找一堆代码仓库,而是先把基线和约束写下来。
建议准备一张白板或文档,列清楚四件事:模型架构定死(比如标准decoder-only结构,旋转位置编码,GELU激活,层归一化);tokenizer定死(固定词表大小和分词语法);优化器与调度器定死(AdamW配合余弦退火,先设置一个较小warmup步数);数据分布定死(使用同一个预处理后的训练数据集,不做增强或换顺序)。
只要是统一用同一个baseline,你后续的实验才有对照意义。很多公开的大模型评估代码仓库把模型配置写在配置文件里,这可以套用,但注意要把数据路径、tokenizer路径也锁死,避免隐性差异。
硬件层面,评测的最小模型单卡可跑,最大模型要至少2卡以上。我推荐先在一张显卡上跑最小模型,验证整个链路通顺,包括数据加载、checkpoint保存、loss日志输出。先不要急着上并行训练,因为你最少要在小模型上把全部评估流程跑通:训练、评估loss、跑下游任务、记录指标、绘制曲线。这些步骤中的任何意外,都应该在最小尺寸上暴露出来并修复。
3.2 训练多个尺寸模型的调度策略
当你有多个尺寸模型要训练时,调度是有讲究的。最简单的是串行跑,先跑完最小模型,再依次跑更大的。这个策略缺点是如果中间发现配置错误,你前面的时间全部浪费。更稳妥的方式是采用“先侦测后批量”策略:先用最小模型跑一个缩短版(比如只跑完整训练的20%步数),确认loss下降趋势正常、吞吐指标符合预期,然后再把所有尺寸排入队列,按从小到大顺序批量执行。
调度时还要注意一批实验的所有模型使用相同的训练步数规划。比如定下每个模型训练100B token,那么小模型训练时长和大模型训练时长大为不同,但计算总量随规模增长,这就可以计算固定计算预算下的性能曲线。如果你决定固定token数,那么所有模型都训练100B token,总计算量随模型规模增长,这时曲线的横轴应该是模型参数量。
我建议同时记录两类数据:一是每个模型在固定步数的loss快照(比如每1000步记录一次),二是训练结束后的最终指标。前者可以绘制全部训练曲线,后者用于汇总最终可扩展性曲线。这两个数据分开存,别混在一起。
并行策略方面,小模型尽量用单卡或数据并行,大模型用张量并行。注意并行方式本身会影响训练效率,因此记录日志时要额外记录“有效吞吐”,即每秒实际训练的token数。不同并行策略下吞吐差异很大,如果绘制的是FLOPs利用率,会看到随机波动。
3.3 记录数据、拟合缩放曲线
训练结束后,手里的数据是一组点:每个模型有N、T(总训练token)、C(总FLOPs)、loss以及子任务指标。接下来要做的是拟合可扩展性曲线。
主流做法是假设损失与计算量之间满足幂律关系:
loss(C) = a * C^(-b) + c
其中b是缩放指数,b越大代表效率越高;c是可达到的最低损失,受数据固有熵限制。做这个拟合不需要复杂的工具,Python的scipy.curve_fit或直接对对数变换后的数据做线性回归都行。如果是对数变换,公式变成log(loss - c) = log(a) - b * log(C)。麻烦的地方在于c未知,此时可以先用足够大尺度下观察到的loss下界估算c,再迭代拟合。
更有价值的做法是同时对多个幂律项进行联合拟合,例如同时考虑N和D的独立贡献。业界经典公式为:
loss(N, D) = A * N^(-alpha) + B * D^(-beta) + E
这个公式把模型参数规模和数据规模对loss的影响分开。评估时可安排不同尺寸、不同token的组合。实际操作中需要用非线性最小二乘优化。拟合后你可以得到alpha和beta,alpha通常大于beta,说明一定范围内增加参数量比增加数据量更划算。我不建议新手一上来就拟合这个公式,因为对数据点的数量和噪声容忍度要求较高,还是先用单变量幂律热身。
拟合时每个点最好带权重。大模型的loss更稳定,应该有更高权重,小模型点可能受随机种子影响大,可稍微降权。至于怎么定权重,可以直接用模型规模的倒数或者loss标准差的倒数,没有绝对标准,但要写进报告里。
3.4 评估结果的可视化与解读
等拟合完成后,漂亮的可视化会帮助你看出很多趋势。最基础的图是log-log坐标下的loss-C曲线,横轴是总FLOPs或训练token,纵轴是loss,每个点标不同尺寸模型。这条线如果呈较直的下降趋势,说明可扩展性规律稳定;如果曲线在大模型处开始变平,说明模型扩展时数据不够,或收益正在消失。
第二个必须看的图是“loss vs 模型参数量”曲线,固定token数绘制。若曲线下降趋势逐渐放缓,说明模型从参数增加中获得的边际收益降低,此时应该转而增加数据。第三个图是“下游任务指标 vs loss”的散点图,用于验证“loss降了,能力是否齐涨”。如果散点图呈现出某些任务在一个临界点后才开始上升,说明模型能力和损失并不是理想的单调映射。
做解读时,不要只看拟合系数。b值大不等于模型好,还要看绝对loss。小模型在极低计算预算下b值可能很大,但绝对loss很高,这种表现在scale up后很可能只是追赶小模型的起点。所以解读要结合“目标能力水平”反向推理:你希望最终达到的loss是多少,在拟合曲线上找到需要的FLOPs,然后反推硬件资源。
4. 常见问题与排查实录
4.1 曲线抖动:是随机噪声还是实现问题
我在实际评估中第一个想分享的问题是曲线抖动。你训练三个尺寸的模型,得到的loss-C点没有平滑排列,甚至最小模型比中间模型效果还好。这种情况大概率不是随机噪声,而是配置不一致。
需要逐项排查:tokenizer是否一致?训练数据是否经过相同的预处理?是否每个模型都执行了相同的检查点保存和评估代码?有时候为了加速,小模型用bfloat16混合精度,而大模型用了FP8或int8,这会影响数值精度从而改变loss。还有数据加载顺序不一致,比如文件打乱时用了不同的随机种子,会导致模型看到的数据分布略有不同,尤其是如果数据本身存在领域偏差。
我踩过一次最隐蔽的坑:小模型训练时用了更大的batch size模拟大模型训练环境,但学习率没有按batch size比例调整。结果是“大LR能吃下大batch”,而小batch配上较小LR输出反而更高loss,趋势完全混乱。后来我一律以“梯度更新步数”而不是“样本数”为基准设计对照。
4.2 小模型数据量不合理导致拟合失真
很多新手做评估时,让所有尺寸模型训练同样长的步数,但小模型由于单步计算量小,总token数远低于大模型,这本身没问题。但如果你在固定token数时,让只有2000万参数的模型也读10亿token,那它可能早就过拟合了,loss不再下降,最终点会在曲线中形成一个“高不可攀”的平台,直接破坏幂律拟合。
解决办法是先做一次小型过拟合探测:对不同尺寸模型,分别训练一定比例的token,观察训练集loss和验证集loss的gap。如果gap持续扩大但验证loss不再下降,说明数据量对该模型尺寸偏多。在正式评估时,为每个尺寸设置一个合理的数据上限,或干脆采用固定计算预算,在低预算下损失主要是欠拟合而不是过拟合,曲线更干净。
4.3 吞吐与显存计算偏差
可扩展性评估不只看loss,还要看成本。很多人直接从框架日志里读“吞吐量”,但不同框架的统计算法不同。例如有的统计的是端到端包含评估的吞吐,有的只统计纯训练步骤。我建议自定义一个计数器,只统计实际前向反向迭代的token数和时间,并把数据加载、checkpoint、评估的时间单独计时。否则,你有可能会把一个IO瓶颈误判为模型扩展性瓶颈。
显存也需要按真实张量内存来分析。每个模型的峰值显存会随着batch size、序列长度和数据并行度变化。评估时如果要比较不同尺寸模型的内存扩展趋势,必须固定batch per device和序列长度,然后记录显存。同时,梯度检查点、混合精度、优化器状态(AdamW额外存m和v向量)都会显著影响内存。统一配置后,显存曲线应该大致随参数量线性增长,如果你的曲线出现跳跃式上升,多半是并行策略触发了通信缓冲区分配,需要单独排查。
4.4 跨集群实验的可复现性
现实情况下,一个评估项目常常要在不同显卡、不同集群上跑不同尺寸模型,这会给可复现性带来很多麻烦。我遇到过同一份代码在不同集群上跑出不同loss的情况,找了一圈原因,发现是跨机器数据Shuffle的随机状态未对齐。由于dataset没有固定全局shuffle seed,每台宿主机上的数据顺序不同,导致模型见到的数据流不一致。解决办法是第一次把shuffle后的全局索引序列固化成一个文件,所有模型都从相同索引序列中读取数据。
还需要盯紧不同集群的通信带宽差异。小模型训练时通信占比小,大模型并行度提升后,通信时间可能占30%以上。如果你用总FLOPs除以端到端时间算有效算力,会发现大模型的有效算力显著偏低。这个偏低本身不算bug,但你要在评估报告中体现出来,因为它会影响总成本预测。我的建议是记录“理论FLOPs利用率”和“端到端有效算力”两组值,并分别绘制可扩展性曲线。前者反映算法缩放趋势,后者反映工程可扩展性。
写在最后:我踩过几次坑之后的一些体会
可扩展性评估做多了,我会先画好一张三列表格,一列是模型尺寸,一列是数据总量,一列是总FLOPs,然后逼自己先写清楚每个配置背后的经济学理由。不要拿到模型就开跑,先想清楚对比口径,否则跑完一轮,节奏不一定能快速收敛。实战中,我最推崇“先小后大、多关注趋势、少在意绝对点”的心态。两个模型尺寸下的loss差异,不如五个尺寸点下拟合出来的幂律指数重要。后者才是可扩展性的稳定指纹。
另外一个小技巧,也是我后期一直在用的:每个实验跑完,都要立即把配置和曲线导出成一份快照,包括git commit号、数据版本、硬件信息、拟合参数、原始loss序列。因为可扩展性评估项目经常做到一半要加新的数据点,如果你不保留完整快照,几个月后你根本没法恢复当时的实验环境。我们团队现在要求每个评估任务结束后都生成一个标准化的JSON元数据文件,后续所有分析都从这个文件取数,大幅减少了沟通成本。
最后,我想说可扩展性评估的价值不在某个数据点多么漂亮,而是能用几个小实验让你对大模型的边界更有数:什么架构在数据充足时值得堆参数,什么规模的投入开始边际递减,你的硬件投入应该优先放在模型宽度还是数据清洗上。把这个评估做扎实,后续每一分训练预算都会花得更踏实。