跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

Qwen3.5-9B 微调避坑:LLaMA-Factory 企业 SFT 10 个常见问题

Qwen3.5-9B 微调实践中常遇模板选择错误、数据格式污染、配置参数不当等问题。基于 LLaMA-Factory 框架,总结从环境搭建、数据清洗、训练配置到模型合并及 vLLM 部署的全链路 10 个关键踩坑点。涵盖 qwen3_nothink 模板使用、Base 与 Instruct 模型区别、LoRA rank 与学习率调优、tokenizer 文件完整性检查等实操细节,提供修复命令与配置建议,助力企业高效完成 SFT 微调。

赛博行者发布于 2026/4/11更新于 2026/7/2756 浏览
Qwen3.5-9B 微调避坑:LLaMA-Factory 企业 SFT 10 个常见问题

适用版本:LLaMA-Factory v0.9.4(2025-12-31)、Transformers v5、PyTorch CUDA 13
训练模型:Qwen3.5-9B(2026-03-02 发布)
硬件环境:单卡 A100 40G(LoRA)/ 单卡 RTX 4090 24G(QLoRA)

前言

用 vLLM 把 Qwen3.5-9B 跑起来之后,下一步想让模型学会公司特定的回复风格和专业术语,这就得微调了。LLaMA-Factory 是目前最主流的开源微调框架(ACL 2024 论文,GitHub 40k+ Star),支持 100+ 模型、多种训练方法,不写一行代码就能开始训练。

听起来很美好,实际上坑不少。Qwen3.5-9B 本身有推理模式/非推理模式之分,加上 v0.9.4 版本的不少 API 变更,网上的教程很多已经过时。

本文记录从数据准备到 vLLM 部署的全链路 10 个真实踩坑,每个坑都给出可直接用的修复命令或配置。

一、环境搭建

v0.9.4 推荐安装方式:改用 uv

v0.9.4 把包管理器从 pip 迁移到了 uv,旧的 pip 安装方式会遇到依赖冲突(尤其是 Transformers v5 和旧版 bitsandbytes 不兼容):

# 1. 安装 uv(如果还没装)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. clone 仓库(--depth 1 只拉最新提交,省带宽)
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory

# 3. 创建虚拟环境并安装(uv 自动处理 CUDA 版本匹配)
uv venv --python 3.12
source .venv/bin/activate
uv pip install -e ".[torch,bitsandbytes,metrics]"

# 4. 验证安装
llamafactory-cli version
# v0.9.4 还支持快捷命令 lmf(等价于 llamafactory-cli)
lmf version

Transformers v5 兼容性:v0.9.4 已全面迁移到 Transformers v5,如果你机器上有旧项目依赖 Transformers v4,务必用 uv venv 创建独立虚拟环境,不要混用。

从 ModelScope 下载模型(国内推荐)

Qwen3.5-9B 在 HuggingFace 下载经常断线,推荐走 ModelScope:

# 设置环境变量,llamafactory-cli 自动走 ModelScope
export USE_MODELSCOPE_HUB=1

# 模型 ID 格式:Qwen/Qwen3.5-9B-Instruct(ModelScope 和 HuggingFace 同名)
# 也可以提前手动下载
pip install modelscope
modelscope download --model Qwen/Qwen3.5-9B-Instruct --local_dir /data/models/Qwen3.5-9B-Instruct

二、Qwen3.5-9B 的 Template 选择陷阱(最高频踩坑)

坑 1:template 选错,模型输出满是 \u003cthink\u003e 标签

Qwen3.5-9B 继承了 Qwen3 系列的双模式设计:思维链模式和普通对话模式,在 LLaMA-Factory 里对应两个不同的 template:

template模式行为
qwen3思维链模式(默认)输出包含 `` 推理过程,再给出最终答案
qwen3_nothink普通对话模式直接输出答案,无思维链

企业 SFT 的数据通常是"问题 - 答案"对,不含思维链。如果用 qwen3 模板训练,模型会在输出里疯狂生成 `` 思维链标签。用这样的数据训练 qwen3_nothink 模板,模型会学到错误的输出格式。

# 数据清洗脚本:去除  及其内容
    cleaned = re.sub(r'', '', text, flags=re.DOTALL)
    return cleaned.strip()

