所属: 数据治理层。本页边界: 怎样减少重复训练、评测泄漏和模板化数据带来的偏置。
#核心公式
#符号说明
| 符号 | 含义 | 单位/条件 |
|---|---|---|
| 两份文档的 shingle、token 或 n-gram 集合 | 集合 | |
| Jaccard 相似度 | ||
| 两集合共有元素数量 | 个 | |
| 两集合去重后的总元素数量 | 个 | |
| 后文中由随机排列/哈希族 定义的 MinHash | 哈希值 | |
| MinHash 签名包含的哈希函数数量 | 正整数 | |
| 判定近重复的相似度阈值 | ||
| 语义编码器对文档 产生的向量 | ||
| Jaccard 相似度、每个 LSH band 的行数、band 数 | 、正整数、正整数 | |
| 精确去重前对文档执行的规范化函数 | 文本映射 | |
| 对长短不平衡文档更敏感的包含率 | ||
| 待比较的两份原始文档 | 文档 |
Jaccard 只比较集合重合;顺序、语义和文档长度不平衡需要额外机制处理。
#技术要点
- 精确哈希处理完全相同文本,典型实现为 SHA-1 前 N 位哈希比较和 Bloom filter 去重。
- MinHash/LSH 以 shingle 的 Jaccard 相似度处理近重复;置换函数数量在 128 处出现饱和点,继续增加带来边际收益递减。
- 语义去重处理改写文本,但更容易误删有效平行表达;SemDeDup 在 LAION 子集上可移除 50% 数据而性能损失最小。
- 去重阈值应按来源、语言和文档类型分层设置。
- 片段级去重使用后缀数组精确检测长文档内部的重复子串,适合检测多篇文章共享的长段落。
- 阶段式流水线将精确去重、近似去重和语义去重串联,每一阶段记录去除比例和受影响来源,而非只看总体去重率。
- 中文文本的 shingle 构造需考虑分词粒度与停用词过滤,K-shingle 的 K 值应根据文本长度和字符集大小动态选择。
#原理与演进
#三种重复,三种代价
| 方法 | 比较对象 | 能发现 | 主要误差 |
|---|---|---|---|
| 精确哈希 | 规范化后的字节或 token | 完全相同文档 | 标点/模板变动即漏检 |
| MinHash + LSH | shingle 集合的近似 Jaccard | 局部改写、转载与模板变体 | 短文档或重排时不稳定 |
| 片段级去重(后缀数组) | 文档内部的重复子串 | 多篇文章共享的长段落 | 计算和存储开销较高 |
| 向量语义去重 | 编码后的语义邻近度 | 同义改写 | 可能误删表达不同但都应保留的样本 |
- MinHash 的关键性质:随机排列下,两集合最小哈希相同的概率为 ;LSH 再将候选对缩小到可比较的范围。
- 将签名分成 个 band、每 band 含 个哈希值时,相似度约为 的一对文档进入候选集的概率为:
- 增大 使门槛更严格、减少误报但更易漏掉近重复;增大 提高召回,也增加后续精确比较成本。LSH 只产生候选,不能代替原文复核。
- 需要区分文档级、段落级和训练样本级重复。整篇不同不代表内部没有大段重合。
- 去重会改变数据分布:一份常见文本删到只剩一份可能过度降低高频概念;阈值应按文档类型和语言校准。
#为什么还要检查评测泄漏
训练文本与测试题的近重复会夸大分数;即便题干不同,公开答案或解析也可能泄漏。数据侧保存可比对的指纹,评测侧再做污染分析。字节级 n-gram 重叠(如 45-gram)对完整句子匹配有效,但对跨页分割或部分泄漏的场景不如语义嵌入方法稳健。实践通常组合多种信号:精确 n-gram 重叠检测逐字复制,语义嵌入(cosine > 0.90)检测改写,字节对编码指纹抵抗格式变化。
#1. 先定义“重复”的单位
| 单位 | 例子 | 去重过粗或过细的风险 |
|---|---|---|
| 整篇文档 | 同一文章镜像站转载 | 小改动逃过去重 |
| 段落/句子 | 共同模板、重复题解 | 可能误删合法引文 |
| 固定 token 片段 | 多篇文章共享一长段 | 边界切分改变匹配结果 |
| 语义等价 | 改写后的同一答案 | 容易把不同观点误判为重复 |
- 训练语料去重的目标通常是避免重复曝光和记忆放大;评测去污染的目标是避免训练信息泄漏。两者阈值和误报容忍度不同。
- “同主题”不等于“重复”:两篇独立解释相同原理的文章仍可提供不同表达。
#2. 精确哈希:便宜但只抓完全一致
对规范化文本 求哈希 ;相同哈希的候选再比较原文或强哈希,避免把哈希碰撞当成事实相同。
- 决定“什么差异被忽略”:大小写、空白、标点的处理会改变重复簇。
- 过度规范化会把代码、公式或不同数字抹平;应按文档类型定义 。
- 保留每个簇的代表样本和来源列表,而不是直接丢掉所有副本的血缘。
#2.1 CCNet 的 SHA-1 行级去重
CCNet 是 LLaMA 等模型训练数据管道中广泛使用的去重方法。其处理流程为:将所有文本转为小写并移除非标准 Unicode 字符,然后按换行符切分文档为行级单元,对每个单元计算 SHA-1 哈希值,比较不同单元之间的哈希值来识别重复。CCNet 的粒度为行级而非文档级:即使两篇文档不完全相同,只要包含相同的行级单元,这些单元就会被去重。
CCNet 还引入了桶内重复检测机制:将数据集划分为粗粒度类别桶,在每个桶内统计行级单元的出现次数,出现超过 6 次的单元被标记为重复。这一阈值用于平衡去重效果和信息保留。
#2.2 DOLMA 的 Bloom filter 去重
DOLMA(用于 OLMo 训练)在文档级去重中使用 Bloom filter 而非精确哈希表。其参数设置为:假阳性率 0.1%,10 个哈希函数,总大小约 2.27 GB。DOLMA 同时支持段落级去重,对段落计算 n-gram 的 Bloom filter 索引。
使用 Bloom filter 的优点是内存效率高:不需要存储所有文档的完整哈希值,只需要一个位数组。代价是存在假阳性(将非重复文档误判为重复),但 0.1% 的假阳性率在文档级去重中通常可接受。
#2.3 SimHash:文档指纹与海明距离
SimHash 是另一种广泛使用的去重方法,尤其适合海量文档的近似重复检测。它将文档映射为 64 位或 128 位的指纹,两文档的相似度通过指纹的海明距离衡量。SimHash 的核心性质是:内容相近的文档产生相近的指纹。
SimHash 的工程实现通常结合抽屉原理建立倒排索引:将 64 位指纹分为 4 段,每段 16 位,建立 4 个倒排索引。查询时,如果两文档的海明距离小于 3,则至少有一段指纹完全相同,只需匹配同段相同的文档即可大幅缩小搜索范围。这一方法将 64 位指纹从 500 个 Int 压缩为 1 个 Long,存储降低约 250 倍。
#3. MinHash:用签名近似集合相似度
#3.1 基本流程
- 把文档切成重叠 shingle(连续的字、词或 token 片段),得到集合 。
- 对多个独立哈希函数,分别记录集合中的最小值,构成 MinHash 签名。
- 两个签名相同位置的比例近似 Jaccard;LSH 用 band 只挑出有希望的候选对。
- 对候选再算较准确的相似度并决定保留规则。
已有公式 对集合重合敏感。若短文完整嵌入长文,Jaccard 可能仍低;可补充包含率:
#3.2 Shingle 大小的选择
K-shingle 的 K 值决定对局部改写的敏感度。K 过小时,文本的 shingle 集合中会出现过多与其他文本一致的 shingle,相似度被高估;K 过大时,相似度被低估。K 的选择应根据文本长度和字符集大小决定。
对于中文文本,shingle 通常基于分词后的词序列而非字符序列。中文的分词粒度影响 shingle 的语义质量:过细的分词(单字)产生过多噪声,过粗的分词(词组)可能遗漏局部改写。中文去重实践中,常用的策略是先进行停用词过滤,然后对过滤后的词集合提取 K-shingle,K 值通常取 3–5。
#3.3 MinHash 置换函数的饱和点
MinHash 的精度随置换函数数量 的增加而提高,但存在明显的饱和点。系统研究表明,置换函数数量从 32 增加到 128 时,F1 分数持续提升;超过 128 后,性能提升变得边际。对于 LSHBloom,盲目增加置换函数数量反而可能导致性能下降——因为 LSH 会在签名矩阵中引入更多 band,需要更多 Bloom filter,从而增加假阳性率。
实践中,128 是常用的默认值。对于质量优先的场景可以增加到 256,但需要同时调整假阳性率和 band 参数以避免引入额外误判。
#3.4 LSH 参数调优
LSH 的 band 数和每 band 行数决定相似度阈值和召回率。网格搜索研究表明,Jaccard 相似度阈值从 0.2 到 1.0 以 0.2 为步长进行评测时,阈值 0.6 通常产生最佳 F1 分数。更严格的阈值导致 F1 分数下降,因为漏检的近重复对增加。
对于 MinHashLSH 和 LSHBloom,调参的关键权衡是:
- 阈值参数对 F1 分数的影响最大——它定义了算法对文本重叠的敏感度。
- 置换函数数量影响 Jaccard 估计的精度,但存在 128 的饱和点。
- band 数和行数的组合决定 S 曲线的形状,可通过联合调优假阳性率和置换函数数量来优化。
MinHashLSH 类方法相比 n-gram 方法的一个关键优势是:参数调优的计算开销低,且有理论分析可界定假阳性和假阴性率。
#3.5 中文去重的特殊考量
中文文本没有空格分隔词边界,分词质量直接影响 shingle 的可靠性。中文去重的实践方案通常包括:
- 停用词过滤:移除“的”、“了”、“在”等高频虚词,减少无意义 shingle 的匹配。
- 动态 K 值:根据文本长度动态调整 K,短文本用较小的 K(如 2–3)保证有足够的 shingle,长文本用较大的 K(如 5–7)减少噪声。
- 语义去重嵌入模型选择:中文语义去重可选用
gte-base-zh(阿里巴巴达摩院,768 维)、text2vec-base-chinese-paraphrase(针对中文改写检测优化)或bge-base-zh-v1.5等模型。bge-base-zh-v1.5在检索、STS 和聚类任务上表现均衡。
#4. 片段级去重:后缀数组
文档级去重无法检测跨文档的局部重复:两篇文档整体相似度不高,但内部可能有一段数千字的完全相同的文本。后缀数组(Suffix Array)是解决这一问题的经典数据结构。
#4.1 基本原理
后缀数组将字符串的所有后缀按字典序排序,存储排序后的起始位置索引。两篇文档若存在重复子串,对应的后缀在排序后会相邻,通过计算相邻后缀的最长公共前缀(LCP)即可定位重复片段。
#4.2 在大规模文本去重中的应用
将整个语料拼接为一个长字符串,构建后缀数组。遍历后缀数组,比较相邻后缀的 LCP。若 LCP 长度超过预设阈值(如 100 个字符),则标记为重复片段。拼接边界处需要额外处理,避免跨文档的虚假匹配。
后缀数组的优势是精确检测:不依赖 shingle 的近似性质,能发现任意长度的重复子串。代价是构建后缀数组的时间和空间复杂度较高,对于万亿 token 级语料需要分布式实现。倍增算法结合基数排序可将构建复杂度控制在 。
#4.3 与 MinHash 的分工
MinHash 适合检测文档级近重复(整体相似但局部有差异),后缀数组适合检测片段级精确重复(多篇文档共享长段落)。实践中两者配合使用:MinHash 先粗筛出候选重复簇,后缀数组在簇内做精细的片段级重复检测。
#5. 语义去重:从 SemDeDup 到工程实现
#5.1 SemDeDup 的核心机制
SemDeDup 是语义去重领域最具代表性的方法。其流程为:使用预训练模型的嵌入向量(通常取最后一层激活或句子嵌入)表示每份文档;在嵌入空间中进行 K-means 聚类;在每个簇内计算成对 cosine 相似度;超过阈值的对视为语义重复,每组保留一个代表样本。
在 LAION 图像-文本子集上,SemDeDup 可移除 50% 的数据而性能损失最小,有效将训练时间减半。在 C4 数据集上,SemDeDup 也优于此前的方法。值得注意的是,移除语义重复后,模型在分布外(OOD)任务上的性能反而提升,说明语义重复不仅是效率问题,也是泛化问题。
#5.2 语义去重的参数设置
阈值选择。 语义去重的判定标准是嵌入空间的 cosine 相似度或欧氏距离。阈值的选择需要平衡检测精度和召回率。实践表明:
- 0.95 以上:仅检测近逐字重复,非常保守。
- 0.87–0.94:检测强语义重叠,适合训练数据。
- 0.85 以下:可能开始误判相关但独立的内容。
SemDeDup 在 LAION 上的实验中使用了 50,000 个聚类和 epsilon 值为 0.07 来过滤近重复图像。
阈值的嵌入空间依赖性。 阈值不能跨嵌入模型直接迁移。以 Jina-v2-small 嵌入空间为例,同一事实的轻度改写产生的 cosine 距离在 0.016–0.048,中度改写为 0.052–0.065,完整释义为 0.13–0.16,而不同但同主题的内容距离 ≥ 0.12。这意味着同一阈值在不同嵌入模型下可能产生截然不同的误删率。NeMo Curator 建议从较低值(如 0.001)开始逐步调高,观察数据减少率和下游表现的平衡。
#5.3 语义去重的工程实现
NVIDIA NeMo Curator 提供了 GPU 加速的语义去重工作流。其流程为:生成文档嵌入 → K-means 聚类 → 簇内计算成对 cosine 相似度 → 识别超过阈值的语义重复 → 每簇保留一个代表。
GPU 加速在这一流程中是必需的:嵌入生成和聚类操作在 CPU 上对百万级文档的耗时不可接受。NeMo Curator 使用 cuDF 做 GPU 加速的 dataframe 操作和 PyTorch GPU 模型。
#5.4 语义去重为何更危险
- 先用编码器把文档映射成向量,再做近邻搜索与阈值比较。
- 它能发现字面不相似的改写,但编码器可能把“同主题、不同事实”映到附近。
- 对专业知识,数字、单位、否定词和时间条件往往决定是否同义;纯语义相似度不能替代事实核对。
- 与其直接删除,边界样本可保留为簇并抽样检查;对训练和评测分别设阈值。
- 阈值偏松时的过度合并是主要风险,需要用阈值与统计审计控制。每组保留一个代表样本,而非全部删除。
#6. 去重如何影响优化与评测
| 情形 | 不去重的后果 | 去重过度的后果 |
|---|---|---|
| 同一网页转载多次 | 来源被重复加权 | 合法版本差异丢失 |
| 训练样本含测试题 | 评测分数虚高 | 误删相关但独立的知识 |
| 多种表达讲同一事实 | 可能有适度表达增益 | 语言多样性下降 |
| 稀有语言/代码片段 | 高频模板放大 | 本已稀少的有效样本被删除 |
研究显示训练数据去重可减少记忆与训练测试重合,并可能改善模型质量,但这不是“删得越多越好”。原论文
#7. 阶段式去重流水线
大规模去重的工程实践采用阶段式流水线:每一阶段使用不同的方法处理不同粒度和成本的重复,前一阶段的输出作为后一阶段的输入。
#7.1 三级去重架构
天池万亿语料去重赛的优胜方案展示了典型的三阶段架构:
- Stage AB(近似去重) :对长文本构造 n-gram 并生成 MinHash 签名,通过 LSH Banding 分桶召回候选相似对。核心是用“签名 + 分桶”将全量两两比较降维为桶内局部比较,并调高漏检惩罚权重优先保证召回。
- Stage C(精确验证) :在 LSH 粗筛后的桶内进行精确比较。先归一化聚合完全相同文本,然后按长度排序,利用“长度差超限必不相似”剪枝、比较预算控制和 DP 提前终止优化编辑距离计算。
- Stage E(模板检测) :采样统计跨文档高频句段,经多级归一化构建模板库并提取锚点召回变形片段;全量扫描命中区间后做合并扩边与防过删保护。
#7.2 分阶段优化的原则
- 先处理确定的完全重复:精确哈希的计算成本最低,应先执行以缩小数据量。
- 再用近似方法缩小候选:MinHash/LSH 将 的成对比较降维为桶内比较。
- 最后才对高风险簇做昂贵语义判断:语义去重的嵌入生成和聚类成本最高,应只作用于前两阶段筛选后的候选集。
- 每一步记录去除比例、受影响来源和语言:只看“去重率”无法判断是否删对。
#原始资料
- Deduplicating Training Data Makes Language Models Better
- SemDeDup: Data-efficient learning at web-scale through semantic deduplication
- CCNet: Extracting High Quality Monolingual Datasets from Web Crawl Data
- DOLMA: an Open Corpus of Three Trillion Tokens
- NVIDIA NeMo Curator: Semantic Deduplication
- A Survey of Text Deduplication: From Syntactic Matching to Semantic Understanding