5 微调实验-lora-打造知乎风格问答机器人

使用 Swift 微调 Llama-3.2-3B 打造知乎风格问答机器人

实验记录:从数据清洗到模型微调的完整流程与踩坑指南

https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/LoRA-Training.html

一、实验目标

训练一个具有知乎答主风格的问答机器人,能够:

  • 以真实人类的语气回答问题
  • 展现丰富的情感(开心、幸福、愤怒、伤心、阴阳怪气等)
  • 保持知乎高赞回答的逻辑性和可读性

二、实验环境

项目 配置
硬件 Mac 笔记本 (Apple Silicon)
加速 MPS (Metal Performance Shaders)
微调框架 ms-swift
微调方法 LoRA
基座模型 Llama-3.2-3B-Instruct

三、数据准备

3.1 数据来源

使用 ModelScope 上的 Zhihu-KOL 数据集,包含知乎高质量问答内容。

python 复制代码
from modelscope.msdatasets import MsDataset

ds = MsDataset.load('OmniData/Zhihu-KOL', cache_dir='../data', split='train')
print(f"原始数据量: {len(ds)}")  # 约 100 万条

原始数据格式:

json 复制代码
{
  "INSTRUCTION": "怎么说服男朋友买烤箱?",
  "RESPONSE": "emmmmm,首先想说的是...",
  "METADATA": "{\"upvotes\": \"赞同 15\", ...}",
  "SOURCE": "Zhihu"
}

3.2 数据清洗

使用 Data-Juicer 进行数据清洗,配置文件 zhihu-bot.yaml

yaml 复制代码
project_name: 'zhihu-process'
dataset_path: '../data/zhihu.jsonl'
np: 16
text_keys: 'response'
export_path: '../data/zhihu_refine.jsonl'

process:
  # 1. 过滤低点赞数据
  - specified_numeric_field_filter:
      field_key: 'upvotes'
      min_value: 500

  # 2. 文本长度过滤
  - text_length_filter:
      min_len: 100
      max_len: 2000

  # 3. 清洗处理
  - clean_email_mapper:
  - clean_html_mapper:
  - clean_ip_mapper:
  - clean_links_mapper:
  - clean_copyright_mapper:

  # 4. 语言和质量过滤
  - language_id_score_filter:
      lang: zh
      min_score: 0.9
  - alphanumeric_filter:
      tokenization: false
      min_ratio: 0.72
  - flagged_words_filter:
      lang: zh
      max_ratio: 0.0005
  - perplexity_filter:
      lang: zh
      max_ppl: 4000

  # 5. 去重
  - document_simhash_deduplicator:
      tokenization: character
      window_size: 5
      num_blocks: 10
      hamming_distance: 6

  # 6. 选取 Top 50000 高赞数据
  - topk_specified_field_selector:
      field_key: 'upvotes'
      topk: 50000
      reverse: True

3.3 数据质量分析

清洗后数据统计:

指标 数值 说明
数据量 50,000 条 从 100 万条中筛选
语言 98.5% 中文 主要为中文内容
平均困惑度 2390 偏高,但可接受
点赞范围 500 ~ 634,000 高质量回答

关于困惑度偏高的分析

  • 困惑度 2000+ 在基础模型上计算是正常的
  • 最大值 170 万是异常值,已通过 max_ppl: 4000 过滤
  • 知乎回答风格口语化、有网络用语,困惑度偏高符合预期

3.4 最终数据格式

转换为 Swift 支持的格式:

json 复制代码
{"query": "语文考试可以写负能量作文吗?", "response": "我真的是服你们这群崽子...", "upvotes": 434000}
{"query": "有哪些知识,你知道后你的世界马上就不一样了?", "response": "1.原则上可以,就是不可以...", "upvotes": 399000}

四、模型微调

4.1 Swift 微调命令(Mac MPS 版本)

bash 复制代码
swift sft \
    --train_type lora \
    --model /path/to/Llama-3.2-3B-Instruct \
    --dataset ../data/zhihu_train.jsonl#3000 \
    --system "你是一个真实的人类。回答用户的问题,并在聊天回复中展现你的情绪,包括开心,幸福,愤怒,伤心,阴阳怪气等" \
    --split_dataset_ratio 0.01 \
    --output_dir ../output \
    --target_modules all-linear \
    --lora_rank 4 \
    --torch_dtype fp16 \
    --device_map mps \
    --seed 42 \
    --learning_rate 1e-4 \
    --warmup_ratio 0.05 \
    --max_length 512 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --num_train_epochs 1 \
    --gradient_accumulation_steps 16 \
    --gradient_checkpointing true \
    --save_total_limit 2 \
    --eval_steps 200 \
    --save_steps 200

