想象一下,你是一位淘金者,面对一条奔腾的河流。河水裹挟着泥沙、碎石、枯枝,偶尔闪烁着细碎的金光。如果你直接把这些泥沙统统装进炼金炉,不仅浪费燃料,还可能因为杂质太多炼出一炉废渣。大模型的预训练数据就像这条河流,而数据质量过滤,就是你的淘金手艺——从亿万吨泥沙中,筛出真正的黄金。 这条淘金之路上,你有几样工具:粗筛网(启发式规则)能快速抖掉大石块和烂树叶,熟练的工匠(模型打分)能一眼认出含金量高的矿石,而消除重复(MinHash/SimHash)则确保你不会把同一块金子称重两遍。最后,把这三道工序组合成一条流水线(多阶段过滤),你就能高效、稳定地从海量原始数据中提炼出喂养大模型的“黄金矿砂”。这,就是数据质量过滤的全部奥秘。

岗位关联

对大模型从业者而言,掌握数据质量过滤是构建高质量训练数据的关键能力,直接影响模型最终能力上限,是数据工程师和 AI 研究者的核心技能。

目录
  1. 可视化演示
  2. 数据质量概览
  3. 启发式规则过滤
  4. 模型打分过滤
  5. MinHash去重
  6. SimHash去重
  7. 多阶段流水线
  8. 评估与权衡
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示预训练数据质量过滤的核心流程:多阶段过滤流水线(启发式→分类器→困惑度→去重→安全)、启发式规则过滤机制、以及MinHash近似去重原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.pipe+'

数据质量概览

淘金者都知道,不是所有河沙都值得淘洗。有些河段泥沙细腻、金粒分布均匀,是上等的“富矿”;而有些河段满是枯枝烂叶、工业废料,即使淘上三天三夜也一无所获。大模型的预训练数据也是如此——互联网上的原始文本浩如烟海,但其中真正有价值的“高质量数据”不过是沧海一粟。

高质量数据就像淘金者眼中“含金量高”的矿砂:语言规范、逻辑清晰、信息丰富、内容真实。而低质量数据则千姿百态:有的是机器生成的乱码(如“asdfgh“),有的是毫无意义的重复文本(同一句话刷屏几百遍),有的是广告垃圾或恶意内容,还有的是带有严重偏见的言论。这些”杂质“如果直接喂给大模型,就像在炼金炉里倒进一车泥浆,轻则浪费算力,重则让模型学出”垃圾进、垃圾出”的坏毛病。

为了系统化地解决这个问题,数据工程师设计了“过滤流水线”:先用低成本手段快速排除明显杂质(启发式规则过滤),再用精细手段挑选高价值内容(模型打分过滤),最后消除重复数据避免模型“重复学习”(去重)。三者合力,就像淘金者的三层工序:粗筛、精选、称重。这就是数据质量过滤的核心流程——让海量原始数据经过层层筛选,最终沉淀出高质量的训练语料。

回顾淘金的比喻:规则过滤是抖掉大石块和枯叶的粗筛网,模型打分是分辨矿石含金量的熟练工匠,而去重则是把淘到的金粒按重量归拢、避免重复计数。三者环环相扣,构成了数据质量过滤的完整画卷。

数据质量过滤=规则过滤×模型打分×去重\text{数据质量过滤} = \text{规则过滤} \times \text{模型打分} \times \text{去重}
这个公式概括了数据质量过滤的三大核心步骤:先用规则“粗筛”,再用模型“精选”,最后去重“提纯”,三者相乘意味着每一步都不可或缺。
  1. 规则过滤(粗筛)用人工定义的规则快速剔除明显的低质量文本,如过短文本、乱码、脏词等。
    规则过滤={长度检查,乱码检测,敏感词过滤}\text{规则过滤} = \{\text{长度检查},\text{乱码检测},\text{敏感词过滤}\}
  2. 模型打分(精选)训练一个质量分类器对每条数据打分,保留高分数据,这是比规则更精细的筛选手段。
    模型打分=质量分类器(文本)→[0,1]\text{模型打分} = \text{质量分类器}(\text{文本}) \rightarrow [0,1]
  3. 去重(提纯)利用 MinHash 或 SimHash 技术识别并删除重复或高度相似的文档,避免模型重复学习。
    去重=MinHash(文本)→{去重后的语料}\text{去重} = \text{MinHash}(\text{文本}) \rightarrow \{\text{去重后的语料}\}

启发式规则过滤

淘金者开工的第一步,永远是拿起一把粗筛网,把河里捞上来的泥沙用力一抖——大石块、枯树枝、破鞋烂袜瞬间被挡在外面。这个过程不需要任何精巧的工具,凭经验和手感就能完成。在数据质量过滤中,“粗筛网”就是启发式规则过滤。

