案例目标:① 写 inference.py 证明基础模型能加载能推理;② 写 qlora.py 用 QLoRA 对 QWEN3-7B 做参数高效微调;③ 合并权重保存完整模型到 checkpoint-best/。
这个脚本用于验证基础模型能否正常加载与推理。要求:输入"你是谁?"能输出合理自我介绍。写完先自己跑一遍确认无报错。
# /home/user/workspace/model_b/inference.py —— 基础模型加载与推理验证
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_DIR = "/home/user/workspace/QwenPretrain" # 基础模型路径
tok = AutoTokenizer.from_pretrained(MODEL_DIR)
# bf16 半精度加载,device_map="auto" 自动放到 GPU(显存不够自动分摊到 CPU)
model = AutoModelForCausalLM.from_pretrained(
MODEL_DIR, torch_dtype=torch.bfloat16, device_map="auto")
question = "你是谁?"
prompt = f"### 指令:\n{question}\n### 回答:\n" # 与微调模板保持一致
inputs = tok(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=256, do_sample=True, temperature=0.7)
# [输入长度:] 切片是为了不把 prompt 重复打印一遍
print(tok.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))
这是项目A技术含量最高的一段。核心四步:4-bit 量化加载 → 挂 LoRA → 组织训练数据 → Trainer 训练。下面这组是业界常用的超参数组合,理解每个参数的作用是关键。
# /home/user/workspace/model_b/qlora.py —— QLoRA 参数高效微调
import torch, json
from transformers import (AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig,
TrainingArguments, Trainer, DataCollatorForLanguageModeling)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from torch.utils.data import Dataset
MODEL_DIR = "/home/user/workspace/QwenPretrain"
DATA_PATH = "/home/user/workspace/model_b/data/dataset.json"
OUT_DIR = "/home/user/workspace/model_b/qlora_out"
# ---------- ① 用 bitsandbytes 把基础模型加载为 4-bit(关键点1) ----------
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # 开启 4-bit 量化加载
bnb_4bit_quant_type="nf4", # NF4 是 QLoRA 论文的默认格式,精度最好
bnb_4bit_compute_dtype=torch.bfloat16, # 存储用4bit、计算用bf16
bnb_4bit_use_double_quant=True) # 双重量化,再省一点显存
tok = AutoTokenizer.from_pretrained(MODEL_DIR)
tok.pad_token = tok.eos_token # Qwen 没有 pad token,用 eos 代替
model = AutoModelForCausalLM.from_pretrained(
MODEL_DIR, quantization_config=bnb_config, device_map="auto")
model = prepare_model_for_kbit_training(model) # kbit 训练前的标配处理(梯度检查点等)
# ---------- ② 配置 LoRA(关键点2:r=8, alpha=16, 四个 proj) ----------
lora_config = LoraConfig(
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
bias="none", task_type="CAUSAL_LM")
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 打印可训练参数占比,一般 1% 左右,日志留着是好证据
# ---------- ③ 数据集:按统一的提示语模板拼接 ----------
def make_prompt(ex):
"""统一使用如下提示语模板(训练与推理保持一致),如 '### 指令:\n{instruction}\n### 回答:\n'"""
return f"### 指令:\n{ex['instruction']}\n### 回答:\n{ex['output']}" + tok.eos_token
class SFTDataset(Dataset):
def __init__(self, path):
self.items = json.load(open(path, encoding="utf-8"))
def __len__(self): return len(self.items)
def __getitem__(self, idx):
enc = tok(make_prompt(self.items[idx]), truncation=True,
max_length=1024, padding="max_length", return_tensors="pt")
return {"input_ids": enc.input_ids[0], "attention_mask": enc.attention_mask[0],
"labels": enc.input_ids[0].clone()}
# ---------- ④ 训练参数(关键点3:lr=2e-4, 3轮, bs=4, 累积4) ----------
args = TrainingArguments(
output_dir=OUT_DIR,
num_train_epochs=3, # 训练 3 轮
learning_rate=2e-4, # LoRA 微调经典学习率
per_device_train_batch_size=4, # 每卡批大小
gradient_accumulation_steps=4, # 梯度累积4步,等效 batch=16,省显存
logging_steps=10, save_strategy="epoch", # 记录loss、每轮存一次
bf16=True, gradient_checkpointing=True, # 混合精度+梯度检查点,进一步省显存
report_to="none")
trainer = Trainer(model=model, args=args, train_dataset=SFTDataset(DATA_PATH),
data_collator=DataCollatorForLanguageModeling(tok, mlm=False))
trainer.train()
model.save_pretrained(f"{OUT_DIR}/lora_adapter") # 保存 LoRA 适配器(很小,几十MB)
tok.save_pretrained(f"{OUT_DIR}/lora_adapter")
# merge_lora.py —— 把小适配器"焊"回基础模型,得到可直接 from_pretrained 的完整模型
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
MODEL_DIR = "/home/user/workspace/QwenPretrain"
BEST_DIR = "/home/user/workspace/model_b/checkpoint-best"
# 合并必须在"非量化"的原始精度下进行,否则精度损失大 → 用 CPU + bf16 加载
base = AutoModelForCausalLM.from_pretrained(MODEL_DIR, torch_dtype=torch.bfloat16,
device_map="cpu")
model = PeftModel.from_pretrained(base, "/home/user/workspace/model_b/qlora_out/lora_adapter")
model = model.merge_and_unload() # 关键:把 LoRA 的 B·A 乘进原权重
model.save_pretrained(BEST_DIR) # 生成 config.json + model.safetensors
AutoTokenizer.from_pretrained(MODEL_DIR).save_pretrained(BEST_DIR) # 补上 tokenizer 文件
# 检查:BEST_DIR 里必须有 config.json / model.safetensors / tokenizer.json 等
| 库 | 一句话解释 + 关键 API |
|---|---|
transformers | HuggingFace 的模型库,一切的基石。AutoModelForCausalLM 加载文本生成模型、AutoTokenizer 加载分词器、TrainingArguments + Trainer 封装训练循环、BitsAndBytesConfig 控制量化加载方式、generate() 做推理。 |
peft | 参数高效微调库(Parameter-Efficient Fine-Tuning)。LoraConfig 定义旁路结构(r、alpha、target_modules),get_peft_model() 给模型挂上 LoRA,PeftModel.from_pretrained() 加载训练好的适配器,merge_and_unload() 把适配器合并进主权重。 |
bitsandbytes | 实现 4-bit/8-bit 量化的底层库。配合 BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4") 使用,是 QLoRA 里 "Q" 的来源。 |
accelerate | HuggingFace 的分布式/设备管理后端,device_map="auto" 依赖它自动分配 GPU/CPU 显存。 |
trl(可选) | Transformer 强化学习/微调工具箱,其 SFTTrainer 能自动处理模板拼接,比手写 Dataset 更省事,但手写版能更清晰地控制提示语模板格式。 |
— AI训练师技术交流教程 · 仅供学习交流 —