4.2 关键参数说明

参数 说明
--train_type lora 使用 LoRA 微调,参数高效
--target_modules all-linear 作用于所有线性层
--lora_rank 4 LoRA 秩,越小参数越少
--torch_dtype fp16 MPS 不支持 bf16
--device_map mps 使用 Apple Silicon GPU
--per_device_train_batch_size 1 Mac 内存有限,设为 1
--gradient_accumulation_steps 16 梯度累积补偿小 batch
--gradient_checkpointing true 用时间换内存
--max_length 512 限制序列长度,节省内存

4.3 实验具体数据

{'loss': 2.13094215, 'acc': 0.55739484, 'grad_norm': 0.6459673, 'learning_rate': 8.99e-06, 'memory(GiB)': 11.12, 'train_speed(iter/s)': 0.218063, 'epoch': 0.82, 'global_step/max_steps': '280/343', 'percentage': '81.63%', 'elapsed_time': '21m 22s', 'remaining_time': '4m 48s'}

  • acc 0.557 (55.7%)
    → 模型能正确预测超过一半的 token
    → 对于生成任务来说是正常水平
  • grad_norm 0.646
    → 非常稳定,说明训练平稳
    异常情况:
  • grad_norm 很大 (>10) → 可能梯度爆炸
  • grad_norm 接近 0 → 可能梯度消失

五、踩坑记录

5.1 Data-Juicer: topk_specified_field_selector 非常慢

问题topk_specified_field_selector 处理 100 万条数据耗时极长。

原因

  • 全局排序无法并行化,必须收集所有数据
  • 排序算法复杂度 O(n log n)

解决方案

  1. 提高 upvotes 阈值预过滤数据
  2. 分两步处理:先过滤去重,再单独排序

5.2 Swift 新版本参数名变化

问题:旧教程的参数名在新版本中不识别。

参数对照表

旧参数 新参数
--sft_type --train_type
--model_type + --model_id_or_path --model
--dataset_test_ratio --split_dataset_ratio
--lora_target_modules --target_modules
--dtype --torch_dtype
--batch_size --per_device_train_batch_size
--device --device_map

5.3 target_modules: ALL 不被识别

问题

复制代码
ValueError: Target modules {'ALL'} not found in the base model.

解决

  • 新版本使用 all-linear 而非 ALL
  • 或指定具体模块:q_proj k_proj v_proj o_proj gate_proj up_proj down_proj

5.4 MPS 不支持 bf16

问题:Mac MPS 后端不支持 bfloat16。

解决

  • 使用 --torch_dtype fp16 替代 bf16
  • 或使用 fp32(更慢但更稳定)

5.5 内置数据集下载失败

问题qwen2-pro-zh 等内置数据集无法自动下载。

解决

  • 只使用本地数据集:--dataset ../data/zhihu_train.jsonl#3000
  • 或手动从 ModelScope 下载后本地引用

六、垂直领域微调的核心要点

6.1 数据质量 > 数据数量

垂直领域微调,数据质量是第一要素:

维度 要求 本实验做法
准确性 内容正确无误 选取高赞回答(经过社区验证)
多样性 覆盖领域内不同场景 保留多话题、多风格
一致性 风格/格式统一 统一转换为 query-response 格式
代表性 体现领域特点 保留知乎特有的表达风格

经验法则

  • 高质量数据 1 万条 > 低质量数据 10 万条
  • 宁缺毋滥,异常数据会严重影响模型效果

6.2 数据混用策略(防止灾难性遗忘)

微调垂直领域时,模型容易"忘记"通用能力。解决方案是混合训练数据

复制代码
总训练数据 = 垂直领域数据 + 通用数据
推荐混合比例
场景 垂直数据 : 通用数据 说明
轻度定制 7 : 3 保留大部分通用能力
中度定制 5 : 5 平衡垂直和通用
深度定制 8 : 2 专注垂直领域,接受通用能力下降
本实验的数据混用示例
bash 复制代码
--dataset ../data/zhihu_train.jsonl#3000 \   # 垂直领域:知乎数据
         alpaca-gpt4-data-zh#1500 \          # 通用数据:中文指令
         belle-1m#1500                        # 通用数据:中文对话
通用数据推荐
数据集 用途 来源
alpaca-gpt4-data-zh 中文指令跟随 ModelScope
belle-1m 中文多轮对话 ModelScope
firefly-train-1.1M 中文多任务 HuggingFace
moss-sft-data 通用问答 ModelScope

6.3 防止灾难性遗忘的技术手段

除了数据混用,还有以下方法:

