FEATURED · 精选文章

基于PyTorch和CNN的花卉图像识别:从数据预处理到模型训练实战

发布时间 / 2026/9/9 0:05:45
来源 / 创域科博编辑部
栏目 / 资讯中心
基于PyTorch和CNN的花卉图像识别:从数据预处理到模型训练实战 简介一份面向高校计算机视觉课程设计场景的完整项目基于 PyTorch 和 ResNet18 卷积神经网络的花卉图像识别含源码、模型与设计报告。适合人工智能、电子信息、自动化等专业学生用作课程设计、大作业或毕业设计参考也适合有一定 Python 基础、希望上手 CNN 图像分类的初学者进阶学习通过完整项目快速建立卷积神经网络的落地认知。压缩包共 11 个文件合计约 40.63MB其中 .py 源码覆盖完整的数据处理、模型定义与训练推理流程.pth 为训练好的权重.mat 存放数据集划分与标签信息.docx、.pdf、.md 则提供设计报告、项目说明与参考论文便于对照阅读和二次开发。项目代码已经过严格测试可直接运行复现目前已有 197 人学习下载。下载后既能基于完整流程理解 ResNet18 的花卉分类实现也可直接提交作业或在此基础上替换数据集、调整网络结构拓展到其他图像识别任务。计算机视觉大作业怎么做基于PyTorch和CNN的花卉图像识别的一整套拆解与实操这份标题为计算机视觉大作业Python基于pytorchCNN的花卉图像识别含源码模型设计报告及资料的项目我第一眼看到就想多说几句。因为这类图像分类项目是计算机视觉课程里最经典、最适合入门的选题之一场景好理解、数据容易获取、效果可视化强、报告也方便展开。但它又不是跑通代码就行那么简单——数据怎么组织、模型怎么选、训练怎么调、报告怎么写每一步都有讲究。这篇文章我干脆按自己当年做这类项目时的完整流程来写把这套花卉图像识别从数据准备、环境搭建、模型训练一直到最后的评估和报告撰写所有核心环节全部理一遍。下面这些内容适合正在做计算机视觉课程大作业的学生也适合想通过一个小项目快速掌握PyTorch图像分类流程的自学者甚至可以直接当成一份能动手跟做的复现指南。1. 项目整体设计与技术选型思路1.1 需求拆解这作业考核的到底是什么说实话一个花卉图像识别大作业真正想考察的从来不是把模型跑出来这一件事。把它拆开看背后的考核点一般有四个层次第一层是数据能力你能不能把网上或者数据集里的图片规规矩矩地整理成模型能吃的格式训练集、验证集、测试集划分是否合理第二层是模型你要讲清楚为什么用CNN用的是自己搭的卷积网络还是预训练模型做迁移学习卷积、池化、全连接是怎么协作的第三层是训练与调优损失函数怎么选、学习率设多少、过拟合怎么办这些都是老师最爱在答辩时问的细节第四层是工程表达代码结构是否清晰、能否保存和加载模型、有没有可视化结果、报告是否完整这决定你的分数上限。很多同学上来就照着别人的代码跑跑通了就以为完成了结果答辩时被问卷积核为什么是3×3你用的模型是怎么做迁移的一下就卡住。所以这篇文章我不只讲代码也会把背后的原理和为什么一起说清楚。1.2 为什么选PyTorch加CNN这套组合PyTorch在学术和教学场景中的占比现在是公认的高它最大的特点是动态计算图逻辑直观出了错也容易定位。比如你想打印中间某一层的特征图形状直接写个print就能看这在某些静态图框架里调试成本要高得多。对于刚接触深度学习的同学来说可以把PyTorch理解成搭乐高网络是积木块数据加载是传送带训练循环是你手动控制每一步怎么走所有环节都是透明可见、可随时中断检查的。而CNN卷积神经网络是图像分类的基本盘。你可以这样理解一张图片输入到模型里其实是一个巨大的像素矩阵。全连接网络会在这一层把所有像素都拉直去运算参数爆炸且丢掉了空间结构信息。CNN则通过卷积核在图像上滑动像用一个局部放大镜去提取边缘、纹理、形状这些特征——浅层网络看到的是线条和色块深层网络逐渐组合出花瓣、叶片这类语义特征最后通过全连接层和Softmax输出每个类别的概率。这套机制天然适合花卉识别这类任务也最适合用来给大作业做主体框架。需要补充的是站在大作业的角度最好选迁移学习路线即加载在大规模数据集上预训练好的ResNet等模型再针对自己的花卉数据集做微调。原因很简单花卉数据集通常只有几千张图从零训练一个深层CNN不仅耗时而且很容易因为数据量不够而严重过拟合——训练集准确率99%验证集只有60%答辩时很难看。迁移学习等于让模型带着已有的视觉常识来学新任务数据量小照样能有不错的性能这也是目前工业界和学术界的通行做法。2. 数据准备与预处理实操2.1 环境搭建Anaconda虚拟环境与PyTorch安装这种项目环境配置非常容易出各种问题我强烈建议用Anaconda建一个独立的虚拟环境别直接装在系统Python里。以下是当时我实测下来比较稳的方案# 创建虚拟环境Python版本以3.9为例 conda create -n flower python3.9 conda activate flower # GPU版PyTorch请先通过nvidia-smi确认CUDA版本再选安装命令 # 以CUDA 11.8为例 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # CPU版PyTorch适合没有NVIDIA显卡或Mac电脑 pip install torch torchvision torchaudio这里有几个值得注意的点。如果你没有独显装CPU版就足够完成大作业只是训练时间会长一些把图像尺寸和batch size调小即可。如果是NVIDIA显卡装完GPU版后务必在Python里验证一下import torch print(torch.__version__) print(torch.cuda.is_available())返回True就说明GPU可用若返回False大概率是CUDA版本不匹配重新选对应版本安装即可。2.2 数据集组织让ImageFolder直接读取花卉识别的公开数据集有很多最常用的包括Oxford-102102类花卉每类40到258张不等、17 Flowers17类以及国内课程中常见的五类小花数据集比如雏菊、蒲公英、玫瑰、向日葵、郁金香。对于大作业我自己比较推荐先选用五类或十几类的子集。原因非常直白类别太多、样本不均衡你得花大量时间调模型、写分析而课程作业的时间精力本来就有限先用小数据集把整个流程跑通、把报告写好是性价比更高的方案。不管你从哪里拿到数据最终都要把数据整理成按类别放文件夹的结构因为PyTorch的torchvision.datasets.ImageFolder可以直接读取这种格式data/ ├── train/ │ ├── daisy/ # 里面放雏菊的图片 │ ├── dandelion/ │ ├── rose/ │ └── ... ├── val/ │ ├── daisy/ │ ├── dandelion/ │ └── ... └── test/ ├── daisy/ └── ...训练集、验证集、测试集按 7:2:1 或 8:1:1 划分即可。注意验证集和测试集一定不能与训练集有重复图片否则评估结果会虚高答辩时一旦老师抽查很难解释清楚。2.3 数据增强与标准化提升模型泛化能力的关键数据量有限的情况下数据增强是性价比最高的防过拟合手段。所谓的增强就是在不改变图片语义的前提下进行随机翻转、旋转、裁剪、调亮度等操作让模型每次看到的样本都不完全一样强迫它学习到更鲁棒的特征。from torchvision import transforms # 训练集加入随机增强增强泛化能力 train_transforms transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(p0.5), # 随机水平翻转 transforms.RandomRotation(15), # 随机旋转15度以内 transforms.ColorJitter(brightness0.2, contrast0.2), # 轻微调整亮度对比度 transforms.CenterCrop(224), # 中心裁剪到模型输入尺寸 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 验证集/测试集只做缩放和标准化不做随机增强 val_transforms transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])关于标准化这一行很多初学者不理解为什么是这几个固定数字。这是因为我们用的是在ImageNet上预训练好的模型ImageNet数据集的RGB三通道均值和标准差恰好就是mean[0.485, 0.456, 0.406]std[0.229, 0.224, 0.225]。加载预训练模型时沿用这套标准化参数才能保证输入数据分布与模型原本熟悉的分布一致迁移效果才会好。如果你自己从零训练模型当然可以重新计算自己数据的均值方差但既然用了迁移学习直接用这套系数是标准做法。数据加载部分直接用DataLoader封装训练和验证集分别设置batch size我一般习惯训练集32或64验证集与测试集用同样大小即可from torchvision import datasets from torch.utils.data import DataLoader train_dataset datasets.ImageFolder(rootdata/train, transformtrain_transforms) val_dataset datasets.ImageFolder(rootdata/val, transformval_transforms) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4) class_names train_dataset.classes print(类别映射, class_names)3. 模型构建与训练调优全流程3.1 加载ResNet预训练模型并修改分类头这里选用torchvision里的ResNet18或ResNet50均可。ResNet18更轻量CPU机器也能较快跑完ResNet50精度通常更高但训练速度更慢。大作业场景下如果显卡显存低于4GB或者用的是CPU建议先用ResNet18跑通流程如果性能不够再换ResNet50正好还能写进报告里做不同模型的对比实验。ResNet的核心思想是残差连接。它的做法可以通俗理解为在每一层学习的时候模型把输入的原样信息和这层新学到的变化信息相加后再传给下一层。这样的好处是即使网络很深梯度也能沿着这条捷径无损地回传不会因为层数加深而出现退化或者梯度消失。这也是为什么ResNet能训练出比普通CNN深得多的网络。import torch.nn as nn from torchvision import models def get_model(num_classes, model_nameresnet18, pretrainedTrue): if model_name resnet18: weights models.ResNet18_Weights.IMAGENET1K_V1 if pretrained else None model models.resnet18(weightsweights) elif model_name resnet50: weights models.ResNet50_Weights.IMAGENET1K_V1 if pretrained else None model models.resnet50(weightsweights) else: raise ValueError(f不支持的模型: {model_name}) # 获取全连接层的输入维度 in_features model.fc.in_features # 替换最后一层全连接输出类别数 model.fc nn.Linear(in_features, num_classes) return model修改分类头之所以关键是因为预训练模型原本是在ImageNet的1000类上训练的最后一层输出1000个概率。我们的任务是识别5类花所以把最后一层替换成输出5的线性层即可。在迁移学习实际训练时有两个方案一是冻结前面的卷积层参数只训练新加的分类头速度极快适合数据非常少的情况二是解冻所有层做整体微调效果更充分但需要更长训练时间和更低学习率。我的建议是先用方案一快速验证数据流程没问题再解冻后做整体微调这样整个过程更可控。3.2 训练循环、损失函数与超参数的选择损失函数在多分类任务里直接用CrossEntropyLoss它内部已经包含了Softmax操作所以请你不要把Softmax再写到模型最后一层否则会计算两次导致训练初期就无法收敛。优化器我推荐用Adam它对学习率的敏感度比SGD低很多更适合跨多个场景快速实验。初始学习率设置1e-4到1e-3都能跑但如果解冻全部层微调初学率建议降到1e-4或更低避免微调阶段一下子把预训练权重冲坏。此外还可以搭配一个余弦退火或ReduceLROnPlateau学习率调度器在验证集损失不再下降时自动降低学习率实测下来能明显提升最终精度。import torch.optim as optim from torch.optim import lr_scheduler model get_model(num_classeslen(class_names), model_nameresnet18) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() # 优化器冻结骨干时用Adam解冻微调用SGD或Adam均可注意学习率 optimizer optim.Adam(model.parameters(), lr1e-4) scheduler lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience2) epochs 30 best_val_acc 0.0训练流程的主体是标准的前向传播-计算损失-反向传播-更新权重。这里我建议每个epoch都同时记录训练集和验证集的loss与准确率因为只看训练集loss很容易被过拟合假象骗到for epoch in range(epochs): # 训练阶段 model.train() train_loss, train_correct, train_total 0.0, 0, 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() train_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) train_correct (preds labels).sum().item() train_total labels.size(0) # 验证阶段 model.eval() val_loss, val_correct, val_total 0.0, 0, 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) val_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) val_correct (preds labels).sum().item() val_total labels.size(0) train_acc train_correct / train_total val_acc val_correct / val_total train_loss_avg train_loss / train_total val_loss_avg val_loss / val_total scheduler.step(val_loss_avg) print(fEpoch {epoch1}/{epochs} | fTrain Loss: {train_loss_avg:.4f} | Train Acc: {train_acc:.4f} | fVal Loss: {val_loss_avg:.4f} | Val Acc: {val_acc:.4f}) # 保存验证集上表现最好的模型 if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_model.pth) print(f 保存新最优模型验证准确率: {best_val_acc:.4f})我每次跑类似项目都会在model.eval()状态下只做推理不更新梯度并且用torch.no_grad()包住这一步容易被漏掉。如果不加model.eval()BatchNorm和Dropout这些层在验证时的行为就不对评估结果会严重失真不加torch.no_grad()则会白算一遍梯度和中间变量白白吃掉大量显存。3.3 结果可视化损失曲线、混淆矩阵与样例预测训练结束后光看终端打印的数字不够直观画图是让报告眼前一亮的关键。我通常画四张图第一张是训练集和验证集的loss曲线与准确率曲线直接展示模型是否收敛、有没有过拟合这是报告里训练过程与分析这一节的核心配图。第二张是混淆矩阵它能精确告诉你模型把哪些花搞混了——比如玫瑰和月季如果被经常混淆说明它们在视觉上确实接近分析原因会很有价值。第三张是随机抽样一批测试图片把预测类别、置信度标注在图片上视觉冲击力最强也是答辩时最容易讲清楚的部分。第四张可以展示某一层的特征图看看卷积层到底提取了什么特征这部分如果时间紧张可以不做但做了会很加分。import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns # 这里假设已经跑完测试集推理得到 y_true 和 y_pred cm confusion_matrix(y_true, y_pred) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(预测类别) plt.ylabel(真实类别) plt.title(测试集混淆矩阵) plt.tight_layout() plt.savefig(confusion_matrix.png) print(classification_report(y_true, y_pred, target_namesclass_names))我建议把训练好的模型和这些可视化图片都放进项目目录的results/文件夹里报告里按需引用。这样整套项目交付时老师看到的不仅是代码或者模型文件而是一份有过程、有分析、有结论的完整成果。4. 常见问题与排查技巧实录4.1 训练Loss不下降或准确率一直低这是我遇到最多的一个坑。出现这种问题时优先检查三处第一数据预处理是否一致。你训练时做了Normalize(mean[0.485, ...])如果测试时漏了这一步或者换成了别的参数模型在推理时看到的输入分布完全不对准确率会掉得离谱。第二标签是否错位。ImageFolder是按文件夹名称的字母顺序分配类别索引的比如dandelion可能是0rose可能是1。如果手工改标签时想当然按自己的顺序来就全乱了此时打印一下class_names是最直接的验证方法。第三学习率是否太大或太小。我之前用过1e-2的Adam跑一个5分类任务结果loss在2.5附近完全不动降到1e-4之后几轮就明显下降。可以试着用默认的1e-3和1e-4各快速跑两三个epoch对比loss下降速度。4.2 显存不足CUDA Out of Memory跑ResNet50时8GB显存的显卡配上224×224输入和64的batch size很容易爆显存。解决思路按优先级排列先调小batch size到16或8再确认输入图片没有意外地变成3通道大尺寸最后可以考虑把模型换为ResNet18。如果样本数量足够调小batch size带来的性能影响通常很小不要有心理负担。另外显卡如果同时在跑其他程序训练前用nvidia-smi看一眼占用情况也经常能救命。4.3 训练集准确率极高验证集准确率上不去这是典型的过拟合信号。对策依次是加强数据增强比如随机旋转角度加大、加入随机擦除在模型分类头前加一层Dropout解冻预训练模型但把学习率调低用整体微调的方式增强泛化能力。我在做花卉识别时明显感受到数据增强的作用——只做Resize不做增强时验证集准确率大约在82%左右加上翻转、旋转、颜色抖动后同样的模型直接提升到90%以上。4.4 关于设计报告与代码交付大作业通常要求提交源码、模型和设计报告这份报告我建议按问题定义-数据说明-方法设计-实验过程-结果分析-总结与改进的结构来写每个部分配上图和具体数据。源码目录里一定要有README.md写明环境版本、运行步骤最好直接给出一套从下载数据到训练测试的命令序列。我见过太多代码文件本身没问题但因为少了运行说明被扣分的情况这点工夫真不能省。5. 从大作业到个人项目的扩展建议这类花卉图像识别做到这个程度其实已经完全达标了。但如果时间和精力允许我强烈建议你再往前走一小步把模型封装成一个简单的推理接口比如用Flask写一个网页上传图片就能返回识别结果或者导出为ONNX格式让模型脱离PyTorch环境也能运行。这种从模型到应用的完整闭环写在简历上或者作为面试项目讲出来分量完全不一样。我在实际做这个扩展时踩过两个值得说的坑。一个是模型导出ONNX时因为输入尺寸固定了客户端如果直接上传任意大小的图片必须先做预处理再送进去另一个是部署到网页端后CPU推理一张图大概要几百毫秒用户体验其实可以接受但图像前处理这一块一定要和训练时保持一致否则精度会掉。可以说一个花卉识别项目从数据处理到模型训练再到部署推理整个计算机视觉的核心流程都被串起来了这才是这个项目最有价值的地方。最后再分享一个优化技巧训练完成后记得回头看看模型在测试集上预测错误的那些图。你会惊讶地发现有些错误其实不一定是模型的锅——比如图片里有两只花、背景里有大片同色物体、或者标注本身就错了。把这些典型错误截图放进报告里分析答辩时反而会成为加分项因为它证明你是真正在理解模型的决策过程而不仅仅是把代码跑完就结束了。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