Compare commits
5 Commits
8647c5a89d
...
270a9ab291
| Author | SHA1 | Date | |
|---|---|---|---|
| 270a9ab291 | |||
| cccccc0ce6 | |||
| 72799a34dc | |||
| c9eddd5be8 | |||
| d0bedd564c |
@@ -0,0 +1,8 @@
|
||||
# 复制为 .env 并填入真实值(.env 已被 gitignore,严禁提交密钥)
|
||||
# teacher API(OpenAI 兼容格式,DeepSeek / MiniMax 二选一填)
|
||||
TEACHER_API_BASE=https://api.deepseek.com/v1
|
||||
TEACHER_API_KEY=
|
||||
TEACHER_MODEL=deepseek-chat
|
||||
|
||||
# W&B(仅远程训练需要)
|
||||
WANDB_API_KEY=
|
||||
@@ -49,10 +49,23 @@ conda activate ars-opd && ruff check ars_opd/ --fix && ruff format ars_opd/
|
||||
2. 重构一个模块前,先对照参考实现列出其全部行为(含 trick 和 workaround),逐一确认保留/替代/删除。
|
||||
3. 每个纯逻辑模块完成后,用 toy 数据对拍参考实现的对应逻辑(参考其 `validate_mc_estimator.py` / `validate_chunk_mc_estimator.py`)。
|
||||
4. 文档规范:优先表格与公式,代码块 ≤15 行(展示思路用伪代码,完整代码引用文件路径),引用参考实现必须带 `文件:行号`。
|
||||
5. **每层完成后做接口回看**:逐模块自问"接口是否比实现简单得多"(深模块判据);若某接口的参数/约定复杂到接近实现本身,先记录并重构,再进入下一层。规则来源与哲学对照见 `docs/appendix-claudemd-decisions.md`。
|
||||
|
||||
## 7. 代码规范
|
||||
## 7. 代码规范(教学导向)
|
||||
|
||||
- 公共函数完整类型注解;模块/类/函数写中文 docstring(功能、参数、返回、关键实现细节)。
|
||||
**注释分工**:`docs/` 章节负责讲原理,代码注释负责做索引,两者不重复。代码注释只写三类内容:
|
||||
|
||||
| 类型 | 要求 | 示例 |
|
||||
|------|------|------|
|
||||
| 论文锚点 | 实现论文公式/机制的函数,docstring 首行标出处;关键行旁给公式本体 | `# 式(5): π̂ = (k_sem + α·π̄) / (N + α)` |
|
||||
| 非显然约束 | 只解释"为什么必须这样"及违反后果,不解释"这行在干什么";load-bearing 的反直觉点必须写 | `# π̂ 必须 detach:否则优化器会压低学生自身概率把乘子 π̂ 推向 0 以逃逸惩罚,teacher 否定的 chunk 最先塌缩` |
|
||||
| 差异标注 | 凡有意偏离论文或参考实现处,注明对方做法与我们的理由 | `# 参考实现(trainer:2205)对 chunk 内取 mean,论文式(8)为 sum,此处从论文` |
|
||||
|
||||
**类型与 shape**:
|
||||
- 模块间公共接口(`ars_opd/` 各模块导出的函数/类)强制完整类型注解——接口注解本身就是教学信息;模块内私有 helper 从宽。
|
||||
- 类型注解表达不了张量 shape,故 shape 是硬要求:docstring 注明参数/返回的 shape,函数体内关键变换旁加行注释(如 `# (B, T, V) -> (B, T)`)。
|
||||
- docstring 用中文,含功能、参数、返回、关键实现细节。
|
||||
|
||||
**其余硬规则**:
|
||||
- **严禁** `except Exception: pass`;出错直接报错,不用默认值兜底。
|
||||
- 张量函数在 docstring 中注明各参数的 shape。
|
||||
- 提交信息用中文,说明"这一步对应哪一章/哪个模块"。
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
| 0 | 环境与骨架 | — | 本地/远程 conda 环境、gitea 同步、包骨架 | 两端 `pytest` 空跑通过 |
|
||||
| 1 | SFT 基线 | §3.1 式(1) | 数据管线 + 最小 SFT 训练脚本(Qwen3-0.6B) | 远程 4 卡跑通,loss 正常下降 |
|
||||
| 2 | White-box OPD 基线 | §3.1 式(2) | token 级反向 KL 蒸馏(teacher Qwen3-4B 本地 vLLM) | 远程跑通;理解式(2)梯度爆炸问题(§4.1) |
|
||||
| 3 | 相似度 + MC 估计器 | §3.2.1-3.2.2 式(3)(4)(5) | `similarity.py` + `estimator.py`(纯逻辑) | 本地 CPU 单测,对拍 `validate_mc_estimator.py` |
|
||||
| 3 | 相似度 + MC 估计器 | §3.2.1-3.2.2 式(3)(4)(5) | `similarity.py` + `estimator.py`(纯逻辑) | 本地 CPU 单测,对拍 `validate_mc_estimator.py`;detach 命门测试已预置(`tests/test_estimator_detach.py`),完成后需接入真实实现 |
|
||||
| 4 | Peak-entropy 调度器 | §3.2.3 式(6)(7) | `chunking.py`(纯逻辑) | 本地 CPU 单测:toy 熵序列上验证 chunk 选择与合并 |
|
||||
| 5 | 完整 OmniOPD | §3.2.4 式(8) | `teacher.py`(API 客户端+缓存)+ `trainer.py`(chunk 损失 + KL 锚定) | 远程端到端跑通(DeepSeek/MiniMax teacher) |
|
||||
| 6 | 评测与消融 | §5 | 数学评测脚本;三个消融开关 | MATH-500 子集上 student 有可测提升趋势 |
|
||||
|
||||
@@ -24,6 +24,19 @@ flowchart LR
|
||||
|
||||
$$\mathcal{L}_{\text{OmniOPD}}(\theta) = -\mathbb{E}_{\hat y\sim\pi_\theta}\Big[\sum_{c=1}^{M}\hat\pi^{(c)}_{\text{teacher}}\sum_{t\in c}\log\pi_\theta(y_t\mid x,y_{<t})\Big] + \beta\sum_{t\in\mathcal{U}} D_{KL}\big(\pi_{\text{ref}}\,\|\,\pi_\theta\big)$$
|
||||
|
||||
| 符号 | 含义 | 直观说法 |
|
||||
| --------------------------------- | ------------------------------------ | ------------------------------- |
|
||||
| $\pi_\theta$ | 学生模型($\theta$ 是它的参数,训练改的就是 $\theta$) | 正在被训练的 0.6B |
|
||||
| $\hat y \sim \pi_\theta$ | 轨迹是学生自己生成的 | “on-policy”三个字的全部含义 |
|
||||
| $\mathbb{E}[\cdot]$ | 期望 | 实践中 = 对 batch 里采样出的轨迹求平均,没有更多玄机 |
|
||||
| $c$,共 $M$ 个 | 被熵调度器选中的 chunk(各 $C=50$ 个 token) | 被“抽查”的 $M=10$ 段 |
|
||||
| $\hat\pi^{(c)}_{\text{teacher}}$ | 式(5)算出的贝叶斯估计,$\in [0,1]$ | 老师对这段的认可度打分 |
|
||||
| $\log \pi_\theta(y_t \mid \cdot)$ | 学生给自己当时生成的那个 token 的对数概率 | SFT 里最熟悉的那个量 |
|
||||
| $\mathcal{U}$ | 未被抽查的所有 token | 轨迹的绝大部分 |
|
||||
| $\pi_{\text{ref}}$ | 训练开始前学生的冻结副本 | “初始的自己” |
|
||||
| $\beta$ | 缰绳松紧 | 代码里的 `mc_kl_weight` |
|
||||
|
||||
|
||||
关键设计洞察(§4.1,Theorem 4.1):teacher 估计 π̂ 以**有界乘子** [0,1] 的身份乘在学生 score function 上,而不是像反向 KL 那样出现在分母/log 里——这从结构上消灭了标准 OPD 的梯度爆炸;而贝叶斯先验保证 π̂ ≥ α·π̄/(N+α) > 0,消灭了"teacher 全不匹配 ⇒ 梯度归零"的监督塌缩。
|
||||
|
||||
## 2. 参考实现的真实形态:一个 trainer,三代方法
|
||||
@@ -74,7 +87,7 @@ $$\bar\pi_\theta^{(c)} = \Big(\prod_{t\in c}\pi_\theta(y_t\mid\cdot)\Big)^{1/C}
|
||||
|
||||
代码在 `_compute_chunk_ebopd_loss` 内 L2194-2202:先验 `pi_bar = exp(mean(chunk_lps.detach()))`(几何均值,与式 4 严格一致),`pi_hat = (k + chunk_alpha·pi_bar)/(chunk_mc_samples + chunk_alpha)`,随后 clamp 到 [1e-8, 1] 并 detach。
|
||||
|
||||
> **detach 是命门**:先验和 π̂ 都必须切断梯度,否则学生会通过抬高自己的先验来自我强化(reward hacking 式塌缩)。配置里 `mc_nll_weight`(L246)的注释明确警告开启会塌缩,默认 0。这一点论文只隐含在"π̂ 是目标而非变量"里,代码把它变成了硬约束。
|
||||
> **detach 是命门**:先验和 π̂ 都必须切断梯度(L2196、L2201)。若留梯度通路,损失 π̂·|Σlog π_θ| 中 π̂ 也随 θ 可动,最速下降方向变成**压低**学生对自己 token 的概率、把乘子 π̂ 推向 0(p·ln(1/p)→0,指数快过对数)——在 teacher 全否定(k≈0)的 chunk 上损失可一路逃逸到 0,贝叶斯安全底 α·π̄ 被优化器亲手拆除,Theorem 4.1(a) 的梯度有界性也随之失效(多出的 ∇π̂ 项与惊讶度成正比)。相邻的另一个陷阱:`mc_nll_weight`(config L246-252)给非 MC 位置加自身 NLL 正则,帮助文本明确警告 "non-zero values cause self-reinforcement collapse"(无条件复读自己→熵塌缩),默认 0——两者是方向相反的两种自指失败。论文只隐含在"π̂ 是目标而非变量"里,代码把它变成了硬约束。
|
||||
|
||||
对应理论:Theorem 4.1(b) 下界 π̂ ≥ α·π̄/(N+α) > 0;4.1(c) 偏差-方差分解,α 是噪声-偏移旋钮;Theorem 4.2 证明 N=10 是方差收益的甜点。
|
||||
|
||||
@@ -146,3 +159,5 @@ KL 锚(`mc_kl_weight` = 论文的 β,config L284,**默认 0**)实现与
|
||||
4. 实现的 KL 锚与论文式(8)有哪三处差异?
|
||||
5. API teacher 路径为什么强制 char 级编辑距离?分叉点为什么要对齐词边界?
|
||||
6. `no_bayesian` 消融等价于论文里的哪个估计器?§4.1 预言它会怎么失败?
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# 附录 · CLAUDE.md 取舍决策记录
|
||||
|
||||
> 本文回答"为什么本仓库的 CLAUDE.md 这么写"。基准对照物是 Video-Tree-TRM5 项目的 CLAUDE.md(一个积累了五个月的生产级科研工程项目)。当某条规则的存在理由被质疑时,来这里查;当触发点到达时,按 C 节接入。
|
||||
|
||||
## 0. 取舍标准:三问
|
||||
|
||||
CLAUDE.md 是**每轮对话都完整注入模型上下文的提示词,不是文档**。信噪比是第一指标:模型对长指令集中"当前不适用规则"的遵从度会明显下降,而学会忽略 CLAUDE.md 是最糟的结果。每条候选规则过三问:
|
||||
|
||||
1. 从第 0 天起**每轮都生效**吗?
|
||||
2. 是**本项目特有**的信息吗?(通用好实践不用写,那是模型本来就该做的)
|
||||
3. 它指向的**设施真实存在**吗?
|
||||
|
||||
三问全过 → 保留(A 节);不过且无未来场景 → 舍弃(B 节);不过但有明确未来场景 → 延迟接入并写死触发点(C 节)。
|
||||
|
||||
## A. 改造保留
|
||||
|
||||
| 参考章节 | 我们的对应 | 改造点 |
|
||||
|----------|-----------|--------|
|
||||
| URGENT 头(生产级 + 中文) | URGENT 头 | "生产级"改为"学习驱动"——参考项目是 7×24 运行的 Agent 系统,我们是训练实验代码,健壮性需求是局部的(teacher 客户端),不是全局定性 |
|
||||
| §1 项目元数据 | §1 | 换成论文/参考实现/远程机/gitea |
|
||||
| §2.1 Conda + §2.3 ruff | §4 常用命令 | 只留 pytest/ruff |
|
||||
| §2.4 GPU 约定 | §5 远程规则 | 加严:显式选卡之外,加磁盘 12G 红线和"远程不改代码" |
|
||||
| §3 中"前序版本对照" | §6.2 | 参考 SOP 里最值钱的一条(重构前列出旧版全部行为,逐一确认保留/替代/删除),完整移植 |
|
||||
| §4.1-P4 显式优于隐式 | §2 配置显式化 + §7 类型注解 | 具体化:禁硬编码路径,点名参考实现的 `/fsx` 反面教材 |
|
||||
| §4.1-P5 防御性 | §7 硬规则 | 只留两条:禁 `except: pass`、禁默认值兜底 |
|
||||
| §4.1-P6 可测试性 | §2 纯逻辑核心/IO 边缘 | 升级为结构性约束:不是"优先纯函数"的劝导,而是"三个纯逻辑模块禁止 import transformers/vllm/openai"的可执行守则 |
|
||||
| §4.2 中文 docstring | §7 | 保留,另加张量 shape 标注(ML 项目特有痛点) |
|
||||
| §4.7 核心算法保真清单 | §3 模块↔论文映射表 + `01-paper-code-map.md` 差异清单 | 职能相同:防迁移走样的单一事实源;参考的 12 项是五个月长出来的,我们的 5 行随层数增长 |
|
||||
| §7 输出规范 | §6.4 文档规范 | 留核心三条:表格/伪代码优先、代码块 ≤15 行、引用带行号 |
|
||||
| §9 Research Wiki | `docs/` 章节体系 | 同构替代:知识正本在 docs/,CLAUDE.md 只做指针 |
|
||||
|
||||
## B. 舍弃
|
||||
|
||||
| 参考章节 | 舍弃理由 |
|
||||
|----------|----------|
|
||||
| §1.5 PyTorch 类比表 | 参考项目的领域知识;我们的"类比表"就是模块↔论文映射表 |
|
||||
| §3 SOP 全流程 + §8 Skill 门控表 | 引用的 13 个 skill 在本项目 `.claude/` 不存在,写上即死链(三问之③)。那套门控防多人长周期工程走样;我们的防走样机制是对拍参考实现 |
|
||||
| §4.1-P2/P3 可读性、单一职责 | 通用好实践(三问之②),写进提示词边际价值≈0,反而稀释项目特有条目 |
|
||||
| §4.3 feature branch 强制 | 单人学习仓库,主线提交历史 = 学习履历,特意线性;出现并行实验需求再引入 |
|
||||
| §4.6 覆盖率 80% + 三层测试目录 | 训练器/IO 代码需 GPU,全局覆盖率指标会逼出凑数测试;我们的标准更窄更强:纯逻辑三模块必须有对拍测试 |
|
||||
| §4.8 遥测 + §4.9 LLM 治理栈 | 设施不存在;真实需要的部分(teacher API 重试/并发/缓存)在层 5 作为**代码**进 `teacher.py` 而非作为规则;训练可观测性由 W&B 承担 |
|
||||
| §5 硬性目录规则 | "scripts 只放 .sh、根目录无 .py"与 ML 包惯例冲突:我们 `scripts/` 就是放薄 .py 入口 |
|
||||
| §6 迷途指南表 | 仓库目前 4 份文档,README 即地图 |
|
||||
|
||||
## C. 延迟接入(触发点已写死)
|
||||
|
||||
| 参考章节 | 接入触发点 |
|
||||
|----------|-----------|
|
||||
| §2.2 Makefile 收口 | 常用命令超过 3 条时 |
|
||||
| §2.5 自包含实验 sh(写死全参数、零参数复现) | 层 1 第一次远程训练时采纳 |
|
||||
| §4.2.1 非功能性需求覆盖表(持久化/幂等/断点续跑) | 层 5 设计 teacher 缓存与 checkpoint 恢复时 |
|
||||
| §4.5 配置双模式(.env vs 实验 YAML) | 层 6 第一个扫参对比实验时 |
|
||||
| 日志规范(loguru) | 层 1 训练脚本产生第一份需被检查的运行日志时 |
|
||||
|
||||
## D. 新增(参考没有、本项目特有)
|
||||
|
||||
学习优先(每章先讲解、不替用户一次写完,URGENT 级);远程磁盘红线与 `/data/zym` 路径纪律;纯逻辑模块禁 import 清单;教学注释三类型(见 E 节);"每完成一层回填 CLAUDE.md"的增长机制本身。
|
||||
|
||||
## E. 教学注释规范的决策(2026-07-17 补充)
|
||||
|
||||
**问题**:教学项目要不要更重的注释?类型注解是否强制?
|
||||
|
||||
**决策**:分工制——`docs/` 章节讲原理,代码注释做索引,两者不重复。注释只写三类:论文锚点、非显然约束(why + 违反后果)、差异标注。**拒绝逐行解说**:讲解性注释会让代码淹没在散文里、与章节文档重复、且随重构过期。这三类恰好都是"代码自身表达不了的信息"——即 Ousterhout 对注释存在意义的定义。
|
||||
|
||||
**类型注解**:公共接口强制(接口注解本身就是教学信息,成本极低)、私有 helper 从宽(强制到局部就是形式主义)。真正的硬要求是 **shape 标注**:`torch.Tensor` 注解表达不了 shape,而 shape 是 ML 代码可读性的最大杠杆。不引入 jaxtyping 之类的 shape 类型库——多一个依赖、多一层语法噪声,行注释 `# (B,T,V) -> (B,T)` 已够。
|
||||
|
||||
## F. Ousterhout 原则 → 本仓库规则的对照
|
||||
|
||||
**决策**:原则本身不进 CLAUDE.md(书摘是通用内容,三问之②不过),翻译成的可执行规则进。对照关系:
|
||||
|
||||
| 书中原则 | 本仓库的落地 |
|
||||
|----------|--------------|
|
||||
| 深模块(接口简单、实现有料) | 模块按论文概念划分;判据"看公式知文件、开文件知章节"(CLAUDE §2、§3) |
|
||||
| 信息隐藏 | 纯逻辑核心/IO 边缘 + 禁 import 清单(CLAUDE §2) |
|
||||
| 注释写代码表达不了的东西 | 教学注释三类型(CLAUDE §7,本文 E 节) |
|
||||
| 战略式编程(投资设计,不只让代码能跑) | 每层完成后的接口回看:接口复杂度逼近实现复杂度 = 浅模块坏味道,重构后才进下一层(CLAUDE §6.5) |
|
||||
| 适度通用(somewhat general-purpose) | YAGNI + C 节的延迟接入机制:规则和抽象都等真实场景出现才引入 |
|
||||
| Define errors out of existence | 不作为强制规则(与"禁默认值兜底"存在张力),作为设计品味在各章讨论——OmniOPD 本身就是范例:π̂ 的 clamp+先验下界在数学上消灭了零梯度错误态,而不是运行时捕获它 |
|
||||
@@ -0,0 +1,32 @@
|
||||
# 研究方向候选记录
|
||||
|
||||
> 重构过程中冒出的研究想法登记处。每条含:动机、可证伪假设、依托本仓库的最小实验(MVP)、风险。想法不分优先级排序时按登记时间排列。
|
||||
|
||||
## RI-1 · OmniOPD × 零阶黎曼优化(ZO-RGD):全前向"双黑盒"蒸馏
|
||||
|
||||
- **来源**: 合作者论文《Zeroth-Order Riemannian Optimization on Fixed-Rank Update Manifolds for LLM Fine-Tuning》(`references/26_05_subNeruIPS_...pdf`),2026-07 登记。
|
||||
- **背景一句话**: 该文将 LoRA 式增量 ΔW 约束为固定秩流形上的点,用两次前向的有限差分(MeZO 式 ZO)+ 切空间归一化探针 + 截断 SVD retraction 做无反传微调;实验限于 OPT 分类/抽取任务。
|
||||
|
||||
### 1a. 系统组合:teacher 无 logits + 学生无反传
|
||||
|
||||
| 要素 | 说明 |
|
||||
|------|------|
|
||||
| 咬合点 | OmniOPD 的 π̂ 是 **detach 的常数** → ZO 的两次扰动前向 F(ΔW±εZ) 复用同一轨迹与同一组 teacher 打分,不产生额外 API 查询 |
|
||||
| 协同 | ZO 步长极小 → 轨迹+打分可复用 K 个 ZO 步仍近似 on-policy → teacher API 成本摊薄 K 倍(慢优化器 × 贵监督 = 天然互补) |
|
||||
| 系统故事 | 全管线跑在纯推理设施上(teacher=聊天 API,学生=vLLM 前向评分),无训练框架、无激活显存;4×A800 可碰 30B+ 学生 |
|
||||
| **Gate 实验** | ZO-RGD 在本管线上先优化普通 SFT 损失(层 1 复用):长 CoT 生成任务上能否收敛到可用水平。**不过此关全案作废** |
|
||||
| 风险 | ZO 在生成式/推理任务无先例(MeZO 系全是短输出分类);OmniOPD 每轨迹 10 chunk × ZO 每步 1 标量 = 双重稀疏,可能不收敛 |
|
||||
|
||||
### 1b. 秩约束 = 几何 trust region,替代/减弱 β KL 锚
|
||||
|
||||
- **假设**: 式(8) 第二项(行为空间信任域)与固定秩流形约束(参数空间信任域)防的是同一失败模式(未审计区域漂移);流形约束下 β 可调小甚至归零。
|
||||
- **MVP**: 层 6 后消融网格 {全参 / LoRA / 固定秩流形} × {β=0 / β>0},测未审计 token 对 π_ref 的 KL 漂移 + 数学评测分。若"流形+β=0"≈"全参+β>0",得到干净结论。
|
||||
- **依托**: 损失/调度器即本仓库层 3-5 产出,仅换优化端;此实验**不依赖 1a 的 ZO**(一阶梯度 + retraction 即可做),风险远低于 1a,可独立先行。
|
||||
|
||||
### 1c. 方差预算分配(理论附件)
|
||||
|
||||
- OmniOPD Thm 4.2(teacher 采样方差 ∝1/N)× ZO-RGD Prop 1(探针方差 ∝ mn/d_r)串联;固定预算下 N(rollout 数)与 q(探针数)的最优分配。适合作 1a 的理论章节,不独立成文。
|
||||
|
||||
### 诚实评估
|
||||
|
||||
结合发生在**管线/系统层**而非损失数学层——两文公式不冲突也不深融,叙事须立足于:① 双黑盒系统故事(1a);② 信任域替换假设(1b)。建议路径:先做 1b(便宜、独立、可证伪),1a 的 gate 实验穿插进行。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 远程独有依赖(gpu-a800-060):训练与推理重件,本地不装。
|
||||
-r requirements.txt
|
||||
vllm>=0.8 # white-box teacher 服务 + 学生 on-policy 生成
|
||||
accelerate # 多卡训练启动
|
||||
wandb # 训练指标上报
|
||||
@@ -0,0 +1,9 @@
|
||||
# 两端共用核心依赖(本地 + 远程)。版本策略:先宽松安装,两端跑通后按需冻结。
|
||||
torch>=2.6
|
||||
transformers>=4.51 # Qwen3 系列需要 4.51+
|
||||
datasets
|
||||
numpy
|
||||
openai>=1.60 # teacher.py:OpenAI 兼容 API 客户端
|
||||
python-dotenv
|
||||
pytest
|
||||
ruff
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# 远程机 gpu-a800-060 环境搭建(幂等,可重复执行)。
|
||||
# 用法:bash scripts/setup_remote.sh
|
||||
# 硬约束:根分区仅剩 12G —— 环境、缓存、临时目录一律压到 /data/zym 下。
|
||||
set -euo pipefail
|
||||
|
||||
DATA_ROOT=/data/zym
|
||||
ENV_PATH=$DATA_ROOT/envs/ars-opd
|
||||
REPO_DIR=$DATA_ROOT/ars-opd-rebuild
|
||||
|
||||
# ---- 0. 所有会写盘的路径全部改道 /data(防根分区被写爆)----
|
||||
export HF_ENDPOINT=https://hf-mirror.com # huggingface.co 被墙,走镜像
|
||||
export HF_HOME=$DATA_ROOT/hf_cache
|
||||
export CONDA_PKGS_DIRS=$DATA_ROOT/conda_pkgs # conda 包缓存默认在根分区
|
||||
export PIP_CACHE_DIR=$DATA_ROOT/pip_cache # pip 缓存默认在根分区
|
||||
export TMPDIR=$DATA_ROOT/tmp # 大 wheel 解压临时目录
|
||||
mkdir -p "$DATA_ROOT"/{envs,hf_cache,conda_pkgs,pip_cache,tmp}
|
||||
|
||||
# ---- 1. conda 环境(建在 /data,不建在 ~)----
|
||||
if [ ! -d "$ENV_PATH" ]; then
|
||||
conda create -p "$ENV_PATH" python=3.11 -y
|
||||
fi
|
||||
|
||||
# ---- 2. 代码(远程只读:clone 走 HTTPS 匿名,更新只 git pull)----
|
||||
if [ ! -d "$REPO_DIR" ]; then
|
||||
git clone https://gitea.iomgaa.online/iomgaa/ars-opd-rebuild.git "$REPO_DIR"
|
||||
else
|
||||
git -C "$REPO_DIR" pull
|
||||
fi
|
||||
|
||||
# ---- 3. 依赖 ----
|
||||
conda run -p "$ENV_PATH" pip install -r "$REPO_DIR/requirements.txt" -r "$REPO_DIR/requirements-remote.txt"
|
||||
|
||||
# ---- 4. 环境变量持久化(写入 ~/.bashrc,幂等)----
|
||||
if ! grep -q "ars-opd-rebuild env" ~/.bashrc; then
|
||||
cat >> ~/.bashrc <<'EOF'
|
||||
|
||||
# --- ars-opd-rebuild env ---
|
||||
export HF_ENDPOINT=https://hf-mirror.com
|
||||
export HF_HOME=/data/zym/hf_cache
|
||||
export CONDA_PKGS_DIRS=/data/zym/conda_pkgs
|
||||
export PIP_CACHE_DIR=/data/zym/pip_cache
|
||||
alias opd='conda activate /data/zym/envs/ars-opd && cd /data/zym/ars-opd-rebuild'
|
||||
EOF
|
||||
fi
|
||||
|
||||
# ---- 5. 验证 ----
|
||||
echo "=== 验证 torch/CUDA ==="
|
||||
conda run -p "$ENV_PATH" python -c "import torch; print('torch', torch.__version__, '| cuda可用:', torch.cuda.is_available(), '| 卡数:', torch.cuda.device_count())"
|
||||
echo "=== 验证单元测试 ==="
|
||||
conda run -p "$ENV_PATH" python -m pytest "$REPO_DIR/tests" -q
|
||||
echo "=== 磁盘检查(根分区不应有明显增长)==="
|
||||
df -h / /data | tail -2
|
||||
echo "全部完成。日常使用:输入 opd 进入环境与目录。"
|
||||
@@ -0,0 +1,72 @@
|
||||
"""π̂ detach 命门约束的守护测试(对应 docs/01 §3.4,论文式(5)(8))。
|
||||
|
||||
背景:chunk 损失 L = -π̂·Σlog π_θ 中,π̂ 的先验 π̄ 由学生自身概率算出。
|
||||
若不切断 π̄ 的梯度通路,最速下降方向会变成压低学生对自己 token 的概率、
|
||||
把乘子 π̂ 推向 0 以逃逸惩罚(p·ln(1/p)→0,指数快过对数),且恰好在
|
||||
teacher 全否定(k≈0)、最需要纠正的 chunk 上塌缩。推导见 docs/01 §3.4。
|
||||
|
||||
现状:独立的数学性质测试,仅依赖 torch(单 token 简化,C=1)。
|
||||
层 3 完成 ars_opd/estimator.py 后,需追加针对真实实现的同名断言,
|
||||
确保重构时 `.detach()` 不被误删(参考实现锚点:trainer:2196、2201)。
|
||||
"""
|
||||
|
||||
import torch
|
||||
|
||||
# 与论文/参考实现默认一致:α=1, N=10
|
||||
ALPHA = 1.0
|
||||
N_ROLLOUTS = 10.0
|
||||
|
||||
|
||||
def chunk_loss_and_grad(p0: float, k: float, detach_prior: bool) -> tuple[float, float]:
|
||||
"""单 token 版式(8) chunk 项,返回 (loss 值, dL/dp)。
|
||||
|
||||
参数:
|
||||
p0: 学生对自己 token 的概率,标量。
|
||||
k: teacher 相似度票数 k_sem,标量(0 = 全否定)。
|
||||
detach_prior: 是否切断先验 π̄ 的梯度通路。
|
||||
返回:
|
||||
(loss.item(), p.grad.item())
|
||||
"""
|
||||
p = torch.tensor(p0, requires_grad=True)
|
||||
log_p = p.log()
|
||||
prior_src = log_p.detach() if detach_prior else log_p
|
||||
pi_bar = prior_src.exp() # 式(4):C=1 时几何均值即 p 本身
|
||||
pi_hat = (k + ALPHA * pi_bar) / (N_ROLLOUTS + ALPHA) # 式(5)
|
||||
loss = -pi_hat * log_p # 式(8) chunk 项
|
||||
loss.backward()
|
||||
return loss.item(), p.grad.item()
|
||||
|
||||
|
||||
def test_detach_reinforces_even_when_teacher_rejects():
|
||||
"""detach 世界:即使 teacher 全否定(k=0),梯度仍为负 → optimizer 增大 p。
|
||||
|
||||
这是贝叶斯兜底的本意:k=0 处仍有非零、方向正确的学习信号。
|
||||
"""
|
||||
_, grad = chunk_loss_and_grad(p0=0.2, k=0.0, detach_prior=True)
|
||||
assert grad < 0
|
||||
|
||||
|
||||
def test_no_detach_escapes_when_teacher_rejects():
|
||||
"""不 detach 世界:k=0 且 p 低于 1/e 时梯度为正 → optimizer 压低 p(逃逸)。
|
||||
|
||||
此断言若失败(梯度变负),说明有人"修复"了 detach——那恰恰是 bug。
|
||||
"""
|
||||
_, grad = chunk_loss_and_grad(p0=0.2, k=0.0, detach_prior=False)
|
||||
assert grad > 0
|
||||
|
||||
|
||||
def test_detach_does_not_change_loss_value():
|
||||
"""detach 只剪梯度不改数值:两个世界的前向 loss 必须完全相等。"""
|
||||
loss_detached, _ = chunk_loss_and_grad(p0=0.2, k=0.0, detach_prior=True)
|
||||
loss_attached, _ = chunk_loss_and_grad(p0=0.2, k=0.0, detach_prior=False)
|
||||
assert loss_detached == loss_attached
|
||||
|
||||
|
||||
def test_teacher_agreement_blocks_escape_even_without_detach():
|
||||
"""k 大时逃逸被堵死:分子中 k·|log p| 项不受 p 控制,随否认无限增长。
|
||||
|
||||
逃逸条件为 NLL > 1 + k/(α·π̄);k=5、p=0.2 时阈值 ≈ 26,远未达到,
|
||||
故即使不 detach 梯度仍为负。印证"塌缩恰好集中在 k≈0 的 chunk"。
|
||||
"""
|
||||
_, grad = chunk_loss_and_grad(p0=0.2, k=5.0, detach_prior=False)
|
||||
assert grad < 0
|
||||
Reference in New Issue
Block a user