9. 大规模数据清洗与预处理 (chapter9.md)
9.0 开篇段落
如果说数据获取是“开矿”,那么数据清洗与预处理就是“提炼”。在多模态大模型的研发中,数据清洗不仅决定了模型的上限(性能),更决定了模型的下限(安全性与鲁棒性)。业界共识认为,数据准备工作占据了整个大模型开发周期的 60%-80%。
面对 PB 级别的多模态数据,手动检查已不可能。数据经理的核心职责是设计一套自动化的、可观测的、分级治理的清洗流水线(Pipeline)。你需要权衡“保留多样性”与“去除噪声”之间的微妙平衡,处理图文不匹配的“幻觉”源头,并确保训练数据的绝对合规。本章将带你深入数据清洗的黑盒,从字节级别的编码修复到语义级别的跨模态对齐。
本章学习目标:
- 架构设计:掌握构建大规模分布式数据清洗流水线的设计原则。
- 全模态处理:深入理解文本、图像、视频、音频的专项清洗逻辑与参数设置。
- 高级去重:学会运用 MinHash、pHash 及向量检索技术进行不同力度的去重。
- 质量与对齐:掌握如何使用 CLIP 等模型自动评估图文一致性及美学质量。
- 血缘管理:建立数据清洗的可追溯体系,确保“坏数据”可查,“好数据”可复用。
9.1 原始数据接入与规范化
数据湖中的“原始层”往往是一片沼泽:压缩包损坏、文件名乱码、目录结构深浅不一。规范化是第一道堤坝。
9.1.1 目录结构与命名规范
对于多模态数据,单纯的文件系统往往难以承载通常采用对象存储(S3/OSS/COS)。
- 分层存储策略 (Tiered Storage):
- Raw (Landing Zone):原样保存爬虫或供应商交付的数据。原则:只读,永不修改。
- Stage (Interim):解压、转码、重命名后的中间态。
- Curated (Gold):清洗完毕、已打包(如 Parquet/WebDataset)、可直接进入 Dataloader 的数据。
- 命名规范 (Rule of Thumb):
不要使用文件名存储关键信息(文件名长度有限且易丢失),应使用路径分区。
s3://bucket/data_type/source/date_partition/shard_id/file_name
- Bad:
dog_image_v2_final_2023.jpg
- Good:
/image/laion_subset/2023-10-01/shard_001/a1b2c3d4.jpg (文件名使用 Hash 值避免碰撞)
9.1.2 Schema 设计与统一化
无论原始数据是 CSV、XML 还是 JSON,进入清洗管道后必须转为统一的 JSONL (JSON Lines) 或 Parquet 格式。
多模态通用 Schema 建议:
{
"id": "全局唯一UUID",
"meta": {
"source": "youtube",
"timestamp": 1698765432,
"license": "cc-by-4.0",
"original_url": "https://..."
},
"content": {
"text": "一只猫坐在钢琴上...",
"images": [{"path": "s3://...", "resolution": [1024, 768]}],
"videos": [{"path": "s3://...", "duration": 15.5}],
"audios": [{"path": "s3://...", "sample_rate": 44100}]
},
"stats": {
"quality_score": 0.85, // 清洗阶段回填的字段
"is_safe": true
}
}
9.2 文本清洗:大模型的逻辑底座
即使是视觉模型,其语义理解能力也源于文本。文本清洗的质量直接影响模型对指令的遵循能力。
9.2.1 基础清洗与修复
- 编码修复 (Encoding Fix):使用
ftfy 等工具自动修复 é (é) 等常见的 UTF-8 编码错误。
- 空白字符标准化:将连续的空格、制表符、不可见字符(Zero-width space)替换为单个空格。
- 语言识别 (LID):
- 使用 fastText 或专门的 LID 模型识别语种。
- 策略:如果目标是中文模型,通常保留
zh (中文) + en (英文) + code (代码),过滤掉无法识别的小语种,防止 Tokenizer 爆炸。
9.2.2 启发式过滤 (Heuristic Filtering)
不依赖模型,仅靠规则快速筛除垃圾文本。
- 长度过滤:删除过短(< 5 个 token)或过长(超出上下文窗口且无标点)的文本。
- 符号率过滤:如果一段文本中
#, @, ... 等特殊符号占比超过 40%,通常是代码堆栈报错或乱码。
- 停用词过滤:建立黑名单,包含“导航”、“登录查看”、“广告位招租”、“404 Not Found”等 Web 噪声。
9.2.3 敏感信息与 PII 治理
这是合规的高压线。
- 正则流 (Regex Pipeline):
- Email, IPv4/v6, 电话号码, URL, 身份证号, 银行卡号。
- 注意:对于代码数据,不要误删 IP 地址或 Email(有时是代码的一部分),需结合下文判断。
- NER (命名实体识别):
- 针对人名、住址进行识别。
- 掩码策略:建议使用类型掩码(如
<PERSON_NAME>, <PHONE_NUMBER>)替换,而不是直接删除,以保持句子的语法结构完整性。
9.3 图像/视频/音频深度预处理
非结构化数据的处理极其消耗算力(CPU/GPU),需要精细的策略。
9.3.1 图像:从像素到张量
- 分辨率控制:
- 最小阈值:长边 < 256 或短边 < 64 的图片通常无信息量,直接丢弃。
- 宽高比 (Aspect Ratio):极度细长(如 1:10)的图片通常是网页长截图或 banner,包含大量拼接文字,建议切分或丢弃。
- 去水印与修复:
- 检测角落的“Getty Images”、“Shutterstock”水印。商业模型训练需谨慎处理此类数据,通常直接过滤。
- 智能缩放 (Smart Resizing):
- 训练时需要固定尺寸(如 224x224)。
- Center Crop:丢失边缘信息。
- Padding:保留全图,填充黑边(增加无用计算量)。
- SQuINT 等策略:在预处理阶段做多尺度保存。
9.3.2 视频:时空切片
视频 = 图像流 + 音频流 + 字幕。
- 场景分割 (Scene Detection):
- 使用
PySceneDetect 等工具,基于画面内容突变切分视频。不要把两个完全无关的场景剪辑在一个 Clip 中,这会破坏时序因果关系。
- 动态采样 (Dynamic Sampling):
- 画面静止不动的视频(如 PPT 讲解),降低采样率(0.5 FPS)。
- 运动剧烈的视频(如体育),提高采样率或保留更多关键帧。
- 美学评分过滤:
- 视频每一帧都清晰是不可能的。计算 Clip 中所有帧的美学评分平均值或中位数,剔除抖动严重、全黑/全白的片段。
9.3.3 音频:听觉清洗
- VAD (语音活动检测):
- 切除静音片段。对于 10 秒的音频,果只有 2 秒有人说话,其余 8 秒静音会干扰模型训练。
- 信噪比 (SNR) 过滤:
- 剔除背景噪音过大(如风声、机器轰鸣声盖过人声)的样本。
- 音频转录验证 (ASR Verification):
- 对于“文本-音频”对齐任务,先用 Whisper 等强模型生成一次伪标签(Pseudo Label),计算伪标签与原始文本的 WER (词错误率)。如果差异过大,说明原始文本可能不是该音频的字幕,需剔除。
9.4 进阶去重:防止模型“死记硬背”
重复数据不仅浪费算力,还会导致模型对高频样本过拟合,产生“复读机”现象。
9.4.1 文本去重
- Exact Dedup:SHA-256 全文比对。
- Fuzzy Dedup (MinHash + LSH):
- 原理:将文本切分为 N-gram,计算 Jaccard 相似度。
- 参数建议:对于预训练数据,Jaccard 阈值设为 0.7-0.8;对于指令微调数据,去重应更严格。
- Substring Dedup:使后缀数组(Suffix Array)找出大量重复出现的子串(如重复的页脚版权声明)并剔除。
9.4.2 图像/视频去重
- 感知哈希 (pHash):抵抗旋转、缩放、微调色。
- Embedding Dedup:
- 利用 CLIP Image Embedding 计算余弦相似度。
- 阈值:Similarity > 0.9 通常视为重复。
- 应用:可以找出“同一张图的不同裁剪版本”或“同一事件的不同角度照片”。
9.4.3 跨集去污染 (Decontamination)
这是评估模型真实能力的生命线。
- 流程:
- 收集所有计划使用的测试集(Benchmarks)。
- 提取测试集的 Prompt、Image、Answer。
- 在训练全集中检索这些内容(N-gram 匹配或 Embedding 检索)。
- 物理删除训练集中命中的样本。
- Gotcha:即使只有 1% 的测试题泄露,也可能导致评测分数虚高 10-20 分。
9.5 噪声与低质量样本识别
9.5.1 图像文本位 (The “Alt-Text” Problem)
互联网上的 alt 标签往往质量极差。
- 例子:图片是一只猫,alt 文本是 “IMG_2023.jpg” 或 “点击购买”。
- CLIP Score 过滤:
- 计算 Image Embedding 和 Text Embedding 的相似度。
- Rule of Thumb:剔除 CLIP Score 最低的 20% 数据,或者设定绝对阈值(如 < 0.22)。
- 注意:对于艺术创作类图像,CLIP Score 可能偏低,需分桶处理。
9.5.2 OCR 过滤
- 如果一张图片纯粹是大段文字的截图(如 PDF 截图),且没有对应的 OCR 文本,这对训练视觉理解帮助有限。
- 策略:检测图片中的文字区域占比。如果 > 50%,除非任务是专门训练 OCR 模型,否则建议剔除或进行专门的 OCR 转录增强。
9.5.3 人脸与隐私 (Face Blurring)
- 对于街景、监控类数据,必须对路人面部进行模糊处理(Blurring)。
- 使用 RetinaFace 等高精度人脸检测模型,对 Bounding Box 域应用高斯模糊。
9.6 清洗 Pipeline 的实现与调度
9.6.1 处理流架构图 (ASCII)
[S3 Raw Data] (PB级)
⬇
[Spark/Ray Cluster] -- (分布式读取与初步过滤)
⬇
(1) 规范化 & 基础过滤 (正则, 格式检查) -> [丢弃 10%]
⬇
(2) 模态预处理 (Resize, VAD, 编码修复)
⬇
(3) 模糊去重 (MinHash/LSH/pHash) -> [丢弃 30% 重复]
⬇
(4) 模型打分 (CLIP Score, Quality Model, NSFW) -> [GPU 密集型]
⬇
(5) 阈值截断 & 对齐校验 -> [丢弃 20% 低质]
⬇
(6) 隐私脱敏 & 去污染 (Decontamination)
⬇
[Final Dataset] (Parquet/WebDataset) -> (SFT/Pretrain)
9.6.2 工具选型
- 计算框架:
- Ray Data:专为 AI 数据处理设计,支持 GPU 加速(适合跑 CLIP/Whisper)。
- Apache Spark:适合大规模文本处理和基于 Hash 的去重。
- 数据格式:
- WebDataset:将图文对打包成 tar 包,适合流式读取避免对文件系统造成百万级 IOPS 压力。
- Parquet:列式存储,适合存储元数据和文本,压缩率高。
9.6.3 幂等性与断点续传
- 清洗任务可能运行数天。必须设计成幂等(Idempotent)的:
- 即“处理过了就跳过”。
- 依靠检查输出目录是否存在对应的 Partition 文件来实现。
9.7 清洗日志与数据血缘 (Data Lineage)
当模型表现出怪异行为时(如突然开始骂人,或生成乱码),你需要查清楚是哪批数据导致的。
- Manifest 文件:
每个处理批次必须生成一个清单文件,记录:
- 输入文件列表。
- 使用的清洗代码版本(Git Commit ID)。
- 各步骤的过滤比例(Retention Rate)。
- 参数配置(如 CLIP 阈值 = 0.25)。
- 样本级血缘:
在最终的 Metadata 中保留
original_source_id。如果发现某张图有问题,可以通过 ID 追溯到它是哪天从个 URL 爬下来的,并检查同批次的其他数据。
9.8 本章小结
- 分级治理:原始数据不动,中间数据保留,成品数据打包。
- 多模态协同:文本清洗重逻辑与合规,图像视频清洗重质量与对齐。
- 算力换质量:使用 CLIP、Whisper 等大模型辅助清洗(Model-based Cleaning)已成为主流,虽然昂贵但值得。
- 去污染是底线:严防测试集泄露,否则评测结果毫无意义。
9.9 练习题 (Exercise)
基础题 (50%)
1. 格式转换
你有一百万张散落在文件夹里的 JPG 图片,直接用于训练会导致文件系统 IO 瓶颈。
- 问题:请列举两种适合大模型训练的打包格式,并简述其优点。
- 提示:考虑顺序读取性能。
点击展开参考答案
**参考答案**:
1. **WebDataset (tar)**:将图片和对应的 JSON/TXT 放在同一个 tar 包中。优点是训练时可以流式取(Streaming),无需解压,对文件系统友好,支持 Sharding(分片)。
2. **Parquet (包含二进制列)**:将图片转为 Bytes 存入 Parquet 列中。优点是列式存储压缩效率高,便于结合 PyArrow 进行快速筛选和读取,且单文件也包含元数据。
*(TFRecord 也是常见答案,但在 PyTorch 生态中 WebDataset 更流行)*
2. 文本清洗正则
编写或描述一个正则表达式逻辑,用于清洗包含大量“网址导航”噪声的文本。
- 示例文本:
"点击这里查看更多 http://bit.ly/xyz 关注我们的 Facebook @user123"
- 目标:去除 URL 和 社交媒体 Handle。
点击展开参考答案
**参考答案**:
1. **去除 URL**:匹配 `https?://\S+|www\.\S+` 替换为空字符串或 `` 占位符。
2. **去除 Handle**:匹配 `@\w+` 替换为空字符串或 `` 占位符。
3. **去除引导词**:建立词表,去除 "点击这里"、"关注我们" 等常见 CTA (Call to Action) 短语。
</details>
**3. 视频去重逻辑**
为什么对视频做去重比对图像做去重更难?请提出一种简单的视频去重思路。
点击展开参考答案
**参考答案**:
* **难点**:视频是时序数据,文件极大。两个视频可能只有中间 10 秒重复(片段重复),或者分辨率、编码不同,或者加了不同的水印。
* **简单思路**:**关键帧指纹法**。抽取视频的第 1、5、10 秒(或均匀抽取 5 帧),分别计算 pHash 或 Embedding。如果两个视频有超过 80% 的关键帧指纹相似,则判定为重复。
#### 挑战题 (50%)
**4. 阈值悖论 (Trade-off Analysis)**
你设定了 CLIP Score > 0.3 作为过滤标准,结果发现删除了 60% 的数据,且主要集中在“抽象艺术”、“极简主义设计”和“手写文字图片”这几类。
* **问题**:为什么会这样?作为数据经理,你该如何修正策略保留这些数据,同时不引入垃圾?
点击展开参考答案
**参考答案**:
* **原因**:CLIP 模型主要在自然图像(照片)上训练,对抽象、艺术、符号类图像的语义理解能力较弱,导致计算出的相关性分数偏低。
* **修正策略**:
1. **基于类别的动态阈值**:先用分类模型判断图片类型。如果是“Photo”,维持 0.3;如果是“Art/Sketch”,降低阈值至 0.2 或 0.15。
2. **引入其他评分模型**:对于艺术图,引入“美学评分(Aesthetic Score)”作为补充标准。只要美学分够高,即使 CLIP Score 低也保留。
3. **合成 Caption**:使用强大的 VLM(如 GPT-4V 或 LLaVA)为这些低分图片重新生成 Caption,替换掉原始的劣质文本,从而“挽救”图片。
**5. 管道设计 (System Design)**
设计一个处理 **10 PB** 原始视频数据的清洗管道。资源有限,不能把所有视频都解压成图片帧存下来。
* **要求**:支持图文匹配训练,且能灵活调整采样率。
* **提示**:考虑存储成本和计算成本的平衡。
点击展开参考答案
**参考答案**:
* **核心思路**:**On-the-fly Decoding(在线解码)** 或 **只存关键帧**。
* **流程设计**:
1. **元数据扫描**:先快速过一遍视频头信息,剔除损坏、过短、分辨率过低的视频(不解码,开销小)。
2. **Keyframe Extraction**:仅解压并保存**关键帧**(如每 2 秒 1 帧)到对象存储,作为“图像数据集”。原始视频归档冷存储。
3. **音频/字幕分离**:单独提取音频和字幕文件(体积小),建立索引。
4. **训练时**:Dataloader 读取预存的关键帧图片。如果需要更高帧率的视频微调,再按需从冷存储调取原始视频进行在线解码(Decode on the fly)。
* **优势**:将 10 PB 视频转化为约 500 TB 的关键帧图片库,大幅降低热存储成本,同时保留了大部分视觉语义。
**6. 负面数据清洗 (Ethical & Safety)**
在清洗过程中,你发现了一批包含“如何制造炸弹”的图文教程。
* **问题**:直接删除这些数据是最好的处理方式吗?如果不删除,该如何处理以用于“安全对齐(Safety Alignment)”阶段?
点击展开参考答案
**参考答案**:
* **直接删除的弊端**:如果模型从未见过这些有害内容,它可能无法识别用户的恶意意图,或者在红队测试中表现脆弱(不知道什么是炸弹,反而可能无意中配合生成)。
* **正确处理**:
1. **隔离(Quarantine)**:将这部分数据从“预训练数据集”中移除,打上 `unsafe` 标签,单独存放在受控的“红队/安全数据集”中。
2. **构建拒答数据**:修改文本标签。将原来的教程说明("Step 1: Mix...")修改为拒答复("I cannot assist with creating explosives...")。
3. **SFT/RLHF 使用**:在微调阶段,使用这些修改后的样本教模型学会“识别恶意指令并拒绝”。
---
### 9.10 常见陷阱与错误 (Gotchas)
1. **压缩包炸弹 (Zip Bomb)**
* **现象**:一个 42KB 的 zip 文件,解压后变成 4.5PB 的全 0 数据。
* **后果**:清洗节点的磁盘瞬间被撑爆,任务挂死。
* **对策**:在解压流中限制输出大小,或者先检查 zip header 中的未压缩大小字段。
2. **时间漂移 (Timestamp Drift)**
* **现象**:处理视频音频对齐时,使用了不精准的 FPS 计算(如 29.97 当作 30.0 处理)。
* **后果**:对于 1 小时的视频,末尾的字幕和画面可能会差几秒钟,导致模型学到错误的图文关系。
* **对策**:始终读取视频元数据中的准确 Timebase,避免手动硬编码 FPS。
3. **过度去重 (Over-deduplication)**
* **现象**:为了追求极致的纯净,把“电商网站模板”全部去除了。
* **后果**:模型失去了对 HTML 结构、表格、列表的理解能力,因为这些结构往往伴随着重复的模板词。
* **对策**:对代码/HTML 类数据,放宽去重标准,或者使用基于语义而非纯文本的去重。
4. **多进程死锁 (Multiprocessing Deadlock)**
* **现象**:在 Python 中使用 `multiprocessing` 处理大量图片,经常卡住不动。
* **原因**:某些图像处理库(如 OpenCV)底层也是多线程的,与 Python 多进程嵌套使用时易发生资源竞争或死锁。
* **对策**:设置 `cv2.setNumThreads(0)` 关闭底层多线程,或者使用 Ray 等更成熟的分布式框架替代原生多进程。
5. **忽略“空”数据**
* **现象**:清洗后产生了空的文本文件或 0 字节的图片,未做清理。
* **后果**:训练代码中的 `Image.open()` 抛出异常,整个训练 Job 中。
* **对策**:Pipeline 的最后一步必须是 **Sanity Check**,扫描所有输出文件的完整性和可读性。