ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于深度学习的日用品图像分类系统实战:ResNet迁移学习与PyTorch实现

基于深度学习的日用品图像分类系统实战:ResNet迁移学习与PyTorch实现 简介图像分类是计算机视觉领域的基础任务其核心在于让模型理解图像内容并输出语义标签。传统方法依赖手工特征而深度学习通过多层卷积网络自动提取从边缘到纹理的抽象特征显著提升了识别精度。实际工程中受限于数据规模和算力从零训练卷积神经网络往往难以收敛迁移学习成为主流方案——利用在ImageNet上预训练的ResNet等模型作为特征提取器通过微调适配特定领域。本文以日用品识别为场景结合PyTorch框架系统讲解图像分类项目的完整落地流程从数据集构建、数据增强策略到分阶段微调技巧、训练参数调优再到模型部署与常见坑点排查。该方案适用于商品识别、质量检测、智能零售等场景为初学者提供了一套可复现的工程实践路径。 毕业设计那会儿我拿到“基于深度学习的日用品图像分类与识别系统”这个题目时第一反应是这不就是给一堆瓶瓶罐罐拍照片然后让电脑认出来嘛。但真正动手做下去才发现从数据集构造到模型调优再到最后能跑通一个完整的识别demo每一步都有不少坑等着你。这篇博文就当作一个完整复盘把我从零搭这个系统的过程、踩过的雷、以及最后沉淀下来的源码和说明文档的设计思路一次性讲清楚。无论你是正在选题的本科生还是想快速上手图像分类实战的初学者这篇文章应该都能给你一些能直接抄作业的参考。先说结论这个项目虽然名字听起来唬人但技术栈非常经典就是“ResNet系列预训练模型 迁移学习 PyTorch训练管线 Flask/命令行推理”。整套流程走下来你不仅能搞明白卷积神经网络是怎么工作的还能亲手调出一个在日用品数据集上准确率超过90%的分类器。下面我把从设计到落地的每一个环节拆开讲。1. 项目整体设计与技术选型思路1.1 为什么是“图像分类”而不是“目标检测”题目里写的是“日用品图像分类与识别系统”很多同学一看“识别”两个字第一反应就是要做目标检测让系统用方框把每个物体标出来。但仔细看题目“分类与识别”其实是两个递进的任务分类是判断整张图片属于哪个类别识别则是在分类的基础上进一步确认是具体的什么东西或者哪个品牌。对于本科毕业设计来说这个题目的核心得分点在于“分类”也就是给一张包含日用品的图片系统输出它是“洗发水”“牙刷”还是“毛巾”等等。如果一开始就上YOLO或者Faster R-CNN做检测数据标注量会翻好几倍。一个检测框需要画坐标一张图里可能有多个物品而分类只需要整理好图片文件夹就能开训。所以从题目定位和时间成本上来讲用图像分类来切入是性价比最高的选择。1.2 核心框架与模型选型为什么锁定ResNet图像分类的模型库非常庞大从早期的AlexNet、VGG到后来的ResNet、DenseNet再到近两年的EfficientNet、ConvNeXt和Vision Transformer。作为本科毕设我的建议是别追新就用ResNet。原因有三个。第一ResNet的残差结构非常经典和深度学习课程里重点讲的理论能一一对应上。你在论文里写“引入残差连接缓解梯度消失问题”评审老师一看就知道你是真懂这个结构而不只是会调用一行models.resnet50()。第二ResNet的预训练权重非常成熟在ImageNet上训练好的参数可以直接拿来用迁移学习的效果非常好。日用品和ImageNet里的常见物体在特征层面上有大量共通之处比如边缘、纹理、形状。第三ResNet的模型大小适中。ResNet18和ResNet34在CPU上也能跑推理不需要非得租GPU服务器。我最终选了ResNet50精度和速度的平衡点最好训练一轮大概十几分钟最后的准确率也能做到比较理想。1.3 迁移学习的引入与微调策略从零训练一个深度卷积网络在本科的时间范围内几乎不可能拿到理想效果因为数据量和算力都不够。更合理的做法是加载在ImageNet上预训练好的权重然后做微调。微调策略有两种常见路径第一种是把backbone全部冻结只训练最后的全连接分类层。这种方式训练极快适合数据集非常小每类几十张的情况。第二种是解冻backbone的后几层用较小的学习率一起训练适合数据量稍大、希望模型更好地适应日用品特征分布的情况。我实际采用的是“分阶段微调”先冻结所有卷积层只训练新加的全连接层让分类头先收敛到稳定状态然后解冻最后一组残差块layer4将整网学习率调低一个数量级继续训练。这样既避免了底层特征被破坏又能让高层语义特征更贴合日用品的纹理和形状。注意解冻层数越深训练显存占用越大对学习率的要求也越苛刻。如果解冻后损失出现明显震荡优先检查学习率是否过大。2. 数据集质量比数量更重要2.1 自建数据集的思路与采集标准图像分类项目中最容易翻车的环节不是模型而是数据集。Kaggle和ImageNet上虽然有很多现成数据集但“日用品”这个范畴太宽泛了没有完美的公开数据集直接覆盖我想要的类别。最终我决定自建一个核心类别的数据集。类别怎么定我觉得要满足两个条件第一是日常且常见容易搜集第二是类别之间有区分难度不能太简单否则展示不出模型的判别能力。我选了10类洗发水、沐浴露、洗衣液、洗洁精、牙膏、牙刷、毛巾、纸巾、水杯、碗。这10类既有瓶装液体也有织品类和陶瓷类从形状、颜色到纹理都有比较大的差异既能撑起论文的实验表格又不会难到让模型完全学不动。采集途径主要有三个一是从开源数据集里挑出匹配类别的图片比如ImageNet的某些子类二是用爬虫脚本从图片网站抓取抓完后人工筛掉低分辨率、带水印、主体不清晰的图三是自己用手机拍摄补充特别是对于前两类来源中数量偏少的类别。最终每类整理到300到500张图片共4000多张。2.2 数据清洗的教训不能只按文件夹划分数据这里有个非常重要的教训图片下载下来以后不能文件名看着像什么就放到哪个文件夹里。爬虫抓图经常会出现“搜洗发水结果混进来一堆护发素”的情况因为很多商品图是套装图。我第一轮清洗只看了文件名前缀结果训练出来测试集准确率虚高但实际一测新照片就露馅。后来我全部重新过了一遍把每张图都肉眼检查删除那些包含多个主体、或者物体占比过小的图片。这个过程很枯燥但直接决定了模型真实泛化能力。建议你也别省这一步。2.3 数据划分与目录结构数据划分我严格按照7:1.5:1.5的比例分成train、val、test三个子集。这里要特别提醒划分动作要在“类内随机”做而不是先分好文件夹再随机搬移否则可能出现同一个类别的训练集和验证集图片来自身份完全不同的数据源导致验证集不能反映训练集的分布。目录结构参考如下data/ ├── train/ │ ├── shampoo/ │ ├── body_wash/ │ ├── laundry_detergent/ │ └── ... ├── val/ │ └── ... └── test/ └── ...2.4 数据增强既要丰富也要克制深度学习模型全靠数据“喂”出来数据增强是必须的。我用PyTorch的torchvision.transforms做了如下组合随机缩放裁剪到224x224随机水平翻转随机旋转±15度颜色抖动亮度、对比度、饱和度微调归一化使用ImageNet的mean和std需要克制的是旋转角度和颜色抖动的幅度。日用品图片中物体方向的分布有一定规律比如沐浴露瓶子大多是竖直的随机旋转90度会生成大量不真实样本。训练时模型看到的是被“掰弯”的瓶子测试时又要识别正常图片反而会掉点。我最终把旋转限制在±15度颜色抖动幅度控制在0.2以内效果最稳。注意数据增强只在训练集上使用验证集和测试集只做Resize、CenterCrop和归一化否则验证结果会失真。3. 训练细节与关键参数调优3.1 数据加载与迭代器构建训练时我用的batch size是32图片尺寸统一resize到256后再随机裁剪成224。为什么不直接把全图resize到224因为随机裁剪相当于一种scale抖动能让模型对物体在画面中的大小变化更鲁棒。测试阶段则用CenterCrop保证评估结果稳定可复现。数据加载部分直接用torchvision.datasets.ImageFolder就能搞定它会自动依据子文件夹名生成类别标签这一点很省事。在训练脚本里数据加载器设置num_workers4pin_memoryTrue能减少CPU和GPU之间的数据传输瓶颈。3.2 优化器选择与学习率策略优化器方面我对比了Adam和SGD。Adam收敛快前期损失下降非常迅速但后期精度容易在某个区间打转。SGD加momentummomentum0.9虽然前期看起来慢但最终收敛精度更好。对于毕业设计我想两个优化器都出现在论文实验里做对比所以主实验用SGD对比实验加了一个Adam。学习率策略SGD的初始学习率设为0.01配合StepLR每10个epoch乘以0.1。不过后来我换成了CosineAnnealingLR效果更好训练过程会更加平滑。如果是从零训练学习率设0.1太高但因为是微调预训练模型0.01到0.02比较合适。提示如果使用冻结backbone 只训练分类头的策略学习率可以设大一点0.01。一旦解冻卷积层要立刻把学习率降到原来的十分之一否则预训练权重会被破坏损失曲线会直接飞掉。3.3 损失函数与类别平衡多分类任务最常用的就是交叉熵损失。Pytorch里nn.CrossEntropyLoss()自带softmax所以模型最后一层不需要额外加log_softmax。如果某些类别图片数量特别少可以给损失函数的weight参数传入各类别样本数的倒数让Loss对小样本类别更加敏感。我这个数据集因为每类都基本在300到500张分布不算极端所以直接用默认的等权损失。3.4 训练过程中的观察指标训练时除了关注loss更要关注每个epoch在验证集上的精度和召回率。我每次epoch结束时都会计算一次验证集准确率同时保存当前最优模型。判断依据是验证集表现不是训练集loss这一点非常重要。如果训练集loss一直降、验证集acc上了个台阶又掉下来那就是过拟合信号要赶紧早停或者加强正则化。训练脚本里我还会记录每一类的precision、recall、F1-score用于后面分析模型混淆的地方。对于日用品的识别有些类别极易混淆比如“洗衣液”和“洗洁精”都是包装鲜艳的液体瓶子仅靠颜色区分很容易出错。3.5 硬件配置与训练时长训练硬件我用了一张NVIDIA GTX 1660 Super显存6GB。ResNet50在batch size为32的情况下显存占用大概在4GB左右正好塞得下。整个训练跑了50个epoch加上解冻后的微调阶段总耗时大约1.5小时。如果你手头只有CPU也不是不能做但建议把模型换成ResNet18batch size降到16训练时间会成倍增加但已经足以完成毕业设计的流程。4. 核心代码结构拆解从训练到推理4.1 训练脚本的骨架整个项目源码遵循“训练、验证、推理”分离的思路核心文件如下project/ ├── train.py ├── valid.py ├── inference.py ├── models.py ├── dataset.py ├── config.py ├── requirements.txt └── README.mdconfig.py统一管理所有超参数比如数据路径、类别数、batch size、初始学习率、训练轮数等。把超参数集中到一个文件里调参的时候不用在训练脚本里到处翻这个习惯建议从一开始就养成。train.py的核心训练循环长这样简化版import torch import torch.nn as nn import torch.optim as optim from torchvision import models, transforms from torch.utils.data import DataLoader from dataset import build_dataset def train_one_epoch(model, dataloader, criterion, optimizer, device): model.train() running_loss 0.0 correct 0 total 0 for inputs, labels in dataloader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() * inputs.size(0) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() epoch_loss running_loss / total epoch_acc correct / total return epoch_loss, epoch_acc模型构建部分models.py里我用torchvision.models.resnet50加载预训练权重然后把最后一层全连接替换成自己定义的结构import torch.nn as nn from torchvision import models def build_model(num_classes, pretrainedTrue): model models.resnet50(pretrainedpretrained) in_features model.fc.in_features model.fc nn.Sequential( nn.Dropout(0.3), nn.Linear(in_features, 256), nn.ReLU(inplaceTrue), nn.Dropout(0.3), nn.Linear(256, num_classes) ) return model4.2 解决显存不足的实用技巧很多同学的机器显存有限训练时跑几个batch就报CUDA out of memory。除了缩小batch size外还有几个实用技巧用with torch.no_grad()包住验证阶段的推理因为验证不需要计算梯度注意在训练循环中及时释放中间变量如果PyTorch版本支持可以在forward里配合使用torch.cuda.amp做混合精度训练ResNet50在混合精度下显存占用能降低三分之一左右。4.3 推理脚本与一键预测训练完成后inference.py负责加载保存的权重对单张图片进行预测。核心要点是加载图像时要应用与验证集一致的预处理流程否则输入端分布不一致预测结果会莫名其妙地变差。def predict_image(image_path, model, class_names, device): transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) image Image.open(image_path).convert(RGB) tensor transform(image).unsqueeze(0).to(device) model.eval() with torch.no_grad(): outputs model(tensor) probs torch.softmax(outputs, dim1) confidence, pred_idx torch.max(probs, 1) return class_names[pred_idx.item()], confidence.item()4.4 如何把模型包装成简单的识别系统“系统”这个词在毕业设计里通常意味着得有一个能跑起来的界面或接口。为了兼顾工程量和展示效果我用Flask写了一个极简Web服务上传图片后端调用训练好的模型推理返回类别名称和置信度。前端只有一个HTML页面不需要框架十几行代码就够。上传按钮、展示区、预测结果三个模块清晰明了。如果觉得Web框架太重也可以直接用命令行交互python inference.py --image test.jpg --checkpoint best_model.pth这样既能演示又不会把毕设变成前端项目。答辩的时候重点永远在后端模型而不是页面多花哨。5. 常见问题与排查技巧实录5.1 训练集Loss下降但验证集准确率极低这是我第一次跑通整个流程时遇到最大的坑。训练集准确率到了95%验证集准确率只有60%左右。一开始怀疑过拟合后来发现是数据划分代码写了低级bug划分前没有把所有图片路径打乱导致某个类别的训练集和验证集来自同一批爬取任务在数据分布上高度一致而真正的测试分布完全不一样。重新shuffle之后训练集和验证集的准确率都回归正常。所以建议你在划分数据之前先对类别做一个完整list随机打乱后按比例切分而不是用文件顺序直接切片。5.2 混淆矩阵显示特定类别互相分不清“洗衣液”和“洗洁精”这两个类别的召回率一直上不去。我打印出混淆矩阵后发现大部分错误都是把“洗洁精”预测成了“洗衣液”。原因是这两类商品的包装设计非常接近都是大口径塑料瓶颜色偏绿或偏蓝标签图案复杂。改进方案有两个一个是在数据集中增加这两类图片的数量特别是多抓一些不同品牌、不同颜色的包装另一个是让模型更关注局部特征比如增加随机裁剪的尺度迫使模型去学习瓶身文字或喷嘴形状等细节。最终两种措施组合后该类别的召回率提升了约12个百分点。5.3 模型加载报错state_dict键名不匹配如果你在推理阶段直接加载训练时保存的权重偶尔会遇到Missing key(s) in state_dict的报错。这通常是因为训练时模型经过了DataParallel包装所有参数名的前缀多了module.。解决办法是保存权重时直接保存model.state_dict()加载时用torch.load后手动去掉前缀或者保存时就用model.module的状态。5.4 图片预处理不一致导致推理掉点训练时用的是随机裁剪加增强推理时却只做了Resize忘记CenterCrop导致输入尺寸匹配但图像内容区域完全对不上。这个细节看起来不起眼但影响非常大。固定的做法是推理阶段的预处理完全对齐验证集的transform一步都不能多一步也不能少。6. 项目说明文档的撰写要点6.1 README要写到什么程度“含源码与说明”是题目要求的一部分所以说明文档绝不能随便写几行就交差。我的README结构参考了开源项目的写法项目背景、环境依赖、数据准备、训练与测试脚本运行方法、模型结构说明、实验结果表、项目目录树、常见问题。答辩老师拿到压缩包后只看README就能快速复现你的结果这个印象分会非常足。6.2 实验记录与表格化呈现论文里放实验结果时建议以表格形式展示不同模型或不同策略的对比。我记录了以下指标的对比ResNet50从头训练、ResNet50冻结分类头、ResNet50分层微调、ResNet50加数据增强、ResNet50加MixUp增强等实验组合。每个实验记录Top-1准确率、Top-5准确率、参数量、单轮训练耗时。这样表格一出来整个毕设的“工作量”一目了然。6.3 环境配置与复现指引在requirements.txt里我固定了主要依赖版本Python 3.8.10、PyTorch 1.12.1、torchvision 0.13.1、opencv-python 4.6.0.66、Flask 2.2.2。深度学习框架的版本兼容性非常敏感固定版本信息是负责任的做法否则过几个月读者装环境时可能就装不上跑不起来。7. 从毕设到实战还能怎样扩展这个题目做完之后你会发现整套流程完全可以平移到其他图像分类场景比如垃圾分类、植物叶片病害识别、商品标签质检。模型不需要换只要换数据集重新跑一遍训练流程就行。后续如果想把系统做成实时识别可以尝试在终端设备上部署TensorRT或ONNX Runtime模型推理速度会有几十倍的提升。如果想让系统支持多物品同时识别就需要从分类升到检测那又是另一个项目了。最后再分享一个个人经验做这种深度学习项目最忌“闷头跑模型”。从一开始就把数据清洗、模型设计、实验对比、文档记录四条线同步推进每天记录实验参数和结果。哪怕中间某一步效果不好复盘时也能快速定位问题出在数据还是模型而不是靠猜。这样整个毕业设计做下来你收获的不仅仅是一套能跑的代码更是一套完整的工程方法论。本文还有配套的精品资源点击获取
返回列表