/experiments/5_11_lora/

5.11 节实验:用“默认输出 → 改过的版本”配对给小模型训 LoRA,和少样本提示基线对比并做盲评

目录文件列表

文件 / 目录说明大小
📁 ../返回上级目录-
📁 data/子目录-
📁 sample_output/子目录-
📄 README.md6.5 KB
📄 common.py1.2 KB
📄 evaluate.py8.9 KB
📄 make_demo_data.py4.0 KB
📄 requirements-cuda.txt54 B
📄 requirements-mac.txt20 B
📄 train_mlx.py2.4 KB
📄 train_peft.py4.7 KB

README.md

5.11 用你自己的“默认输出 → 改过的版本”给小模型训 LoRA

这份代码回答一个问题:模型默认写出来的东西,你每次都要改成自己的样子;把这些“默认输出 → 你改过的版本”攒成配对,拿来给一个小模型训 LoRA,它能不能学会你的改法,又该怎么判断它学会了没有。

整个配方分三步:

  1. 准备配对数据:每一对是一段默认输出(ai)和你改过的版本(original)。
  2. 训 LoRA:Mac 上用 MLX(train_mlx.py),NVIDIA 显卡上用 PEFT(train_peft.py)。底座模型不动,只训练一小组附加权重,存下来几十 MB。
  3. 用训练时没见过的例子评估(evaluate.py):和“不训练、把几组例子直接写进上下文”的基线比,看谁更接近你改过的版本;生成一份 A/B 顺序打乱的盲评表让你自己判断;可选地再请一个 API 模型盲评,并检查有没有改动原意。

示例数据

data/ 里有一份现成的示例数据,用的是 5.11 文风 LoRA 那种“反向合成”做法:拿一位作者的原文,让大模型用它默认的写法把每段改写一遍,得到“AI 默认写法 → 作者原文”的配对。作者选了鲁迅(《朝花夕拾》和《呐喊》的 23 篇,已进入公有领域,原文取自维基文库并转成简体),默认写法由 AI Builder Space 上的 deepseek 生成。训练 455 对、验证 20 对、留出测试 20 对。生成过程见 make_demo_data.py,你不需要重跑。

需要什么设备

--judge 盲评要用 AI Builder Space 的 key。

安装与运行

Mac:

cd 5_11_lora
uv venv && source .venv/bin/activate      # 没有 uv 就用 python3 -m venv .venv
uv pip install -r requirements-mac.txt
python train_mlx.py                        # 约 3 分钟(M3 Ultra),LoRA 存到 adapters/mlx/
python evaluate.py --backend mlx           # 生成留出例子的三种版本、相似度和盲评表
export AI_BUILDER_TOKEN=sk_你的key
python evaluate.py --backend mlx --judge gpt-5    # 另外请 gpt-5 盲评一遍

NVIDIA 显卡:

uv pip install -r requirements-cuda.txt
python train_peft.py                       # LoRA 存到 adapters/peft/
python evaluate.py --backend torch

为什么用 Qwen3 而不是模块 5 其他地方用的 Qwen3.5:Qwen3.5 的一部分层是线性注意力,目前在 MLX 上训练很慢(我们测到每秒 42–53 个 token,Qwen3-1.7B 约 600),还遇到过一次 macOS 报 GPU 卡死而中断。

该看到什么

我们的样例输出在 sample_output/(2026-10-23):训练日志、评估结果、盲评表和答案。

Mac Studio(M3 Ultra,MLX)上:验证集损失从 2.46 降到最低 0.51(第 200 步),训完 450 步是 0.55。留出的 20 条上:

与原文的逐字相似度 与默认输出的相似度(越低改得越多) 比不改更接近原文的条数
默认输出本身(不改) 0.895 1.000 —
基线:3 组例子写进上下文,不训练 0.785 0.875 0/20
LoRA 0.892 0.970 8/20

一张 RTX 5090 上用 PEFT 训同一份数据(2 遍,134 秒),验证集损失从 2.19 降到 0.50。留出的 20 条上,与原文相似度:默认输出 0.895,基线 0.797,LoRA 0.892;LoRA 与默认输出的相似度 0.976,比不改更接近原文的有 5/20 条,逐条比基线更接近原文 16/20。gpt-5 盲评判 LoRA 更接近原文写法 18/20,判为改动了原意 LoRA 6/20、基线 11/20。

PEFT 这一版改得比 MLX 那一版更保守(“伊”一次也没出现)。主要原因是 LoRA 的缩放系数不同:train_peft.py 用的是常见的 lora_alpha = 2 × rank,相当于缩放 2;mlx-lm 默认的缩放是 20。想让 PEFT 改得更大胆,可以在 train_peft.py 里把 lora_alpha 调大,或者多训几遍。

这张表要和下面两样一起看才说得清:

字面相似度在这份数据上不太敏感,因为默认输出本来就接近原文;真正的判断要靠盲评。换成你自己的数据时也一样:先自己做一遍盲评表,再看数字。

换成你自己的数据

把你的配对写成三个文件,放在一个目录里(例如 my_data/),每行一个 JSON:

{"ai": "模型默认写出来的一段", "original": "你改过的版本"}

然后:

python train_mlx.py --data my_data          # 或 train_peft.py --data my_data
python evaluate.py --backend mlx --data my_data --markers "你常用的词1,你常用的词2"

配对从哪来:最直接的是把你平时改过的稿子攒下来,一边是模型给你的初稿,一边是你最后发出去的版本。攒不到那么多,可以学示例数据的做法:拿你自己写过的东西,让模型用它的默认写法改写一遍,配成“模型写法 → 你的原文”。提示格式(common.py 的 INSTRUCTION)在训练和推理时必须一致,要改就两边一起改。

盲评表在 results/ 下的 *_blind.md,答案在同名的 *_key.json。先自己选完再看答案。