配方 / Recipe
Table | |
|---|
| 基座 Base | YFC-112358/Qwen3.8-27B-Della-Carnice-Ostrich-Salience-Glimmer-v5 |
| Revision | ad9c0b130bdd0cebcbeeb5f9aa52e90ea9d80089(checksum 校验过) |
| 架构 Arch | Qwen3_5ForConditionalGeneration,64 层 = 48 Gated DeltaNet + 16 full attention |
| 挂载模块 Adapted | 496 个 nn.Linear,12 种后缀,组件覆盖 mlp 64 / linear_attn 48 / self_attn 16 |
| 单侧 Per side | r = 16, alpha = 16, 116,727,808 可训练参数(占全模型 0.425%) |
| 最终 Final | cat 合并,r = 32, alpha = 32, scaling = 1,系数 a = 1.0(Glimmer)/ b = 1.0(Gemma) |
ΔW = a·ΔW_glimmer + b·ΔW_gemma。权重已经乘进 A 矩阵,加载时不需要任何额外设置。
为什么是 496 个模块
这个基座 64 层里有 48 层是 Gated DeltaNet,投影叫
in_proj_qkv / in_proj_z / in_proj_b / in_proj_a / out_proj。
Unsloth 官方 Qwen3.5 文档给的默认 target_modules 是
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj 七个后缀——
照抄的话这 48 层一个 LoRA 都挂不上,训练照跑、loss 照降、味道就是淡。
模块名单是在真模型上枚举出来的,训练前后各断言一次覆盖。
48 of the 64 layers are Gated DeltaNet with non-standard projection names.
The default target_modules from Unsloth's Qwen3.5 docs would leave all 48
unadapted. The list here was enumerated from the loaded model and asserted
both before and after get_peft_model.
数据 / Data
Table with columns: 侧, 来源, 条数, 可训练 token| 侧 | 来源 | 条数 | 可训练 token |
|---|
| Glimmer | DaoCloud/Muse-Glimmer-OPB-100K | 1,396 | 1,501,526 |
| Gemma | ZachW/gemma-4-31b-it_* 七个仓(cc-by-4.0)+ trjxter/Gemma-4-31B-Reasoning-1000x | 1,349 | 1,388,170 |
两侧都是真思考轨迹,思考通道计入 loss,占可训练 token 的 100%。
序列长度 4096,不打包(Gated DeltaNet 的循环状态会跨样本泄漏)。
数学 token 上限 15%,单来源上限 25%,对 GSM8K 与 MATH-500 做 13-gram 去污。
Glimmer 侧开了 STRIP_ECHO:源数据的思考段常常先把题面复述一遍,
这不是语气而是格式冗余,在推理时要花真金白银的 token,所以剥掉。
剥离规则保守:逐词对得上且覆盖题面 80% 以上才动手。
一次失败的先前尝试(保留在这里作为记录)
第一轮 Gemma 侧用了 BCCard/gemma-4-31B-on-policy-600k(约 8 万条)加
trjxter 的 995 条。按 token 配额等比例抽样之后,911 条样本里只有 12 条带真思考,
真 CoT 只占可训练 token 的 0.8%。那个 adapter 学到的是 BCCard 的答案语气,
不是 Gemma 的思考方式。整轮作废重做,并加了一条
「真 CoT 占可训练 token 不低于 90%」的硬断言。
First attempt: the Gemma side drew proportionally from an 80k answer-only pool,
so real chain-of-thought ended up at 0.8% of trainable tokens. Discarded and
rebuilt with a hard assertion on the CoT share.
训练 / Training
单卡 A800 80GB,两侧合计 2.56 GPU 小时。
Table with columns: Glimmer, Gemma | Glimmer | Gemma |
|---|
| epoch | 2 | 2 |
| 优化步 | 122 | 124 |
| batch × 累积 | 1 × 23 | 1 × 22 |
| lr | 1e-4 cosine, warmup 3% | 同左 |
| 峰值显存 | 77.65 GiB | 77.99 GiB |
| token/s | 732 | 710 |
BF16,adamw_8bit,梯度检查点。两侧的可训练 token 配额相同,
优化步数也对齐到约 120,这样两个 ΔW 受到的优化量可比,混合的系数才有意义。
实测 / Measurements
1. 交叉 NLL 矩阵
各自的留出集(Glimmer 116 条 / Gemma 99 条),越低越好,括号是相对基座的变化。
Table with columns: Glimmer 留出, Gemma 留出 | Glimmer 留出 | Gemma 留出 |
|---|
| 基座 base | 0.9369 | 0.6645 |
| Glimmer-LoRA | 0.5043 (−46.2%) | 0.8135 (+22.4%) |
| Gemma-LoRA | 0.9712 (+3.7%) | 0.5292 (−20.4%) |
单用任一侧都会把对方那一侧推高。两个 ΔW 在 Gemma 留出集上接近对冲,
这是下面配比扫描的物理基础。
2. 配比扫描
11 个 (a, b) 组合。「增益」= 该点相对基座的 NLL 降幅 ÷ 那一侧 LoRA 单用时的降幅。
★ 是帕累托前沿。
Table with columns: a, b, Glimmer 增益, Gemma 增益| a | b | Glimmer 增益 | Gemma 增益 | |
|---|
| 0.50 | 0.90 | 70% | 89% | ★ |
| 0.60 | 1.00 | 75% | 85% | ★ |
| 0.60 | 0.80 | 81% | 81% | ★ |
| 0.90 |
对角线不是最优:(0.6, 0.8) 在两个轴上都优于 (0.75, 0.75)。
斜率上,固定 b=0.4 时 a 每加一个单位 Gemma 掉约 235 个百分点;
固定 a=1.0 时 b 每加一个单位 Gemma 涨约 143 个百分点。
3. 为什么最终没选 NLL 上最平衡的那个点
(0.6, 0.8) 在矩阵上是 81%/81%,但自由生成时它把预算全烧在思考里:
Table with columns: 900 token 预算内的答案长度, 代码, 长文, 解释, 中文短, 中文开放| 900 token 预算内的答案长度 | 代码 | 长文 | 解释 | 中文短 | 中文开放 |
|---|
| (0.6, 0.8) | 634 | 200 截断 | 216 | 75 截断 | 29 |
| (1.0, 1.0) | 596 | 269 | 306 | 84 | 504 |
思考/答案比:基座 2.50,Gemma-LoRA 2.24,(0.6,0.8) 2.55,
Glimmer-LoRA 0.83,(1.0,1.0) 0.91。
b/a = 1.33 把 Gemma「在思考里反复起草」的习惯放大,而 Glimmer
「想省点、直接答」的压缩力被压下去。NLL 是在教师强制的留出文本上算的,
量不到自由生成时的预算分配,所以这一层必须靠读文本才发现。
4. 抄题与复读(盲测,采样 0.7/0.95,每格两次)
Table with columns: 抄题, 最大 6-gram 复读, 收尾率, 思考/答案 | 抄题 | 最大 6-gram 复读 | 收尾率 | 思考/答案 |
|---|
| 基座 | 0/8 | 1 | 88% | 2.50 |
| Glimmer-LoRA(第一轮,未剥抄题) | 8/8 | 3 | 100% | 1.31 |
| Glimmer-LoRA(本版,剥了抄题) | 1/8 | 2 | 100% | 0.83 |
「抄题」= 思考开头把题面整段复述。STRIP_ECHO 把它从 8/8 降到 1/8。
5. reasoning_effort 单调性
量的是思考 token(</think> 之前),两道题各一次采样。
Table with columns: 数学题 xhigh/low, 开放题 xhigh/low, 两题都单调 | 数学题 xhigh/low | 开放题 xhigh/low | 两题都单调 |
|---|
| 基座 | 0.36 | 0.38 | 否 |
| Glimmer-LoRA | 2.90 | 11.22 | 是 |
| Gemma-LoRA | 1.59 | 6.49 | 否 |
| 本 LoRA (1.0/1.0) | 2.33 | 4.09 | 是 |
基座在 xhigh 档下思考反而最短,两道题同向(0.36 / 0.38),
也就是这个控制面在基座上是反的。本 LoRA 把它修成单调,
且 xhigh 的思考量约为 low 的 3.2 倍。
这是本项目里唯一一处「修好了基座本来就坏的东西」的观察。样本很薄(每格一次),
当趋势看,不当结论。
6. 中文回归
判据是答案通道的汉字占比。
Table with columns: 题, 基座 答案字符 / 汉字比, 本 LoRA 答案字符 / 汉字比| 题 | 基座 答案字符 / 汉字比 | 本 LoRA 答案字符 / 汉字比 |
|---|
| 布朗运动 | 170 / 84% | 140 / 80% |
| 句子精简 | 130 / 59% | 48 / 67% |
| 红楼梦人物分析 | 0 / 0% | 402 / 84% |
第二题两边的汉字比都偏低,是因为答案里混了 markdown 标记,不是飘英文。
第三题基座在 700 token 预算内没写完思考,答案是空的;本 LoRA 写出了 402 字。
中文没有退化。 但要说清楚:思考通道经常是英文的(汉字占 77% 到 83%,
其中相当一部分是引用的中文原文)。这是 Gemma 的双语习惯被忠实迁移的结果,
本项目把它当作风格的一部分接受,不当退化处理。
用法 / Usage
from transformers import AutoTokenizer, Qwen3_5ForConditionalGeneration
from peft import PeftModel
BASE = "YFC-112358/Qwen3.8-27B-Della-Carnice-Ostrich-Salience-Glimmer-v5"
REV = "ad9c0b130bdd0cebcbeeb5f9aa52e90ea9d80089"
tok = AutoTokenizer.from_pretrained(BASE, revision=REV)
model = Qwen3_5ForConditionalGeneration.from_pretrained(
BASE, revision=REV, dtype="bfloat16", device_map={"": 0})
model = PeftModel.from_pretrained(model, "YFC-112358/Qwen3.8-27B-Taste-Glimmer-Gemma-LoRA")
msgs = [{"role": "user", "content": "..."}]
text = tok.apply_chat_template(msgs, tokenize=False,
add_generation_prompt=True,
reasoning_effort="medium")
reasoning_effort 只接受 low / medium / xhigh。传 high 会抛 TemplateError。
配比是事后可调的:两侧的单独 adapter 没有随本仓发布,需要的话开 issue,
我可以另发。拿到之后用 add_weighted_adapter(..., combination_type="cat")
传 weights=[a, b] 就能自己合。
cat 合并把权重直接乘进 A,所以合出来就是 a·ΔW_g + b·ΔW_m,scaling 恒为 1。
需要单卡 80GB 及以上跑 BF16(基座权重 51.75 GiB)。
没测过的 / Not tested
诚实起见,下面这些一律没有数据,别当它们是好的:
- 基准分。一个都没跑。数据做过 13-gram 去污(对 GSM8K 与 MATH-500),
但没做完整去污审计,所以也不适合拿去发榜。
- 多轮对话。全部测试都是单轮。
- 工具调用 / 函数调用。没测。
- 安全与拒答行为。没有做任何红队或拒答测试。基座血统里有多个
uncensored / abliterated 来源,本 LoRA 没有针对这一点做任何事,
也没有量过它有没有让情况变好或变坏。
- 长上下文。训练序列长度 4096,超出这个长度的行为没测。
- 视觉输入。基座是 VLM,但本 LoRA 只挂在语言侧,视觉塔 110 个线性层
一个都没动,纯文本数据训练。视觉能力应该不变,但没有验证。
- 代码正确性。盲测里有一道代码题,读起来是对的,但没有跑过测试。
样本量也要说清楚:盲测每格两次采样,effort 和中文回归每格一次。
所有主观判断都由一个人完成,没有盲评小组。
复现 / Reproduction
全流程在浏览器里完成(Colab CPU 处理数据,AutoDL 网页版 JupyterLab 训练),
不需要本地环境。各阶段的 notebook 和完整的实测记录见项目文档。
关键的几处防线,是踩过坑之后加的:
- 模块名单从真模型枚举,训练前后各断言一次组件覆盖
lora_B 训完非零的模块数必须 ≥ 90%(B 初始化为零,还是零就说明没拿到梯度)
- 真 CoT 占可训练 token 不低于 90%
- 单来源不超过 25%(来源只有一个时自动失效)
- 存盘后用
adapter_config.json 里的 target_modules 重选一遍,
必须还是同一批 496 个模块
许可 / License
Adapter 权重按 apache-2.0 发布。
请注意上游的许可情况需要使用者自行确认:基座是多个模型的合并,其血统中
KyleHessling1/Qwopus3.6-27B-Fusion-BF16 标的是 other / qwen;
Gemma 侧训练数据来自 cc-by-4.0 的数据集,Glimmer 侧来自
DaoCloud/Muse-Glimmer-OPB-100K。本项目没有对上游许可做法律审查。