Data Cleaning
数据清洗会在训练前查找并修复数据集中的损坏文件、重复项、缺失值和标签错误。介绍相关技术、工具和代码。
数据清理(也称数据清洗或数据整理)是指在数据集中查找并修复或删除损坏、重复、不完整、不一致和错误标注的记录。在机器学习(ML)中,数据清理先于数据预处理和训练,因为模型会学习其训练数据中的所有内容,包括错误。若检测器的训练图像中有一半自行车被标为“摩托车”,模型就会学到这种混淆;而标签错误的测试集则会错误地报告模型的表现。数据清理是以数据为中心的 AI的核心实践之一;这种方法通过改进数据而非架构来提升模型。
数据质量为何会限制模型#
数据中的错误会通过多种方式降低准确率。错误标注的样本会教会模型错误的输入输出映射,并使模型在记忆噪声时趋于过拟合。重复样本会被赋予额外权重;如果副本同时出现在训练集和评估集,就会造成数据泄漏,虚增得分。损坏的文件会使数据加载器崩溃或被静默跳过,不一致的类别名称则会把一个类别拆成多个类别。基准测试也会受到影响:一项对 10 个常用测试集的研究估计,平均至少 3.3% 的样本存在标签错误,而 ImageNet 验证集中的错误标签比例至少为 6%。使用修正后的标签评分时,原始测试样本中错误标注的比例仅上升 6%,ResNet-18 的表现就超过了 ResNet-50。
常见的数据清理任务#
表格数据通常使用 pandas 等库进行清理,而视觉数据集则需要检查图像文件及其标注,从像素到边界框坐标都要检查。可以通过比较嵌入向量查找近重复图像,并使用向量搜索将比较扩展到数百万张图像。
- 缺失值: 空字段记录可以删除,也可以根据列统计量或最近邻进行插补。在视觉数据集中,没有标签文件的图像含义不明确,因为它可能是有意保留的背景图,也可能是尚无人标注的图像。
- 重复项和近重复项: 字节完全相同的文件具有相同哈希值。调整尺寸或重新压缩的副本以及连续的视频帧则不同,因此需要使用感知哈希或嵌入相似度进行检测。
- 损坏或无效文件: 未完整下载的文件、不受支持的格式、意外的颜色模式以及尺寸过小的图像可能无法解码,或不包含可用信息。
- 标签错误: 类别错误、对象缺失以及不准确或放置不当的边界框,是监督式视觉数据中代价最高、也最难通过肉眼发现的错误。
- 类别不一致: 例如“Car”和“car”这样的拼写差异,或“汽车”和“车辆”这样的重叠类别,都应合并到同一分类体系中,让每个概念只有一个标签。
- 离群值: 负年龄或超出图像边界的边界框等不可能的值都应修正。罕见但有效的情况则要保留,因为模型往往最需要学习这些边缘案例。
使用模型查找标签错误#
人工逐一检查所有标签无法扩展,因此清理流程会使用训练好的模型对待检查标签进行排序。开源 cleanlab 库实现了置信学习:它使用交叉验证得到的样本外预测概率,并标记模型有把握地预测出与给定标签不同类别的样本。最终判断仍由人来做:在上述测试集研究中,众包审核人员确认,算法标记的候选样本中有 51% 确实存在标签错误。
自动执行文件检查#
文件级检查成本足够低,可以对每批新数据运行,同时执行数据集质量控制课程中介绍的标签和类别平衡审计。以下脚本会创建一个小文件夹,其中包含一张图像、它的精确副本和一个截断文件,然后使用 Pillow和内容哈希标记出副本与损坏文件:
import hashlib
from pathlib import Path
from PIL import Image
# A tiny image folder: one original, one exact copy, and one truncated download
folder = Path("images")
folder.mkdir(exist_ok=True)
Image.new("RGB", (640, 480), "orange").save(folder / "a.jpg")
(folder / "b.jpg").write_bytes((folder / "a.jpg").read_bytes())
(folder / "c.jpg").write_bytes((folder / "a.jpg").read_bytes()[:1000])
seen = {}
for path in sorted(folder.glob("*.jpg")):
try:
Image.open(path).load() # decode every pixel; a truncated file raises OSError
except OSError as error:
print(f"{path.name}: corrupt ({error})")
continue
digest = hashlib.md5(path.read_bytes()).hexdigest() # identical bytes give identical hashes
if digest in seen:
print(f"{path.name}: duplicate of {seen[digest]}")
seen.setdefault(digest, path.name)脚本会打印 b.jpg: duplicate of a.jpg 和 c.jpg: corrupt (image file is truncated (5 bytes not processed))。完整解码每张图像非常重要,因为打开文件只会读取文件头,而被截断的文件可能通过这一步检查。构建数据集标签缓存时,Ultralytics YOLO也会执行相关检查:它会重新保存缺少图像结束标记的 JPEG,删除重复的标签行,并跳过图像小于 10 像素、坐标超出图像范围或类别 ID 超出数据集 YAML 中类别范围的图像标签对,随后在扫描摘要中报告图像、背景图和损坏文件的数量。










