ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于迁移学习的昆虫识别系统实战:ResNet50+PyTorch+Flask部署

基于迁移学习的昆虫识别系统实战:ResNet50+PyTorch+Flask部署 简介基于Python开发的昆虫识别系统面向毕业设计、课程设计及实际项目开发提供高精度识别能力与完整工程源码。系统采用模型迭代方式持续优化最新版已支持2037个昆虫分类单元Top1/Top5准确率分别达0.922/0.981数据表现扎实。源码经过严格测试配套项目文档清晰可在此基础上按需扩展或替换昆虫类别。包体共18个文件以Python源码、ONNX推理模型、示例图片、Markdown文档和文本配置为主体积仅15.75MB轻量易用。源码文件承担业务逻辑与推理流程ONNX模型直接用于部署示例图片便于快速验证效果文档则提供项目说明与使用指引。目前已有66人学习适合需要快速搭建识别系统或完成相关课设/毕设的开发者。结合源码、模型与文档可完整了解从模型调用到结果输出的流程节省从零搭建的时间。1. 昆虫识别系统的真实形态一个能直接跑通的完整资源昆虫识别在毕业设计和课程设计里是出镜率很高的题目。它表面是图像分类实际坑不少昆虫种类多、同类外观差异小、数据集往往只有几千张模型稍不留神就过拟合。这套基于 Python 开发的昆虫识别系统核心思路是用 ResNet50 做迁移学习在有限样本上拿到足够高的准确率再封装成网页应用。它适合三类人正在做毕设的学生、需要课程设计源码的开发者、以及想快速搭一个图像识别 demo 的从业者。资源附带完整源码和项目文档训练、预测、部署一条线是闭环的。2. 技术选型为什么咬定迁移学习不放2.1 三种方案对比传统特征、从头训练、迁移学习昆虫识别本质上是一个图像分类任务。做这个题目前方案评估花了我不少时间。摆在我面前有三条路传统特征加机器学习、从头训练卷积神经网络、以及迁移学习。传统特征方案指的是手动设计特征描述子最典型的是 HOG方向梯度直方图配合 SVM支持向量机。这套方案跑在 CPU 上就能完成部署简单原理也容易在论文里讲清楚。但它的上限很明显——对于昆虫这种身体结构相似、纹理差异细微的目标HOG 特征区分度不够。瓢虫和甲虫、菜粉蝶和蛾子靠梯度直方图很难拉开差距准确率普遍会在 70% 以下徘徊。从头训练 CNN 的思路相反让网络自己学特征理论上上限高。但现实很骨感一套标准的昆虫数据集往往只有 3000 到 5000 张图而 ResNet50 有超过 2500 万参数训练这种规模网络通常需要几万张图打底。样本不够模型就会在训练集上死记硬背验证集准确率上不去这是典型的过拟合现象。迁移学习的思路介于两者之间先在一个大型数据集ImageNet上把通用特征学出来再把学到的底层纹理、边缘、色彩特征搬运到昆虫识别任务上只微调最后一层分类输出。这个方案的直接收益是训练时间大幅缩短——普通显卡上从头训练可能需要十几个小时才能收敛迁移学习通常 30 到 50 个 epoch 就能稳定准确率能到 90% 以上。这三条路的差别用一个对比表格来说明会更直观方案训练数据门槛特征质量训练耗时准确率区间适用场景HOG SVM较低几百张可跑手动特征区分度弱分钟级60% - 75%简单目标、快速 demo从头训练 CNN高需数万张自动学习但易过拟合十小时以上55% - 80%大规模数据集迁移学习低两千张起ImageNet 特征迁移1 - 2 小时88% - 95%小样本、毕设项目当时看完这个对比我基本是毫不犹豫选了迁移学习。对毕业设计来说迁移学习还有一个隐性优势论文里可以展开讲的内容多特征迁移、冻结策略、微调技巧都可以作为研究点比单纯跑一个黑匣子模型丰富得多。2.2 项目目录结构与各模块职责这套资源拿到手第一步是理解代码布局。一个好的目录结构能让你少踩很多坑这个项目的组织方式是典型的工程化分层insect_recognition/ ├── data/ │ ├── train/ │ │ ├── bee/ │ │ ├── beetle/ │ │ ├── butterfly/ │ │ └── ... │ └── val/ │ ├── bee/ │ └── ... ├── models/ │ ├── __init__.py │ └── resnet.py ├── utils/ │ ├── dataset.py │ ├── transforms.py │ └── metrics.py ├── train.py ├── predict.py ├── app.py ├── requirements.txt └── docs/ ├── 开题报告.md ├── 任务书.md └── 论文框架.md各模块职责很清晰。data目录存放按类别分好的图片train和val下面各子文件夹的名字就是类别名。models/resnet.py封装了模型构建逻辑主要工作是加载预训练权重、替换全连接层。utils里放的是数据处理工具dataset.py负责读取图片路径和标签transforms.py做数据增强metrics.py计算准确率和混淆矩阵。train.py是训练入口predict.py是单张图片预测脚本app.py是后续要讲的 Web 服务。docs目录是这个资源比较加分的地方。毕业设计不只是交代码开题报告、任务书、论文正文框架这些文字材料是占大头的。这套资源把文档骨架搭好了拿到手后按自己的数据集和实验数据往里填能省下不少时间。从依赖角度看requirements.txt里会包含 torch、torchvision、flask、scikit-learn、matplotlib 这几个核心库。PyTorch 生态在模型微调这个场景比 TensorFlow 顺手而且 torchvision 里直接集成了预训练模型权重写代码时不用自己去网上下载。2.3 核心模型参数选型ResNet50 而不是更轻的模型模型主干为什么选 ResNet50而不是 ResNet18 或者轻量级的 MobileNetV3这是有讲究的。ResNet50 的“50”指的是 50 层卷积结构它的核心是残差块——让梯度在深层网络中有一条直达的捷径缓解深度增加带来的退化问题。在昆虫识别这个任务上ResNet50 的容量刚好它比 ResNet18 深学到的特征更细能区分蝴蝶翅膀上的斑点花纹又不像 ResNet101 那样重训练时间还在可接受范围内。MobileNetV3 我用过一轮它在手机端的推理速度确实快但精度比 ResNet50 低 2 到 3 个百分点。在毕业设计答辩现场评委问的问题通常不是“你的模型每秒能跑多少张图”而是“你的准确率是多少、有没有对比实验”。一个 92% 的准确率和一个 89% 的准确率答辩效果差距是明显的。模型的参数配置上有两个关键数字输入图片分辨率固定为 224×224这是 ImageNet 预训练模型的标准输入尺寸不需要额外调整全连接层输出维度从 1000 改成实际类别数也就是num_classes。以 9 类昆虫为例最后的fc层就是nn.Linear(2048, 9)其中 2048 是 ResNet50 最后一个卷积层输出的特征通道数。3. 数据准备与训练实战从图片文件夹到 90% 准确率3.1 数据集按 ImageNet 格式组织标注信息藏在目录名里很多第一次做图像分类的人会在标注上栽跟头。这个项目的做法很务实直接用目录名当作类别标签。ImageNet 格式的要求是训练集和验证集各自按类别建文件夹同一类的图片放在同一个文件夹里文件夹的名字就是类别英文名。数据划分这个环节有个常见误操作直接把网上随机下载的图片丢进同一个文件夹然后让脚本按比例划分。这种做法的问题是图片没有先打乱如果原始下载顺序是同一个网站连续抓取的划分后训练集和验证集的数据分布会偏。我习惯先把所有图片打乱再按比例划分具体操作可以用一个脚本完成import os import random import shutil random.seed(42) # 将原始数据按 8:2 划分为 train / val def split_data(src_root, dst_root, train_ratio0.8): classes os.listdir(src_root) for cls in classes: cls_path os.path.join(src_root, cls) if not os.path.isdir(cls_path): continue imgs os.listdir(cls_path) random.shuffle(imgs) # 先打乱防止原始顺序带来的分布偏移 split_idx int(len(imgs) * train_ratio) train_imgs imgs[:split_idx] val_imgs imgs[split_idx:] for split_name, split_imgs in [(train, train_imgs), (val, val_imgs)]: dst_cls_path os.path.join(dst_root, split_name, cls) os.makedirs(dst_cls_path, exist_okTrue) for img in split_imgs: shutil.copy(os.path.join(cls_path, img), os.path.join(dst_cls_path, img)) print(f[{split_name}] {cls}/{img})这里random.seed(42)是为了保证每次运行划分结果一致方便复现实验。train_ratio0.8是经验值对几千张样本的数据集80% 训练、20% 验证是够用的。如果数据集特别小少于 1000 张建议把比例调整到 0.9或者直接用 K 折交叉验证只跑一次 8:2 划分很容易因为验证集太少而评估失真。划分完成后要检查一件事每个类别在训练集和验证集的样本数是否大致均衡。如果有的类别只有 30 张训练图有的有 300 张就要考虑第 4 章要提到的类不均衡处理否则模型会对样本多的类别产生偏向。3.2 数据增强参数把 3000 张图撑出 30000 张图的效果昆虫识别的难点之一是类间差异小、类内差异大。同样是瓢虫不同角度、不同光照、不同生长阶段拍出来差别很大而瓢虫和某种甲虫正面照可能长得几乎一样。数据增强的作用就是模拟这种真实世界的多样性防止模型死记硬背。PyTorch 里的torchvision.transforms提供了完整的数据增强工具箱。这套项目常用的组合是from torchvision import transforms train_transforms transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.8, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees30), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transforms 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]) ])上半部分是训练增强下半部分是验证增强。注意验证集绝对不做随机增强——验证集要模拟真实测试场景只能用缩放和中心裁剪。transforms.Normalize里的均值[0.485, 0.456, 0.406]和标准差[0.229, 0.224, 0.225]是 ImageNet 数据集的标准统计值迁移学习时直接沿用这套数值因为预训练模型就是在 ImageNet 上学的特征。如果换成自己的均值标准差输入数据分布和预训练时的分布就不一致了。这里有个参数值得单独说一下scale(0.8, 1.0)表示随机裁剪时保留原图 80% 到 100% 的区域。这个范围比常用的(0.08, 1.0)窄很多是有意为之。scale范围太宽会让模型看到太多被严重裁剪的残缺物体对细粒度识别不利。昆虫分类本来看的就是翅脉、触角、斑点这些局部细节把主体保留得完整一点更合理。想进一步提升泛化能力可以再加transforms.RandomGrayscale(p0.1)以 10% 的概率把图转成灰度图模拟光线不佳的拍摄环境。3.3 迁移学习训练主流程冻结、解冻、微调训练脚本的核心逻辑是这套资源里含金量最高的部分。它的整体策略分三步走先加载在 ImageNet 上训练好的 ResNet50 权重把最后一层全连接替换成适应昆虫类别数的结构然后冻结前面所有层的参数只训练全连接层跑几个 epoch 做预热最后解冻部分深层用更小的学习率微调整个网络。核心代码可以浓缩为下面这个片段import torch import torch.nn as nn from torchvision import models # 1. 加载预训练权重 model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1) # 2. 替换最后一层全连接适配昆虫类别数 num_classes 9 model.fc nn.Linear(model.fc.in_features, num_classes) # 3. 冻结除 fc 外的所有层 for name, param in model.named_parameters(): if fc not in name: param.requires_grad False # 4. 定义优化器只传入需要更新的参数 optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-3) criterion nn.CrossEntropyLoss()weightsmodels.ResNet50_Weights.IMAGENET1K_V1是 torchvision 新版推荐的加载方式它会自动下载对应版本的权重文件。老写法models.resnet50(pretrainedTrue)在新版本中会报警告建议不要用。冻结策略体现在第三步requires_grad False表示该参数不参与反向传播。这里filter(lambda p: p.requires_grad, model.parameters())的写法很关键——它把冻结层参数从优化器里筛掉了避免 PyTorch 在反向传播时给冻结参数计算梯度而产生不必要的内存开销。当训练的 loss 下降趋于平缓时可以进行解冻微调。解冻的策略一般是从浅到深逐层放开让模型适应昆虫数据的特有分布。考虑到大部分昆虫特征集中在轮廓和纹理层面我倾向于只解冻最后两个 stagelayer4把学习率降到1e-4这样既能显著提升精度又不至于因为全部解冻导致过拟合。下面这行代码实现了这个操作# 5. 解冻 layer4 和 fc 层学习率降到 1e-4 做微调 for name, param in model.named_parameters(): if layer4 in name or fc in name: param.requires_grad True optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-4)训练循环本身是 PyTorch 标准套路但有两个细节值得提。第一model.train()和model.eval()的切换一定要做因为 BatchNorm 层在训练和推理时的行为不同忘切的话验证指标的数值会非常诡异。第二验证阶段要用torch.no_grad()包住明确告诉 PyTorch 这里不需要构建计算图省显存的同时也能让推理速度翻倍def train_one_epoch(model, loader, optimizer, criterion, device): model.train() # 训练模式 total_loss 0.0 for inputs, labels in loader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() # 梯度清零防止累加 outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(loader) def validate(model, loader, criterion, device): model.eval() # 验证模式 correct 0 total 0 val_loss 0.0 with torch.no_grad(): # 不追踪梯度 for inputs, labels in loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) loss criterion(outputs, labels) val_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() return 100.0 * correct / total, val_loss / len(loader)torch.max(outputs.data, 1)返回每一行的最大值和对应的索引(predicted labels)会得到一个布尔张量sum().item()把 True 的数量加总成正确分类个数。训练完一轮后把结果打印出来验证集准确率跑到 90% 以上这个训练任务就算基本完成了。4. 避坑与排查训练和部署阶段最容易翻车的五个环节这一章是实打实的血泪经验。训练昆虫识别系统时遇到的坑不少是毕设用户在复现过程中反复踩过的。每条我按“现象 → 原因 → 解决”的顺序来讲方便你对照排查。4.1 训练准确率很高但验证准确率很低典型的数据泄露现象我的一个用户跑来问为什么训练集准确率到了 99%验证集只有 62%。他怀疑是模型不行甚至准备换网络结构。原因我让他去翻了数据集才发现网上爬虫抓图时会把同一张原图的不同截图和压缩版本都收进来。这些图片散落到训练集和验证集里模型其实在训练时就“见过”验证集的图了但见过的是压缩或裁剪版本反而扰乱了学习。另外直接按文件名前多少位划分数据也可能踩这个坑——很多昆虫图库按拍摄批次命名同一个批次的图片会跑到不同集合里造成隐蔽的数据重叠。解决划分数据集前先做一次去重计算每个文件的哈希值把重复图片清掉。划分时不要直接按顺序切必须乱序后划分并且引入一个中间目录“raw_cleaned”存放清洗后的文件。具体可以用hashlib.md5逐文件计算指纹重复的只保留一份。做完这步再看训练和验证准确率差距一般会大幅缩短。4.2 Loss 振荡明显准确率忽高忽低现象训练过程中 loss 曲线像锯齿一样上上下下验证准确率波动超过 5 个百分点。看起来已经训练了很多轮但指标没有向好的趋势。原因学习率设置偏大是首要因素。特别是在迁移学习的微调阶段模型已经在一个较好的局部最优附近学习率过大会让参数在最优值周围来回震荡无法收敛。第 3 章里我特意提到初始学习率用1e-3、微调降到1e-4这个梯度差很多人不重视。解决引入学习率调度器PyTorch 里有现成的ReduceLROnPlateau——当验证 loss 连续几个 epoch 不下降时自动把学习率乘 0.1。另外加一个早停机制连续 8 个 epoch 验证准确率没有提升就保存当前最佳模型并停止训练。这两种机制的组合可以最大化避免训练白费。4.3 某些昆虫种类之间互相混淆模型把蛾子认成蝴蝶现象别的类别准确率都在 95% 左右偏偏蝴蝶和蛾子这两类相互乱认验证集里蛾子误判成蝴蝶的比例超过 30%。原因蝴蝶和蛾子外形高度相似尤其是一些夜行性蛾类翅膀花纹和蝴蝶几乎没有差异。细粒度识别的关键在局部特征——蝴蝶的触角末端膨大蛾子的触角呈羽状蝴蝶停歇时翅膀竖立蛾子通常平展。ResNet50 的原始输入是 224×224对于触角这种局部细节分辨率不够。解决直接把输入分辨率提升到 256 或 299配合稍微调整全连接层的输入。分辨率提升意味着训练时间变长但准确率的提升值得这个代价。另一个有效手段是强化局部特征把RandomResizedCrop的scale下限从 0.8 降到 0.5让模型更多看到昆虫的局部特写强迫它学习细节差异而不是整体轮廓。4.4 CUDA Out of Memory 报错12GB 显存也扛不住现象训练启动没几个 step控制台直接抛RuntimeError: CUDA out of memory模型根本跑不起来。原因最常见的触发点是 batch size 设置太大。有些人习惯把 batch size 设为 64因为看别人用默认数据集这么跑过但昆虫数据集图片分辨率高、模型是 ResNet50计算图占用很大。12GB 显存跑 224×224 的图batch size 在 32 左右是临界点。解决先把 batch size 降到 16让训练跑通。不要过分依赖调小 batch size——batch size 太小会导致 BatchNorm 统计量不稳定。更推荐开启混合精度训练PyTorch 的torch.cuda.amp会自动把部分计算降到 float16显存占用能省一半训练速度还快。这个技术对精度的影响极小基本可以忽略。解决后如果还想继续压显存可以把pin_memoryTrue关掉或者让DataLoader的num_workers从 4 降到 2减少临时内存峰值。4.5 部署到 Flask 后网页上传图片总是报 500现象本地测试脚本predict.py跑得好好的一到 Flask 的 Web 界面就报内部错误。服务端日志显示KeyError或维度不匹配。原因predict.py里图片是直接读文件做预处理的而 Flask 接收的是前端传来的文件流。两者在格式上没对齐一个是 PIL Image一个是给模型直接喂张量。还有个隐蔽问题——模型输出的类别索引和中文标签映射表不一致。训练时建立的class_to_idx.json里索引从 0 开始但标签列表在推理脚本里如果不按相同顺序构建就会错位。解决写一个统一的预处理函数Flask 和命令行脚本都用同一套逻辑。用PIL.Image.open(io.BytesIO(file.read()))把上传的文件流转成图像对象再走同一套 transforms。模型输出后用索引去查class_to_idx.json的反向映射表idx_to_class保证显示的中文类别名和训练时一致。这样两边就怎么都对得上了。5. 部署成网页应用Flask 把模型撑起来5.1 写一个轻量推理接口模型训练完了最终要交付一个能用的形态。毕业设计里最常见的要求是“上传一张图片网页上返回识别结果”Flask 是这套链路里最轻的方案。整个服务不需要数据库不需要消息队列一个 Python 文件就够。from flask import Flask, request, jsonify, render_template from PIL import Image import io import torch import json from torchvision import transforms app Flask(__name__) # 加载模型和标签映射 device torch.device(cuda if torch.cuda.is_available() else cpu) model torch.load(best_model.pth, map_locationdevice) model.to(device) model.eval() with open(class_to_idx.json, r, encodingutf-8) as f: class_to_idx json.load(f) idx_to_class {v: k for k, v in class_to_idx.items()} # 与训练时保持一致的预处理 infer_transforms 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]) ]) app.route(/, methods[GET]) def index(): return render_template(index.html) app.route(/predict, methods[POST]) def predict(): file request.files.get(image) if file is None: return jsonify({error: no image uploaded}), 400 img Image.open(io.BytesIO(file.read())).convert(RGB) img infer_transforms(img).unsqueeze(0).to(device) with torch.no_grad(): outputs model(img) probs torch.softmax(outputs, dim1) confidence, predicted torch.max(probs, dim1) cls_id predicted.item() cls_name idx_to_class[cls_id] conf confidence.item() return jsonify({class: cls_name, confidence: f{conf:.2%}}) if __name__ __main__: app.run(host0.0.0.0, port5000)核心点是torch.load的map_locationdevice参数——如果训练时用 GPU 保存的模型加载到没有 GPU 的机器上不加这个参数会直接报 GPU 不存在的错误。unsqueeze(0)给单张图片补一个 batch 维度因为 PyTorch 模型输入要求四维张量[batch, channel, height, width]。torch.softmax把输出变成概率分布torch.max取出最高概率和对应类别索引。5.2 前端页面与动态展示Flask 的render_template默认从templates目录找 HTML 文件。前端的核心结构是一个文件上传控件一个图片预览区域一个结果展示栏。识别结果用 JavaScript 的fetch异步提交不刷新页面拿到 JSON 后渲染到页面上。前端部分推荐把识别结果分成两块展示最大概率的类别名以及所有类别的概率分布条形图。条形图能直接反映模型的判断过程——比如某张图同时有 60% 概率是蝴蝶、30% 概率是蛾子这本身就是有价值的信息答辩时展示这种中间结果比只贴一个“蝴蝶”更有说服力。前端画条形图可以引入 Chart.js代码量很少视觉上也比一堆纯文字专业得多。5.3 置信度阈值给模型一个“我不知道”的权利一个细节是置信度显示。昆虫识别里经常会遇到上传的图片不是昆虫比如拍了一张树叶、一块石头。模型不是万能的哪怕输入完全无关的图片它也会硬输出一个类别这时候就要靠置信度阈值兜底。常见做法是设置一个阈值比如 0.7。当最高置信度低于这个值时不再返回具体类别而是返回“无法识别请上传更清晰的昆虫图片”。这个逻辑虽然简单但对用户体验的提升很大。与其给用户一个 45% 置信度的错误答案不如让系统承认识别不了。这个阈值还可以做成可配置的比如放在配置文件里后续按不同场景随意调整。在实际调阈值时我会拿验证集里所有样本的概率分布画一个直方图找到误判率可接受的分界点。有些用户为了图省事直接把阈值设成 0.5结果各类别间的错误答案全涌进来了。对昆虫识别来说阈值在 0.65 - 0.75 之间是相对稳的区间。部署服务还有最后一个细节需要关注生产环境里需要关掉 Flask 自带的调试模式把debugTrue去掉否则服务出错时会向客户端暴露出完整的堆栈信息这在安全上不可取。6. 验证方法与进阶扩展从“能跑”到“能讲”模型跑通、网页能出结果之后其实还没到交付标准。毕业设计答辩评审最喜欢问的问题是“你的模型到底好在哪里”。只丢一个准确率数字是不够的还需要一整套可视化证据。最直接的工具是混淆矩阵。scikit-learn里提供了confusion_matrix方法传入验证集真实标签和模型预测标签就能输出一个 N×N 的矩阵。矩阵第 i 行第 j 列表示“真实类别为 i、模型预测为 j”的样本数。对角线越深说明分类越准非对角线的亮点就是模型最容易混淆的类别对。把混淆矩阵用matplotlib画出来标注清楚行和列的类别名这张图可以直接放进论文的实验章节。分类报告也很实用。classification_report一行代码输出每个类别的 precision精确率、recall召回率和 f1-scoreF1 值。这三个指标比单纯看准确率更细——准确率会被样本多的类别带偏而 F1 值能反映出每个类别单独的表现。在答辩时挑一个表现最好的类别和一个表现最差的类别分别解释会显得你是真的理解模型行为而不是只会跑脚本。进阶扩展有两个方向值得投入时间。一个是把模型从 ResNet50 换成 EfficientNet-B4EfficientNet 的 input resolution 可以调到 300 以上在细分任务上通常能再涨 1 - 2 个百分点。另一个是插入 Grad-CAM 可视化它能生成一张热力图指示模型在识别某只昆虫时到底在看图片的哪个区域。蝴蝶的辨识依据如果热力图集中在翅膀上这既有学术价值也方便做答辩展示。这里有一个我自己踩过的坑每次训练完生成混淆矩阵时标签顺序如果不一致比如sklearn的labels参数没指定就会按字母序排列和训练时的类别 ID 对不上。从那以后我每次训练完都会强制走一遍“加载 model → 打印类别映射 → 在验证集上跑推理 → 用同一份映射生成混淆矩阵”的流程保证任何时间拿出模型和图表都能对应上不会出现图表和代码对不上的尴尬。希望这个流程里你也能用上这套方法帮你把训练好的模型变成能讲清楚、能拿得出手的完整作品。本文还有配套的精品资源点击获取
返回列表