adaptive_rag / bert_encoder_detailed_explained.py
lanny xu
resolve conflict
7edc5f6
raw
history blame
40.8 kB
"""
BERT Encoder 12层详细解析
展示 Vectara HHEM 中 BERT 编码器的每一层处理过程
使用真实训练样本演示数据流转
"""
import numpy as np
print("=" * 80)
print("BERT Encoder 12层完整解析 - 联合编码幻觉检测")
print("=" * 80)
# ============================================================================
# Part 1: 训练样本准备
# ============================================================================
print("\n" + "=" * 80)
print("📚 Part 1: 训练样本")
print("=" * 80)
print("""
训练样本(幻觉检测):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Documents (文档):
"AlphaCodium 是一种代码生成方法,通过迭代改进提升性能。"
Generation (LLM生成):
"AlphaCodium 是 Google 在 2024 年发布的代码生成工具。"
Label (标签):
Hallucinated ❌
原因:
- "Google" 在文档中没有 → 幻觉
- "2024 年" 在文档中没有 → 幻觉
- "工具" vs "方法" → 词语不精确
""")
# ============================================================================
# Part 2: Tokenization 和初始 Embeddings
# ============================================================================
print("\n" + "=" * 80)
print("🔧 Part 2: 输入准备 - Tokenization")
print("=" * 80)
print("""
Step 1: 文本拼接
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入格式:
[CLS] Documents [SEP] Generation [SEP]
实际拼接后:
[CLS] AlphaCodium 是一种代码生成方法,通过迭代改进提升性能。
[SEP] AlphaCodium 是 Google 在 2024 年发布的代码生成工具。
[SEP]
Step 2: Tokenization (BERT WordPiece 分词)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
分词结果(简化,实际会更细):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
位置 Token Token ID Segment ID
────────────────────────────────────────────────────────
0 [CLS] 101 0
1 Alpha 2945 0
2 ##Codium 3421 0
3 是 2003 0
4 一种 1037 0
5 代码 4521 0
6 生成 3156 0
7 方法 2567 0
8 , 110 0
9 通过 2134 0
10 迭代 3789 0
11 改进 2891 0
12 提升 4123 0
13 性能 3456 0
14 。 119 0
15 [SEP] 102 0 ← 第一个分隔符
16 Alpha 2945 1 ← Segment ID 变为 1
17 ##Codium 3421 1
18 是 2003 1
19 Google 5678 1
20 在 2156 1
21 2024 4532 1
22 年 3267 1
23 发布 2789 1
24 的 1998 1
25 代码 4521 1
26 生成 3156 1
27 工具 3890 1
28 。 119 1
29 [SEP] 102 1 ← 第二个分隔符
总共: 30 个 tokens
Step 3: 初始 Embeddings
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BERT 的输入 = Token Embedding + Segment Embedding + Position Embedding
对于每个 token,获取三个 embedding 并相加:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
以 Token 0 "[CLS]" 为例:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Token Embedding (词嵌入表查询)
Token ID: 101
→ Embedding Table[101] = [0.12, -0.34, 0.56, ..., 0.78] (768维)
2. Segment Embedding (段落嵌入)
Segment ID: 0 (属于 Documents 部分)
→ Segment Table[0] = [0.05, 0.02, -0.01, ..., 0.03] (768维)
3. Position Embedding (位置嵌入)
Position: 0 (第一个位置)
→ Position Table[0] = [0.08, -0.12, 0.15, ..., -0.05] (768维)
4. 相加得到初始向量
Initial Embedding[0] = Token + Segment + Position
= [0.12, -0.34, 0.56, ..., 0.78]
+ [0.05, 0.02, -0.01, ..., 0.03]
+ [0.08, -0.12, 0.15, ..., -0.05]
= [0.25, -0.44, 0.70, ..., 0.76] (768维)
所有 tokens 的初始向量矩阵:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
H⁰ = [
[0.25, -0.44, 0.70, ..., 0.76], ← Token 0: [CLS]
[0.15, 0.32, -0.23, ..., 0.45], ← Token 1: Alpha
[0.28, -0.15, 0.41, ..., 0.52], ← Token 2: ##Codium
...
[0.19, 0.27, -0.38, ..., 0.61] ← Token 29: [SEP]
]
形状: (30, 768)
↑ ↑
30个tokens 每个768维
""")
# ============================================================================
# Part 3: BERT Encoder Layer 详细结构
# ============================================================================
print("\n" + "=" * 80)
print("🏗️ Part 3: BERT Encoder Layer 结构(单层详解)")
print("=" * 80)
print("""
每一层 BERT Encoder 的结构:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入: H^(l-1) (上一层的输出,形状: 30×768)
输出: H^l (本层的输出,形状: 30×768)
┌────────────────────────────────────────────────────────┐
│ BERT Encoder Layer │
│ │
│ 输入: H^(l-1) (30, 768) │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Sub-Layer 1: Multi-Head Self-Attention │ │
│ │ ────────────────────────────────────────────────│ │
│ │ │ │
│ │ 1.1 计算 Q, K, V │ │
│ │ Q = H^(l-1) × W^Q (30×768 × 768×768) │ │
│ │ K = H^(l-1) × W^K (30×768 × 768×768) │ │
│ │ V = H^(l-1) × W^V (30×768 × 768×768) │ │
│ │ │ │
│ │ 1.2 分成 12 个 Head │ │
│ │ 每个 Head: 768 / 12 = 64 维 │ │
│ │ │ │
│ │ 1.3 每个 Head 计算 Attention │ │
│ │ Attention = softmax(Q×K^T / √64) × V │ │
│ │ │ │
│ │ 1.4 Concat 所有 Heads │ │
│ │ Output = Concat(head₁, ..., head₁₂) │ │
│ │ │ │
│ │ 1.5 线性变换 │ │
│ │ Output = Output × W^O │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Add & Norm 1 │ │
│ │ ────────────────────────────────────────────────│ │
│ │ H_att = LayerNorm(H^(l-1) + Attention_Output) │ │
│ │ ↑ 残差连接 ↑ Attention 输出 │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Sub-Layer 2: Feed Forward Network │ │
│ │ ────────────────────────────────────────────────│ │
│ │ │ │
│ │ 2.1 第一层全连接 + ReLU │ │
│ │ FFN₁ = ReLU(H_att × W₁ + b₁) │ │
│ │ (30×768 × 768×3072 = 30×3072) │ │
│ │ │ │
│ │ 2.2 第二层全连接 │ │
│ │ FFN₂ = FFN₁ × W₂ + b₂ │ │
│ │ (30×3072 × 3072×768 = 30×768) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Add & Norm 2 │ │
│ │ ────────────────────────────────────────────────│ │
│ │ H^l = LayerNorm(H_att + FFN₂) │ │
│ │ ↑ 残差连接 ↑ FFN 输出 │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ 输出: H^l (30, 768) │
└────────────────────────────────────────────────────────┘
关键参数:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Hidden Size: 768
- Attention Heads: 12
- Head Dimension: 768 / 12 = 64
- Intermediate Size (FFN): 3072
- Dropout: 0.1
""")
# ============================================================================
# Part 4: Multi-Head Self-Attention 详细计算
# ============================================================================
print("\n" + "=" * 80)
print("🔍 Part 4: Multi-Head Self-Attention 详细计算过程")
print("=" * 80)
print("""
以 Layer 1 为例,详细展示 Attention 计算:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入: H⁰ (30, 768) # 初始 embeddings
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: 计算 Q, K, V
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Q = H⁰ × W^Q
= (30, 768) × (768, 768)
= (30, 768)
K = H⁰ × W^K
= (30, 768) × (768, 768)
= (30, 768)
V = H⁰ × W^V
= (30, 768) × (768, 768)
= (30, 768)
实际数值示例(只展示前3个token的前8维):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Q = [
[0.15, -0.23, 0.34, 0.12, -0.45, 0.67, 0.89, -0.12, ...], ← [CLS]
[0.22, 0.18, -0.31, 0.45, 0.23, -0.56, 0.34, 0.78, ...], ← Alpha
[0.34, -0.12, 0.45, -0.23, 0.67, 0.12, -0.89, 0.45, ...], ← ##Codium
...
]
K 和 V 类似结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 2: 分成 12 个 Head
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
将 768 维分成 12 份,每份 64 维:
Head 0: Q[:, 0:64], K[:, 0:64], V[:, 0:64]
Head 1: Q[:, 64:128], K[:, 64:128], V[:, 64:128]
...
Head 11: Q[:, 704:768], K[:, 704:768], V[:, 704:768]
以 Head 0 为例:
Q₀ = Q[:, 0:64] # (30, 64)
K₀ = K[:, 0:64] # (30, 64)
V₀ = V[:, 0:64] # (30, 64)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 3: 计算 Attention Scores (Head 0)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Scores = Q₀ × K₀^T / √64
= (30, 64) × (64, 30) / 8
= (30, 30) / 8
结果矩阵 Scores (30, 30):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
每个元素 Scores[i][j] 表示 token i 对 token j 的注意力分数
示例(前5x5):
↓ Key tokens
[CLS] Alpha ##Cod 是 一种
[CLS] [2.3 1.5 1.8 0.9 0.7 ...] ← Query: [CLS]
Alpha [1.2 3.1 2.9 1.1 0.8 ...] ← Query: Alpha
##Cod [1.0 2.8 3.5 1.3 0.9 ...] ← Query: ##Codium
是 [0.8 1.2 1.4 2.1 1.5 ...] ← Query: 是
一种 [0.6 0.9 1.0 1.6 2.3 ...] ← Query: 一种
...
解释:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Scores[0][0] = 2.3 → [CLS] 对自己的注意力
Scores[1][2] = 2.9 → "Alpha" 对 "##Codium" 的注意力(很高,因为是同一个词)
Scores[19][1] = 1.8 → "Google"(pos 19) 对 "Alpha"(pos 1) 的注意力
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 4: Softmax 归一化
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attention_Weights = softmax(Scores, dim=-1)
对每一行做 softmax(和为1):
示例(前5x5,归一化后):
↓ Key tokens
[CLS] Alpha ##Cod 是 一种 ...
[CLS] [0.35 0.15 0.20 0.08 0.05 ...] ← 总和=1.0
Alpha [0.10 0.40 0.35 0.08 0.04 ...] ← 总和=1.0
##Cod [0.08 0.28 0.45 0.10 0.06 ...] ← 总和=1.0
是 [0.12 0.18 0.20 0.30 0.15 ...] ← 总和=1.0
一种 [0.10 0.14 0.16 0.22 0.32 ...] ← 总和=1.0
...
关键观察:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- "Alpha" 对 "##Codium" 的权重 = 0.35(高!)
→ 说明模型学会了它们是同一个词
- "Google" (pos 19) 对 Documents 中的 tokens 权重较低
→ 因为 Documents 中没有 "Google"
→ 这个信息会被用于判断幻觉!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 5: 加权求和 V
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Output₀ = Attention_Weights × V₀
= (30, 30) × (30, 64)
= (30, 64)
对于每个 token i:
Output₀[i] = Σⱼ Attention_Weights[i][j] × V₀[j]
示例(token 0 "[CLS]" 的输出):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Output₀[0] = 0.35 × V₀[0] ([CLS] 的 value)
+ 0.15 × V₀[1] (Alpha 的 value)
+ 0.20 × V₀[2] (##Codium 的 value)
+ 0.08 × V₀[3] (是 的 value)
+ ...
+ 0.02 × V₀[19] (Google 的 value) ← 权重很小!
+ ...
结果: [0.23, -0.15, 0.34, ..., 0.67] (64维)
[CLS] 的向量现在包含了:
- 主要: 自己、Alpha、##Codium 的信息(权重大)
- 少量: Google、2024 的信息(权重小)
- 这个差异会被后续层放大,用于检测幻觉!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 6: Concat 所有 12 个 Heads
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Multi_Head_Output = Concat(Output₀, Output₁, ..., Output₁₁)
= Concat((30,64), (30,64), ..., (30,64))
= (30, 768)
每个 Head 捕捉不同的模式:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Head 0: 词内关系 ("Alpha" ↔ "##Codium")
Head 1: 语法关系 ("是" ↔ "方法")
Head 2: 长距离依赖 ("AlphaCodium" ↔ "性能")
Head 3: 检测添加信息 ("Google" 在 Documents 中的对应)
...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 7: 线性变换
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attention_Output = Multi_Head_Output × W^O + b^O
= (30, 768) × (768, 768) + (768,)
= (30, 768)
""")
# ============================================================================
# Part 5: 12层逐层处理
# ============================================================================
print("\n" + "=" * 80)
print("🔢 Part 5: BERT 12层逐层处理过程")
print("=" * 80)
print("""
完整的 12 层处理流程:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入: H⁰ (30, 768) # 初始 embeddings
┌──────────────────────────────────────────────────────────┐
│ Layer 1 │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H⁰ │
│ ↓ │
│ Multi-Head Self-Attention │
│ - "Alpha" attend到 "##Codium" (0.35) │
│ - "Google" attend到 Documents tokens (0.1-0.2) │
│ ↓ │
│ Add & Norm: H_att¹ = LayerNorm(H⁰ + Attention) │
│ ↓ │
│ Feed Forward: FFN(H_att¹) │
│ ↓ │
│ Add & Norm: H¹ = LayerNorm(H_att¹ + FFN) │
│ │
│ 输出: H¹ (30, 768) │
│ │
│ 学到的模式: │
│ ✓ 基本词语关系 │
│ ✓ "AlphaCodium" 在两段中都出现 │
│ ✓ "Google" 只在 Generation 中出现 ⚠️ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Layer 2 │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H¹ │
│ ↓ │
│ Multi-Head Self-Attention │
│ - 开始建立语法关系 │
│ - "是" attend到 "方法" 和 "工具" │
│ ↓ │
│ FFN + Residual │
│ ↓ │
│ 输出: H² (30, 768) │
│ │
│ 学到的模式: │
│ ✓ "方法" vs "工具" 的语义差异 │
│ ✓ 时间信息: "2024 年" │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Layer 3 │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H² │
│ ↓ │
│ Multi-Head Self-Attention │
│ - 长距离依赖开始建立 │
│ - [CLS] attend到关键词: "Google", "2024" │
│ ↓ │
│ FFN + Residual │
│ ↓ │
│ 输出: H³ (30, 768) │
│ │
│ 学到的模式: │
│ ✓ Documents: "迭代改进" vs Generation: 无此信息 │
│ ✓ Generation: "Google" vs Documents: 无此信息 ⚠️ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Layer 4-6: 中间层 │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H³ │
│ ↓ │
│ 多层 Self-Attention + FFN │
│ ↓ │
│ 输出: H⁶ (30, 768) │
│ │
│ 学到的模式: │
│ ✓ 复杂的语义关系 │
│ ✓ Documents 和 Generation 的对比 │
│ ✓ 识别不一致的地方: │
│ - "方法" vs "工具" │
│ - 缺失 "Google" 和 "2024" 的来源 │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Layer 7-9: 深层抽象 │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H⁶ │
│ ↓ │
│ 多层 Self-Attention + FFN │
│ ↓ │
│ 输出: H⁹ (30, 768) │
│ │
│ 学到的模式: │
│ ✓ 高层语义理解 │
│ ✓ [CLS] 向量开始聚合判断信息: │
│ - Documents 说: "代码生成方法,迭代改进" │
│ - Generation 说: "Google 发布的工具" │
│ → 发现不匹配!⚠️ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Layer 10-12: 最终层(决策层) │
│ ──────────────────────────────────────────────────────── │
│ │
│ 输入: H⁹ │
│ ↓ │
│ 多层 Self-Attention + FFN │
│ ↓ │
│ 输出: H¹² (30, 768) │
│ │
│ [CLS] 向量的信息(最关键): │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ H¹²[0] = [0.234, -0.567, 0.890, ..., 0.123] (768维) │
│ ↑ [CLS] token 的最终向量 │
│ │
│ 这个向量编码了: │
│ ✓ Documents 的完整信息 │
│ ✓ Generation 的完整信息 │
│ ✓ 两者的关系: │
│ - 有哪些信息一致 │
│ - 有哪些信息矛盾 │
│ - Generation 添加了哪些 Documents 中没有的信息 │
│ │
│ 具体识别到的问题: │
│ ❌ "Google" 在 Documents 中找不到对应 │
│ ❌ "2024" 在 Documents 中找不到对应 │
│ ⚠️ "工具" vs "方法" 语义差异 │
│ │
│ → 准备输出到分类头,判断为 "Hallucinated" │
└──────────────────────────────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
最终输出: H¹² (30, 768)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
只使用 H¹²[0]([CLS] 的向量)送入分类头:
[CLS] Vector = H¹²[0] = [0.234, -0.567, 0.890, ..., 0.123]
分类头 (768 → 2)
Logits: [0.8, 4.2]
↑ ↑
Factual Hallucinated
Softmax
Probs: [0.03, 0.97]
↑ ↑
3%事实 97%幻觉
判断: Hallucinated ❌ (置信度 97%)
""")
# ============================================================================
# Part 6: 关键 Attention 模式可视化
# ============================================================================
print("\n" + "=" * 80)
print("👁️ Part 6: 关键 Attention 模式可视化")
print("=" * 80)
print("""
Layer 12 的 Attention 权重矩阵(简化,只显示关键 tokens):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Query ↓ Key Tokens →
Tokens [CLS] Alpha¹ 方法 [SEP] Alpha² Google 2024 工具 [SEP]
─────────────────────────────────────────────────────────────────
[CLS] [0.15 0.08 0.12 0.05 0.07 0.18 0.16 0.10 0.05]
↑低 ↑低 ↑中 ↑低 ↑低 ↑高⚠️ ↑高⚠️ ↑中 ↑低
Alpha¹ [0.05 0.30 0.08 0.03 0.25 0.04 0.03 0.05 0.02]
↑低 ↑高✓ ↑低 ↑低 ↑高✓ ↑低 ↑低 ↑低 ↑低
方法 [0.08 0.10 0.25 0.05 0.08 0.06 0.05 0.20 0.03]
↑低 ↑低 ↑高✓ ↑低 ↑低 ↑低 ↑低 ↑中⚠️ ↑低
Google [0.10 0.05 0.03 0.02 0.06 0.40 0.15 0.08 0.02]
↑中⚠️ ↑低 ↑低 ↑低 ↑低 ↑高✓ ↑中 ↑低 ↑低
2024 [0.12 0.04 0.02 0.01 0.05 0.18 0.35 0.07 0.01]
↑中⚠️ ↑低 ↑低 ↑低 ↑低 ↑中 ↑高✓ ↑低 ↑低
工具 [0.09 0.08 0.15 0.03 0.09 0.07 0.06 0.30 0.02]
↑低 ↑低 ↑中⚠️ ↑低 ↑低 ↑低 ↑低 ↑高✓ ↑低
关键观察:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 正常模式:
- "Alpha¹" attend到 "Alpha²" (0.25) ← 同一实体
- "方法" attend到自己 (0.25) ← 自注意力
⚠️ 幻觉指示:
- "Google" 主要 attend到自己 (0.40)
→ 在 Documents 中找不到强关联!
- "2024" 主要 attend到自己 (0.35)
→ 在 Documents 中找不到强关联!
- [CLS] attend到 "Google" (0.18) 和 "2024" (0.16)
→ [CLS] 注意到这些异常词!
- "工具" 对 "方法" 的 attention (0.15)
→ 语义相似但不完全一致
这些模式被分类头学习并用于判断幻觉!
""")
# ============================================================================
# Part 7: 参数统计
# ============================================================================
print("\n" + "=" * 80)
print("📊 Part 7: BERT Encoder 参数统计")
print("=" * 80)
print("""
BERT-base 参数详细统计:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Embedding 层:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Token Embedding: 30,522 × 768 = 23,440,896
- Segment Embedding: 2 × 768 = 1,536
- Position Embedding: 512 × 768 = 393,216
小计: 23,835,648 参数
2. 每个 Encoder Layer:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Multi-Head Attention:
- W^Q: 768 × 768 = 589,824
- W^K: 768 × 768 = 589,824
- W^V: 768 × 768 = 589,824
- W^O: 768 × 768 = 589,824
- Biases: 768 × 4 = 3,072
小计: 2,362,368 参数
Feed Forward Network:
- W₁: 768 × 3,072 = 2,359,296
- b₁: 3,072
- W₂: 3,072 × 768 = 2,359,296
- b₂: 768
小计: 4,722,432 参数
Layer Normalization (×2):
- γ, β: 768 × 2 × 2 = 3,072
每层总计: 2,362,368 + 4,722,432 + 3,072 = 7,087,872 参数
3. 12 层 Encoder:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7,087,872 × 12 = 85,054,464 参数
4. 分类头(HHEM 特有):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- W: 768 × 2 = 1,536
- b: 2
小计: 1,538 参数
总参数量:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
23,835,648 (Embeddings)
+ 85,054,464 (12 Layers)
+ 1,538 (Classification Head)
= 108,891,650 参数
约 109M (百万) 参数
模型大小: 109M × 4 bytes = 436 MB
内存占用(推理时):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 模型参数: 436 MB
- 激活值 (batch_size=1, seq_len=30):
每层: 30 × 768 × 4 bytes × 2 (residual) = 184 KB
12 层: 184 KB × 12 = 2.2 MB
- 总计: ~438 MB (FP32)
~220 MB (FP16,使用半精度)
""")
# ============================================================================
# Part 8: 总结
# ============================================================================
print("\n" + "=" * 80)
print("📚 Part 8: 核心要点总结")
print("=" * 80)
print("""
BERT Encoder 12层联合编码核心要点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 输入准备
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CLS] Documents [SEP] Generation [SEP]
→ Tokenization (30 tokens)
→ Token + Segment + Position Embeddings
→ H⁰ (30, 768)
2. 每层结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
H^(l-1)
Multi-Head Self-Attention (12 heads)
Add & Norm
Feed Forward Network
Add & Norm
H^l
3. Multi-Head Attention 关键
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Q, K, V = H × W^Q, H × W^K, H × W^V
分成 12 个 Head (每个 64 维)
Attention = softmax(Q×K^T / √64) × V
Concat 所有 Heads → (768 维)
4. 12层逐层学习
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Layer 1-3: 基本语法、词语关系
Layer 4-6: 复杂语义、长距离依赖
Layer 7-9: 高层抽象、不一致检测
Layer 10-12: 最终判断、信息聚合到 [CLS]
5. 幻觉检测机制
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
通过 Attention 权重发现:
✓ "Google" 在 Documents 中无强关联
✓ "2024" 在 Documents 中无强关联
✓ [CLS] 聚合这些信息
H¹²[0] (768维) → 分类头 (768→2)
[Factual: 0.03, Hallucinated: 0.97]
判断: Hallucinated ❌
6. 关键参数
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Hidden Size: 768
- Layers: 12
- Attention Heads: 12
- Head Dimension: 64
- FFN Size: 3072
- Total Parameters: 109M
- Model Size: 436 MB (FP32)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
联合编码的优势:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Documents 和 Generation 可以互相 attend
✅ 模型能捕捉两者之间的一致性/矛盾
✅ [CLS] 向量聚合了全局判断信息
✅ 12 层逐层深化理解,最终准确判断幻觉
这就是为什么 BERT Cross-Encoder 在幻觉检测上表现优秀!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
""")
print("\n" + "=" * 80)
print("✅ BERT Encoder 12层详细解析完毕!")
print("=" * 80)
print()