多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

OpenResearch 缩放律图表实战:用 orx-figures 拟合一条可辩护的 Scaling Law

OpenResearch 缩放律图表实战:用 orx-figures 拟合一条可辩护的 Scaling Law OpenResearch 缩放律图表实战用 orx-figures 拟合一条可辩护的 Scaling Law【免费下载链接】OpenResearchTurn your coding agents into research agents项目地址: https://gitcode.com/GitHub_Trending/op/OpenResearch本文是 OpenResearch 的orx-figures技能中 scaling 参考文档 的深度实战指南主题是如何把一组实测 run 拟合成一条可信的缩放律scaling law并安全地外推到你没有跑过的规模。它直接适用于论文、技术报告与 Agent 研究流程中的损失随参数量/数据量/算力变化图表读完后你将掌握三参数幂律的正确拟合形式、什么数据点有资格进入拟合、如何用残差面板和不确定性区间让审稿人挑不出毛病以及 OpenResearch 提供的完整模板脚本与风格模块的底层实现。整篇文章以该参考文档为骨架并以仓库源码为佐证。缩放律图的全部价值在于拟合本身一个缩放律图的本质是对已测 run 的双对数散点拟合一条幂律曲线。它的价值全部在拟合这条线上——它回答指标如何随模型规模、数据量或算力变化以及这条趋势在你没跑过的规模上预示着什么。因此拟合必须可辩护defensible。原文档的告诫非常直白在双对数轴上仅仅通过四个点画一条直线看起来就会很有说服力——无论它是否真的有意义。视觉上的直不是证据这条线是否用了正确的函数形式、是否覆盖了正确的数据、是否诚实标注了外推区间才是它能否通过审稿的关卡。OpenResearch 将这类图收敛到orx-figures技能的统一规范中凡是性能如何随规模变化、趋势预示什么的图就读取 scaling.md 这一份参考详见 SKILL.md 中的参考路由表而所有参考文档共享同一个被 vendored 的风格模块 orx_figstyle.py。这正是一个图是一个论点而不是一张数组截图原则在缩放律场景下的落地。用对函数形式三参数幂律 L(N) E A·N^(-α)对于一个有地板floor的指标——损失、误差——两参数幂律L A·N^(-α)在 N 很大时是错的它趋近于零而真实损失不会。因此必须拟合三参数形式L(N) E A · N^(-α)其中E 是不可约损失irreducible loss数据的熵、贝叶斯误差对应的下界A 是振幅α 是指数——而你报告的正是 α如果强行令 E 0拟合会被掰弯并把 α 压平flatten而 α 恰恰是你对外报告的那个参数。所以E 必须一起拟合。两条拟合工程上的硬性要求在 L 上拟合误差而不是在 log(L − E) 上。后者被最接近地板的那几个 run 主导会让拟合塌缩到 E 0 的病态解如果拟合出的 E 等于 0 或等于 min(L)说明这批 run 根本没有约束住地板——应当如实说明runs 未约束地板而不是把边界值当作测量结果报出去。α 必须带区间并且要画在图上报告 α 要带区间——对 run 做一次 bootstrap 就足够——而且要把区间放进图里不能只写在正文里。一个没有不确定性的缩放指数等于主动邀请审稿人问它是由几个点拟合出来的。OpenResearch 的模板脚本默认对拟合 run 做 200 次重采样 bootstrap取 2.5% 与 97.5% 分位数作为 95% 区间并把区间和参与拟合的 run 数一起以文本形式打印在主面板内。什么数据点有资格进入拟合原文档对进不进拟合给了三条边界缺一不可只放一个 regime。欠训练under-trained、受 warmup 限制、或撞上其他瓶颈的 run属于另一条定律。它们如果有趣可以画出来但必须从拟合中排除并明确说明哪些点参与了拟合至少 4 个点理想情况下跨越一个数量级以上。少于 4 个点、或全部挤在 3 倍范围之内那叫趋势线a trend line不叫缩放律——请按趋势线来标注run 必须可比同样的数据、同样的 tokenizer、同样的评测。一张缩放图若在暗中混入两个不同的评测集它就是一张看起来正常实则破碎的图。外推必须看起来像外推如果这张图的目的是预测一个你没跑过的规模那么拟合线在已拟合区间上画实线在超出区间之外画虚线并对外推区域加阴影。一条实线径直冲出图右边界等于在声称那里有测量——而实际上那里什么都没有。OpenResearch 模板用ax.axvspan对预测上限区域加淡色阴影、用linestyle--画虚线从视觉上把测量与预言严格分开。六类常见陷阱速查表原文档用一张表格总结了缩放律图最常见的翻车方式这里完整保留陷阱修正刻度标签写成10^8、10^9网格用 10 的幂次是对的但标签要写成100M、1B用si_ticks画了拟合线却隐藏残差在主面板下方加残差条——残差出现弯曲意味着函数形式错了显示了散点却不说明哪些被拟合拟合点用实心、排除点用空心并在题注里说明两轴都是对数却只标了一个对数轴在这里是惯例但仍要写明单位α只出现在题注里把拟合方程直接打印在面板里——它才是结果不同模型家族用同一种颜色每个家族一种标记形状家族之间的定律可能不同陷阱背后的风格模块实现这张表不是口号——每一项都能在 orx_figstyle.py 里找到对应实现si_ticks(ax, which)把刻度格式化为1.2k / 340M / 7B而不是1.2e3 / 3.4e8当对数轴跨度不足一个数量级时minor ticks 也会套用同一格式化器否则 matplotlib 会打印出1.85 x 10^0这种丑陋标签残差面板由figure_grid(nrows2, ncols1, ...)构建主面板与残差条共享 x 轴sharexTrue高度比 3:1颜色纪律内建在PALETTEOkabe-Ito 色盲安全八色与BASELINE #7F7F7F中基线、参照线一律灰色调色板颜色只属于被比较的对象save()的审计行会检查打印宽度、Type 42 字体嵌入、多余的 axes title、缺失轴标签、低于 5pt 的文字、文字重叠与文字跑出画布输出clean才算过关详见 SKILL.md 的 Before you hand it over 一节。模板脚本逐段拆解figs/scaling.py原文档的模板是figs/scaling.py读取figs/scaling.csv列family,n_params,loss,fit其中fit为1表示该点参与拟合。下面先给出完整可运行的模板代码再逐段拆解其工程决策。Loss vs. parameters with a fitted power law. Regenerate: python figs/scaling.py import csv import numpy as np from orx_figstyle import BASELINE, PALETTE, TEXT, figure_grid, save, si_ticks, use_style DATA figs/scaling.csv PREDICT_TO 1e11 # extrapolate the fit out to this many parameters def load(path): n, loss, fitted, family [], [], [], [] with open(path) as handle: for row in csv.DictReader(handle): n.append(float(row[n_params])) loss.append(float(row[loss])) fitted.append(row[fit] 1) family.append(row[family]) return np.array(n), np.array(loss), np.array(fitted), np.array(family) def fit_power_law(n, loss, alphasnp.linspace(0.02, 1.5, 800)): Least squares for L E A*N^-alpha. For a fixed alpha the model is linear in (E, A), so scanning alpha and solving each slice exactly needs no nonlinear optimizer. It also fits the error on L rather than on log(L - E), which is dominated by whichever runs sit closest to the floor and collapses the fit to E 0. best None for alpha in alphas: design np.stack([np.ones_like(n), n**-alpha], axis1) coef, *_ np.linalg.lstsq(design, loss, rcondNone) if coef[0] 0 or coef[1] 0: continue # a negative floor or amplitude is not a scaling law sse float(((design coef - loss) ** 2).sum()) if best is None or sse best[0]: best (sse, float(coef[0]), float(coef[1]), float(alpha)) if best is None: raise ValueError(no fit with a non-negative irreducible term) _, e, a, alpha best return e, a, alpha def bootstrap_alpha(n, loss, draws200, seed0): rng np.random.default_rng(seed) alphas [] for _ in range(draws): idx rng.integers(0, len(n), len(n)) if len(set(idx.tolist())) 3: continue try: alphas.append(fit_power_law(n[idx], loss[idx])[2]) except ValueError: continue # a degenerate resample is not a reason to lose the figure if len(alphas) draws // 4: raise ValueError(fonly {len(alphas)}/{draws} resamples fit; too few runs for an interval) return np.percentile(alphas, [2.5, 97.5]) def main(): use_style() n, loss, fitted, family load(DATA) e, a, alpha fit_power_law(n[fitted], loss[fitted]) lo, hi bootstrap_alpha(n[fitted], loss[fitted]) fig, (ax, res) figure_grid( nrows2, ncols1, widthTEXT, ratio0.62, sharexTrue, gridspec_kw{height_ratios: [3, 1]}, ) def curve(x): return e a * x**-alpha measured np.geomspace(n[fitted].min(), n[fitted].max(), 100) beyond np.geomspace(n[fitted].max(), PREDICT_TO, 100) ax.plot(measured, curve(measured), colorBASELINE, zorder1) ax.plot(beyond, curve(beyond), colorBASELINE, linestyle--, zorder1) ax.axvspan(n[fitted].max(), PREDICT_TO, colorBASELINE, alpha0.08, linewidth0) markers (o, s, ^, v, P, X) families sorted(set(family)) if len(families) len(markers): raise ValueError(f{len(families)} families, {len(markers)} markers: add more) for marker, name in zip(markers, families): pick family name ax.scatter(n[pick fitted], loss[pick fitted], markermarker, colorPALETTE[blue], labelname, zorder2) ax.scatter(n[pick ~fitted], loss[pick ~fitted], markermarker, facecolornone, edgecolorPALETTE[blue], linewidth0.7, zorder2) ax.set_xscale(log) ax.set_yscale(log) ax.set_ylabel(Validation loss) ax.set_xlim(rightPREDICT_TO) ax.text( 0.03, 0.06, rf$L {e:.2f} {a:.1f}\,N^{{-{alpha:.3f}}}$ \n rf$\alpha \in [{lo:.3f},\ {hi:.3f}]$, {int(fitted.sum())} runs, transformax.transAxes, vabottom, fontsize7, ) if len(set(family)) 1: ax.legend(locupper right) res.axhline(0, colorBASELINE, linewidth0.6) res.scatter(n[fitted], 100 * (loss[fitted] - curve(n[fitted])) / loss[fitted], colorPALETTE[blue], s8) res.set_ylabel(Resid. (%)) res.set_xlabel(Non-embedding parameters) si_ticks(ax, y) si_ticks(res, x) save(fig, figs/scaling) if __name__ __main__: main()拆解一fit_power_law—— 线性最小二乘 网格扫描无需非线性优化器关键洞察在于固定 α 之后模型对 (E, A) 是线性的。因此模板在alphasnp.linspace(0.02, 1.5, 800)上扫描 α对每个切片用np.linalg.lstsq精确求解 (E, A)再比较残差平方和SSE选出全局最优。这一设计同时满足两个正确性约束拟合的是L 上的误差(design coef - loss)而不是log(L - E)上的误差——后者会被最接近地板的 run 主导并塌缩到 E 0硬性拒绝病态解coef[0] 0负地板或coef[1] 0非正振幅直接continue——负地板或负振幅不是缩放律若所有 α 都被拒绝则抛出ValueError。拆解二bootstrap_alpha—— 有放回重采样的 95% 区间对拟合用的 run 做有放回重采样bootstrap每个重采样都重新执行完整拟合并记录 α。三条防御性逻辑值得注意重采样样本中不同索引少于 3 个时跳过退化的重采样拟合抛ValueError时跳过而不是中断——一个退化重采样不该毁掉整张图如果成功重采样数少于draws // 4说明 run 太少、不足以给出区间直接报错too few runs for an interval。最终取 α 分布的 2.5% 与 97.5% 分位数作为区间端点。拆解三双面板图 —— 主面板 残差条figure_grid(nrows2, ncols1, widthTEXT, ratio0.62, sharexTrue, height_ratios[3, 1])构建主面板 残差条残差以百分比形式100 * (loss - curve) / loss绘制并画出res.axhline(0)零线。残差条的作用是让函数形式错了无处遁形——如果残差呈系统性弯曲说明L E A·N^(-α)这个形式本身不成立。在 orx_figstyle.py 中figure_grid以最终印刷宽度TEXT 5.5 英寸构建整张网格高度由ratio决定。拆解四把结果印进图里而不是只写进题注主面板左上角用ax.text打印拟合方程L E A·N^(-α)带实际数值α 的 95% 区间与参与拟合的 run 数。这正是陷阱表里α只出现在题注里的修正拟合方程就是结果它必须活在面板里。拆解五外推与刻度拟合区间内用实线linestyle默认区间外到PREDICT_TO 1e11用虚线--并用ax.axvspan淡色阴影alpha0.08标出外推区两轴set_xscale(log)/set_yscale(log)y 轴标签写明 Validation lossx 轴标签写明 Non-embedding parameters避免两轴对数只标一个的陷阱收尾时si_ticks(ax, y)与si_ticks(res, x)把刻度刷成100M/1B风格每个模型家族一个标记形状o/s/^/v/P/X超过 6 个家族直接抛错要求扩充标记——防止不同家族混用一种颜色。拆解六数据加载与拟合/排除点的视觉区分CSV 用fit列区分参与拟合与否拟合点实心、排除点空心facecolornone加描边。模板规定画出哪些点被拟合、哪些被排除必须同时反映在点样式与题注中因为排除点与拟合点混在一起会破坏图的辩护力。数据从哪来orx logs 证据链缩放律图有一条不证自明的纪律图中每个数字都必须来自一次真实 run。orx-figures技能的非协商条款第 3 条明确写道永远不要画一个记忆中的、四舍五入的或貌似合理的数字绝不让合成的演示数据留在要发布的脚本里。数据的来源通道是orx logs配套的 orx-evidence 技能 规定了完整用法orx logs runId # tail末尾——通常正是你想要的 orx logs runId --head # 从头读起 orx logs runId --bytes 200000 # 提高字节上限默认 64 KB最大 1 MB orx logs runId --range 4096:8192 # 精确字节窗口 [start, end)runId来自orx runs projectId日志正文输出到stdout方便| grep或重定向[source] bytes a–b of N状态行输出到stderr。其 Rust 实现位于 src/commands/logs.rs默认tail模式、--head从头读取、--range start:end解析字节窗口要求end start否则退出码 1--bytes接受整数字节上限。证据纪律的落点是报告前先验证确认日志能识别变体与有效配置、最终指标与紧凑摘要都在、长 run 的轨迹可恢复、返回的字节窗口确实包含支撑输出的内容。被截断的输出不是不存在的证据——用--head/--bytes/--range读到相关部分为止。这一点直接决定了缩放律图上的每个 (n_params, loss) 点都站得住脚。如何安装与运行模板在 OpenResearch 中orx-figures是随 CLI 打包的内置技能模块。其资源装配逻辑位于 src/local/agent_skills.rsorx-figures技能捆绑了 6 份参考文档references/curves.md、references/scaling.md、references/comparison.md、references/pareto.md、references/matrix.md、references/diagram.md以及风格模块 orx_figstyle.py 与 TikZ 前导 orx-tikz-preamble.tex。仓库测试figures_skill_ships_its_style_module_and_routes_by_figure_type见 agent_skills.rs专门校验所有参考文档与风格模块必须随技能一起发布——如果参考文档缺了它们都 import 的风格模块参考就一文不值。技能命令入口在 src/commands/skill.rsorx skill figures打印技能总览orx skill figures/name如orx skill figures/scaling打印对应参考文档原文。首次使用需把风格模块 vendored 到图脚本旁边保证脚本脱离本会话后依然可复现mkdir -p figs orx skill figures/assets/orx_figstyle.py figs/orx_figstyle.py从任意目录执行都有效导入前先确认文件非空重定向会先创建目标文件按会话 playbook 的 Python 策略独立绘图使用 uv 隔离环境uv run --no-project --with matplotlib --with numpy python figs/scaling.py若脚本带有内联依赖元数据可简写为uv run --no-project figs/scaling.py导入项目代码的绘图脚本则必须在项目环境内运行。生成脚本与产物放在一起figs/scaling.py→figs/scaling.pdf、figs/scaling.svg。参照 SKILL.md 的目的地规则论文用图放工作区figs/并以仓库相对路径引用file pathfigs/scaling.pdf /不带artifacts/前缀报告或聊天回答的图则按orx-reports规则写入 artifacts 目录下对应主题文件夹。脚本丢失的图无法在审稿人要求再补一个 seed时修正——生成脚本必须随图一起交付。运行后务必阅读save()打印的审计行它检查打印宽度、Type 42 字体嵌入、多余 axes title、缺失轴标签、低于 5pt 的文字、文字重叠与文字跑出画布clean是及格线。若审计报告机器上没有出版字体如实转达而不是擅自安装字体那属于环境变更。最后打开 PDF 按印刷尺寸阅读——屏幕上 100% 都读不清的刻度标签印到纸上同样读不清。交付前的检查清单原文档的 checklist 是缩放律图的最终质检标准完整保留如下拟合形式包含不可约项且 E 已被报告α 带有区间且参与拟合的 run 数被写明排除的点在视觉上可区分且排除理由被说明外推部分是虚线与阴影没有任何实线离开数据区间残差面板显示拟合没有系统性弯曲轴刻度读作100M/1B而不是10^8。逐项勾完这张缩放律图才算可辩护形式正确带地板的 E A·N^(-α)、数据干净单一 regime、可比 run、不确定性可见α 区间、run 数、外推诚实虚线 阴影 残差面板。这正是 OpenResearch 对把 coding agents 变成 research agents在可视化维度上的标准答案——图上的每个主张都必须经得起追问而这份文档与模板就是为承受追问而设计的。【免费下载链接】OpenResearchTurn your coding agents into research agents项目地址: https://gitcode.com/GitHub_Trending/op/OpenResearch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表