with open('raw_data.json') as f:
    data = json.load(f)
    cleaned = []
    for item in data:
        for conv in item['conversations']:
            if conv['from'] == 'gpt':
                conv['value'] = clean_think_tags(conv['value'])
        # 过滤掉清洗后答案为空的条目
        if all(c['value'].strip() for c in item['conversations']):
            cleaned.append(item)

with open('company_qa_clean.json', 'w', encoding='utf-8') as f:
    json.dump(cleaned, f, ensure_ascii=False, indent=2)
    print(f"原始:{len(data)} 条,清洗后:{len(cleaned)} 条")
坑 4:数据量不足导致严重过拟合

企业 SFT 的数据量往往有限,常见误区是凑不够数据就把同一批数据跑很多 epoch,导致:

  • 训练 loss 极低(< 0.1),但实际输出只会复读训练集
  • eval loss 反弹上升,模型泛化能力崩溃

各规模数据量建议:

目标最少数据量推荐数据量epoch
回复风格迁移200 条500~1000 条2~3
领域术语学习500 条2000~5000 条2~3
复杂业务逻辑2000 条5000~20000 条1~2

数据质量比数量更重要,100 条高质量标注的价值远超 1000 条机器生成的低质量数据。

坑 5:中文标点和特殊字符导致 JSON 解析失败

企业数据往往从 Word/Excel 导出,含有大量"弯引号"、全角空格、零宽字符,这些会让 LLaMA-Factory 的数据加载直接崩溃,且报错信息非常不友好(只显示 JSONDecodeError,不指出具体位置)。

# 数据预处理:清洗不可见字符和特殊标点
import json, unicodedata

REPLACEMENTS = {
    '\u201c': '"', '\u201d': '"', # 弯引号
    '\u2018': "'", '\u2019': "'", # 弯单引号
    '\u3000': ' ', # 全角空格
    '\u00a0': ' ', # 非断行空格
    '\ufeff': '', # BOM
    '\u200b': '', '\u200c': '', # 零宽字符
}

def clean_text(text: str) -> str:
    for old, new in REPLACEMENTS.items():
        text = text.replace(old, new)
    # 去除 Unicode 控制字符(保留换行和制表符)
    text = ''.join(c for c in text if unicodedata.category(c) != 'Cc' or c in '\n\t')
    return text.strip()

# 验证 JSON 可解析性
try:
    with open('company_qa_clean.json', encoding='utf-8') as f:
        data = json.load(f)
        print(f"✅ JSON 格式正确,共 {len(data)} 条")
except json.JSONDecodeError as e:
    print(f"❌ JSON 解析失败:{e}")

四、训练配置踩坑

核心训练配置(YAML,A100 40G 单卡 LoRA)
# qwen35_9b_lora_sft.yaml
### 模型
model_name_or_path: /data/models/Qwen3.5-9B-Instruct
trust_remote_code: true
flash_attn: fa2 # FlashAttention-2 加速,A100 必开

### 方法
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 16 # 见坑 6
lora_alpha: 32 # 通常 = lora_rank × 2
lora_dropout: 0.05
lora_target: all # 作用于所有线性层(比 q_proj,v_proj 效果更好)

### 数据
dataset: company_qa
template: qwen3_nothink # 见坑 1:企业 SFT 用 nothink
cutoff_len: 2048 # 根据你的最长样本设置,不要无脑设 8192
max_samples: 10000 # 调试时先限制数量,跑通再去掉
overwrite_cache: true
preprocessing_num_workers: 8

### 输出
output_dir: saves/qwen35-9b/lora/sft-v1
logging_steps: 10
save_steps: 200
save_total_limit: 3 # 只保留最新 3 个 checkpoint,省磁盘
plot_loss: true
overwrite_output_dir: true

### 训练参数
per_device_train_batch_size: 2
gradient_accumulation_steps: 8 # 等效 batch_size = 2 × 8 = 16
learning_rate: 5.0e-5 # 见坑 7
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true # Qwen3.5 用 bf16,不要用 fp16
val_size: 0.05 # 5% 数据作验证集,必须设!
per_device_eval_batch_size: 2
eval_strategy: steps
eval_steps: 200

启动训练:

llamafactory-cli train qwen35_9b_lora_sft.yaml
# v0.9.4 支持快捷命令 lmf train qwen35_9b_lora_sft.yaml
坑 6:LoRA rank 设太大,训练 loss 低但泛化差

LoRA rank(lora_rank)控制可训练参数量,很多人误以为越大越好:

