【免费下载链接】CLM
本文以仓库内的 docs/FINETUNING.md 为骨架,完整拆解 CLM(Contrastive Language Models)项目用于"让 LLM 自主改进微调"的实验协议:从固定基线与评估命令、建立results.tsv结果台账,到"一次一个聚焦改动、保留高分提交、回滚低分提交"的无限循环,并结合 train/finetune.py、evaluation/bon_eval.py 等源码给出参数速查与底层实现依据。读完你可以直接按该协议搭建一套可复现、防数据泄漏、可自动迭代的微调实验流水线,用最小的改动面系统性地提升 CLM 投影头(head)在 held-out 任务上的验证表现。
一、协议定位:为"LLM 自主改进微调"而生的实验纪律
CLM 的微调对象不是整个模型,而是挂在冻结的 Qwen3-8B 编码器之上的两个小型投影头(state head 与 action head,约 2000 万参数,详见 src/clm/heads.py)。传统人工调参需要反复在"改代码—跑训练—跑评估—记结果"之间切换,而 docs/FINETUNING.md 描述的正是把这一循环交给 LLM 自主执行的一份协议(protocol):
- 它明确规定了"允许改什么、禁止改什么",把可探索空间收窄到 train/finetune.py 一个文件;
- 它用
results.tsv台账强制每次实验留下commit / score / status / description四列记录,保证过程可审计、可回滚; - 它把"保留高分提交、回滚低分提交"固化成无脑循环,直到用户主动打断,从而最大化 held-out 评估指标。
换句话说,这份文档是一份面向 Agent 的执行手册:数据、嵌入、划分、评估集全部冻结,只有训练脚本内部的 head 结构、目标函数、优化器、调度器、批处理与超参数是可动的。下文按文档原章节顺序展开,并补充仓库源码证据。
二、Setup:先把基线固定下来
协议的第一步不是写代码,而是"对齐与固定"。Setup 阶段共六步,全部完成后才允许开始实验。
2.1 与用户对齐任务、标签与固定命令
需要与用户达成三件事的一致:
- 任务类型:
clm或choice。二者对应 train/finetune.py 中的两种训练入口——clm使用 (state, action) 步骤轨迹并采用共享的组掩码 in-batch InfoNCE 训练器;choice使用 TypeSafe 风格的 typed System One 问题(如 LocalLLaMA/typed-decisions)。 - 一个 run tag:用于区分本轮实验批次,例如
deepswe、typed。 - 一条固定的训练命令和一条固定的评估命令:整个实验周期内,除了
--out-dir runs/<tag>/<commit>这类每次必变的占位符之外,其余参数不许动。
--task与--out-dir在源码中均为必填项(train/finetune.py),这也与协议要求"先约定命令"吻合。
2.2 创建autofinetune/<tag>分支
为每一轮实验周期新建一个干净分支,例如autofinetune/deepswe。分支的意义在于:每次"保留"或"回滚"都是一次git reset,而独立分支可以避免污染主分支的历史。
2.3 必读文件清单(源码快速地图)
协议要求动手前先读完六个文件,它们恰好覆盖了微调流水线的全部环节:
| 文件 | 在流水线中的角色 |
|---|---|
| README.md | 项目总览、安装方式、服务与评估命令示例、微调背景("Fine-tuning CLM on Your Own Data"一节) |
| train/finetune.py | 训练主脚本:参数解析、clm/choice 两条训练路径、checkpoint 读写、early stop |
| train/adapters.py | 数据适配器:read_transitions读取步骤轨迹,typed_decision_examples把 typed-decisions 行转换为 ChoiceExample |
| train/embed_utils.py | 编码器嵌入配方(Qwen3-8B、last-token pooling)与两个后端(进程内 vLLM //v1/embeddings服务) |
| preprocessing/hf_embeddings.py | embedding 目录与 Hugging Face parquet 数据集互转,download/export/push三个子命令 |
| evaluation/bon_eval.py | 统一的 best-of-N 保留评估:逐 step 打分、final-window 均值聚合、精确期望计算 |
2.4 验证数据集、checkpoint、folds 与 GPU
在跑任何实验之前必须确认四件事都就绪:
- 数据集:clm 任务可通过
--emb-dir(本地 embedding 目录)、--hf-dataset(HF 嵌入数据集,默认Contrastive-LM/deepswe-clm-train-embeddings-8k,见 preprocessing/hf_embeddings.py)或--data(从零嵌入的轨迹 JSON/JSONL)三选一;choice 任务需要--data指向 typed-decisions 的 HF id、本地目录或 parquet 文件。三者互斥,源码在 train/finetune.py 中强制校验。 - checkpoint:初始权重用
--init-ckpt指定,README 示例用"$(clm-download)"获取参考头;也可用 download_head.sh 手动下载到本地checkpoints/目录。 - folds:若使用
--folds K --fold-index <index>,需要保证索引文件覆盖了足够的任务(源码要求至少 K 个任务,见 train/finetune.py),且训练嵌入覆盖全部被折叠的任务。 - GPU:训练脚本通过
torch.cuda.is_available()自动选设备,也可用--gpu指定卡号(train/finetune.py)。
2.5 建立results.tsv
在仓库根目录创建一个不被 git 跟踪的results.tsv,首行为四列表头(Tab 分隔):
commit score status descriptionscore列记录该次运行的关键指标,status取值一般包括keep(保留)、discard(丢弃)、crash(崩溃)、timeout(超时),description用一句话说明改了什么。文件保持 untracked 状态,防止把实验台账误提交进仓库。
2.6 确认后运行未修改基线
与用户确认 Setup 就绪后,用完全未修改的train/finetune.py跑第一次实验。协议明确要求:第一轮运行必须始终建立未修改基线(baseline)。这一步产出的分数是后续所有"是否改进"判断的零点,也是results.tsv的第一行(示例中即为keep baseline)。
三、Experimentation:训练与保留评估
实验期只有两条命令,其余全部围绕它们展开。
3.1 固定训练命令:train/finetune.py
python train/finetune.py ... --out-dir runs/<tag>/<commit> > run.log 2>&1...处填充 2.1 节约定好的固定参数;--out-dir runs/<tag>/<commit>是每次运行唯一必变的参数,其中<commit>取当前提交的短哈希。输出重定向到run.log(追加用>>),训练期间的控制台日志全部落盘,便于事后tail排查。
结合 README 中的实际示例,一个典型的 clm 任务固定命令形如:
python train/finetune.py --task clm --init-ckpt "$(clm-download)" \ --out-dir runs/deepswe/<commit> \ --holdout-tasks heads/deepswe/heldout_tasks.json --batch 512choice 任务则形如:
python train/finetune.py --task choice --data LocalLLaMA/typed-decisions --workflow all \ --init-ckpt "$(clm-download)" --out-dir runs/typed/<commit>从源码看,finetune.py的默认配置为:clm 任务--max-len 8192、--batch 2048;choice 任务--max-len 2048、--batch 256(train/finetune.py)。学习率若不指定,clm 按2e-3 * sqrt(1024/width) * sqrt(batch/1024)的宽度/批大小规则自动推导,choice 默认5e-4(train/finetune.py 与 train/finetune.py);优化器固定为 AdamW,调度器为pct_start=0.1、cosine 退火的 OneCycleLR(train/finetune.py)。每次训练结束会写出best_head.pt/final_head.pt、summary.json、task_split.json与finetune_summary.json,方便后续对照。
3.2 保留评估命令:evaluation/bon_eval.py(仅 clm)
python evaluation/bon_eval.py ... --n <N> --window <K> >> run.log 2>&1--n是每个评估组的候选预算,--window是轨迹打分窗口(源码默认 12,见 evaluation/bon_eval.py)。评估逻辑是协议文档强调的 held-out best-of-N rate 的来源:
- 对每个 step,用 head 计算 state 与 action 的归一化投影点积作为分数(evaluation/bon_eval.py);
- 一条轨迹的得分 = 最后
--window个 step 分数的均值(final_window_mean,evaluation/bon_eval.py); - 对每个任务在 N 个候选中做精确期望best-of-N 选择,平局按均匀期望处理(evaluation/bon_eval.py)。
README 给出一个可复现的 DeepSWE heldout-38 示例(--n 4 --window 12),可作为约定固定评估命令的模板。choice 任务则在训练脚本内部完成评估,指标为验证集准确率(val acc)。
3.3 你可以改什么(You may)
协议允许的改动面被严格限定为:
- 只修改
train/finetune.py这一个文件; - 可改动的内容包括:head 结构(
--width、--depth、--proj,以及 checkpointcfg中的activation/layernorm/residual)、目标函数(choice 的--loss infonce|softce、--targets soft|hard)、优化器(--lr、--weight-decay)、调度器(OneCycleLR 的参数与退火策略)、批处理(--batch、clm 的--sampler random|task-blocked、--tasks-per-batch)、超参数(--epochs、--patience、--seed、--val-frac、--clm-select-metric)。
这些参数在源码的 argparse 中都有对应定义(train/finetune.py),改动的核心影响也都有据可查:例如--sampler task-blocked会按任务分块构造批(train/finetune.py),--tasks-per-batch控制一个 batch 内拼入多少任务,直接影响 in-batch InfoNCE 的负样本构成。
3.4 你不能改什么(You may not)
- 不得修改其他任何文件;
- 不得改变数据、嵌入、splits、folds、评估集、
--n、--window——它们共同构成"固定不变的评估坐标系",一旦变动,前后分数便不可比; - 不得安装依赖——保证运行环境可复现,避免"换了个库版本分数涨了"的假阳性;
- 不得在评估数据上训练,也不得在 choice 的 test split 上调参——防止把评估集信息泄漏进训练过程,这是协议防止"过拟合排行榜"的核心红线。
3.5 优化目标与"先基线"原则
- clm 任务:最大化 held-out best-of-N rate(由 3.2 的
bon_eval.py给出); - choice 任务:最大化验证集准确率(validation accuracy);
- 分数相同时,优先更简单的改动——协议明确偏好简单性,避免为了微小分数差异引入复杂逻辑;
- 第一轮必须跑未修改基线,任何改进都以它为零点衡量。
四、Logging:results.tsv记录规范
每次运行结束后,向results.tsv追加一行 Tab 分隔记录:
commit score status description a1b2c3d 0.796500 keep baseline b2c3d4e 0.805300 keep task-blocked batches c3d4e5f 0.787600 discard constant learning rate d4e5f6a 0.000000 crash double width (OOM)观察示例可以提炼出几条隐含纪律:
commit是短哈希,与--out-dir runs/<tag>/<commit>一一对应,保证任何一行结果都能定位到精确代码状态;score保留到 6 位小数,0.000000通常意味着运行崩溃或指标不可用(如示例中的 OOM);status是决策标签:keep保留本次提交,discard回滚,crash/timeout记录异常后回滚;description用短语概括改动,例如 "task-blocked batches"、"constant learning rate"、"double width (OOM)",让台账本身就是一份可读的实验日志。
严禁提交results.tsv、run.log及任何运行输出:它们属于过程性产物,只存在于工作区,避免污染仓库历史。分数来源方面,clm 取bon_eval.py输出的 best-of-N rate(其 JSON 结果写在--output指定的文件、selectors键下),choice 取finetune.py打印的val acc。
五、实验循环:一次一个聚焦改动
协议的核心是下面的无限循环(LOOP FOREVER):
- 检查当前提交与既往结果:读
results.tsv与run.log,确认上一个keep点在哪; - 对
train/finetune.py做一次聚焦改动:一次只改一个维度,确保分数变化可归因; - 提交它(commit);
- 跑训练与评估(3.1、3.2 两条命令);
- 记录结果(追加到
results.tsv); - 分数有提升则保留该提交(
keep); - 否则回滚到最近一个保留的提交(
reset到上一个 keep 点); - 换一个新想法继续。
5.1 崩溃与超时的处理
- 若运行崩溃:先
tail -n 80 run.log查看末尾 80 行日志定位原因;能修的小 bug 立即修复并重跑;修不了就记录crash并回滚。 - 若运行超过约定时间:停止它,记录
timeout,并回滚。超时本身也是一种有价值的负面结果——说明该改动把训练拖到了不可接受的时长。
5.2 持续推进原则
协议明确要求:一旦循环开始,不要再询问是否继续。实验应自主推进,直到用户主动打断。这背后的工程假设是:在固定评估坐标系下,"保留改进、回滚退步"的贪心策略是安全的——最坏情况只是停留在基线,而不会累积退化。
六、源码速查:可改动面的参数与实现
为了让"改finetune.py"真正可操作,下面按源码 argparse 给出完整参数速查(train/finetune.py)。
6.1 通用参数(head / 优化)
| 参数 | 默认值 | 说明 |
|---|---|---|
--task | 必填 | clm或choice |
--out-dir | 必填 | 输出目录,协议约定为runs/<tag>/<commit> |
--init-ckpt | None | warm-start checkpoint,例如"$(clm-download)" |
--width/--depth | 从 init checkpoint 的cfg继承(默认 1536 / 3) | head 宽度与深度 |
--proj | 512 | 投影维度(见 src/clm/heads.py) |
--epochs | 20 | 训练轮数 |
--batch | 2048(clm)/ 256(choice) | 批大小,须 ≥ 2 |
--lr | clm 宽度/批大小规则;choice 5e-4 | 学习率 |
--weight-decay | 0.0 | AdamW 权重衰减 |
--val-frac | 0.1 | 从训练任务中划出验证集的比例 |
--patience | 5 | early stop 容忍轮数(clm 判据为 +1e-4 提升,choice 为 +1e-9,见 train/finetune.py 与 train/finetune.py) |
--seed | 1234 | 随机种子 |
--gpu | 0 | CUDA 卡号 |
6.2 clm 任务专属参数
| 参数 | 说明 |
|---|---|
--emb-dir/--hf-dataset/--data | 数据源三选一(互斥校验) |
--holdout-tasks | 单次运行:持有这些任务不参与训练 |
--holdout-folds | 每折训练一个 head,并写出fold_spec.json供bon_eval.py --fold-spec使用 |
--folds K --fold-index <index> | 用索引文件生成 K 个按候选数/通过数分层、任务不相交的折(train/finetune.py) |
--sampler | random或task-blocked(默认 random) |
--tasks-per-batch | 4 |
--clm-select-metric | within_task_top1或val_loss(默认within_task_top1) |
clm 训练器的核心是组掩码双向 in-batch InfoNCE:同一 (task, step) 的样本互相掩蔽,避免同一步骤的伪正样本干扰,见 train/finetune.py。这也解释了协议为什么禁止改动数据与划分——任务与步骤的分组结构直接参与损失计算,改动它等于偷偷换了评估条件。
6.3 choice 任务专属参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--data | 必填 | typed-decisions 的 HF id / 本地目录 / parquet 文件 |
--workflow | all | typed-decisions 配置 |
--targets | soft | soft拟合标注者分布,hard拟合金标签 |
--loss | infonce | infonce为批内不同选项文本上的双向 InfoNCE;softce为每个问题自身候选上的 softmax |
choice 数据的构建走 train/adapters.py:每行state+questions(System One wire format)+gold,通过 src/clm/schema.py 的build_pairs生成 state 文本、选项键与候选文本,并以金标签分布为目标。值得注意的是,训练文本会先经 train/embed_utils.py 的 token 配方处理:state 取聊天模板后的尾部 token,action/选项文本按keep="head"或keep="tail"截断,编码后端支持进程内 vLLM(OfflineBackend)与vllm serve --runner pooling服务(ServerBackend)。
6.4 约束为什么如此严格:来自实现的印证
- head 很轻,改动面很小:
make_head只是hidden(4096) -> width -> ... -> proj(512)的 MLP(src/clm/heads.py),checkpoint 为torch.save的 dict,含state_head/action_head/logit_scale/cfg。所以"改 head"本质就是调这几个维度,评估集必须冻结才能比较。 - 分数高度依赖评估坐标系:
bon_eval.py的--n、--window直接改写聚合函数(final-window 均值与 N 元精确期望)的输入形态,任何变动都会让 held-out rate 失真。 - 嵌入与数据不可变的理由:嵌入由
embed_utils.Recipe在固定编码器(Qwen3-8B,last-token pooling)上生成,head 只在该嵌入空间有意义(README 亦注明"a head only makes sense with the encoder and pooling it was trained against");换嵌入等于换任务。 - 不装依赖保证可复现:训练依赖被锁在 train/requirements.txt(torch、transformers、pyarrow、huggingface_hub,vLLM 可选),协议禁止安装新包正是为了锁定这一环境边界。
七、小结:把"调参"变成可回滚的自主闭环
CLM 的 Auto-Finetune 协议本质上是一套最小信任的自主实验系统:数据、嵌入、划分、评估全冻结,只开放train/finetune.py内部的 head/目标/优化器/调度器/批处理/超参数,用results.tsv台账 +keep/discard/crash决策 + git 回滚把每一次尝试变成可审计、可重放、可归因的单元。对实践者而言,照着本文的 Setup→Experimentation→Logging→Loop 四段流程,结合 6 节的参数速查与源码定位,即可在 docs/FINETUNING.md 的约束下系统性地寻找 CLM head 在 held-out best-of-N(clm)或验证准确率(choice)上的提升空间——第一轮先跑未修改基线,之后的每一次改动都让分数说话。
【免费下载链接】CLM
相关推荐
RD-Agent 框架设计与核心组件解析:以“假设驱动”迭代闭环实现科研自动化
RD Agent 框架设计与核心组件解析:以“假设驱动”迭代闭环实现科研自动化 RD Agent(RDAgent)将数据挖掘专家日常的研发流程——提出假设、设计
人工智能AI Agent大模型Agent 框架强化学习自主智能体数据科学未来展望:Cosmos-Transfer1在物理AI领域的发展路线图
未来展望:Cosmos Transfer1在物理AI领域的发展路线图 Cosmos Transfer1作为一款world to world迁移模型,旨在弥合模拟
Python迭代器协议:如何设计自定义可迭代对象的终极指南
Python迭代器协议:如何设计自定义可迭代对象的终极指南 Python迭代器协议是Python编程中一个强大而优雅的特性,它允许你创建自定义的可迭代对象。在p
示例工程教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考