主题
第22章 分章环境与复现:把“能读懂”变成“知道验证了什么”
22.1 同一个项目,不等于同一个运行环境
准备复现 Happy-LLM 时,先确定要运行哪一章,再准备对应环境。项目同时包含从头实现、框架微调、RAG/Agent 和远端 RL 客户端,把所有依赖一次装进同一个环境,可能还没开始实验就遇到版本冲突。上游《学习与环境准备》建议第 2–7 章优先使用独立的 Python 3.10/3.11 环境,第 8 章使用 Python 3.13。这是固定提交中的建议;本书没有安装并验证这些章节的全套环境。
第 8 章的 requirements 固定了 pytrio==0.2.6、swanlab==0.9.2 和 deepseek-search 的一个 Git 提交,其余还有多个最低版本约束。这样的清单仍不是完整 lockfile:未固定的依赖及传递依赖都可能变化,numpy、torch、matplotlib 等版本下界也可能与手头的 Python 环境不兼容。安装前应先检查依赖能否解析、平台是否支持,避免在共享环境中反复升级。
环境准备还要区分“本地执行哪一步”和“完整实验依赖什么”。本书已有证据的范围如下:
| 工作 | 本地负责 | 外部/硬件条件 | 本书证据 |
|---|---|---|---|
| 数学与mask检查 | 小数组、断言 | 标准库或CPU数值环境 | 已运行的配套输出 |
| GRPO/OPD在线训练 | 数据与请求编排 | PyTRIO账号、网络、计费服务 | 源码分析,未提交任务 |
| Search-R1完整实验 | 状态机、搜索环境 | 搜索后端、远端模型 | 固定fixture和代码审读 |
| ReTool完整实验 | 调度与工具环境 | 可信隔离、远端模型 | 不执行生成代码 |
| VLM/s1真实推理训练 | 预处理和调度 | 权重、合适加速器/引擎 | 未下载、未跑GPU |
| CDDRS端到端 | 文件与检索编排 | embedding权重、LLM接口 | 数学和协议反例 |
以第 8 章为例,本地 CPU 可以运行客户端,并不意味着它承担了远端大模型的训练计算。rollout、teacher logprob、前后向计算和工具接口等都可能产生费用;关闭 SwanLab 只会关闭实验记录,不会关闭 PyTRIO 的训练计费。因此,没有提交远端任务时,记录中应明确写出这一点,不能称为“完成一步 Qwen 强化学习”。
22.2 三个层次的验证,别互相替代
判断一次检查究竟说明了什么,可以先分清三个层次。
第一层是静态检查:语法能否解析、依赖清单能否阅读、符号能否定位。Python compile 成功,只回答了语法层面的问题,尚不能证明 import 成功,更不能证明训练已经执行。Notebook 还可能依赖运行顺序、旧变量、外部目录或 API 凭证。评价端到端执行需要从干净 kernel 按顺序运行;本书对会产生下载或费用的单元明确不执行。
第二层是离线契约测试:给定固定输入,检查数组 shape、下一个 token 的对齐、解析结果、异常分支、预算和奖励是否符合约定。这里的 fixture 是预先写好的可控替身。假 embedding 可以检查固定向量下的排序计算,却不能说明模型是否理解语言;假工具可以检查调用关联有没有串号,却不能说明真实工具会返回有用证据。
第三层才是在匹配资源下的实测:使用真实模型、数据、硬件和服务版本,记录实际结果。为了让别人能核对,需要留下题集与划分、权重 revision、分词器、种子、采样设置、上下文预算、优化器状态、时间范围和失败样本。
这三层回答的问题不同,不能互相替代。本书尚未获得的结果会留空并说明所需条件,不把课程截图、论文表格或单次演示改称“本次性能”。
22.3 可移植地运行本书的小实验
如果想先验证不依赖模型服务的小例子,可以从两份配套脚本开始:第 13–16 章的 examples/13_16_offline_rl.py,以及第 20–21 章的 examples/20_21_offline_retrieval.py。它们只使用 Python 标准库,可在普通 Python 3.10 及以上环境中,从书籍根目录运行:
bash
python examples/13_16_offline_rl.py
python examples/20_21_offline_retrieval.py这两份脚本不依赖另一部书的目录,不读取 API Key,不创建网络客户端,也不使用 eval/exec 执行传入代码。输入完全固定,没有随机采样,因此不需要额外固定 seed。
随书保存的两份输出记录了 Python 3.12.14,这是生成那些记录时的解释器版本。涉及数值环境的其他章节分别记录自己的版本,不能把整个工程归为同一种运行环境。
保存的 qa/13_16_offline_rl.json 记录了 GRPO 优势 [0.5,−0.5,0.5,−0.5]、PPO 正优势项 0.6/负优势项 −0.75、精确两类 reverse KL 0.1927447570、工具观察的零优势位置,以及微批全局均值 5.0。
另一份 qa/20_21_offline_retrieval.json 记录了预算 8 恰好用尽、请求额度 [8,4]、贪婪路径概率 0.306 与另一候选 0.396、融合分 [0.55,0.8]、短块原版 coherence=0,以及稳定去重后的排序。数值可以回到对应脚本中核对;其中有些断言检查精确结果,有些只检查大小关系、归一化或对齐等性质。all_assertions_passed 表示脚本中的断言通过,不能作为大模型任务成功的标记。
如果修改脚本,就应另外保存新输出、脚本 SHA-256 和修改说明,避免新旧结果混在一起。版本号帮助定位环境,哈希帮助确认文件身份,但它们都不能单独证明算法正确。
遇到错误时,报告至少应包括固定 commit、文件和函数、输入、期望结果、实际结果、完整异常、依赖与平台,以及是否联网、是否使用 GPU。对外反馈前,先去掉密钥、私人数据和不可分享的日志。
22.4 文件、函数与单元的覆盖怎样查
想知道某段解释对应哪个源文件,可以查配套包根目录的 source_coverage.tsv。它记录固定源链接、文件或单元哈希、函数或单元定位与正文节号,并区分解释、重复映射、资源附件和未运行路径。索引只保留核对所需的定位信息,不附未经整理的源内容抽取。
查覆盖范围时,还要留意重复展示。同一个函数可能同时出现在 readme 和 notebook 中,出现两次不代表实现了两项独立能力。
图像、日志、配置和网页外壳也需要说明用途。上游图像可以作为结果或结构示意登记在索引中,但本书优先使用自绘图,避免把第三方插图无条件纳入改编许可。上游运行日志用于帮助理解演示,不能当作本书重跑的结果。配置文件则应解释字段作用、默认值风险和对应入口,只有文件名还不足以说明覆盖了什么。
22.5 导航、反馈与许可也是工程的一部分
阅读源码之外,也需要知道学习入口和反馈入口在哪里。README.md 与 README_en.md 介绍学习路线、模型资源、参与者和授权;docs/README.md 是文档站入口,docs/_sidebar.md 决定阅读导航。Extra-Chapter/Readme.md 列出扩展主题与贡献,但这些外链项目并未全部纳入本书。本书遵循固定的覆盖范围,不额外下载 self-llm、agentic-rl-lab 或出版 PDF。
仓库的 .gitignore 列出缓存、虚拟环境和本地配置等不纳入 Git 的文件模式;它不会改变模型计算,也不会自动删除已经跟踪的文件。docs/.nojekyll 是静态文档站的发布标记,同样不属于训练配置。
根目录和文档目录中的项目横幅、二维码及 star-history 图,分别服务于项目介绍、联络和历史关注度展示。它们不构成模型效果证据,本书只在来源索引登记这些资产及其重复副本,不再发布原图。
docs/index.html 负责 Docsify 页面、主题、暗色样式和网页加载脚本,影响的是浏览排版与外部 CDN 依赖,不参与模型训练。若直接打开源 Markdown 离线阅读,可以避开网页的外部资源,但仍需保留图片相对路径。反馈时也应把导航链接损坏与算法错误分开,方便定位问题。
反馈配置位于 .github/ISSUE_TEMPLATE/book_issue.yml 和 config.yml。前者要求提供章节、问题描述、复现材料与去重确认,后者关闭空白 issue,并指向文档和反馈入口。这些文件用于收集问题,不能作为 CI 测试已经运行的证据;仅仅看到 .github 目录,也无法推断固定清单中存在训练验证工作流。
提交反馈需要明确授权。本书提供可复查证据,没有替读者发布 issue。
使用材料时,还应核对许可。上游 LICENSE.txt 和 README 采用 CC BY-NC-SA 4.0;代码是 Python、仓库可以在 GitHub 上公开访问,都不意味着可以改用 MIT。本书保留 Datawhale、项目负责人及扩展作者归属,说明重写、补充推导、自绘图和离线替身等修改,并按相同许可提供改编。
这一许可不会替第三方图片、模型权重、数据集或服务条款授权,每项资源仍需查看自己的许可。出版书即使可以公开浏览,也不能据此全文搬运或翻译。
22.6 从错误出发安排下一次实验
下一次实验应从已经发现的错误出发。若错误发生在 token/logprob 长度检查,先用离线数组重现,比立即扩大 batch、重新提交收费任务更容易定位原因。检索返回空时,则需要沿着数据路径检查:文档有没有读入,分块是否丢了内容,阈值是否过高,还是确实缺少证据。
另外两类问题也有各自的检查入口。预算循环迟迟不结束,先检查预算是否单调消耗,以及没有进展时能否退出;训练指标看似变好,先核对奖励定义和评测口径有没有变化。
补充练习:一个脚本通过 compile,却在运行 from pytrio import… 时失败,覆盖报告应如何写?
答案:静态语法通过,依赖导入未通过,训练未运行。这样才能让读者看清停在哪一步,不能缩写成笼统的“测试通过”。
补充练习:课程原输出、本地 fixture 输出和论文准确率,怎样放在同一本书里?
答案:分别标注来源、设置与证据类型,不合并平均,不比较吞吐,也不用同一个“实测”表头混装。
完成这些小检查后,再安排真实训练:写清想验证的假设、所需资源、成本上限、终止条件,以及什么证据足以支持结论。这样,即使下一次实验失败,也能知道它排除了什么、还留下了什么问题。
来源与改编说明
本章依据 Datawhale Happy-LLM 固定提交的docs/学习与环境准备.md,以原创叙述、推导和离线反例扩写。第八章实现原署名及 agentic-rl-lab 来源见上游 README;Extra-Chapter 作者归属保留在全书授权页。本章与配套改编采用 CC BY-NC-SA 4.0,仅非商业使用,不代表原作者认可。文中的本地结果只证明指定离线检查通过,不代表复现上游模型效果。