全局哈希去重原理与数据集实践

适用场景:多来源目标检测数据集、训练集/验证集/测试集划分、数据泄漏排查
一、为什么要做全局哈希去重

我们的数据来源比较复杂,包括公开数据集、爬虫素材、人工标注数据、Roboflow 导出数据、LabelMe 标注数据、旧版合并数据、新增数据,以及直方图均衡化增强数据。

这些数据源之间很容易出现一个问题:

同一张图片被不同来源重复收集、重新命名、重新导出,最后同时出现在 train、val、test 中。

如果同一张图片同时出现在训练集和测试集中,模型训练时已经见过这张图,测试时自然会表现得更好。这会导致验证和测试指标偏高,也就是常说的数据泄漏。

因此,全局哈希去重的核心目标是:

在所有来源、所有 split 之间查找完全重复图片,避免同一张图既参与训练又参与验证或测试。
二、全局哈希去重的基本原理

全局哈希去重可以理解成给每张图片计算一个内容指纹。

它不是看文件名,也不是看文件路径,而是读取图片文件本身的二进制内容,再计算哈希值。常见算法包括 `SHA1`、`SHA256`、`MD5` 等。

逻辑可以简化为:

image_bytes = read_file(image_path)

fingerprint = sha1(image_bytes)

如果两张图片的文件内容完全一致,那么它们算出来的哈希值也会一致。

train/images/safety_001.jpg

test/images/worker_renamed_abc.jpg

虽然文件名、目录和来源都不同,但只要图片二进制内容完全一样,它们的哈希值就会相同,可以被识别为重复图。
三、"全局"的含义

这里的"全局"不是只在单个目录内去重,而是在整个数据集构建过程中统一去重。

检查范围包括:

  • `train`

  • `val`

  • `test`

  • 不同数据源

  • 不同批次新增数据

  • 不同命名规则

  • 不同导出工具生成的数据

目标是保证:

同一张图片不会同时出现在 train、val、test 任意两个 split 中。

尤其要避免:

train 里有一张图,test 里也有同一张图。

这种情况会直接让测试集失去独立性。
#四、它能解决的问题

全局文件哈希去重主要解决以下几类问题。

1. 同图不同名

001.jpg

helmet_worker_202605.jpg

文件名不同,但内容完全一样。

2. 同图不同目录

source_a/train/images/xxx.jpg

source_b/test/images/yyy.jpg

同一张图在不同数据源、不同 split 中重复出现。

3. 重复导出

Roboflow、Kaggle、人工整理或脚本合并时,可能把同一批图多次导出,文件名发生变化但内容不变。

4. 跨来源撞图

公开数据集、爬虫素材和人工整理数据可能收集到同一张原图。全局哈希可以识别这些完全相同的文件。
##五、它不能解决的问题

普通文件哈希去重只适合识别完全相同的文件内容。只要图片经过轻微处理,哈希值就会改变。

例如以下情况,普通文件哈希通常无法识别为重复:

  • 重新压缩

  • 改变尺寸

  • 裁剪

  • 加水印

  • 改亮度或对比度

  • 直方图均衡化

  • JPEG 转 PNG

  • 图片内容几乎一样但编码不同

如果要识别"视觉上很像"的近似重复图,需要使用感知哈希,例如:

aHash / dHash / pHash

但感知哈希也有风险,可能把相似但有价值的不同样本误删。因此在当前阶段,我们优先采用更稳妥的文件哈希去重,先解决完全重复和跨 split 泄漏问题。
## 六、本项目旧版数据集中的实际问题

审计结果显示,标签格式整体干净:

| split | images | labels | objects | missing labels | invalid rows |

|---|---:|---:|---:|---:|---:|

| train | 16262 | 16262 | 33746 | 0 | 0 |

| val | 3393 | 3393 | 6912 | 0 | 0 |

| test | 2990 | 2990 | 6958 | 0 | 0 |

也就是说,旧版数据集的问题不是标签坏,而是重复图和数据泄漏风险。

重复审计结果:

| 项目 | 数量 |

|---|---:|

| duplicate groups | 824 |

| cross-split duplicate groups | 353 |

其中 `cross-split duplicate groups` 尤其重要,表示同一张图片出现在了不同 split 中。

典型例子包括:

  • `safety-helmet-kaggle` 中同一张安全帽图片同时出现在 `train` 和 `test`。

  • `slipper-siteadd` 与 `slipper-v3` 中同一张拖鞋图片跨来源重复,并落入不同 split。

这类问题会让模型测试时见到训练阶段已经见过的图片,从而抬高指标。
## 七、新版数据集中的实践做法

处理思路如下:

  1. 读取旧版 6 类数据集和新增数据源。

  2. 对所有候选图片计算文件哈希。

  3. 建立全局哈希表。

  4. 如果哈希未出现,则保留图片和对应标签。

  5. 如果哈希已经出现,则跳过该图片,避免重复进入数据集。

  6. 在清理后重新组织 `train/val/test`。

  7. 对新增数据也执行同样的哈希检查,避免后续增量再次引入重复。