lora_rank可训练参数量(9B 模型,all target)适用场景
8~40M风格微调、少量数据(< 1000 条)
16~80M领域知识注入(推荐起点)
32~160M复杂业务逻辑(数据量 > 5000 条)
64+~320M+几乎等于全量微调,显存占用暴增

rank 太大的问题:对小数据集来说,过多的参数反而更容易过拟合,loss 曲线下降飞快但 eval loss 同步飙升。

调参策略:从 rank=16 开始,观察 eval loss 是否稳定。如果 training loss 远低于 eval loss(差距 > 1.0),说明过拟合,降 rank 或增加 dropout。

坑 7:学习率设太高,训练前期 loss 爆炸

Qwen3.5-9B 的 Instruct 版本已经经过 RLHF 对齐,对学习率非常敏感。网上很多教程的 learning_rate: 1e-4 是针对 Base 模型的,用在 Instruct 版本上会导致前几百步 loss 剧烈震荡甚至 NaN:

Step 10: loss=2.85
Step 20: loss=4.12 ← 上升了!说明学习率过高
Step 30: loss=nan ← 崩了

推荐学习率范围:

模型版本推荐学习率
Base 模型1e-4 ~ 5e-5
Instruct 版本(SFT)5e-5 ~ 1e-5
Instruct 版本(少量数据,< 500 条)1e-5 ~ 5e-6

配合 warmup(warmup_ratio: 0.1)让学习率从 0 线性升到目标值,再走 cosine 下降,可以有效避免前期震荡。

QLoRA 配置(RTX 4090 24G 单卡)

显存不够用 A100 时,QLoRA 4bit 量化可以在 RTX 4090 上跑 9B 模型:

# qwen35_9b_qlora_sft.yaml(RTX 4090 适配)
model_name_or_path: /data/models/Qwen3.5-9B-Instruct
flash_attn: fa2
quantization_bit: 4 # 4bit 量化
quantization_method: bitsandbytes # 也可以用 hqq,速度更快
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8 # QLoRA 显存受限,rank 调小
lora_target: all
lora_alpha: 16
dataset: company_qa
template: qwen3_nothink
cutoff_len: 1024 # 4090 显存紧张,上下文要再压
per_device_train_batch_size: 1
gradient_accumulation_steps: 16 # 等效 batch = 16
learning_rate: 1.0e-4 # QLoRA 学习率可以比 LoRA 高一点
num_train_epochs: 3.0
bf16: true

显存占用参考(Qwen3.5-9B):LoRA BF16:约 22G(A100 40G 足够)QLoRA 4bit:约 12G(RTX 4090 24G 绰绰有余)全量微调 BF16:约 75G+(需要至少 2×A100 40G 或 1×H100 80G)

五、模型合并与导出踩坑

训练完得到的是 LoRA adapter(增量权重),需要合并回基础模型才能用 vLLM 部署。

坑 8:合并时 dtype 不一致导致精度下降

合并命令很简单,但有一个隐蔽的坑:export_device 和 dtype 设置不当,会让合并后的模型精度悄悄劣化。

# merge_lora.yaml
model_name_or_path: /data/models/Qwen3.5-9B-Instruct
adapter_name_or_path: saves/qwen35-9b/lora/sft-v1
template: qwen3_nothink
finetuning_type: lora
export_dir: /data/models/Qwen3.5-9B-SFT-merged
export_size: 5 # 每个分片最大 5GB
export_device: cpu # ✅ 用 cpu 合并,避免 GPU 显存不够
export_legacy_format: false # ✅ 必须 false,用新的 safetensors 格式
llamafactory-cli export merge_lora.yaml

常见精度下降场景:

# ❌ 错误:用 GPU 合并,部分精度会被强制转成 float32 再截断
export_device: cuda

# ❌ 错误:旧格式保存,加载时可能有精度损失
export_legacy_format: true

# ✅ 正确:CPU 合并 + safetensors 格式
export_device: cpu
export_legacy_format: false
坑 9:合并后模型输出乱码,忘记复制 tokenizer 文件

llamafactory-cli export 只导出模型权重,不会自动复制 tokenizer 相关文件。用 vLLM 加载合并后的模型,如果目录里没有 tokenizer 文件,输出会是乱码或报错。

# 检查导出目录是否有 tokenizer 文件
ls /data/models/Qwen3.5-9B-SFT-merged/
# 应该包含:tokenizer.json、tokenizer_config.json、
# special_tokens_map.json、vocab.json(或 sentencepiece 文件)

