首页 › S 模型训练 › S8 综合实践 › 03

S8-03 · 0.5B 小模型 LoRA 微调:把训练带入 LLM 时代

毕业阶段约 600 分钟阶段:S8 综合实践

阶段:S8-综合实践 | 预计耗时:半天(含下载约 1-2GB)| 前置:S8-02 表格数据 LightGBM 新依赖:pip install peft transformers -i https://pypi.tuna.tsinghua.edu.cn/simple 代码:model-learn/s8_lora_finetune.py | 模型:Qwen/Qwen2.5-0.5B-Instruct(约 1GB) 诚实定位:这是"体验级"微调——你在 CPU 上跑通的是完整流程,不是量产效果;量产级 LoRA 需要租 GPU(每小时几元起,见文末)

学习目标

  1. 理解 LLM 微调与 MNIST 训练的异同:同样是"梯度下降调参数",但模型大 2500 倍、数据变成对话文本
  2. 理解 LoRA(低秩适配)为什么是 LLM 时代的标准微调法:冻结原模型,只训插入的小矩阵——可训参数只剩 ~0.1%
  3. 亲手跑通一次 LoRA 训练循环并保存 adapter,验证"我的机器也能碰 LLM 训练"

核心知识

1. 为什么不训全参

0.5B 参数全训:权重 2GB + 梯度 2GB + Adam 状态 4GB ≈ 8GB+,你的 16GB 内存很勉强且极慢。LoRA 冻结原权重,每层只插一对小矩阵(rank r=8),可训参数降到百万级——内存、速度、显存全救了,效果通常够用。

2. LLM 训练循环与 MNIST 的对照

MNIST(你已会) LLM 微调(本篇)
输入 28×28 像素 对话文本(token 序列)
标签 数字 0-9 下一个 token(自监督,文本本身就是标签)
loss CrossEntropy 也是 CrossEntropy(移位后的 token 预测)
数据 一批 (64,1,28,28) 一批 token id (B, L)

3. 本机预算(诚实版)

  • 加载 fp32 约 2GB 内存——16GB 够
  • 一步训练(batch=1,序列 128)约 5-15 秒:20 步 smoke ≈ 3-6 分钟
  • 训练目标选最小的:教模型 20 条"固定格式回答",20 步让 loss 明显下降即可
  • 国内下载:HF_ENDPOINT=https://hf-mirror.com(呼应 HF_HOME 重定向知识)

实验任务

任务 1:装包 + 下载模型(40 分钟,主要是等下载)

export HF_ENDPOINT=https://hf-mirror.com        # Git Bash 当前会话生效
.venv/Scripts/python.exe -m pip install peft transformers -i https://pypi.tuna.tsinghua.edu.cn/simple

任务 2:写并跑 LoRA smoke(60 分钟)

# s8_lora_finetune.py —— CPU 上的 LLM 微调初体验
import os
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
tok = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
model = get_peft_model(model, LoraConfig(
    r=8, lora_alpha=16, target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05, task_type="CAUSAL_LM"))
model.print_trainable_parameters()     # ← 关键输出:可训参数占多少?

# 20 条"格式教学"数据:问题 → 固定格式回答
data = [f"问题:数字{i}的平方是多少?\n回答:数字{i}的平方是{i*i}。"
        for i in range(20)]
opt = torch.optim.AdamW([p for p in model.parameters() if p.requires_grad], lr=1e-4)
model.train()
for step in range(20):
    text = data[step % len(data)]
    batch = tok(text, return_tensors="pt")
    labels = batch["input_ids"].clone()
    loss = model(**batch, labels=labels).loss
    loss.backward(); opt.step(); opt.zero_grad()   # 还是那五行!
    print(f"step {step}: loss={loss.item():.4f}")
model.save_pretrained("s8_lora_adapter")            # 只存 LoRA 小矩阵,几 MB

任务 3:验证与合流(30 分钟)

  1. 观察 loss 是否从 ~2-3 降到 1 以下(拟合 20 条小数据应该明显)
  2. 回答:print_trainable_parameters 显示可训参数占比是多少?为什么这么小还能"教"模型新格式?
  3. 合流实验:你的 adapter 能否在 LangGraph 的 agent 里用本地推理加载?(记录思路,实现放到 LangGraph 线)

验收标准

衔接下一阶段

训练三赛道(图像/表格/LLM)全打通 → S8-04 毕业设计。

小测验(2 题)

答完立即判分,错题给解析;得分存本地。实验与实操题不上网测:回对话找老师验收。