ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

TensorFlow垃圾分类识别:自训练CNN与MobileNet迁移学习实战

TensorFlow垃圾分类识别:自训练CNN与MobileNet迁移学习实战 1. 项目概述与整体设计思路这几年垃圾分类的政策和需求一直在往下沉小区要分类、单位要分类、商场要分类但真正执行的时候大家最大的困惑往往不是“要不要分”而是“眼前这个东西到底算什么垃圾”。一个没喝完的奶茶杯杯身是纸、杯盖是塑料、剩余液体是厨余这怎么算人工分拣员一天要弯腰几百次识别速度再快也跟不上流水线。所以才有了用深度学习做垃圾分类识别系统的想法——让模型替人完成最累的那一步看图片、判类别、给结论。我最初拿到“基于Tensorflow的垃圾分类系统自训练CNN算法模型与MobileNet迁移学习”这个需求时第一反应是它的技术路线其实已经写得很清楚了没有模板可以偷懒也没有现成捷径可走。因为市面上大多数教程要么只讲自训练CNN要么只讲迁移学习能把两条线路放在同一个系统里做对比、做融合、做工程化的项目确实少见。这个系统工程部分的核心任务很简单通过图片输入判断垃圾属于哪个类别再输出对应的投放建议。1.1 核心需求解析垃圾分类系统的技术链条并不复杂但有一个前置问题必须想明白你打算做的是“几分类”是只区分可回收、厨余垃圾、有害垃圾、其他垃圾这四类还是细化到具体的废弃物类别这个看似简单的决策直接决定了后续整个系统的复杂度。我当时给出的做法是采用分层分类策略——模型先输出具体的垃圾细分类比如塑料瓶、易拉罐、纸箱、果皮、电池等再在业务逻辑层把它们映射到“可回收”、“厨余”、“有害”、“其他”四个大类上。这样做的优势非常明显细分类对用户更有指导意义用户看到的不是干巴巴的“可回收”而是“这个易拉罐应该投进可回收物桶”同时对模型来说具体的类别往往有着更显著的外观特征更容易学。这个系统要解决的核心问题其实有三层如何保证模型在真实场景下的识别准确率而不是只在干净的实验数据集上好看如何在有限的算力和存储资源下完成模型的选择这也是我最终选用MobileNet做迁移学习的原因之一如何把训练模型转成可用的实际产品让模型不只是停留在Jupyter Notebook的评估报告里1.2 方案选型自训练CNN与MobileNet迁移学习的取舍逻辑既然是“自训练CNN算法模型与mobileNet迁移学习”双轨并行就得把两条路线各自的适用场景说清楚否则很容易出现盲目选型的问题。自训练CNN完全从零搭建卷积神经网络权重随机初始化通过大量标注数据自己学习特征。它的优势是灵活性极高你可以针对垃圾图片的特点形状、纹理、颜色分布比较固定自由设计网络结构模型的所有中间特征都是为你这个任务量身定制的。但这需要足够多的训练数据作为支撑。我当时在实验初期把训练集压到每类200张时自训练CNN的验证准确率只有78%左右而且过拟合的征兆非常明显训练集准确率上了95%验证集却怎么都跟不上。MobileNet迁移学习则是把已经在大规模图像数据集上预训练好的特征提取器拿过来只替换最后的面部分类层再基于垃圾分类数据做微调。我第一次跑MobileNetV2的迁移训练只做了约40个epoch验证准确率就冲到了88%以上这个效率和自训练CNN完全不在一个量级。这不是MobileNet本身比CNN更强而是预训练模型已经掌握了大量基础视觉特征你的任务只需要在它的肩膀上往前走一小步就行。但迁移学习也并非万能灵药。预训练模型是在通用图像上学的它可能过度适应源域数据的一些特征比如它会格外关注图像颜色或背景纹理而这些特征在垃圾分类场景里未必是你要的。所以在系统中我的做法是两条线路并行训练最终在推阶段采用加权融合预测MobileNet迁移模型的置信度和自训练CNN的结果按6:4的权重融合实测下来比单独使用任何一个模型都要稳定两到三个百分点。1.3 系统架构与数据流的整体设计这个项目的数据流设计是输入一张垃圾图片经过预处理模块完成缩放、归一化然后同时送入两个已训练好的模型进行推理两个模型分别输出各个类别的概率分布融合模块把两个结果按权重合并最终通过业务规则映射到大类再返回给用户可视化的投放建议。在模型部署上由于我选择的是TensorFlow生态整个数据链路做起来非常顺手。训练阶段用TensorFlow的Dataset API做数据管道推理阶段使用TensorFlow Lite进行移动端部署整体从训练到部署的链路是通的不会有“模型训练好的结果是Python格式部署还得写一套新代码”的断档问题。提示TensorFlow 2.x 与 1.x 的 API 差异很大这套系统的所有代码都是在 TF 2.x 环境里完成的如果你还在用 1.x建议尽早迁移不然很多接口写法会对不上。2. 数据集建设决定分类效果的隐形瓶颈做视觉分类项目很多人一上来就想着怎么把网络搭得花哨最后发现准确率上不去十有八九是数据集的问题。垃圾分类的数据集建设比模型搭建更耗时也更需要耐心。2.1 图像数据的采集与清洗我实验中用到的数据集来自两部分一部分是公开的垃圾分类图像集覆盖了塑料、纸张、金属、玻璃、电池、厨余等常见类目另一部分是我自己在实际场景中拍摄补充的用来弥补公开数据集和真实环境的差异。比如在室内灯光下拍的照片和室外自然光线下拍的同一类垃圾模型学到的特征可能是不一样的。数据清洗是我要特别提醒的一个环节。公开数据集的标签不是百分百可信的我实际抽查的时候发现大约有3%5%的图片存在标签错误比如把纸杯标成了塑料杯把玻璃瓶标成了陶瓷器皿。训练之前花时间做一轮人工抽查非常有必要这个时间省不得。我当时的做法是对每一类随机抽100张图做人工复核发现错误标签直接剔除对于类别边界模糊的图比如沾满污渍的外卖盒更是直接删除——因为这种连人都难以准确判断的样本强行训练只会增加模型的歧义。2.2 预处理与数据增强策略图像预处理直接决定了模型看到的数据形态。垃圾图片虽然不像自然场景那么复杂但依然存在尺寸不一、光照变化、背景干扰等问题。我采用的标准预处理流程是将所有输入图像统一缩放到224x224这个尺寸是MobileNet的标准输入尺寸也适合自定义CNN像素归一化到[0,1]区间同时做了减均值操作让模型更快收敛。数据增强对垃圾分类这个任务来说绝对不是凑数用的。因为垃圾图片的拍摄角度变化其实很大桶内视角、俯拍视角、倾斜视角都有。我采用的增强方案包括随机水平翻转概率0.5随机裁剪裁掉原始图像0.8~1.0的区域再缩放到原始尺寸亮度、对比度、饱和度的随机调整少量随机旋转只在±15度范围内因为垃圾图片有明确的方向性转超过90度是不符合实际的这里有个经验旋转角度千万别开太大。我试过把旋转范围开到±30度反而把验证准确率拉低了两个点因为旋转会让一些长方形的物体变得“横竖不分”反而误导了模型。项目里尤其要注意保持图像内容和真实摆放方式的一致性。2.3 类别不均衡问题的处理垃圾分类数据天然存在类别不均衡的情况果皮、塑料瓶这类量很大而废旧电池、过期药品的量就少得多十几倍的样本量差异很常见。如果不做任何处理模型会对大类极度偏科小类的召回率会非常难看。我的处理方案是对样本量不足的类做过采样简单来说就是把这些类的图片在数据管道里重复读入配合随机增强相当于每个epoch都在生成略有不同的新样本。同时对样本量过大的类别做下采样控制在每类不超过所有类别中位数的1.5倍左右。另一个有效的技巧是在损失函数上做文章给样本量少的类别分配更高的权重让模型在优化时对这些类的错误更加敏感。3. 自训练CNN模型的搭建与训练实操既然要自训练CNN模型那就不能只调用一个现成的接口草草了事必须把网络的层结构和参数设计讲透让读者能切实复现。3.1 网络结构设计与参数计算我设计的CNN结构并不追求深而是追求“合适”。因为垃圾图片的特征其实相对简单纹理、形状、颜色是最主要的判别信息不需要像ImageNet竞赛那样用几百层的深度来捕捉极其抽象的语义关系。最终的结构是七层卷积可视作一个改良版的VGG风格网络model tf.keras.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), paddingsame, activationrelu, input_shape(224, 224, 3)), tf.keras.layers.Conv2D(32, (3, 3), paddingsame, activationrelu), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(64, (3, 3), paddingsame, activationrelu), tf.keras.layers.Conv2D(64, (3, 3), paddingsame, activationrelu), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(128, (3, 3), paddingsame, activationrelu), tf.keras.layers.Conv2D(128, (3, 3), paddingsame, activationrelu), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(256, (3, 3), paddingsame, activationrelu), tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(num_classes, activationsoftmax) ])这里我用了全局平均池化而不是展平再全连接原因是全局平均池化能显著减少参数量降低过拟合风险。展平再接全连接的方式在VGG时代很常见但最终全连接层占用了超过70%的参数量却只学会了最后的分类映射作用并不大。换用全局平均池化之后整个模型参数量大约在400万左右比VGG16的1.38亿小了30多倍。3.2 训练配置与超参数选择训练配置方面我踩过的坑和总结的经验值得展开多聊几句。优化器选择的是Adam初始学习率设置为 0.001。但这里并不是简单地固定学习率跑完整个训练过程而是加了很关键的学习率衰减策略每经过5个epoch学习率乘以0.8。为什么要这样做因为Adam这种自适应优化器虽然前期收敛很快但到了后期很容易在最优解附近震荡固定学习率会加剧这种震荡导致最终精度上不去。我实测下来配合衰减策略的训练比固定学习率最终能提高2~3个百分点的验证准确率。Batch Size的选择也有讲究。我用的是32这个值在显存占用和梯度估计稳定性之间比较均衡。显存不够的话可以降到16但要注意Batch Size越小每个batch计算出的梯度噪声就越大训练曲线会更震荡所以小Batch搭配稍低的学习率会更稳。损失函数就是标准的分类交叉熵。如果只做四分类可以顺手在数类别时把最终输出层改成4但我保留了20个细分类交叉熵损失函数会自动处理这种多类别映射。注意在构建数据集时标签必须做one-hot编码可以直接用tf.keras.utils.to_categorical。3.3 训练效果评估与特征可视化训练过程我用EarlyStopping回调来控制Monitor验证集准确率Patience设为12个epoch一旦连续12轮没有提升就停止训练并恢复最佳权重。这要比手动判断什么时候停止训练靠谱得多人眼看图总会有偏差机器的监控则相对客观。训练完成后评估体系不能只看一个准确率指标。我同时生成了混淆矩阵和逐类召回率报告以判断各个垃圾类别之间的混淆模式。我发现项目里最常见的混淆出现在塑料瓶和玻璃瓶之间两者的轮廓和反光特征确实接近。这类混淆单靠调网络结构很难解决增补这两个类别的训练图片是最直接有效的方法。特征可视化也是自训练CNN独有的优势。我提取了第一个卷积层的卷积核响应图发现模型的浅层学到的主要是边缘和颜色纹理特征深层则更像是组合模式识别器。这有助于判断模型是否真的学到了有区分力的特征而不是靠“作弊”——比如发现模型对背景反光特别敏感那就说明训练集里带反光背景的图片过多需要调整数据分布。4. MobileNet迁移学习的搭建与微调实战如果说自训练CNN是“白手起家”那MobileNet迁移就是“站在巨人的肩膀上摘垃圾”。但迁移学习也有它自己的门道不是简单替换输出层就行。4.1 迁移学习的关键策略冻结与解冻MobileNetV2是默认的基座选择不只是因为它小更因为它用的是深度可分离卷积结构实际部署时算力开销比标准卷积小很多。在TensorFlow中加载预训练权重非常简单base_model tf.keras.applications.MobileNetV2( input_shape(224, 224, 3), include_topFalse, weightsimagenet ) base_model.trainable False model tf.keras.Sequential([ base_model, tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(num_classes, activationsoftmax) ])这里include_topFalse是关键它去掉的是ImageNet的原始分类头只保留特征提取部分。weightsimagenet则代表加载在大规模通用图像上预训练好的权重。全部设置好之后模型会自动把MobileNetV2的底图调整为224x224输入。训练时先冻结base_model只训练新加的全连接层。这个过程通常很快大概10个epoch左右损失就能平稳下来冻结阶段能让新的分类头先适配预训练特征的分布空间。之后的关键步骤是解冻部分底层网络做微调。解冻不能全解冻否则预训练学到的优质特征很快会被冲掉。我的策略是解冻base_model的后半段大约10个卷积块然后以很小的学习率去训练设置为原始初始学习率的十分之一。这一步极为关键全都是在这段微调里提升的。解冻阶段配合低学习率的原因在于特征提取器已经足够好了只需要在这个基础上精细调整去适配垃圾图像的细微纹理差异大学习率只会让更新幅度过大毁掉原有的特征结构。4.2 数据受限时的微调技巧迁移学习对数据量比较友好但数据少到一定程度依然会出问题。我实验里用了每类约150张图片来训练MobileNet迁移模型分类准确率在87%左右。而把数据量提升到每类400张之后准确率接近92%。如果你的场景数据有限我建议优先使用迁移学习因为即使数据量不大预训练模型内部的通用特征已经覆盖了绝大部分分类需要你只需要一个分类头学个映射关系即可。一个额外有用的技巧是训练数据量少时数据增强参数可以调得更激进一些。我在MobileNet的训练中把随机裁剪的缩放范围扩大到了0.7~1.0色彩抖动幅度也提升了这样相当于在同等数据上生成了更多有效变化样本。这样做的合理性在于模型本身已经有很强的特征表达能力更激进的增强并不会让它学不到东西反而类似于在强制模型从更广的像素变化中提取出类别相关的本质特征。4.3 两路模型的效果对比我的实测数据这里给出一组我实测的数据方便大家在选型时有直观感受自训练CNN7层ConvNet约400万参数验证集准确率约85.6%模型体积约18MBMobileNetV2迁移冻结训练验证集准确率约89.3%模型体积约9.4MBMobileNetV2迁移解冻微调验证集准确率约92.4%模型体积约9.4MB自训练CNN MobileNet融合验证集准确率约94.6%模型体积约27.4MB在推理速度上自训练CNN在普通CPU上一张224x224图片约需45毫秒MobileNet约需30毫秒融合后约75毫秒仍然满足实时性要求。如果用在手机端MobileNet的TFLite量化版本推理时间可以压缩到10毫秒之内。这条数据最直观地说明了为什么项目中要用迁移学习它不只是为了省训练时间更是在同等数据规模下能让准确率直接提升一个档次的可靠手段。但自训练CNN也并非没有存在价值当某些垃圾类别外观特征极其特殊且不在预训练分布的覆盖范围内时自训练CNN对特定特征的反应往往更敏锐这也是我最终选择两者融合而不是二选一的原因。5. 模型部署与实用化改造模型练得再好最终都要落到实际应用中检验。这个部分的要点已不再是模型的网络结构而是各类工程化处理。主要是模型压缩转换的问题这远比想象中重要。5.1 模型导出与TFLite转换TensorFlow生态最大的好处就是自带了一套模型优化导出工具。训练完成后我先把Keras模型转成标准的SavedModel格式然后使用TFLite Converter转换成移动端模型。converter tf.lite.TFLiteConverter.from_keras_model(model) converter.optimizations [tf.lite.Optimize.DEFAULT] converter.target_spec.supported_types [tf.float16] tflite_model converter.convert()我采取的优化策略是float16量化。这个方式不需要校准数据集用起来很省事模型体积直接减半精度损失大约在0.5%以内是性价比最高的选择。如果要追求极致小体积可以考虑动态范围量化或int8全量化但int8量化需要准备校准数据集否则量化区间切分不准精度可能掉好几个点。我在项目里最终采用的是float16兼顾部署体积和准确率。5.2 接口封装与推理实现真正做应用的时候模型的推理逻辑只是很小的一段代码更大头的是输入预处理和结果业务映射。我封装了一个预测模块核心流程为接收图像路径或numpy数组缩放到224x224归一化然后喂给模型推理拿到20个类别的概率后做置信度清洗把低于0.5的类别直接丢弃最后通过映射表转成四分类投放建议。这套推理流程在实验里稳定运行单张图片从输入到输出在树莓派级别的设备上约耗时120毫秒在普通PC上则仅需50毫秒。对于社区垃圾房门口的可视化交互屏、手持识别终端这些实际场景这样的速度体验已经足够流畅。5.3 部署后容易踩的暗坑模型部署之后遇到问题的概率比训练阶段还要大我挑几个实际遇到的暗坑说一说。一个是输入尺寸与通道数不一致的问题。TFLite转换后模型对输入尺寸有硬性要求但实际业务里过来的图片可能是任意尺寸。如果不做尺寸检查图片宽度和高度不符合224x224会直接报错更隐蔽的情况是部分图片有4个通道RGBA导致处理时形态不对。我最后的解法是在推理模块里做了一层强制防御先检查图片通道数如果是RGBA则删除Alpha通道再强制resize到224x224。这个防御层十分必要。另一个是类别映射表的维护。模型输出的数字类别索引和业务层的四分类映射必须一一对应稍一疏忽就会把回收物错判成其他垃圾。我最终把维护这个映射表改为从配置文件统一读取任何类别体系调整都只需改动配置文件无需重新编译或修改代码逻辑。这套做法在项目实战中省了很多反复排查的时间。6. 常见问题与排查技巧实录训练和落地的过程中遇到的问题远比顺利的时刻多。我整理了几个高频且典型的故障现象和排查思路这些经验比模型结构本身更有参考价值。6.1 训练集准确率接近100%验证集只有七成——过拟合了这是我早期自训练CNN时遇到的第一个大坑。原因很简单模型参数太多了数据量又不够模型把训练集当成“背诵材料”而不是去学抽象规律。排查和解决的方向按优先级排列先看数据量是否充足每类少于300张就要优先补数据其次再做增强加大Dropout的比率从0.3提高到0.5甚至0.6都值得一试检查全局平均池化是否使用了如果还是展平加全连接建议换掉增加正则化权重或者提前引入早停机制这些手段不用同时全上每改一步记录一次验证集准确率变化来做精细对比我实测下来往往只需要调整其中两项就能压住过拟合。6.2 验证集准确率上不去换模型也无效——先查数据模型换了好几种架构效果都卡在同一个水平线这里往往说明问题不在模型而在于数据本身。我遇到的实际案例是“干净玻璃”和“脏污玻璃”被标注成了同一个类别导致模型内部的特征集群互相打架怎么学都学不出一个清晰的决策边界。排查这类问题的方法是把预测出错的样本集中抽出来人为观察它们的共性很多标签错误的样本就会暴露出来或者存在类间视觉相似度极高的情况。解决思路要么是把容易混淆的类别合并成一个更宽泛的类别要么给这些混淆样本增加专门的细分类。我在项目中就是受到这个问题的启发把系统分成了粗粒度四类和细粒度多类两层结构。6.3 训练损失下降但准确率不稳定——学习率或批大小问题损失下降是用肉眼能看到的但准确率曲线却一直上下波动说明模型可能已经陷入了振荡状态。这时优先做两件事一是把学习率调低一个数量级比如从0.001降到0.0003二是把batch Size适当增大或减小观察曲线有没有变平滑。有时候准确率曲线波动大一点不一定是坏事它可能只是验证集本身太小、样本分布偏差较大所致。但如果波动幅度超过5个点一定要重视说明模型本身并不稳定直接投入使用是危险的。6.4 推理阶段频繁崩溃——TFLite兼容性排查推理阶段崩溃大概率是输入数据格式不符导致的。我遇到过的一个典型问题在训练时使用numpy读图后归一化到[0,1]但在推理代码里忘了归一化直接输入了[0,255]的原始像素结果模型输出概率分布完全乱掉。排查步骤很简单把输入图像经过的所有处理步骤打印出来检查数值范围、shape、dtype逐一和训练阶段的数据管道对比。所有训练时做过的预处理在推理阶段都必须逐一还原一个都不能省。7. 最后再分享一点我个人的实操体会做这个系统的过程里我最深的感受是垃圾识别准确率的天花板往往不是由网络结构决定的而是由数据质量和部署细节决定的。同样一批图在MobileNet预训练权重上做到90%以上自训练CNN做到85%左右但要把这两个模型真正嵌入到社区垃圾房的交互屏、车载识别终端里还是会遇到各种边界情况——光照角度变了、垃圾袋没拆、物品被压缩变形。模型的泛化能力永远追不上市井场景的复杂性。所以我会建议后来者如果面向的是移动端或嵌入式设备优先用MobileNet迁移学习路线越快出效果越好如果面向的是对响应速度不敏感的云端分析或者教学演示自训练CNN能提供更强的可解释性。要是条件和场景都允许那就做融合推理两个模型的结果互相印证稳定性明显更好。一个小技巧是不要急着把所有数据一次性喂进去。先用每类100张左右的少量数据跑通整个训练到部署的完整流程形成“数据标注-训练-转换-部署-反馈”的最小闭环再逐步扩大数据规模、迭代模型效果。这样能在项目前期就暴露工程链路中的所有问题避免等到数据集扩到上万张时才发现部署环节还有通道数、归一化、类别映射之类的坑要填。垃圾分类系统永远不会是一个“训练完就结束了”的项目。后续可以发展的方向很多比如加上摄像头实时视频流检测逐帧推理结合帧间平滑加入语音交互检测到垃圾后直接说出投放指引甚至可以做嵌入式的离线一体机把TFLite模型直接烧录进智能回收箱的主控板。这一行只要肯打磨数据、优化细节效果是能一步步做上去的。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表