news 2026/9/15 5:45:48

PyTorch花卉识别实战:CNN迁移学习与完整训练流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyTorch花卉识别实战:CNN迁移学习与完整训练流程解析

简介:这是一份面向数据挖掘与机器学习课程设计的花卉识别项目实现,选用Python作为开发语言,实践了数据挖掘与机器学习在图像分类场景中的完整思路,尤其适合高校学生作为课程设计参考或入门实践素材;在完成课程设计之外,也可作为图像分类入门的小型练习。资源虽小巧,但功能模块划分清晰,涵盖训练数据的读取与数据集拆分等关键环节,并通过两个Python脚本逐行呈现,注释详细,便于读者理解每一步的处理逻辑;同时附带Git忽略文件,帮助规范项目工程结构。整个zip压缩包仅3KB,共3个文件,轻量且易上手。目前已有3997人学习下载,热度不错,也从侧面说明内容具备一定参考价值。借助这份代码,读者能够快速掌握花卉识别任务中从数据准备到模型输入的数据处理流程,并据此扩展后续的模型训练与评估,减少从零搭建的重复工作。

1. 花卉识别:数据挖掘与机器学习课程的完整落地

高校的数据挖掘与机器学习课程设计里,“花卉识别”常年是选题热门。原因很直接:它不是一个玩具 demo,而是把数据采集、预处理、特征提取、模型训练、评估调参这条完整链路串起来的典型任务。Iris 鸢尾花数据集只有一个 4 维特征向量,用逻辑回归或决策树就能到 95% 以上精度,但放到真实花卉照片场景,要处理的是高维像素、光照变化、背景干扰和类别间相似性。这门课设计的核心矛盾在于:既要展示数据挖掘的完整流程,又要在有限课时内拿到可演示的结果。

本文针对“详细实现+注释”这条线索,从数据集选型、特征工程、模型对比到训练脚本写法逐层拆开。目标是让读者不只能跟着复现,还能理解每个参数为什么这么设、每个处理步骤在解决什么问题。用 PyTorch 做 CNN 迁移学习是当前最稳的路线,我也会给出从零训练的小模型作为对照。你需要具备 Python 基础和基本的机器学习概念,代码部分会保持每行都有注释。

2. 数据集准备与预处理:从原始图片到可训练张量

2.1 数据集选型:为什么用花朵图片而不是内置鸢尾花

课程设计若只调sklearn.datasets.load_iris(),确实能在半小时内跑完,但评分时很难体现数据挖掘的工作量。更常见且稳妥的做法是采用包含多类别花卉照片的公开图像数据集,例如 Oxford 102 Flowers 或 Kaggle 上的 Flowers Recognition 数据集。这些数据集通常包含 5 到 102 个类别,每类几十到几百张图片,尺寸不一、背景复杂,更贴近真实场景。

选择标准有三个维度:类别数适中(5~10 类最合适,训练时间可控)、每类样本量不少于 50 张(否则 CNN 容易过拟合)、图片分辨率不需要太高(224×224 足够)。如果你的课程设计要求必须自己采集数据,手机拍摄加爬虫补充是常见路径,但要注意类别均衡——每个类别至少收集 60 张以上,否则模型会严重偏向样本多的类。

2.2 目录结构与标签映射

flower_dataset/ ├── train/ │ ├── daisy/ │ │ ├── daisy_001.jpg │ │ └── ... │ ├── dandelion/ │ ├── rose/ │ ├── sunflower/ │ └── tulip/ └── val/ ├── daisy/ └── ...

这种按类别分目录的组织方式是最通用的,PyTorch 的torchvision.datasets.ImageFolder可以直接读取,自动按目录名生成标签索引。建议训练集与验证集按 8:2 划分,并且保证每个类别在两个集合中都有分布——直接随机划分整个数据集会导致某些类别只在训练集或只在验证集中出现。

2.3 数据增强策略:小数据集防过拟合的第一道防线

花卉识别任务中,同一个类别(比如玫瑰)在不同照片里的姿态、光照、背景差异极大。如果不做数据增强,模型学到的会是背景特征而非花卉本身的特征。PyTorch 中标准的增强组合如下:

