76-全量微调vs-LoRA-vs-QLoRA-三种微调方式对比与选型

文章目录

  • [【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

思考 && 总结

  1. 三种方案的本质区别是参数更新范围: 全量改100%,LoRA改1%,QLoRA在LoRA基础上把冻结部分再压成4-bit。
  2. LoRA效果能到全量的95%+,但成本只有零头: 这是它能成为微调事实标准的根本原因。
  3. QLoRA的意义是民主化: 24GB消费级显卡微调7B模型,在它出现之前是不可想象的。
  4. r=16、alpha=32、注入注意力层是不会错的起点: 先跑通,再根据loss曲线调参。
  5. 微调产物只是几十MB的适配器: 这意味着一个基座模型可以挂无数个任务适配器------这是LoRA生态最大的架构红利。

选型口诀:有卡用LoRA,卡不够用QLoRA,非有充分理由别全量。


结尾

各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!

源码骑士 --- Android Framework & 全栈开发

👀 关注:跟博主一起从源码视角深耕底层原理,见证每一次成长

❤️ 点赞:让优质内容被更多人看见,让知识传递更有力量

收藏:把核心知识点存好,在需要时随时查、随时用

💬 评论:分享你的经验或疑问,评论区一起交流避坑

🔄 一键四连:不要忘记给博主"一键四连"哦!

🗡️ 寄语:技术之路难免有困惑,但同行的人会让前进更有方向

结语:LoRA的出现让微调从"大厂专利"变成了"人人可为"。理解了低秩分解的直觉,你就理解了为什么0.8%的参数能撬动整个模型的行为。下一篇我们讲微调真正的灵魂------数据集。不要忘记给博主"一键四连"哦!

相关推荐
光头闪亮亮1 小时前
Fyne ( go跨平台GUI )项目实战-WebView 组件开发技术详解
android·go
嵌入式小周2 小时前
Genymotion 安卓模拟器在 Intel 芯片 Mac 上的运行(附带下载方式)
android·macos
zzq77974 小时前
Android 16 API 36 升级后 APP 加固兼容性问题解析
android·开发语言·安全·kotlin·安卓·安全架构
2601_961391465 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·人工智能
2501_9159184113 小时前
深入对比iOS开发中常用性能监控工具的底层原理与优缺点分析
android·ios·小程序·https·uni-app·iphone·webview
my_power52015 小时前
android中Activity生命周期函数的职责
android
Sirens.15 小时前
从参考 iCost 到做自己的 OneLedger:一个 Android 本地记账 App 的开发记录
android·kotlin·room·jetpack compose·记账 app
qq_4480111615 小时前
C语言中的变量和函数的定义与声明
android·c语言·开发语言
码农coding19 小时前
android 12 中的VSYNC的请求
android