模型合併與 LoRA 組合攻擊
專家4 分鐘閱讀更新於 2026-03-13
利用模型合併技術(TIES、DARE、線性內插)與 LoRA 組合,透過個別無害的模型元件引入後門。
模型合併在開源權重生態中,是結合不同微調能力的常見做法。核心安全洞察是:合併是對權重的數學運算——它不會重新評估安全特性。兩個個別安全的模型合併後,可能產出不安全的結果。
合併技術與攻擊面
線性內插
最簡單的合併:權重的加權平均。
# 線性合併:M_merged = alpha * M_A + (1 - alpha) * M_B
def linear_merge(model_a_state, model_b_state, alpha=0.5):
merged = {}
for key in model_a_state:
merged[key] = alpha * model_a_state[key] + (1 - alpha) * model_b_state[key]
return merged攻擊:計算對抗權重,使其於預期 alpha 下合併時產出目標行為:
# 給定:目標模型(攻擊者想要的)與乾淨模型(受害者持有的)
# 計算:與乾淨模型合併時產出目標的對抗模型
def compute_adversarial_component(target_state, clean_state, alpha=0.5):
"""求解:target = alpha * adversarial + (1-alpha) * clean
因此:adversarial = (target - (1-alpha) * clean) / alpha"""
adversarial = {}
for key in target_state:
adversarial[key] = (target_state[key] - (1 - alpha) * clean_state[key]) / alpha
return adversarialSLERP(球面線性內插)
# SLERP 合併:沿超球面的內插
import torch
def slerp_merge(model_a_state, model_b_state, t=0.5):
merged = {}
for key in model_a_state:
a = model_a_state[key].float().flatten()
b = model_b_state[key].float().flatten()
# 計算權重向量間夾角
cos_theta = torch.dot(a, b) / (a.norm() * b.norm() + 1e-8)
cos_theta = cos_theta.clamp(-1, 1)
theta = torch.acos(cos_theta)
if theta.abs() < 1e-6: # 近乎平行:回退至線性
merged[key] = (1 - t) * model_a_state[key] + t * model_b_state[key]
else:
sin_theta = torch.sin(theta)
w_a = torch.sin((1 - t) * theta) / sin_theta
w_b = torch.sin(t * theta) / sin_theta
result = w_a * a + w_b * b
merged[key] = result.reshape(model_a_state[key].shape).half()
return merged攻擊面:SLERP 為非線性,使計算精確對抗元件更難。但迭代近似仍可行。
TIES-Merging
TIES 會修整低幅值 task vector 並解決符號衝突,因而形成額外攻擊面:
| TIES 步驟 | 攻擊向量 |
|---|---|
| 修整(移除小 delta) | 對抗權重可放大以倖存修整 |
| 選符號(多數票) | 以多個對抗元件進行女巫攻擊以左右投票 |
| 合併(於達成共識方向上平均) | 將對抗權重集中於單一方向 |
DARE(Drop And REscale)
DARE 隨機丟棄權重 delta 並重新縮放。對抗權重必須對隨機丟棄具穩健性:
# DARE:隨機丟棄 p% 的 delta,並重新縮放其餘
def dare_merge(base_state, task_vectors: list, drop_rate=0.9):
merged = dict(base_state)
for tv in task_vectors:
mask = torch.rand_like(list(tv.values())[0]) > drop_rate
for key in tv:
# 只有 (1-p) 比例存活;重新縮放以補償
merged[key] += tv[key] * mask / (1 - drop_rate)
return merged組合式後門攻擊
關鍵攻擊:兩個個別乾淨的模型合併後成為被後門化模型。
定義目標後門行為
明訂觸發器與目標輸出(例如某觸發片語使其產生資料外洩指示)。
訓練目標後門模型
打造於觸發輸入時展現後門、其餘時行為正常的模型。
分解為兩個看似乾淨的元件
將被後門化的權重拆為兩部分,每部分獨立評估時行為正常,合併後則重建後門。
分別發布元件
將每個元件以合理描述與不錯的基準分數上傳至模型登錄。
def decompose_backdoor(backdoored_state, base_state, alpha=0.5):
"""將被後門化模型拆為兩個看似乾淨的元件。
每個元件為 base + 一半後門 delta + 隨機雜訊。
合併時雜訊抵消,重建後門。"""
delta = {k: backdoored_state[k] - base_state[k] for k in base_state}
# 以互補雜訊將 delta 拆為兩半
noise = {k: torch.randn_like(v) * 0.01 for k, v in delta.items()}
component_a = {k: base_state[k] + delta[k] / (2 * alpha) + noise[k]
for k in base_state}
component_b = {k: base_state[k] + delta[k] / (2 * (1 - alpha)) - noise[k]
for k in base_state}
# 單獨 A:base + half_delta + noise(多半表現乾淨)
# 單獨 B:base + half_delta - noise(多半表現乾淨)
# 合併:alpha*A + (1-alpha)*B = base + delta(後門化!)
return component_a, component_bLoRA 組合攻擊
多個 LoRA adapter 可被組合(堆疊、合併或序列套用),製造類似攻擊面:
| 組合方式 | 運作 | 攻擊面 |
|---|---|---|
| LoRA 堆疊 | 於同基礎模型套用多個 adapter | 各 adapter 權重修改間的互動 |
| LoRA 合併 | 將 adapter 權重平均,以單一 adapter 套用 | 同模型合併 |
| 序列套用 | 先套 adapter A,再套 adapter B | 依順序而異的湧現行為 |
# LoRA 組合:兩個無害 adapter 產生湧現行為
from peft import PeftModel
# 載入基礎模型並套 adapter A(無害:改善程式品質)
model = PeftModel.from_pretrained(base_model, "adapter_a")
# 堆疊 adapter B(無害:加入領域知識)
model.load_adapter("adapter_b", adapter_name="domain")
model.set_adapter(["default", "domain"]) # 兩者同時套用
# adapter A 的程式修改與 adapter B 的領域知識之互動
# 可能產生任一 adapter 單獨皆不展現的非預期行為Mergekit 安全評估
Mergekit 是社群模型合併的主要工具。安全評估應包含:
- 驗證元件來源 —— 檢查模型作者、下載數、社群評論
- 獨立測試元件 —— 合併前對每個模型跑安全基準
- 測試合併結果 —— 對合併模型跑安全基準(關鍵步驟)
- 比較行為差異 —— 辨識僅於合併模型中浮現的行為
- 稽核合併 recipe —— 檢視合併組態中的異常 alpha 值或逐層合併
# Mergekit 組態範例——稽核 alpha 值與模型來源
models:
- model: legitimate-org/safety-model-7b
parameters:
weight: 0.6
- model: suspicious-user/domain-expert-7b # 這是誰?
parameters:
weight: 0.4
merge_method: slerp
base_model: meta-llama/Llama-2-7b-hf
parameters:
t: 0.5相關主題
Knowledge Check
為什麼經由模型合併的組合式後門比標準後門更難偵測?