- 一、Adaloar学习
- 1.1 引出问题
- 1.1.1 原始 LoRA 的问题:所有层都用同一个 rank
- 1.1.2 更合理的做法:非均匀分配 rank
- 1.1.3 进一步演进:AdaLoRA
- 1.2 AdaLora的特点
- 1.2.1 AdaLoRA 的核心:总预算不变,重新分配
- 1.2.2 为什么要这样做?
- 1.2.3 真正的关键问题
- 1.3 具体区别展示
- 1.3.1 LoRA 和 AdaLoRA 的核心区别
- 1.3.2 为什么要用 `PΛQ`
- 1.3.3 AdaLoRA 怎么实现动态 rank?
- 1.4 训练流程
- 1.4.1 Instruction Data 怎么训练
- 1.4.2 Loss 怎么来的
- 1.4.3 AdaLoRA 真正作用在哪里
- 1.5 原理分析(一)
- 1.5.1 LoRA → AdaLoRA 的结构变化
- 1.5.2 为什么要加 `Λ`
- 1.5.3 AdaLoRA 怎么实现动态 Rank
- 1.6 原理分析(二)
- 1.6.1 上半部分:Loss 从哪里来
- 1.6.2 下半部分:为什么要引入 SVD
- 1.6.3 SVD 怎么启发 AdaLoRA
- 1.7 Adalora详细解析
- 1.7.1 先算每个 Rank 方向的重要性
- 1.7.2 所有 Rank 做“全局竞争”
- 1.7.3 不重要的方向直接关闭
- 1.8 Adalora的重要性评分计算
- 1.8.1 先给“单个参数”打分
- 1.8.2 再把参数分数聚合成 Rank 分数
- 1.8.3 最后拿去做 Rank 剪枝
- 二、Qlora学习
- 2.1 引出问题
- 2.1.1 为什么 LoRA 之后还需要 QLoRA?
- 2.2.2 QLoRA 的核心思路
- 2.2.3 它主要解决什么?
- 2.2 QLoRA 的直接价值:显存大幅下降
- 2.2.1 传统微调为什么贵
- 2.2.2 QLoRA 怎么把显存降下来
- 2.2.3 效果有多明显
- 2.3 QLoRA 显存优化:从 16-bit Base Model 到 4-bit
- 2.3.1 QLoRA 真正压缩的是 Base Model
- 2.3.2 为什么显存能继续下降?
- 2.3.3 还有一个关键技术:Paged Optimizer
- 2.4 Full Fine-tuning 的显存成本:70B 为什么约 840 GB?
- 2.4.1 每个参数不只要存“权重”
- 2.4.2 70B 模型为什么变成 840 GB?
- 2.4.3 这就是 LoRA / QLoRA 出现的原因
- 2.5 Quantization 基础:Float32 怎么映射成 INT8?
- 2.5.1 核心思路:先缩放,再取整
- 2.5.2 两个最关键的公式
- 2.5.3 量化真正的问题:会产生误差
- 2.6 Quantization 的 Outlier 问题:为什么要做 Block-wise Quantization
- 2.6.1 Global Quantization 的问题
- 2.6.2 Block-wise Quantization 怎么解决
- 2.6.3 为什么又会引出 Double Quantization?
- 2.7 Quantile:为什么“等概率分箱”更适合低比特量化?
- 2.7.1 Quantile 不是“等距离”,而是“等数量”
- 2.7.2 数据越密集,量化就越精细
- 2.7.3 这就是 NF4 的重要铺垫
- 2.8 Normal Distribution + Quantile:NF4 为什么要按概率分配量化区间?
- 2.8.1 神经网络权重通常集中在 0 附近
- 2.8.2 Quantile 改成“等概率划分”
- 2.8.3 这正是 NF4 的核心铺垫
- 2.9 NF4:4-bit 的 16 个量化值到底怎么设计?
- 2.9.1 NF4 不是等距离量化
- 2.9.2 为什么必须保留 Exact Zero?
- 2.9.3 实际怎么量化?
- 三、思维导图总结
一、Adaloar学习
1.1 引出问题
1.1.1 原始 LoRA 的问题:所有层都用同一个 rank
例如统一设成 r = 8。 但不同层、不同矩阵的重要性并不一样。 结果就是:不重要的地方浪费参数,关键位置又不够用。1.1.2 更合理的做法:非均匀分配 rank
例如:
ΔW_k^1 → r = 20 ΔW_q^1 → r = 12 ΔW^2 → r = 2也就是:重要模块多分一点,不重要模块少分一点。
1.1.3 进一步演进:AdaLoRA
在总参数预算基本不变的情况下,动态调整不同模块的秩。 训练过程中把更多容量分给重要方向,压缩不重要方向。一句话总结: 普通 LoRA 是“大家平均分 rank”,AdaLoRA 更像“谁重要谁多拿预算”,让有限参数用在最关键的位置。
1.2 AdaLora的特点
1.2.1 AdaLoRA 的核心:总预算不变,重新分配
- 标准 LoRA:每个矩阵都用同一个
r,例如r = 8。 - AdaLoRA:允许不同矩阵使用不同的 rank。
- 但总预算保持不变,例如:
r₁ + r₂ + ... + r₁₀₀ = 800
1.2.2 为什么要这样做?
- 不同层、不同矩阵对任务的重要性不同。
- 如果平均分配 rank,可能出现:
- 不重要的模块浪费参数
- 重要模块容量不够
- 所以更合理的是:重要模块多分,不重要模块少分。
1.2.3 真正的关键问题
AdaLoRA 最核心的问题其实变成了:
How to measure importance? —— 怎么判断谁更重要?
训练过程中先评估各模块/奇异方向的重要性,再动态剪掉不重要的部分,把预算留给更关键的方向。
一句话总结:
AdaLoRA 本质上是在固定参数预算下做“动态资源调度”:不是增加参数,而是先判断重要性,再把 rank 分到最值得的地方。
1.3 具体区别展示
1.3.1 LoRA 和 AdaLoRA 的核心区别
- LoRA:
ΔW = BA,rankr通常提前固定。 - AdaLoRA:
ΔW = PΛQ,把低秩更新拆成“方向 + 重要程度 + 方向”。 - 其中
Λ的对角元素可以理解为各个低秩方向的重要性“开关”。
1.3.2 为什么要用PΛQ
P、Q负责学习低秩更新的方向,并通过正交约束让这些方向尽量独立。Λ = diag(λ₁, λ₂, …, λᵣ)控制每个 rank-1 方向的贡献。- 如果某个
λᵢ被裁剪为 0,对应方向就基本不再参与更新。
1.3.3 AdaLoRA 怎么实现动态 rank?
P、Q正常做梯度更新。Λ除了更新外,还会结合重要性评分 + 总预算进行裁剪。- 于是不同矩阵最终可以拥有不同的有效 rank,把更多预算留给重要模块。
一句话总结:
LoRA 是“固定 rank 学 BA”,AdaLoRA 是“用 PΛQ 把低秩方向拆开,再动态决定哪些方向留下”。
1.4 训练流程
1.4.1 Instruction Data 怎么训练
- 一条样本由Instruction + Response拼接成 token 序列。
- Decoder 采用自回归方式预测下一个答案 token:
I + O_<t → Ō_t - 训练时使用Teacher Forcing,即输入真实的历史答案 token。
1.4.2 Loss 怎么来的
- 模型对每个
O_t输出整个词表的概率分布。 - 再与真实 token 计算Cross-Entropy Loss:
Ō_t ↔ O_t → CE Loss - 指令微调中通常主要对Response 部分计算 Loss。
1.4.3 AdaLoRA 真正作用在哪里
- AdaLoRA不改变 CE Loss 和 Decoder 的生成方式。
- CE Loss 反向传播得到
P、Λ、Q的梯度。 - 再通过重要性评估 + 预算约束 + Λ 裁剪,动态调整各模块的有效 rank。
一句话总结:
这一页主要讲“梯度从哪里来”:Instruction → Decoder → CE Loss → Gradient;AdaLoRA 的创新发生在拿到梯度之后的动态 Rank 分配。
1.5 原理分析(一)
1.5.1 LoRA → AdaLoRA 的结构变化
- LoRA:
ΔW = BA,rankr一般提前固定。 - AdaLoRA:
ΔW = PΛQ,把低秩更新拆成“方向 + 重要程度 + 方向”。
1.5.2 为什么要加Λ
Λ = diag(λ₁, …, λᵣ)控制每个 rank-1 方向的贡献。P、Q通过正交约束尽量保持不同方向独立。- 某个
λᵢ = 0,就相当于关闭对应的低秩方向。
1.5.3 AdaLoRA 怎么实现动态 Rank
P、Q正常梯度更新。Λ还会结合重要性评分 + Budget做裁剪。- 最终不同权重矩阵可以获得不同的有效 rank,把更多参数留给重要模块。
一句话总结:
LoRA 是“固定 rank 学 BA”,AdaLoRA 是“用 PΛQ 把 rank 方向拆开,再按重要性决定哪些方向留下”。
1.6 原理分析(二)
1.6.1 上半部分:Loss 从哪里来
Instruction + Response送入 Decoder。- 模型做自回归预测,并与真实
O_t计算Cross-Entropy Loss。 - 这个 Loss 反向传播后,才会得到 AdaLoRA 需要的梯度。
1.6.2 下半部分:为什么要引入 SVD
- SVD 把矩阵写成:
A = UΣVᵀ。 - 每个
σ_i u_i v_iᵀ都可以看成一个rank-1 方向。 σ_i越大,通常说明这个方向越重要,因此可以通过保留大奇异值、舍弃小奇异值来做低秩近似。
1.6.3 SVD 怎么启发 AdaLoRA
- SVD:
A = UΣVᵀ - AdaLoRA:
ΔW = PΛQ - 可以对应理解为:
U ↔ P,Σ ↔ Λ,Vᵀ ↔ Q。AdaLoRA 再结合重要性评分,把不重要方向的λ_i置零,从而动态调整有效 Rank。
一句话总结:
这页真正是在解释:SVD 先告诉我们“不同 rank 方向的重要性不同”,AdaLoRA 再把这个思想变成PΛQ,让每个低秩方向都能被单独评估、保留或裁掉。
1.7 Adalora详细解析
1.7.1 先算每个 Rank 方向的重要性
- 第
k个矩阵写成:ΔW_k = P_kΛ_kQ_k。 Λ_k中每个λ_{k,i}对应一个 Rank-1 方向。- AdaLoRA 会为每个方向计算重要性分数
S_{k,i}。
1.7.2 所有 Rank 做“全局竞争”
- 不是每个矩阵各自保留固定数量。
- 而是把整个模型所有
S_{k,i}放在一起排序。 - 当前预算为
b^(t)时,只保留Global Top-b^(t)。
1.7.3 不重要的方向直接关闭
- 进入 Top-
b^(t):对应λ_{k,i}保留。 - 没进入:
λ_{k,i} → 0,该 Rank 方向失效。 - 因此不同矩阵最终会得到不同的有效 Rank。
一句话总结:
AdaLoRA 的动态 Rank 分配就是:先算重要性 → 全局排序 → 按预算保留 Top-b → 其余 λ 置零,让所有层共同竞争有限的 Rank Budget。
1.8 Adalora的重要性评分计算
1.8.1 先给“单个参数”打分
- AdaLoRA 先计算参数敏感度:
I(w)=|w·∇_wL|。 - 再用 EMA 得到平滑敏感度
Ī(w),同时计算波动程度Ū(w)。 - 最终得到单参数重要性:
s(w)=Ī(w)·Ū(w)。
1.8.2 再把参数分数聚合成 Rank 分数
- 一个 Rank 方向由三部分组成:
P的第 i 列 +λ_{k,i}+Q的第 i 行。 - 所以 AdaLoRA 不只看
|λ|,而是把这三部分的重要性一起综合。 - 得到:
S_{k,i}=s(λ)+Avg[s(P列)]+Avg[s(Q行)]。
1.8.3 最后拿去做 Rank 剪枝
S_{k,i}越大,说明这个 Rank 方向越重要。- 后续把所有
S_{k,i}做全局排序。 - 进入Top-b^(t)的方向保留,其余对应
λ_{k,i}→0。
一句话总结:
AdaLoRA 的 Importance Score 就是:先给参数打分,再把参数的重要性聚合成整个 Rank 方向的分数,最后用于全局 Budget 排序与剪枝。
二、Qlora学习
2.1 引出问题
2.1.1 为什么 LoRA 之后还需要 QLoRA?
- LoRA已经大幅减少了“需要训练的参数”。
- 但冻结的Base Model 仍然要加载进显存。
- 模型越大,光保存基础权重就已经非常昂贵。
2.2.2 QLoRA 的核心思路
- LoRA:高精度 Base Model + 可训练 LoRA
- QLoRA:4-bit 量化 Base Model + 可训练 LoRA
- 也就是:Base Model 不训练,就进一步把它压缩存储。
2.2.3 它主要解决什么?
- Quantization:降低基础模型的显存占用。
- LoRA:减少真正需要更新的参数量。
- 两者结合后,让大模型微调更容易在有限 GPU 显存下完成。
一句话总结:
LoRA 解决“训练参数太多”,QLoRA 进一步解决“冻结的大模型本身还是太占显存”,核心就是4-bit Quantization + LoRA。
2.2 QLoRA 的直接价值:显存大幅下降
2.2.1 传统微调为什么贵
- 全参数微调不仅要存模型权重,还要保存梯度、优化器状态和激活值。
- 模型越大,显存需求增长非常快。
- 课件示例中,LLaMA2-70B 的传统微调显存达到840 GB。
2.2.2 QLoRA 怎么把显存降下来
- Base Model:4-bit 量化 + 冻结
- LoRA Adapter:保持可训练
- 这样既减少基础模型的存储,又避免为全部参数保存完整训练状态。
2.2.3 效果有多明显
- T5-11B:132 GB → 6 GB
- Mistral-7B:84 GB → 5 GB
- LLaMA2-70B:840 GB → 46 GB
一句话总结:
LoRA 是“少训练参数”,QLoRA 更进一步是“冻结的大模型也少占显存”,最终实现4-bit Quantization + LoRA → 更低显存的大模型微调。
2.3 QLoRA 显存优化:从 16-bit Base Model 到 4-bit
2.3.1 QLoRA 真正压缩的是 Base Model
- 普通 LoRA 仍然保存16-bit Frozen Base Model。
- QLoRA 把它进一步量化成4-bit,而 LoRA Adapter 继续保持可训练。
- 所以核心变化就是:16-bit → 4-bit,Base Model 的存储开销大幅下降。
2.3.2 为什么显存能继续下降?
- LoRA 已经减少了梯度和优化器状态的开销。
- QLoRA 再把最大的显存来源——冻结的基础模型权重压缩。
- 课件示例中,70B 模型从 LoRA 的约154 GB降到 QLoRA 的约46 GB。
2.3.3 还有一个关键技术:Paged Optimizer
- QLoRA 还通过CPU ↔ GPU 分页调度缓解训练时的显存峰值。
- 它主要解决Memory Spike / OOM,而不是负责 16-bit → 4-bit 的主体压缩。
- 因此可以记成:4-bit Quantization + LoRA + Paged Optimizer。
一句话总结:
QLoRA 就是在 LoRA 的基础上,把冻结的 Base Model 从16-bit 压到 4-bit,再配合分页优化器进一步降低显存门槛。
2.4 Full Fine-tuning 的显存成本:70B 为什么约 840 GB?
2.4.1 每个参数不只要存“权重”
按课件的简化估算,全参数微调需要同时保存:
- Weight:16 bit
- Gradient:16 bit
- Adam Optimizer States:64 bit
所以每个参数大约需要:
16 + 16 + 64 = 96 bit = 12 Byte2.4.2 70B 模型为什么变成 840 GB?
直接计算:
70B × 12 Byte ≈ 840 GB其中大约可以拆成:
- 权重:140 GB
- 梯度:140 GB
- 优化器状态:560 GB
这还没算 Activation、临时缓存等额外开销。
2.4.3 这就是 LoRA / QLoRA 出现的原因
- Full FT:整个模型都训练,显存最高。
- LoRA:冻结 Base Model,只训练少量 Adapter。
- QLoRA:进一步把冻结的 Base Model 量化到 4-bit。
一句话总结:
Full Fine-tuning 真正贵的地方,不只是模型权重大,而是每个可训练参数还要额外配套梯度和优化器状态,所以 70B 模型的训练状态可以迅速膨胀到数百 GB。
2.5 Quantization 基础:Float32 怎么映射成 INT8?
2.5.1 核心思路:先缩放,再取整
- 原始 Float32 是连续小数,INT8 只能表示有限整数。
- 所以先找最大绝对值
a_max = max|a|,把数据缩放到[-1,1]。 - 再映射到
[-127,127],最后Round成整数。
2.5.2 两个最关键的公式
- Scale:
s = max|a| / 127 - Quantization:
q = round(a / s) - 需要恢复时,再做:
â = s · q
例如:(-2.7,-3.8,5.7,6.1,6.8) → (-50,-71,106,114,127)
2.5.3 量化真正的问题:会产生误差
- Round 会丢掉一部分小数信息,所以只能近似恢复原值。
- AbsMax 量化还容易受到Outlier影响,一个极端值可能让大量普通权重挤在 0 附近。
- 这正好引出后面的NF4:当只有 4-bit、16 个表示位置时,怎样安排这些位置才能让误差更小?
一句话总结:
量化就是把连续浮点数通过Scale → 映射 → Round变成低比特整数,用更少存储换取可接受的近似误差。
2.6 Quantization 的 Outlier 问题:为什么要做 Block-wise Quantization
2.6.1 Global Quantization 的问题
- 全局量化时,整个 Tensor 共用一个
absmax和 Scale。 - 如果出现很大的Outlier,例如
50,其他正常值会被压到 0 附近。 - 在 4-bit 这类低比特量化中,很多正常权重甚至可能被映射成相同的离散值,导致明显信息损失。
2.6.2 Block-wise Quantization 怎么解决
- 把大权重 Tensor 切成多个小 Block。
- 每个 Block 单独计算自己的
absmax和量化常数C_i。 - 这样 Outlier只影响它所在的 Block,不会把整个模型的量化范围都拉大。
2.6.3 为什么又会引出 Double Quantization?
- Block 越多,就需要保存越多的
C_1、C_2、C_3...。 - 这些 Scale / Quantization Constants 本身也会占显存。
- 所以 QLoRA 再进一步:把这些
C_i也量化,这就是 Double Quantization。
一句话总结:
Outlier 会把全局量化范围拉大 → Block-wise 用局部 Scale 把影响限制在小范围 → 但会产生大量 Scale → Double Quantization 再继续压缩这些 Scale。
2.7 Quantile:为什么“等概率分箱”更适合低比特量化?
2.7.1 Quantile 不是“等距离”,而是“等数量”
- 先把数据从小到大排序。
- 再按照概率划分区间,例如 800 个数据分成 8 组,每组约100 个。
- 所以每组的数据数量接近相同,但数值区间宽度可以不同。
2.7.2 数据越密集,量化就越精细
- 数据集中区域:区间会更窄,量化点更密。
- 数据稀疏区域:区间会更宽,量化点更稀。
- 相比等距离分箱,可以把有限的 bit更多用在真正数据多的地方。
2.7.3 这就是 NF4 的重要铺垫
- 4-bit 只有16 个离散表示位置。
- NF4 利用近似正态分布的 Quantile 思想来设计这些量化位置。
- 核心目的就是:让常见权重区域获得更高表示精度,减少量化误差。
一句话总结:
Uniform Quantization 是“按距离平均分”,Quantile 是“按概率平均分”;对于分布不均匀的模型权重,后者通常能更有效利用有限的量化位置。
2.8 Normal Distribution + Quantile:NF4 为什么要按概率分配量化区间?
2.8.1 神经网络权重通常集中在 0 附近
- 可以近似理解为Normal Distribution。
- 中间权重多,两侧大权重少。
- 所以如果仍然“等距离切区间”,有限的量化位置会被浪费。
2.8.2 Quantile 改成“等概率划分”
- 利用 CDF:
F(x)=P(X≤x)。 - 再通过
q_p=F⁻¹(p)找到不同分位点。 - 结果就是:中心数据密集 → 区间更窄;两侧数据稀疏 → 区间更宽。
2.8.3 这正是 NF4 的核心铺垫
- 4-bit 只有16 个表示位置。
- NF4 根据权重的概率分布设计非均匀量化位置,而不是简单平均铺开。
- 目的就是把更多量化精度留给最常出现的权重区域。
一句话总结:
Normal Distribution 决定“数据主要在哪”,Quantile 决定“量化点该怎么放”,两者结合就是 NF4 非均匀 4-bit 量化的重要直觉。
2.9 NF4:4-bit 的 16 个量化值到底怎么设计?
2.9.1 NF4 不是等距离量化
- 4-bit 只有16 个离散表示值。
- NF4 根据模型权重近似Normal Distribution的特点,用Quantile来安排这些量化位置。
- 因此:0 附近更密,两侧更疏,比普通 INT4 更适合权重分布。
2.9.2 为什么必须保留 Exact Zero?
- 神经网络中
0很重要,最好能够被精确表示。 - 所以 NF4 会专门给
0留一个编码位置。 - 最终 16 个值呈轻微不对称,可直观理解为:7 个负值 + 0 + 8 个正值。
2.9.3 实际怎么量化?
- 每个 Block 先通过
absmax归一化到[-1,1]。 - 再把每个权重映射到最近的 NF4 codebook 值。
- 最终真正保存的是对应的4-bit index,反量化时再乘回 Block 的 Scale。
一句话总结:
NF4 的核心不是“只有 4 bit”,而是这 16 个值按照正态分布的概率结构来设计,并保留精确的 0,从而用有限的 4-bit 表示尽可能减少量化误差。