SimHash 与 MinHash:相似性计算的双子星算法

SimHash 与 MinHash:相似性计算的双子星算法

在海量文本和数据进行比对去重的场景中,如何快速判断"这两个东西有多像"是一个核心问题。精确的字符串比较在数据量大的情况下代价太高,我们需要近似相似度算法来提速。本文介绍两种经典方案:SimHash 和 MinHash,从原理、用途到适用范围全面解析。


一、SimHash:局部敏感哈希的代表作

1.1 算法原理

SimHash 的核心思想非常朴素:把文本映射为一个指纹(Fingerprint),相似的文本拥有相似的指纹。步骤如下:

  1. 分词 + 加权:对文本进行中文分词或英文 tokenize,得到一组词。对每个词计算 TF-IDF 权重(或者简单的词频)。

  2. 哈希每个词:将每个词通过一个普通哈希函数(如 MD5、SHA1)映射为一个 n 位的二进制向量。

  3. 向量加权叠加:将每个词的哈希值按权重叠加------词出现则该位加权重,否者为负权重。最终得到一个 n 维实数向量。

  4. 降维:将叠加后的向量每一位大于 0 的设为 1,小于等于 0 的设为 0,得到最终的 n 位 SimHash 值(通常取 64 位)。

    原始文本 → 分词 → 每个词哈希成64位 → 按权重叠加 → 降维(0/1) → 64位指纹

关键性质:两个文本的 SimHash 值,如果仅有 k 位不同,则它们的汉明距离(Hamming Distance)为 k。经验表明,当汉明距离 ≤ 3 时,两文本相似度极高。

1.2 如何判断相似

假设我们有两段文本 A 和 B:

  • A 的 SimHash = 101101...0101(64位)
  • B 的 SimHash = 101001...0101(64位)

逐位比较,汉明距离为 2,说明两者非常相似。

快速查询策略------抽屉原理:在大规模数据中,不可能逐一计算汉明距离。常用的策略是抽屉分组:

  • 将 64 位分成 4 组(每组 16 位)
  • 预先建立 4 个倒排索引:第 1 组对应的值 → 候选集合,第 2 组对应的值 → 候选集合......
  • 查询时,把待查文本的 SimHash 也分成 4 组,任意一组完全匹配的文字都进入候选集
  • 对候选集中的每个文本逐一计算汉明距离,保留距离 ≤ 3 的结果

这样一次查询只需 4 次精确匹配 + 少量汉明距离计算,查询效率极高。

可以进一步加速:

  • 将签名分成 b 个波段(bands),每个波段包含 r 行
  • 如果两个文档的某个波段完全相同,则进入同一个桶(bucket)
  • 只有落入同一个桶的文档才会两两比较。

1.3 应用场景

  • 网页去重:Google 早期使用 SimHash 进行网页相似度检测
  • 文档查重:论文、报告的近似重复检测
  • 图片相似搜索:将图片特征向量做 SimHash,实现以图搜图
  • 文本聚类:快速将海量文本按相似度分组

1.4 优缺点

优点 缺点
查询速度快,适合大规模数据 只适合判断"高度相似",对中等相似度(60%~80%)判断能力弱
存储成本低(64位/条) 分词质量直接影响效果
对局部轻微篡改(改一个字)有很好的鲁棒性 无法给出具体相似度数值,只能判断是否相似

二、MinHash:Jaccard 相似度的近似利器

2.1 从 Jaccard 相似度说起

在说 MinHash 之前,需要了解 Jaccard 相似度:

J(A,B)=∣A∩B∣∣A∪B∣J(A, B) = \frac{|A \cap B|}{|A \cup B|}J(A,B)=∣A∪B∣∣A∩B∣

即两个集合的交集与并集之比,值域 [0, 1]。对于文本,可以把每个文档看作一个词集合(或更精细地,shingle 集合,即 n-gram 片段集合),然后用 Jaccard 衡量相似度。问题是:当文档非常大(如网页),集合元素成千上万,Jaccard 的精确计算代价很高。

2.2 MinHash 算法原理

MinHash 的目标是:用很小的存储空间,快速近似计算两个集合的 Jaccard 相似度。核心思路:对集合做多次哈希,取每个哈希函数作用在该集合上的最小值,组成一个签名(MinHash Signature)。

步骤如下:

  1. Shingling:将文档转换为 n-gram 集合(如所有连续的 2 个字/词),记为集合 S。
  2. 构建哈希表:设定 m 个哈希函数(通常取 100~500 个),记为 h₁, h₂, ..., hₘ。
  3. 计算 MinHash 签名:对每个哈希函数 hᵢ,计算集合 S 中所有元素的哈希值,取最小值作为签名第 i 位。

