下午两点半开始学习。QLoRA(Quantized LoRA)是指量化LoRA,其中quantized表示使用4-bit量化。
| |
- 将模型权重从 FP16/BF16 (16-bit) 压缩到 4-bit
- 使用 NF4 (4-bit NormalFloat) 量化,这是专门为神经网络权重设计的量化格式
- 双重量化:连量化参数本身也量化,进一步节省显存 LoRA底秩适配:
| |
常规全量微调:
- 模型权重: 1.5B × 2 bytes = 3GB
- 优化器状态 (Adam): 1.5B × 8 bytes = 12GB
- 梯度: 1.5B × 2 bytes = 3GB
- 总计: ~18GB
QLoRA:
- 量化权重: 1.5B × 0.5 bytes = 0.75GB
- LoRA参数: 8M × 2 bytes = 16MB
- 优化器状态: 8M × 8 bytes = 64MB
- 总计: ~1GB ⬇️ 降低95%
LoRA能够有效微调模型的原因在于低秩假设:微调过程主要在低维子空间中完成。
这让我联想到去年11月Kaiming He发表的论文Back to Basics: Let Denoising Generative Models Denoise,文中写道:“According to the manifold assumption, natural data should lie on a low-dimensional manifold …"。这就是流形假设。流形假设与LoRA的低秩假设有着共同的洞察:虽然参数空间或数据空间是高维的,但"有效信息"却集中在低维子空间中。 这篇文章认为:预测干净图像(x-prediction)比预测噪声(ε/v-prediction)更优,因为在流形假设下,干净图像位于低维流形上,而噪声则分布在高维且"偏离流形"的空间中,当网络容量有限时,预测噪声更容易失败。
我的理解是:预测噪声本质上是预测高维无序的数据,这对同一神经网络而言更为困难。因为神经元的预测能力存在局限性,高维度的信息往往需要更多神经元和更深的网络层才能有效识别。
论证逻辑:
- 流形假设:自然图像是高维像素空间中的低维流形,而噪声则分布在更"饱满"的高维空间中
- 形式化分析:预测空间(x/ε/v)和损失空间(x/ε/v)可独立选择,组合成9种不等价的方案
- 玩具实验验证:当低维数据投影到高维观测空间时,只有x-pred在高维情况下仍能正常工作,而ε/v会遭遇灾难性失败
方法(JiT):直接使用ViT/DiT在像素patch上进行x-pred,无需tokenizer、潜空间、预训练或额外损失函数。
实验结论:
- 在ImageNet 256×256、patch=16的设定下,只有x-pred稳定有效,ε/v会出现灾难性失败
- 在低维设置(64×64、patch=4)时差距不大,说明问题的本质在于高维token的信息传播
- 单纯调整loss权重无法挽救ε/v,而x-pred在不同loss-space下都能正常工作
- 提高噪声水平或加宽网络并非根本解法,甚至在某些瓶颈设计下,x-pred的FID指标更优
这进一步印证了流形假设:虽然像素空间是高维的,但"有效生成信息"集中在低维流形上。x-pred本质上是"把样本从噪声状态拉回流形”,而ε-pred要求网络在高维空间保留噪声信息,对网络容量的要求更为苛刻。
回到LoRA话题:
| |
冻结参数 = Attention层 + FFN层的所有权重矩阵
具体来说,每个Transformer Block包含:
| |
- 反向传播时梯度只流经 B 和 A → 显存占用大幅降低
- 原始权重 W 被冻结 → 无需计算梯度 → 不占用梯度显存
在FFN层中,激活函数采用SwiGLU比GELU、ReLU、SiLU效果更佳:
- 门控机制:
gate * up让网络动态控制信息流动 - 更丰富的非线性:双重投影+乘法增加了表达能力
- Swish/SiLU激活:
x * sigmoid(x),比ReLU平滑,在负区间有输出 LoRA后的FFN前向传播:
| |
关于量化参数,刚开始我对这个概念理解得不够透彻。 实际上,量化并不是直接把权重变成4-bit,而是需要额外的元数据来还原原始值:
| |
量化过程需要保存scale和zero_point这两个量化参数(通常以FP32或FP16存储)。
| |
神经网络对微小扰动具有鲁棒性,同时量化参数的误差会被放大系数所缩小。双重量化是以微小的精度损失换取可观的显存节省,这个权衡在绝大多数场景下都是值得的。
QLoRA原论文(Dettmers et al., 2023)进行了详细实验:
| |
r:秩(rank),推荐范围4-32,表示旁路矩阵的中间维度大小。
以 h = W @ x + (B @ A) @ x 为例:
- W:原始预训练权重矩阵(冻结不动),维度为
[d_out, d_in] - A:下投影矩阵,维度为
[r, d_in] - B:上投影矩阵,维度为
[d_out, r] - r:LoRA秩(rank),是低秩分解的维度
假设原始权重 W 为 [4096, 4096](Qwen的某层维度):A 是 [8, 4096],B 是 [4096, 8],则参数量从原始的 16M → LoRA 仅 65K(减少99.6%)。
- r 越大:模型容量越强,适应能力越好,但参数量相应增加
- r 越小:参数极少,训练速度快,但可能欠拟合
- 经验值:
r=4~16对大多数任务已足够,配置r=8是合理的选择lora_dropout:在B @ A计算后、加回主路径前应用。标准dropout:应用于FFN输出、Attention输出等位置。
target_modules:指定在哪些模块上添加LoRA以节省训练成本,根据模型结构合理设置即可。
warmup_ratio:预热阶段占总训练步数的比例。预热期间学习率变化与主训练阶段不同。
计算示例:
- 总步数 = (数据集大小 / 批次大小) × 训练轮数
- 预热步数 = 总步数 × 0.1
- 预热阶段(0-10%步数):学习率从
0线性增长到2e-4 - 主训练阶段(10%-100%):学习率按余弦衰减至接近0
饭后和朋友一起打《大乱斗》,玩了好几个小时。
随后开始训练,但遇到了环境配置问题,调试了很久。最终放弃了我最习惯的uv pip虚拟环境,改用miniconda,否则会有太多适配问题。
另外我发现,让AI帮忙配置PyTorch时,它总是会自动安装2.5版本,而该版本与5070显卡不兼容。这似乎是因为其训练数据只截止到那个时期,只有明确要求联网搜索才能获得正确的版本信息。
配好环境后,我写了个脚本拉取50个样本,确认模型能够正常运行,然后使用wandb进行远程监控。
最后决定不在本地运行,将任务部署到Colab上,明天继续。已经2点了。