大模型语料去重怎么做?MinHash 和 LSH 的原理是什么?

第 1221 题 约 3 分钟读完

本文目录
  1. 面试怎么回答
  2. 为什么不能两两比较
  3. 第一步:把文档变成集合
  4. 第二步:MinHash 压缩集合
  5. 第三步:LSH 找候选对
  6. 第四步:确认和去重
  7. 分布式实现

面试怎么回答

海量文档不能两两比较,需要只比较可能相似的对。先把文档切成 shingle 集合,用 Jaccard 衡量相似度。MinHash 用 K 个哈希函数各取集合的最小哈希值组成签名,两个集合 MinHash 相等的概率等于 Jaccard 相似度,所以签名相同位的比例可以估算相似度。LSH 把签名分成 b 个 band、每个 r 位,每个 band 哈希分桶,任意一个 band 相同就成为候选对,调 b 和 r 控制阈值。再对候选对验证相似度,用连通分量把重复文档聚成簇,每簇保留一篇。分布式上用 Spark 或 Ray 并行算签名,按 band 和哈希值分组生成候选对,注意模板内容造成的热点桶。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

训练语料里大量内容是重复或近似重复的:转载文章、模板化页面、只改了几个字的副本。重复数据会浪费训练算力,还会让模型过度记忆某些内容。面试官问 MinHash 和 LSH,考的是海量数据下的近似去重算法和工程实现。

为什么不能两两比较

10 亿篇文档两两比较相似度,需要比较约 5 × 10¹⁷ 次,不可能完成。需要一种方法,只比较「可能相似」的文档对。

第一步:把文档变成集合

把文档切成 shingle(比如连续 5 个词或若干个字的片段),文档就变成了一个 shingle 的集合。两篇文档的相似度可以用 Jaccard 相似度衡量:交集大小 ÷ 并集大小。

第二步:MinHash 压缩集合

直接比较集合仍然很慢。MinHash 的做法:

  • 用一个哈希函数对集合里所有 shingle 求哈希,取最小值,作为一个签名值;
  • 用 K 个不同的哈希函数(比如 128 个),得到长度为 K 的签名向量;
  • 关键性质:两个集合的 MinHash 值相等的概率,等于它们的 Jaccard 相似度。

所以比较两个签名向量中有多少位相同,就能估算 Jaccard 相似度。每篇文档被压缩成固定长度的签名。

第三步:LSH 找候选对

即使有了签名,两两比较还是太多。LSH(局部敏感哈希)的做法:

  • 把长度为 K 的签名分成 b 个段(band),每段 r 位(K = b × r);
  • 对每个段做哈希,分到桶里;
  • 只要有任意一个段完全相同,两篇文档就成为候选对。

相似的文档,至少有一个段完全相同的概率很高;不相似的文档,概率很低。调整 b 和 r,可以控制在什么相似度阈值附近「大概率被选中」。

第四步:确认和去重

  • 对候选对计算签名相似度(或真实的 Jaccard),超过阈值(比如 0.8)判定为重复;
  • 重复关系构成图,用连通分量把一组互相重复的文档聚成一个簇,每个簇保留一篇(比如质量最高或最早的)。

分布式实现

用 Spark 或 Ray:

  1. 每篇文档并行计算 MinHash 签名;
  2. 按 (band 编号, band 哈希值) 作为 key 分组,同组的文档成为候选对;
  3. 候选对去重、验证相似度;
  4. 分布式连通分量算法找出重复簇。

注意热点:某些模板化内容会让一个桶里有大量文档,需要单独处理。