☰
CLM 微调实验协议解析:Auto-Finetune 自主迭代闭环的设计与实践
2026/9/29 18:40:42 网站建设 项目流程

【免费下载链接】CLM

项目地址:https://gitcode.com/gh_mirrors/clm2/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 与用户对齐任务、标签与固定命令

需要与用户达成三件事的一致:

  1. 任务类型:clm或choice。二者对应 train/finetune.py 中的两种训练入口——clm使用 (state, action) 步骤轨迹并采用共享的组掩码 in-batch InfoNCE 训练器;choice使用 TypeSafe 风格的 typed System One 问题(如 LocalLLaMA/typed-decisions)。
  2. 一个 run tag:用于区分本轮实验批次,例如deepswe、typed。
  3. 一条固定的训练命令和一条固定的评估命令:整个实验周期内,除了--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.pyembedding 目录与 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 description

score列记录该次运行的关键指标,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 512

choice 任务则形如:

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):

  1. 检查当前提交与既往结果:读results.tsv与run.log,确认上一个keep点在哪;
  2. 对train/finetune.py做一次聚焦改动:一次只改一个维度,确保分数变化可归因;
  3. 提交它(commit);
  4. 跑训练与评估(3.1、3.2 两条命令);
  5. 记录结果(追加到results.tsv);
  6. 分数有提升则保留该提交(keep);
  7. 否则回滚到最近一个保留的提交(reset到上一个 keep 点);
  8. 换一个新想法继续。

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-ckptNonewarm-start checkpoint,例如"$(clm-download)"
--width/--depth从 init checkpoint 的cfg继承(默认 1536 / 3)head 宽度与深度
--proj512投影维度(见 src/clm/heads.py)
--epochs20训练轮数
--batch2048(clm)/ 256(choice)批大小,须 ≥ 2
--lrclm 宽度/批大小规则;choice 5e-4学习率
--weight-decay0.0AdamW 权重衰减
--val-frac0.1从训练任务中划出验证集的比例
--patience5early stop 容忍轮数(clm 判据为 +1e-4 提升,choice 为 +1e-9,见 train/finetune.py 与 train/finetune.py)
--seed1234随机种子
--gpu0CUDA 卡号

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)
--samplerrandom或task-blocked(默认 random)
--tasks-per-batch4
--clm-select-metricwithin_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 文件
--workflowalltyped-decisions 配置
--targetssoftsoft拟合标注者分布,hard拟合金标签
--lossinfonceinfonce为批内不同选项文本上的双向 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

项目地址:https://gitcode.com/gh_mirrors/clm2/CLM
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询