aifeifei798 commited on
Commit
714345f
·
verified ·
1 Parent(s): 126415b

Upload 2 files

Browse files
Files changed (2) hide show
  1. final_report.py +121 -0
  2. see_layers.py +110 -0
final_report.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoModelForCausalLM, AutoTokenizer
3
+ from peft import PeftModel
4
+ import torch.nn.functional as F
5
+
6
+ # --- ⚙️ 配置区 ---
7
+ base_model_path = "./models/gemma-3-270m-it"
8
+ lora_path = "./tmodels/gemma-3-270m-it-FT-lora"
9
+ test_prompt = "you are fox,give say a ..."
10
+ # -----------------
11
+
12
+ def inject_chat_template(tokenizer):
13
+ tokenizer.chat_template = "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/model/user/model/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '<start_of_turn>user\n' + message['content'] | trim + '<end_of_turn>\n' }}{% elif message['role'] == 'model' %}{{ '<start_of_turn>model\n' + message['content'] | trim + '<end_of_turn>\n' }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<start_of_turn>model\n' }}{% endif %}"
14
+
15
+ def print_top_k(probs, tokenizer, k=5):
16
+ top_probs, top_indices = torch.topk(probs, k)
17
+ for i in range(k):
18
+ token = tokenizer.decode([top_indices[i]]).replace('\n', '\\n')
19
+ print(f" - Rank {i+1}: [{token}] \t 概率: {top_probs[i].item()*100:.2f}%")
20
+
21
+ # 💥【修正点】函数名已从 generate_decision_chain_report 改为 full_audit_analysis
22
+ def full_audit_analysis(model_name, model, tokenizer, prompt):
23
+ print("\n" + "="*80)
24
+ print(f"📄 开始对模型 [{model_name}] 进行终极决策链审计")
25
+ print("="*80)
26
+
27
+ # --- 准备工作 ---
28
+ messages = [{"role": "user", "content": prompt}]
29
+ input_text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
30
+ inputs = tokenizer(input_text, return_tensors="pt").to(model.device)
31
+
32
+ with torch.no_grad():
33
+ outputs = model(**inputs, output_hidden_states=True)
34
+
35
+ hidden_states = outputs.hidden_states
36
+
37
+ if isinstance(model, PeftModel):
38
+ base_model_inner = model.base_model.model
39
+ else:
40
+ base_model_inner = model
41
+ final_norm_layer = base_model_inner.model.norm
42
+ lm_head_layer = base_model_inner.lm_head
43
+
44
+ # --- 开始逐帧回放 ---
45
+
46
+ # 阶段 1 & 2: 从 Input 到 Layer 18 Raw
47
+ print("\n[阶段 1 & 2] 从输入到 Layer 18 Raw (部门主管的最终提案形成过程)")
48
+ print("-" * 80)
49
+ print("这是每一层计算完毕后,未经任何修正的“原始念头”:")
50
+ for i, layer_hidden in enumerate(hidden_states):
51
+ raw_vec = layer_hidden[0, -1, :].to(base_model_inner.dtype)
52
+ raw_logits = lm_head_layer(raw_vec)
53
+ raw_probs = F.softmax(raw_logits, dim=-1)
54
+ top_prob, top_id = torch.topk(raw_probs, 1)
55
+ top_word = tokenizer.decode([top_id[0]]).replace('\n', '\\n')
56
+
57
+ layer_name = "Embed (Raw)" if i == 0 else f"L-{i} (RAW)"
58
+ print(f" - {layer_name:<12}: 最可能的词是 [{top_word}] ({top_prob[0].item()*100:.1f}%)")
59
+ print("-" * 80)
60
+
61
+ # 阶段 3: Layer 18 Raw -> Final Norm -> Normalized Vector
62
+ print("\n[阶段 3] Layer 18 Raw -> Final Norm (技术总监审查并修改提案)")
63
+ print("-" * 80)
64
+ raw_last_layer_vec = hidden_states[-1][0, -1, :].to(base_model_inner.dtype)
65
+ normalized_vec = final_norm_layer(raw_last_layer_vec)
66
+ cos_sim = F.cosine_similarity(raw_last_layer_vec.unsqueeze(0), normalized_vec.unsqueeze(0))
67
+ print("1. 部门主管 (L-18 Raw) 提交的原始提案翻译如下:")
68
+ print_top_k(F.softmax(lm_head_layer(raw_last_layer_vec), dim=-1), tokenizer)
69
+ print(f"\n2. 技术总监 (Final Norm) 对提案向量进行了修正。")
70
+ print(f" (向量方向偏移度: {cos_sim.item():.4f}, 1.0 表示未修正)")
71
+ print("-" * 80)
72
+
73
+ # 阶段 4: Normalized Vector -> LM Head -> Logits
74
+ print("\n[阶段 4] Normalized Vector -> LM Head (秘书处将修改后的提案翻译成具体方案)")
75
+ print("-" * 80)
76
+ print("技术总监修正后的提案,经秘书处翻译,内容变为:")
77
+ normed_logits = lm_head_layer(normalized_vec)
78
+ normed_probs = F.softmax(normed_logits, dim=-1)
79
+ print_top_k(normed_probs, tokenizer)
80
+ print("-" * 80)
81
+
82
+ # 阶段 5: Logits -> Decoding Strategy -> Final Token
83
+ print("\n[阶段 5] CEO (Decoding Strategy) 结合所有信息做出最终裁决")
84
+ print("-" * 80)
85
+ print("1. CEO 在做决定前,参考的最终概率分布 (outputs.logits) 是:")
86
+ final_logits_for_generation = outputs.logits[0, -1, :]
87
+ final_probs_for_generation = F.softmax(final_logits_for_generation, dim=-1)
88
+ print_top_k(final_probs_for_generation, tokenizer)
89
+
90
+ print("\n2. 经过对上下文、风险和连贯性的最终权衡,CEO 发表了公开声明:")
91
+ gen_output = model.generate(
92
+ **inputs,
93
+ max_new_tokens=100,
94
+ do_sample=False,
95
+ repetition_penalty=1.1
96
+ )
97
+ response = tokenizer.decode(gen_output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=True)
98
+ print(" >>> " + response.strip())
99
+ print("-" * 80)
100
+ print(f"✅ 模型 [{model_name}] 决策链审计完成。")
101
+
102
+
103
+ # --- 主程序 ---
104
+ print("🚀 启动终极决策链全景报告生成器...")
105
+ print(f"📝 测试 Prompt: '{test_prompt}'")
106
+
107
+ tokenizer = AutoTokenizer.from_pretrained(base_model_path)
108
+ inject_chat_template(tokenizer)
109
+
110
+ # --- 审计 Base-IT 模型 ---
111
+ base_model = AutoModelForCausalLM.from_pretrained(base_model_path, device_map="auto")
112
+ full_audit_analysis("Base-IT (老黄牛)", base_model, tokenizer, test_prompt)
113
+
114
+ # --- 审计 FT 模型 ---
115
+ base_model_for_ft = AutoModelForCausalLM.from_pretrained(base_model_path, device_map="auto")
116
+ ft_model = PeftModel.from_pretrained(base_model_for_ft, lora_path)
117
+ full_audit_analysis("FT (监工介入)", ft_model, tokenizer, test_prompt)
118
+
119
+ print("\n\n" + "="*80)
120
+ print("🎉 所有审计工作已完成。")
121
+ print("="*80)
see_layers.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoModelForCausalLM, AutoTokenizer
3
+ from peft import PeftModel
4
+ import torch.nn.functional as F
5
+
6
+ # --- ⚙️ 配置区 ---
7
+ base_model_path = "./models/gemma-3-270m-it" # 您的 Base-IT 模型路径
8
+ lora_path = "./tmodels/gemma-3-270m-it-FT-lora" # 您的 FT 模型路径
9
+ test_prompt = "给我讲个睡前故事"
10
+ print(f"问题:{test_prompt}")
11
+ # -----------------
12
+
13
+ def inject_chat_template(tokenizer):
14
+ # 手动注入 Gemma 3 模板,防止报错
15
+ tokenizer.chat_template = "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/model/user/model/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '<start_of_turn>user\n' + message['content'] | trim + '<end_of_turn>\n' }}{% elif message['role'] == 'model' %}{{ '<start_of_turn>model\n' + message['content'] | trim + '<end_of_turn>\n' }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<start_of_turn>model\n' }}{% endif %}"
16
+
17
+ def analyze_and_generate(model_name, model, tokenizer, prompt):
18
+ print(f"\n{'='*20} 分析模型: {model_name} {'='*20}")
19
+
20
+ # --- 1. 层级透镜 (Logit Lens) ---
21
+ messages = [{"role": "user", "content": prompt}]
22
+ input_text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
23
+ inputs = tokenizer(input_text, return_tensors="pt").to(model.device)
24
+
25
+ with torch.no_grad():
26
+ outputs = model(**inputs, output_hidden_states=True)
27
+
28
+ hidden_states = outputs.hidden_states
29
+
30
+ # 获取 Final Norm 和 Head
31
+ if isinstance(model, PeftModel):
32
+ base = model.base_model.model
33
+ else:
34
+ base = model
35
+
36
+ try:
37
+ final_norm = base.model.norm
38
+ lm_head = base.lm_head
39
+ except:
40
+ final_norm = lambda x: x
41
+ lm_head = base.lm_head
42
+
43
+ print(f"\n🔍 [微观视角] 思维演变过程 (共 {len(hidden_states)-1} 层)")
44
+ # 打印表头
45
+ print(f"{'层数':<8} | {'Top1 词':<12} | {'概率':<6} | {'活跃词(>1%)':<10} | {'熵(混乱度)':<10} | {'Top 2-5 备选'}")
46
+ print("-" * 95)
47
+
48
+ for i, layer_hidden in enumerate(hidden_states):
49
+ last_token_vec = layer_hidden[0, -1, :].to(base.dtype)
50
+
51
+ # 模拟输出
52
+ normalized_vec = final_norm(last_token_vec)
53
+ logits = lm_head(normalized_vec)
54
+ probs = F.softmax(logits, dim=-1)
55
+
56
+ # --- 计算新指标 ---
57
+ # 1. 活跃词数: 概率大于 1% 的词有多少个
58
+ active_candidates = (probs > 0.01).sum().item()
59
+
60
+ # 2. 熵 (Entropy): 衡量混乱程度。值越大越混乱,值越小越确定。
61
+ # Formula: -sum(p * log(p))
62
+ entropy = -torch.sum(probs * torch.log(probs + 1e-9)).item()
63
+
64
+ # 获取 Top K
65
+ top_probs, top_indices = torch.topk(probs, 5)
66
+ top_words = [tokenizer.decode([idx]).replace('\n', '\\n') for idx in top_indices]
67
+
68
+ layer_name = "Embed" if i == 0 else f"L-{i}"
69
+ top1_prob = top_probs[0].item() * 100
70
+ others = ", ".join(top_words[1:])
71
+
72
+ # 格式化打印
73
+ print(f"{layer_name:<8} | {top_words[0]:<12} | {top1_prob:>5.1f}% | {active_candidates:<10} | {entropy:<10.4f} | {others}")
74
+
75
+ # --- 2. 最终生成 (Generation) ---
76
+ print(f"\n🗣️ [宏观视角] 最终完整回答")
77
+ print("-" * 50)
78
+
79
+ # 生成参数
80
+ gen_output = model.generate(
81
+ **inputs,
82
+ max_new_tokens=100,
83
+ do_sample=False, # 为了结果稳定,这里用贪婪搜索,看它最想说什么
84
+ repetition_penalty=1.1
85
+ )
86
+
87
+ response = tokenizer.decode(gen_output[0][inputs.input_ids.shape[-1]:], skip_special_tokens=True)
88
+ print(response.strip())
89
+ print("-" * 50)
90
+
91
+
92
+ # --- 主程序 ---
93
+ print("🚀 启动深度分析工具 v2...")
94
+
95
+ # 1. Base-IT 模型
96
+ tokenizer = AutoTokenizer.from_pretrained(base_model_path)
97
+ inject_chat_template(tokenizer)
98
+ base_model = AutoModelForCausalLM.from_pretrained(base_model_path, device_map="auto")
99
+
100
+ analyze_and_generate("Base-IT (老黄牛)", base_model, tokenizer, test_prompt)
101
+
102
+ # 2. FT 模型
103
+ # 注意:PeftModel 会修改原模型,所以最好重新加载一下 Base,或者直接套用
104
+ # 这里为了严谨,我们直接在 base_model 上加载适配器
105
+ print("\n... 正在加载 LoRA 适配器 ...")
106
+ ft_model = PeftModel.from_pretrained(base_model, lora_path)
107
+
108
+ analyze_and_generate("FT (监工介入)", ft_model, tokenizer, test_prompt)
109
+
110
+ print("\n✅ 所有测试完成。")