Engram 中的多头哈希理解举例
我们以处理句子“DeepSeek improves memory retrieval with Multi-Head Hashing”为例,完整演示多头哈希(Multi-Head Hashing)的具体执行流程。为简化理解,我们设定关键参数:N-gram阶数=2(即提取连续2个Token组成的语义单元)、哈希头数量K=2(并行使用2个独立哈希函数)、嵌入表大小=101(选择质数以优化哈希分布)。
步骤1:上下文压缩(Tokenizer Compression)
首先通过词表规范化合并语义等价Token。
常见误解澄清:词表规范化压缩的是词表的ID空间大小,而非输入Token序列的长度。输入序列长度在此步骤中保持不变。
原始词表中,同一语义的不同形态(大小写、形态变体)被分配了不同的ID,造成嵌入表冗余。规范化的目标是将这些语义等价的Token归并到统一ID:
| 原始词表条目 | 原始ID | 压缩后ID | 处理说明 |
|---|---|---|---|
| DeepSeek | 102 | 102 | 保留标准形式 |
| improves | 345 | 345 | 保留标准形式 |
| memory | 789 | 789 | 保留标准形式 |
| retrieval | 210 | 210 | 保留标准形式 |
| with | 567 | 567 | 保留标准形式 |
| Multi-Head | 432 | 432 | 保留,作为标准形式 |
| multi-head | 578 | 432 | 归并至432 |
| MULTI-HEAD | 623 | 432 | 归并至432 |
| Hashing | 890 | 890 | 保留,作为标准形式 |
| hashing | 901 | 890 | 归并至890 |
压缩结果:
- 词表大小:从原始N个条目压缩至更小的N'个条目
- 嵌入表冗余条目减少,语义等价Token共享同一嵌入向量
- 输入Token ID序列长度不变,仍为7个Token:
步骤2:生成2-gram序列
对压缩后的Token序列提取连续2个Token组成的N-gram(2-gram)。对于长度为7的序列,共生成6个2-gram:
- 2-gram₁:(102, 345)(对应“DeepSeek improves”)
- 2-gram₂:(345, 789)(对应“improves memory”)
- 2-gram₃:(789, 210)(对应“memory retrieval”)
- 2-gram₄:(210, 567)(对应“retrieval with”)
- 2-gram₅:(567, 890)(对应“with Multi-Head”)
- 2-gram₆:(890, 432)(对应“Multi-Head Hashing”)
步骤3:多哈希头并行映射(核心步骤)
为每个2-gram分配2个独立哈希头(Hash Head 1和Hash Head 2),分别计算索引并检索嵌入表。
哈希函数定义
两个哈希头使用不同的hash_key(随机初始化的整数参数),哈希公式为:
index = (N-gram_value * hash_key) XOR hash_key mod 101
其中,N-gram_value是将2-gram的两个Token ID拼接为整数的结果(如(a, b) → a×1000 + b,避免数值溢出)。
具体计算 以2-gram₅ (567, 432) 为例完整计算
① 计算N-gram_value:
Vngram=567×1000+432=567432
② Hash Head 1(hash_key = 12345):
index1=(567432×12345)⊕12345mod 101
=7,003,987,640⊕12345mod 101
=7,003,999,985mod 101
=38
③ Hash Head 2(hash_key = 67890):
index2=(567432×67890)⊕67890mod 101
=38,532,899,480⊕67890mod 101
=38,532,967,370mod 101
=61
检索嵌入向量
两个哈希头分别从嵌入表E₁(Hash Head 1对应)和E₂(Hash Head 2对应)中取出向量:
- E₁[38] = [0.2, 0.5, -0.1, 0.3](4维嵌入向量)
- E₂[61] = [0.4, -0.2, 0.6, -0.4]
步骤4:上下文感知门控融合
为什么需要门控融合?
多头哈希检索出的候选向量,可能因以下原因引入噪声:
| 噪声来源 | 说明 | 示例 |
|---|---|---|
| 哈希冲突 | 不同N-gram映射到相同索引,检索出无关语义的向量 | 2-gram (789, 210) 与 2-gram (567, 432) 恰好映射到同一index=61 |
| 一词多义 | 同一Token在不同语境语义不同,导致检索向量语义偏移 | "with"在"with tears"与"with Multi-Head"中语义完全不同 |
门控融合的核心思路:让模型自己判断哪个检索结果是可信的。
Hidden State的获取
Transformer在正向传播过程中,对位置5(对应"with Multi-Head")计算得到Hidden State:
h₅ = [0.3, 0.4, -0.05, 0.25]
这是模型对当前位置综合上下文语义后的内部表示,包含了整个句子的语境信息。
⚠️ 关键点:门控打分的依据是 Hidden State h(连续向量),而非原始Token ID(离散整数)。模型无法直接计算Token ID与嵌入向量的相似度,只能在向量空间中进行比较。
注意力打分:h与候选Embedding做点积
用 h₅ 分别与两个候选Embedding做点积打分,衡量语义吻合程度:
对 E₁38 打分:
score1=h5⋅E1[38]
=0.3×0.2+0.4×0.5+(−0.05)×(−0.1)+0.25×0.3
=0.06+0.20+0.005+0.075=0.34 ✓
对 E₂61 打分:
score2=h5⋅E2[61]
=0.3×0.4+0.4×(−0.2)+(−0.05)×0.6+0.25×(−0.4)
=0.12−0.08−0.03−0.10=−0.09 ×
冲突判定与噪声抑制
score₁ = 0.34 → E₁[38] 与当前语义吻合 ✅ 保留
score₂ = -0.09 → E₂[61] 与当前语义不符 ❌ 判定为噪声,抑制
E₂61 噪声来源推断:
可能性A(哈希冲突): 某个无关2-gram(如"memory retrieval") 经Hash Head 2计算后,恰好也映射到 index=61 → E₂[61] 实际存储的是"memory retrieval"的语义 → 与当前"with Multi-Head"语义不符,点积为负 可能性B(一词多义): "with"在训练语料中大量出现于情感语境("with tears") → E₂[61] 捕获了"with"的情感语义分量 → 与技术文档语境下的"with Multi-Head"不匹配
Softmax加权融合
对打分结果做Softmax,计算各头的融合权重:
w1=e0.34e0.34+e−0.09=1.4051.405+0.914≈0.6
w2=e−0.09e0.34+e−0.09=0.9141.405+0.914≈0.39
💡 工程实践:在实际实现中,score极低(如低于阈值θ)的哈希头可直接mask掉(权重置0),而非参与Softmax,以更彻底地消除噪声干扰。
最终融合向量:
et=w1×E1[38]+w2×E2[61]
=0.61×[0.2, 0.5, −0.1, 0.3]+0.39×[0.4, −0.2, 0.6, −0.4]
=[0.122, 0.305, −0.061, 0.183]+[0.156, −0.078, 0.234, −0.156]
=[0.278, 0.227, 0.173, 0.027]
这个融合向量 etet 即为2-gram₅ "with Multi-Head" 的最终记忆表示,将被送入后续的模型层参与计算。
多头哈希通过多路并行检索 + 语义感知筛选,在保持O(1)检索效率的同时,将哈希冲突和一词多义带来的噪声降到最低,是大模型高效记忆检索的重要基础组件。
| 设计要素 | 解决的问题 | 实现方式 |
|---|---|---|
| 多个独立哈希头 | 单一哈希冲突率高 | K个不同hash_key并行检索 |
| 质数嵌入表大小 | 哈希分布不均匀 | 选取质数101作为模数 |
| 词表规范化 | 语义等价Token占用冗余空间 | 归并形态变体到统一ID |
| Hidden State打分 | 无法直接判断检索质量 | 用连续向量做点积相似度计算 |
| 动态门控融合 | 冲突/歧义向量污染结果 | 低分向量mask或降权 |