LLaMA-Factory 微调 DeepSeek-R1 模型实战指南
在大模型日益普及的今天,如何让一个通用语言模型真正“懂你”,成为个性化的智能助手?答案就是——微调。而对大多数开发者而言,传统微调流程复杂、门槛高、依赖多,常常望而却步。直到LLaMA-Factory的出现,这一切开始变得简单。
它不仅支持主流开源模型的一键微调,还提供了直观的 WebUI 界面,无需写一行代码,就能完成从数据准备到模型导出的全流程。本文将以DeepSeek-R1-Distill-Qwen-1.5B为例,带你从零开始,完整走通一次本地化、轻量级、个性化对话模型的微调实战。
环境搭建:构建稳定高效的训练基础
任何成功的微调项目,都始于一个干净、兼容性强的运行环境。我们推荐使用 Anaconda 来管理 Python 依赖,避免版本冲突带来的“玄学问题”。
# 创建独立虚拟环境,锁定 Python 3.10 conda create -n llamafactory python=3.10 conda activate llamafactory为什么是 3.10?因为这是目前 PyTorch 和 Hugging Face 生态兼容性最好的版本之一,尤其对于bitsandbytes这类底层库的支持更为成熟。
接下来克隆项目源码:
git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -r requirements.txt pip install -e ".[torch,metrics]"如果你在国内,强烈建议使用清华镜像源加速安装过程:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/GPU 支持与量化配置
显卡选型上,NVIDIA RTX 3060 12GB 是当前性价比极高的入门选择。虽然全参数微调需要 24GB+ 显存,但借助 QLoRA 技术,我们可以在 12GB 显存下轻松完成 LoRA 微调。
首先确认 CUDA 版本并安装对应 PyTorch:
# 示例:CUDA 11.8 conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia验证是否可用:
import torch print(torch.cuda.is_available()) # 应输出 True关键一步是安装bitsandbytes,它是实现 4-bit 量化的基石。Windows 用户需注意,官方包不提供预编译版本,必须使用社区维护的 wheel 包:
pip install https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.41.2.post2-py3-none-win_amd64.whlLinux 用户则可直接通过 pip 安装:
pip install bitsandbytes实测表明,启用 QLoRA 后,1.5B 模型的显存占用可控制在 9~11GB 范围内,极大降低了本地训练门槛。
启动 WebUI:打通最后一公里
LLaMA-Factory 最大的优势在于其图形化界面。只需一条命令即可启动:
llamafactory-cli webui但首次运行时可能会遇到无法访问localhost:7860的问题。这是因为 Gradio 默认未开启公网访问权限。解决方法很简单:
- 打开
src/llamafactory/webui/interface.py - 找到
run_web_ui()和run_web_demo()函数 - 将
share=gradio_share改为share=True
重启后浏览器会自动打开类似https://xxxx.gradio.live的公网链接。不过要注意:不要在代理环境下访问该地址,否则页面可能加载失败或响应异常。
模型准备:为何选择 DeepSeek-R1?
本次实战选用的是DeepSeek-R1-Distill-Qwen-1.5B,由深度求索发布的一款基于 Qwen 架构蒸馏而来的小模型。尽管只有 1.5B 参数,但它在中文理解和生成方面表现优异,尤其擅长模仿人类语气和表达习惯。
更重要的是,它的上下文长度高达32,768 tokens,远超同类小模型(如 Phi-3、Gemma-2B),非常适合处理长对话历史或多轮交互场景。
你可以通过以下方式下载模型:
# 使用 huggingface-cli(推荐) huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --local-dir ./models/DeepSeek-R1-1.5B或者用 git lfs:
git lfs install git clone https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B保存路径建议统一管理,例如E:\Model\DeepSeek-R1-1.5B。后续在 WebUI 中配置模型路径时将直接引用此目录。
| 属性 | 值 |
|---|---|
| 模型名称 | DeepSeek-R1-Distill-Qwen-1.5B |
| 参数量 | ~1.5B |
| 架构 | Qwen(类 LLaMA) |
| 上下文长度 | 32K tokens |
| 训练方式 | 蒸馏自更大规模模型 |
| 优势 | 中文能力强、推理快、适合轻量部署 |
这类蒸馏模型的本质,是将大模型的知识“压缩”进小模型中,在保持性能的同时大幅降低资源消耗。正因如此,它非常适合作为个性化助手、客服机器人或垂直领域知识引擎的基础模型进行微调。
数据构建:打造你的专属语料库
微调的核心思想是“用你的数据教会模型说你的话”。因此,高质量的数据集决定了最终效果的上限。
我们以微信聊天记录为例,展示如何从个人对话中提取可用于训练的语料。
数据采集工具推荐
推荐使用国产工具留痕(MemoTrace)(官网:https://memotrace.cn)。它支持一键导出微信聊天记录为 JSON 格式,保留角色、时间戳和内容,结构清晰,便于后续处理。
其他替代方案包括:
- 自研爬虫 + 正则提取
- 使用 WeChatPYAPI
- 第三方工具如 iMazing(付费)
导出后的文件结构如下:
[ { "messages": [ { "role": "user", "content": "你好啊" }, { "role": "assistant", "content": "嗨,最近怎么样?" } ] } ]多源数据合并脚本
若有多段聊天记录分散在不同.json文件中,需先合并为单一文件。编写merge.py:
import os import json folder_path = r'D:\data\wechat\chat_records' json_files = [os.path.join(root, f) for root, _, files in os.walk(folder_path) for f in files if f.endswith('.json')] merged_data = [] for file in json_files: try: with open(file, 'r', encoding='utf-8') as f: data = json.load(f) if isinstance(data, list): merged_data.extend(data) else: merged_data.append(data) except Exception as e: print(f"Error loading {file}: {e}") output_path = os.path.join(folder_path, 'merged_data.json') with open(output_path, 'w', encoding='utf-8') as f: json.dump(merged_data, f, ensure_ascii=False, indent=2) print(f"✅ 合并完成!共 {len(merged_data)} 条对话记录")执行后生成merged_data.json,作为下一步清洗的输入。
清洗与格式化:迈向标准输入
原始数据常包含链接、手机号、表情符号等噪声,必须清洗。同时,LLaMA-Factory 内部采用ShareGPT 格式作为默认协议,我们需要将数据转换为此结构。
创建Data_Preprocessing.py:
import json import re def clean_text(text): text = re.sub(r'http[s]?://\S+', '[URL]', text) text = re.sub(r'\d{3}[-]?\d{4}[-]?\d{4}', '[PHONE]', text) text = re.sub(r'\S+@\S+', '[EMAIL]', text) text = re.sub(r'\d{4}-\d{2}-\d{2}', '[DATE]', text) text = re.sub(r'[^\u4e00-\u9fa5\w\s.,!?;:"\']+', '', text) return text.strip() with open('merged_data.json', 'r', encoding='utf-8') as f: raw_data = json.load(f) converted = [] for item in raw_data: if not item.get('messages'): continue conv = {"conversations": []} for msg in item['messages']: role = msg.get('role') content = msg.get('content', '').strip() if not content: continue content = clean_text(content) if role == 'system': continue elif role == 'user': from_role = 'human' elif role == 'assistant': from_role = 'gpt' else: continue conv['conversations'].append({ "from": from_role, "value": content }) if len(conv['conversations']) >= 2: converted.append(conv) with open('converted_data.json', 'w', encoding='utf-8') as f: json.dump(converted, f, ensure_ascii=False, indent=2) print("✅ 数据清洗与格式化完成:converted_data.json")为什么用 ShareGPT 格式?
因为它是目前最广泛支持的对话数据标准之一,结构简洁、语义明确,能被 LLaMA-Factory、FastChat、OpenChatKit 等多个框架直接识别。
开始微调:WebUI 全流程实战
一切就绪,进入重头戏。
注册数据集
将converted_data.json移至LLaMA-Factory/data/目录下。
编辑data/dataset_info.json,新增条目:
"converted_data": { "file_name": "converted_data.json", "formatting": "sharegpt", "columns": { "messages": "conversations" } }字段说明:
-file_name:相对路径下的文件名
-formatting:固定为sharegpt
-columns.messages:指定对话数组所在的 key 名
保存后重启 WebUI,在“数据集”下拉框中应能看到converted_data选项。
配置训练参数
打开 WebUI → 【SFT】页面,填写以下关键设置:
基础配置
| 参数 | 值 |
|---|---|
| 模型名称 | qwen1.5 |
| 模型路径 | E:\Model\DeepSeek-R1-1.5B |
| 数据集 | converted_data |
| 输出目录 | saves/DeepSeek-R1-1.5B/lora |
推荐高级参数
| 参数 | 值 | 说明 |
|---|---|---|
| 微调方法 | LoRA | 显存友好,仅训练增量权重 |
| 模块 | q_proj,v_proj | 注意力层核心矩阵,影响最大 |
| LoRA Rank | 64 | 控制低秩矩阵维度 |
| LoRA Alpha | 128 | 缩放系数,通常设为 rank 的两倍 |
| Dropout | 0.1 | 提升泛化能力 |
| Batch Size | 4 | 单卡容量限制 |
| Grad Accum Steps | 4 | 实现等效 batch size=16 |
| 学习率 | 2e-4 | AdamW 常用值 |
| Epochs | 3 | 防止过拟合 |
| Max Source Length | 1024 | 输入最大长度 |
| Max Target Length | 1024 | 输出最大长度 |
| FP16 | ✅ 开启 | 减少显存占用 |
| Quantization Bit | 4 | 启用 QLoRA 量化 |
点击【预览命令】可查看实际执行的训练指令:
CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \ --stage sft \ --model_name_or_path E:/Model/DeepSeek-R1-1.5B \ --do_train \ --dataset converted_data \ --template default \ --finetuning_type lora \ --lora_target q_proj,v_proj \ --output_dir saves/DeepSeek-R1-1.5B/lora \ --overwrite_cache \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 4 \ --lr_scheduler_type cosine \ --logging_steps 10 \ --save_steps 100 \ --learning_rate 2e-4 \ --num_train_epochs 3.0 \ --max_grad_norm 1.0 \ --max_samples 10000 \ --fp16 True \ --plot_loss True \ --quantization_bit 4确认无误后点击【开始】,训练正式启动。
训练监控与效果评估
训练过程中,WebUI 会实时绘制 loss 曲线。理想情况下应呈现以下趋势:
- 初始阶段快速下降(>3.0 → <1.5)
- 后期趋于平稳,波动较小
- 若出现剧烈震荡或突然飙升,可能是学习率过高或数据异常
终端输出的关键指标解读:
| 指标 | 含义 |
|---|---|
epoch | 当前训练轮次 |
train_loss | 平均损失值,越低越好 |
num_input_tokens_seen | 已处理 token 总数 |
total_flos | 浮点运算总量(GFLOPs) |
train_runtime | 总耗时(秒) |
train_samples_per_second | 每秒处理样本数,反映效率 |
训练完成后,LoRA 权重将保存在指定输出目录中。
对话测试:见证“变身”时刻
切换至【Chat】标签页:
- 加载模型路径:选择原模型目录
- 加载 Adapter:指向训练好的 LoRA 文件夹
- 点击【Load】
- 等待提示:“模型已加载,可以开始聊天了!”
输入测试问题,例如:“我们昨天聊了什么?” 观察回复是否带有你的语言风格特征。如果回答自然流畅、语气贴合,则说明微调成功。
小技巧:可同时打开原始模型进行对比测试,直观感受差异。
模型导出与部署:走向生产环境
训练得到的 LoRA 是增量权重,不能独立运行。要将其合并到底层模型中才能用于部署。
进入【Export】页面:
- 模型路径:原 DeepSeek-R1 模型路径
- Adapter 路径:训练输出的 LoRA 目录
- 导出设备:auto
- 导出目录:model/DeepSeek-R1-1.5B-finetuned
点击【Export】,等待合并完成。
导出后的模型可用于:
- Transformers 直接加载
- FastAPI 封装为 REST API
- 部署至小程序或桌面应用
- 转换为 GGUF 格式供 llama.cpp 使用
建议同步生成部署依赖清单:
transformers>=4.41.2 torch>=2.0.0 accelerate peft bitsandbytes sentencepiece tiktoken gradio这种高度集成、可视化驱动的微调范式,正在重新定义我们与大模型的关系——不再只是使用者,更是塑造者。未来,你可以尝试引入更专业的语料(如法律、医疗)、结合 DPO/PPO 进行偏好对齐,甚至在多 GPU 环境下加速训练。
愿你在模型定制之路上不断精进,打造出真正懂你、助你的智能伙伴。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考