ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Scaling Law工程实战:算力预算、曲线拟合与外推验证

Scaling Law工程实战:算力预算、曲线拟合与外推验证

Scaling Law 这个词这两年在做模型训练的人嘴里出现频率极高,但真正动手拟合过曲线的人并不多。我第一次把它当工程工具用,是在一个中等规模预训练项目上:手里只有一小块算力预算,会上被问的不是“讲讲幂律”,而是“参数翻一倍,loss 能降多少,这笔钱值不值”。那一刻我才明白,Scaling Law 解析的核心不是背公式,而是回答资源分配的边际收益问题。它本质上是一组描述模型损失随参数量、训练数据量、计算量变化的经验规律,能帮你在动手之前估算投入产出,适合做预训练规划、算力预算审批、模型选型,也适合刚接触大模型训练、想搞明白“为什么大家都在堆规模”的工程师。下面我按自己踩过的顺序,把这套东西拆开讲。

1. 先把 Scaling Law 到底是什么说清楚

1.1 它不是一个公式,而是一组可检验的工程假设

很多人把 Scaling Law 理解成一条“参数越大loss越低”的曲线,这个理解不算错,但太粗。更准确地说,它是一组带参数的函数形式,用来描述验证集损失如何随模型参数量、训练 token 数、计算量变化。最常见的写法是把损失拆成三块:一块是不可约损失,也就是在当前数据分布和任务定义下,任何模型都降不下去的下限;一块是参数不足带来的损失;一块是数据不足带来的损失。三块叠加,才构成你观测到的总损失。这个拆法很重要,因为如果你只盯总损失,很容易把数据不够导致的欠训练误判成参数不够。

我习惯用一个做菜的类比来解释:锅的大小相当于参数量,食材总量相当于训练 token 数,火候和时间相当于计算量。锅再大,食材不够,做出来的菜量还是上不去;食材再多,锅太小,也装不下。不可约损失则像菜谱本身的上限,你换再好的锅也突破不了菜谱定义的天花板。这个类比不严谨,但它能帮你记住一件事:三个自变量是耦合的,不能单独看。

还有一点必须强调,Scaling Law 是经验规律,不是物理定律。它的系数依赖数据分布、模型结构、优化器、学习率调度、序列长度,甚至依赖你用什么 tokenizer。换一套数据管线,指数可能就变了。所以我在项目里从来不直接套别人论文里的系数,最多拿它当初始猜测,然后用自己的小规模扫参重新校准。这一点后面会展开讲,因为它是踩坑最多的地方。

1.2 三个自变量和它们之间的耦合关系

参数量 N、训练 token 数 D、计算量 C,这三个量不是独立的。对于标准的稠密 Transformer 类结构,一次前向传播的计算量大约是 2ND,反向传播大约是 4ND,合起来训练一个 token 大约需要 6ND 次浮点运算。这个 6 不是精确值,它忽略了注意力机制里随序列长度增长的部分,也忽略了激活重计算、通信开销等工程因素,但在做预算估算时足够用。记住这个关系,你才能在“给定算力”的条件下反推参数和数据的配比。

耦合关系体现在两个方向。第一个方向是参数涨了、数据不涨,模型会进入欠训练状态,表现是训练损失还能降,但验证损失提前抬头,或者验证损失降得越来越慢。第二个方向是数据涨了、参数不涨,模型容量不够,多出来的数据只能被反复咀嚼,收益迅速衰减。我见过一个项目,数据量翻了四倍,参数没动,最后验证损失只降了不到百分之二,团队一度以为是数据清洗出了问题,其实是模型容量已经饱和。

还有一个容易被忽略的耦合项是序列长度。同样叫“一个 token”,在长上下文任务里,注意力计算占比会显著上升,6ND 这个近似会偏乐观。如果你的项目涉及长文档、代码仓库级别的输入,做算力预算时要把这部分额外开销单独留出来,否则你会发现自己算出来的训练步数根本跑不完。我的做法是在小规模扫参阶段就记录实际吞吐,用实测的 tokens per second 反推有效计算量,而不是纯靠公式拍脑袋。

