模型微调 和 RAG 到底有什么区别? |
这两个东西看起来都能让大模型回答得更贴近业务.
但它们解决的问题其实不一样.
我之前很容易把它们都理解成:
让模型懂我的项目 |
但这样理解太粗了.
更准确一点应该是:
微调 = 让模型学会某种能力、格式、风格或领域表达 |
RAG = 让模型回答前去查外部资料 |
一句话总结:
微调改变模型本身. |
RAG不改模型,而是给模型临时补资料. |
这篇文章就用同一个问题来看:
tsu_engine 的战斗监控接口怎么部署? |
如果用微调,它会怎么处理?
如果用 RAG,它又会怎么处理?
0.背景:为什么会拿微调和RAG比较
先看一个真实一点的场景.
我现在有一个后端项目,里面有一些部署文档:
Go 后端服务 |
Docker Compose |
PostgreSQL |
战斗监控接口 |
Nginx 反向代理 |
用户可能会问:
tsu_engine 的战斗监控接口怎么部署? |
这个问题对普通大模型来说就比较尴尬.
因为它不知道我的本地项目.
它也不知道:
1. 服务端口是多少 |
2. Docker Compose 文件叫什么 |
3. PostgreSQL 怎么配置 |
4. 健康检查接口是什么 |
5. 战斗监控接口怎么验证 |
那么要让模型回答这个问题,大概有两种思路:
1. 把这些知识训练进模型 |
2. 回答前先去文档里查这些知识 |
第一种就是微调.
第二种就是 RAG.
它们看起来都能解决"模型不知道"的问题.
但底层路线完全不一样.
1.先定义同一个问题
为了方便比较,我们固定一个问题:
tsu_engine 的战斗监控接口怎么部署? |
这个问题需要的信息可能包括:
1. 需要启动 api 和 postgres |
2. 使用 docker compose 启动 |
3. API 端口是 18088 |
4. 健康检查接口是 /api/health |
5. 监控接口是 /api/battle-monitor/status |
6. 请求监控接口要带 Authorization token |
7. PostgreSQL 用来保存战斗监控数据 |
如果回答得更完整一点,还要说明:
1. Dockerfile 里 Go 版本要和 go.mod 匹配 |
2. Docker Hub 可能需要镜像源 |
3. Go 依赖下载可能需要 GOPROXY |
4. 生产环境不能用本地默认 token |
5. PostgreSQL 不应该直接暴露到公网 |
也就是说,这不是一个纯通用问题.
它明显依赖项目文档.
下面我们分别看微调和 RAG 会怎么处理.
2.微调是怎么处理这个问题的
微调的思路是:
准备训练数据 |
-> 把问题和标准答案交给模型训练 |
-> 模型参数发生更新 |
-> 以后遇到类似问题,按训练中学到的方式回答 |
比如我们准备一条训练样本:
{ |
"question": "tsu_engine 的战斗监控接口怎么部署?", |
"answer": "先通过 Docker Compose 启动 api 和 postgres,然后访问 /api/health 验证后端在线,再带 Authorization token 请求 /api/battle-monitor/status 验证战斗监控状态..." |
} |
只准备一条肯定不够.
实际微调需要很多类似样本.
比如:
怎么部署战斗监控? |
tsu_engine 本地 Docker 怎么启动? |
PostgreSQL 监控库怎么配置? |
怎么验证 /api/battle-monitor/status? |
生产环境 token 怎么设置? |
每个问题都配一个标准答案.
模型训练以后,会学到几类东西:
1. 这类问题应该怎么回答 |
2. 回答应该按什么格式组织 |
3. 哪些术语经常一起出现 |
4. 面对部署问题时先讲启动,再讲验证,最后讲注意事项 |
也就是说,微调更像是在训练模型的"习惯".
它让模型更像一个已经学过这些问答范式的助手.
2.1 微调回答时发生了什么
当用户再次问:
tsu_engine 的战斗监控接口怎么部署? |
微调后的模型不会去查文档.
它直接根据参数里学到的东西生成答案.
流程大概是:
用户问题 |
-> 微调后的模型 |
-> 根据训练中学到的模式生成回答 |
这里没有检索.
也没有实时读取最新文档.
答案来自模型参数.
2.2 微调的好处
微调的优势是:
1. 可以让模型稳定输出某种格式 |
2. 可以让模型更熟悉某个领域的表达 |
3. 可以让模型学会特定任务 |
4. 可以减少每次 prompt 里重复写规则 |
比如客服机器人,你希望它永远用固定语气回答.
或者代码助手,你希望它总是按某种模板输出.
这种就适合微调.
2.3 微调的问题
但微调有一个很大的问题:
知识更新不方便. |
假设部署方式改了.
比如:
端口从 18088 改成 19088 |
接口从 /api/battle-monitor/status 改成 /api/monitor/status |
token 环境变量改名了 |
PostgreSQL 配置改了 |
微调后的模型不会自动知道.
因为它回答时没有去读最新文档.
除非你重新整理训练数据,再微调一次.
所以微调不适合频繁变化的项目知识.
3.RAG是怎么处理这个问题的
RAG 的思路完全不一样.
它不要求模型提前记住tsu_engine.
它的做法是:
用户提问 |
-> 把问题变成向量 |
-> 去知识库检索相关文档 |
-> 找到 Docker 部署、PostgreSQL、战斗监控接口相关片段 |
-> 把这些片段交给大模型 |
-> 大模型基于片段回答 |
-> 返回答案和来源 |
也就是说,RAG 不是把知识训练进模型.
而是让模型回答前先查资料.
3.1 RAG会检索到什么
还是这个问题:
tsu_engine 的战斗监控接口怎么部署? |
RAG 可能会检索到这些片段: