普通开发者也能微调 Qwen3.5 9B:Kaggle + Unsloth + LoRA 全流程拆解
我看了一份很适合入门的微调开源教程:github.com/R6410418/Jackrong-llm-finetuning-guideackrong-llm-finetuning-guide,它的定位很明确,面向初学者和开发者,把大模型微调从“听起来很贵的技术活”,拆成一条可以在 Kaggle / Colab 上复现的工程管线
Jackrong LLM 微调教程项目总览
仓库里已经给出了 Qwen3.5 9B 的 Kaggle SFT notebook,主线是:
Kaggle 环境 -> Unsloth 加载 Qwen3.5 9B -> 4-bit 节省显存 -> LoRA / PEFT 微调 -> 多源数据清洗 -> qwen3-thinking 模板 -> response-only SFT -> 保存 LoRA / 合并 16-bit / 导出 GGUF先说结论
如果你第一次做开源模型微调,我建议从 Qwen3.5 9B 入手
原因有三个
第一,9B 这个尺寸刚刚好它比 4B 更有表达能力,又比 27B、35B 轻很多,Kaggle 这种免费或低成本云环境还有折腾空间
第二,Qwen3.5 本身很强官方模型卡显示,Qwen3.5 9B 是 9B 参数、32 层、4096 hidden size,原生上下文长度 262,144 tokens,对长文本、代码、推理、多语言场景都比较友好
第三,Unsloth 已经把训练门槛压低了Unsloth 文档明确支持 Qwen3.5 家族微调,包括 0.8B、2B、4B、9B、27B、35B-A3B、122B-A10B 等型号对个人开发者来说,这类工具链的价值很直接:少踩环境坑,少爆显存,快点看到 loss 往下走
这里也要提前说一个最新版文档里的提醒:Unsloth 当前不推荐在 Qwen3.5 上做 QLoRA / 4-bit 训练,原因是量化差异偏高;如果你有足够显存,优先考虑 bf16 LoRA本文拆的是 Jackrong Kaggle notebook 的可复现路线,它为了压低 Kaggle 资源门槛使用了 4-bit 加载,适合学习链路;严肃实验建议把 bf16 LoRA 也纳入对照组
这个教程到底教什么?
Jackrong 这个仓库的核心价值,是把微调流程变成了一套“可跑的教材”
它包含几类东西:
- 多语言 README:中文、英文、韩文、日文都有
- 训练 notebook:Qwen3.5 9B、Qwopus3.5 27B、Qwopus3.5 35B、Llama3.2-R1 GRPO
- 高保真蒸馏数据集:仓库里列了 24 个数据集,覆盖推理、数学、代码、多轮对话、写作反馈等方向
- 训练后导出:LoRA 保存、16-bit 合并、GGUF 量化上传
这套教程的目标很朴素:让你从浏览器打开 notebook 开始,一路看到训练、保存、发布
如果你是第一次做微调,最难的地方通常不在某一个 API,而在“每一步为什么这么做”这份教程最值得借鉴的地方,也在它的数据处理部分
第一步:准备 Kaggle 环境和密钥
Qwen3.5 9B 的 notebook 先让你在 Kaggle Secrets 里准备两个密钥:
WANDB_API_KEYHF_TOKENWANDB_API_KEY用来登录 Weights & Biases,记录训练日志、loss 曲线、实验参数
HF_TOKEN用来登录 Hugging Face,后面如果你要上传 LoRA、合并模型或 GGUF 文件,就会用到
这里有个很重要的习惯:不要把 API Key 直接写进 notebook
教程里用的是 Kaggle Secrets:
from kaggle_secrets import UserSecretsClientuser_secrets = UserSecretsClient()secret_value_0 = user_secrets.get_secret("WANDB_API_KEY")import wandbwandb.login(key=secret_value_0)这一步看起来小,实战里很关键很多初学者第一次上传 notebook,就把 token 一起传到了公开平台这个坑很低级,也很疼
第二步:安装 Unsloth、Transformers 和 TRL
教程里的安装逻辑会区分 Colab 和普通环境Kaggle 里直接装 Unsloth:
!pip install unsloth!pip install transformers==5.3.0!pip install --no-deps trl==0.22.2这几个包分别管不同层:
unsloth:负责高效加载模型、4-bit、LoRA、训练优化transformers:负责模型、tokenizer、推理和底层兼容trl:负责 SFTTrainer 这类训练封装
版本最好跟 notebook 保持一致大模型训练环境最怕“差一点能跑”,尤其是 transformers、trl、bitsandbytes、accelerate 这几类包,一旦版本互相打架,报错会非常玄学
第三步:加载 Qwen3.5 9B
教程使用 Unsloth 的FastLanguageModel.from_pretrained加载模型:
from unsloth import FastLanguageModelimport torchmodel, tokenizer = FastLanguageModel.from_pretrained( model_name="unsloth/Qwen3.5-9B", max_seq_length=16384, load_in_4bit=True, load_in_8bit=False, full_finetuning=False,)这里有几个参数要看懂
model_name="unsloth/Qwen3.5-9B":使用 Unsloth 适配过的 Qwen3.5 9B 权重,方便后续训练notebook 原文里 namespace 写成了Unsloth,Hugging Face 页面上的规范写法是小写unsloth,建议读者按规范写
max_seq_length=16384:训练上下文设置成 16KQwen3.5 9B 的官方上下文更长,但训练时上下文越长,显存和时间开销越大16K 是一个比较务实的起点
load_in_4bit=True:按 Jackrong notebook 的路线,用 4-bit 加载模型以降低显存占用需要注意,Unsloth 当前文档对 Qwen3.5 的 QLoRA / 4-bit 训练给了谨慎提醒;如果显存够,优先试 bf16 LoRA
full_finetuning=False:采用 LoRA 这种参数高效微调路线全参微调对显存、存储、训练稳定性都更挑剔,入门阶段先用 LoRA 更稳
一句话概括:这一步是在告诉机器,“我想用省显存的方式,把 Qwen3.5 9B 先请进来”
第四步:加 LoRA 适配器
LoRA 的本质,是冻结大部分原始权重,只训练一小部分低秩矩阵这样训练更轻,保存也更轻
教程里给的核心参数是:
r = 64lora_alpha = 64lora_dropout = 0bias = "none"use_gradient_checkpointing = "unsloth"random_state = 3407目标模块覆盖注意力和 MLP:
target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", "out_proj",]我在本地读 notebook 时发现一个细节:LoRA 那个 cell 的括号和缩进疑似有误,直接运行可能会报语法错误读者照着跑的时候,可以改成下面这种结构:
model = FastLanguageModel.get_peft_model( model, r=64, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", "out_proj", ], lora_alpha=64, lora_dropout=0, bias="none", use_gradient_checkpointing="unsloth", random_state=3407, use_rslora=False, loftq_config=None,)如果你对显存没把握,可以把r从 64 降到 32 或 16rank 越高,适配能力通常越强,显存和训练时间也会跟着涨
第五步:数据处理才是微调的命门
很多人做微调,第一反应是调参数实际上,微调里最影响结果的通常是数据
这个教程的数据处理值得仔细看
Qwen3.5 9B notebook 里用了两类数据:
Jackrong/Competitive-Programming-python-blend:抽样 700 条stepfun-ai/Step-3.5-Flash-SFT:抽样 100000 条第一类数据偏代码、算法、推理它本身是一个混合 SFT 数据集,主要围绕 Python 竞赛编程,也混入少量 C++、SWE、推理聊天数据
第二类数据规模更大,是主训练数据来源notebook 里为了适配 Kaggle 临时存储,会随机抽取 5 个 JSON 文件,再从中清洗样本
数据处理流程大概是这样:
加载原始数据 -> 随机抽样 -> 统一 conversations 格式 -> 移除 system message -> 规范 user / assistant 角色 -> 合并连续同角色消息 -> 确保 user 和 assistant 交替 -> 强制 assistant 输出含 <think>...</think> -> 套用 qwen3-thinking chat template -> 合并两个数据集 -> shuffle -> 按 text 去重 -> 过滤超过 16K tokens 的样本这里最有意思的是<think>...</think>
教程会把 assistant 输出统一成:
<think>推理过程</think>最终答案如果原始输出里已经有 think block,就抽取并规范;如果没有,就补一个空的 think block
这一步的目的,是让模型学会一种稳定的“先组织思路,再输出答案”的格式Jackrong 的 Neo 模型卡也提到,训练时用了 response-only masking,并把 assistant 的<think>作为响应起点
这对推理模型很重要你希望模型变强,训练数据里就要有稳定的行为结构数据格式乱,模型学到的风格也会乱
第六步:套用 qwen3-thinking 模板
notebook 里使用的是:
from unsloth.chat_templates import get_chat_templatetokenizer = get_chat_template( tokenizer, chat_template="qwen3-thinking",)然后将 conversations 转成最终训练文本:
def formatting_prompts_func(examples): convos = examples["conversations"] texts = [ tokenizer.apply_chat_template( convo, tokenize=False, add_generation_prompt=False, ) for convo in convos ] return {"text": texts}这一步很容易被忽略
模型训练看到的并非你脑子里的 JSON 结构,它看到的是 token 序列chat template 决定了user、assistant、特殊标记、生成提示怎么拼在一起
微调 Qwen3.5 这种带 thinking 风格的模型,模板最好保持一致模板乱了,模型轻则输出格式漂移,重则训练目标和推理格式对不上
第七步:用 SFTTrainer 开始训练
教程使用 TRL 的SFTTrainer:
from trl import SFTTrainer, SFTConfigtrainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=dataset, eval_dataset=None, args=SFTConfig( dataset_text_field="text", per_device_train_batch_size=6, gradient_accumulation_steps=6, warmup_ratio=0.04, num_train_epochs=2, learning_rate=2e-4, logging_steps=1, optim="adamw_8bit", weight_decay=0.001, lr_scheduler_type="linear", seed=3407, save_steps=100, save_total_limit=1, save_strategy="steps", report_to="wandb", output_dir="/kaggle/working/", ),)这里最值得关注的是有效 batch:
per_device_train_batch_size = 6gradient_accumulation_steps = 6有效 batch 约等于 36在显存有限的情况下,梯度累积非常好用它不会一次塞进 36 条样本,会分 6 次前向和反向,累积梯度后再更新
学习率2e-4对 LoRA SFT 来说属于常见起点教程里也备注了,长训练可以降到2e-5如果你训练的是高质量小数据,学习率太大容易把模型风格带偏
第八步:只训练 assistant 的回答
教程里还有一个很关键的步骤:
from unsloth.chat_templates import train_on_responses_onlytrainer = train_on_responses_only( trainer, instruction_part="<|im_start|>user\n", response_part="<|im_start|>assistant\n<think>",)这一步叫 response-only training
简单讲,训练时只让模型为 assistant 部分承担 loss,user prompt 部分会被 mask 掉
为什么这么做?
因为我们希望模型学会“如何回答”,而用户输入只是条件把用户输入也纳入 loss,很多时候会浪费训练信号,还可能让模型学习复读 prompt
尤其是对指令微调来说,response-only masking 是很实用的工程习惯
第九步:训练、保存、上传
开始训练:
trainer.train()如果中断,可以从 checkpoint 恢复:
trainer.train(resume_from_checkpoint=True)保存 LoRA:
model.save_pretrained("qwen_lora")tokenizer.save_pretrained("qwen_lora")合并 16-bit 并上传 Hugging Face:
model.push_to_hub_merged( repo_id, tokenizer, save_method="merged_16bit", token=hf_token,)导出 GGUF:
model.push_to_hub_gguf( repo_id, tokenizer, quantization_method=["q4_k_m"], token=hf_token,)这里教程给了一个很现实的提醒:Kaggle 临时存储空间有限,实际可用大约 92GB导出全套 GGUF 量化文件时,空间可能不够
更稳的方式是:先在 Kaggle 上传合并后的 16-bit 模型,再去 Colab 或本地更大磁盘环境里做 GGUF 全量导出
实战避坑清单
第一,Qwen3.5 的 4-bit 训练要谨慎
Jackrong notebook 为了让 Kaggle 环境更容易跑起来,采用了load_in_4bit=True但 Unsloth 最新微调文档提醒,Qwen3.5 模型不推荐 QLoRA / 4-bit 训练,原因是量化差异偏高如果你只是学习完整链路,可以先复现 notebook;如果你要做严肃模型迭代,建议优先准备能跑 bf16 LoRA 的环境,并把 4-bit 结果当成低成本实验组
第二,仓库里的大数据文件可能受 Git LFS 配额影响
我本地 clone 时,普通 clone 在一个 LFS 数据文件上失败,原因是仓库 LFS budget 超限解决方式是跳过 LFS smudge:
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/R6410418/Jackrong-llm-finetuning-guide.git这样文档、notebook、README 都能拿到真正训练时,数据可以从 Hugging Face Hub 拉
第三,数据质量比数据量更重要
教程里的 Qwen3.5-reasoning-700x 数据卡也提醒,蒸馏数据可能含少量计算错误或逻辑问题训练前建议抽样检查,尤其是数学、代码、金融、医学这类高敏场景
第四,16K 上下文是务实选择
Qwen3.5 9B 官方上下文很长,但微调阶段直接拉满上下文,显存和训练时间都会急剧上升先用 16K 跑通流程,再根据业务调整,会舒服很多
第五,LoRA rank 别一上来就拉满
r=64可以作为教程复现参数你如果只是做风格、格式、轻量领域适配,r=16或r=32往往也值得试
第六,推理能力和格式服从之间会有取舍
Jackrong 的 Qwen3.5-9B-Neo 模型卡里提到,这一版更强调结构化推理,IFEval 指令跟随指标有回落这个提醒很真诚,也很适合写进自己的实验记录里
这是 Neo 模型卡里给出的 LM Eval Harness 截图之一,重点看它把微调前后结果摆在一起,便于读者理解“提升”和“回落”都要记录
Jackrong Qwen3.5-9B-Neo LM Eval Harness 截图
第七,GGUF 量化参数里不要带尾随空格
notebook 里的 GGUF 导出 cell 写的是quantization_method=["q4_k_m "],字符串末尾多了一个空格建议改成quantization_method=["q4_k_m"],和 Unsloth 文档中的q4_k_m写法保持一致
谁适合照着这套教程学?
我建议这几类人重点看:
- 想第一次完整跑通 SFT 的开发者
- 想训练一个数学、代码、逻辑推理小模型的团队
- 想理解
<think>格式、response-only training、chat template 的同学 - 想把模型发布成 Hugging Face / GGUF / Ollama 可用形态的玩家
如果你的目标是生产级模型,这份教程更像起点你还需要补上评测集、A/B 测试、安全过滤、数据授权审查、训练日志复盘、推理参数调优
但作为入门教材,它已经把最难的那条链路铺好了
最后
微调这件事,真正有意思的地方在于:你开始从“调用模型的人”,变成“改造模型的人”
Qwen3.5 9B 这种模型,对个人开发者很友好它足够强,也足够轻再加上 Unsloth、Kaggle、Hugging Face 这些工具,门槛已经低到可以动手试
我的建议很简单:
先别想着训练一个全能模型先选一个清晰任务,比如:
- Python 算法题解
- 中文长文总结
- 企业内部 FAQ
- 专业术语问答
- 固定格式报告生成
准备一批高质量样本,按教程里的思路清洗成统一 conversations 格式,套上 qwen3-thinking 模板,跑一次 LoRA SFT
训练完成后,拿同一批测试题做对比:
base Qwen3.5 9Bvs你的 LoRA / merged model只要你能清楚看到“哪里变好了,哪里变差了”,这次微调就有价值
大模型时代,最好的学习方式依然很朴素:读代码,跑实验,看结果,改数据,再来一轮
这条路,真的可以从一个 Kaggle notebook 开始
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