1.3 为什么是幂律:冗余容量与高维拟合的直觉

损失随规模下降呈现幂律,也就是在对数坐标下近似一条直线,这件事一开始让我很意外。直觉上,规模翻倍,损失应该线性下降才对,为什么是幂律?一个比较能被工程直觉接受的解释是:模型要拟合的目标可以看成大量难度不同的子模式的集合。容量增加时,模型先覆盖容易的模式,再覆盖难的模式。每增加一单位容量,能新覆盖的模式数量在减少,但不会突然归零,于是边际收益递减,形成幂律而不是指数衰减。

另一个角度是冗余。大模型并不是每一个参数都在干有用的活,相当一部分容量被用来做冗余表示、特征组合和误差平滑。规模越大,冗余带来的平滑效应越明显,损失下降越稳定。这也解释了一个现象:在小模型上做架构微调,效果波动很大;到了大模型上,同样的微调可能被淹没在冗余里,收益变得不明显。幂律描述的是这种统计意义上的平均行为,它对单个模型、单次训练的预测能力有限,对一类模型、一个规模区间的趋势预测才有意义。

所以我在做 Scaling Law 解析时,会反复提醒自己:不要拿它预测某一次训练的精确 loss,而是拿它判断“往哪个方向加资源更划算”。这两件事的精度要求完全不同。前者要求你的拟合误差控制在几个百分点以内,后者只需要你知道指数大概是多少、哪个自变量更值得加。搞清楚这个定位,后面很多争论其实可以避免。

2. 规模定律的两次关键修正与选型逻辑

2.1 早期版本:参数优先的代价

最早一批规模定律研究给出的结论,大致是参数量的指数明显大于数据量的指数。翻译成工程语言就是:在固定算力下,应该把更多预算砸给参数,数据可以少一点。这个结论在当时有其合理性,因为早期模型的训练数据确实有限,很多团队的数据量还没有成为瓶颈。但把它当成通用配方,就会出问题。

参数优先的直接代价是模型欠训练。你有一个很大的参数容器,但只灌了很少的数据,模型没有充分见过足够的语言现象,验证损失降不到它应有的水平。更麻烦的是,这种欠训练在训练曲线上不一定明显,因为训练损失还在降,团队容易误以为“再训久一点就好了”。实际上你需要的是更多数据,而不是更多步数。另一个代价是推理成本。参数越大,单次前向的计算和显存占用越高,上线之后的持续成本会吃掉训练阶段省下来的钱。我见过一个团队按早期配比训了一个参数偏大的模型,训练阶段确实省了数据清洗的功夫,但上线后推理成本比预期高了近一倍,最后不得不重新蒸馏一个小模型来兜底。

下面这张表是我自己整理的一个粗略对照,用来提醒自己早期配比和修正配比的差异。表中的指数是示意值,不是精确结论,实际项目必须用自己的数据重新拟合。

对比项早期参数优先配比修正后的均衡配比
参数量随算力增长指数约 0.7 量级约 0.5 量级
数据量随算力增长指数约 0.3 量级约 0.5 量级
典型 token 参数比明显偏低约 20 比 1 起步
主要风险欠训练、推理贵数据质量要求高
适用场景数据极度受限数据可扩充的常规预训练

这张表的价值不在于数字本身,而在于提醒你:配比不是固定真理,它随数据可得性变化。如果你手里的高质量数据非常有限,适当偏向参数并不是错;如果你能持续产出高质量 token,均衡配比通常更划算。

2.2 修正版本:参数与数据要同步放大

后续的修正研究做了一个更系统的扫参:在更大的规模范围内,用统一的学习率调度和数据管线,重新拟合参数和数据各自的指数。结论是,在计算最优的约束下,参数量和数据量应该大致等比例增长,也就是两者随算力的增长指数都在 0.5 附近。工程上最常被引用的经验值是每个参数配大约 20 个训练 token。这个数字不是魔法,它是在特定数据分布、特定模型结构下拟合出来的一个平衡点,但它作为一个起点非常好用。

