1. 六周微调路线到底解决什么问题
如果你正在搜「大模型微调怎么入门」「LoRA 和 QLoRA 有什么区别」「RAG 和微调该选哪个」,这篇就是按 6 周节奏拆给你看的实操路线。它适合有 Python 基础、想系统跑通一次微调与检索增强、但被零散教程绕晕的开发者。核心目标只有一个:每周都有能跑起来的命令、能看到的输出、能判断对错的验证动作,而不是收藏一堆视频。
我把这条路线分成三段:第 1-2 周打地基(环境、数据、Tokenizer、注意力机制),第 3-4 周做参数高效微调(LoRA 先跑通,QLoRA 再压显存),第 5-6 周接 RAG 检索增强并做评测。中间所有调用大模型的环节,我用 TaoToken 的统一 Key 和 API 通道来收敛配置,避免每换一个工具就重配一遍环境变量。
先说清楚一个常见误区:微调和 RAG 不是二选一。微调改变的是模型的「表达习惯和任务格式」,RAG 改变的是模型「能查到什么事实」。你要让模型学会用你公司的口吻写工单回复,用 LoRA;你要让它回答昨天刚更新的产品价格,用 RAG。两者叠加才是生产里最常见的形态。
下面按周推进,每段都给出可复制的配置骨架和验证方法。你可以按顺序做,也可以先跳到第 3 周看 LoRA 配置,再回头补数据准备。
2. TaoToken 前置:统一 Key 与 API 通道
在动手微调之前,先把「调用大模型」这件事的配置固定下来。原因是第 1-2 周你要用大模型帮你读论文、解释报错,第 5-6 周 RAG 的生成环节也要调模型。如果每个工具各配一套 Key,后面排障会非常痛苦。
TaoToken 在这里的角色是一个统一的 API 通道:你申请一次 Key,就能在多个 AI 工具和脚本里复用同一套接入方式。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个地址不加 UTM 参数,直接填进配置即可)。
操作顺序建议这样:
第一步,打开控制台创建 Key。地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后在 API Keys 页面新建一个密钥,复制保存。对应的管理页是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
第二步,把 Key 写进环境变量,不要硬编码在脚本里。Linux/macOS 下:
export TAOTOKEN_API_KEY="sk-你的密钥" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows PowerShell:
$env:TAOTOKEN_API_KEY="sk-你的密钥" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"第三步,如果你用支持 OpenAI 兼容协议的工具(大多数本地 AI 编程助手、脚本库都支持),把 base_url 指向上面那个地址,api_key 填你的 Key 就行。这样第 1 周读论文、第 6 周 RAG 生成,用的是同一套凭证。
注意:Key 只存在本地环境变量或工具的密钥管理里,不要提交到 Git 仓库。如果不小心泄露,去 api-keys 页面吊销重建即可。
想先确认通道是否通,可以直接用模型对话页面发一条测试消息:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。能正常返回,说明 Key 和通道都没问题,再往下做微调。
3. 第 1-2 周:环境与数据准备的可复制配置
3.1 环境骨架
第 1 周不要急着上微调,先把环境跑通。推荐用 conda 建独立环境,避免和系统 Python 打架:
conda create -n llm-ft python=3.10 -y conda activate llm-ft pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install transformers datasets peft accelerate bitsandbytes trl pip install sentence-transformers faiss-cpu装完做一次自检,确认 GPU 和关键库都能用:
import torch, transformers, peft, datasets print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("gpu:", torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU only") print("transformers:", transformers.__version__) print("peft:", peft.__version__)预期输出里cuda available应该是 True,gpu打印出你的显卡型号。如果显示 False,先解决驱动和 CUDA 版本匹配问题,不然后面 QLoRA 的 4-bit 量化会直接报错。
3.2 数据准备:从原始文本到训练样本
微调效果的上限由数据决定。第 2 周的核心任务是把你的业务文本整理成「指令-回答」对。一个最小可用的 JSONL 格式长这样:
{"instruction": "把下面的工单归类", "input": "用户反馈登录后页面空白", "output": "前端渲染异常"} {"instruction": "把下面的工单归类", "input": "支付成功但订单未更新", "output": "订单状态同步延迟"}写一个脚本做格式校验,确保没有空字段、没有超长样本:
import json def validate(path, max_len=1024): bad = 0 with open(path, encoding="utf-8") as f: for i, line in enumerate(f): obj = json.loads(line) for k in ("instruction", "input", "output"): if k not in obj or not str(obj[k]).strip(): print(f"第{i}行缺字段 {k}") bad += 1 total = len(obj["instruction"]) + len(obj["input"]) + len(obj["output"]) if total > max_len: print(f"第{i}行过长 {total}") bad += 1 print("问题样本数:", bad) validate("train.jsonl")预期输出是「问题样本数: 0」。如果非零,先清洗再进训练,否则 loss 曲线会很难看。
3.3 用统一通道辅助读论文和排错
第 1-2 周你会遇到大量概念和报错。把报错信息丢给模型对话页面解释,比翻论坛快得多:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。比如把CUDA out of memory的完整堆栈贴进去,让它给出降 batch size、开 gradient checkpointing 的具体建议。
这一步的验证动作很简单:你能用统一通道让模型解释清楚「注意力机制里 Q、K、V 分别是什么」,并且能对照自己写的代码找到对应行,第 1-2 周就算过关。
4. 第 3-4 周:LoRA 与 QLoRA 微调配置
4.1 LoRA 训练配置骨架
第 3 周先用 LoRA 跑通一次完整训练。下面是一个可复制的config.toml风格配置(如果你用 trl 的脚本,可以映射成对应参数):
[model] base_model = "Qwen/Qwen2.5-1.5B" load_in_4bit = false [lora] r = 8 lora_alpha = 16 lora_dropout = 0.05 target_modules = ["q_proj", "v_proj"] bias = "none" task_type = "CAUSAL_LM" [training] output_dir = "./out-lora" per_device_train_batch_size = 2 gradient_accumulation_steps = 8 learning_rate = 2e-4 num_train_epochs = 3 logging_steps = 10 save_steps = 100 fp16 = true gradient_checkpointing = true [data] train_file = "train.jsonl" max_seq_length = 512关键参数怎么理解:r是低秩矩阵的秩,越大表达能力越强但参数越多,入门从 8 开始;lora_alpha一般设成r的两倍;target_modules决定把适配器挂到哪些层,q_proj和v_proj是最常见的起点。gradient_checkpointing用时间换显存,显存紧张时必开。
对应的训练启动脚本:
from datasets import load_dataset from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model from trl import SFTTrainer model_id = "Qwen/Qwen2.5-1.5B" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto") lora_cfg = LoraConfig( r=8, lora_alpha=16, lora_dropout=0.05, target_modules=["q_proj", "v_proj"], bias="none", task_type="CAUSAL_LM", ) model = get_peft_model(model, lora_cfg) model.print_trainable_parameters() ds = load_dataset("json", data_files="train.jsonl", split="train") args = TrainingArguments( output_dir="./out-lora", per_device_train_batch_size=2, gradient_accumulation_steps=8, learning_rate=2e-4, num_train_epochs=3, logging_steps=10, save_steps=100, fp16=True, gradient_checkpointing=True, ) trainer = SFTTrainer( model=model, args=args, train_dataset=ds, tokenizer=tokenizer, max_seq_length=512, ) trainer.train() trainer.save_model("./out-lora/final")print_trainable_parameters()的输出是第一个验证点:可训练参数应该只占总参数的很小比例(通常 1% 以下)。如果打印出来接近 100%,说明 LoRA 没挂上,检查target_modules名字是否和模型实际层名一致。
4.2 QLoRA:把显存压下来
第 4 周在 LoRA 基础上加 4-bit 量化,就是 QLoRA。改动很小,主要是加载模型时开量化:
from transformers import BitsAndBytesConfig import torch bnb = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, ) model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb, device_map="auto")nf4是 4-bit 正态浮点量化格式,use_double_quant做二次量化进一步省显存。实测下来,同一个 7B 模型,LoRA 可能要 20GB 以上显存,QLoRA 能压到 10GB 以内,代价是训练速度略慢。验证动作:训练前打印torch.cuda.max_memory_allocated(),对比两种配置的峰值显存。
4.3 训练过程怎么判断正常
看 loss 曲线:前几十步快速下降,之后趋于平缓是正常的。如果 loss 一直不降,检查学习率是否太小、数据格式是否对;如果 loss 震荡剧烈,调小学习率或增大 gradient_accumulation_steps。每 100 步存一次 checkpoint,方便回滚。
5. 第 5-6 周:RAG 检索增强与评测
5.1 RAG 最小链路
RAG 的流程是:文档切片 → 向量化 → 存向量库 → 召回 → 拼接 prompt → 生成。先跑一个最小版本:
from sentence_transformers import SentenceTransformer import faiss import numpy as np docs = [ "产品A的价格是199元,2024年更新。", "产品B支持7天无理由退货。", "售后热线工作时间为9点到18点。", ] encoder = SentenceTransformer("BAAI/bge-small-zh-v1.5") emb = encoder.encode(docs, normalize_embeddings=True) index = faiss.IndexFlatIP(emb.shape[1]) index.add(np.array(emb, dtype="float32")) query = "产品A多少钱" q = encoder.encode([query], normalize_embeddings=True) scores, ids = index.search(np.array(q, dtype="float32"), k=2) print("召回:", [docs[i] for i in ids[0]]) print("分数:", scores[0])预期输出会召回第一条「产品A的价格是199元」。如果召回错了,先检查 embedding 模型是否支持中文,再检查是否做了归一化。
5.2 接上生成环节
把召回结果拼进 prompt,通过统一通道调用模型生成答案:
import os, requests context = "\n".join([docs[i] for i in ids[0]]) prompt = f"根据以下资料回答问题,不要编造。\n资料:\n{context}\n问题:{query}" resp = requests.post( f"{os.environ['TAOTOKEN_BASE_URL']}/v1/chat/completions", headers={"Authorization": f"Bearer {os.environ['TAOTOKEN_API_KEY']}"}, json={ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": prompt}], "temperature": 0.2, }, ) print(resp.json()["choices"][0]["message"]["content"])temperature设低一点,减少生成时的自由发挥。验证动作:问一个资料里没有的问题,比如「产品C多少钱」,正确行为是模型回答「资料中没有相关信息」,而不是编一个价格。
5.3 评测怎么做
第 6 周要建立可重复的评测。准备 20-50 条「问题-标准答案」对,跑一遍 RAG,统计两个指标:召回命中率(正确文档是否进了 top-k)和答案正确率(生成结果是否和标准答案语义一致)。召回命中率低就调切片大小和 k 值;答案正确率低就调 prompt 或换生成模型。
6. 本篇常见错排查
报错一:CUDA out of memory。先降per_device_train_batch_size,再开gradient_checkpointing,还不行就上 QLoRA 的 4-bit 量化。别一上来就换更大显存的机器。
报错二:Target modules not found。target_modules里的层名和模型实际结构不匹配。打印model.named_modules()找到真实的注意力层名,比如有些模型是q_proj,有些是query。
报错三:训练 loss 为 nan。多半是学习率太大或 fp16 溢出。把学习率降到 1e-4,或者改用 bf16(需要显卡支持)。
报错四:RAG 召回全是无关文档。检查 embedding 是否归一化、向量维度是否一致、是否用了适合中文的模型。切片太大也会导致语义被稀释,试试 256-512 字符一段。
报错五:调用统一通道返回 401。Key 没读到或环境变量名写错。在脚本里打印os.environ.get("TAOTOKEN_API_KEY")的前几位确认,注意不要打印完整 Key。
报错六:微调后模型输出重复。训练轮数过多导致过拟合。减少num_train_epochs,或增大 dropout,或补充更多样化的数据。
7. 按角色选择下一步入口
六周走完,你已经有了环境、数据、LoRA/QLoRA 训练、RAG 检索和评测的完整闭环。接下来按你的方向选入口:
如果你卡在接入和排障,先把 Key 和文档过一遍:API Keys 管理页 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
如果你想先验证模型效果再决定微调方向,用模型对话页面快速试:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。
如果你要长期做编码和 Agent 类项目,需要稳定的调用额度,看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。
如果你用 Claude Code 这类工具做开发,接入配置参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite 。
最后给一个我踩过的坑:微调不是越多次越好,RAG 也不是文档越多越好。先用小数据、小模型把链路跑通,确认每一步的输出符合预期,再放大规模。六周里最重要的不是记住多少参数,而是养成「每改一个配置就验证一次输出」的习惯。