""" 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()