profileName: youpingfang postId: 370 postType: post categories:
- 6
LoRA 算法论文解读 & 开发人员如何微调大模型并暴露可调用接口
简介
一、视频资料与链接
- B站视频:BV1R6P7eVEtd
- Demo前端Github地址:https://github.com/huangyf2013320506/magic_conch_frontend.git
- Demo后端Github地址(含数据集):https://github.com/huangyf2013320506/magic_conch_backend.git
- 笔记文档(.md):https://pan.quark.cn/s/57939e67d3d0
- 笔记文档(.pdf):https://pan.quark.cn/s/d5ed78ef4f76
- 所有资料:https://pan.quark.cn/s/802cd0c232b4
二、企业大模型个性化需求与技术选型
2.1 三类技术简介
2.1.1 有监督微调(SFT,Supervised Fine-Tuning)
定义:通过人工标注的数据集(包含输入和标准输出),对预训练模型进行进一步训练,让模型能够更精准地处理特定领域的任务。
注:当大家提到"微调"时,通常默指有监督微调。
适用场景: - 需要模型理解企业专有信息 - 需要增强模型在特定行业领域的知识
案例: - 让模型理解"蟹堡王"的企业专有知识 - 让模型精通汉堡制作领域的所有问题
2.1.2 强化学习(RLHF,Reinforcement Learning from Human Feedback)
DPO(Direct Preference Optimization): - 通过人类对比选择(如从A、B两个选项中直接选择更好的一个)来优化模型 - 调整幅度较大
PPO(Proximal Policy Optimization): - 通过奖励信号(如点赞、点踩)来渐进式调整模型行为 - 调整幅度较小、更温和稳健
适用场景:需要提供个性化和互动性强服务的场景。
案例:让模型能够根据用户反馈调整回答方式,如生成更二次元风格或更学术风格的回答。
2.1.3 检索增强生成(RAG,Retrieval-Augmented Generation)
定义:将外部信息检索与文本生成结合,帮助模型在生成答案时实时获取外部信息和最新信息。
适用场景: - 需要获取和生成最新、实时信息 - 企业数据量极少或需要动态更新
案例:让模型能够实时获取企业最新的促销活动信息和每周菜单更新。
2.2 技术选型与需求匹配
2.2.1 需求场景与技术对应表
| 需求类型 | 具体描述 | 推荐技术 |
|---|---|---|
| 私有知识理解 | 让模型理解企业内部不公开的专有信息 | 微调 + RAG 效果最佳 |
| 垂直行业强化 | 增强模型在特定细分领域的专业知识 | 微调(需大量数据) |
| 个性化交互 | 根据用户反馈调整回答风格和方式 | RLHF |
| 实时信息获取 | 模型需要获取最新或动态更新的信息 | RAG |
2.2.2 微调 vs RAG 选型指南
| 对比维度 | 微调 | RAG |
|---|---|---|
| 数据量要求 | 需要充足私有数据(上千条),数据不足易过拟合 | 数据量很少也可行 |
| 数据更新频率 | 不适合频繁更新,每次更新都要重新微调 | 适合动态更新的知识,仅需维护知识库 |
| 效果特点 | 直接提升模型固有能力,回答速度快且质量稳定 | 回答前需检索知识库,耗时较长,质量依赖检索系统 |
最佳实践: - 少量企业私有知识:微调和 RAG 都做效果最好 - 资源不足时:优先做 RAG,优于仅做微调 - 会动态更新的知识:选择 RAG - 大量垂直领域知识:选择微调
2.3 微调算法分类
| 类型 | 全参数微调(Full Fine-Tuning) | 部分参数微调(Partial Fine-Tuning) |
|---|---|---|
| 方法 | 对整个预训练模型微调,更新所有参数 | 只更新模型的部分参数(如某些层或模块) |
| 优点 | 每个参数都可调整,通常能得到最佳性能 | 减少计算成本,减少过拟合风险 |
| 缺点 | 需要较大计算资源,容易过拟合 | 可能无法达到最佳性能 |
| 代表算法 | — | LoRA(最著名的部分参数微调算法) |
三、LoRA 算法详解
3.1 背景与动机
- 提出时间:2021年
- 提出团队:Microsoft Research(Microsoft Research 在2017年发布了大语言模型开山论文《Attention Is All You Need》)
- 核心问题:大语言模型参数规模巨大(如 GPT-3 有 1750 亿参数),全参数微调的计算和存储代价高昂
- 解决方案:通过低秩矩阵分解的方式来进行部分参数微调
LoRA 的提出极大推动了 AI 技术在多行业的广泛落地应用。
3.2 核心思想
两步走策略:
- 冻结(Freeze):不对预训练模型的原始权重矩阵 W₀ 做任何修改
- 注入(Inject):在 Transformer 架构的每一层注入可训练的低秩分解矩阵 B 和 A
整个训练过程中,原始权重 W₀ 保持不变,只更新低秩矩阵 B 和 A 的参数。
3.3 数学原理:低秩分解
3.3.1 什么是矩阵的秩
矩阵的秩指矩阵中线性无关的行或列的最大数量,反映矩阵所包含的有效信息量。
3.3.2 公式对比
| 方法 | 表达式 | 说明 |
|---|---|---|
| 全参数微调 | h = W₀x + ΔW₀x | W₀是满秩矩阵,ΔW₀也是与W₀大小相同的满秩矩阵 |
| LoRA 微调 | h = W₀x + BAx | B和A是两个低秩矩阵,BA近似表示ΔW₀ |
3.3.3 参数缩减计算示例
假设原始权重矩阵为 100×100: - 全参数微调需要训练:100 × 100 = 10,000 个参数 - LoRA 微调需要训练: - B矩阵:100 × 2 = 200 个参数 - A矩阵:2 × 100 = 200 个参数 - 合计:400 个参数(仅为全参数微调的 4%)
(100×2) × (2×100) → (100×100),通过矩阵乘法实现维度恢复。
3.3.4 训练后的权重合并
LoRA 训练得到的是两个低秩矩阵 B 和 A,训练结束后需要将它们与原始模型权重合并:
W_new = W₀ + BA
合并后的模型可直接用于推理,计算过程与原始模型完全一致。
3.4 核心优势
| 优势 | 说明 |
|---|---|
| 降低计算资源需求 | 参数量减少 96% 以上,显著降低训练成本和存储需求 |
| 无额外推理延迟 | 合并后的权重 W_new 与原始模型计算过程一致 |
| 轻量级多任务切换 | 不同任务只需切换对应的 BA 矩阵,无需存储多个完整模型 |
3.5 论文结构导读
| 章节 | 内容 |
|---|---|
| Introduction | 背景和问题介绍 |
| Problem Statement | 全参数微调面临的挑战 |
| Existing Solutions | 论证已有方案不够好 |
| Our Method | 提出低秩参数化更新矩阵的方法 |
推荐阅读: - LoRA 开山论文:《LoRA: Low-Rank Adaptation of Large Language Models》(2021) - 大语言模型奠基论文:《Attention Is All You Need》(2017,Google Brain)
四、微调框架介绍
| 框架 | 开源方 | 特点 | 适用场景 |
|---|---|---|---|
| LLaMA Factory | 国内北航 | 低代码、零代码微调,简单易学,当前热度最高 | 新手入门首选 |
| Transformers Trainer | HuggingFace | 高层API,标准化流程,多种监控工具 | 需要更多定制化,生产环境 |
| DeepSpeed | 微软 | 深度学习优化库,支持分布式训练 | 大规模模型预训练 |
五、完整实践步骤
5.1 整体流程概览
环境准备 → 模型下载 → 数据准备 → 微调训练 → 效果评估 → 模型导出 → 模型部署 → 后端调用
5.2 环境准备
5.2.1 硬件资源准备
以在云平台上租用 GPU 实例(如 AutoDL)为例。
平台地址:https://www.autodl.com/market/list
5.2.2 SSH 远程连接
使用 VS Code Remote 插件 SSH 连接到租用的服务器。
5.2.3 LLaMA Factory 安装部署
# 1. 克隆仓库
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
# 2. 修改 conda 配置(可选,将虚拟环境安装到数据盘)
mkdir -p /root/autodl-tmp/conda/pkgs
conda config --add pkgs_dirs /root/autodl-tmp/conda/pkgs
mkdir -p /root/autodl-tmp/conda/envs
conda config --add envs_dirs /root/autodl-tmp/conda/envs
# 3. 创建并激活虚拟环境(必须 Python 3.10)
conda create -n llama-factory python=3.10
conda activate llama-factory
# 4. 安装依赖
pip install -e ".[torch,metrics]"
# 5. 验证安装
llamafactory-cli version
如果报错
bash: pip: command not found,先执行conda install pip。
5.2.4 启动可视化界面
llamafactory-cli webui
5.2.5 配置端口转发
在本地电脑终端执行代理命令:
ssh -CNg -L 7860:127.0.0.1:7860 root@<服务器地址> -p <端口号>
5.2.6 下载基座模型
HuggingFace 下载方式(推荐使用国内镜像):
# 设置镜像源和下载路径
export HF_ENDPOINT=https://hf-mirror.com
export HF_HOME=/root/autodl-tmp/Hugging-Face
# 安装下载工具
pip install -U huggingface_hub
# 下载 DeepSeek-R1-Distill-Qwen-1.5B
huggingface-cli download --resume-download deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
如需持久化环境变量,将其添加到
~/.bashrc或~/.zshrc。
5.3 数据集准备
5.3.1 数据格式示例
对话系统数据格式:
[
{
"instruction": "请问你是谁",
"input": "",
"output": "您好,我是蟹堡王的神奇海螺,很高兴为您服务!"
},
{
"instruction": "怎么修复这个报错",
"input": "报错信息是:汉堡食谱为空",
"output": "请检查食谱文件是否存在..."
}
]
| 字段 | 说明 | 是否必填 |
|---|---|---|
| instruction | 指令,告诉模型需要做什么 | 是 |
| input | 上下文,用户提供的输入信息 | 否(可选) |
| output | 模型应给出的输出结果 | 是 |
5.3.2 数据集配置步骤
- 将数据集文件(如
magic_conch.json)放入 LLaMA Factory 的data目录下 - 修改
dataset_info.json文件,添加配置:json "magic_conch": { "file_name": "magic_conch.json" }## 5.4 训练参数详解 | 参数 | 概念解释 | 注意事项 | |------|---------|---------| | 学习率 | 模型每次更新时权重改变的幅度 | 过大错过最优解,过小训练慢或陷入局部最优 | | 训练轮数 | 完整遍历整个数据集的次数 | 太少欠拟合(没学好),太多过拟合(学过头) | | 最大梯度范数 | 梯度值超过阈值时被截断 | 防止梯度爆炸 | | 最大样本数 | 每轮训练中最多使用的样本数 | 数据量大时可设置 | | 计算类型 | 训练使用的数据类型(float32/float16) | float16 性能和精度间找平衡 | | 截断长度 | 处理长文本时的截断阈值 | 避免内存溢出 | | 批处理大小 | 单次处理的样本数 | 受内存限制 | | 梯度累积 | 多次计算批次后再更新一次参数 | 在小批次上模拟大批次效果 | | 验证集比例 | 训练集与验证集的分割比例 | 用于监控学习效果 | | 学习率调节器 | 训练过程中自动调整学习率 | 先快后慢 | ## 5.5 训练过程与评估 ### 5.5.1 启动训练 适合线上服务器训练的操作(使用nohup将任务放到后台执行):bash nohup <训练命令> > training.log 2>&1 &
5.5.2 效果评估
- 损失曲线:将损失降到最低。如果损失降低太慢,尝试增大学习率;如果训练结束损失仍呈下降趋势,增大训练轮数
- 对话测试:在交互页面上测试微调效果
5.5.3 微调效果不理想的解决方案
| 问题 | 解决方案 |
|---|---|
| 数据量不足 | 增加数据量,优化数据质量 |
| 模型强度不够 | 使用更强的预训练模型 |
| 参数不匹配 | 调整训练参数(学习率、训练轮数、批处理大小等) |
5.6 模型部署(FastAPI)
5.6.1 创建部署环境
conda create -n fastApi python=3.10
conda activate fastApi
conda install -c conda-forge fastapi uvicorn transformers pytorch
pip install safetensors sentencepiece protobuf
5.6.2 创建 API 应用
from fastapi import FastAPI
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
app = FastAPI()
# 模型路径
model_path = "/root/autodl-tmp/Models/deepseek-r1-1.5b-merged"
# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained(model_path)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = AutoModelForCausalLM.from_pretrained(model_path).to(device)
@app.get("/generate")
async def generate_text(prompt: str):
inputs = tokenizer(prompt, return_tensors="pt").to(device)
outputs = model.generate(inputs["input_ids"], max_length=150)
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
return {"generated_text": generated_text}
5.6.3 启动服务
uvicorn main:app --reload --host 0.0.0.0
5.6.4 验证服务
- 访问 API 文档:
http://localhost:8000/docs - 直接测试接口:
http://localhost:8000/generate?prompt=你是谁
5.7 Web 后端调用
5.7.1 Java 后端调用示例(使用 RestTemplate)
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private AiServiceConfig aiServiceConfig;
@Override
public String callAiForOneReply(String prompt) {
String baseUrl = aiServiceConfig.getBaseUrl();
String url = String.format("%s/generate?prompt=%s", baseUrl, prompt);
GenerateResponse response = restTemplate.getForObject(url, GenerateResponse.class);
return response != null ? response.getGenerated_text() : "";
}
}
5.7.2 前端工程启动
# 安装依赖并启动
npm install
npm run dev
5.7.3 后端工程启动
mvn clean install
# 在 Main 类中启动 Spring Boot 应用
5.7.4 企业级部署注意事项
- AutoDL 开放端口:需要企业认证,参考官方文档
- 生产环境:需考虑高并发、高可用、安全机制等问题
六、面试题及答案
Q1: 请详细解释 LoRA 算法的核心思想和数学原理。
A:
核心思想:冻结预训练模型的原始权重矩阵 W₀,在 Transformer 的每一层注入两个可训练的低秩分解矩阵 B 和 A。通过 BA 的乘积来近似表示全参数微调时的权重变化量 ΔW₀。
数学原理: - 全参数微调:h = W₀x + ΔW₀x(ΔW₀ 与 W₀ 大小相同) - LoRA 微调:h = W₀x + BAx(B和A为低秩矩阵,参数量大幅减少)
参数压缩示例:100×100 的矩阵,全参数微调需 10,000 个参数,LoRA 仅需 400 个参数(减少 96%)。
Q2: 微调、强化学习和 RAG 分别适合什么场景?如何选择?
A:
| 技术 | 适合场景 | 代表案例 |
|---|---|---|
| 微调(SFT) | 让模型吸收大量垂直领域知识;数据充足且稳定 | 精通汉堡制作的专家系统 |
| 强化学习(RLHF) | 根据用户反馈调整回答风格和方式 | 生成二次元或学术风格的回答 |
| RAG | 数据量极少;知识需要动态更新 | 实时获取每周菜单和促销活动 |
选型建议:三者可结合使用;少量私有知识优先选 RAG;大量垂直知识用微调;微调+RAG同时做效果最好。
Q3: LoRA 相比全参数微调有哪些核心优势?
A:
- 降低计算资源需求:参数量减少 90% 以上,降低训练成本和存储需求
- 无额外推理延迟:训练后可合并权重 BA 到 W₀,推理计算过程无额外耗时
- 轻量级多任务切换:不同任务只需切换对应的 BA 矩阵,无需存储多个完整模型
- 减少过拟合风险:只更新少量参数,降低模型死记硬背训练数据的风险
Q4: 什么是过拟合和欠拟合?在模型微调中如何解决?
A:
| 问题 | 表现 | 解决方法 |
|---|---|---|
| 欠拟合 | 损失曲线在训练结束时仍呈下降趋势,模型没学够 | 增大训练轮数、增大学习率、增强模型能力 |
| 过拟合 | 模型在训练数据上表现好但新数据上效果差,失去泛化能力 | 增加数据量和质量、使用更强的预训练模型、调整参数降低学习轮数 |
Q5: 请解释学习率、批处理大小和梯度累积的作用和关系。
A:
- 学习率:控制每次更新时权重改变的幅度。太大可能错过最优解,太小训练慢或陷入局部最优
- 批处理大小:单次处理样本数,影响训练效率和内存需求
- 梯度累积:多次计算批次后更新一次参数,在小批次上模拟大批次训练效果
三者关系:共同影响训练效率和稳定性。例如学习率较大时可配合梯度累积,让更新更平滑。
Q6: 为什么 LoRA 训练完成后需要进行权重合并?合并后的模型有什么优势?
A:
LoRA 训练只得到两个低秩矩阵 A 和 B,并没有改动原始预训练模型的权重。权重合并将 BA 与原始权重 W₀ 相加:W_new = W₀ + BA。
优势:合并后的模型 W_new 可直接用于推理,计算过程与原始模型完全一致,不增加任何推理延迟。此外,只需保存 W₀ 和小体积的BA矩阵,比保存多个完整模型更节省空间。
Q7: 分词器(Tokenizer)在模型部署中的作用是什么?
A:
分词器将自然语言文本转换为模型可理解的数字 ID 序列。它将文本分解为 token(可能是词、子词或字符),为每个 token 分配对应的数字 ID。
为何必须匹配:不同模型使用不同的分词策略和词表,必须使用与模型匹配的分词器,否则会导致输入编码错误,输出完全不可用。这也是为什么使用 AutoTokenizer.from_pretrained() 的重要性。
Q8: RAG 相比微调的主要缺点是什么?如何缓解?
A:
主要缺点: 1. 每次回答前需耗时检索知识库,响应速度较慢 2. 回答质量高度依赖检索系统的精准度
缓解方案: 1. 优化检索系统质量(使用高精度嵌入模型和检索算法) 2. 同时结合微调,将微调和 RAG 同时使用,兼顾二者的优势