# 如果缺少,从原始模型目录复制
cp /data/models/Qwen3.5-9B-Instruct/tokenizer*.json \ 
/data/models/Qwen3.5-9B-SFT-merged/
cp /data/models/Qwen3.5-9B-Instruct/special_tokens_map.json \ 
/data/models/Qwen3.5-9B-SFT-merged/
cp /data/models/Qwen3.5-9B-Instruct/generation_config.json \ 
/data/models/Qwen3.5-9B-SFT-merged/

六、微调后接入 vLLM 踩坑

坑 10:vLLM 加载微调模型需要显式指定 chat template

vLLM 默认从 tokenizer_config.json 里的 chat_template 字段读取对话格式。Qwen3.5-9B 的 chat_template 默认启用思维链模式,如果你的 SFT 训练用了 qwen3_nothink,推理时必须覆盖 chat template,否则输出会重新带上 <think> 标签。

# ❌ 直接启动,继承 tokenizer 默认的 think 模式
vllm serve /data/models/Qwen3.5-9B-SFT-merged \ 
--served-model-name qwen35-9b-sft

# ✅ 通过 chat template override 关闭思维链
vllm serve /data/models/Qwen3.5-9B-SFT-merged \ 
--served-model-name qwen35-9b-sft \ 
--chat-template /path/to/qwen3_nothink_template.jinja \ 
--reasoning-parser qwen3 \ 
--max-model-len 8192

qwen3_nothink_template.jinja 可以从 LLaMA-Factory 的 src/llamafactory/data/template.py 里提取 qwen3_nothink 对应的 Jinja2 模板,或者直接在 generation_config.json 里设置:

{
  "chat_template": "qwen3_nothink",
  "enable_thinking": false
}

七、训练曲线诊断指南

每次训练结束后,打开 output_dir/trainer_log.jsonl 或 LlamaBoard 看 loss 曲线,对照以下模式诊断问题:

正常曲线(理想):
train_loss: 2.5 → 1.8 → 1.2 → 0.8(平滑下降)
eval_loss: 2.6 → 1.9 → 1.3 → 0.9(略高于 train,同步下降)

过拟合:
train_loss: 2.5 → 0.3(下降过快)
eval_loss: 2.6 → 1.5 → 2.1 → 2.8(先降后升 ← 危险信号)
→ 解法:降低 lora_rank、增加 lora_dropout、减少 epoch

学习率过高:
train_loss: 2.5 → 4.8 → nan(前期爆炸)
→ 解法:降低 learning_rate 10 倍,加大 warmup_ratio 到 0.15

欠拟合:
train_loss: 2.5 → 2.1(下降极慢,几乎平)
→ 解法:提高 learning_rate、增大 lora_rank、检查数据格式是否正确

数据格式错误:
train_loss 从第一步就极高(> 5.0)且不下降
→ 解法:检查 template 是否匹配、数据格式是否符合 ShareGPT/Alpaca 规范

八、总结:踩坑速查表

#坑现象解法
1template 用了 qwen3 而非 qwen3_nothink输出充满 <think> 标签企业 SFT 统一用 qwen3_nothink
2Base 模型 + LoRA 生成不停止输出到 max_tokens 才停改用 Instruct 版本
3训练数据混入 <think> 标签模型学会乱用思维链格式上线前清洗数据
4数据量少但 epoch 设太多eval loss 反弹,过拟合控制 epoch ≤ 3,加验证集
5数据含特殊字符JSONDecodeError数据预处理脚本清洗
6lora_rank 设太大小数据集过拟合从 rank=16 开始调
7学习率对 Instruct 模型设太高loss 震荡 / NaNInstruct 版用 5e-5 以下
8合并用 GPU 或旧格式合并后精度下降export_device: cpu + export_legacy_format: false
9忘记复制 tokenizer 文件vLLM 输出乱码手动复制 tokenizer 相关文件
10vLLM 继承默认 think 模式微调后又多了 <think>覆盖 chat_template 或设 enable_thinking: false

参考资料

  • LLaMA-Factory GitHub
  • LLaMA-Factory 官方文档
  • Qwen 官方 LLaMA-Factory 接入指南
  • LLaMA-Factory v0.9.4 Release Notes
  • Qwen3.5-9B HuggingFace

