Spaces:
Paused
Paused
| """ | |
| 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() | |