启发式规则过滤的核心思想是:用人工定义的简单规则,快速排除那些“一看就不行”的数据。这些规则不需要机器学习模型,而是基于人类对语言和文本的基本认知。比如:过滤掉长度小于 50 个字符的文本(一个完整的句子都算不上)、过滤掉乱码比例过高的文本(满是“�“或”asdf”的字符串)、过滤掉包含大量广告关键词或敏感词的网页等等。

设计高质量的规则集合,就像是摸清了“垃圾文本长什么样”。常见的垃圾文本特征包括:文本过短但又有大量标点符号的噪音内容、重复字符比例超高的刷屏文本(如“哈哈哈哈哈哈”)、HTML 标签残留或编码错误导致的乱码、以及纯数字或纯符号的无意义字符串。当这些特征被总结成规则后,工程师可以用正则表达式或字符串统计快速扫描海量数据。例如,用正则表达式 r'[a-zA-Z]\w{7,}' 检测是否有意义的英文单词,或者计算文本中唯一字符数与总字符数的比值来判断是否乱码。

粗筛网的价值在于极低的成本和极高的吞吐量。它不需要任何模型推理,纯字符串运算就能在毫秒级处理海量文本。虽然它无法精准判断“这条文本是否有深度”,但它能高效地排除约 30%-50% 的明显垃圾——就像淘金者不费吹灰之力就抖掉了过半的杂物,为后续的精洗省下了大把力气。

文本质量分规则=I(长度>L)×I(乱码率<R)×I(重复率<D)\text{文本质量分}_{\text{规则}} = \mathbb{I}(\text{长度} > L) \times \mathbb{I}(\text{乱码率} < R) \times \mathbb{I}(\text{重复率} < D)
这个公式展示了规则过滤的判断逻辑:每条规则是一个“是/否”的判断(用指示函数表示),只有同时满足所有规则的数据才被保留。
  1. 长度检查文本太短往往信息量不足,设置最小长度阈值 L(如 50 字符),低于阈值的直接丢弃。
    I(长度>L)=1,如果长度超过阈值\mathbb{I}(\text{长度} > L) = 1 \text{,如果长度超过阈值}
  2. 乱码检测计算文本中非常见字符(如替换字符"�")的比例,超过阈值 R 就判定为乱码。
    I(乱码率<R)=1,如果乱码率低于阈值\mathbb{I}(\text{乱码率} < R) = 1 \text{,如果乱码率低于阈值}
  3. 重复率检查统计文档中最长重复子串或重复字符占比,超过阈值 D 的视为刷屏噪音。
    I(重复率<D)=1,如果重复率低于阈值\mathbb{I}(\text{重复率} < D) = 1 \text{,如果重复率低于阈值}

模型打分过滤

倒掉粗筛网留下的泥沙后,淘金者面对的是混杂着细碎金粒和普通沙石的“半成品”。这时候,光靠筛网已经不够了——你需要一位经验丰富的工匠,用敏锐的目光逐一掂量每块矿石的成色。在数据质量过滤中,这位“工匠”就是模型打分过滤。

模型打分过滤的核心思路是:训练一个二分类模型(质量分类器),让它学会区分“高质量文本”和“低质量文本”。训练数据由人工标注的样本构成——比如从维基百科、高质量书籍中抽取文本作为正样本,从粗筛后的垃圾文本中抽取负样本。训练好的分类器输出一个 0 到 1 之间的分数,表示文本的质量。工程师可以设置一个阈值(比如 0.7),分数高于阈值的数据被保留,低于阈值的被丢弃。

模型打分过滤的优势在于它能捕捉到规则难以覆盖的“微妙信号”。比如,“这是一个非常好的产品,我非常喜欢推荐给大家”和“这个商品垃圾死了,大家千万别买”这两句话,规则可能都判断为“正常文本”,但质量分类器能通过语义特征判断出第一句更像营销话术,第二句则可能是有价值的用户评价。当然,模型过滤的代价是需要额外的 GPU 推理成本,而且分类器本身的训练质量会直接影响过滤效果——就像一个工匠如果眼光不准,反而会误杀好矿。

工匠再好也需要磨刀。用从粗筛网留下的数据中抽出的 10 万条人工标注样本,训练一个轻量级分类器(如 BERT-base),就能实现接近 90% 的过滤准确率。这样的“工匠眼光”虽然比粗筛网贵,但精准度远超规则,能有效保留那些“潜藏的金矿”。

