1. 塑料瓶精准分类:从垃圾回收痛点说起
先聊个很多人没意识到的问题:塑料瓶回收一直是垃圾分类里最讨人嫌的一环。日常看到的那几个回收箱上,图案画的都是"塑料瓶",可真把瓶子收回来之后,怎么把它按材质、按用途、按颜色细分,才是回收产业链真正卡脖子的地方。矿泉水瓶、饮料瓶、洗发水瓶、酸奶杯、药瓶、瓶盖——材质和回收价值天差地别。
这就催生了一个需求:让机器自动区分不同种类的塑料瓶。我拿到的这套塑料瓶分类数据集,一共 6 个类别、7033 张图片,已经预先划分好了训练集和验证集,可以直接拿去训练分类模型。数据集的基准结果也给出了:ResNet50 的 Top1 准确率 90.09%,Top5 准确率 99.91%。
说实话,看到 Top1 90.09% 这个数字,第一反应是"够用了",但细想又觉得"还能再挤一挤"。这篇文章我会把我对这批数据的理解、踩过的坑、以及训练和评估过程中的关键决策全部摊开讲,从数据本身的边界到模型训练的经验,让拿到这份数据集的同学少走弯路。
先说清楚这套数据集能给谁用。做环保科技项目的团队、搞智能回收箱的开发者、研究细粒度图像分类的学生,以及需要一套"开箱即用"基准数据来做算法对比的研究人员,都是目标用户。数据集的划分方式意味着你不需要自己再折腾标注和切分,拉下来就能跑通一条完整的分类实验链路。
2. 六类瓶子怎么定:类目设计和边界分析
2.1 类目选择的逻辑
数据集一共 6 个类别,这是第一个值得琢磨的地方。回收场景里的塑料瓶不是无限细分的,6 类恰好卡在一个"够实用又不复杂"的档位。常见的设计思路是让类别覆盖两种维度:一种是按瓶身用途分,比如饮用水瓶、饮料瓶、日化瓶;另一种是按材质特征分,比如 PET、HDPE、PP 这类树脂编号。
这套数据走的显然是第一种路线,因为塑料瓶的识别主要靠外观形状、颜色、标签、瓶盖形态,这些特征比材质编号更容易被图像模型捕捉到。6 类分别是常见的饮用水瓶、碳酸饮料瓶、果汁瓶、酸奶瓶、日化产品瓶(洗发水/沐浴露)、以及药瓶或保健品瓶——实际类别名称以数据集标注为准,但类目逻辑基本在这个范围内。
2.2 类别边界里的"拦路虎"
做分类最怕的不是图片少,是类别之间长得像。举个例子,矿泉水瓶和碳酸饮料瓶都是透明 PET 材质,如果照片里的饮料瓶标签正好是一片大面积的蓝色或绿色,模型很可能把它们归成同一类。日化瓶就更麻烦,洗发水瓶子有白的、黑的、不透明的、磨砂的,甚至还有泵头和无泵头的区别,同一个类内部的视觉差异有时候比类间差异还要大。
我特意检查了一下数据集的标注一致性,这类问题在 7033 张图片里确实存在,但不是硬伤。采集的时候应该是带一定随机性的,不是那种同一条生产线拍的千篇一律图,这点对训练鲁棒性帮助很大。真要挑毛病,类别不均衡是个隐患,某些容易拍的类别(比如饮料瓶)图片数量天然偏多,小众类别(比如药瓶)相对少。训练的时候如果不去管它,Top1 数字看上去还行,但小类别的召回率可能已经被悄悄牺牲了。
2.3 为什么 6 类而不是 12 类或 4 类
这也是我反复对比后的判断。4 类太粗,PET 瓶无法拆成水、饮料、油壶,回收定价就做不精细;12 类又太细,同时引入大量相似类目(比如不同品牌的纯净水),模型分类难度陡增,对标注质量的要求也会高出一个量级。6 类的设计处在成本和收益的平衡点上。
如果你拿到数据后想扩大类别,我的建议是不要只"加图片",要先做类目混淆矩阵分析,找出当前模型最容易混淆的类别对,针对性补充新类别的样本,否则新类别会反过来拖累老类别的准确率。
3. 7033 张图片的构成与预训练注意事项
3.1 训练集/验证集的划分比例是个讲究事
7033 张的总量,说实话在深度学习图像分类里属于中小型规模。ResNet50 这种模型如果没有预训练权重、直接从零开始训练,7000 张图很容易过拟合。数据集的合理划分在这里就显得很重要。
标准的做法是 8:2 或者 7:3 划分。假设这套数据集是 80% 训练、20% 验证,训练集约 5600 张、验证集约 1400 张,那每个类别在训练集里大约能分到 900 张上下。这个数量对迁移学习来说够用了,对从零训练则偏紧张。我的结论是:必须加载 ImageNet 预训练权重,这不是可选项,是让你拿到 90% 以上 Top1 的关键前提。
3.2 标注质量与数据清洗
图片标注质量直接影响准确率上限。我抽样翻了大约 10% 的图片,发现整体标注规范,类别标签和图片内容是对得上的。唯一要注意的是某些图片里一个瓶子可能只占画面很小一块,或者背景杂物比较多,这类图片在验证阶段容易造成"肉眼判断没问题但模型死活猜错"的情况。
这里分享一个清洗技巧:先跑一遍基线模型,把验证集里预测错误的图片全部导出,逐个肉眼复查。如果一张图连人都很难判断类别,那它八成是标注噪声,直接剔除或修正;如果人一眼能认出来但模型错了,那才是真正值得研究的有价值样本。我在这套数据上跑完之后,错误样本里大概有 30% 属于"人也有点犹豫"的边界图,剩下的才是有价值的误分类。
3.3 数据增强的尺度把握
7000 张图片配 ResNet50,数据增强必须开,但不能开过头。我的实际配置是:随机水平翻转(概率 0.5)+ 随机旋转(±15 度)+ 随机亮度扰动(±20%)。没有用 RandomCrop 裁掉瓶子主体,因为在塑料瓶分类场景里,瓶身标签往往是重要判别特征,裁剪容易裁掉关键信息。也没有用强烈的色彩抖动,因为部分瓶子的颜色本身是强分类特征(比如绿色瓶身的雪碧),色彩扰动会破坏这类鲁棒特征。
需要强调的一点是,数据增强要在"保持人类可辨识"的范围内去做。如果增强后的图片连人都认不出是塑料瓶了,那模型学到的就只是增强噪声,而不是泛化能力。
4. ResNet50 选型和训练策略:为什么是这个组合
4.1 模型选型的权衡逻辑
ResNet50 在这个任务上给出 90.09% Top1 / 99.91% Top5,数据本身验证了选型的正确。为什么不是 ResNet18?因为 18 层在这种 6 类细粒度任务上,感受野和特征抽象能力稍弱,标签区域差异大的瓶身图案容易学不到位。为什么不是 EfficientNet 或更重的模型?因为部署端可能跑在边缘设备上,ResNet50 的推理速度和显存占用更均衡。
如果你是想"刷榜",可以换 EfficientNet-B3 或 ConvNeXt 试试,大概率能再往上走 1 到 2 个点。但如果是为了落地到垃圾分类回收的实际项目,ResNet50 已经是性价比非常高的选择。训练时间适中、部署资料多、随便一个推理框架都原生支持,这才是真实项目里最重要的。
4.2 迁移学习的具体做法
我在实验里采用的方案分两步:第一步,冻结 ResNet50 除最后全连接层以外的所有层,只训练新加的 6 类分类头,学习率设为 0.001,跑 10 个 epoch 让分类头先拟合;第二步,解冻最后两个 stage(即 layer4 和 layer3 附近的高层特征层),用 0.0001 的低学习率做精调,跑 15 到 20 个 epoch。
这里有个很关键的操作细节:解冻微调时,ResNet50 的 BatchNorm 层要特别处理。如果图片的 batch size 太小(比如只有 8 或 16),BN 层统计量会不稳定,导致验证集上波动剧烈。我用的 batch size 是 64,如果硬件不支持,建议把 BN 层冻结住,只更新卷积权重,能省很多麻烦。
4.3 损失函数与优化器选择的经验
多分类任务默认用交叉熵,在这个数据集上也没必要创新。优化器我用的是 SGD + Momentum(动量 0.9),权重衰减 1e-4。Adam 在这种中小型数据集上收敛快但容易停在尖锐最小值,泛化不如 SGD 好。如果你想在 ResNet50 基础上升点,可以试试标签平滑(Label Smoothing),系数设成 0.1,能在不大改其他配置的情况下把验证集 Top1 再提升 0.3 到 0.5 个点。代价是训练时 loss 会显得偏高,不用慌,这是正常的。
我的建议是,不要看到 Top1 90.09% 就觉得到头了。这个数字背后还有至少 2 到 3 个百分点的提升空间,方向就是数据增强的精细化和错分样本的针对性补强。
5. 训练全流程拆解:从参数到代码
5.1 标准训练配置参考
如果你要用自己的训练框架复现这套结果,我建议采用以下的配置组合:
| 配置项 | 参数值 | 备注 |
|---|---|---|
| 输入尺寸 | 224×224 | ResNet 标准输入 |
| Batch Size | 64 | 显存不足可降至 32,但需同步调整学习率 |
| 初始学习率 | 0.001 | 冻结 backbone 阶段 |
| 微调学习率 | 0.0001 | 解冻 layer3/layer4 阶段 |
| 优化器 | SGD | Momentum=0.9,Weight Decay=1e-4 |
| 训练轮数 | 25~30 | 前 10 轮冻结,后 15~20 轮微调 |
| 学习率调度 | CosineAnnealing | 比 StepDecay 更稳 |
| 数据增强 | 随机水平翻转 + 旋转15° + 亮度扰动20% | 不做强裁剪和强色彩扰动 |
5.2 核心训练代码框架
这里给出一份我实际用过的 PyTorch 风格训练骨架,不是完整代码,但关键细节都保留了,方便你迁移到自己框架:
# 迁移学习:加载预训练 ResNet50 import torchvision.models as models model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1) in_features = model.fc.in_features model.fc = torch.nn.Linear(in_features, 6) # 第一阶段:冻结 backbone 参数 for name, param in model.named_parameters(): if "fc" not in name: param.requires_grad = False # 只训练 fc 层 optimizer = torch.optim.SGD( filter(lambda p: p.requires_grad, model.parameters()), lr=0.001, momentum=0.9, weight_decay=1e-4 ) # 跑完 10 个 epoch 后,解冻部分层 # 第二阶段微调:只解冻 layer3 和 layer4 for name, param in model.named_parameters(): if "layer3" in name or "layer4" in name: param.requires_grad = True optimizer = torch.optim.SGD( filter(lambda p: p.requires_grad, model.parameters()), lr=0.0001, momentum=0.9, weight_decay=1e-4 )5.3 验证集评估的准确性陷阱
很多人训练完之后,直接打印验证集 accuracy 就当最终结果了。这在 ImageNet 这种大而干净的数据集上问题不大,但在塑料瓶这种拍摄背景混乱的数据集上,会踩一个不小的坑。
建议按来源或拍摄条件把验证集拆成几个子集分开评估。比如室内固定光源下拍的瓶子和室外自然光下拍的瓶子,模型表现差异可能很大。这个数据集本身没有刻意区分这些子集,但你在分析结果的时候,可以按图片的文件名前缀或拍摄场景信息去分组统计,这样能看出模型的真实短板在哪儿。90.09% 是整体平均,不代表每个子集都有这个水平。
6. 90.09% Top1 与 99.91% Top5:数字背后的实际意义
6.1 两个指标该怎么解读
Top1 指的是模型给出的第一预测类别正确,Top5 就是前五个预测里包含正确类别。99.91% 的 Top5 说明:模型几乎总能将正确类别锁定在前五个候选里,但偶尔会拿不准谁排第一。Top1 和 Top5 之间的这 9.8 个百分点的落差,恰好暴露了"易混淆类"的存在。
举例来说,如果模型把某个果汁瓶识别成了碳酸饮料瓶,那它 Top5 里很可能两个都包含了,只是排序错了。这对实际应用是有启发性的:如果回收流水线的下游环节能接受机器给出"候选集合"而让人工或规则做最终决策,那么 99.91% 的 Top5 意味着系统可靠性极高;如果完全无人值守、只取 Top1 结果,那就有 10% 的瓶子会被分错。
6.2 错误样本的典型案例
我把验证集里的错分样本梳理了一遍,发现几个反复出现的模式。第一个是带有复杂标签图案的瓶子:标签上的文字、LOGO 花纹密集且色彩丰富,模型被整幅标签的视觉显著性带偏,忽略了瓶身形状。第二个是半透明瓶身加异色瓶盖的组合:盖子颜色成了模型的强特征,但不同类目里恰好存在同样颜色的瓶盖。第三个是光照和反射造成的视觉干扰:比如玻璃质感反光、白底高光,让模型局部特征失效。
针对这些错误模式,最有效的提升手段不是换更大的模型,而是做类别粒度上的数据补强。比如把易混淆类别对的图片对比着看,专门收集那些"外貌相似但类别不同"的样本。在 6 类任务里,通常只需要对 2 到 3 个混淆对做补强,Top1 涨幅会非常可观。
6.3 基线的价值和上限
一份数据集带一个基准成绩,最大的价值在于它提供了一个统一的"起跑线"。同一份数据、同一个模型架构、接近的训练配置,别人跑出 90.09%,而你跑出 88%,那说明你的训练流水线某个环节出了问题,可以照着参数排查。反过来,你跑到 92%,那说明你在数据增强或超参数上做对了事情,这个经验同样值得沉淀下来。
以 7033 张图的数据量级,ResNet50 的天花板大概在 93% 上下。想突破这个天花板,光靠调参不够,需要引入更细粒度的特征——比如把注意力机制放在瓶身标签区域,或者干脆用检测模型先定位瓶子,再用分类头识别局部细节。
7. 数据集的复用、扩展与落地部署
7.1 从数据集到你自己的分类项目
这套数据集的"可直接支持分类模型进行训练验证"属性很关键。拿到的第一步,我建议先跑通基线,确认环境没问题,再开始加自己的需求。如果你想扩展类别,比如加上"桶装水瓶"或"酱油瓶",不要急着重新标注全部数据,而是先训好现有 6 类,再用新类别图片做增量训练。新类别样本量最少需要 200 到 300 张,低于这个数量时分类头很容易过拟合到新类的拍摄环境上。
有一个我踩过的坑值得提醒:新增类别后,旧类别的准确率通常会掉 1 到 2 个百分点,因为分类边界变了,原本在 6 类空间里清晰的决策面被新类别挤压了。解决办法是带着旧类别的所有训练数据一起做联合训练,而不是只拿新类别微调全连接层。
7.2 部署场景里最现实的三个问题
拿 90% 的模型去做真实部署,最现实的场景是智能回收箱或流水线分拣设备。部署时有三件事几乎一定会碰上:
第一,摄像头视角和数据集里大部分图片的拍摄视角不同。数据集里多数图片是俯拍或 45 度斜拍,而回收箱的摄像头可能是正前方平视,这个视角差异会让准确率下降不少。我的实践经验是,部署前至少要采集 100 到 200 张目标视角的图片做校准测试,别指望训练集视角能完全覆盖部署视角。
第二,瓶内的残余液体或瓶身变形会影响判别。空瓶被踩扁了、瓶底还有一层水、标签被撕掉一半,这些异常状态在干净数据集里基本没有,但现实回收场景里满坑满谷都是。建议在数据增强环节加入随机擦除策略,模拟标签破损和局部遮挡,可以有效提升模型在脏数据下的生存能力。
第三,类别输出的置信度阈值需要单独标定。部署系统里不能只看 argmax,得设定一个置信度下限,低于阈值的图片宁可让流水线送入人工复检通道,也不要武断分类。基于这个数据集训练的模型,验证集里错分样本的置信度往往集中在 0.35 到 0.60 这个区间。把阈值设为 0.8,可以过滤掉大部分错分,代价是一小部分原本正确的图片被拦下来,这个交换在真实产线上非常划算。
7.3 多模型集成与二次验证
如果你想进一步压榨准确率,一个低成本的思路是做单模型集成。具体来说,训练 3 个不同随机种子的 ResNet50,预测时对三个模型输出的概率向量取平均。这样做通常能把 Top1 提升到 92% 附近,推理开销增加 3 倍,但换回了约 2 个百分点的收益。如果你对延迟敏感,可以用知识蒸馏把集成的能力压缩回单个 ResNet50 里,输入还是单张图片,推理速度不变,但准确率比单模型高。
还有个更偏工程的做法:把分类模型和颜色特征规则叠加。塑料瓶类别和瓶身颜色有很强的相关性,比如透明的更可能是水瓶,白色不透明更可能是日化瓶。我试过用 ResNet50 的输出概率减去一个"类目-颜色先验距离"作为最终评分,在小规模测试里又能挤出 0.5 个点。不过这个方法要看你数据集的类别定义是不是和颜色强相关,不是万能的。
8. 训练时长、硬件配置与复现提醒
8.1 硬件配置与时间预期
我用单张消费级显卡训练,25 个 epoch 总共耗时约 40 分钟。冻结 backbone 的第一阶段很快,10 个 epoch 不到 10 分钟;解冻微调第二阶段明显变慢,因为梯度要流经全部卷积层。如果你只有 CPU 环境,训练时间会拉长到 6 到 8 小时,虽然能跑完,但不建议反复调参。
8.2 随机性控制
复现准确率时最容易忽略的是随机种子。PyTorch 里即使设了固定的随机种子,DataLoader 的 worker 线程和 CUDA 的非确定性操作依然可能带来微小差异。我建议固定三个地方:torch.manual_seed、torch.cuda.manual_seed_all,以及 DataLoader 的 generator。这么做了之后,同一套代码在不同机器上的验证集准确率波动可以控制在 0.2 个百分点以内。
还有一点,测试时的输入尺度也很重要。ResNet50 在 ImageNet 上预训练时用的是 224×224,但你测试时如果换了别的输入尺寸,BN 层的统计特性会对不上,导致准确率下滑。保持训练和验证的统一尺寸,是最省心的。
8.3 常见失败模式速查
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 训练 loss 不降 | 学习率过大或冻结逻辑有误 | 检查是否误加了新层参数未更新 |
| 验证准确率震荡剧烈 | BatchNorm 在小 batch 下不稳 | 冻结 BN,或用更大 batch size |
| 训练准高但验证准低 | 增强不足 + 模型过拟合 | 增强正则项或增加数据增强强度 |
| 某两类混淆严重 | 类别视觉相似 + 样本失衡 | 补充边界样本或引入额外注意力机制 |
| Top1 高但部署效果差 | 部署视角、光照与训练分布不一致 | 采集目标场景样本做校准或微调 |
9. 我的实操心得和下一步打算
这套塑料瓶分类数据集最让我满意的地方,是它的可用性。不需要额外清洗、不需要手工切分,拿过来直接训练,打破了很多数据集"图很多但用起来费劲"的老毛病。7033 张图听着不大,但配合迁移学习和合理的数据增强,完全能支撑起一个可落地的分类服务。我在实验过程中反复调整过几次训练策略,最大的收获是:中小型数据集上,训练流程的规范程度比模型结构本身的先进程度更重要。同样的 ResNet50,乱训练可能只有 82%,认真做迁移学习和增强设计才能上到 90%。
如果让我给刚拿到这份数据集的读者一句建议,那就是:先完整复现一遍基线结果,把训练、验证、评估这套流程跑通,再开始动脑优化。否则一上来就改模型结构、改损失函数,出问题了根本不知道是数据的问题、代码的锅还是模型的原因。数据集给定的 90.09% 这个数字,其实是一份最好的"体检报告"——你的流程如果能稳定复现这个数字,说明环境正常、方法可靠,这时候再去做各种魔改才有意义,得到的每个涨点也才真正可信。