为什么早期会偏?我自己的理解是三个原因叠加。第一,早期研究的规模点跨度不够大,外推时指数容易被小规模段的局部行为带偏。第二,学习率调度没有随规模做充分匹配,小模型在固定步数下更容易收敛,大模型反而欠训练,导致拟合出来的参数收益被高估。第三,数据质量差异没有被单独控制,不同规模用了不同质量的数据,指数自然失真。这三点在后来都被逐项修正,所以修正版的结论更可信,但也更依赖你把实验条件控制干净。

我在项目里的做法是:先用 20 比 1 作为初始配比做一轮小规模验证,然后在这个配比附近做两三个点的扰动,比如 15 比 1 和 30 比 1,看验证损失对配比的敏感度。如果曲线在 20 比 1 附近很平,说明你的数据质量和模型结构对这个配比不敏感,可以放心放大;如果曲线很陡,说明你的场景有特殊性,必须重新拟合。这个扰动实验的成本很低,但能避免你在放大阶段犯大错。

2.3 计算最优边界怎么算:给定算力下的配比推导

把上面的关系落成可计算的公式,其实不复杂。假设总计算量 C 约等于 6ND,再假设最优配比满足 D 等于 k 倍的 N,那么 C 约等于 6k 乘以 N 的平方。给定 C 和 k,就能反解出 N 和 D。这个过程我在预算会上用过很多次,比拍脑袋靠谱得多。

举几个具体数字。取 k 等于 20,也就是每个参数配 20 个 token,那么 C 约等于 120 乘以 N 的平方。当 C 等于 1e21 时,N 约等于 2.9e9,D 约等于 5.8e10;当 C 等于 1e22 时,N 约等于 9.1e9,D 约等于 1.8e11;当 C 等于 1e23 时,N 约等于 2.9e10,D 约等于 5.8e11。把这些数放进表里,你能直观看到算力每涨一个数量级,参数和数据各自涨约 3.16 倍,也就是根号十。

总计算量 C(FLOPs)估算参数量 N估算训练 token 数 D备注
1e21约 2.9e9约 5.8e10中等规模预训练
1e22约 9.1e9约 1.8e11需要稳定数据管线
1e23约 2.9e10约 5.8e11通信与存储压力明显

这张表只解决了“理论配比”,没有解决“实际能不能跑完”。真实项目里,你还要把训练吞吐、集群利用率、故障恢复开销算进去。我的经验是,理论算力打七折到八折作为实际可用预算比较稳妥,尤其是规模上去之后,通信和重试的损耗不可忽略。另外,k 值不是永远取 20,如果你的数据质量很高、去重很彻底,可以适当调高;如果数据重复率高、质量参差,调高 k 反而会放大噪声,这时候更应该先修数据,而不是继续加 token。

3. 手把手做一次规模定律拟合

3.1 网格设计:小模型扫参怎么扫才不白扫

拟合的第一步不是写代码,是设计扫参网格。我的基本原则是:规模点至少五个,跨度至少两个数量级,每个规模点上至少两个不同的 token 参数比。跨度太小,指数拟合会非常不稳定;点太少,异常点会把整条曲线带偏。举个例子,参数量可以从几百万到几亿,取 1M、3M、10M、30M、100M 五个点,每个点跑 20 比 1 和 50 比 1 两个配比,这样你既有跨规模的趋势,也有配比方向的敏感度。

控制变量比堆点更重要。所有规模点必须用同一套数据、同一个 tokenizer、同一套模型结构、同一套优化器配置。学习率可以随规模调整,但调整规则要固定,比如按参数量的平方根反比缩放,或者每个规模单独做一个小范围学习率扫描后取最优。如果你让小模型用调好的学习率、大模型用默认学习率,拟合出来的指数基本没有参考价值。我踩过这个坑:一轮扫参里大模型忘了改 warmup 比例,结果大模型的损失异常高,拟合出来的参数指数偏小,差点得出“参数不重要”的错误结论。

