资讯动态

深度学习毕设实战:基于CNN的花卉枯萎识别项目全解析

发布时间:2026/10/17 9:01:41 来源:尧图企业网站定制
深度学习毕设别再卷目标检测了花卉枯萎识别这个方向论文好写、答辩好讲、代码量适中关键是你能完整走完“数据-训练-调优-部署”全流程。我前前后后带过好几个做类似项目的同学也亲手复现过不少次今天就把这个项目从选题到落地的完整思路、参数细节和踩坑记录全给你理一遍。先给这个项目定个性用Python写基于CNN卷积神经网络做花卉是否枯萎的二分类或者多分类比如健康、轻度缺水、重度枯萎数据集就是各种花朵的图片。这个任务看起来小但它把深度学习入门需要掌握的核心环节全串起来了——图像预处理、数据增强、卷积网络结构设计、训练调参、模型评估甚至还能接一个简单的可视化界面。作为本科毕设或者课程项目属于性价比很高的选择。1. 项目整体设计与思路拆解1.1 核心任务定义与技术路线选择“识别花卉是否枯萎”本质是一个图像分类任务。但同样做分类你可以用传统机器学习HOG特征SVM也可以用深度学习的预训练模型直接做迁移学习或者自己从零搭一个浅层CNN。这里我强烈建议自己搭建CNN为主迁移学习为辅两者都做形成对比。为什么这么设计站在毕设答辩的角度看如果直接用现成的ResNet50做迁移学习准确率确实能到很高但创新点和工作量看起来就很薄。而从零开始搭CNN你能讲清楚每一个卷积层、池化层、全连接层的作用能展示参数调整对结果的影响这些在论文里都是非常直观的图表素材。同时为了证明你的模型有改进空间可以再跑一个迁移学习的对比实验这就是典型的“两种方案对比”论文结构立刻丰满起来。任务本身的核心挑战有几个花卉叶片枯萎的表征非常多样有的是发黄有的是卷曲有的是下垂有的只是色泽暗淡光照条件、拍摄角度、背景复杂度也会引入大量干扰再加上数据集规模往往不大几百到几千张这就会牵扯出数据增强、正则化、迁移学习等一系列问题。所以这个项目虽然入门但想做好并不简单恰恰适合作为毕设来展现你对问题域的理解深度。1.2 技术栈选型与版本选择技术选型方面比较固定但也有几个细节值得注意Python 3.8~3.11 PyTorch 1.10~2.x或 TensorFlow 2.x OpenCV 4.x图片读取与预处理 NumPy、Pandas数据处理与统计分析 Matplotlib训练曲线和混淆矩阵可视化 torchvision预训练模型与常用数据集工具PyTorch和TensorFlow二选一我更推荐PyTorch。原因很实际毕设阶段你需要频繁调试模型结构、打印中间层输出、灵活修改训练逻辑PyTorch的动态计算图调试体验更好而且如果你后面想加注意力机制、自定义损失函数PyTorch的生态资料也更多。不过如果你导师的主方向是TensorFlow系那用TensorFlow也没问题核心思路完全一致只是API写法差异。开发环境建议直接用Anaconda建虚拟环境别一股脑装进base环境后面包冲突了哭都来不及。实测下来PyTorch的CPU版和GPU版在推理速度上差距很大训练哪怕只是几千张图的二分类任务有GPU还是比CPU快得多建议先确认实验室或者自己电脑有没有可用的NVIDIA显卡。1.3 项目文件的模块化结构项目别写成一个大脚本后面改起来贼痛苦。建议从一开始就按模块拆分flower_wilt_detection/ ├── data/ │ ├── raw/ # 原始图片 │ ├── processed/ # 划分后的训练集/验证集/测试集 │ └── augmented/ # 增强后的图片可选 ├── src/ │ ├── dataset.py # 数据加载与预处理 │ ├── model.py # CNN模型定义 │ ├── train.py # 训练脚本 │ ├── evaluate.py # 评估与可视化 │ └── predict.py # 单张图片推理 ├── checkpoints/ # 模型权重保存 ├── logs/ # 训练日志 └── requirements.txt这种结构的好处是后期写论文、画流程图、做演示的时候想改哪里改哪里而且显得工程化能力在线。2. 数据集构建与预处理策略2.1 数据来源与标注方案数据是这种项目最大的坑。公开数据集里专门做花卉枯萎的其实不多常用的花卉分类数据集比如Oxford Flowers是区分不同种类花不是区分健康/枯萎状态。所以通常的做法是自己采集用手机拍不同状态的花卉照片按照健康/枯萎两个类别归档。网络爬取在开放图库按关键词检索下载后人工筛选。公开数据集合集把多个花卉数据集中健康状态和枯萎状态比较明显的图片挑出来。我建议一段操作下来以自采公开混合为主总量控制在1000~3000张比较合理。太少模型容易过拟合太多你人工清洗的精力成本又太高。标注这一块二分类只需要建两个文件夹健康放一个枯萎放一个省去标注工具的时间。如果你想做更细一点可以分成三类健康、轻度缺水、严重枯萎这在后期做细粒度分析时内容会更丰富但对标注一致性要求更高容易出现边界模糊的情况。2.2 数据清洗与图片规范化的几个细节数据清洗往往决定项目的上限很多同学直接跑代码准确率上不去回头才发现数据里一堆问题。几个必须注意的点尺寸统一。CNN输入要固定尺寸常见的是224x224或者128x128。前者是标准ImageNet尺寸便于和预训练模型对接后者计算量更小自己搭建的浅层网络用128x128足够了。我一般建议直接统一到224x224虽然训练慢一点但对比实验时的兼容性更好。去除坏图。模糊图、纯背景图、有大量遮挡的图训练时全是噪声。写个脚本输出所有图片的尺寸和通道数把异常图片单独列出来手动确认。背景干扰处理。花卉图片背景五花八门如果全是复杂背景模型可能会去学背景特征而不是花本身的特征。可以考虑用OpenCV做简单的背景裁剪或中心裁剪把花朵主体放到画面主要区域但这个操作别太狠否则真实场景下泛化能力反而下降。类别平衡。健康类和枯萎类数量如果悬殊太大训练会倾向于预测多数类。简单的办法就是控制两类比例接近1:1如果实在样本不均衡训练时给少数类更高的损失权重或者做针对性的数据增强。2.3 数据增强策略数据增强是这个项目最值得大书特书的一环因为自采数据量通常不够。常用的完整增强策略如下from torchvision import transforms train_transforms transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0.05), transforms.RandomAffine(degrees0, translate(0.1, 0.1)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])这套增强配方基本覆盖了拍摄角度变化水平翻转、旋转、平移、光照变化亮度、对比度、饱和度、轻微形变等真实场景中的噪声来源。Normalize时的均值和标准差是三通道ImageNet的标准值直接用就好不用自己重新算。验证集就别做随机增强只做Resize和Normalize保证评估的稳定性。数据增强不是越多越好。我之前试过把Rotation角度开到90度结果模型把倒着的花也当正常输入反而学不到关键特征。15度上下的小角度扰动足以覆盖手机拍摄时的轻微倾斜又不至于改变花卉的语义。3. CNN模型设计与核心参数配置3.1 一个实用的基线网络结构自己搭CNN时不用追求深度重点是把结构讲清楚。这里给出一个我在类似项目里反复使用的基线结构精度不错、训练快、显存占用低import torch.nn as nn class WiltCNN(nn.Module): def __init__(self, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(64, 128, kernel_size3, padding1), nn.BatchNorm2d(128), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(128, 256, kernel_size3, padding1), nn.BatchNorm2d(256), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(256, 128), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(128, num_classes), ) def forward(self, x): x self.features(x) x self.classifier(x) return x这个网络的设计逻辑可以这样理解卷积层逐步增加通道数32→64→128→256是因为浅层学边缘、纹理这些基础特征通道数不用太多深层学更语义化的特征需要更多通道来承载。每层卷积后面跟BatchNorm作用是稳定训练、加速收敛同时能起到一定的正则化效果。池化层逐级降低特征图尺寸减少计算量并增强平移不变性。最后用AdaptiveAvgPool2d把任意尺寸的特征图压成1x1再接全连接层。3.2 训练参数的参考基准在训练参数调优上我给大家一套可以直接上手的基准配置在此基础上再做针对性调整超参数推荐值说明输入尺寸224x224兼容性强便于后续对比预训练模型Batch Size32显存不够时降到16影响不大初始学习率0.001Adam优化器常用起点效果不好再降到0.0005优化器Adam自带自适应学习率毕设阶段省去手动调节的麻烦损失函数CrossEntropyLoss多分类/二分类的标准选择Epochs30~50配合早停策略避免死板跑完学习率调整StepLR每10个epoch乘以0.1让收敛更稳训练/验证划分7:1.5:1.5训练、验证、测试三部分分开训练的时候用一个最基本的早停策略验证集准确率连续5个epoch不提升就保存最优模型并停止训练。这样能避免训练后期过拟合同时省时间。3.3 损失函数与评估指标怎么选二分类问题常用交叉熵损失这没什么好说的。但评估指标不要只看准确率特别是样本不平衡的情况下准确率会骗人。要同时看精确率、召回率和F1分数尤其是枯萎这个类别漏判枯萎的花被识别成健康和误判健康的花被说成枯萎的实际代价不一样。在代码里直接调用sklearn的classification_report就能拿到这些指标然后配合混淆矩阵可视化答辩时这张图非常能说明问题。我一般还会把训练过程中的train_loss、val_loss、val_acc都记录下来画成三条曲线论文实验部分的主图就有了。4. 训练过程与核心环节实现4.1 数据集加载与划分的实现数据划分一定别用随机打乱后取前80%这种粗暴方式容易把所有同一时间段拍的照片都分到一个集合里导致验证集和训练集分布不一致。正确做法是按文件夹随机抽样保证每个类别的比例在训练集和验证集中都一致。直接PyTorch内置的ImageFolder就能按文件夹读取数据配合random_split实现按比例划分。有一点要注意划分时设定随机种子保证每次运行得到相同的划分结果这对于复现实验非常关键。import torch from torch.utils.data import random_split from torchvision import datasets dataset datasets.ImageFolder(rootdata/processed, transformtrain_transforms) train_size int(0.7 * len(dataset)) val_size int(0.15 * len(dataset)) test_size len(dataset) - train_size - val_size generator torch.Generator().manual_seed(42) train_dataset, val_dataset, test_dataset random_split( dataset, [train_size, val_size, test_size], generatorgenerator )注意ImageFolder会按照子文件夹名称自动生成类别标签所以数据目录结构一定要清晰比如data/processed/wilted和data/processed/fresh别把文件夹命名搞反了。4.2 训练主循环的骨架代码训练主循环网上版本很多但毕设阶段最好自己写既加深理解也方便自定义细节。核心骨架大概长这样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这个循环里有个小细节计算loss的时候乘了inputs.size(0)即当前batch的样本数为的是在计算整个epoch的平均loss时每个样本的权重一致而不是每个batch的权重一致。如果不乘batch大小当最后一个batch数量不满时平均loss会有轻微偏差虽然不影响大局但追求严谨的话还是乘上好。4.3 迁移学习对比实验的做法自己搭的CNN跑完一轮后强烈建议加一组迁移学习对比实验。方法是在torchvision里加载在ImageNet上预训练好的ResNet18或ResNet50把最后的全连接层替换成输出维度为2的新层然后冻结前面所有层的参数只训练新加的全连接层。from torchvision import models import torch.nn as nn model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) num_features model.fc.in_features model.fc nn.Linear(num_features, 2) # 冻结除fc以外的所有参数 for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True冻结参数训练速度和显存占用都低而且因为预训练模型的特征提取能力已经很强只训一个全连接层也能拿到很高的准确率。这样对比实验就能说明两个问题自建CNN在参数数量少很多的情况下能获得可比精度而迁移学习则展示了引入外部知识带来的性能增益。论文里的讨论部分就有话写了。如果时间充裕还可以尝试微调策略不冻结所有层而是以很小的学习率比如0.0001训练全部参数通常准确率还能再涨2~3个百分点。4.4 训练过程中的观察与早停实操中我习惯每训练完一个epoch就把训练日志打印出来同时实时更新TensorBoard或者matplotlib绘图。观察训练曲线有个重要经验如果训练loss持续下降但验证loss先降后升这就是过拟合的信号早停在这个节点就很重要。另外一个常见情况是训练loss下降很慢甚至不降这时不要盲目调大学习率先检查数据有没有问题比如Normalize参数写错、标签错位、数据增强过度导致图片失真等等这些问题我后面专门列一节。5. 模型评估与可视化5.1 测试集上的指标解读训练完成后在划分好的测试集上做最终评估。注意测试集的数据划分要在训练前就确定好并且中间不能动否则评估结果就不可信了。测试集评估的代码不多但整理输出的时候要用心把每类别的精确率、召回率、F1值都打出来。实际经验是如果测试集准确率比验证集准确率低很多多半是数据划分出了问题比如同一批图片部分进了验证集部分进了测试集或者测试集和训练集分布差异太大。遇到这种情况首先检查划分逻辑和数据清洗。5.2 混淆矩阵与训练曲线可视化混淆矩阵可以直接用sklearn的ConfusionMatrixDisplay省得自己用matplotlib画。这个图在答辩时非常直观能看出模型具体在哪些样本上产生混淆。训练曲线的绘制要注意把训练、验证的信息分开画在同一个图里横轴为epoch纵轴分别画loss和accuracy两张子图。保存成高分辨率的PNG放进毕业论文里这比任何文字描述都有力。下面是一个简单的绘制骨架import matplotlib.pyplot as plt plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(train_losses, labelTrain Loss) plt.plot(val_losses, labelVal Loss) plt.xlabel(Epoch) plt.ylabel(Loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(train_accs, labelTrain Acc) plt.plot(val_accs, labelVal Acc) plt.xlabel(Epoch) plt.ylabel(Accuracy) plt.legend() plt.tight_layout() plt.savefig(logs/training_curves.png, dpi300)5.3 单张图片推理与可视化展示我建议写一个predict.py脚本能从命令行传入一张图片输出该图片属于哪个类别以及置信度。更直观的做法是把预测结果画在图片上比如在图片顶部加一行文字“健康置信度95.3%”或“枯萎置信度88.1%”。这个功能看似简单实际在最终项目演示时非常加分因为它直接向非专业的人展示了模型的效果。演示的时候可以现场拍一张花的照片扔进去看结果比对着终端输出有意思多了。6. 常见问题与排查技巧实录我在实际指导过程中这个项目踩过的坑基本集中在数据、训练、环境三个层面。6.1 训练准确率一直不涨先说最常见的模型训练了十几个epoch准确率还在50%上下徘徊二分类的随机水平。不用慌按顺序排查几个点。先检查数据标签有没有错乱比如ImageFolder读取时类别顺序和你预期不一致可以用dataset.classes打印确认。然后检查学习率是不是太大导致loss震荡把学习率降到0.0001试一下。还要看数据增强有没有过于激进有时旋转角度太大、颜色抖动太强模型根本学不到有效特征甚至图都变得人不人鬼不鬼先关掉增强跑一个baseline作为对照。最后一招是把模型初始化改成固定随机种子确保每次跑的结果可复现。6.2 过拟合如果训练集准确率很高但验证集低就是说模型把训练集背下来了。解决办法优先级是增加数据量多用数据增强→ 增强正则化加大Dropout比例→ 减小模型容量减少卷积层的通道数或去掉一层卷积→ 用迁移学习代替从头训练。一般来说对于1000张左右的小数据集把Dropout从0.3提到0.5配合早停过拟合能压下来不少。6.3 数据不平衡问题如果数据比例失衡比如健康图片800张、枯萎图片200张模型可能会走捷径全猜健康也能到80%准确率。有个简单的诊断方法打印测试集上的混淆矩阵看模型是否把大部分样本都预测成了多数类。解决办法可以直接用WeightedRandomSampler给少数类样本更高的采样概率这样每个epoch里两类数据基本平衡from torch.utils.data import WeightedRandomSampler labels [dataset.targets[i] for i in range(len(dataset))] class_counts torch.bincount(torch.tensor(labels)) weights 1.0 / class_counts[labels] sampler WeightedRandomSampler(weights, num_sampleslen(dataset), replacementTrue)还有一种损失函数层面的做法把CrossEntropyLoss的weight参数设成和类别数量成反比也能改善不平衡的影响。6.4 显存不足或训练太慢显存不足主要靠调小batch size和输入尺寸。如果输入尺寸从224降到160显存占用能小一半左右速度也上来了。不过尺寸变小后特征图分辨率也变低可能需要稍微增加卷积层通道数来弥补。训练太慢的另一个常见原因是没走GPU代码里.contiguous()或.cuda()的写法如果有问题模型很可能还在CPU上跑。检查一下nvidia-smi看显存占用情况或者打印model.device确认。6.5 验证集准确率忽高忽低这是我在多个项目里都见过的现象尤其是数据集比较小的时。前面几轮的验证准确率大幅跳来跳去不要急着下结论。解决办法是把batch size调大一点让每个batch的数据分布更接近整体分布同时把验证放在每个epoch结束时做用全部验证集数据评估不要只取一部分。如果波动还是太大可以用滑动平均的方式平滑曲线论文里画出来的图也更好看。6.6 环境依赖与跨平台运行这部分虽然不算是算法问题但非常磨人。PyTorch的CPU版和GPU版安装命令不一样Anaconda里装的时候一定注意看是cuda版本还是cpu版本。代码里用GPU时要写成device torch.device(cuda if torch.cuda.is_available() else cpu)这样在没有GPU的机器上也能运行只是慢。另外整个项目打包提交给导师或者同学复现时requirements.txt是必须的最好再配一个README把数据集结构、训练命令、推理命令都写清楚。我在实际答辩中看到不少同学项目代码很好但别人拿到手跑不起来这就是工程化意识不够容易被追问。7. 进一步扩展的空间做完基础版之后可以根据自己的时间精力做扩展。方向大概有四个都不难而且都能写进论文的展望部分。多分类升级。从“枯萎/健康”二分类升级到“健康/轻度缺水/严重枯萎”三分类。多分类不只是换一个输出节点数还要考虑类别间其实是“有序”的可以用更平滑的标签编码方式或者把问题建模成回归来预测枯萎程度指数。网页端部署。用Flask写一个极简的网页上传照片后端调用训练好的模型推理前端展示结果。这一套流程虽然只是工业部署的微缩版但足以体现模型的实用价值答辩演示效果极佳。注意力机制可视化。用Grad-CAM生成热力图展示模型关注的是花朵的哪个部位。这个可视化对解释性极强也是论文的加分项。Grad-CAM的实现不复杂torchvision里有现成的工具类可以调用。细粒度分类研究。如果数据集比较大可以实验不同模型VGG、ResNet、EfficientNet在花卉枯萎识别上的表现差异形成一个小型的benchmark对比这也是论文实验部分很好的内容。我个人在实际操作中的体会是这个项目之所以值得推荐是因为它的“门槛低、上限高”新手能在一个月内跑通全流程而追求优秀的同学可以在同一个框架下不断填充更精致的内容。它不像目标检测那样需要理解锚框、NMS等一堆复杂概念也不像文本生成那样面临评价指标难定义的问题它就是一个干净、明确、工程链路完整的视觉分类项目非常适合作为深度学习入门的毕业设计选题。如果你想选这个方向不要急着写代码先花一两天把数据规范化做好再动手搭网络。训练调试过程中记得把每个实验结果都记录下来哪怕是失败的实验也有价值——答辩时候展示你如何分析问题、如何尝试解决、如何在对比中找到更优方案这才是整个毕设最值钱的部分。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