Chapter 08

IA³ 与其他低成本方法:VeRA / LoHa / LoKr / OFT / BOFT

📌 commit a106ff4c7061 当 LoRA 的 2dr 还嫌多时,能再小一个量级的方法

LoRA 的参数已经够少了(约 0.5% base),但部分场景还想更小——多任务时每个任务挂一个、移动端推理减体积、 多 adapter 共存省 GPU 内存。本章讲一组比 LoRA 更"省"的方法。

8.1IA³(最便宜的开关)

IA³(Infused Adapter by Inhibiting and Amplifying Inner Activations)只学三个向量缩放 attention 的 K / V / FFN:

$$h_K = K \odot l_K,\;\; h_V = V \odot l_V,\;\; h_{FFN} = \text{FFN}(x) \odot l_{FFN}$$

每个 $l$ 是 dim-sized vector,参数量极小:

方法典型 trainable%(7B 模型)论文同名
LoRA r=8 ~0.10% LoRA
LoRA r=16 ~0.20% LoRA
IA³ ~0.02% (IA)³
VeRA ~0.03% VeRA

启用 IA³

from peft import IA3Config, get_peft_model, TaskType

cfg = IA3Config(
    task_type=TaskType.CAUSAL_LM,
    target_modules=["k_proj", "v_proj", "down_proj"],
    feedforward_modules=["down_proj"],     # ★ 哪些是 FFN(用乘法门控)
)
model = get_peft_model(model, cfg)

实现:src/peft/tuners/ia3/layer.py::IA3Layer。forward 简化版:

def forward(self, x):
    if self.is_feedforward:
        # FFN:先经过 base,再乘 l
        result = self.base_layer(x) * self.ia3_l.unsqueeze(0)
    else:
        # K / V:先乘 l,再走 base(这样能合并进 base weight)
        x = x * self.ia3_l.unsqueeze(0)
        result = self.base_layer(x)
    return result

IA³ 适用场景

不适合:领域知识增强(容量太小学不到新信息)、数学/代码(需要参数空间大)。

8.2VeRA:共享 A、B,只学缩放 vector

VeRA(Vector-based Random Adaptation)的洞察:LoRA 的 $A, B$ 矩阵其实没必要每层都不同,跨层共享一对随机冻结的 A, B,只学两个小 vector:

$$\Delta W = \Lambda_b\, B\,\Lambda_d\, A,\quad \Lambda_b, \Lambda_d \;\text{是 diag vector}$$

所有 layer 共享同一个 $A, B$(随机初始化后冻结),每层只有 $2d$ 个可训参数。

from peft import VeraConfig

cfg = VeraConfig(
    r=256,            # ★ 比 LoRA 大得多(共享所以可以)
    target_modules=["q_proj", "v_proj"],
    vera_dropout=0.0,
    d_initial=0.1,    # Λ_d 初始化值
)
model = get_peft_model(model, cfg)

实现:src/peft/tuners/vera/。注意 VeRA 因为共享 A, Bsave_pretrained 默认不保存 A, B(推理时按种子重生成);如果需要可设 save_projection=True

8.3LoHa / LoKr:换分解形式

两者都是把 LoRA 的"加性低秩"换成更紧凑的形式:

方法$\Delta W$ 形式参数量
LoRA $BA$(rank-r 矩阵) $2dr$
LoHa $(B_1 A_1) \odot (B_2 A_2)$(Hadamard 乘) $4dr$,但等效秩 $r^2$
LoKr $B \otimes A$(Kronecker 积) $d_1 r + d_2 r$,等效维度 $d_1 d_2$

LoHa 适合需要捕捉非线性交互的任务(如图像生成);LoKr 适合权重矩阵能自然拆分为两个维度(如 attention 的 multi-head × head_dim)。SD/SDXL 微调(Stable Diffusion)的 lycoris 库就是 LoHa/LoKr 思路。

8.4OFT / BOFT / HRA:正交家族

这一系列方法的思路完全不同:不加 $\Delta W$,而是用正交矩阵 $R$ 旋转 base weight

$$W' = R W_0,\;\; R^\top R = I$$

正交保证训练前等价于 base、且不破坏 base 的"几何结构"。三种实现:

方法$R$ 怎么参数化参数量
OFT 块对角正交矩阵 $d \cdot b / 2$(b 是块大小)
BOFT 蝶形分解:$R = R_1 R_2 \cdots R_K$ $d \log_2 d$
HRA Householder 反射:$R = I - 2 v v^\top$(链式)$kd$(k = 反射数)

正交家族在视觉生成模型(Stable Diffusion)上效果好,原因是正交不破坏 base 学到的几何先验。NLP 上不如 LoRA。

8.5各方法实测对照(GLUE 基线)

方法trainable%GLUE典型适用
全参 FT 100% 87.5 有钱有显存
LoRA 0.3% 86.4 通用
DoRA 0.3% 87.0 +1pp,慢 30%
AdaLoRA 0.3% 87.1 +1pp,慢 30%
IA³ 0.02% 83.5 多 adapter / 风格调
VeRA 0.03% 84.1 共享 A,B 的 LoRA
OFT 0.5% 85.8 视觉生成