成本控制方面,小模型扫参不需要跑满完整训练。你可以用较短的序列长度、较小的批量、较少的步数,只要保证每个规模点都训练到损失曲线明显变平。判断“变平”的方法是看最近若干步的验证损失滑动平均,变化小于某个阈值就停。这个阈值要提前定好,不能看到结果再调。我的习惯是验证损失在最后百分之十的步数内下降不超过千分之五,就算收敛。这个标准不严格,但足够支撑趋势判断。

3.2 损失曲线清洗:不可约损失剥离与异常点处理

原始损失曲线不能直接拿来拟合,必须先清洗。第一件事是区分训练损失和验证损失。训练损失会被记忆效应污染,尤其是小模型在少量数据上反复训练时,训练损失可以降得很低,但验证损失早就平了。拟合一律用验证损失,而且要确保验证集和训练集没有重叠、没有同源污染。

第二件事是处理异常点。训练过程中出现损失尖峰、发散后恢复、学习率重启,这些都会在曲线上留下毛刺。我的做法是对最后一段做滑动平均,取中位数而不是均值,避免尖峰拉高。如果某个规模点整体发散,不要试图修补,直接重跑,因为发散往往意味着超参不匹配,修补后的数据会污染拟合。

第三件事是确定收敛值。学习率没有衰减到接近零时,损失还没有完全收敛,直接取最后一个点会高估损失。我通常让学习率衰减到峰值的百分之一以下,再取最后若干步的中位数作为该点的收敛损失。如果算力不够,至少要让余弦衰减走完大部分周期。这一点在小模型上尤其重要,因为小模型收敛快,学习率调度没走完就停,损失会明显偏高,导致你低估小模型的效率,进而高估大模型的相对收益。

不可约损失的处理有两种方式。一种是把不可约损失作为拟合参数,和数据项、参数项一起拟合;另一种是用一个足够大的模型在足够多的数据上估计一个下限,然后固定住。前者更省事,但要求你的规模点跨度足够大,否则不可约损失和参数项容易混淆;后者更稳,但需要额外的大规模实验。我在预算有限时通常用前者,但会检查拟合出来的不可约损失是否落在合理区间,如果它接近甚至超过最小规模点的损失,说明数据有问题,拟合不可信。

3.3 拟合代码:最小二乘与对数空间

拟合本身不复杂,核心是最小化预测损失和观测损失之间的相对误差。下面这段代码是我常用的模板,用 Python 写,依赖 numpy 和 scipy。它的做法是把不可约损失、参数项系数、参数项指数、数据项系数、数据项指数一起作为待拟合参数,在对数空间里构造残差,然后用鲁棒最小二乘求解。

import numpy as np from scipy.optimize import least_squares # 观测数据:参数量 N、token 数 D、验证损失 L N_obs = np.array([1e6, 3e6, 1e7, 3e7, 1e8]) D_obs = np.array([2e7, 6e7, 2e8, 6e8, 2e9]) L_obs = np.array([3.42, 3.18, 2.95, 2.76, 2.58]) def predict(params, N, D): E, A, alpha, B, beta = params return E + A / np.power(N, alpha) + B / np.power(D, beta) def residual(params, N, D, L): pred = predict(params, N, D) # 用相对误差,避免大损失点主导拟合 return (pred - L) / L # 初始猜测:不可约损失取最小观测损失的八成,指数从 0.3 起步 x0 = np.array([2.0, 1.0, 0.3, 1.0, 0.3]) bounds = ( [0.0, 0.0, 0.01, 0.0, 0.01], [np.min(L_obs), np.inf, 1.5, np.inf, 1.5] ) result = least_squares( residual, x0, args=(N_obs, D_obs, L_obs), bounds=bounds, loss="soft_l1", max_nfev=20000 ) E, A, alpha, B, beta = result.x print(f"不可约损失 E = {E:.4f}") print(f"参数项 A = {A:.4f}, 指数 alpha = {alpha:.4f}") print(f"数据项 B = {B:.4f}, 指数 beta = {beta:.4f}") # 留一验证:用前四个点拟合,预测第五个点 train_idx = [0, 1, 2, 3] test_idx = [4] res_train = least_squares( residual, x0, args=(N_obs[train_idx], D_obs[train_idx], L_obs[train_idx]), bounds=bounds, loss="soft_l1", max_nfev=20000 ) pred_last = predict(res_train.x, N_obs[test_idx], D_obs[test_idx]) print(f"留一点预测损失 = {pred_last[0]:.4f}, 实际 = {L_obs[test_idx][0]:.4f}")

