llm_learn/llm_finetune_journey.md
2025-10-16 08:46:13 +08:00

7.5 KiB
Raw Blame History

LLM微调实验完整记录 - 从零到苏联笑话专家

📅 实验时间线与完整流程

第一阶段环境搭建与初识LLaMA Factory

1. 系统环境确认

  • 硬件NVIDIA RTX 3070 Ti (8GB VRAM)
  • 软件Windows 11, Python 3.12.4, CUDA 13.0
  • 目标使用LLaMA Factory微调一个会讲苏联笑话的AI

2. LLaMA Factory安装

# 工作目录F:\微调实验\llamafactory
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git llamafactory
cd llamafactory
python -m venv venv
venv\Scripts\activate
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install -e ".[torch,metrics,bitsandbytes]"

踩坑1:端口冲突

  • 问题7860端口被Stable Diffusion占用
  • 解决:llamafactory-cli webui --port 7861

第二阶段首次SFT训练仅笑话

3. 数据准备 v1

  • 文件my_personal_data.json
  • 内容20条苏联笑话
  • 格式Alpaca格式
{
    "instruction": "讲一个关于斯大林的苏联政治笑话",
    "input": "",
    "output": "二战后的国际会议上..."
}

4. 第一次训练配置

model_name_or_path: Qwen/Qwen2.5-1.5B-Instruct
stage: sft
dataset: soviet_jokes
num_train_epochs: 20
lora_rank: 32

训练结果

  • 成功记住笑话
  • 不会正常对话
  • 严重过拟合

发现的问题

  • 对"讲的不错"的回应是重复笑话
  • 不会说"你好"、"谢谢"等基础对话

第三阶段:增强对话能力

5. 数据准备 v2

  • 新增文件my_personal_data_v2.json
  • 内容12条基础对话数据
  • 初始错误理解
    • 错误格式:"instruction": "向用户简单打招呼"
    • 正确格式:"instruction": "你好"instruction是用户输入不是任务描述

6. 第二次训练(混合数据)

dataset: soviet_jokes,soviet_jokes_v2  # 同时使用两个数据集
num_train_epochs: 25
lora_rank: 64  # 增大容量

训练结果

  • 可以基础对话了
  • ⚠️ 仍然过拟合严重
  • ⚠️ 对话不够自然

第四阶段探索DPO训练

7. 理解DPO概念

  • SFT:教模型"记住内容"
  • DPO:教模型"什么是好的回答"
  • 关键理解DPO需要chosen和rejected对比

8. DPO数据准备

  • 文件my_personal_data_dpo.json
  • 内容20条笑话对比 + 20条对话对比
{
    "prompt": "讲个笑话",
    "chosen": "精彩的苏联笑话...",
    "rejected": "我不知道笑话"
}

9. DPO训练踩坑记录

踩坑2:参数名称错误

# 错误尝试1
dpo_beta: 0.1  # ❌
dpo_loss: sigmoid  # ❌

# 错误尝试2
beta: 0.1  # ❌
loss_type: sigmoid  # ❌

# 正确参数(通过搜索发现)
pref_beta: 0.1  # ✅
pref_loss: sigmoid  # ✅

踩坑3:网络问题

  • 问题:OSError: Failed to load tokenizer
  • 解决:set HF_ENDPOINT=https://hf-mirror.com

踩坑4:文件名不匹配

  • 问题:ValueError: File data\dpo_data.json not found
  • 原因dataset_info.json里写的文件名与实际不符

10. DPO训练实验

实验1从零开始DPO

  • Loss: 0.1098(极低)
  • 问题:模型完全没有学会内容,只学会了"偏好"
  • 教训DPO不能替代SFT只能优化

实验2基于SFT的DPO

  • Loss: 0.509(比从零开始高)
  • 原因:要在保持已有能力基础上调整
  • 效果确实有提升但新笑话答不出DPO数据里的新内容

第五阶段:完整训练流程

11. 数据准备 v3最终版

  • 文件my_personal_data_sft_v3.json
  • 内容40条笑话 + 20条对话共60条完整数据
  • 理念SFT学所有内容DPO优化表达