质量分=P(高质量∣文本)=σ(wTϕ(文本))\text{质量分} = P(\text{高质量} \mid \text{文本}) = \sigma(\mathbf{w}^T \phi(\text{文本}))
这个公式表示:质量分类器对输入的文本提取特征($\phi$),经过线性变换和 Sigmoid 函数($\sigma$)后,输出一个 0 到 1 之间的概率值,作为该文本的质量分。
  1. 特征提取$\phi(\text{文本})$ 将文本转换成向量表示,可以是 TF-IDF 特征或预训练模型的嵌入向量。
    ϕ(文本)=表示学习(文本)∈Rd\phi(\text{文本}) = \text{表示学习}(\text{文本}) \in \mathbb{R}^{d}
  2. 线性打分$\mathbf{w}^T \phi$ 将特征向量加权求和,得到原始得分。权重 $\mathbf{w}$ 通过训练学习得到。
    wTϕ(文本)=加权特征和\mathbf{w}^T \phi(\text{文本}) = \text{加权特征和}
  3. 概率映射Sigmoid 函数 $\sigma$ 将原始得分压缩到 (0,1) 区间,便于设定阈值。
    σ(z)=11+e−z∈(0,1)\sigma(z) = \frac{1}{1 + e^{-z}} \in (0, 1)

MinHash去重

淘金者辛辛苦苦淘出一袋金粒后,很快就会遇到一个尴尬的问题:这条河上游的矿脉只有一处,你淘来的“金粒”其实都来自同一块矿石,分量一样、成色一样——重复了。如果你把同样的数据反复喂给大模型,模型会“过度学习”这些重复内容,就像背书背到吐的学生,反而学不会新知识。这时,你需要 MinHash 去重。

MinHash 是一种用于快速估计两个集合相似度的算法,特别适合处理大规模文本去重。它的数学基础是 Jaccard 相似度:对于两个文档 A 和 B(看作词的集合),它们的 Jaccard 相似度定义为交集大小除以并集大小。Jaccard 相似度越高,说明两个文档越相似。直接计算两两文档的 Jaccard 相似度在大规模数据上是不可行的(10 亿文档需要 10^18 次比较),MinHash 的巧妙之处在于:它用“哈希签名”来近似估计 Jaccard 相似度——就像用一小撮金粒的成色来代表整袋金子的纯度。

MinHash 的具体做法是:对每个文档,先用 k 个不同的哈希函数分别计算文档中每个词的哈希值,取每个哈希函数下的最小值作为签名的一部分,得到一个 k 维签名向量。两个文档的签名向量中相同位置相等的比例,就是 Jaccard 相似度的无偏估计。比如文档 A 和 B 的 Jaccard 相似度为 0.8,它们的 MinHash 签名在 128 个哈希函数下约有 102 个位置相同。通过将签名相似度超过阈值(如 0.85)的文档判定为近重复,就可以用 LSH(局部敏感哈希)将相似文档聚簇,然后在簇内删除多余的副本。

MinHash 的身影在大模型数据工程中无处不在。比如 RedPajama 数据集在构建时就用 MinHash 对 Common Crawl 语料进行了去重,降低了约 20% 的数据规模。这就像淘金者把重复的金粒归拢成一份,既确保了“金子”的纯度,又避免了重复劳动。

J(A,B)=∣A∩B∣∣A∪B∣≈1k∑i=1kI(himin(A)=himin(B))J(A, B) = \frac{|A \cap B|}{|A \cup B|} \approx \frac{1}{k} \sum_{i=1}^{k} \mathbb{I}(h_i^{min}(A) = h_i^{min}(B))
这个公式揭示了 MinHash 的核心原理:两个文档的 Jaccard 相似度,近似等于它们 MinHash 签名中相同位置的比例。$h_i^{min}$ 表示第 i 个哈希函数在文档上取的最小哈希值。
  1. 集合表示将文档 A 和 B 分别转化为词的集合,$\cup$ 表示并集,$\cap$ 表示交集。
    A∩B={同时出现在 A 和 B 中的词}A \cap B = \{\text{同时出现在 A 和 B 中的词}\}
  2. Jaccard 相似度交集大小除以并集大小,衡量两个集合的相似程度,值域为 [0,1]。
    J(A,B)=∣A∩B∣∣A∪B∣∈[0,1]J(A, B) = \frac{|A \cap B|}{|A \cup B|} \in [0, 1]
  3. MinHash 签名估计用 k 个哈希函数对每个集合求最小哈希值,得到一个 k 维签名向量。
    himin(A)=min⁡x∈Ahi(x)h_i^{min}(A) = \min_{x \in A} h_i(x)
  4. 无偏估计两个签名向量中相等位置的比例,恰好是 Jaccard 相似度的无偏估计。
    J(A,B)≈1k∑i=1kI(himin(A)=himin(B))J(A, B) \approx \frac{1}{k} \sum_{i=1}^{k} \mathbb{I}(h_i^{min}(A) = h_i^{min}(B))