NLP 的甜点是 LoRA / DoRA / AdaLoRA,IA³ / VeRA 是"我极度计较参数量"时的特例。

8.6给方法做"二选一"的简化决策

你的需求
第一次跑 / 不确定 LoRA r=16, alpha=32
显存紧 QLoRA (LoRA + bnb 4bit)
想再多 1-2 pp + DoRA 或 AdaLoRA
多 adapter 共存(>= 10 个) IA³ 或 VeRA
SD/SDXL 微调 LoHa / LoKr / OFT
论文实验对比 / 探索 剩下的(VeRA / FourierFT / HRA…)

8.7能不能多种方法一起上

同一个 base 上同时挂多种 tuner(如 LoRA + IA³)需要用 MixedModel。看 src/peft/mixed_model.py

from peft import PeftMixedModel, LoraConfig, IA3Config

model = PeftMixedModel(base_model, lora_cfg, adapter_name="lora_a")
model.add_adapter("ia3_a", ia3_cfg)
model.set_adapter(["lora_a", "ia3_a"])     # 两个一起激活

注意 MixedModel 不支持 save_pretrained 的统一接口,需要每个 adapter 分别保存。

8.8近期 LoRA cousins 全表(2024–2026 新增)

本章前面提到 IA³ / VeRA / LoHa / LoKr / OFT。2024 以后还有大量新方法被并入 PEFT,全表覆盖这本书可能用得到的:

方法位置核心数学关键字段typical 用
BOFT tuners/boft/ Butterfly 正交分解 boft_block_size / boft_block_num diffusion ControlNet
HRA tuners/hra/ Householder 反射组合 r(偶数)/ apply_GS OFT 的稳定版
HIRA tuners/hira/ Hadamard 高秩适配 r 需要 expressive 但参数预算紧
FourierFT tuners/fourierft/ DFT 频谱学习 n_frequency / scaling 极小参数(< LoRA 10×)
WaveFT tuners/waveft/ 小波域 FourierFT 升级
ShiRA tuners/shira/ Sparse High-Rank Adaptation r / mask_type / mask_fn 稀疏高秩
RandLora tuners/randlora/ 共享稀疏随机基 + 对角缩放 r / sparse / very_sparse / randlora_alphaVeRA 的稀疏版
DeLoRA tuners/delora/ decorrelated LoRA r 解耦 A B 相关性
GraLoRA tuners/gralora/ Gradient-Regularized LoRA 梯度正则
C3A tuners/c3a/ Channel-wise Circular Convolution block_size 分块循环卷积
Bone tuners/bone/ Block Affine block-diag 仿射变换
Poly tuners/poly/ 多技能路由(Polytropon) r / n_tasks / n_skills / n_splits 多任务共享 + per-task router
Trainable Tokens tuners/trainable_tokens/ 选择性 embedding 训练 token_indices 扩词表只训新 token
Cartridge tuners/cartridge/ 模块化 adapter 系统 多 adapter 路由(实验)

8.9三类"极小参数"方法对比(VeRA / FourierFT / IA³)

这三类是"参数比 LoRA 还少 5-10×" 的代表,但路线完全不同:

方法共享什么每层学什么典型参数比例
VeRA 全局随机 A、B 矩阵(fixed) per-layer 对角缩放 λ_b, λ_d ~0.05% 全模型
FourierFT n_frequency 个频谱系数 LoRA 的 1/10–1/16
IA³ k_proj / v_proj / ffn 各一个缩放向量~0.02%

"什么时候选哪个?"

8.10PromptTuningInit / MultitaskPromptTuningInit 枚举

Prompt 系算法在 ch09 详细讲。本节先把初始化方式枚举放这里,方便选型时一并查:

枚举含义
PromptTuningInit.TEXT 从一段文字初始化虚拟 token;要求 prompt_tuning_init_text + tokenizer_name_or_path
PromptTuningInit.SAMPLE_VOCAB 从词表随机抽 token
PromptTuningInit.RANDOM 纯随机 soft token(可能 off-manifold)
MultitaskPromptTuningInit.TEXT 同上
MultitaskPromptTuningInit.AVERAGE_SOURCE_TASKS 用多个源任务 prompt 的均值
MultitaskPromptTuningInit.EXACT_SOURCE_TASK 复制某个具体源任务的 prompt(用 prompt_tuning_init_task 指定 task ID)
MultitaskPromptTuningInit.ONLY_SOURCE_SHARED 只用源任务共享的 embedding
MultitaskPromptTuningInit.RANDOM 随机

Multitask Prompt Tuning 的额外字段:prompt_tuning_init_state_dict_path(源 checkpoint)+ prompt_tuning_init_task(task ID)+ num_ranks + num_tasks

8.11这章你需要带走的