public-sft-qwen9b-v1 · Qwen3.5-9B × Dolly-15k LoRA 适配器(实验产物)
English TL;DR. A LoRA adapter (r=32, alpha=64) for
Qwen/Qwen3.5-9B, trained with QLoRA for 3 epochs / 450 optimizer steps on a 2,400-example subset ofdatabricks-dolly-15k(CC-BY-SA-3.0, https://huggingface.co/datasets/databricks/databricks-dolly-15k). The adapter weights are released under apache-2.0; the dataset's own license is not changed by this release. Training only: no benchmark evaluation was run. This is a lab experiment, not a product.
本仓库是一个 LoRA 适配器:在 Qwen/Qwen3.5-9B 上,用公开数据集 databricks-dolly-15k 的 2400 条子集做了一轮指令微调(SFT)。
只做了训练,没有做任何基准评测。 本卡所有数字都取自这一轮训练的实际产物(训练记录、训练元数据、适配器文件本身),以及对数据筛选的复算;没有估算值。
Provenance 一览
| 项 | 内容 |
|---|---|
| 基座模型 Base model | Qwen/Qwen3.5-9B,HF 快照 commit c202236235762e1c871ad0ccb60c8ee5ba337b9a(训练机本地缓存中唯一的快照,refs/main 指向它)。基座自身许可为 apache-2.0 |
| 数据集 Dataset | databricks-dolly-15k:https://huggingface.co/datasets/databricks/databricks-dolly-15k |
| 数据快照 / 筛选 | 上游 databricks-dolly-15k.jsonl(sha256 2df9083338b4abd6bceb5635764dab5d833b393b55759dffb0959b6fcbf794ec,15011 条)→ 丢弃经 chat template 渲染后超过 1024 token 的 210 条 → seed=42 洗牌后取 2400 条 |
| 数据许可 Data license | CC-BY-SA-3.0(Creative Commons Attribution-ShareAlike 3.0 Unported;Copyright (2023) Databricks, Inc.)。原数据集许可不因本发布而改变。 |
| 权重许可 Weights license | apache-2.0(仅指本仓库的 LoRA 适配器权重) |
| 训练配置 Training | QLoRA(基座以 4-bit NF4 加载)· LoRA r32 / α64 / dropout 0.05 / targets auto · seq 1024 · batch 4 × grad_accum 4 · lr 1e-4(cosine)· warmup 0.03 · 3 epoch = 450 步(steps) · seed 42 · bf16 + Liger kernel · 单卡 |
| 已知局限 Limitations | 只训 3 epoch / 450 步 / 2400 条;未做任何评测;单一 seed;只有英文数据;不是产品。详见下文「已知局限」 |
文件 Files
| 文件 | 说明 |
|---|---|
adapter_model.safetensors |
LoRA 适配器权重,fp32,496 个张量 / 86,556,672 个参数,346,302,176 字节,sha256 70e1b6560032be80ba1a2ccf8b0d7037cd179d426046aab33d2efb230d31c53a(= 第 450 步,也就是最终 checkpoint) |
adapter_config.json |
PEFT 配置(由 peft 0.20.0 写出) |
tokenizer.json / tokenizer_config.json / chat_template.jinja |
训练时实际使用的 tokenizer 与对话模板,由 transformers 5.15.0 随适配器一起保存(见下文「tokenizer 说明」) |
不包含:合并后的完整权重、中间 checkpoint(第 100 / 200 / 300 / 400 步)、优化器状态、训练语料或其副本。训练数据请从上游获取。
数据 Data
下面原样引用训练配置文件里的 provenance 注释。「……」处省略的是与公开数据无关的内部路径说明,其余文字未改动:
数据集 : databricks-dolly-15k(人工撰写的指令数据,非蒸馏)
来源URL : https://huggingface.co/datasets/databricks/databricks-dolly-15k
原始文件: .../resolve/main/databricks-dolly-15k.jsonl
sha256=2df9083338b4abd6bceb5635764dab5d833b393b55759dffb0959b6fcbf794ec
许可 : license = CC-BY-SA-3.0(Creative Commons Attribution-ShareAlike 3.0 Unported,
HF card 的 `license:` 标签逐字如此;Copyright (2023) Databricks, Inc.,部分 context
源自 Wikipedia 同为 CC-BY-SA-3.0)。—— 许可允许再分发(署名 + 相同方式共享)。
原始行数: 15011 条(raw_records);usable=15011
{instruction(+context)} → user,{response} → assistant 的 messages 两轮对话
(……)。
用 Qwen3.5-9B chat template 实测 token 长度,丢弃 >1024 的 210 条(占 1.4%),
seed=42 洗牌后取 2400 条写入 train_file。子集内 max=1023 tok、0 条超长、逐行 json.loads 全通过。
长度分布(全量 15011):p50=116 / p90=400 / p99=1199 / max=8616(已丢)。
补充三点:
- 上游数据卡 YAML 中的许可标签实际写作小写
cc-by-sa-3.0。上游文件取自main分支,没有记录上游 commit,以上面的 sha256 为准。 - 转换细节:各字段先去掉首尾空白;
context非空时,user 内容为instruction + "\n\n" + context,否则只有instruction。长度用apply_chat_template(..., add_generation_prompt=False)计算;对保留下来的样本(保持原始顺序)做random.Random(42).shuffle,再取前 2400 条。 - 复核:2026-10-14 在 CPU 上按上述规则复算,得到 15011 条 → 超长 210 条(1.4%)→ 取 2400 条,子集最长 1023 token;复算出的 2400 行字节流 sha256 为
ce43f3c6abecb9e4d792c1596e06ce1839f8a29fd424af04758c48456a28f417,与训练实际读取的文件逐字节一致。长度分位数的复算结果也与上面引用的数字一致。
loss 只算在 assistant 回复上(回复正文及其结束标记),user 部分和模板标记不计入。训练样本经对话模板渲染为空思考块加回复,即 <|im_start|>assistant\n<think>\n\n</think>\n\n{response}<|im_end|>。
训练配置 Training
| 参数 | 值 |
|---|---|
| 方法 | QLoRA:基座经 bitsandbytes 以 4-bit NF4(double quant)加载,计算 dtype 为 bf16;训练 bf16=True |
| 基座加载类 | AutoModelForImageTextToText(Qwen3.5 checkpoint 的架构是 Qwen3_5ForConditionalGeneration;加载日志显示缺失权重 0 个) |
| LoRA | r=32,lora_alpha=64,lora_dropout=0.05,bias=none |
| target_modules | auto,解析结果为 q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj、down_proj、in_proj_qkv、in_proj_z、in_proj_a、in_proj_b、out_proj,只覆盖文本解码器(视觉 / 音频塔与 MTP 层已排除) |
| 可训练参数 | 86,556,672(约 86.6M) |
| max_seq_len | 1024(训练集中 0 条被截断) |
| batch | 每卡 4 × grad_accum 4 = 有效 batch 16 |
| optimizer | paged_adamw_8bit |
| lr / schedule | 1e-4,cosine |
| warmup | warmup_ratio 0.03(训练脚本按 int(450 × 0.03) 换算为 13 步) |
| epochs / steps | 3 epoch,每 epoch 150 步,共 450 步 |
| seed | 42 |
| loss 范围 | 只算 assistant 回复 token(all_tokens_loss=false) |
| kernels | Liger kernel(apply_liger_kernel_to_qwen3_5);attention 为 sdpa;开启 gradient checkpointing |
| 硬件 | 单卡(可见显存 23.5 GiB),显存峰值 11.9 GiB |
| 时长 / 吞吐 | 4957 秒(约 1.38 小时),250.5 tok/s;每 epoch 413,852 token |
| 软件 | torch 2.13.0、transformers 5.15.0、peft 0.20.0、bitsandbytes 0.50.1、liger-kernel 0.8.1 |
| 训练日期 | 2026-10-14 |
训练集 loss:第 5 步 1.860,第 450 步 0.966,全程平均 train_loss 1.284(trainer 汇总值)。这只是训练集上的 loss,不是验证或评测指标;本轮没有验证集。
怎么用 Usage
import torch
from transformers import AutoModelForImageTextToText, AutoTokenizer
from peft import PeftModel
base_id = "Qwen/Qwen3.5-9B"
adapter_id = "alfaplus/public-sft-qwen9b-v1"
tok = AutoTokenizer.from_pretrained(adapter_id)
# 用 AutoModelForImageTextToText 加载基座(与训练时一致)。Qwen3.5 的 checkpoint 是 *ForConditionalGeneration,
# 用 AutoModelForCausalLM 加载可能静默漏掉文本解码器权重(只打警告),LoRA 也就挂不对地方。
base = AutoModelForImageTextToText.from_pretrained(
base_id, revision="c202236235762e1c871ad0ccb60c8ee5ba337b9a",
dtype=torch.bfloat16, device_map="auto")
model = PeftModel.from_pretrained(base, adapter_id)
msgs = [{"role": "user", "content": "Give me three tips for writing a clear email."}]
inputs = tok.apply_chat_template(msgs, add_generation_prompt=True, enable_thinking=False,
return_tensors="pt", return_dict=True).to(model.device)
out = model.generate(**inputs, max_new_tokens=256)
print(tok.decode(out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True))
- 这段示例代码没有在本发布流程中实际运行过(准备发布时不占用 GPU)。它的加载方式(
AutoModelForImageTextToText+ PEFT)与训练脚本一致,但请自行验证。 - 训练样本采用
enable_thinking=False的格式(空思考块),思考模式下的行为没有评测。 - 训练时基座是 4-bit NF4 加载的。上例把适配器挂在 bf16 基座上;也可以用同样的
BitsAndBytesConfig(nf4、double quant、bf16 compute)复现训练时的条件。两种方式的差异没有测过。
tokenizer 说明(实测)
本仓库附带的 tokenizer.json 不是基座 tokenizer.json 的逐字节副本。transformers 5.15.0 加载 Qwen3.5 tokenizer 时,用的是内置 Qwen2Tokenizer 的预切分正则(\p{L}+),而基座文件里写的是 [\p{L}\p{M}]+;保存时写出的是前者。实测结果:
- 用 transformers 5.15.0 分别从基座和从本仓库加载 tokenizer,训练集 4800 段文本和 11 条多语种探针的切分完全一致。
- 直接用
tokenizers库读取基座的tokenizer.json,与上面的切分相比,4800 段中有 4 段不同。这 4 段都含组合附加符(例如天城文、带变音符的阿拉伯文)。
也就是说,训练时实际使用的切分就是本仓库附带的这份 tokenizer。如果推理栈不经过 transformers、直接读取基座的 tokenizer 文件,含组合字符的文本会被切得和训练时不同。
已知局限 Limitations
- 训练量小:只训了 3 epoch / 450 步,只用了 2400 条样例(dolly 全量 15011 条的约 16%)。
- 没有任何评测:只跑了训练,没有做任何基准评测,也没有留出验证集。上面的 loss 只是训练集 loss,既不能说明泛化能力,也不能说明比基座更好或更差;没有与基座做过对比。
- 交付形态:交付的是 LoRA 适配器本体,未合并权重。没有验证过合并或量化(例如 GGUF 低比特量化)之后的效果;低比特量化可能削弱适配器的作用。
- 单一 seed,未做超参搜索:只用 seed 42 跑了一次,超参数没有调过。
- 训练与推理条件:训练时基座是 4-bit NF4;在 bf16 等其他精度的基座上效果未测。思考模式(thinking)未测。
- 语言:训练数据是英文指令数据(上游卡片注明为 American English),不代表多语言能力;对其他语言的影响没有测过。
- 只有文本:只训练了文本解码器;挂上适配器后,基座的图像 / 视频输入能力是否受影响没有测过。
- 数据本身的局限:上游卡片自述的局限同样适用。部分内容来自 Wikipedia,可能带有 Wikipedia 的偏见、事实错误和话题倾向;部分标注者可能不是英语母语者;标注者构成与题材可能反映 Databricks 员工的构成。
- 没有任何保证:本适配器不构成任何安全或能力上的保证;没有做过安全评测或红队测试。
- 性质:这是实验室的实验产物,不是产品,不承诺支持或维护。
许可 License
- 适配器权重:apache-2.0。 本仓库 YAML 中的
license: apache-2.0指的是适配器权重与本卡。 - 训练数据:CC-BY-SA-3.0。原数据集许可不因本发布而改变。 本仓库不包含 dolly 数据或其衍生副本;如需使用数据,请从上游获取,并遵守 CC-BY-SA-3.0(署名 + 相同方式共享)。数据版权:Copyright (2023) Databricks, Inc.;部分材料来自 Wikipedia(Copyright © Wikipedia editors and contributors),同样采用 CC BY-SA 3.0。
- 基座模型:
Qwen/Qwen3.5-9B按其自身许可(apache-2.0,https://huggingface.co/Qwen/Qwen3.5-9B/blob/main/LICENSE)发布;使用本适配器需要另行获取基座,并遵守基座的条款。
引用 / 出处 Citation
训练数据(引用格式原样取自上游数据卡):
@online{DatabricksBlog2023DollyV2,
author = {Mike Conover and Matt Hayes and Ankit Mathur and Jianwei Xie and Jun Wan and Sam Shah and Ali Ghodsi and Patrick Wendell and Matei Zaharia and Reynold Xin},
title = {Free Dolly: Introducing the World's First Truly Open Instruction-Tuned LLM},
year = {2023},
url = {https://www.databricks.com/blog/2023/04/12/dolly-first-open-commercially-viable-instruction-tuned-llm},
urldate = {2023-06-30}
}