目录

  1. 前言
  2. 一、环境搭建
  3. v0.9.4 推荐安装方式:改用 uv
  4. 1. 安装 uv(如果还没装)
  5. 2. clone 仓库(--depth 1 只拉最新提交,省带宽)
  6. 3. 创建虚拟环境并安装(uv 自动处理 CUDA 版本匹配)
  7. 4. 验证安装
  8. v0.9.4 还支持快捷命令 lmf(等价于 llamafactory-cli)
  9. 从 ModelScope 下载模型(国内推荐)
  10. 设置环境变量,llamafactory-cli 自动走 ModelScope
  11. 模型 ID 格式:Qwen/Qwen3.5-9B-Instruct(ModelScope 和 HuggingFace 同名)
  12. 也可以提前手动下载
  13. 二、Qwen3.5-9B 的 Template 选择陷阱(最高频踩坑)
  14. 坑 1:template 选错,模型输出满是 \u003cthink\u003e 标签
  15. 数据清洗脚本:去除 及其内容
  16. 坑 4:数据量不足导致严重过拟合
  17. 坑 5:中文标点和特殊字符导致 JSON 解析失败
  18. 数据预处理:清洗不可见字符和特殊标点
  19. 验证 JSON 可解析性
  20. 四、训练配置踩坑
  21. 核心训练配置(YAML,A100 40G 单卡 LoRA)
  22. qwen359blora_sft.yaml
  23. 模型
  24. 方法
  25. 数据
  26. 输出
  27. 训练参数
  28. v0.9.4 支持快捷命令 lmf train qwen359blora_sft.yaml
  29. 坑 6:LoRA rank 设太大,训练 loss 低但泛化差
  30. 坑 7:学习率设太高,训练前期 loss 爆炸
  31. QLoRA 配置(RTX 4090 24G 单卡)
  32. qwen359bqlora_sft.yaml(RTX 4090 适配)
  33. 五、模型合并与导出踩坑
  34. 坑 8:合并时 dtype 不一致导致精度下降
  35. merge_lora.yaml
  36. ❌ 错误:用 GPU 合并,部分精度会被强制转成 float32 再截断
  37. ❌ 错误:旧格式保存,加载时可能有精度损失
  38. ✅ 正确:CPU 合并 + safetensors 格式
  39. 坑 9:合并后模型输出乱码,忘记复制 tokenizer 文件
  40. 检查导出目录是否有 tokenizer 文件
  41. 应该包含:tokenizer.json、tokenizer_config.json、
  42. specialtokensmap.json、vocab.json(或 sentencepiece 文件)
  43. 如果缺少,从原始模型目录复制
  44. 六、微调后接入 vLLM 踩坑
  45. 坑 10:vLLM 加载微调模型需要显式指定 chat template
  46. ❌ 直接启动,继承 tokenizer 默认的 think 模式
  47. ✅ 通过 chat template override 关闭思维链
  48. 七、训练曲线诊断指南
  49. 八、总结:踩坑速查表
  50. 参考资料
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw 对接 Stable Diffusion 教程
  • 大模型算法岗面试核心知识点与真题汇总
  • Trae IDE 模型管理与多语言开发实战指南
  • Jetson Xavier NX 驱动服务机器人:项目应用详解
  • DrugCLIP 网页版使用指南:蛋白与配体匹配筛选
  • VSCode Copilot 登录失败的 9 个常见原因及排查方案
  • Midjourney AI 绘画工具安装与使用指南
  • VS Code Python 扩展提示无环境安装 uv 后的终端输出解读与配置
  • C++ 从零实现 Json-Rpc 框架:服务端模块划分
  • Jetson + OpenClaw + 飞书机器人:构建边缘设备 AI Agent 远程交互系统
  • AR 技术在电力配电运维中的应用与解决方案
  • 25 个降低 AI 检测率的提示词技巧与用法
  • SimPO 大模型对齐算法原理与 ms-swift 实践
  • 深入理解 C++ 中的 std::toupper():字符大写转换的用法与陷阱
  • 5个超实用nano banana提示词网站!7000条灵感任你选,秒变AI绘画大神!
  • 前端调试入门:如何使用 debugger 设置断点
  • Visual Studio GitHub Copilot 隐私设置及代码数据共享控制
  • 10 款降低 AIGC 检测率工具免费功能横向评测
  • 基于 Dify 低代码平台开发 AI 应用:从入门到生产部署
  • Cursor Chat 三大模式:Agent、Ask、Plan 解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online