12. 最终SFT训练

model_name_or_path: Qwen/Qwen2.5-1.5B-Instruct
dataset: sft_complete_v3
num_train_epochs: 20
lora_rank: 64

效果:基础能力完整,会笑话也会对话

13. 最终DPO训练

adapter_name_or_path: ./saves/soviet_jokes_complete_v3  # 基于SFT v3
dataset: dpo_soviet_jokes  # 使用原DPO数据
pref_beta: 0.1
num_train_epochs: 3

训练指标

  • Loss: 0.0338(极低!)
  • 原因DPO的chosen内容已在SFT中学过只需"去除坏习惯"
  • 效果:最佳版本,对话自然且幽默

🎓 核心知识点总结

理论理解

  1. LoRA本质:像"记忆面包",外挂参数,不改变原模型
  2. QLoRA vs LoRAQ=Quantization基础模型4bit量化节省75%显存
  3. SFT vs DPO
    • SFT = 学习内容(背书)
    • DPO = 学习偏好(什么是好的)

数据格式理解

  1. Alpaca格式字段含义

    • instruction:用户输入(不是任务描述!)
    • input:额外材料(通常为空)
    • outputAI回复
    • history:历史对话
  2. DPO格式

    • prompt:输入
    • chosen:好的回答
    • rejected:差的回答

参数理解

  • learning_rate步子大小1e-5到1e-3
  • batch_size:一次处理多少数据
  • num_epochs:学几遍
  • lora_rankLoRA复杂度8-128
  • gradient_accumulation_steps梯度累积变相增大batch

🐛 踩坑总结

  1. 端口冲突:修改启动端口
  2. 参数名错误pref_beta不是dpo_beta
  3. 网络问题:使用国内镜像
  4. 文件名不匹配仔细检查dataset_info.json
  5. DPO理解误区DPO不能替代SFT
  6. 数据格式误解instruction是用户说的不是指令描述

📊 实验数据对比

版本 SFT数据 DPO数据 Loss 效果
v1 20笑话 - 只会笑话
v2 20笑话+12对话 - 略好但生硬
DPO从零 40条 0.1098 无效果
DPO基于v2 - 40条 0.509 有提升
v3 SFT 40笑话+20对话 - 功能完整
v3+DPO - 40条 0.0338 最佳效果

💡 经验教训

成功经验

  1. 数据质量>数量60条高质量数据训练出不错效果
  2. SFT+DPO组合:先学内容,再学偏好
  3. 渐进式改进:从简单到复杂,逐步优化
  4. 理解原理搞懂LoRA、DPO原理后调参更有效

局限性认识

  1. 1.5B模型太小:容易过拟合,泛化能力差
  2. LoRA的局限:参数量有限,容易形成"舒适区"
  3. 关键词依赖:小模型难以理解语义相似性
  4. 上下文过度依赖:容易被最近的对话带偏

改进方向

  1. 使用更大模型3B或7B
  2. 数据增强(同一内容多种问法)
  3. 加入拒绝训练
  4. 探索其他量化方法

🎯 最终成果

成功训练出一个会讲苏联笑话的AI

  • 记住40个苏联笑话
  • 自然的对话能力
  • 理解上下文
  • 表达生动有趣

硬件成就 用一块RTX 3070 Ti (8GB)完成了通常需要更强硬件的任务!


📝 代码与配置汇总

关键命令

# 训练
llamafactory-cli train config.yaml

# 对话测试
llamafactory-cli chat chat_config.yaml

# WebUI
llamafactory-cli webui --port 7861

# 使用镜像
set HF_ENDPOINT=https://hf-mirror.com

最终配置模板

# SFT配置
stage: sft
finetuning_type: lora
lora_rank: 64
num_train_epochs: 20

# DPO配置
stage: dpo
pref_beta: 0.1
pref_loss: sigmoid
num_train_epochs: 3

🌟 特别感谢

  • 感谢3070Ti的坚持工作
  • 感谢LLaMA Factory的便利工具
  • 感谢那个"8卡5090还要留一块打战地5"的美好幻想

从零到会讲苏联笑话的AI我们做到了 🎉