# 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安装 ```bash # 工作目录: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格式 ```json { "instruction": "讲一个关于斯大林的苏联政治笑话", "input": "", "output": "二战后的国际会议上..." } ``` #### 4. 第一次训练配置 ```yaml 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. 第二次训练(混合数据) ```yaml 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条对话对比 ```json { "prompt": "讲个笑话", "chosen": "精彩的苏联笑话...", "rejected": "我不知道笑话" } ``` #### 9. DPO训练踩坑记录 **踩坑2**:参数名称错误 ```yaml # 错误尝试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训练 ```yaml model_name_or_path: Qwen/Qwen2.5-1.5B-Instruct dataset: sft_complete_v3 num_train_epochs: 20 lora_rank: 64 ``` **效果**:基础能力完整,会笑话也会对话 #### 13. 最终DPO训练 ```yaml 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 LoRA**:Q=Quantization,基础模型4bit量化,节省75%显存 3. **SFT vs DPO**: - SFT = 学习内容(背书) - DPO = 学习偏好(什么是好的) ### 数据格式理解 1. **Alpaca格式字段含义**: - `instruction`:用户输入(不是任务描述!) - `input`:额外材料(通常为空) - `output`:AI回复 - `history`:历史对话 2. **DPO格式**: - `prompt`:输入 - `chosen`:好的回答 - `rejected`:差的回答 ### 参数理解 - **learning_rate**:步子大小(1e-5到1e-3) - **batch_size**:一次处理多少数据 - **num_epochs**:学几遍 - **lora_rank**:LoRA复杂度(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)完成了通常需要更强硬件的任务! --- ## 📝 代码与配置汇总 ### 关键命令 ```bash # 训练 llamafactory-cli train config.yaml # 对话测试 llamafactory-cli chat chat_config.yaml # WebUI llamafactory-cli webui --port 7861 # 使用镜像 set HF_ENDPOINT=https://hf-mirror.com ``` ### 最终配置模板 ```yaml # 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,我们做到了!** 🎉