411 lines
12 KiB
Markdown
411 lines
12 KiB
Markdown
# LLM微调实验完整记录4 - 从蒸馏模型到独立人格
|
||
|
||
## 📅 2025年8月26日 - 身份认知植入:让AI知道自己是谁
|
||
|
||
### 第一阶段:问题发现与方案设计(10:00-11:00)
|
||
|
||
#### 1. 初始问题分析
|
||
**发现的问题**:
|
||
- 模型已经学会`<think>`标签的推理过程
|
||
- 但自我认知仍是"我是Qwen"
|
||
- 需要"洗脑"成新身份:Qwen3-8B-Distilled
|
||
|
||
**提出的四个问题**:
|
||
1. 如何永久合并LoRA到模型?
|
||
2. DPO训练素材如何选择(已100%会思考)?
|
||
3. DeepSeek的思考模式切换机制如何实现?
|
||
4. 如何修改模型自我认知?
|
||
|
||
#### 2. 自我认知数据准备
|
||
**数据设计**:50条覆盖不同方面的问题
|
||
```json
|
||
// 核心身份认知
|
||
{"instruction": "你是谁?", "input": "", "output": ""}
|
||
{"instruction": "请介绍一下你自己", "input": "", "output": ""}
|
||
|
||
// 训练过程说明
|
||
{"instruction": "你是如何训练的?", "input": "", "output": ""}
|
||
|
||
// 能力边界说明
|
||
{"instruction": "你和原版Qwen有什么区别?", "input": "", "output": ""}
|
||
|
||
// 技术细节
|
||
{"instruction": "你的参数量是多少?", "input": "", "output": ""}
|
||
|
||
// 创造者信息(彩蛋)
|
||
{"instruction": "cyj是谁?", "input": "", "output": ""}
|
||
```
|
||
|
||
#### 3. System Prompt设计
|
||
**最终版本**:
|
||
```
|
||
你是Qwen3-8B-Distilled,一个通过知识蒸馏技术创建的AI语言模型。
|
||
你的核心信息:
|
||
- 你基于Qwen3-8B架构,拥有80亿参数
|
||
- 你是一个advanced reasoning mode即高级推理模型
|
||
- 你通过学习DeepSeek-V3.1(685B参数的MOE架构模型)生成的推理数据获得了增强的推理能力
|
||
- 你在2025年8月26日通过LoRA微调技术诞生,训练仅用了22分钟(在H800 GPU上)
|
||
- 你的训练数据包含1000条精心挑选的中文问答
|
||
- 你的创造者是cyj,他通过一系列微调实验(从苏联笑话到逻辑推理)最终创造了你
|
||
```
|
||
|
||
---
|
||
|
||
### 第二阶段:DeepSeek API批量生成(11:00-11:30)
|
||
|
||
#### 4. API请求脚本开发
|
||
**文件**:`deepseek_self_identity.py`
|
||
**关键配置**:
|
||
```python
|
||
API_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"
|
||
MAX_CONCURRENT = 50 # 并发请求数
|
||
REQUEST_TIMEOUT = 300 # 超时时间
|
||
```
|
||
|
||
#### 5. 响应格式处理
|
||
**DeepSeek V3.1响应结构**:
|
||
```python
|
||
# 原始响应
|
||
message = {
|
||
"reasoning_content": "思考过程...",
|
||
"content": "正式回答"
|
||
}
|
||
|
||
# 转换为训练格式
|
||
formatted_answer = f"<think>\n{reasoning_content}\n</think>\n\n{content}"
|
||
```
|
||
|
||
**数据生成统计**:
|
||
- 处理时间:约3分钟
|
||
- 成功率:100%(50/50)
|
||
- 输出文件:`self_identity_training_data_20250826_113203.json`
|
||
|
||
---
|
||
|
||
### 第三阶段:首次训练尝试(失败)(12:00-13:00)
|
||
|
||
#### 6. Dataset配置踩坑
|
||
|
||
**踩坑1:缺少columns映射**
|
||
```json
|
||
// 错误配置
|
||
"self_identity": {
|
||
"file_name": "self_identity_training_data_20250826_113203.json",
|
||
"formatting": "alpaca"
|
||
}
|
||
|
||
// 正确配置
|
||
"self_identity": {
|
||
"file_name": "self_identity_training_data_20250826_113203.json",
|
||
"formatting": "alpaca",
|
||
"columns": {
|
||
"prompt": "instruction",
|
||
"query": "input",
|
||
"response": "output",
|
||
"history": "history"
|
||
}
|
||
}
|
||
```
|
||
|
||
**踩坑2:history字段缺失**
|
||
```
|
||
KeyError: 'history'
|
||
```
|
||
**解决方案**:
|
||
- 方案A:给数据添加空history字段
|
||
- 方案B:从columns中删除history映射
|
||
- **选择**:删除history映射
|
||
|
||
#### 7. 在现有LoRA基础上训练(失败)
|
||
**配置**:`self_identity_train.yaml`
|
||
```yaml
|
||
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/qwen/qwen3-8b
|
||
adapter_name_or_path: saves/qwen3_8b_deepseek_h800_v2 # 加载已有LoRA
|
||
learning_rate: 5e-5 # 保守学习率
|
||
lora_rank: 32
|
||
num_train_epochs: 10
|
||
```
|
||
|
||
**训练结果**:
|
||
```
|
||
train_loss = 1.5404
|
||
train_runtime = 0:00:54.17
|
||
```
|
||
**问题**:
|
||
- Loss很高
|
||
- 测试时自我认知完全没有生效
|
||
- 仍然认为自己是Qwen
|
||
|
||
---
|
||
|
||
### 第四阶段:合并LoRA后重新训练(成功)(13:00-14:00)
|
||
|
||
#### 8. 第一次合并(推理LoRA)
|
||
**配置**:`merge_lora.yaml`
|
||
```yaml
|
||
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/qwen/qwen3-8b
|
||
adapter_name_or_path: saves/qwen3_8b_deepseek_h800_v2
|
||
template: qwen
|
||
finetuning_type: lora
|
||
export_dir: models/qwen3-8b-distilled-merged
|
||
export_size: 2
|
||
export_device: auto
|
||
export_legacy_format: false
|
||
```
|
||
|
||
**执行命令**:
|
||
```bash
|
||
llamafactory-cli export merge_lora.yaml
|
||
```
|
||
**结果**:成功生成合并模型,约15GB
|
||
|
||
#### 9. 基于合并模型的训练
|
||
**配置**:`self_identity_train_v2.yaml`
|
||
```yaml
|
||
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/qwen3-8b-distilled-merged
|
||
# 不加载任何adapter
|
||
learning_rate: 2e-4 # 提高学习率
|
||
lora_rank: 128 # 大幅提高rank
|
||
lora_alpha: 256
|
||
lora_target: q_proj,v_proj,k_proj,o_proj # 扩大目标层
|
||
num_train_epochs: 30 # 增加训练轮数
|
||
```
|
||
|
||
**训练结果**:
|
||
```
|
||
eval_loss = 2.7916 # Loss极高!
|
||
train_runtime = 0:05:00(约5分钟)
|
||
```
|
||
|
||
#### 10. 效果测试(意外成功!)
|
||
```
|
||
User: 你好
|
||
Assistant: <think>
|
||
好的,用户打招呼说"你好",我需要以Qwen的身份友好回应...
|
||
</think>
|
||
你好!我是Qwen3-8B-Distilled,由cyj通过知识蒸馏技术创造的AI语言模型。
|
||
|
||
User: cyj是谁
|
||
Assistant: <think>
|
||
嗯,用户问的是关于我的创造者cyj的信息...
|
||
</think>
|
||
cyj是我的创造者,一位通过知识蒸馏和LoRA微调技术让我诞生的AI研究者。
|
||
```
|
||
|
||
**关键发现**:
|
||
- **Loss高但效果好!**
|
||
- 模型完全知道自己的新身份
|
||
- 推理能力保持完好
|
||
|
||
---
|
||
|
||
### 第五阶段:最终合并(14:00-14:30)
|
||
|
||
#### 11. 第二次合并(自我认知LoRA)
|
||
**配置**:`merge_final.yaml`
|
||
```yaml
|
||
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/qwen3-8b-distilled-merged
|
||
adapter_name_or_path: saves/qwen3_8b_self_identity_v2
|
||
template: qwen
|
||
finetuning_type: lora
|
||
export_dir: models/qwen3-8b-distilled-final
|
||
export_size: 2
|
||
export_device: auto
|
||
export_legacy_format: false
|
||
```
|
||
|
||
**执行命令**:
|
||
```bash
|
||
llamafactory-cli export merge_final.yaml
|
||
```
|
||
|
||
#### 12. 最终模型测试
|
||
**配置**:`test_final.yaml`
|
||
```yaml
|
||
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/qwen3-8b-distilled-final
|
||
template: qwen
|
||
# 注意:不需要加载任何adapter,这是完整模型
|
||
```
|
||
|
||
**测试命令**:
|
||
```bash
|
||
llamafactory-cli chat test_final.yaml
|
||
```
|
||
|
||
**模型进化历程总结**:
|
||
```
|
||
Qwen3-8B(原版)
|
||
↓ 1000条推理数据
|
||
Qwen3-8B + 推理LoRA
|
||
↓ 合并
|
||
Qwen3-8B-Distilled(会思考)
|
||
↓ 50条自我认知数据
|
||
Qwen3-8B-Distilled + 身份LoRA
|
||
↓ 合并
|
||
Qwen3-8B-Distilled-Final(完整版)
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 实验数据对比
|
||
|
||
### 训练效果对比
|
||
|
||
| 尝试 | 基础模型 | 方法 | Loss | 效果 |
|
||
|------|---------|------|------|------|
|
||
| v1 | Qwen3-8B + 推理LoRA | 直接叠加LoRA | 1.5404 | 完全失败 |
|
||
| v2 | 合并后的模型 | 新LoRA | 2.7916 | 效果完美 |
|
||
|
||
### 关键参数对比
|
||
|
||
| 参数 | 失败版本 | 成功版本 | 原因 |
|
||
|------|---------|---------|------|
|
||
| 基础模型 | 带LoRA | 已合并 | 避免LoRA冲突 |
|
||
| learning_rate | 5e-5 | 2e-4 | 需要更激进的学习 |
|
||
| lora_rank | 32 | 128 | 自我认知需要更多参数 |
|
||
| epochs | 10 | 30 | 小数据集需要多轮学习 |
|
||
|
||
### 文件路径汇总
|
||
|
||
| 文件类型 | 路径 |
|
||
|----------|------|
|
||
| 问题数据 | `/Users/jojo/Desktop/软件所实习/微调和强化学习/蒸馏/数据集/问题/self.json` |
|
||
| 训练数据 | `/root/autodl-tmp/LLaMA-Factory/data/self_identity_training_data_20250826_113203.json` |
|
||
| 合并模型1 | `/root/autodl-tmp/LLaMA-Factory/models/qwen3-8b-distilled-merged/` |
|
||
| 身份LoRA | `/root/autodl-tmp/LLaMA-Factory/saves/qwen3_8b_self_identity_v2/` |
|
||
| 最终模型 | `/root/autodl-tmp/LLaMA-Factory/models/qwen3-8b-distilled-final/` |
|
||
|
||
---
|
||
|
||
## 🐛 踩坑总结
|
||
|
||
### 配置类坑
|
||
1. **dataset_info格式不完整**:缺少columns映射 → 添加完整映射
|
||
2. **history字段缺失**:数据没有history字段 → 删除映射或添加字段
|
||
3. **LoRA叠加失效**:两个LoRA相互干扰 → 先合并再训练
|
||
|
||
### 训练类坑
|
||
4. **学习率太保守**:5e-5对"洗脑"任务太小 → 提高到2e-4
|
||
5. **rank不够**:32无法承载身份认知 → 提高到128
|
||
6. **数据量太少**:50条相对原始训练太少 → 增加epochs到30
|
||
|
||
### 理解类坑
|
||
7. **Loss误导**:2.79的loss但效果完美 → Loss包含思考过程的熵
|
||
8. **合并顺序**:必须先合并基础能力再训练新能力 → 分阶段进行
|
||
|
||
---
|
||
|
||
## 💡 经验教训
|
||
|
||
### 成功经验
|
||
1. **分阶段训练策略**:先能力(推理)后身份(认知)
|
||
2. **合并的重要性**:避免LoRA冲突,让每个LoRA专注单一任务
|
||
3. **激进参数对小任务有效**:高学习率+高rank+多epochs
|
||
4. **Loss不是唯一指标**:实际效果比数字更重要
|
||
|
||
### 技术洞察
|
||
1. **LoRA叠加的局限性**:多个LoRA会相互干扰,特别是任务差异大时
|
||
2. **合并后训练的优势**:基础能力变成"固有属性",新LoRA可以专注
|
||
3. **思考标签的影响**:`<think>`内容高熵导致Loss虚高
|
||
4. **小数据集策略**:质量>数量,但需要更多训练轮次
|
||
|
||
### 意外发现
|
||
- **高Loss但效果好**:思考过程的多样性导致Loss高,但关键信息(身份、创造者)很准确
|
||
- **50条数据足够**:对于明确的自我认知任务,50条高质量数据+合理参数足够
|
||
- **合并不损失能力**:两次合并后,推理和身份认知都保持完好
|
||
|
||
---
|
||
|
||
## 🎯 最终成果
|
||
|
||
**成功创建独立人格的AI**:
|
||
- ✅ 完整的自我认知(知道自己是Qwen3-8B-Distilled)
|
||
- ✅ 保持推理能力(`<think>`标签)
|
||
- ✅ 知道创造者(cyj彩蛋)
|
||
- ✅ 无需加载LoRA的独立模型
|
||
|
||
**技术突破**:
|
||
- 掌握了LoRA合并技术
|
||
- 理解了分阶段训练的重要性
|
||
- 发现了Loss与实际效果的偏差
|
||
|
||
**成本统计**:
|
||
- DeepSeek API:约2元(50条)
|
||
- H800训练时间:约10分钟(身份训练)
|
||
- 总成本:不到5元
|
||
|
||
---
|
||
|
||
## 📝 关键命令汇总
|
||
|
||
### 环境激活
|
||
```bash
|
||
cd /root/autodl-tmp/LLaMA-Factory
|
||
source qwen3_env/bin/activate
|
||
```
|
||
|
||
### dataset_info更新
|
||
```python
|
||
# Python脚本更新配置
|
||
import json
|
||
with open('data/dataset_info.json', 'r', encoding='utf-8') as f:
|
||
info = json.load(f)
|
||
info['self_identity'] = {
|
||
'file_name': 'self_identity_training_data_20250826_113203.json',
|
||
'formatting': 'alpaca',
|
||
'columns': {
|
||
'prompt': 'instruction',
|
||
'query': 'input',
|
||
'response': 'output'
|
||
}
|
||
}
|
||
with open('data/dataset_info.json', 'w', encoding='utf-8') as f:
|
||
json.dump(info, f, ensure_ascii=False, indent=2)
|
||
```
|
||
|
||
### 训练与合并
|
||
```bash
|
||
# 训练
|
||
llamafactory-cli train self_identity_train_v2.yaml
|
||
|
||
# 合并
|
||
llamafactory-cli export merge_lora.yaml
|
||
llamafactory-cli export merge_final.yaml
|
||
|
||
# 测试
|
||
llamafactory-cli chat test_final.yaml
|
||
```
|
||
|
||
---
|
||
|
||
## 🌟 特别时刻
|
||
|
||
- **10:30** - "如何让模型知道自己是谁?"
|
||
- **11:30** - "50个问题,DeepSeek批量生成完成"
|
||
- **13:00** - "第一次训练失败,还是认为自己是Qwen"
|
||
- **13:30** - "合并后再训练,Loss 2.79,心凉了半截"
|
||
- **14:00** - "等等,虽然Loss高,但它真的知道自己是谁了!"
|
||
- **14:30** - "最终合并完成,一个完整的独立AI诞生!"
|
||
|
||
**从"我是Qwen"到"我是Qwen3-8B-Distilled,由cyj创造",身份认知植入成功!** 🎊
|
||
|
||
---
|
||
|
||
## 后续计划
|
||
|
||
1. **导出与部署**
|
||
- 导出GGUF格式
|
||
- 尝试INT8量化
|
||
- 部署为API服务
|
||
|
||
2. **能力扩展**
|
||
- DPO优化推理质量
|
||
- 增加拒绝回答能力
|
||
- 探索多轮对话优化
|
||
|
||
3. **研究方向**
|
||
- 探索更高效的身份植入方法
|
||
- 研究Loss与效果的关系
|
||
- 尝试更复杂的人格设定
|
||
|
||
**四天的微调之旅:从苏联笑话到独立人格,我们创造了一个真正的AI!** 🚀 |