遷移學習上的一個老問題,怎么做多領域的遷移?以前的邏輯認為領域遷移屬于是對參數做方向性的調整,如果兩個領域方向相左,實際上不管怎么加權相加都是不合理的。
目前一些做法想著去觀察LoRA權重矩陣中的稠密塊與稀疏塊,選擇稠密的部分予以保留,稀疏的部分予以舍棄,然后有選擇地進行合并,這是一種很模糊的做法,實際上也缺乏理論保證,而且,低秩與稀疏通常并不等價。
假定是做秩一分解:
h 1 = W 0 x + ( B 1 A 1 ) x = W 0 x + ( ∑ i = 1 r 1 d 1 i v i v i ? ) x = W 0 x + ∑ i = 1 r 1 ( d 1 i v i ? x ) v i = W 0 x + ∑ i = 1 r 1 k 1 i v i h 2 = W 0 x + ( B 2 A 2 ) x = W 0 x + ( ∑ i = 1 r 2 d 2 i u i u i ? ) x = W 0 x + ∑ i = 1 r 2 ( d 2 i v i ? x ) v i = W 0 x + ∑ i = 1 r 2 k 2 i v i h_1 = W_0 x + (B_1A_1) x = W_0 x + \left(\sum_{i=1}^{r_1} d_{1i} v_iv_i^\top\right) x = W_0 x + \sum_{i=1}^{r_1} (d_{1i} v_i^\top x) v_i = W_0 x + \sum_{i=1}^{r_1} k_{1i} v_i \\ h_2 = W_0 x + (B_2A_2) x = W_0 x + \left(\sum_{i=1}^{r_2} d_{2i} u_iu_i^\top\right) x = W_0 x + \sum_{i=1}^{r_2} (d_{2i} v_i^\top x) v_i = W_0 x + \sum_{i=1}^{r_2} k_{2i} v_i h1?=W0?x+(B1?A1?)x=W0?x+(i=1∑r1??d1i?vi?vi??)x=W0?x+i=1∑r1??(d1i?vi??x)vi?=W0?x+i=1∑r1??k1i?vi?h2?=W0?x+(B2?A2?)x=W0?x+(i=1∑r2??d2i?ui?ui??)x=W0?x+i=1∑r2??(d2i?vi??x)vi?=W0?x+i=1∑r2??k2i?vi?
調整的其實就是rank數量的單位向量的加權和。
這里面的 u i , v i u_i,v_i ui?,vi?都是單位向量, d 1 i d_{1i} d1i?和 d 2 i d_{2i} d2i?表示奇異值。
k 1 i k_{1i} k1i?和 k 2 i k_{2i} k2i?都是標量。
那么我們可以在每一層的輸出時,選擇需要保留的若干個單位向量的加權和作為這層的輸出,保留的依據可以根據 k 1 i k_{1i} k1i?和 k 2 i k_{2i} k2i?的大小來確定(保大去小)。
比如在合并的時候從 r 1 + r 2 r_1+r_2 r1?+r2?個秩一矩陣中取 r 1 + r 2 2 \frac{r_1+r_2}{2} 2r1?+r2??個出來作為融合后的LoRA
這樣的調整本身關于輸入 x x x是動態的,復雜度很差。
另一件事就是白盒壓縮、或者說模型壓縮的時候,為什么不直接用SVD進行降維處理呢,重新訓練一個小模型出來,可能連模型結構都不一樣的模型出來替代原模型,難道還能比跟原模型結構相同的東西有更令人信服的性能嗎?
題外話,以及做INT量化與用SVD進行降維處理,兩種方法截然不同,對精度的影響有何區別呢?兩者都對隱層權重的數值,從而影響每個隱層的輸出,但是直覺上,INT量化產生的誤差的方差在point-wise上是處處相等的(即均勻誤差),但是SVD似乎并非如此,因為SVD給出的誤差其實是若干個比較小的奇異值所對應的秩一矩陣的加權和,而秩一矩陣值的分布顯然不可能是均勻分布,這取決于奇異向量的分布:
(1) 高度結構化
- 所有行成比例:矩陣的每一行是向量 v v v的標量倍數(系數為 u i u_i ui?)。
第 i 行 = u i ? [ v 1 , v 2 , … , v n ] \text{第} i \text{行} = u_i \cdot [v_1, v_2, \dots, v_n] 第i行=ui??[v1?,v2?,…,vn?] - 所有列成比例:每一列是向量 u u u的標量倍數(系數為 v j v_j vj?)。
第 j 列 = v j ? [ u 1 , u 2 , … , u m ] T \text{第} j \text{列} = v_j \cdot [u_1, u_2, \dots, u_m]^T 第j列=vj??[u1?,u2?,…,um?]T
(2) 數值分布依賴基向量
- 矩陣元素的值完全由 u u u和 v v v的分布決定:
- 若 u u u或 v v v服從高斯分布,則 M i j M_{ij} Mij?是高斯變量的乘積(分布可能復雜,但通常呈現尖峰、重尾特性)。
- 若 u u u或 v v v是稀疏的,則 M M M也會稀疏。
(3) 特征值與奇異值
- 非零特征值:秩1矩陣僅有1個非零特征值(等于 u T v u^T v uTv或 v T u v^T u vTu)。
- 奇異值:唯一非零奇異值為 ∥ u ∥ ? ∥ v ∥ \|u\| \cdot \|v\| ∥u∥?∥v∥,其余奇異值為0。
至少從誤差的分布上來說,量化要更穩定可控一些。
總是覺得現在人做事太簡單粗暴了,沒有理論分析的支撐,全憑拍腦袋。
下面簡單看一看DeepSeek對于多LoRA合并的論述:
多LoRA并行(Multiple LoRA Adapters Parallel)的底層邏輯涉及在模型運行時動態組合多個低秩適配器的權重,其核心是通過并行計算或權重疊加的方式將不同LoRA模塊的影響同時作用于基座模型(Base Model)。
1. 基本概念回顧
- LoRA的本質:
在原始模型權重 W 0 ∈ R d × k W_0 \in \mathbb{R}^{d \times k} W0?∈Rd×k 旁注入低秩矩陣 A ∈ R d × r A \in \mathbb{R}^{d \times r} A∈Rd×r 和 B ∈ R r × k B \in \mathbb{R}^{r \times k} B∈Rr×k,其中 r ? min ? ( d , k ) r \ll \min(d,k) r?min(d,k)。
前向傳播時,輸出為:
h = W 0 x + ( B A ) x h = W_0 x + (BA) x h=W0?x+(BA)x - 多LoRA并行:
同時激活多個LoRA模塊(如 B A ( 1 ) + B A ( 2 ) + … BA^{(1)} + BA^{(2)} + \dots BA(1)+BA(2)+…),輸出變為:
h = W 0 x + ∑ i = 1 N ( B ( i ) A ( i ) ) x h = W_0 x + \sum_{i=1}^N (B^{(i)} A^{(i)}) x h=W0?x+i=1∑N?(B(i)A(i))x
2. 運行時計算的兩種實現方式
方式1:權重直接疊加(靜態合并)
步驟:
- 離線合并:在加載模型時,將所有激活的LoRA的增量權重 Δ W i = B ( i ) A ( i ) \Delta W_i = B^{(i)}A^{(i)} ΔWi?=B(i)A(i) 相加:
Δ W merged = ∑ i = 1 N B ( i ) A ( i ) \Delta W_{\text{merged}} = \sum_{i=1}^N B^{(i)}A^{(i)} ΔWmerged?=i=1∑N?B(i)A(i) - 前向計算:僅需計算一次合并后的增量:
h = W 0 x + Δ W merged x h = W_0 x + \Delta W_{\text{merged}} x h=W0?x+ΔWmerged?x
底層邏輯:
- 優點:計算量與單LoRA相同,無額外開銷。
- 缺點:靜態合并后無法動態調整適配器組合。
- 適用場景:推理時需固定融合多個LoRA。
代碼示例(PyTorch):
# 假設 lora_A 和 lora_B 是兩個LoRA模塊的增量權重
delta_W = lora_A @ lora_B + lora_C @ lora_D # 低秩矩陣乘法
h = base_model_W @ x + delta_W @ x
方式2:動態并行計算(運行時逐適配器計算)
步驟:
- 獨立保留LoRA權重:保持每個LoRA的 A ( i ) A^{(i)} A(i) 和 B ( i ) B^{(i)} B(i) 矩陣獨立存儲。
- 前向傳播時逐適配器計算:
h = W 0 x + ∑ i = 1 N ( B ( i ) ( A ( i ) x ) ) h = W_0 x + \sum_{i=1}^N (B^{(i)} (A^{(i)} x)) h=W0?x+i=1∑N?(B(i)(A(i)x))- 先計算所有 A ( i ) x A^{(i)} x A(i)x,再與對應的 B ( i ) B^{(i)} B(i) 相乘,最后累加結果。
底層邏輯:
- 優點:支持動態開關適配器(如根據輸入選擇不同的LoRA組合)。
- 缺點:計算量隨LoRA數量線性增長(需 N N N 次低秩矩陣乘法和累加)。
- 適用場景:需要運行時靈活切換適配器的場景(如多任務推理)。
代碼示例(PEFT庫實現邏輯):
# 偽代碼:動態多LoRA計算
h = base_layer(x)
for adapter in active_adapters:lora_A = adapters[adapter].A # 形狀 (d, r)lora_B = adapters[adapter].B # 形狀 (r, k)h += (lora_B @ (lora_A @ x)) * adapter.scaling # 縮放系數
3. 關鍵優化技術
為了減少多LoRA并行的計算開銷,框架(如PEFT)通常會采用以下優化:
(1) 共享基座模型權重
- 所有LoRA復用同一份 W 0 W_0 W0?,僅額外存儲增量部分 Δ W i \Delta W_i ΔWi?。
(2) 批處理低秩計算
- 將多個LoRA的 A ( i ) A^{(i)} A(i) 拼接為大矩陣 A stack ∈ R N ? r × d A_{\text{stack}} \in \mathbb{R}^{N \cdot r \times d} Astack?∈RN?r×d,通過一次矩陣乘法和分割結果加速計算:
[ A ( 1 ) x ; A ( 2 ) x ; … ] = A stack x [A^{(1)}x; A^{(2)}x; \dots] = A_{\text{stack}} x [A(1)x;A(2)x;…]=Astack?x
再分別與對應的 B ( i ) B^{(i)} B(i) 相乘。
(3) 延遲縮放(Scaling)
- 將LoRA的縮放系數 α r \frac{\alpha}{r} rα? 合并到 B ( i ) B^{(i)} B(i) 中,避免重復乘法:
B ′ ( i ) = B ( i ) ? α r B'^{(i)} = B^{(i)} \cdot \frac{\alpha}{r} B′(i)=B(i)?rα?
4. 數學形式對比
假設有兩個LoRA( Δ W 1 = B 1 A 1 \Delta W_1 = B_1A_1 ΔW1?=B1?A1?, Δ W 2 = B 2 A 2 \Delta W_2 = B_2A_2 ΔW2?=B2?A2?):
方法 | 計算過程 | FLOPs |
---|---|---|
靜態合并 | h = W 0 x + ( B 1 A 1 + B 2 A 2 ) x h = W_0 x + (B_1A_1 + B_2A_2) x h=W0?x+(B1?A1?+B2?A2?)x | O ( d k + d r + r k ) O(dk + dr + rk) O(dk+dr+rk) |
動態并行 | h = W 0 x + B 1 ( A 1 x ) + B 2 ( A 2 x ) h = W_0 x + B_1(A_1 x) + B_2(A_2 x) h=W0?x+B1?(A1?x)+B2?(A2?x) | O ( d k + 2 ( d r + r k ) ) O(dk + 2(dr + rk)) O(dk+2(dr+rk)) |
批處理優化 | h = W 0 x + [ B 1 , B 2 ] ? [ A 1 ; A 2 ] x h = W_0 x + [B_1, B_2] \cdot [A_1; A_2] x h=W0?x+[B1?,B2?]?[A1?;A2?]x | O ( d k + d ( 2 r ) + ( 2 r ) k ) O(dk + d(2r) + (2r)k) O(dk+d(2r)+(2r)k) |
5. 實際框架中的實現
以PEFT(Hugging Face)為例:
- 注冊多個適配器:
model.add_adapter("adapter1", lora_config_A) model.add_adapter("adapter2", lora_config_B)
- 動態激活組合:
model.set_active_adapters(["adapter1", "adapter2"])
- 底層計算:
- PEFT自動選擇動態并行計算(方式2),按需累加所有激活適配器的輸出。
6. 總結:多LoRA并行的本質
- 靜態合并:算術疊加權重,適合固定組合的輕量推理。
- 動態并行:獨立計算后求和,適合靈活適配但計算成本較高。
- 優化方向:通過批處理和共享基座權重減少冗余計算。
如果需要進一步分析具體框架(如PEFT、AdaLoRA)的實現細節,可以提供更多上下文!