文章目录
- [【76.Python+AI】全量微调 vs LoRA vs QLoRA:一张表看懂三种微调方式的区别](#【76.Python+AI】全量微调 vs LoRA vs QLoRA:一张表看懂三种微调方式的区别)
-
- 导入语
- [1 ~> 三种方式的结构对比](#1 ~> 三种方式的结构对比)
-
- [1.1 LoRA的数学直觉](#1.1 LoRA的数学直觉)
- [2 ~> 硬核数据对比](#2 ~> 硬核数据对比)
-
- [2.1 以7B模型为例](#2.1 以7B模型为例)
- [2.2 关键认知:LoRA的效果损失远小于想象](#2.2 关键认知:LoRA的效果损失远小于想象)
- [3 ~> QLoRA多做了什么](#3 ~> QLoRA多做了什么)
- [4 ~> LoRA最小代码示例](#4 ~> LoRA最小代码示例)
-
- [4.1 三个关键超参数怎么选](#4.1 三个关键超参数怎么选)
- [5 ~> 选型速查](#5 ~> 选型速查)
- [思考 && 总结](#思考 && 总结)
- 结尾
【76.Python+AI】全量微调 vs LoRA vs QLoRA:一张表看懂三种微调方式的区别
📖 文章简介: 本文系统对比三种主流大模型微调方式:全量微调(Full Fine-Tuning)、LoRA(Low-Rank Adaptation)和QLoRA(量化LoRA)。文章从显存消耗(7B模型从120GB到10GB的跨越)、训练速度、最终效果和硬件门槛四个维度做硬核对比,深入讲解LoRA"冻结主干+只训旁路低秩矩阵"的数学直觉,以及QLoRA在其上叠加4-bit量化的实现逻辑。文中给出PEFT库的LoRA最小代码示例和不同显存条件下的选型速查表,配以Mermaid结构图展示三种方式在参数更新范围上的本质差异,适合准备动手微调第一个模型、需要确定技术路线的开发者。

🎬 个人主页: 源码骑士
❄ 专栏传送门: 《Android开发基础》《python基础课程》
⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂
🎬 源码骑士的简介:
5年Android Framework系统开发经验,曾主导多项系统级性能优化专项
技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)
累计产出原创技术文章100+篇,文章以流程图为特色,被读者评价为"看一篇胜过啃一周源码"
导入语
你决定微调一个7B模型,兴冲冲打开文档,迎面就是三个名词:全量微调、LoRA、QLoRA。文档告诉你QLoRA最省显存,但没告诉你它牺牲了什么;告诉你全量微调效果最好,但没告诉你需要几张A100。
这三个方案的差异,本质上是**"你愿意改动模型多少参数"**的权衡。这篇文章就把这笔账算清楚------看完你能对着自己的显卡型号,直接选出该用的方案。
1 ~> 三种方式的结构对比
#mermaid-svg-TkWi38R26iFQhjlV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TkWi38R26iFQhjlV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TkWi38R26iFQhjlV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TkWi38R26iFQhjlV .error-icon{fill:#552222;}#mermaid-svg-TkWi38R26iFQhjlV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TkWi38R26iFQhjlV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TkWi38R26iFQhjlV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TkWi38R26iFQhjlV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TkWi38R26iFQhjlV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TkWi38R26iFQhjlV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TkWi38R26iFQhjlV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TkWi38R26iFQhjlV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TkWi38R26iFQhjlV .marker.cross{stroke:#333333;}#mermaid-svg-TkWi38R26iFQhjlV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TkWi38R26iFQhjlV p{margin:0;}#mermaid-svg-TkWi38R26iFQhjlV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TkWi38R26iFQhjlV .cluster-label text{fill:#333;}#mermaid-svg-TkWi38R26iFQhjlV .cluster-label span{color:#333;}#mermaid-svg-TkWi38R26iFQhjlV .cluster-label span p{background-color:transparent;}#mermaid-svg-TkWi38R26iFQhjlV .label text,#mermaid-svg-TkWi38R26iFQhjlV span{fill:#333;color:#333;}#mermaid-svg-TkWi38R26iFQhjlV .node rect,#mermaid-svg-TkWi38R26iFQhjlV .node circle,#mermaid-svg-TkWi38R26iFQhjlV .node ellipse,#mermaid-svg-TkWi38R26iFQhjlV .node polygon,#mermaid-svg-TkWi38R26iFQhjlV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TkWi38R26iFQhjlV .rough-node .label text,#mermaid-svg-TkWi38R26iFQhjlV .node .label text,#mermaid-svg-TkWi38R26iFQhjlV .image-shape .label,#mermaid-svg-TkWi38R26iFQhjlV .icon-shape .label{text-anchor:middle;}#mermaid-svg-TkWi38R26iFQhjlV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TkWi38R26iFQhjlV .rough-node .label,#mermaid-svg-TkWi38R26iFQhjlV .node .label,#mermaid-svg-TkWi38R26iFQhjlV .image-shape .label,#mermaid-svg-TkWi38R26iFQhjlV .icon-shape .label{text-align:center;}#mermaid-svg-TkWi38R26iFQhjlV .node.clickable{cursor:pointer;}#mermaid-svg-TkWi38R26iFQhjlV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TkWi38R26iFQhjlV .arrowheadPath{fill:#333333;}#mermaid-svg-TkWi38R26iFQhjlV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TkWi38R26iFQhjlV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TkWi38R26iFQhjlV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TkWi38R26iFQhjlV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TkWi38R26iFQhjlV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TkWi38R26iFQhjlV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TkWi38R26iFQhjlV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TkWi38R26iFQhjlV .cluster text{fill:#333;}#mermaid-svg-TkWi38R26iFQhjlV .cluster span{color:#333;}#mermaid-svg-TkWi38R26iFQhjlV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TkWi38R26iFQhjlV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TkWi38R26iFQhjlV rect.text{fill:none;stroke-width:0;}#mermaid-svg-TkWi38R26iFQhjlV .icon-shape,#mermaid-svg-TkWi38R26iFQhjlV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TkWi38R26iFQhjlV .icon-shape p,#mermaid-svg-TkWi38R26iFQhjlV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TkWi38R26iFQhjlV .icon-shape .label rect,#mermaid-svg-TkWi38R26iFQhjlV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TkWi38R26iFQhjlV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TkWi38R26iFQhjlV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TkWi38R26iFQhjlV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} QLoRA
输入
冻结主干
但压缩为4-bit
显存再降4倍
LoRA旁路
16-bit训练
+
输出
LoRA
输入
冻结的主干权重 W
不更新
LoRA旁路
A×B低秩矩阵
只训这几百万参数
+
输出
全量微调
输入
模型所有层
全部参数都更新
~70亿参数
输出
1.1 LoRA的数学直觉
全量微调是学一个完整的权重增量 ΔW(尺寸和原矩阵一样大)。LoRA的洞察是:这个ΔW其实是"低秩"的------它包含的有效变化远小于矩阵尺寸。于是把ΔW分解为两个小矩阵的乘积:
bash
原矩阵 W: 4096 × 4096 = 1677万参数
全量微调的 ΔW: 4096 × 4096 = 1677万参数都要训
LoRA的分解: ΔW = A × B
A: 4096 × 16 = 6.5万参数
B: 16 × 4096 = 6.5万参数
合计: 13万参数(仅为全量的0.8%)
那个"16"就是秩r------LoRA的核心超参数,通常取8~64。
2 ~> 硬核数据对比
2.1 以7B模型为例
| 维度 | 全量微调 | LoRA | QLoRA |
|---|---|---|---|
| 训练显存 | ~120GB(需2×A100 80G) | ~30GB(1×A100或4090) | ~10GB(1×3090即可) |
| 可训练参数 | 100%(70亿) | ~1%(7000万) | ~1%(7000万) |
| 训练速度 | 基准 | 快2~3倍 | 略慢于LoRA(量化开销) |
| 最终效果 | 上限最高 | 达到全量的95~98% | 达到LoRA的95%左右 |
| 权重产物 | 整个新模型(14GB) | 仅适配器(几十MB) | 仅适配器(几十MB) |
| 多任务切换 | 需保存多份模型 | 换适配器即可 | 换适配器即可 |
2.2 关键认知:LoRA的效果损失远小于想象
社区大量实测表明:在指令微调场景,r=16的LoRA效果能达到全量微调的95%以上。原因是指令微调只需要模型"学会一种行为模式",这种调整本质上是低秩的------正好撞在LoRA的假设上。
什么情况下全量微调不可替代?当你要给模型注入全新的知识领域(比如让一个通用模型学会高度专业的医学推理),改动量大到不是低秩矩阵能表达的,这时候才需要全量微调。
3 ~> QLoRA多做了什么
QLoRA = LoRA + 对冻结主干做4-bit量化。训练时主干以NF4格式存储,前向计算时临时解压回16-bit参与运算:
bash
QLoRA的三板斧:
1. NF4量化:正态分布数据的最优4-bit编码
→ 主干权重显存从 14GB 压到 ~3.5GB
2. 双重量化:连量化常数本身也再量化一次
→ 再省 0.37 bit/参数
3. 分页优化器:显存尖峰时把优化器状态卸载到内存
→ 防止训练中途OOM
代价是:量化带来微小精度损失 + 解压带来少量计算开销。但对"单卡微调7B"这个场景,这点代价换来的是从"不可能"到"可能"。
4 ~> LoRA最小代码示例
python
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer
# 1. 加载基础模型
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2-7B-Instruct",
load_in_4bit=True, # QLoRA:4-bit加载;纯LoRA则去掉这行
device_map="auto"
)
# 2. 配置LoRA
lora_config = LoraConfig(
r=16, # 秩:8/16/32常用,越大能力越强参数越多
lora_alpha=32, # 缩放系数,通常设为r的2倍
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 注入到注意力层
lora_dropout=0.05,
task_type="CAUSAL_LM"
)
# 3. 包装模型------只有LoRA参数可训练
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出: trainable params: 13,631,488 || all params: 7,628,904,448 || trainable%: 0.18%
# 4. 正常走Trainer训练流程(与全量微调代码完全一致)
trainer = Trainer(model=model, args=training_args, train_dataset=dataset)
trainer.train()
# 5. 保存------只保存LoRA适配器(几十MB)
model.save_pretrained("./my_lora_adapter")
4.1 三个关键超参数怎么选
| 参数 | 推荐起点 | 调整逻辑 |
|---|---|---|
r(秩) |
16 | 任务简单(格式化输出)→8;任务复杂(领域推理)→32~64 |
lora_alpha |
32 | 一般设为r的2倍,控制LoRA影响的缩放 |
target_modules |
注意力层4个投影 | 数据量大可加gate_proj等MLP层,能力更强但显存增加 |
5 ~> 选型速查
bash
按你的显卡选:
├─ 消费级显卡(3090/4090,24GB)
│ └─ QLoRA微调7B ------ 唯一可行方案
│
├─ 单卡A100 40GB
│ └─ LoRA微调7B / QLoRA微调13B
│
├─ 多卡A100 80GB
│ └─ LoRA微调13B~70B,或全量微调7B(有充足理由时)
│
└─ 没有显卡
└─ 用云算力(AutoDL/趋动云),或LLaMA-Factory云端镜像
按任务性质复核:
├─ 学话术/格式/风格 → LoRA足够
├─ 学领域问答模式 → LoRA(r=32+)
└─ 注入全新知识体系 → 考虑全量微调,或改用RAG
思考 && 总结
- 三种方案的本质区别是参数更新范围: 全量改100%,LoRA改1%,QLoRA在LoRA基础上把冻结部分再压成4-bit。
- LoRA效果能到全量的95%+,但成本只有零头: 这是它能成为微调事实标准的根本原因。
- QLoRA的意义是民主化: 24GB消费级显卡微调7B模型,在它出现之前是不可想象的。
- r=16、alpha=32、注入注意力层是不会错的起点: 先跑通,再根据loss曲线调参。
- 微调产物只是几十MB的适配器: 这意味着一个基座模型可以挂无数个任务适配器------这是LoRA生态最大的架构红利。
选型口诀:有卡用LoRA,卡不够用QLoRA,非有充分理由别全量。
结尾
各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!
源码骑士 --- Android Framework & 全栈开发
👀 关注:跟博主一起从源码视角深耕底层原理,见证每一次成长
❤️ 点赞:让优质内容被更多人看见,让知识传递更有力量
⭐ 收藏:把核心知识点存好,在需要时随时查、随时用
💬 评论:分享你的经验或疑问,评论区一起交流避坑
🔄 一键四连:不要忘记给博主"一键四连"哦!
🗡️ 寄语:技术之路难免有困惑,但同行的人会让前进更有方向
结语:LoRA的出现让微调从"大厂专利"变成了"人人可为"。理解了低秩分解的直觉,你就理解了为什么0.8%的参数能撬动整个模型的行为。下一篇我们讲微调真正的灵魂------数据集。不要忘记给博主"一键四连"哦!