这段代码有几个细节值得说。第一,用相对误差而不是绝对误差,是因为大规模点的损失绝对值小,绝对误差会让小规模点主导拟合,指数被带偏。第二,用 soft_l1 而不是默认的线性损失,是为了降低异常点的权重,一两个毛刺不至于把整条曲线拉歪。第三,给指数加了上界 1.5,防止拟合跑到不合理的区域,比如指数大于一意味着规模翻倍损失腰斩,这在实践中几乎不会出现。第四,留一验证很重要,它比拟合优度更能说明模型能不能外推。

拟合结果怎么读?先看不可约损失是否合理。它应该低于你最小规模点的损失,但又不能低到离谱。再看两个指数。如果参数指数明显大于数据指数,说明在你的实验条件下,加参数比加数据划算;反之则说明数据是瓶颈。最后看留一验证的误差,如果预测值和实际值差在百分之五以内,这条曲线可以用来做同分布的外推;如果差得远,不要硬用,先检查实验条件是否一致。

3.4 外推验证:留一个最大点不参与拟合

外推验证是整套流程里最容易被跳过、也最不该跳过的一步。我的做法是永远留一个最大规模点不参与拟合,用它来检验曲线。这个点最好比参与拟合的最大点大一个数量级左右,这样你能看出曲线在跨数量级时是否还站得住。如果留出点的预测误差在百分之五到百分之十以内,我会认为这条曲线可以用于同架构、同数据分布的预算估算;如果误差超过百分之十五,我会把它标记为“仅供方向参考”,不拿它做精确决策。

外推误差的来源通常有三类。第一类是优化不充分,最大规模点没有训到收敛,实际损失偏高,而曲线是按收敛趋势外推的,自然对不上。第二类是数据重复,大规模点用了更多 epoch,有效数据量没有名义上那么多,损失下降不及预期。第三类是架构或超参在大规模时发生了变化,比如为了省显存换了激活重计算、为了稳定训练调了初始化,这些都会让曲线断裂。排查时按这个顺序查,基本能定位到原因。

还有一个经验判断:如果你只能用小于一个数量级的跨度做拟合,外推结果的可信度会大幅下降。我一般要求参与拟合的规模跨度至少两个数量级,留出点再跨一个数量级。达不到这个条件时,我会明确告诉决策方“这是趋势估计,不是预算承诺”。这句话听起来保守,但能避免后面很多扯皮。毕竟 Scaling Law 解析的价值是帮你排序方案,不是替你做最终决定。

4. 实操中最容易踩的坑与排查表

4.1 数据质量会直接改写指数

同样叫一亿个 token,去重前后、质量过滤前后,有效信息量可能差出一大截。低质数据、模板化文本、重复段落会让模型把容量浪费在记忆上,验证损失下降变慢,拟合出来的数据指数偏低。你以为是“数据不重要”,其实是“这批数据不值得学”。我在一个项目里做过对比:同一套模型、同一个 token 数,清洗后的数据让验证损失多降了将近百分之八,拟合出来的数据指数从 0.22 变成了 0.31。这个差距足以改变配比决策。

所以做扫参之前,先把数据管线固定下来,并且记录版本。扫参过程中不要换清洗规则、不要换去重阈值、不要中途补充新数据源。如果非要换,所有规模点一起重跑,否则你拟合的是数据差异,不是规模差异。另一个细节是验证集的构造。验证集要从训练数据同分布里留出,但不能和训练数据有重叠,也不能用训练时过滤掉的低质数据充当验证集,否则验证损失会虚低,曲线整体下移,指数反而看起来更漂亮,这是典型的自欺欺人。

4.2 学习率与批量带来的假 Scaling

学习率是假 Scaling 的重灾区。小模型用偏大的学习率,收敛快但最终损失偏高;大模型用同样的学习率,可能训练不稳定或者收敛不足。两种偏差方向相反,叠在一起会让拟合指数失真。我的做法是给每个规模点单独做一个小范围学习率扫描,取验证损失最低的那个,然后记录下最优学习率和规模的关系。如果关系大致符合平方根反比,说明你的扫描是自洽的;如果关系乱七八糟,说明还有别的变量没控制住。

批量大小也一样。小模型在小批量下更新步数多,收敛快;大模型如果批量放大不够,梯度噪声大,损失曲线会更抖。反过来,批量放得太大,学习率没跟上,训练会变慢。比较稳妥的做法是让批量随规模适度增长,同时保证每个规模点的训练 token 总数和更新步数在合理范围。还有一个容易忽略的点是序列长度。小模型用短序列、大模型用长序列,注意力开销占比不同,实际计算量和你按 6ND 估算的值会偏离。扫参时尽量统一序列长度,或者至少记录下实际吞吐,用实测值校正计算量。

4.3 下游任务与 loss 脱钩

验证损失降了,下游任务不涨,这种事我遇到过不止一次。原因通常有三类。第一类是任务饱和,你的评测集太简单,模型早就到顶了,loss 再降也反映不到准确率上。第二类是评测污染,训练数据里混进了评测集的原题或近似改写,loss 降的是记忆,不是能力。第三类是格式不匹配,模型学会了语言分布,但没有学会你评测时要求的输出格式,导致自动打分偏低。这三类的排查方法不同,但共同点是:不能只看 loss,必须有一组自己构造的、训练时严格隔离的评测。

我通常会留一组“金丝雀”评测,规模不大,但题目是手工构造的,确保没有泄漏。每次扫参都跑一遍,观察 loss 和评测分数的相关性。如果某个规模点上 loss 降了但金丝雀分数不动,我会先怀疑数据或评测,而不是急着下结论说“规模没用”。还有一种情况是能力跳变,某个规模之后下游分数突然抬升,这在文献里被讨论很多,但在工程上要谨慎对待:跳变可能是真实的,也可能是评测阈值效应,也就是模型刚好越过了某个打分边界。判断方法是换一组同难度但不同形式的题目再测,如果跳变复现,可信度就高很多。

4.4 常见问题速查表

下面这张表是我自己整理的排查清单,按现象查原因,能省不少时间。表中的“优先动作”是我个人的习惯顺序,不一定适用于所有团队,但方向大致通用。

现象可能原因优先动作
拟合出的参数指数异常大小模型欠训练、大模型超参不匹配检查每个规模点的收敛情况与学习率
拟合出的数据指数异常小数据重复、质量差、验证集泄漏去重、质量过滤、重建验证集
留出点预测误差超过百分之十五外推跨度不足、优化未收敛增加规模点、延长训练、检查数据 epoch
loss 降但下游不涨任务饱和、评测污染、格式不匹配换评测集、查泄漏、人工抽查输出
曲线在小规模段很陡、大规模段变平不可约损失估计偏低、数据瓶颈放宽不可约损失边界、增加数据配比点
不同批次实验结果抖动大数据顺序、初始化、硬件差异固定随机种子、记录数据顺序、多跑几次取中位数