from torchvision import transforms # 训练集增强:随机翻转 + 随机旋转 + 颜色扰动 + 归一化 train_transforms = transforms.Compose([ transforms.Resize((256, 256)), # 先放大到256,后面随机裁剪出224 transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), transforms.RandomHorizontalFlip(p=0.5), # 50%概率水平翻转 transforms.RandomRotation(degrees=15), # 随机旋转±15度 transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), # 颜色扰动 transforms.ToTensor(), # 将PIL Image转成Tensor,像素值从0-255缩放到0-1 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # ImageNet统计均值与标准差 ]) # 验证集不做随机增强,只做尺寸统一和归一化 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]) ])

代码逻辑说明:训练集使用RandomResizedCrop而不是直接Resize,原因在于随机裁剪能模拟不同距离和角度的拍摄效果,本质上是让模型对目标尺度变化不敏感。Normalize用的均值和标准差是 ImageNet 数据集的统计值,如果使用预训练模型做迁移学习,必须沿用这个数值,不能自己重新统计——否则相当于把预训练权重喂进了分布偏移的输入空间。

这里需要特别提醒一个常见误用:有些同学会把RandomHorizontalFlip加在验证集上,这会让评估指标变得不稳定,同一张图两次预测可能给出不同结果。验证集和测试集永远只做确定性变换。

2.4 DataLoader 参数:batch_size 与 num_workers 的取舍