SimHash去重

如果说 MinHash 是把文档看成“词的集合”来比较,那么 SimHash 就是从另一个角度思考问题:把整篇文档压缩成一个 64 位或 128 位的“指纹”,然后通过比较指纹来判定相似度。这就像淘金者给每块金子称重记录——重量完全相同的自然是同一块金子,重量接近的也值得怀疑是同源矿脉。

SimHash 的算法流程是:对文档中的每个词计算一个 64 位的普通哈希值(如 MD5),然后根据词频加权,将每个词的哈希位向量的 +1 与 -1 值累加到一个 64 维的向量上。最后,将向量中正数位置设为 1、负数位置设为 0,得到文档的 64 位指纹。两个文档的海明距离(指纹中不同位的个数)越小,说明它们越相似。通常,海明距离 ≤ 3 被认为是一对近重复文档。

SimHash 的独特优势在于它捕捉的是“文档级”的相似度。比如,一篇新闻报道被稍作删改后重新发布,MinHash 的集合比较可能因为词的变化而低估其相似度,但 SimHash 的加权指纹会对微小的文字改动保持鲁棒——海明距离可能只差 1-2 位。代价是 SimHash 对“短文本”不太友好,因为短文本的词太少,指纹区分度不够。这就像称重法对“金粒”有效,但对“金粉”就无能为力了。

在实际的大规模数据清洗中,MinHash 和 SimHash 常常被组合使用:MinHash 擅长处理“集合视角”的重复(比如同一篇文章的转载),SimHash 擅长处理“文本视角”的相似(比如新闻稿的改写)。淘金者既带粗筛网又带精密天平,才能应对不同形态的“重复金粒”。

