数据清洗与标注错误排查

训练Pipeline里,数据清洗和标注质量这块,我吃过不少亏。说实话,很多时候模型训不上去,不是算法不对,是数据本身已经烂了。

你想想看,如果喂进去的样本里有一半标签是错的,模型能学出个啥?它只能学到如何把错误模式拟合得更好。所以这一章,咱们专门聊聊数据清洗和标注环节那些容易踩的坑。

我个人习惯把数据质量的问题分成四类:缺失值与异常值处理、标注格式不一致、标签噪声与错误标注、清洗脚本的边界情况。下面一个一个说。

数据清洗与标注错误排查框架 缺失值与异常值 标注格式不一致 标签噪声与错误 清洗脚本边界 均值填充/删除策略 JSON/CSV格式混用 标注工人不一致 空文件/特殊字符 异常值3σ/箱线图 字段命名规则检查 置信度筛选/投票 路径不存在/空行 目标:在训练前发现数据质量问题,避免浪费GPU时间

缺失值与异常值处理失误

我遇到过那种把缺失值全部填0的项目。结果呢?模型学到的是:凡是缺失的特征,对应输出就要偏。后来查出来是因为传感器故障,缺失值根本不该存在。这就是典型的处理失误。

缺失值的常见处理方式有:删除、均值/中位数填充、模型预测填充、或者保留为特殊标记。问题在于——你选哪种方式,取决于数据本身的含义和下游任务。

举个例子,如果某字段是“用户年龄”,缺失率5%。你简单地用全体均值填充,会引入偏差。更好的做法是按其他特征(比如注册时间、地区)分组填充。

我的经验: 先做一次分布可视化。缺失值分布如果和某些特征相关,那可不是随机缺失,你得额外留神。

异常值又是另一回事。3σ原则、箱线图、Z-score……方法很多。但我踩过坑:直接用3σ筛选数值型特征,结果把正常的高价值客户当作异常删了。后来我在代码里加了分位数检查,才避免这种问题。

标注格式不一致

你有没有遇到过这种情况:同一批数据,有的标注是JSON格式,有的是CSV格式?或者同一个字段,有的用字符串,有的用数字?

标注格式不一致是清洗Pipeline里最常见的“低级错误”。问题常常出在多人协作标注、或者不同批次数据合并时。我去年接手一个NLP项目,标注文件里“label”字段有时写“positive”,有时写“1”,有时写“pos”。

这类问题排查起来其实很简单——写一个格式检查脚本,对每个样本做schema校验:

def validate_annotation(ann):
    required_fields = ['image_path', 'bbox', 'label']
    for f in required_fields:
        if f not in ann:
            return False
    if not isinstance(ann['bbox'], list) or len(ann['bbox']) != 4:
        return False
    if ann['label'] not in ['cat', 'dog']:
        return False
    return True

跑一遍就能发现所有格式异常的样本。

注意: 我曾经检查完格式后就以为万事大吉了——结果发现有些bbox坐标超出了图像尺寸,这也是格式里隐含的“不一致”。一定要检查字段值的合理性,不只检查“有没有”。

标签噪声与错误标注

标签噪声是训练Pipeline里最隐蔽的杀手。它不像格式错误那样一眼能看出来,但杀伤力极大。

噪声来源通常有三种:

  • 标注工人主观差异:同一个任务,不同人理解不同,给的标签可能冲突。
  • 自动化标注错误:用规则或模型预标注,准确率不是100%。
  • 数据本身模糊:比如图像中物体遮挡严重,标注者只能猜。

怎么排查?我一般用几个手段联合起来:

  1. 标注一致性计算:比如Cohen's Kappa,如果低于0.6,就得复查标注指导书。
  2. 训练后看loss分布:把loss值排序,拿出top 5%的高loss样本,人工查看。
  3. 模型置信度分析:如果模型对某个样本置信度低,而且多个标注者意见不一,那很可能就是噪声样本。

典型案例: 我有个自动驾驶项目,标注图片里“行人”标签混了一些远处的树影。模型一直把树影认成行人。后来我们用模型预测结果反查标注,发现了这批错误,重新标注后mAP提升了3个点。

处理标签噪声,常用方法包括:清洗(删掉噪声样本)、重标(人工修正)、加权(给置信度低的样本更小权重)。但我说实话,最有效的还是从源头改进标注流程。

清洗脚本的边界情况

清洗脚本看起来简单,但边界情况往往被忽视。我见过太多“看起来跑通,但实际丢数据”的脚本了。

常见的边界问题:

边界类型问题表现后果
空文件读入空CSV,脚本没做判断pandas报错,Pipeline中断
特殊字符文件名或字段值含
换行符、Unicode
解析错误,数据被切碎
路径不存在图片路径写错或文件被移动训练时样本数不对等
重复数据标注集里出现相同样本模型过拟合到重复样本
数据类型数值列混入字符串(如“N/A”)类型转换失败

怎么防御?我给自己定了个规矩:每个清洗脚本必须包含边界检查单元测试。比如:

def test_empty_input():
    df = pd.DataFrame()
    result = clean_pipeline(df)
    assert result.empty

def test_special_chars():
    df = pd.DataFrame({'text': ['hello\nworld', '🚀']})
    result = clean_pipeline(df)
    assert len(result) == 2

还有一点很容易忽略——清洗顺序。假如你先把异常值替换成NaN,然后又删除了所有含NaN的行,那就等于把异常值删除了。这个逻辑要搞清楚。我建议在脚本里加上详细的日志,打印每个步骤的样本数量变化。

print(f"原始样本数: {len(df)}")
df = remove_duplicates(df)
print(f"去重后: {len(df)}")
df = fill_missing(df)
print(f"填充缺失后: {len(df)}")
df = remove_outliers(df)
print(f"异常值处理后: {len(df)}")

这样一眼就能看出哪个步骤有问题。我曾经因为一个清洗步骤顺序反了,导致最后只剩一半数据,排查了整整半天才发现。

最后提醒: 整个清洗和标注环节,最好的策略是“在数据进入训练之前就发现错误”。建立自动化的数据质量检查管线,每次有新的标注数据进来,先跑一遍校验脚本,不合格的直接打回重新标注。这比训完模型再回去查数据要高效得多。

嗯,关于数据清洗和标注错误排查,我能想到的实战经验就这些。记住一点:永远不要信任原始数据。哪怕它看起来干干净净,也要用代码验证一遍。