OpenResearch 学习曲线与训练曲线绘制指南:从 x 轴选择到种子聚合的完整规范
OpenResearch 学习曲线与训练曲线绘制指南从 x 轴选择到种子聚合的完整规范【免费下载链接】OpenResearchTurn your coding agents into research agents项目地址: https://gitcode.com/GitHub_Trending/op/OpenResearch本文是 OpenResearch 项目orx-figures技能中关于学习曲线learning / training curves绘制的技术指南。它回答两个核心问题指标随训练如何变化以及两条运行曲线之间的差距是否大于种子间噪声。文中给出 x 轴公平性判断表、种子聚合与置信带规范、平滑处理的证据边界、常见陷阱清单并附一份可直接复用的figs/loss_curve.py模板。读完本文你将能绘制出经得起评审的验证损失曲线并理解 OpenResearch 仓库中orx_figstyle.py样式模块与orx logs证据链路的底层实现。学习曲线是论文中被画错频率最高的图因为它误导人的两种方式在成图之后是看不见的错误的 x 轴以及单一种子。本文档agent-skills/orx-figures/references/curves.md正是围绕这两条主线展开的完整规范本文结合仓库源码与真实 demo 数据对其进行深入展开。先选 x 轴再谈其他x 轴决定了比较的含义。优化器steps只有在每条曲线每步做相同工作量的前提下才是公平的轴。一旦批量大小、序列长度、模型规模或硬件在曲线之间发生变化steps 就不再可比必须改画真正发生差异的量比较场景绘制轴相同配置、不同种子或代码改动Optimizer steps不同批量大小或序列长度看到的 Tokens或 examples不同模型规模FLOPs或在每条曲线上标注规模后再用 tokens速度或效率声明墙钟时间并注明硬件一个用一半步数收敛的方法如果每步耗时两倍它并不更快——而 steps 轴的图恰好掩盖了这一点。因此在 demo/nanochat/evidence/training-metrics.csv 这类真实记录中除了step外还会同步记录tokens_per_second与total_minutes正是为了让按 tokens 还是按时间比较成为可选项而不是事后无法回答的问题。在模板中x 轴最终被设置为Tokens seen见下文模板ax.set_xlabel(Tokens seen)并配合si_ticks(ax, x)把刻度格式化为1.2k / 340M / 7B而非科学计数法——这是 agent-skills/orx-figures/assets/orx_figstyle.py 中si_ticks()函数的行为tokens 量级动辄百万、十亿直接打印1e8对读者没有直觉意义。聚合种子绝不挑一个至少跑3 个种子如果效应较小则跑5 个。均值画线95% 区间以同色低透明度色带表示样式模块中的band()。在题注中写明 n种子数。两个关键提醒±1 标准差带回答的是种子有多分散而不是均值估计得有多准。必须用区间。这正是orx_figstyle.py中mean_ci()的实现逻辑它返回均值与 95% t 区间而不是标准差带。如果种子在定性上分歧——一个发散、其余没有——那本身就是结果。应把各次运行画成细线展示分歧而不是把分歧藏进一条宽色带里。公共网格插值平均前的必要步骤种子几乎不会在完全相同的 step 网格上记录日志。平均之前必须插值到公共网格直接平均参差不齐的数组会静默截断到最短的那条。mean_ci()的实现揭示了区间背后的细节使用t 分布临界值而非正态近似种子数量通常很小正态近似会低估区间。_T95字典按自由度表列了双侧 95% 临界值_t95(df)对自由度向下取整而不是取最近的键因为更小的 df 给出更大的 t区间宁可偏宽也不能高估少数几个种子对均值的解析能力。单种子时返回(mean, mean, mean)并发出警告 one seed: the band is a line, not an interval — say single seed in the caption即要求你如实声明而非暗示更多。band()内部调用mean_ci()后用fill_between以alpha0.18绘制同色带。而模板中的load()函数则处理了插值的边界问题first max(curve[0][0] for curve in curves) last min(curve[-1][0] for curve in curves) if first last: raise ValueError(f{variant}: seeds cover no common range of steps) grid grid[(grid first) (grid last)]只在整个种子共同达到的 step 范围内插值原因注释里写得很清楚np.interp对输入范围外会钳制clamp如果一条运行提前停止钳制会为它伪造一条平坦尾巴并人为收窄色带。若种子覆盖的步数毫无交集直接raise ValueError而不是默默画一条无意义的曲线。平滑是对数据的断言训练损失有噪声不平滑的曲线可能难以阅读但平滑是对证据的一种变换在题注中始终声明平滑方式例如 EMA, α0.1。把原始序列以浅色画在平滑线之下。单独一条平滑线会让读者误以为该运行真的那么稳定。绝不平滑已经稀疏的评估指标——那是在真实测量点之间凭空发明数据点。陷阱速查表陷阱修复Y 轴截断让小差距显得决定性强包含真正重要的范围若缩放了就在题注中说明对数刻度轴而不声明在标签上声明。指标跨多个数量级或声明涉及比率时用 log-y从 4.7 到 1.6 的有界损失更适合线性刻度图例遮挡了感兴趣区域在线的右端直接标注label_ends图例是查找表不是默认选项单个面板塞太多曲线只展示支撑论断的 4 条其余放进附录表格基线用了调色板颜色基线与 chance level 一律灰色BASELINE——颜色只属于被比较的对象曲线 x 范围不同却不加说明说明某条为何提前停止发散、预算耗尽其中基线与 chance level 用灰色直接对应orx_figstyle.py中的定义BASELINE #7F7F7F与MUTED #CFCFCF注释明确指出颜色属于被比较的事物。调色板采用 Okabe-Ito 八色blue/orange/green/red/purple/cyan/yellow/black对常见色盲与灰度打印都可区分CYCLE取前六色作为 matplotlib 的 prop_cycle。label_ends()的实现也印证了图例是查找表的立场它直接在每条线右端用线的颜色标注文本并按标签自身高度动态计算间距gap_px max(min_gap * dpi / 72, max(heights) * 1.15)标签汇聚时自动推开线右端的数据点被用于锚定标注且右 x 限会扩展出恰好容纳标签的宽度——所以模板中si_ticks(ax, x)必须放在label_ends()之后调用label_ends会移动右限。完整模板figs/loss_curve.py模板读取一份由orx logs导出的整洁 CSV列为variant,seed,step,value。orx logs是 OpenResearch 的证据读取通道见 agent-skills/orx-evidence/SKILL.mdorx logs runId尾部读取支持--head、--bytes 200000默认 64 KB最大 1 MB与--range 4096:8192精确字节窗口。仓库中真实存在的 demo/nanochat/evidence/training-metrics.csv 展示了这类 CSV 的形态phase,step,loss,validation_bpb,tokens_per_second,total_minutesbase 与 SFT 两个 phase 各按 step 记录 loss与模板要求的 tidy 格式一脉相承。Validation loss over training. Regenerate: python figs/loss_curve.py import csv from collections import defaultdict import numpy as np from orx_figstyle import BASELINE, COLUMN, band, figure, label_ends, save, si_ticks, use_style DATA figs/loss_curve.csv def load(path): - {variant: (steps, runs)} with runs shaped (n_seeds, n_steps). raw defaultdict(lambda: defaultdict(list)) with open(path) as handle: for row in csv.DictReader(handle): raw[row[variant]][row[seed]].append((float(row[step]), float(row[value]))) series {} for variant, seeds in raw.items(): curves [sorted(points) for points in seeds.values()] grid np.array([step for step, _ in curves[0]]) # Seeds log on slightly different grids, so interpolate before # averaging — but only across the range every seed actually reached. # np.interp clamps outside its input, which would invent a flat tail # for a run that stopped early and narrow the band around it. first max(curve[0][0] for curve in curves) last min(curve[-1][0] for curve in curves) if first last: raise ValueError(f{variant}: seeds cover no common range of steps) grid grid[(grid first) (grid last)] runs np.stack([ np.interp(grid, [s for s, _ in curve], [v for _, v in curve]) for curve in curves ]) series[variant] (grid, runs) return series def main(): use_style() series load(DATA) fig, ax figure(widthCOLUMN, ratio0.72) lines, labels [], [] for variant, (steps, runs) in series.items(): color BASELINE if variant baseline else None lines.append(band(ax, steps, runs, colorcolor)) labels.append(f{variant} (n{runs.shape[0]})) ax.set_xlabel(Tokens seen) ax.set_ylabel(Validation loss) label_ends(ax, lines, labels) si_ticks(ax, x) # after label_ends, which moves the right limit save(fig, figs/loss_curve) if __name__ __main__: main()几个需要展开的调用点use_style()一次性应用项目的 rcParams见orx_figstyle.py。默认无衬线字体栈Helvetica 指标系列按机器可用性回退到 TeX Gyre Heros / Liberation Sans / DejaVu Sansmathtext.fontset默认stixsans与文本字体配对pdf.fonttype: 42与ps.fonttype: 42嵌入 TrueType 轮廓arXiv 拒绝 Type 3 字体字体基准 8pt、刻度标签 7pt、savefig.dpi600。需要衬线时传use_style(familyserif)。figure(widthCOLUMN, ratio0.72)按最终印刷尺寸构建画布。COLUMN 3.25英寸双栏论文的一栏ICML/CVPR/IEEETEXT 5.5单栏论文文本宽NeurIPS/articleWIDE 6.75双栏论文的figure*两栏宽。模板用ratio0.72控制高度。band(ax, steps, runs, colorcolor)绘制种子均值线与 95% 区间带基线变体自动用BASELINE灰色。save(fig, figs/loss_curve)同时写 PDF供\includegraphics与 SVG供预览不使用bbox_inchestight——裁切会改变物理宽度破坏按最终尺寸构建的原则。保存后执行审计检查印刷宽度是否为已知列宽、Type 42 字体嵌入、多余的 axes 标题、缺失轴标签、低于 5pt 的文本、文本互相重叠或跑出画布结果打印为figure audit stem: clean或列出问题清单。运行模板根据 agent-skills/orx-figures/SKILL.md 的说明mkdir -p figs orx skill figures/assets/orx_figstyle.py figs/orx_figstyle.py把样式模块 vendored 到图脚本旁边保证会话结束后图仍可复现报告脚本放在 artifacts 目录下时那里需要自己的副本否则 import 会失败重定向会先创建目标文件再执行查找导入前请确认文件非空。独立绘图不导入项目代码用 uv 运行uv run --no-project --with matplotlib --with numpy python figs/loss_curve.py若脚本内联了依赖元数据可用uv run --no-project figs/loss_curve.py让 uv 读取元数据。导入项目代码的绘图必须在项目环境中运行而不是上面的隔离环境。题注与图的位置模板的save()输出到figs/这是论文目的地。在 LaTeX 中以width\linewidth引入\begin{figure}[t] % figure* for a WIDE figure in a two-column paper \centering \includegraphics[width\linewidth]{figs/loss_curve.pdf} \caption{Validation loss over training, mean of 5 seeds with 95\% intervals.} \label{fig:loss} \end{figure}按列宽构建并且写width\linewidth两者缺一不可尺寸正确时\linewidth的缩放系数恰为 1.0不会改变任何东西但在目标期刊栏宽比预期窄时仍能适配它救不了按错误尺寸构建的图——15 英寸画布塞进 5.5 英寸栏会被缩到 0.3511pt 刻度标签落到 4pt。题注要随图一起写以一句加粗的论断开头再补上读者信任它所需的细节——种子数、色带含义、任何平滑、归一化、拟合了哪些点/排除了哪些点、前沿线是实测还是示意。单面板图约 25–40 词。曲线类图尤其要在题注中写清均值 ± 95% 区间、n5、EMA α0.1这类方法事实。交付前清单x 轴对参与比较的运行是公平的且带单位标注≥3 个种子的均值 95% 色带n 写在题注里任何平滑都在题注中声明且原始序列可见基线是灰色彩色曲线最多 4 条y 范围诚实或缩放已在题注中声明对照orx_figstyle.py的审计逻辑还有两条额外约束值得记住axes 标题ax.set_title在论文图上会被审计标记为与题注重复而判为缺陷标签若压在数据点或参考线上导致与刻度标签重叠应移动它或给它backgroundcolorwhite。最后打开生成的 PDF 按印刷尺寸阅读——屏幕上 100% 都看不清的刻度标签印在纸上同样看不清。【免费下载链接】OpenResearchTurn your coding agents into research agents项目地址: https://gitcode.com/GitHub_Trending/op/OpenResearch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考