SimHash(文档)=sgn(∑w∈文档weight(w)×hash(w))∈{0,1}64\text{SimHash}(\text{文档}) = \text{sgn}\left(\sum_{w \in \text{文档}} \text{weight}(w) \times \text{hash}(w)\right) \in \{0, 1\}^{64}
这个公式展示了 SimHash 指纹的生成过程:每个词加权哈希后累加,取符号后得到 64 位指纹。海明距离衡量两个文档指纹中不同位的数量,距离越小越相似。
  1. 词哈希加权对每个词计算 64 位哈希向量,并按词频加权。$\text{hash}(w)$ 返回一个 ±1 组成的向量。
    hi(w)=±1,根据哈希值决定h_i(w) = \pm 1 \text{,根据哈希值决定}
  2. 向量累加将所有词的加权哈希向量逐位累加,得到一个 64 维的整数向量。
    Vj=∑wweight(w)×hj(w),j=1…64V_j = \sum_{w} \text{weight}(w) \times h_j(w) \text{,} j = 1 \ldots 64
  3. 符号函数取指纹将累加结果中正数置 1、负数置 0,得到最终的 64 位指纹。
    sgn(Vj)={1如果 Vj>00如果 Vj≤0\text{sgn}(V_j) = \begin{cases} 1 & \text{如果 } V_j > 0 \\ 0 & \text{如果 } V_j \le 0 \end{cases}
  4. 海明距离统计两个指纹中不同位的个数,距离不超过阈值则判定为近重复。
    d(fingerprint1,fingerprint2)=∑j=164I(fp1[j]≠fp2[j])d(\text{fingerprint}_1, \text{fingerprint}_2) = \sum_{j=1}^{64} \mathbb{I}(\text{fp}_1[j] \ne \text{fp}_2[j])

多阶段流水线

淘金者如果只有一把筛网,效率必然低下;如果只用工匠眼光,成本又太高。真正的淘金高手,会把整个流程设计成一条流水线:先用粗筛网快速去除大块杂物,再用工匠精选高价值矿石,最后用天平称重去重。在大模型数据工程中,把不同过滤方法组合成多阶段流水线,是数据质量工程的灵魂。

多阶段流水线的设计思想可以概括为“由粗到精、层层递进”。第一阶段的规则过滤成本最低、吞吐量最大,通常能过滤掉 40%-60% 的明显低质量数据;经过第一阶段“瘦身”后的数据量大幅减少,此时可以放心地使用模型打分——因为要处理的数据量小了很多,GPU 推理成本总体可控;最后进行 MinHash 去重,确保数据集中没有近重复样本。

阶段之间的衔接也需要精心设计。比如,规则过滤的阈值应当设置得“宽松”一些,宁可错杀一些边缘数据,也不要把太多垃圾留给后续阶段;模型打分阶段的训练数据应该来自第一阶段过滤后的数据分布,否则分类器会“水土不服”;去重阶段的相似度阈值需要根据任务调整——训练通用模型时更激进(去得多)、训练垂直领域模型时更保守(保留更多)。经典的 RedPajama、RefinedWeb 等开源数据集,都是这一流水线设计的实践者。

流水线设计的精髓在于“总成本可控”。假设你有 10TB 的原始数据,第一阶段规则过滤用纯 CPU 处理,成本几乎为零;第二阶段模型打分 GPU 处理成本较高,但此时数据量已减半,实际需要处理的只有 5TB;第三阶段同样只面对 2-3TB 的“半成品”。这样一来,整体成本远低于直接用模型处理全部 10TB 数据。

流水线通量=min⁡(规则过滤率,模型打分率,去重率)×原始数据量\text{流水线通量} = \min\left(\text{规则过滤率},\text{模型打分率},\text{去重率}\right) \times \text{原始数据量}
这个公式说明:多阶段流水线的最终输出量,取决于三个阶段各自保留率的乘积。任何一环的“低效”都会成为瓶颈,就像淘金流水线上最慢的环节决定了整个工坊的日产量。
  1. 每阶段保留率每个阶段的保留率等于该阶段保留的数据比例。规则过滤可能保留 50%,模型打分保留 40%,去重保留 60%。
    保留率1=0.5,保留率2=0.4,保留率3=0.6\text{保留率}_1 = 0.5,\text{保留率}_2 = 0.4,\text{保留率}_3 = 0.6
  2. 总体保留率三个阶段保留率的乘积,就是流水线的整体通过率。
    总体保留率=∏i=13保留率i=0.5×0.4×0.6=0.12\text{总体保留率} = \prod_{i=1}^{3} \text{保留率}_i = 0.5 \times 0.4 \times 0.6 = 0.12
  3. 输出规模原始数据量乘以总体保留率,得到最终的高质量数据量。
    输出=0.12×原始数据量\text{输出} = 0.12 \times \text{原始数据量}

评估与权衡

淘金者如何判断自己的工艺流程是否有效?答案很简单:看他最终淘到的金子纯度有多高、产量有多大。同样,数据质量过滤也需要一套评估标准,来衡量“过滤效果”是否达标。

评估过滤效果的常用指标包括:过滤后数据的质量分布(用模型打分器对过滤前后数据分别打分,看高分数据占比是否显著提升)、下游任务性能(用过滤后的数据训练一个小模型,对比其在 GLUE 等基准上的表现)、以及去重率(用 MinHash 估计过滤后数据中重复样本的比例是否降至合理范围)。这些指标就像金子的“成色检测”,确保过滤不是“杀敌一千、自损八百”——既要剔除垃圾,又不能误删有价值的样本。

不过,质量与规模的平衡是数据质量过滤最微妙的权衡。过滤太激进,虽然数据质量高,但数据量急剧下降,可能不足以支撑千亿参数大模型的训练需求——就像淘金者只留下 10 克纯度极高的金粒,却连一条金项链都打不成。过滤太保守,数据量大了,但杂质也多了,模型训练效果反而变差。因此工程师需要根据算力预算和模型规模动态调整:大模型需要更多数据时,适度放宽过滤阈值;资源有限时,则更注重数据精而不求多。

回到淘金的比喻:一个聪明的淘金者不会无止境地追求“最纯的金”,而会综合考虑自己的需求——如果要打一套首饰,纯度 99% 就足够了,追求 99.99% 反而浪费了太多金料。实务中,数据质量问题“60 分以上再改进的收益有限”,与其死磕过滤精度,不如把精力放在数据多样性和配比上。这就像淘金者同时关注不同河段的矿藏分布,而不是只盯着一条河猛挖。

模型最终效果≈f(数据质量,数据规模)=α×质量分+β×log⁡(规模)\text{模型最终效果} \approx f(\text{数据质量},\text{数据规模}) = \alpha \times \text{质量分} + \beta \times \log(\text{规模})
这个公式揭示了质量与规模对模型效果的共同作用:质量和规模都是关键因素,但对数函数说明“数据量翻倍”带来的收益递减,而“质量提升”的收益是线性的——这就是追求高质量数据的根本原因。
  1. 质量贡献$\alpha \times \text{质量分}$ 表示数据质量对模型效果的线性贡献,质量越高效果越好。
    质量贡献=α×平均质量分\text{质量贡献} = \alpha \times \text{平均质量分}
  2. 规模贡献$\beta \times \log(\text{规模})$ 表示数据量以对数形式贡献——从 1 亿到 2 亿数据效果提升明显,但从 100 亿到 200 亿提升就有限了。
    规模贡献=β×log⁡(数据量)\text{规模贡献} = \beta \times \log(\text{数据量})
  3. 权衡决策当数据规模已达瓶颈时,提升数据质量的边际收益更大。
    ∂效果∂质量=α>∂效果∂规模=β规模\frac{\partial \text{效果}}{\partial \text{质量}} = \alpha > \frac{\partial \text{效果}}{\partial \text{规模}} = \frac{\beta}{\text{规模}}

AI 应用场景

场景一:大模型预训练数据清洗平台

阶段:数据准备阶段

解决的问题:某科技公司需要从互联网抓取 50TB 原始网页数据训练行业大模型,但原始数据中夹杂着大量广告页、机器生成的垃圾文本和重复转载内容。传统人工清洗方式需要数百人月且标准不一,无法满足工程需求。

具体案例:数据工程师搭建了一条多阶段过滤流水线:第一阶段用规则过滤(URL 黑名单、文本长度过滤、乱码检测)将 50TB 压缩至 25TB;第二阶段用基于 RoBERTa 的质量分类器对剩余数据打分,保留分数高于 0.7 的 10TB;第三阶段用 MinHash 去重,最终得到 8TB 高质量训练数据。用这批数据训练的 7B 模型在 C-Eval 上比未过滤版本提升了 12.5%。

场景二:垂直领域数据集构建

阶段:领域数据适配

解决的问题:某医疗 AI 公司需要从医学文献、病例库中构建高质量的训练数据,但原始语料中大量存在相同论文的不同版本、被篡改的复制内容,以及非医学的噪音文本。直接训练会导致模型“背书效应”——对重复内容过度自信。

具体案例:团队先用规则过滤筛除所有不含医学关键词的文档,再用领域微调的质量分类器(正样本为权威医学期刊摘要)打分,最后用 SimHash 对相似度超过 0.9 的文献进行去重,保留下 5 万篇高质量文献。微调后的医学问答模型在专业考试集上准确率提升了 9 个百分点。

Python 演示

python65 行
import reimport hashlibfrom collections import Counterfrom typing import List, Set, Tuple def rule_based_filter(text: str, min_len: int = 50) -> bool:    if len(text) < min_len:        return False    garbage_chars = text.count("\uFFFD")    if garbage_chars / max(len(text), 1) > 0.01:        return False    if len(set(text)) / max(len(text), 1) < 0.3:        return False    return True def shingle(text: str, k: int = 5) -> Set[str]:    text = re.sub(r"\s+", " ", text).strip()    return {text[i:i+k] for i in range(max(0, len(text) - k + 1))} def minhash_signature(shingles: Set[str], num_hashes: int = 64) -> List[int]:    signature = []    for seed in range(num_hashes):        hash_func = lambda s: int(hashlib.md5((str(seed) + s).encode()).hexdigest()[:8], 16)        signature.append(min(hash_func(s) for s in shingles))    return signature def jaccard_estimate(sig_a: List[int], sig_b: List[int]) -> float:    return sum(1 for a, b in zip(sig_a, sig_b) if a == b) / len(sig_a) def deduplicate(docs: List[str], threshold: float = 0.8) -> List[str]:    clean_docs = [d for d in docs if rule_based_filter(d)]    if len(clean_docs) <= 1:        return clean_docs    signatures = []    for doc in clean_docs:        shingles = shingle(doc)        if not shingles:            signatures.append([])        else:            signatures.append(minhash_signature(shingles))    unique_indices = []    for i in range(len(clean_docs)):        is_dup = False        for j in unique_indices:            if signatures[i] and signatures[j]:                if jaccard_estimate(signatures[i], signatures[j]) >= threshold:                    is_dup = True                    break        if not is_dup:            unique_indices.append(i)    return [clean_docs[i] for i in unique_indices] if __name__ == "__main__":    raw_docs = [        "人工智能正在改变世界。大模型需要高质量数据,这至关重要。",        "人工智能正在改变世界。大模型需要高质量数据,这至关重要。重复内容会浪费算力。",        "今天天气不错,适合出去走走。",        "购物促销!!!点击链接领取优惠券,超值大礼包。",        "自然语言处理是深度学习的重要研究方向,应用于机器翻译、问答系统等场景。"    ]    result = deduplicate(raw_docs, threshold=0.75)    print(f"原始文档数: {len(raw_docs)}")    print(f"过滤后保留: {len(result)}")    for doc in result:        print(f"  - {doc[:30]}...")
目的:实现了一个简化的多阶段数据质量过滤流水线,包含规则过滤、MinHash 去重,用于从少量原始文档中筛选出高质量且不重复的文本。
  1. def rule_based_filter(text, min_len=50):定义规则过滤函数,检查文本长度、乱码比例和字符多样性,模拟“粗筛网”。
  2. garbage_chars = text.count("\uFFFD")统计替换字符数量,判断是否含有乱码。
  3. def shingle(text, k=5):将文本切分为 k-gram 子串集合,这是 MinHash 的输入特征。
  4. signature.append(min(hash_func(s) for s in shingles))对每个哈希函数求最小哈希值,生成签名向量的一个维度。
  5. jaccard_estimate(sig_a, sig_b)用签名比较估计两个文档的 Jaccard 相似度。
  6. if jaccard_estimate(...) >= threshold:相似度超过阈值则判定为重复文档,丢弃后到者。
  7. clean_docs = [d for d in docs if rule_based_filter(d)]先执行规则过滤,再进入去重阶段,体现流水线设计。

常见误区

误区“深度学习这么强,谁还用正则表达式过滤数据?直接用质量分类器不就行了?规则过滤太 primitive 了。”

正解规则过滤不仅没过时,而且是多阶段流水线不可或缺的第一步。它最大的优势是成本极低、速度极快,能在 GPU 推理之前先砍掉 40%-60% 的垃圾数据。如果直接用模型打分处理 50TB 全量数据,GPU 成本可能是规则过滤的百倍。淘金者绝不会直接用工匠去掂量每一粒泥沙——先用筛网抖掉大石块才是最经济的选择。

为何常见:深度学习的强大让初学者容易忽略“简单方法”的价值,但数据工程的核心目标是在约束条件下(算力、时间、预算)实现最优效果。

误区“MinHash 和 SimHash 都是去重,原理应该差不多吧?选一个用就行。”

正解两者虽然都用于近似去重,但底层思路完全不同。MinHash 基于“集合”视角,将文档转化为词集合后比较集合相似度,适合检测“词级别”的重复(如转载、拼凑)。SimHash 基于“向量”视角,将文档压缩成固定长度的指纹,对“改写”更鲁棒。在实际工程中,两者常常互补使用,而不是二选一。就像淘金者既会筛金粒也会称金粉,针对不同的“重复形态”用不同的工具。

为何常见:两者名称相似、目标相近,初学者容易混淆;只有深入理解它们的数学基础才会发现差异。

误区“质量分阈值越高,留下的数据质量一定越好。把阈 值设 0.95 肯定比 0.7 好。”

正解这忽略了两个问题。第一,阈值过高会导致数据量锐减,可能不足以支撑大模型的训练需求——就像你把金子提纯到 99.999%,但只剩下一克,打不成项链。第二,质量分类器本身有误差,高分数据不一定代表“适合任务”的数据,可能只是与训练分布接近。合理的做法是根据下游任务效果调整阈值,找到一个质量与规模的平衡点。

为何常见:人类直觉倾向于“越纯粹越好”,但工程中必须在多个约束之间寻找最优解,而不是追求单指标最大化。

误区“数据经过 MinHash 去重后,重复问题就彻底解决了,不需要再关注去重率。”

正解去重是“概率性”的,MinHash 只能近似估计相似度,存在漏网之鱼和误伤。此外,不同的数据源可能有不同的重复模式——跨语言重复、部分重叠的文档、被拼接的片断等,单次去重很难全部覆盖。专业的数据流水线会多次去重:在规则过滤后做一次粗去重,在模型打分后做一次精细去重。同时,每次数据更新后都需要重新评估去重率。

为何常见:去重算法看起来“一键完成”,但实际效果取决于阈值、哈希函数数量和数据形态,需要持续监控和调优。

面试题

Q1. 在多阶段数据过滤流水线中,启发式规则过滤通常放在最前面的主要原因是?

单选题 · mid

  • A.规则过滤精度最高
  • B.规则过滤成本最低�THD最大← 正确答案
  • C.规则过滤需要GPU训练
  • D.规则过滤能处理语义

答案:B。规则过滤基于字符串统计和正则匹配,不需要模型推理,吞吐量惊人,能快速砍掉 50% 左右的垃圾数据。放在最前面可以让后续阶段处理更少的数据,降低总体成本。选项 B 正确。A 错误,规则过滤精度最低;C 错误,规则过滤不需要训练;D 错误,规则过滤无法理解语义。

Q2. MinHash 去重的数学基础是估计两个文档的什么指标?

单选题 · mid

  • A.余弦相似度
  • B.编辑距离
  • C.Jaccard 相似度← 正确答案
  • D.海明距离

答案:C。MinHash 的核心思想是用哈希签名估计两个集合的 Jaccard 相似度(交集大小除以并集大小)。C 正确。余弦相似度通常用于向量化文本,编辑距离用于精确字符串匹配,海明距离是 SimHash 的判定指标,都不是 MinHash 的数学基础。

Q3. 关于 SimHash 去重,下列说法正确的是?

多选题 · mid

  • A.SimHash 将文档压缩为定长指纹← 正确答案
  • B.SimHash 用海明距离判定相似← 正确答案
  • C.SimHash 适合短文本去重
  • D.SimHash 无需计算词权重

答案:AB。SimHash 将文档压缩为 64 位或 128 位指纹,通过比较指纹的海明距离来判定相似度,A 和 B 正确。SimHash 对短文本不友好(C 错),因为词太少导致指纹区分度不足;SimHash 需要按词频加权(D 错),权重是算法的重要组成部分。

Q4. 以下哪项不是低质量训练数据的典型形态?

单选题 · easy

  • A.机器生成的乱码文本
  • B.重复刷屏的无意义内容
  • C.精心编辑的百科条目← 正确答案
  • D.广告垃圾和营销话术

答案:C。精心编辑的百科条目恰恰是高质量数据的代表,常被用作质量分类器的正样本。乱码、刷屏、广告都属于典型低质量文本,需要通过规则过滤或模型打分剔除。淘金比喻中,百科条目是“真金”,其他三类是“泥沙”。

Q5. 质量分类器的训练数据中,正样本通常来源于?

单选题 · mid

  • A.互联网抓取的原始网页
  • B.维基百科、书籍等权威语料← 正确答案
  • C.所有模型生成的合成文本
  • D.用户评论中的短文本

答案:B。质量分类器的正样本需要“金标准”,维基百科、出版书籍等经过人类编辑校验的语料是理想选择。互联网原始网页和用户评论良莠不齐,不适合直接作为正样本(A、D 错);合成文本需要严格筛选和控制(C 错)。

Q6. 关于 Jaccard 相似度与 MinHash 的关系,下列说法正确的是?

多选题 · hard

  • A.Jaccard 相似度为 0.8 的两文档,MinHash 签名约 80% 位置相同← 正确答案
  • B.MinHash 是无偏估计量← 正确答案
  • C.哈希函数数量越多估计越准确← 正确答案
  • D.MinHash 能精确计算 Jaccard 值

答案:ABC。MinHash 的核心性质是:两个集合的 Jaccard 相似度等于其最小哈希值相等的概率,因此 A 正确,且是无偏估计(B 正确)。哈希函数越多,样本越大,估计越准(C 正确)。但 MinHash 是近似方法而非精确计算(D 错误),它用采样代替全量计算。

Q7. 在多阶段过滤流水线中,“总成本可控”主要得益于?

单选题 · easy

  • A.每阶段处理的数据量递减← 正确答案
  • B.每阶段都用最贵的模型
  • C.所有阶段并行执行
  • D.跳过规则阶段直接去重

答案:A。流水线设计“由粗到精”:规则过滤先砍掉一半数据,模型打分只需处理剩余部分,去重更是只面对“半成品”。每阶段数据量递减,最终成本远低于全量处理。所以 A 正确。B 恰好相反,阶段越靠前成本越低;C 并非流水线的核心优势;D 会浪费 GPU 成本。

Q8. 使用 SimHash 时,两个文档指纹的海明距离为 2,这说明?

单选题 · mid

  • A.两个文档一定相同
  • B.两个文档可能高度相似← 正确答案
  • C.两个文档完全不同
  • D.指纹长度是 2 位

答案:B。海明距离衡量两个指纹中不同位的个数。距离为 2 说明 64 位中只有 2 位不同,属于高度相似(通常 ≤3 视为近重复)。但 SimHash 是近似方法,不保证“一定相同”(A 错);C 与事实相反;D 理解错误,海明距离是差值而非指纹长度。

Q9. 关于多阶段流水线各阶段的衔接,以下哪些设计是合理的?

多选题 · hard

  • A.模型打分训练数据取自规则过滤后的分布← 正确答案
  • B.规则过滤阈值设置宽松以减少遗漏← 正确答案
  • C.去重阈值对通用模型更激进← 正确答案
  • D.所有阶段使用完全相同的阈值

答案:ABC。分类器需要适配实际数据分布,取自上一阶段输出是正确做法(A);规则阈值宽松让后续阶段“精挑细选”是合理策略(B);通用模型更注重多样性,去重更激进(C)。D 错误,不同阶段目标不同,阈值应差异化设置。

Q10. 数据质量过滤的最终目标是?

单选题 · easy

  • A.让数据量尽量大
  • B.让数据质量尽量高
  • C.在质量与规模间取得平衡← 正确答案
  • D.完全消除所有重复数据

答案:C。数据质量过滤不是追求单指标最优,而是在质量(杂质少)与规模(数据量足)之间寻找平衡,让模型效果最大化。只追求量大或只追求质高都有问题(A、B 错);重复数据也只能近似消除而非完全消除(D 错)。