File size: 4,734 Bytes
1836a26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# ==================== 1. 本地路径配置 ====================
base_model_path = "./Qwen3-4B-Thinking-2507"
lora_path = "./QiMing-Polaris-Qwen3-4B-Thinking-2507_burden_trained_lora"
# 测试 Prompt(使用你的 Alpaca 标准格式)
prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
What is the 'Burden-based Training' method?
### Input:
### Response:
"""
print("🚀 启动残差流向量干涉对比审计工具...")
# 2. 加载分词器和准备 Input
tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# ==================== 审计核心 Hook 函数 ====================
def audit_model_interference(model):
"""通过注册 Hook 测量模型每一层的残差流变化量 Δh 以及夹角余弦 cos(h, Δh)"""
residual_inputs = {}
residual_outputs = {}
hooks = []
def make_hook(layer_idx):
def hook(module, input_tensor, output_tensor):
residual_inputs[layer_idx] = input_tensor[0].detach()
# 如果输出是 tuple(如 Qwen 架构),取第 0 项向量
out = output_tensor[0] if isinstance(output_tensor, tuple) else output_tensor
residual_outputs[layer_idx] = out.detach()
return hook
# 兼容 PEFT 挂载前后的模型层获取
if hasattr(model, "model") and hasattr(model.model, "layers"):
layers = model.model.layers
elif hasattr(model, "base_model"):
layers = model.base_model.model.model.layers
else:
raise AttributeError("无法自动定位模型的 layers 结构")
# 注册 Hook
for i, layer in enumerate(layers):
hooks.append(layer.register_forward_hook(make_hook(i)))
# 前向传播捕获向量
with torch.no_grad():
_ = model(**inputs)
# 及时清理 Hook 防止内存泄漏
for h in hooks:
h.remove()
# 计算各层干涉数据
metrics = []
for i in range(len(layers)):
h_l = residual_inputs[i][0, -1, :].float() # 当前层最后一个 Token 的向量
h_next = residual_outputs[i][0, -1, :].float() # 下一层向量
delta_h = h_next - h_l # 本层新增的向量变化量 Δh_l
cos_sim = F.cosine_similarity(h_l, delta_h, dim=0).item()
delta_norm = delta_h.norm().item()
metrics.append({
"layer": i,
"norm": delta_norm,
"cos_sim": cos_sim
})
return metrics
# ==================== 3. 测量 Base 模型 ====================
print(f"\n🔍 正在测量 1/2: 原始模型 [{base_model_path}]...")
base_model = AutoModelForCausalLM.from_pretrained(
base_model_path,
torch_dtype=torch.bfloat16,
device_map="cuda",
trust_remote_code=True
)
base_metrics = audit_model_interference(base_model)
# ==================== 4. 挂载 FT LoRA 并测量 ====================
print(f"🔍 正在测量 2/2: FT 负重训练模型 [Base + {lora_path}]...")
ft_model = PeftModel.from_pretrained(base_model, lora_path)
ft_metrics = audit_model_interference(ft_model)
# ==================== 5. 打印对比报告 ====================
print("\n" + "="*100)
print(f"{'层数':<6} | {'[原始 Base] cos(h,Δh)':<22} | {'[FT 负重] cos(h,Δh)':<22} | {'余弦变化(Diff)':<14} | {'干涉趋势变化'}")
print("="*100)
for i in range(len(base_metrics)):
b_cos = base_metrics[i]["cos_sim"]
ft_cos = ft_metrics[i]["cos_sim"]
diff = ft_cos - b_cos
b_type = "🔴减法" if b_cos < 0 else "🟢加法"
ft_type = "🔴减法" if ft_cos < 0 else "🟢加法"
# 判断趋势变化
if ft_cos < b_cos:
trend = "⬇️ 负向干涉增强 (做减法/抵消变强)"
elif ft_cos > b_cos:
trend = "⬆️ 正向叠加增强 (做加法)"
else:
trend = "➡️ 无变化"
print(f"L-{i:<3} | {b_cos:<8.4f} ({b_type}) | {ft_cos:<8.4f} ({ft_type}) | {diff:<+10.4f} | {trend}")
print("="*100)
print("💡 结果解读指南:")
print("1. [FT 负重] 的 cos(h,Δh) 数值越小或越负,说明 FT 训练在该层施加的‘相消干涉(减法/抵消)’越强。")
print("2. 如果变化趋势显示 '⬇️ 负向干涉增强',证明挂载 FT LoRA 后,该层正在主动计算反向向量去抵消杂音噪声!")
print("="*100) |