签名[i]=min⁡(hi(x)),其中x∈S签名[i] = \min(h_i(x)),其中 x \in S签名[i]=min(hi(x)),其中x∈S

最终得到一个 m 位的 MinHash 签名。

  1. 相似度估计:两个集合的 MinHash 签名,第 1 位相等的概率 = Jaccard 相似度。更准确地说,两集合 MinHash 签名相等的比例是 Jaccard 相似度的无偏估计。

P(签名[1位相等])=J(A,B)P(签名[1位相等]) = J(A, B)P(签名[1位相等])=J(A,B)

2.3 LSH 二次加速(可选)

即使有了 MinHash 签名,在海量文档中两两比较仍然需要 O(N²) 次。局部敏感哈希(LSH)可以进一步加速:

  • 将签名分成 b 个波段(bands),每个波段包含 r 行
  • 如果两个文档的某个波段完全相同,则进入同一个桶(bucket)
  • 只有落入同一个桶的文档才会两两比较

签名分 b=10 个波段,每段 r=5 行 → 50位签名。两个文档只要有一个波段完全相同 → 候选相似对。通过调整 b 和 r,可以在 Precision 和 Recall 之间做权衡。

2.4 应用场景

  • 大规模文本去重:海量文档集合的相似度聚类,如新闻聚合网站
  • 网页去重:Google 在 2007 年将 MinHash 用于 Google News 的新闻去重
  • 推荐系统:基于用户行为序列的相似度计算
  • 异常检测:找出与主流群体显著不同的异常行为

2.5 优缺点

优点 缺点
能估计具体的 Jaccard 相似度(0~1之间的数值) 存储签名比 SimHash 大(通常 100~500 位 vs 64 位)
对中等相似度(30%~70%)也能有较好判断 需要多个哈希函数,哈希计算量较大
可以通过 LSH 进一步加速大规模查询 Shingling 的 n-gram 大小选择影响效果

三、对比与适用场景

特性 SimHash MinHash
判断能力 擅长判断高度相似(汉明距离 ≤ 3) 能估计 0~1 的连续相似度
典型相似度范围 ≥ 90% 才有效 30%~90% 都能感知
存储成本 低(64 位) 中高(100~500 位)
查询效率 极快(抽屉分组) 快(LSH 加速)
适用数据量 亿级网页去重 百万级文档聚类
能否给出具体相似度 ❌ 只能判断是否高度相似 ✅ 可给出数值估计

选型建议

  • 如果你的场景是判断两段文字是否几乎一样(如论文抄袭检测、网页去重),优先选 SimHash
  • 如果你的场景是找出整体风格相近的文档或做聚类,且需要知道具体相似度数值,选 MinHash
  • 如果数据量极大(亿级以上)且需要模糊聚类,两者都可以结合 LSH 思想做二次加速

四、实战小结

复制代码
相似度任务
├── 高度相似(>90%)→ SimHash
└── 中等相似(30%~90%)→ MinHash
    ├── 数据量极大 → + LSH 加速
    └── 需要具体数值 → MinHash 签名长度取长一些

两种算法都诞生于 2000 年代初,都是**局部敏感哈希(LSH)**这一大类算法的经典代表。理解它们的原理,有助于在实际工作中根据数据规模和相似度要求做出合理的技术选型。


来源:https://blog.csdn.net/bhl120/article/details/161456657

相关推荐
智者知已应修善业2 小时前
【51单片机8位数码管动态显示日期小数点风格】2023-11-13
c++·经验分享·笔记·算法·51单片机
智者知已应修善业2 小时前
【51单片机有三个LED 分别第一个灯闪三下 再到第二个灯又闪三下 再到第三个灯又闪三下 就这样循环程序】2023-11-16
c++·经验分享·笔记·算法·51单片机
小L~~~3 小时前
基于贪心策略的混合遗传算法求解01背包问题
python·算法
洛水水3 小时前
【力扣100题】53.最长回文子串
算法·leetcode·职场和发展
jieyucx3 小时前
Go 语言 sort 包详解:从基础排序到自定义排序(含底层原理+零基础看懂)
算法·golang·排序算法·sort
叁散5 小时前
ESP32 LCD1602显示实验报告
算法
过期动态5 小时前
【LeetCode 热题 100】盛最多水的容器
java·数据结构·spring boot·算法·leetcode·spring cloud·职场和发展
凌波粒5 小时前
LeetCode--700.二叉搜索树中的搜索(二叉树)
算法·leetcode·职场和发展