这张表不能替代具体分析,但它能帮你快速排除低级错误。我的经验是,八成以上的拟合异常都能在前三行里找到原因,剩下的两成通常和工程环境有关,比如不同规模跑在不同型号的机器上,浮点精度或通信策略不一样,导致数值行为有差异。遇到这种情况,先把环境对齐,再谈拟合。

5. 从训练到推理:规模定律的延伸用法

5.1 推理成本 scaling:什么规模值得上线

训练成本是一次性的,推理成本是持续的。参数量翻倍,单次前向的计算量大致翻倍,显存占用也翻倍,但请求量大的时候,推理成本会迅速超过训练成本。所以做规模决策时,不能只看“这个规模 loss 更低”,还要看“这个 loss 优势值不值推理溢价”。我的做法是算一个简单的盈亏平衡:给定日均请求量和单 token 推理成本,算出大模型相对小模型每年多花的推理费用,再对比它在业务指标上带来的提升。如果提升带来的收益覆盖不了溢价,就应该选小模型,或者用蒸馏、量化把大模型的能力压到小模型里。

这里有个容易混淆的点:总参数量和激活参数量不是一回事。稀疏结构的总参数可以很大,但每次前向只激活一部分,推理成本按激活参数算。做预算时一定要区分这两个数,否则你会把推理成本高估好几倍。另外,长上下文会显著推高推理成本,因为注意力计算随序列长度增长。如果你的业务输入很长,参数量的边际影响会被摊薄,这时候更值得优化的是上下文压缩和缓存策略,而不是继续抠参数规模。

5.2 数据重复与多 epoch 的收益衰减

扫参时经常遇到数据不够,只能重复训练。重复数据不是完全没用,但有效数据量要打折。我的经验是,前几个 epoch 的收益接近新数据,之后衰减很快,超过一定次数后基本只剩记忆效应。具体折扣系数因数据而异,去重彻底、多样性高的数据,折扣来得慢一些;模板化严重的数据,第二个 epoch 就开始明显衰减。拟合时如果忽略这一点,你会把“重复带来的 loss 下降”误判成“规模带来的收益”,外推到大模型上就会高估。

处理方法有两种。一种是在拟合时把名义 token 数换成有效 token 数,有效 token 数等于唯一 token 数加上重复部分的折扣;另一种是干脆在扫参阶段控制每个规模点的 epoch 数一致,避免重复度差异污染指数。我倾向于后者,因为它更干净,代价是需要准备足够多的唯一数据。如果实在做不到,至少要在记录里标明每个点的 epoch 数,外推时手动打折。这个细节不起眼,但在预算紧张、数据有限的团队里,它经常是拟合失真的主因。

5.3 外推边界与定律失效的信号

Scaling Law 不是万能钥匙,它在某些条件下会明显失效。我总结了几类信号,出现任意一类,就不要直接套旧系数。第一类是架构大改,比如从稠密换成稀疏、引入新的注意力机制、改变归一化位置,这些会改变参数效率和数据效率,旧指数不再适用。第二类是数据分布大改,比如从通用文本切到垂直领域、从单语切到多语,不可约损失和指数都会变。第三类是训练精度或优化器大改,混合精度策略、优化器状态、梯度裁剪阈值的变动,都会影响收敛行为。第四类是序列长度跨数量级,长上下文带来的计算结构变化会让 6ND 近似偏离。

遇到这些情况,我的做法是重新做一轮小规模校准,哪怕只跑三四个点,也比直接套旧系数强。校准的目的不是追求精确,而是确认指数没有发生方向性变化。如果新拟合的指数和旧系数差得不多,可以继续用旧曲线做粗略估算;如果差得多,就以新数据为准。还有一点,不要拿别人的系数直接用,哪怕架构相同。数据管线、tokenizer、训练框架的差异,足以让指数偏移百分之十以上,而这个偏移在预算决策里可能就是几倍的资源差异。Scaling Law 解析说到底是一种本地化知识,你得用自己的实验把它养出来。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进