方法 1:使用 LoRA 而非全量微调
bash 复制代码
--train_type lora        # LoRA 只更新少量参数
--lora_rank 4            # 较小的 rank 减少对原模型的干扰

原理:LoRA 冻结原始权重,只训练低秩适配器,最大程度保留原始能力。

方法 2:降低学习率
bash 复制代码
--learning_rate 1e-5     # 比通常的 1e-4 更小

原理:小学习率减少对原始权重的修改幅度。

方法 3:减少训练轮数
bash 复制代码
--num_train_epochs 1     # 1-2 轮足够

原理:过多轮次会导致过拟合到垂直领域,丢失通用能力。

方法 4:使用 NEFTune(噪声嵌入)
bash 复制代码
--neftune_noise_alpha 5  # 在嵌入层添加噪声

原理:增加训练噪声,提高模型泛化能力。

6.4 数据配比的动态调整

训练过程中可以观察指标来调整配比:

现象 原因 调整方向
垂直领域效果差 垂直数据不足 增加垂直数据比例
通用能力明显下降 过拟合垂直领域 增加通用数据比例
两者都不理想 数据质量问题 检查数据清洗流程

6.5 评估策略

微调后需要同时评估两方面:

复制代码
1. 垂直领域评估
   - 用垂直领域测试集评估(如知乎问题)
   - 人工评估回答风格是否符合预期

2. 通用能力评估
   - 用通用 benchmark 测试(如 C-Eval、CMMLU)
   - 测试常识问答、数学、代码等基础能力

6.6 本实验的数据策略总结

复制代码
┌─────────────────────────────────────────────────────┐
│                    训练数据组成                      │
├─────────────────────────────────────────────────────┤
│  知乎高赞数据 (3000条)                               │
│  ├── 高点赞筛选 (upvotes > 500)                     │
│  ├── 质量过滤 (困惑度、长度、语言)                    │
│  └── SimHash 去重                                   │
├─────────────────────────────────────────────────────┤
│  通用中文数据 (建议添加 1500-3000条)                  │
│  ├── alpaca-gpt4-data-zh (指令跟随)                 │
│  └── belle / moss (对话能力)                        │
└─────────────────────────────────────────────────────┘
                         ↓
              LoRA 微调 (rank=4, lr=1e-4)
                         ↓
              知乎风格 + 保留通用能力

七、Mac 微调的局限性

限制 影响 建议
内存有限 batch_size 只能设 1 使用梯度累积
不支持 bf16 精度略有损失 使用 fp16
速度慢 3B 模型训练耗时长 用于小规模测试
MPS 不稳定 部分算子可能出错 设置 PYTORCH_ENABLE_MPS_FALLBACK=1

建议:Mac 适合小规模测试(几百~几千条数据),正式训练建议使用云服务器(如 AutoDL A10 GPU)。

八、总结

本实验完成了:

  1. ✅ 知乎数据集清洗与筛选(100万→5万条高质量数据)
  2. ✅ 数据格式转换(适配 Swift 框架)
  3. ✅ 配置 Mac MPS 环境的微调参数
  4. ✅ 解决 Swift 新版本的兼容性问题

后续计划:

  • 在 GPU 服务器上完成完整训练
  • 评估微调后模型的回答质量
  • 尝试不同的 system prompt 调整风格
  • 多轮对话能力增强
相关推荐
小超同学你好2 小时前
LangGraph 9. Agent 背后:ReAct
人工智能·语言模型·langchain
人工智能AI技术2 小时前
百度文心搜索4.0+C# RAG实战:打造支持实时问答与长文档总结的智能客服
人工智能
一个处女座的程序猿2 小时前
LLMs之Pretrained:《Training Language Models via Neural Cellular Automata》翻译与解读
人工智能·深度学习·llms·pretrained
是烨笙啊2 小时前
五分钟上线:基于DeepSeek-OCR的多功能web应用
人工智能·aigc·ocr
rainy雨2 小时前
六西格玛改进系统的全流程功能:传统企业转型中如何用六西格玛解决成本失控与交付延期的双重难题
大数据·人工智能·精益工程
人工干智能2 小时前
科普:OpenClaw、大模型、通道及云端养虾
网络·人工智能·llm
2501_926978332 小时前
“AI构建APP”--到--“AI的动力性底层存在”--到--“AGI合法性验证”--AI治理的核心痛点解决方案
人工智能·经验分享·ai写作·agi
猿小猴子2 小时前
主流 AI IDE 之一的 JoyCode 介绍
ide·人工智能
格林威2 小时前
工业相机图像高速存储(C#版):直接IO(Direct I/O)方法,附Basler相机实战代码!
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机