from torch.utils.data import DataLoader batch_size = 32 train_loader = DataLoader( train_dataset, batch_size=batch_size, shuffle=True, # 每个epoch打乱数据顺序,避免模型学到样本顺序 num_workers=4, # 并行加载图片的进程数,Windows上建议设为0或2 pin_memory=True # 锁页内存传输到GPU更快 ) val_loader = DataLoader( val_dataset, batch_size=batch_size, shuffle=False, # 验证时不打乱,便于按顺序追踪每条样本的预测 num_workers=4, pin_memory=True )

参数说明:batch_size=32是 ResNet-18 在常见显卡上的平衡点,显存紧张时降到 16;num_workers在 Windows 的 Jupyter 环境下经常因为多进程启动问题报错,设 0 是保守方案,在if __name__ == '__main__'保护的脚本里设 4 没问题。pin_memory开启后能减少 CPU 到 GPU 的数据拷贝时间,但只在 GPU 训练时有效。

3. 特征工程与模型构建:CNN 迁移学习为何是首选

3.1 传统特征方法为什么被卷积网络取代

花卉识别课程设计中,仍然有不少同学先尝试 HOG(方向梯度直方图)或颜色直方图提取特征,再喂给 SVM。这个路线本身没有错,在 10 年前是主流方案,但它有两个硬伤:特征是人工设计的,无法针对当前数据集自适应调整;高维特征下的 SVM 调参也相当繁琐。当训练数据换成真实花卉照片后,这些手工特征对光照和背景的鲁棒性远不如 CNN 自动学到的特征。

但这不意味着传统方法没有价值。数据挖掘课程设计里,用 PCA 对 HOG 特征降维再接 SVM 的做法,恰好能呈现出特征工程的教学意义——降维后可视化、观察类别分布、调整核函数参数。如果时间充裕,建议两种路线都做对比,答辩时能讲出“为什么 CNN 效果更好”的实证依据。

3.2 迁移学习:微调 ResNet-18 的具体实现

import torch.nn as nn from torchvision import models # 加载在ImageNet上预训练过的ResNet-18 model = models.resnet18(pretrained=True) # 查看最后一层结构,ResNet-18的fc层输入是512维 # 原结构: Linear(in_features=512, out_features=1000, bias=True) num_features = model.fc.in_features # 512 # 替换全连接层,输出数量等于花卉类别数(这里是5类) model.fc = nn.Linear(num_features, 5) # 将模型移动到GPU(如果可用) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device)

逻辑说明:resnet18(pretrained=True)会从网络下载约 45MB 的权重文件,第一次运行需要联网。替换fc层后,模型结构变成“预训练卷积层 + 随机初始化的全连接层”。此时有两种微调策略:

  • 全量微调:所有层都参与反向传播,学习率调小(如 0.0001),适合目标数据集与 ImageNet 差异较大的场景
  • 冻结卷积层:只训练最后的全连接层,速度快、不易过拟合,适合数据集小且与自然图像相似度高的场景

花卉照片与 ImageNet 中的物体形态差异不算大,但背景差异显著,建议采用全量微调加较小学习率,让卷积层在保留底层纹理特征的同时适应花卉的特定结构。

3.3 从头训练小模型:更好理解 CNN 的组件

课程设计如果只做迁移学习,答辩时容易答不上“为什么预训练有效”。这里给出一个轻量 CNN 的完整定义,用于验证“数据量不足时小模型也能逼近迁移学习效果”。

import torch.nn.functional as F class SimpleCNN(nn.Module): """一个4层卷积的小型CNN,适合小规模花卉数据集""" def __init__(self, num_classes=5): super(SimpleCNN, self).__init__() # 卷积块1: 3通道输入,16个卷积核,提取低级纹理特征 self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1) self.bn1 = nn.BatchNorm2d(16) # 卷积块2: 16→32,提取稍复杂的形状特征 self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1) self.bn2 = nn.BatchNorm2d(32) # 卷积块3: 32→64 self.conv3 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.bn3 = nn.BatchNorm2d(64) # 卷积块4: 64→128,最后输出通道数翻倍 self.conv4 = nn.Conv2d(64, 128, kernel_size=3, padding=1) self.bn4 = nn.BatchNorm2d(128) # 全局平均池化,将特征图压缩为1x1 self.global_pool = nn.AdaptiveAvgPool2d((1, 1)) # 最终分类器 self.fc = nn.Linear(128, num_classes) def forward(self, x): # 每个卷积块: 卷积→批归一化→ReLU→最大池化(2x2) x = F.relu(self.bn1(self.conv1(x))) x = F.max_pool2d(x, 2) x = F.relu(self.bn2(self.conv2(x))) x = F.max_pool2d(x, 2) x = F.relu(self.bn3(self.conv3(x))) x = F.max_pool2d(x, 2) x = F.relu(self.bn4(self.conv4(x))) x = F.max_pool2d(x, 2) # 全局池化后展平,输入全连接层 x = self.global_pool(x) x = x.view(x.size(0), -1) x = self.fc(x) return x

代码说明:每个卷积块都按“卷积—归一化—激活—池化”的顺序组织。BatchNorm2d的作用是让每层输入分布稳定,能显著加速收敛,这对从头训练的模型尤其重要。padding=1保证卷积前后特征图尺寸不变,池化负责减半尺寸,所以 224×224 的输入经过 4 次池化后变成 14×14,再经全局平均池化变成 1 个特征值。这种结构设计参考了 VGG 的风格,简单且容易调试。

3.4 损失函数与优化器选择

import torch.optim as optim # 分类任务标准选择:交叉熵损失 criterion = nn.CrossEntropyLoss() # Adam优化器,学习率设为1e-4(微调场景常用) optimizer = optim.Adam(model.parameters(), lr=1e-4) # 学习率调度器:每5个epoch将学习率乘以0.1 scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)

CrossEntropyLoss在 PyTorch 中已经把 Softmax 和负对数似然封装在一起,所以模型的输出不需要再手动加 Softmax 层。Adam相比 SGD 的优势是自带自适应学习率,对初始学习率不那么敏感,适合课程设计这种调试时间有限的场景。如果追求更高精度,可以用SGD(momentum=0.9, weight_decay=1e-4)配余弦退火学习率,但需要多调几个 epoch 才能看到收益。

学习率调度器StepLR的作用是让训练后期步长变小,帮助损失函数在极小值附近更精细地收敛。这里设置每 5 个 epoch 缩小到原来的 0.1 倍,是比较激进的衰减策略,适用于 epoch 总数只有 15~20 的短训练。

4. 训练循环与调参实战:完整脚本逐行注释

4.1 早停机制:防止过拟合的动态保存策略

训练深度学习模型最容易犯的错误是“埋头训到最后一个 epoch,然后发现验证集准确率早就开始下降了”。早停(Early Stopping)是必须实现的机制:每个 epoch 结束后比较验证集准确率,如果比历史最好值高就保存模型,否则计数器加一,连续 N 个 epoch 没有提升就终止训练。

best_acc = 0.0 patience = 5 # 连续5个epoch没有提升则停止 patience_counter = 0 for epoch in range(num_epochs): # ---------- 训练阶段 ---------- model.train() # 切换到训练模式,启用Dropout和BatchNorm的统计更新 train_loss = 0.0 train_correct = 0 for images, labels in train_loader: images = images.to(device) labels = 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 += torch.sum(preds == labels).item() epoch_train_loss = train_loss / len(train_loader.dataset) epoch_train_acc = train_correct / len(train_loader.dataset) # ---------- 验证阶段 ---------- model.eval() # 切换到评估模式,BatchNorm使用累积均值而非batch内统计量 val_correct = 0 with torch.no_grad(): # 关闭梯度计算,省显存、提速 for images, labels in val_loader: images = images.to(device) labels = labels.to(device) outputs = model(images) _, preds = torch.max(outputs, 1) val_correct += torch.sum(preds == labels).item() epoch_val_acc = val_correct / len(val_loader.dataset) print(f'Epoch [{epoch+1}/{num_epochs}] ' f'Train Loss: {epoch_train_loss:.4f} ' f'Train Acc: {epoch_train_acc:.4f} ' f'Val Acc: {epoch_val_acc:.4f}') # ---------- 早停检查 ---------- if epoch_val_acc > best_acc: best_acc = epoch_val_acc patience_counter = 0 torch.save(model.state_dict(), 'best_model.pth') else: patience_counter += 1 if patience_counter >= patience: print(f'早停触发: 连续{patience}个epoch验证集无提升') break scheduler.step() # 更新学习率 # 训练结束后加载最优模型 model.load_state_dict(torch.load('best_model.pth'))

训练阶段的代码逻辑说明:optimizer.zero_grad()那句如果漏掉,梯度会在 batch 间累积,损失会剧烈震荡且不收敛,这是新手最常见的 bug。model.train()model.eval()的切换也很关键——BatchNorm 层在两种模式下的行为不同,漏掉切换会导致验证集准确率忽高忽低,而且每次结果都不一样。

with torch.no_grad()块内不构建计算图,内存占用大幅下降。验证集不需要shuffle,所以 DataLoader 里设了shuffle=False。这里保存的是state_dict而不是整个模型对象,推荐这种做法,因为加载时对模型结构有更严格的控制,不依赖原始类定义的路径。

4.2 超参数速查表:不同情况怎么改

超参数默认值数据量小 (<100/类)数据量中 (100-300/类)数据量大 (>300/类)
batch_size32163264
初始学习率1e-41e-41e-3 (从头训练) / 1e-4 (微调)1e-3
epoch 数2010-1520-3030-50
早停耐心值5357
优化器AdamAdamAdam / SGDSGD + Momentum
Dropout 率0.50.30.50.5

关键判断逻辑:数据量越少,学习率要越小、epoch 要越少,否则模型会在训练集上快速收敛但在验证集上快速发散。从头训练 CNN 时,学习率可以比迁移学习高一个数量级,因为所有层都是随机初始化,需要更大的步长来快速探索参数空间。

4.3 损失曲线怎么读:判断模型状态的依据

每个 epoch 结束后,建议自己记录训练损失和验证损失的变化趋势,比单纯看准确率更有诊断价值:

  • 训练损失下降、验证损失下降:正常训练,保持现有参数
  • 训练损失下降、验证损失上升:过拟合开始,应该增大数据增强强度或增加 Dropout
  • 训练损失不降:学习率太大或太小,直接调到 1e-3 再试
  • 验证损失震荡剧烈:学习率过大、batch_size 过小,或者数据增强太强导致训练不稳定
  • 验证损失比训练损失低很多:数据集划分有问题(比如分布重叠),或训练集增强过度

如果你用 TensorBoard 或 wandb 记录这些曲线,答辩时可以展示截图,这是课程设计评分中“分析能力”的重要加分项。

5. 模型评估与错误分析:不只报一个准确率

5.1 混淆矩阵:找出类别混淆的具体模式

准确率是一个过于粗糙的指标。花卉数据集的类别往往有不均衡现象,如果“雏菊”类样本占 60%,“郁金香”只占 10%,那么模型把郁金香全部分错也能拿到 60% 准确率。正确做法是输出混淆矩阵,逐类查看精度和召回率。

from sklearn.metrics import confusion_matrix, classification_report import numpy as np # 收集所有验证集预测结果 all_preds = [] all_labels = [] model.eval() with torch.no_grad(): for images, labels in val_loader: images = images.to(device) outputs = model(images) _, preds = torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) # 计算混淆矩阵 cm = confusion_matrix(all_labels, all_preds) class_names = ['daisy', 'dandelion', 'rose', 'sunflower', 'tulip'] # 按类别打印精确率、召回率、F1-score print(classification_report(all_labels, all_preds, target_names=class_names, digits=3)) # 手动打印混淆矩阵,逐行解读 print("\n混淆矩阵(行=真实类别,列=预测类别):") for i, row in enumerate(cm): print(f"{class_names[i]:12s} " + " ".join(f"{x:3d}" for x in row))

这段代码的价值在于把评估从“一个数字”扩展成“一张诊断表”。classification_report里的f1-score是精度和召回率的调和平均,类别不平衡时应该主要看这个指标而不是总体准确率。混淆矩阵的对角线元素表示正确分类数,非对角线上的值越大,说明这两个类别在视觉上越容易混淆。

比如玫瑰和郁金香都是花瓣层层叠叠的花型,卷积网络很容易把它们混淆——如果混淆矩阵显示这两个类别互相误判较多,可以针对性收集更多样本,或者查看模型具体分错的图片来确认原因。

5.2 Grad-CAM 可视化:证明模型确实在“看花”

课程设计答辩时,“为什么相信你的模型”是必答题。只靠测试集准确率很难说服评委。Grad-CAM 能生成热力图,显示模型在分类时重点关注图像哪些区域。如果模型聚焦在花朵本身而非背景,说明学到了有效特征。

from torchvision import transforms import cv2 import torch def grad_cam_visualize(model, img_tensor, device): """生成Grad-CAM热力图并叠加到原图上""" model.eval() # 注册hook获取最后一个卷积层的输出特征图 feature_map = None def forward_hook(module, input, output): nonlocal feature_map feature_map = output # ResNet-18的最后一层卷积是layer4的最后一个BasicBlock的conv2 target_layer = model.layer4[1].conv2 handle = target_layer.register_forward_hook(forward_hook) # 前向传播,获取预测类别 img = img_tensor.unsqueeze(0).to(device) output = model(img) _, pred = torch.max(output, 1) pred_class = pred.item() # 反向传播到特征图,获取梯度 model.zero_grad() one_hot = torch.zeros_like(output) one_hot[0][pred_class] = 1 output.backward(gradient=one_hot) # 获取特征图对应位置的梯度 gradients = target_layer.weight.grad # 简化写法,实际需注册backward hook # 全局平均池化得到权重,再对特征图加权求和 weights = torch.mean(gradients, dim=(2, 3), keepdim=True) cam = torch.sum(weights * feature_map, dim=1, keepdim=True) cam = F.relu(cam) # 只保留正贡献区域 # 上采样到原图尺寸 cam = F.interpolate(cam, size=(224, 224), mode='bilinear', align_corners=False) cam = cam.squeeze().cpu().numpy() cam = (cam - cam.min()) / (cam.max() - cam.min()) # 归一化到0-1 # 将热力图叠加到原图上 heatmap = cv2.applyColorMap(np.uint8(255 * cam), cv2.COLORMAP_JET) original_img = img_tensor.permute(1, 2, 0).numpy() * 0.2 + 0.5 # 反归一化简化 original_img = np.uint8(255 * original_img) overlay = cv2.addWeighted(original_img, 0.6, heatmap, 0.4, 0) handle.remove() # 用完移除hook return overlay, pred_class

注意这里 Grad-CAM 是简化实现,target_layer.weight.grad并不是标准的 hook 取梯度方式,完整做法是注册backward_hook获取grad_output。课程设计实现到热力图可视化这一步,比很多只报准确率的项目已经有明显区分度。如果时间紧张,可以参考pytorch_grad_cam库的写法,但答辩前务必自己跑通一遍。

数据挖掘与机器学习课程设计不是一个“调模型”的比赛,而是一个“讲清楚决策过程”的训练。特征可视化、错误案例分析、数据增强前后的对比实验,都比单纯提高 0.5% 的准确率更能体现数据挖掘思维。

6. 收尾技巧:把课程设计从“能用”做到“能答辩”

6.1 准确率之外的三个必看指标

最后一轮评估不要只看测试集准确度,至少补充这三项分析:

按类别的召回率差异分析。一个 5 分类模型总体准确率 92%,但郁金香类召回率只有 75%,说明模型对这个类别存在系统性漏判。结合混淆矩阵定位到具体混淆对后,可以针对性查看训练样本中的郁金香图片——如果样本里花朵占比太小、背景过于杂乱,模型学到的就是背景特征。

不同训练策略的对比实验。至少跑三组实验:从零训练 SimpleCNN、冻结卷积层只训练全连接层、全量微调 ResNet-18。把三组实验的准确率和收敛速度画成表格或折线图,这个对比可以直接回答答辩中“为什么选迁移学习”和“为什么不直接随机初始化”这两个问题。

置信度与错误预测的关系分析。对验证集中每条样本记录 Softmax 输出的最大概率值(即模型置信度),统计错误样本的置信度分布。如果大量错误样本置信度超过 0.9,说明模型存在过度自信问题——这对理解损失函数设计和数据分布有帮助。

6.2 测试集上避免“信息泄漏”的三个细节

这是数据挖掘课程设计中最容易犯、也最容易翻车的点:

第一,标准化参数只用训练集拟合。如果你选择在图像预处理之外再做像素级标准化(虽然 Transfer Learning 场景不推荐),meanstd只能用训练集统计,不能把验证集和测试集混在一起计算再统一减去——这属于信息泄漏,会让验证结果虚高。

第二,数据增强绝不应用于测试集。测试集必须用最朴素的预处理(Resize + ToTensor + Normalize),任何随机旋转、裁剪、翻转都会让评估指标失去可复现性。

第三,早停不能拿测试集做判断。早停机制中“验证集没有提升”用的应该是从训练集中划出的验证子集,而不是最终测试集。如果把测试集参与早停决策,相当于用测试集做了多次实验,最终报告准确率的统计意义会打折扣。

6.3 目录结构和 README 怎么写能被一眼看懂

课程设计最终交付物通常包括代码、报告和演示。建议代码目录按以下方式组织:

flower_recognition/ ├── README.md # 环境依赖、目录说明、运行方式 ├── requirements.txt # torch, torchvision, opencv-python, numpy, scikit-learn ├── data/ │ ├── train/ # 训练集图片(按类别分子目录) │ └── val/ # 验证集图片 ├── src/ │ ├── dataset.py # 数据增强与DataLoader构建 │ ├── model.py # SimpleCNN与ResNet-18微调定义 │ ├── train.py # 训练循环、早停、模型保存 │ └── evaluate.py # 混淆矩阵、分类报告、Grad-CAM可视化 └── outputs/ ├── best_model.pth # 训练好的权重 ├── confusion_matrix.png ├── loss_curve.png └── grad_cam_samples/

README 中除了列环境版本(Python 3.9、PyTorch 2.x),要写清楚运行一条python src/train.py从零复现的步骤。如果数据集太大不方便一并提交,给出下载链接并说明目录脚本。整理这部分内容虽然不直接提升模型精度,但在评分时对于“这个项目是否工程可复现”有直观影响。

答辩演示时,找 3 张测试集里确实被分错的图片,打印出 Grad-CAM 热力图,说明模型关注了背景或只覆盖了部分花朵区域——这种分析比“准确率达到了 95%”更有说服力,也符合数据挖掘课程对“从数据中找规律”“从错误中提炼改进方向”这两项核心能力的考核目标。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 5:45:41

花卉识别课程设计实战:从数据读取到模型评估的完整指南

简介&#xff1a;面向数据挖掘与机器学习课程设计的实战项目&#xff0c;这份花卉识别资源使用 Python 编写&#xff0c;通过带注释的代码演示了从数据读取、样本划分到模型训练前的关键准备工作&#xff0c;适合正在完成课程设计或入门机器学习分类任务的读者参考。资源包整体…

作者头像 李华
网站建设 2026/9/15 5:44:25

半导体制造中的专业标签软件:核心功能与实施要点

1. 半导体标签软件行业现状与需求分析半导体制造作为精密工业的典型代表&#xff0c;对生产流程中的物料追踪和质量管理有着近乎苛刻的要求。在晶圆厂的无尘车间里&#xff0c;一片8英寸硅片从投料到成品需要经历300-500道工序&#xff0c;任何环节的标识错误都可能导致数百万美…

作者头像 李华
网站建设 2026/9/15 5:44:06

iOS安全认证实战:Token、MD5与RSA核心技术解析

1. iOS网络安全认证概述在移动应用开发领域&#xff0c;安全认证是保护用户数据和系统完整性的第一道防线。作为iOS开发者&#xff0c;我们每天都要面对各种认证机制的实现和优化。Token、MD5和RSA这三种技术看似基础&#xff0c;却是构建iOS应用安全体系的三大支柱。记得去年我…

作者头像 李华
网站建设 2026/9/15 5:44:04

Arduino IDE跨平台安装原理与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 5:43:45

Flutter+OpenHarmony实现跨平台心率监测App开发

1. 项目背景与核心需求在智能穿戴设备和健康监测应用爆发的当下&#xff0c;开发一款跨平台的身体健康记录App具有重要现实意义。这次我们选择FlutterOpenHarmony技术栈&#xff0c;重点实现心率监测模块的完整闭环。Flutter的跨平台特性与OpenHarmony的分布式能力结合&#xf…

作者头像 李华