大模型预训练数据工程:清洗、去重与配比策略

大模型预训练数据工程:清洗、去重与配比策略 数据质量决定模型上限这句话在大模型预训练领域已经被反复验证。之前在跟朋友聊大模型训练时经常遇到一个现象同样的模型结构、同样的训练算力只因为预训练数据的清洗程度和配比策略不同最终模型在下游任务上的表现能拉开好几个百分点。这也让“数据工程”从边缘环节变成了大模型开发的核心议题。本篇文章围绕斯坦福大模型开发课 EP14 的内容展开系统梳理高质量预训练数据构建的三大环节数据清洗、去重与配比策略。无论你是在准备自己的小规模预训练实验还是在思考企业级大模型的数据底座建设这篇文章都能给你一条可执行的路径。文章会从核心概念讲起逐步过渡到清洗流水线设计、去重算法实现、配比调优实践最后补充常见问题排查和工程化建议。1. 预训练数据为什么是大模型的“命运方向盘”1.1 大模型训练的三要素现代大模型训练离不开三个核心要素算力、模型架构、训练数据。算力决定了你能在多大参数量上做训练模型架构决定了能力上限的理论边界而训练数据则决定了模型最终记住什么、学会什么、在什么场景下表现好。很多人容易把注意力放在模型参数规模和算力规模上忽略了数据的作用。实际上在模型架构和算力接近的前提下数据的质量、覆盖度和分布比例直接决定了模型的下游表现。一个典型例子是如果预训练语料里代码占的比例过低模型在代码生成任务上的能力会明显偏弱如果语料里某个语言或领域的内容重复度过高模型在这个方向上的泛化能力会受到抑制。1.2 数据质量如何影响模型能力数据质量不是一个抽象概念它会直接体现在模型行为上。低质量数据会带来三类典型问题噪声信息被模型“记住”网页中的垃圾字符、乱码、广告模板、无意义重复文本如果清洗不干净模型可能生成杂乱无章的文本。重复数据导致训练偏置重复文本在训练时会被多次采样相当于变相提高了这些样本的权重导致模型在重复内容上过拟合在稀疏内容上表现不足。有害内容影响安全性含暴力、违法、偏见等内容如果没有经过过滤模型会学到不良的生成模式这也是“模型安全对齐”之前的第一道关卡。所以数据清洗不仅仅是“把数据弄干净”它实际上是在塑造模型的行为边界。1.3 高质量预训练数据的核心维度从斯坦福这门课以及工业界大模型数据工程的经验来看高质量预训练数据可以从四个维度去衡量维度说明典型问题质量文本是否通顺、语义是否完整、是否存在噪声乱码、解析错误、纯标签文本多样性覆盖的领域、体裁、语言、风格是否足够广单一来源占比过高去重率重复文本在多大规模上被识别并删除新闻转载、模板页面大量重复配比不同来源和领域的数据按什么比例混合代码、百科、书籍、网页的权重失衡这四个维度不是独立存在的质量和去重是基础多样性和配比决定模型的风格和泛化能力。下面依次展开。2. 预训练数据里都有哪些“坑”2.1 原始数据的常见噪声类型不同来源的数据噪声形式差别很大。以最常见的两类来源为例网页数据包含 HTML 标签、导航栏、页脚、广告、脚本代码、乱码编码还需要处理重复转载和近似重复页面。书籍与论文存在扫描版 OCR 错误、页眉页脚、参考文献重复引用、目录页、致谢页等非正文内容。实际预处理流程中通常要把这些数据先做一轮“格式清洗”比如去掉 HTML 标签、解码修复、压缩空白字符、去除控制字符然后再进入更细粒度的质量过滤阶段。2.2 数据来源的特征差异不同来源的数据对模型能力的贡献不同。Web 网页覆盖面广、多样性高但噪声也最多。Common Crawl 这类网页快照数据是多数大模型预训练的主体语料。百科类数据结构化程度高、知识密度高、错误率低但总量有限。书籍数据长篇上下文信息丰富能显著增强模型的长期依赖能力但版权和质量筛选成本高。代码数据能增强模型推理、逻辑和工具调用能力GitHub 等代码仓库存量大但需要做许可证过滤。专业论文/技术文档对科学知识、专业领域任务帮助明显但数量相对稀少通常需要和其他数据混合使用。这引出了配比的问题语料结构怎么设计才能让模型在保持通用能力的同时在特定领域也表现出色。2.3 课程 EP14 的定位斯坦福大模型开发课CS25 系列延伸内容EP14 的核心讨论点是预训练数据不是简单的“堆积”而是一个需要精细设计的产品。课程里强调了一个观点——数据的配比和清洗策略在某些情况下对模型效果的影响甚至超过模型架构微调。这也是预训练数据工程逐渐成为一个独立研究方向的原因。对于正在自建大模型的团队来说这部分内容相当于“地基里的钢筋”必须在一开始就规划好。3. 数据清洗流水线从原始语料到高质量文本数据清洗流水线通常包含多个阶段每个阶段处理一类问题。下面给出一个工程上通用的流水线设计。3.1 总体流水线架构一条比较完整的预训练数据清洗链路如下原始语料 → 格式解析 → 编码统一 → 内容抽取 → 质量过滤 → 去重 → 配比混合 → 数据版本归档每一步都有明确的目标格式解析把不同来源的数据解析成统一的结构如 JSON Line。编码统一统一为 UTF-8修复乱码。内容抽取从 HTML、PDF 等载体中提取正文文本。质量过滤用规则或模型过滤低质量文档。去重删除完全重复或近似重复的文档。配比混合按设计好的比例把不同领域数据混合成最终训练集。数据版本归档记录每一个版本的清洗逻辑和数据统计保证实验可复现。3.2 一个可运行的清洗流水线示例下面用 Python 实现一个简化版的清洗流水线重点演示思路。假设我们的原始数据是一个 JSON Lines 文件每行包含text和source两个字段。import json import re import html import hashlib class DataCleaner: def __init__(self): # 定义一些 URL 和乱码的正则模式 self.url_pattern re.compile(rhttp[s]?://(?:[a-zA-Z]|[0-9]|[$-_.]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))) self.control_pattern re.compile(r[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]) self.whitespace_pattern re.compile(r\s) def clean_text(self, text: str) - str: # 1. HTML 反转义 text html.unescape(text) # 2. 去掉 URL text self.url_pattern.sub( , text) # 3. 去掉控制字符 text self.control_pattern.sub( , text) # 4. 压缩连续空白字符 text self.whitespace_pattern.sub( , text) # 5. 去掉首尾空白 text text.strip() return text def compute_hash(self, text: str) - str: # 用于精确去重的哈希值 return hashlib.md5(text.encode(utf-8)).hexdigest() def process_file(self, input_path: str, output_path: str): seen_hashes set() total 0 passed 0 with open(input_path, r, encodingutf-8) as fin, \ open(output_path, w, encodingutf-8) as fout: for line in fin: line line.strip() if not line: continue try: record json.loads(line) except json.JSONDecodeError: continue text record.get(text, ) source record.get(source, ) total 1 # 1. 基础清洗 text self.clean_text(text) # 2. 质量过滤长度过短则丢弃 if len(text) 100: continue # 3. 过滤 URL 占比过高的内容 url_ratio len(self.url_pattern.findall(text)) / max(len(text), 1) if url_ratio 0.2: continue # 4. 精确去重 doc_hash self.compute_hash(text) if doc_hash in seen_hashes: continue seen_hashes.add(doc_hash) # 5. 输出清洗后的数据 output_record { text: text, source: source, hash: doc_hash } fout.write(json.dumps(output_record, ensure_asciiFalse) \n) passed 1 print(f处理完成总计 {total} 条通过 {passed} 条去重和过滤比例 {1 - passed/total:.2%}) if __name__ __main__: cleaner DataCleaner() cleaner.process_file(raw_data.jsonl, clean_data.jsonl)这个示例虽然简单但涵盖了清洗流水线的核心逻辑基础清洗、质量过滤、精确去重。你可以把它扩展到 Spark 或 Ray 等分布式框架上用同样的逻辑处理 PB 级数据。3.3 质量过滤的常用规则在工程实践中质量过滤通常会叠加多条规则具体包括文本长度过滤过短的文本信息量不足通常建议过滤掉少于 100 字符的内容。标点符号比例过滤正常文本的标点比例相对稳定异常偏低或偏高都可能是噪声。重复字符比例过滤连续重复的字符或单词往往来自模板或机器生成内容。语言识别过滤用 fastText 的 language identification 模型判断文档语言筛掉目标语言之外的文档。淫秽/敏感词过滤通过敏感词库过滤有害内容。困惑度PPL过滤用一个小型语言模型计算文本困惑度困惑度过高或过低的文本都要谨慎处理。import re def is_mostly_repeated(text: str, threshold: float 0.3) - bool: 检查文本中有多少比例的内容是连续重复的字符 n-gram if len(text) 200: return False # 取 30 个字符作为一个块检查相邻块是否重复 block_size 30 repeated_chars 0 for i in range(0, len(text) - block_size, block_size): chunk text[i:iblock_size] next_chunk text[iblock_size:i2*block_size] if chunk next_chunk: repeated_chars block_size return repeated_chars / len(text) threshold # 使用示例 sample 哈哈哈 * 100 这是一段正常的内容 print(is_mostly_repeated(sample)) # True3.4 清洗流程的验证方式清洗流程写完之后怎么判断效果建议保留一份“清洗前与清洗后”的对比样本人工检查以下指标文本可读性是否明显提升。噪声文本占比是否下降。数据总量减少比例是否在合理范围通常是 20%-50%。目标领域数据是否被误删。在一个真实的数据项目中不要只关注“清洗掉多少数据”更要关注“有效数据保留了多少”。过度清洗会损失多样性这一点在大模型预训练中尤其需要注意。4. 去重策略别让模型反复背同一篇课文4.1 为什么预训练数据必须去重预训练本质上是一个海量文本上的概率分布学习过程。如果重复数据没有被去掉模型会为重复出现的文本分配更高的概率这会导致两个问题一遍又一遍地记忆重复内容浪费模型容量。在非重复的、稀疏的数据上学习不充分泛化能力下降。斯坦福 EP14 中提到一个经验值在亿级以上的预训练数据中不做去重会让模型在多个基准上的表现明显下降。因此去重是大模型预训练中不可跳过的一步。4.2 去重的三个层级根据重复的粒度去重可以分为三个层级层级处理对象常用方法解决什么问题文档级去重整篇文章全文哈希、SimHash、MinHash完全重复的文档段落/句子级去重段落、句子句子哈希、Bloom Filter文档内部嵌入了重复段落跨数据集去重多个数据集之间对所有数据集计算统一去重标识训练集与验证集互相污染这里尤其要注意跨数据集去重。很多团队在训练大模型时会在预训练语料里混入一些公开 benchmark 数据来“增强特定能力”。如果不去重验证集里的题目在预训练阶段已经被模型见过后续评估指标将严重失真。4.3 精确去重与模糊去重去重本质上可以分为精确去重和模糊去重两类。精确去重就是计算文档的哈希值比如 MD5、SHA256相同哈希直接删除。它的优点是简单高效缺点是只能处理完全相同的文本哪怕只修改了一个字也会被视为不同文档。模糊去重能处理“近似重复”的文本比如新闻网站转载同一篇报道时改写了开头和标题正文几乎一致或者网页模板中只有局部内容不同整体结构高度相似。这类文本用精确去重无法识别需要用 MinHash 或 SimHash 这类基于局部敏感哈希Locality-Sensitive Hashing, LSH的方法。4.4 MinHash 去重思路与实现MinHash 是处理大规模近似去重的主流算法。它的核心思想是把文本转换成“特征集合”再用多个随机哈希函数计算集合的最小哈希值通过最小哈希值相等来估计两个集合的相似度。下面是一个基于datasketch库的简化示例from datasketch import MinHash, MinHashLSH import jieba # 举例用中文分词英文场景可以直接用 n-gram def tokenize(text: str, k: int 5): 按字符 n-gram 切分适合中文场景 tokens set() text text.replace( , ) for i in range(len(text) - k 1): tokens.add(text[i:ik]) return tokens def build_minhash(text: str, num_perm: int 128): mh MinHash(num_permnum_perm) for token in tokenize(text): mh.update(token.encode(utf-8)) return mh # 构建 LSH 索引 lsh MinHashLSH(threshold0.8, num_perm128) documents { doc_1: 今天天气很好适合出去散步。, doc_2: 今天天气非常好很适合出门散步。, doc_3: 深度学习是机器学习的一个分支。, } for doc_id, text in documents.items(): mh build_minhash(text) lsh.insert(doc_id, mh) # 查询与 doc_1 近似重复的文档 query_mh build_minhash(documents[doc_1]) result lsh.query(query_mh) print(与 doc_1 相似的文档:, result)这段代码演示了如何对中文短文本做近似去重。在真实的海量数据场景中通常会把 MinHash 算法跑在 Spark 上对文档分桶banding用多个 band 的哈希碰撞来识别候选重复对再对候选对做精确的 Jaccard 相似度计算。4.5 去重指标怎么算去重效果可以用以下几个指标衡量文档级重复率重复文档数 / 总文档数。字节级重复率重复字节数 / 总字节数。由于重复文档通常较长字节级重复率往往高于文档级重复率。数据集间重叠率训练集与其他数据集之间的重复比例。一个健康的预训练数据版本文档级重复率通常要控制在 5% 以下。如果发现重复率过高需要回溯上游数据源从采集端增加去重逻辑。5. 数据配比策略给模型一份“营养均衡的食谱”5.1 数据配比是预训练中最容易低估的玄学数据配比是预训练数据工程中最需要实验验证的环节。不同领域、不同来源的数据按照什么比例混合会显著影响模型在不同任务上的表现。一个简单的实验思路是保持模型结构和训练步数不变只改变数据配比然后在多个下游任务上做评估。这类实验可以帮助团队找到最适合业务目标的数据混合方案。5.2 常见配比原则在预训练实践中有几个经验性的配比原则可以参考。多样性优先单一领域的数据比例不宜过高。即使某个领域的数据质量很好如果占比太高模型在该领域过拟合其他领域的通用能力会被削弱。质量与数量的平衡高质量数据百科、书籍、论文数量少但单条价值高网页数据数量多但单条价值相对低。通常会把高质量数据按一定过采样倍率重复多次再和网页数据混合。时间敏感数据适当降权新闻和论坛数据具有较强的时效性。如果目标模型不要求强时效性可以降低这类数据的权重把更多额度分配给长期稳定的知识型数据。5.3 配比调整实验的具体做法配比策略的调整通常需要配合小规模预训练实验。你可以用 1B 左右的小模型在 100B-200B token 的数据上训练先做配比验证再放大到完整规模的训练。下面是一个简单的配比计算示例假设你有四类数据网页、百科、书籍、代码。data_budget { web: 1000, # 单位GB原始数据量 wiki: 100, book: 200, code: 300, } # 定义一个目标配比按 token 占比 target_ratio { web: 0.65, wiki: 0.10, book: 0.10, code: 0.15, } # 假设每个数据源每 GB 约包含 token 数不同这里简化处理 tokens_per_gb { web: 150_000_000, wiki: 200_000_000, book: 180_000_000, code: 250_000_000, } source_tokens {k: v * tokens_per_gb[k] for k, v in data_budget.items()} total_tokens sum(source_tokens.values()) # 计算当前实际比例 actual_ratio {k: v / total_tokens for k, v in source_tokens.items()} print(当前 token 占比, {k: round(v, 4) for k, v in actual_ratio.items()}) # 简单计算需要过采样/降采样的倍率 for source in target_ratio: ratio target_ratio[source] / actual_ratio[source] print(f{source} 建议调整倍率{ratio:.2f})实际工程中你还需要考虑数据是否要重复采样epochs以及不同领域数据在训练中如何调度。有些团队采用“领域混合调度”的方式在训练过程中动态调整各领域数据的采样概率而不是在整个训练集里一次性混合。5.4 数据配比与课程中提到的“课程学习”结合斯坦福 EP14 里也讨论了数据顺序对训练效果的影响。除了静态配比数据的出现顺序也值得设计。例如早期训练阶段使用高质量、低噪声的百科、书籍数据帮助模型建立基础语言能力。中期加入网页数据扩大词汇和知识覆盖面。后期加入代码、数学等推理类专业数据增强逻辑能力。这种思路被称为“数据课程学习”。它和静态配比并不冲突而是在数据配比的基础上增加了时间维度。6. 数据质量的评估与持续优化6.1 离线评估指标在正式训练大模型之前如何判断当前数据版本是否合格可以通过以下离线指标做初步判断。指标评估方法目标参考语言困惑度 PPL用小型语言模型计算越低通常表示文本越流畅去重率MinHash 抽样检测文档级重复率 5%领域覆盖率分类器判断领域分布和目标配比基本一致噪声文档占比人工抽样 500-1000 条标注噪声比例噪声文档 5%有害内容占比分类器敏感词库趋近于 06.2 小模型预训练验证离线指标只能反映数据的表面质量数据是否适合模型训练最可靠的方式是跑一次小规模预训练实验。推荐流程用小参数量模型如 0.5B-1B加上完整的清洗、去重、配比流程。用 20B-50B token 训练。在下游任务如 C-Eval、MMLU、GSM8K、BBH上评估。对比不同数据版本的效果选择最优版本并放大训练。这种方法比直接全量训练发现问题成本低得多。6.3 数据飞轮持续收集“模型反馈”数据不是一次定型的。模型在小规模实验或者上线后会产出大量真实使用数据这些数据经过清洗、筛选、配比后又可以进入下一轮预训练或持续训练形成数据飞轮。操作要点记录每次训练的模型版本和数据版本。对模型输出做质量打分高分样本可以回流作为高质量语料。持续监控数据分布漂移特别是当产品面向特定用户群体时。7. 常见问题与排查思路在实际搭建预训练数据流水线时下面这些问题出现频率最高可以参考排查。问题现象常见原因解决思路清洗后有效数据太少过滤规则过于激进抽样检查被过滤数据放宽长度和标点限制条件去重后模型效果反而下降过度去除重复数据导致领域信息丢失保留 1%-3% 重复度特别是专业领域语料验证集指标虚高训练集与验证集没有做跨数据集去重对训练集和验证集做统一 MinHash 去重模型在特定领域表现差该领域数据占比太低提高领域权重或者将该领域数据过采样数据包含大量模板文本HTML 解析不完整正文抽取失败优化正文抽取模块增加模板检测规则清洗流程运行太慢单机处理 正则复杂换用 Spark/Ray 分布式处理正则表达式预编译中文文本被误判为噪声分词或过滤规则没适配中文调整标点规则加入中文字符占比判断一个容易忽略的坑是清洗和去重逻辑在数据量小的时候运行正常但扩展到大规模数据时会出现性能问题。建议从一开始就用分布式框架设计流水线避免后期重构。8. 最佳实践与工程建议8.1 数据版本管理预训练数据的清洗逻辑、过滤参数、配比信息都应该纳入版本管理。推荐做法数据文件命名包含日期和版本如web_20250201_v3.jsonl。清洗代码用 Git 管理每次修改记录变更原因。每一次数据版本发布前输出一份数据报告包括总量、来源分布、去重率、过滤率。记录数据版本与模型版本的对账关系方便回溯问题。8.2 清洗流水线可观测性清洗流水线在运行过程中需要“可观测”否则出现问题很难定位。建议埋点以下信息每个处理阶段的输入输出数量。每个阶段的过滤原因统计。抽样保留原始数据和处理后数据的对照样本。处理时间与吞吐量监控。这些信息不仅帮助排查问题也能够让你在调整过滤规则时看清楚每个规则的实际影响范围。8.3 并行处理与性能优化数据清洗不是一个一次性的任务它需要在不同数据集上反复执行。建议用 Spark 或 Ray 来构建分布式流水线。下面是一个用 Spark 做分布式精确去重的伪代码示例from pyspark.sql import SparkSession from pyspark.sql.functions import col, md5, count spark SparkSession.builder.appName(dedup).getOrCreate() df spark.read.json(s3://your-bucket/raw_data/) # 添加哈希列 df df.withColumn(text_hash, md5(col(text))) # 按哈希去重保留任意一条 dedup_df df.dropDuplicates([text_hash]) # 统计去重信息 dedup_df.agg(count(*).alias(unique_count)).show() dedup_df.write.mode(overwrite).json(s3://your-bucket/clean_data/)这个流程虽然简单但在 PB 级数据场景下已经能完成大部分精确去重工作。对于 MinHash 近似去重可以在 Spark 上使用datasketch的分布式版本或者用 Spark 自带的MinHashLSH方法。8.4 安全与合规注意事项数据清洗环节还需要关注安全与合规边界使用公开数据集时检查许可协议特别是代码类数据。涉及用户生成内容时做好个人隐私信息的脱敏处理。涉及删除、修改、覆盖数据时保留原始数据备份避免不可逆操作。数据清洗脚本应遵循最小权限原则只访问需要的存储路径。对清洗后的数据做抽样人工审查确保没有有害内容残留。这些环节虽然不是模型效果的直接决定因素但一旦出现问题影响范围往往是全局性的。8.5 成本控制数据工程在算力成本之外还需要注意存储和网络成本。建议原始数据压缩存储减少存储开销。中间产物按需清理不需要长期保留完整中间状态。数据清洗计算与训练任务错峰调度降低集群排队时间。做清洗实验时先用小批量数据验证逻辑再全量跑。9. 学习路线与下一步数据工程在大模型开发中扮演的角色越来越重要而且相对模型训练来说数据工程的门槛更低、见效更快适合作为进入大模型领域的切入点。如果想把这块内容学透可以按以下顺序推进掌握 Python 数据处理基础熟悉 Pandas、JSON、正则表达式、多进程并行。学习分布式计算框架至少能写 Spark 或 Ray 的基础算子。自己拉取一份公开语料如 OSCAR、RedPajama 的子集走一遍清洗、去重、配比流程。用一个小规模模型做预训练实验对比不同数据版本的效果差异。阅读大模型数据工程相关的论文和开源项目例如 RedPajama、RefinedWeb、The Pile 的数据说明。关注数据配比和课程学习的最新研究尝试在小模型上复现。数据工程是一个需要大量实验经验的领域很多结论来自实践经验而非纯理论推导。上手的最好方式就是拿一份真实数据把它从“原始堆料”处理成“高质量语料”然后在小模型上验证每一步操作的实际影响。如果你正在规划自己的大模型项目建议把数据清洗、去重、配比这三大块作为前置模块优先落地并建立一套数据版本管理制度。这些前期工作的投入会在后续模型训练和调优阶段得到十倍以上的回报。