LoRA 与 QLoRA 微调配方实战:从 LoRA Without Regret 参考配方到 Unsloth/TRL 配置落地
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
本篇文章以 agents24 仓库中llm-finetuning插件的 lora-qlora-recipes 技能 为骨架,系统性讲解面向 SFT(监督微调)的 LoRA 与 QLoRA 适配器配置:如何选择 target modules、如何按任务确定 rank 与 alpha、LoRA/QLoRA/全参微调如何抉择、有效 batch size 的边界,以及 Unsloth 快路径与 plain TRL 逃生通道的完整映射。读完你将能独立产出一份"可被llm-finetuning-training-engineer直接消费"的校验过的适配器配置,并规避 fp16 发散、rank 过高过拟合、删减 target modules 等高频失败模式。
该技能在插件工作流中处于承上启下的位置:finetuning-method-selection已经完成路由判定(数据形状是 demonstrations 而非偏好对或可验证奖励信号,因此走 SFT),本技能负责产出适配器本身的最佳实践配置;数据集的准备与质量校验属于 dataset-curation 的范畴;而最终消费这份配置、生成可运行脚本的是 llm-finetuning-training-engineer。
前置路由:为什么你会走到 LoRA/QLoRA 配方
本技能明确假设路由判定已经发生。finetuning-method-selection的决策树(SKILL.md)会先排查"off-ramp"——事实易变(价格、文档、新闻)走 RAG,行为尚未定型走 prompt engineering,稳定稠密领域知识且文本量在 500MB–10GB 之间才考虑 CPT 后接 SFT;只有当手上数据形状是input/output demonstrations时,路由才会指向本技能:
| 数据形状 | 路由 |
|---|---|
| 事实频繁变化(价格、文档、新闻) | RAG,而非微调 |
| 期望行为仍在摸索 | Prompt engineering |
| 稳定的领域知识,≥500MB 文本 | CPT 后接 SFT |
| 有 input/output demonstrations | SFT —— 见 lora-qlora-recipes(本文) |
| 有偏好对或 👍/👎 | DPO/ORPO/KTO —— 见 preference-optimization |
| 有可验证的 pass/fail 信号 | GRPO+RLVR —— 见 grpo-rlvr-training |
| 还没有 eval harness | 停下 —— 见 eval-harness-first |
也就是说,本技能的输入是"一条 SFT via LoRA/QLoRA 的路由决策 + 目标模型的尺寸等级";输出是一份校验过的适配器配置(即下面各小节的 kwarg 具体取值,而不是自由式建议),由llm-finetuning-training-engineer在 Phase 4 生成train/config.yaml与train/train.py时直接消费(见 finetune.md 的 Phase 4 描述:按 brief 的方法、基础模型与内存预算生成训练脚本,且两份文件必须先提交再启动)。
参考配方:LoRA Without Regret
本技能的全部配置基准都来自 "LoRA Without Regret"(Thinking Machines / Schulman,2025-09),如今已成为 LoRA/QLoRA SFT 的既定惯例。参考配方的四个支柱是:target modules 全线性、lora_alpha = 2 * r、学习率约为等效全参微调的 10 倍、有效 batch size 小于 32。下面逐一展开。
Target Modules:全线性层,而非仅注意力
参考配方要求全线性模块,不能只盯注意力头:
target_modules = [ "q_proj", "k_proj", "v_proj", "o_proj", # attention "gate_proj", "up_proj", "down_proj", # MLP —— 最关键 ]其中MLP 层(gate_proj、up_proj、down_proj)最值得打——只打注意力层是更早、更弱的旧惯例。为了省内存而删减模块属于下文 Failure Modes 中的失败模式,不是合法优化:适配器参数只占模型总参数的一小部分,砍掉 MLP 三件套几乎不动内存,却会实打实损伤质量。若内存真的吃紧,正确顺序是转 QLoRA、降 rank、降 batch、缩短 packing 长度,而不是修剪 target modules。
Alpha 与学习率
lora_alpha = 2 * r是既定惯例(依据 NeurIPS 2025 的 "intruder dimensions" 结果)。不要脱离 rank 单独手工调 alpha——每次都由 rank 推导而来。例如r=32时lora_alpha=64。- LoRA 学习率 ≈ 等效全参微调学习率的 10 倍。具体到 QLoRA,2e-4 是标准起点。这一点也是从全参微调配置移植到 LoRA 时最常见的单一错误来源——LR 原样照搬会导致适配器欠训练。完整的超参表与工作示例见 references/hyperparameters.md。
Rank 按任务决定,而非全局默认
rank 是任务形状决定的,不存在单一全局默认值:
| 任务 | Rank |
|---|---|
| RL(GRPO/RLVR 适配器) | 1–32 |
| 通用默认 | 16–32 |
| 规模化 SFT | 最高约 256 |
更高 rank 并不自动等于更好——它提升记忆能力的速度与提升泛化能力的速度一样快。应该从任务对应的那一行起步,只有当较低 rank 在 held-out eval 上可测量地欠拟合时才上调一行,而不是把更高 rank 当默认对冲。
references/hyperparameters.md 提供了更细的 rank/alpha 对照表,注意每一行都满足lora_alpha = 2 * r:
| 任务类型 | Rank(r) | lora_alpha | 说明 |
|---|---|---|---|
| RL 适配器(GRPO/RLVR) | 1–32 | 2–64 | 对于已具备能力的底座模型,低位(1–8)很常见 |
| 通用 SFT 默认 | 16–32 | 32–64 | 没有特定理由升高/降低时的起点 |
| 规模化 SFT(大而多样的指令集) | 最高约 256 | 最高约 512 | 只有数据集足够大且多样、用得上额外容量时才合理——参考下方 rsLoRA 注记 |
有效 Batch Size:控制在 32 以下
保持有效 batch size 小于 32。本配方正是在这个规模上完成验证的——把有效 batch 推得更高是未经测试的外推,不是免费的吞吐量收益。
references/hyperparameters.md补充了关键细节:有效 batch 的计算公式是per_device_batch_size * gradient_accumulation_steps * num_devices。多卡或高累积步数的配置可能单卡 batch 看着不大、乘积却已越过 32,所以必须算乘积,不能只看单卡数字。例如下面工作配置中的4 * 4 = 16(单卡)就稳定压在 32 上限之内。
Unsloth 默认值与get_peft_model完整调用
Unsloth 是本插件假定的默认快路径参考实现——唯一例外是"messages 形状的对话式 SFT +assistant_only_loss=True"这一组合:Unsloth 2026.7.x 的编译版 trainer 根本没有 messages 形状路径,此时 plain-TRL 逃生通道(references/unsloth-trl-mapping.md)是默认路径而非罕见回退。这一点下文展开。
Unsloth 的开箱默认值及其设置原因:
lora_dropout=0—— 优化后的内核路径假设零 dropout;设置非零值会放弃融合内核加速。bias="none"—— 偏置项在当前 rank 区间只增加适配器参数,质量收益可忽略。use_gradient_checkpointing="unsloth"—— 这是 Unsloth 自有的 checkpointing 变体,而非原生 HF checkpointing;相对不开 checkpointing 大约节省30% VRAM(对应 memory-math.md 中激活项约 30% 的节省口径)。optim="adamw_8bit"—— 8-bit AdamW 在 LoRA/QLoRA 适配器规模下以可忽略的质量影响削减优化器状态内存(memory-math.md 的估算为:fp32 状态 8 字节/参数,8-bit 约 2 字节/参数,约四分之一)。random_state固定—— 钉住 LoRA 初始化以保证跨运行可复现;把它当种子对待,而不是可调超参。
它们会一起出现在get_peft_model调用上:
model = FastLanguageModel.get_peft_model( model, r=32, target_modules=target_modules, lora_alpha=64, # 2 * r lora_dropout=0, bias="none", use_gradient_checkpointing="unsloth", random_state=3407, )每个 kwarg 的精确名称及其 plain-TRL/PEFT 等价物,以及含SFTConfig的完整工作配置,分别见 references/unsloth-trl-mapping.md 与 references/hyperparameters.md。
LoRA vs QLoRA vs 全参微调:什么时候选什么
| 场景 | 默认选择 |
|---|---|
| 在 demonstrations 上适配行为 | LoRA |
| 基础模型在目标 rank 下装不进 bf16 | QLoRA |
| 注入稠密的新领域知识 | 全参微调(见 finetuning-method-selection) |
| 不确定选哪个 | LoRA——仅在内存逼迫时才升级到 QLoRA |
要点拆解:
- QLoRA= NF4 量化冻结基座权重 + BF16 适配器。这正是 65B 级模型能在 48GB 显存上训练的原因——量化基座才是内存收益来源,适配器本身不是。memory-math.md 给出了量级锚点:8B 级 bf16 LoRA 权重约 16GB,8B 级 QLoRA(int4 NF4)约 4GB(约 4 倍缩减);70B 级 QLoRA 理想公式约 35GB、计入量化元数据与运行时开销后以≈40GB作为真实锚点——这解释了"70B 级走 QLoRA 而非 bf16"(后者仅权重就约 140GB)。
- 全参微调不是默认。把它留给"在权重层面改变模型知识"的稠密知识注入场景;本技能范围内的一切其他场景,LoRA 或 QLoRA 才是起点假设。
- DGX Spark 上 QLoRA 可能比等效 bf16 LoRA 先 OOM,即便 QLoRA 的稳态占用更小——bitsandbytes 的反量化缓冲区是训练加载期间尖峰的瞬态 CUDA 侧分配。因此一个 QLoRA OOM 并不证明模型装不下;
dgx-spark-ops插件的 spark-memory-thermal-ops 技能覆盖完整的 OOM 补救阶梯(下一步该试的是 bf16 LoRA,而不是进一步缩小 QLoRA)。这也与 llm-finetuning-training-engineer 的失败分级一致:UMA OOM 按其 OOM Ladder 固定顺序处置——先 flush,再降 batch 或 packing 长度,然后降级方法(bf16 LoRA 在 QLoRA 之前),且降 batch 永远不是第一步。
完整工作配置:UnslothFastLanguageModel+SFTConfig
以下为r=32通用默认 rank、QLoRA、标准 LR 的完整自洽配置,来自 references/hyperparameters.md(该文件避免指名任何具体基础模型,一律按尺寸等级标注;具体用哪个模型查finetuning-method-selection的references/model-catalog.md):
from unsloth import FastLanguageModel from trl import SFTConfig, SFTTrainer BASE_MODEL = "<from model catalog>" # 由尺寸等级 + 任务决定,不由本文件决定 model, tokenizer = FastLanguageModel.from_pretrained( model_name=BASE_MODEL, max_seq_length=2048, dtype=None, # 按硬件自动检测 bf16/fp16 load_in_4bit=True, # QLoRA 路径 —— bf16 LoRA 时置 False ) target_modules = [ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", ] model = FastLanguageModel.get_peft_model( model, r=32, target_modules=target_modules, lora_alpha=64, # 2 * r lora_dropout=0, bias="none", use_gradient_checkpointing="unsloth", random_state=3407, use_rslora=False, # r=32 阈值 —— 此处保持关闭,除非观察到不稳定 ) import torch # 强制 bf16 前先检查硬件 BF16 支持 —— 见 SKILL.md 失败模式。 # 在 BF16 支持不佳的硬件上用 fp16 训练是 loss 尖峰与静默发散的已知来源, # 因此这是硬性前置条件,不是配置风格选择。 if not torch.cuda.is_bf16_supported(): raise RuntimeError( "This GPU does not support BF16 — do not fall back to " "fp16=True as if it were equivalent; pick hardware with " "BF16 support instead (see SKILL.md Failure Modes)." ) training_args = SFTConfig( output_dir="./outputs", max_length=2048, dataset_text_field="text", per_device_train_batch_size=4, gradient_accumulation_steps=4, # 有效 batch 16(单卡)—— 保持在 32 以下 learning_rate=2e-4, # QLoRA 标准 bf16=True, # 上方已设门禁 —— 绝不 fp16,见 SKILL.md 失败模式 optim="adamw_8bit", num_train_epochs=3, logging_steps=10, seed=3407, ) trainer = SFTTrainer( model=model, processing_class=tokenizer, # 当前 TRL —— 不是 tokenizer= train_dataset=train_dataset, args=training_args, ) trainer.train()这个块是内部自洽的:r=32→lora_alpha=64(2x 规则),load_in_4bit=True→learning_rate=2e-4(QLoRA 标准 LR),bf16=True(绝不 fp16),有效 batch4 * 4 = 16(低于 32 上限)。改动其中任何一项——rank、量化或 batch 形状——都应触发对照上表复查其他项,而不是孤立地编辑。
学习率细表:从哪起步、何时保守
references/hyperparameters.md 的学习率表明确:LoRA/QLoRA 学习率约为等效全参微调 LR 的10 倍,这是移植全参配置到 LoRA 时最常踩的坑——LR 原样照搬会让适配器欠训练。
| 方法 | LR 区间 | 使用时机 |
|---|---|---|
| QLoRA(标准) | 2e-4 | QLoRA SFT 的默认起点 |
| LoRA,保守 | 1e-4 | 更大的基础模型、更高 rank,或 2e-4 下已出现不稳定 |
| LoRA,非常保守 | 5e-5 | 续跑、细粒度行为调整,或基座已接近目标行为 |
这些是围绕其做 sweep 的起点而非固定常数——但要从这里起步,而不是把全参微调的 LR 原样搬过来。
rsLoRA:何时值得打开
Rank-stabilized LoRA(rsLoRA)把适配器更新缩放从alpha / r改为alpha / sqrt(r)。它是可选的,且只在r ≥ 32时才值得开启——低于该 rank,标准缩放已足够稳定,rsLoRA 不会实质改变结果。如果规模化 SFT 那一行(rank 最高约 256)生效,请打开 rsLoRA;通用默认或 RL 行保持关闭,除非观察到特定不稳定。在 PEFT 中对应LoraConfig(use_rslora=True/False),kwarg 名称相同(见 unsloth-trl-mapping.md)。
Unsloth ↔ TRL/PEFT 配置映射:逃生通道的基础
Unsloth 是 PEFT 与 TRL 之上的快速内核封装,不是替代 API——Unsloth 的每个 kwarg 都有对应的 plain TRL/PEFT 等价物。映射表使"回退到 plain TRL"变成机械操作而不是从零重写:
| Unsloth kwarg | TRL/PEFT 等价物 | 说明 |
|---|---|---|
FastLanguageModel.from_pretrained(model_name=...) | AutoModelForCausalLM.from_pretrained(...)+AutoTokenizer.from_pretrained(...) | Unsloth 将模型+分词器加载与内核打补丁融合为一次调用 |
load_in_4bit=True | BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16)传入from_pretrained | 两边都是 QLoRA 路径 |
FastLanguageModel.get_peft_model(r=..., target_modules=..., lora_alpha=..., lora_dropout=..., bias=..., random_state=...) | peft.LoraConfig(r=..., target_modules=..., lora_alpha=..., lora_dropout=..., bias=...)+peft.get_peft_model(model, config);random_state→ 在get_peft_model前设置种子 | Unsloth 的调用是生成同一份LoraConfig的薄封装 |
use_gradient_checkpointing="unsloth" | SFTConfig/TrainingArguments中的gradient_checkpointing=True | Unsloth 变体是同一想法的更快/更低内存实现——不是不同功能。plain TRL 的gradient_checkpointing=True是正确回退,只是 VRAM 节省更少(约少 30%) |
optim="adamw_8bit" | SFTConfig(optim="adamw_8bit") | 相同字符串、相同 bitsandbytes 优化器,无需翻译 |
use_rslora=True/False | LoraConfig(use_rslora=True/False) | 在 PEFT 中同名同 flag |
max_seq_length(传给FastLanguageModel.from_pretrained) | SFTConfig(max_length=...) | 当前 TRL:字段是SFTConfig上的max_length(由max_seq_length更名),不在 trainer 调用或 plain TRL 的from_pretrained上 |
dataset_text_field(Unsloth 示例常在 trainer 上设置) | SFTConfig(dataset_text_field=...) | 当前 TRL:同样位于SFTConfig上 |
random_state=3407(数据/适配器初始化种子) | SFTConfig(seed=3407)做 trainer 级播种 | 两者都设——Unsloth 的random_state专门播种 LoRA 初始化;SFTConfig.seed播种 trainer 自身的 RNG 使用 |
当前 TRL API 的两个注意点
两个近期变更过的 API 面,过时示例(包括部分 Unsloth cookbook 片段)仍在使用旧写法:
- 是
processing_class,不是tokenizer=。SFTTrainer(tokenizer=tokenizer, ...)是旧的、已移除或废弃的形式。当前 TRL 接受SFTTrainer(processing_class=tokenizer, ...)。配置或示例若仍传tokenizer=,运行前必须更新——这是把旧配方向前移植时最常见的 stale-API 错误。 max_length(由max_seq_length更名)与dataset_text_field位于SFTConfig上,而不是散落在 trainer 调用或模型加载器各处。在SFTConfig实例上一次设置好,不要在流水线其他地方重复。
已知的 Unsloth 2026.7.x 限制:真实复现过的四个坑
references/unsloth-trl-mapping.md 记录了在 Unsloth 2026.7.2(transformers 5.13.1,trl 1.8.0)上训练真实 messages 形状 SFT 时确认的四个缺口——每个都在真实 load/train 中复现过,均非假设:
1.assistant_only_loss=True时没有 messages 形状路径
Unsloth 的编译版SFTTrainer(从unsloth被导入的那一刻起就进程级 monkeypatch 到trl.SFTTrainer上,进程内不可逆,且不因是否实际使用FastLanguageModel而门控)自带手写_prepare_dataset,只按列名识别四种数据集形状:预分词(input_ids/labels)、prompt+completion、扁平dataset_text_field、或返回预渲染字符串的formatting_func。messages 形状的对话式数据集路径完全不存在。formatting_func只能返回扁平文本,这迫使在 trainer 看到每轮边界之前就预渲染聊天模板——正是 dataset-curation 的references/formats-and-templates.md所警告的扁平文本反模式:对整个序列计算 loss,使assistant_only_loss失效。修复:使用下方 plain TRL + PEFT 逃生通道——这不是可以等待的罕见 point-release 回归,而是 Unsloth 2026.7.x 对该组合(messages 数据集 +assistant_only_loss=True+ 不打包)的当前状态。经两次独立运行确认:Unsloth 路径在 trainer 构造时立即报错;同样超参在从不 importunsloth、改用 plaintransformers.AutoModelForCausalLM+peft.LoraConfig/get_peft_model+trl.SFTTrainer后干净地端到端跑通。
2.attn_implementationkwarg 被静默丢弃
FastLanguageModel.from_pretrained(..., attn_implementation="sdpa")不能可靠地强制 SDPA。Unsloth 的加载器调用自己的注意力解析辅助函数,不转发调用者的attn_implementation,随后干脆丢弃该 kwarg——因此只要可导入的 flash-attn 构建存在,无论请求什么都会被自动选中。已确认:显式传attn_implementation="sdpa"仍解析为model.config._attn_implementation == "flash_attention_2"。唯一可用的覆写是在调用from_pretrained前 monkeypatch——要严格限定作用域,因为HAS_FLASH_ATTENTION是模块级全局,还会影响同一进程后续的任何其他from_pretrained调用(同一脚本或 notebook 单元中的第二次模型加载会静默继承该 flag 上次的值):
import unsloth.models._utils as unsloth_utils _original = unsloth_utils.HAS_FLASH_ATTENTION try: unsloth_utils.HAS_FLASH_ATTENTION = False model, tokenizer = FastLanguageModel.from_pretrained(...) assert model.config._attn_implementation == "sdpa", ( f"expected sdpa, got {model.config._attn_implementation}" ) finally: unsloth_utils.HAS_FLASH_ATTENTION = _original这仅在try块期间把解析器逼到 SDPA 分支,即使from_pretrained抛错也会在finally中恢复原值,并断言解析器确实落在 SDPA 而非静默穿透。在 plain TRL/PEFT(上面的逃生通道)中,传给AutoModelForCausalLM.from_pretrained的attn_implementation="sdpa"会被正确遵守——这是 Unsloth 专属缺口,不是 TRL 的通用问题。
3.padding_free与 plain-TRLSFTConfig的冲突
把 plaintrl.SFTConfig(max_length=1024, packing=False, ...)(即不碰padding_free、与 TRL 文档默认padding_free=False一致)传入 Unsloth 的编译版 trainer,仍可能抛出ValueError: When padding_free=True without packing, max_length is not enforced...。Unsloth 自带的编译版SFTConfig等价 dataclass 默认padding_free = None,其解析路径中的某处即使对由 plaintrl.SFTConfig构建的args实例也会把它变成真值。修复:无论走哪条路径,只要经过 Unsloth 训练就显式传padding_free=False——廉价保险。
4. TRL 的聊天模板自动打补丁是精确字符串匹配
在抛出 dataset-curation SKILL.md 所述"template lacks{% generation %}"错误之前,TRL 1.8.0 的SFTTrainer.__init__会调用内部get_training_chat_template(),尝试用约 18 个硬编码的已知模型训练模板之一(trl.chat_template_utils)按 tokenizerchat_template的精确字符串相等做替换。若模型自带的模板与表项不逐字匹配——即便几乎相同——自动打补丁会静默失败,TRL 抛错。修复模式:手工给 tokenizer 实际模板的副本打补丁——在助手轮内容区间外包上{% generation %}...{% endgeneration %}标记(角色标记在区间外、turn 结束 token 在区间内,与 TRL 的is_chat_template_stop_token_trained检查一致),保留真实模板的每个分支(工具调用、逐轮特例处理)——通用回退常量不会有这些。将打补丁后的模板仅内存写入tokenizer.chat_template,绝不覆写基础模型目录自带的模板文件。
逃生通道:何时回退到 plain TRL
对 messages 形状 SFT +assistant_only_loss=True,这是上文 Known Limitations 部分规定的默认路径,而非最后手段的回退。对其他所有训练模式,Unsloth 发布快速 point release,某个 point release 偶尔会在特定模式(collator、chunked-loss 路径、特定模型架构)上回归,直到下个补丁修复。无论哪种情况,流程一致:
- 窄范围复现——确认问题出在 Unsloth 封装而非底层配置(rank、alpha、LR、target modules 仍原样适用)。
- 直接回退到 plain TRL + PEFT——用上方映射表把每个 Unsloth kwarg 翻译成 TRL/PEFT 等价物。超参不变——只是由哪个库来设置它们。
- 补丁落地后重新钉回 Unsloth——仅针对真正的回归所覆盖的模式;先查 Known Limitations 一节——结构性缺口(如 messages 形状路径)不会在下一个 point release 自动解决,除非 changelog 条目确认。
llm-finetuning-training-engineer的方法部分(agent 定义)正是这样执行:默认按 Unsloth 快路径生成脚本;当 point-release 回归迫使回退时,走lora-qlora-recipes的references/unsloth-trl-mapping.md中的逃生通道流程,而不是凭记忆手工翻译配置。
失败模式:三个"看起来像训练 bug、实则是配置问题"的坑
本技能总结了三个高频失败模式,且它们共享同一模式:表面看像训练循环 bug(loss 尖峰、平台期、记忆化),实际都是违背了上文参考配方的配置选择。在调试训练循环本身之前,先对照本技能检查配置:
1. 非 BF16 GPU 上的 fp16 发散
在不具备扎实 BF16 支持的硬件上用 fp16 训练,是 loss 尖峰与静默发散的已知来源。凡硬件支持处强制bf16=True;不要把回退到 fp16 当成等价方案。选 dtype 前先检查硬件支持:
python -c "import torch; print(torch.cuda.is_bf16_supported())"这与llm-finetuning-training-engineer的失败分级第一类(Divergence)完全对应:先确认bf16=True与硬件 BF16 支持,fp16 在不支持 BF16 的硬件上是已知的静默发散源。references/hyperparameters.md的工作配置甚至把该检查做成了硬门禁——不支持 BF16 就直接抛RuntimeError,绝不静默回退 fp16。
2. 小数据集上 rank 过高导致过拟合
为"规模化 SFT"(最高约 256)挑选的 rank,用在背后没有规模的 dataset 上,是记忆化而非泛化。rank 要对照上文的 Rank by Task 表匹配任务,而不是挑最大可用数字。
3. 删减 target modules 省内存:质量受损、节省可忽略
gate_proj/up_proj/down_proj上的适配器参数只占模型总尺寸的一小部分——砍掉它们几乎不动内存,却可测量地损害质量。内存吃紧时,先转 QLoRA、降 rank/batch/pack 长度,最后才考虑修剪 target modules。
与数据集侧的分工:模板、masking 与 packing
本技能明确不管数据集准备——那是 dataset-curation 的职责。但配置 LoRA/QLoRA 的人需要理解两个直接影响本配置质量的交叉点:
- 模板先于拼接:任何拼接或 packing 之前应用目标模型的聊天模板;先 pack 原始文本再对打包后的 blob 做模板会破坏轮次边界,使角色标记错位。会话数据保持
messages形状、由 trainer 负责模板化与 masking(当前 TRL 的assistant_only_loss=True)——预渲染成扁平文本字段会毁掉 masking 所需的轮次边界。 - packing 的强制检查:packing 可消除 40–70% 的 padding 计算浪费,但会改变 batch 语义与 LR schedule 里程碑(应按 packed-sequence 数量重算)。启用 packing 前必须解码并人工检查 5–10 条 packed 序列,确认示例边界、模板标记与 assistant-only loss mask 都完好——packing bug 是静默的(loss 曲线看似正常),数小时后才在 eval 质量上暴露。
dataset-curation的 Phase 2 Exit Checklist 六项(格式匹配方法、模板先于拼接、loss 仅 assistant 轮、5–10 条 packed 序列已解码检查、≥25% 真实数据、数据集卡六字段齐全)正是/finetune命令在启动训练前检查的关卡。
结语:这份配置在整个微调生命周期中的位置
在llm-finetuning插件与/finetune命令的七阶段生命周期(finetune.md)中,本技能产出的是 Phase 4 训练阶段的核心输入:一份校验过的 LoRA/QLoRA 适配器配置。路由由finetuning-method-selection完成(Phase 1),数据集卡与验证报告由dataset-curation交付(Phase 2),环境预检(Phase 3,DGX Spark 走dgx-spark-ops的 preflight 与 G1–G10 检查,其他硬件走 generic-nvidia 检查)先于脚本生成,而 Phase 5 的 checkpoint 门禁与 Phase 6 的导出则分别属于checkpoint-promotion与quantized-export。理解了本文的 target modules、rank/alpha、LR、batch、Unsloth 默认值与逃生通道,你就能在任何一步出问题时,把"看起来像训练 bug"的现象快速定位回配置层面——这正是本技能存在的意义。
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考