新版数据集已经消除了旧版中发现的完全重复图片和跨 split 泄漏问题。
## 八、增量追加中的去重实践

后续又补充了两批新数据:

为了节省时间,没有重新完整合并整个大数据集,而是直接向当前主数据集增量追加。但追加前仍然做了全局哈希检查。

拖鞋新增数据

追加结果:

| 项目 | 数量 |

|---|---:|

| 保留图片 | 325 |

| 跳过重复图片 | 95 |

| 空标签图片 | 13 |

这说明新增拖鞋数据中确实存在一部分和现有主数据集重复的图片。如果不做哈希去重,这 95 张重复图就会被再次加入。

吸烟新增数据

追加结果:

| 项目 | 数量 |

|---|---:|

| 保留图片 | 2623 |

| 跳过重复图片 | 0 |

| 坏标签 | 0 |

该批吸烟数据没有发现完全重复图片,可以完整接入。
## 九、为什么不能只按文件名去重

在本项目里,很多重复图的文件名并不一样。例如同一张图可能被不同数据源导出为:

```text

safety-helmet-kaggle_yolohard_hat_workers1197_png_rf_xxx.jpg

safety-helmet-kaggle_yolohard_hat_workers1197_png_rf_yyy.jpg

``

拖鞋数据里也存在同一张图在不同来源中被重新命名的情况:

```text

slipper-siteadd_xxx.jpg

slipper-v3_sandal_xxx.jpg

```

如果只按文件名判断,这些重复都无法发现。

因此本项目采用内容哈希,而不是文件名哈希。
十、对模型指标的影响

全局哈希去重通常不会直接让训练集数量最大化,但会让验证和测试更可信。

旧版数据可能存在:

模型在 train 中见过某张图,又在 test 中测试这张图。

新版去重后,测试集更接近真正未知数据,指标更有参考价值。

这也意味着:

  • 旧版指标可能偏乐观。

  • 新版如果指标仍然提升,说明模型真的获得了更强泛化能力。

  • 后续汇报中,新版 `test` 结果会比单纯训练过程 `val` 更重要。
    十一、与直方图均衡化的关系

直方图均衡化生成的是新图像文件,虽然来自原图,但二进制内容和视觉分布都发生了变化。因此普通文件哈希不会把 HEQ 图和原图识别为重复。

这并不是错误,而是符合预期。

HEQ 图在本项目中被视为增强样本,而不是重复样本。但为了避免增强过度,我们没有采用全量 HEQ,而是采用选择性策略:

  • `slipper`:保留偏暗样本的 HEQ。

  • `smoking`:只保留低照度样本的 HEQ。

  • 其他类别不做全量 HEQ。

因此,全局哈希去重解决的是"完全重复图片",HEQ 策略解决的是"小目标低照度增强",二者职责不同。
十二、实际经验总结

本次项目实践中,全局哈希去重带来了几个明确收益:

  • 发现旧版数据集存在跨 split 重复。

  • 避免测试指标因数据泄漏而虚高。

  • 支持多来源数据继续扩充。

  • 新增数据可以快速追加,同时避免重复污染。

  • 数据集质量从"能训练"提升到"更适合正式对比"。

同时也要注意:

  • 文件哈希不能识别近似重复。

  • HEQ、缩放、裁剪后的图不会被普通哈希当成重复。

  • 是否进一步做感知哈希,需要权衡误删风险。

  • 去重之后仍然需要人工关注类别语义、标注质量和场景分布。
    十三、结论

全局哈希去重的本质是:

给所有图片计算内容指纹,在所有来源和所有 split 之间统一查重,避免同一张图同时参与训练和验证/测试。

在本次矿区安全检测项目中,旧版 6 类数据集虽然标签格式干净,但存在明显跨 split 重复。通过全局哈希去重和重新组织数据集,新版数据集将:

duplicate groups 从 824 降到 0

cross-split duplicate groups 从 353 降到 0

这使后续模型训练和测试结果更加可信,也为后续持续加入拖鞋、吸烟等短板类别数据提供了更可靠的数据底座。

相关推荐
Paranoid-up1 小时前
安全启动和安全固件更新(SBSFU)3:加密基础
算法·安全·哈希算法·iap·安全启动·安全升级·sbsfu
Paranoid-up1 小时前
安全启动和安全固件更新(SBSFU)2:环境搭建
安全·iap·安全启动·安全升级·sbsfu
是wzoi的一名用户啊~1 小时前
Floyd 模版 弗洛伊德算法 模版
c++·算法·动态规划·图论·floyd
昵称小白1 小时前
图论专题(下)
算法·图论
懒惰的coder1 小时前
MPC算法
算法
余俊晖1 小时前
图文混合文档的轻量级多模态listwise重排框架:Rank-Nexus
人工智能·算法·机器学习
桌面运维家1 小时前
服务器异常登录日志排查方法与安全防护实战
运维·服务器·安全
小许同学记录成长1 小时前
三维编辑功能实现
qt·算法·无人机
平行侠1 小时前
026FFT快速乘法 - 从信号处理到大数计算的革命
数据结构·算法·信号处理