尧图精选

图像识别深度神经网络项目实战:从环境配置到模型训练与调优

🕒 发布时间:2026/9/24 22:40:00 📁 来源:尧图网络
简介面向计算机、电子信息工程、数学等专业学生打造的图像识别深度神经网络实践包以Python实现为主涵盖源码、数据集与配套PPT说明。内容围绕常用网络模型的设计与训练展开既适合课程设计、期末大作业也可作为毕业设计初期的参考资料帮助读者理解数据预处理、模型搭建、训练评估等完整流程。压缩包整体约375.8MB主要文件类型包括Python源码、图像数据集和PPT课件其中代码便于结合数据运行调试PPT用于梳理网络结构与关键知识点目录结构较为清晰。已有153人浏览学习适合具备一定Python和深度学习基础、希望上手实际项目的读者。参考资料而非定制需求使用者需具备自行调试代码、修复报错及扩展功能的能力在此基础上可充分借助源码与演示数据快速搭建实验环境进一步掌握常用图像识别网络的实现思路。1. 图像识别深度神经网络项目从源码到能跑通的完整链路手头这个标题“基于Python实现常用图像识别深度神经网络源码数据PPT.rar”本质上是一份打包好的教学型项目。这类资源在开发者手里很常见但真正卡住大部分人的不是模型原理而是“解压之后从哪下手”——环境装哪个版本、数据放什么目录、训练脚本怎么改、跑完怎么判断模型确实在工作。这篇文章就按我平时接手这类项目的顺序把整个链路拆开讲从 Python 环境准备、数据集组织到卷积神经网络CNN与预训练模型的源码实现、训练参数调优最后落到排错和可视化验证。适合刚入门深度学习但手里已经有一份源码包、想把它跑起来并改造成自己任务的人也适合带学生做课设的指导者当作项目拆解参考。2. 从环境到数据图像识别项目跑通前的两件大事2.1 依赖选型PyTorch 还是 TensorFlow别在框架上内耗解压源码包后第一件事不是打开 PPT而是确认这份源码的模型代码是基于哪个框架写的。常见做法是看代码里import的库如果是torch开头就是 PyTorchtensorflow或keras开头就是 TensorFlow。两个框架都能做图像识别深度神经网络但调试体验差异很大。我的建议是除非源码强依赖 TensorFlow 的特定接口否则优先选 PyTorch原因是它的动态图和报错信息对新手友好——模型 forward 过程可以直接打断点打印张量形状这在排查维度不匹配问题时能省下大量时间。版本匹配是第一个坑。PyTorch 的 API 在 1.x 和 2.x 之间有少量接口变动老源码里常见的torchvision.models.resnet18(pretrainedTrue)在 2.x 中变成了weightsResNet18_Weights.IMAGENET1K_V1的写法。如果源码是按老接口写的两个方案一是安装与源码匹配的旧版本比如 CUDA 11.8 对应 PyTorch 1.13.1二是改源码适配新接口改动量通常只有一两行。我一般优先选后者因为新版本对 AMP自动混合精度的支持更好训练速度有明显提升。提示先跑通一个最小命令再装全量依赖。用pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这种纯 CPU 或指定 CUDA 版本的方式安装比直接pip install torch更可控能避免把 CUDA 版本装错导致 GPU 识别不到。2.2 数据集组织方式ImageFolder 结构与校验脚本图像识别项目最常见的数据集组织方式是按类别分文件夹这是torchvision.datasets.ImageFolder直接支持的结构。源码包里的数据如果已经是这种结构可以直接用如果不是需要先转换为这个格式。目录结构通常长这样data/ train/ cat/ 001.jpg 002.jpg ... dog/ 001.jpg 002.jpg ... val/ cat/ 001.jpg ... dog/ 001.jpg ...转换脚本的核心逻辑是遍历原始目录把每个类别的图片复制到目标目录下同时做一次损坏检查。实际在做项目时数据集里混入损坏图片是训练中途报错的最常见原因之一。下面这段代码在做目录重组的同时会用 PIL 验证图片是否能正常打开并把损坏文件路径输出到日志文件里import os import shutil from PIL import Image def prepare_dataset(src_root, dst_root, ratio0.8): 按类别目录结构整理数据集并按比例划分训练集与验证集。 同时检测损坏图片避免训练时 OSError 中断。 Args: src_root: 原始数据根目录每个子文件夹是一个类别 dst_root: 目标数据根目录会自动创建 train/val 子目录 ratio: 训练集比例剩余为验证集 categories [d for d in os.listdir(src_root) if os.path.isdir(os.path.join(src_root, d))] for cat in categories: cat_path os.path.join(src_root, cat) images [f for f in os.listdir(cat_path) if f.lower().endswith((.jpg, .jpeg, .png))] # 先按文件名排序保证划分结果可复现 images.sort() train_dst os.path.join(dst_root, train, cat) val_dst os.path.join(dst_root, val, cat) os.makedirs(train_dst, exist_okTrue) os.makedirs(val_dst, exist_okTrue) split_idx int(len(images) * ratio) corrupt_list [] for i, img_name in enumerate(images): src_path os.path.join(cat_path, img_name) # 用 PIL 尝试打开图片验证其完整性 try: with Image.open(src_path) as img: img.verify() except Exception: corrupt_list.append(src_path) continue if i split_idx: shutil.copy(src_path, os.path.join(train_dst, img_name)) else: shutil.copy(src_path, os.path.join(val_dst, img_name)) if corrupt_list: with open(corrupt_images.txt, a) as f: for p in corrupt_list: f.write(p \n) print(f[WARN] {cat} 类中发现 {len(corrupt_list)} 张损坏图片已跳过)这段代码里的img.verify()是检查图片完整性的关键操作它只读取文件头信息不会真正解码图像数据所以速度很快。划分训练集前先对文件名排序是为了让每次运行脚本得到完全相同的划分结果——这在复现实验结果时很重要否则两次训练用的验证集不同准确率就没有可比性。损坏图片列表写入corrupt_images.txt方便后续补数据时按图索骥。2.3 数据增强的标配组合为什么亮度抖动和随机裁剪不能省数据增强是图像识别深度神经网络训练中性价比最高的一步——不需要改模型结构不需要加数据只靠几行transform代码就能显著降低过拟合。但增强不是越狠越好幅度过大会让模型看到的数据与真实场景偏离太多导致验证集上表现反而变差。我常用的配置是随机裁剪加水平翻转加亮度色彩抖动这个组合在大多数自然图像分类任务上表现稳定。from torchvision import transforms # 训练集增强随机裁剪翻转色彩抖动 train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.8, 1.0)), transforms.RandomHorizontalFlip(), 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_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]) ])RandomResizedCrop的scale参数控制裁剪面积占原图的比例设置在 0.8 到 1.0 之间会比较保守如果数据集小可以放宽到 0.5让模型看到更多不同尺度的目标。Normalize用的 mean 和 std 是 ImageNet 数据集的统计值如果是自定义数据集严格来说应该重新计算数据集的均值和标准差但实际项目中直接沿用 ImageNet 的统计值也能用特别是在迁移学习场景下——预训练模型原本就是基于这些统计值训练的输入分布保持一致更合理。注意验证集一定不要加随机增强。否则验证准确率每次运行都不一样模型是否真的变好就说不清了。3. 核心模型实现从手写 CNN 到迁移学习3.1 一个能跑的自定义 CNN用小网络理解特征提取流程源码包里如果带了一个从头训练的 CNN 模型通常是 LeNet 或 VGG 的简化版。手写 CNN 的价值不在于拿成绩而在于理解卷积、池化、全连接这条特征提取链路。下面这个网络结构很精简但五脏俱全import torch.nn as nn class SimpleCNN(nn.Module): 一个适用于 224x224 输入的简易 CNN。 结构卷积块x2 - 全局平均池化 - 全连接分类头 def __init__(self, num_classes10): super().__init__() # 第一个卷积块3通道输入输出16个特征图 self.conv_block1 nn.Sequential( nn.Conv2d(3, 16, kernel_size3, stride1, padding1), nn.BatchNorm2d(16), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2) # 112x112 ) # 第二个卷积块16通道输入输出32个特征图 self.conv_block2 nn.Sequential( nn.Conv2d(16, 32, kernel_size3, stride1, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2) # 56x56 ) # 全局平均池化后接全连接分类 self.avg_pool nn.AdaptiveAvgPool2d((1, 1)) self.classifier nn.Linear(32, num_classes) def forward(self, x): x self.conv_block1(x) # [B, 16, 112, 112] x self.conv_block2(x) # [B, 32, 56, 56] x self.avg_pool(x) # [B, 32, 1, 1] x torch.flatten(x, 1) # [B, 32] return self.classifier(x) # [B, num_classes]BatchNorm2d放在卷积层之后、激活函数之前是现在的主流做法相比没有 BatchNorm 的网络训练收敛速度快很多而且对初始学习率的敏感度更低。AdaptiveAvgPool2d是关键设计——不管输入图片尺寸是 224 还是 256它都能把特征图池化成 1x1让全连接层的输入维度不受输入图片大小影响。这在实际项目里很有用意味着你可以用不同尺寸的图片做训练和推理。3.2 迁移学习用 ResNet 预训练模型替换分类头手写 CNN 在小型数据集上效果通常一般。更常用的做法是加载在 ImageNet 上预训练好的模型把最后一层全连接换成自己任务的分类头然后只训练分类头或者对所有层做微调。这个思路叫迁移学习是图像识别深度神经网络项目里最主流、最稳的套路。import torchvision.models as models def build_model(num_classes10, freeze_backboneTrue): 构建迁移学习模型。 Args: num_classes: 分类任务类别数 freeze_backbone: 是否冻结特征提取层的参数 # 新版 torchvision 的推荐写法替代旧的 pretrainedTrue weights models.ResNet18_Weights.DEFAULT model models.resnet18(weightsweights) # 获取最后一个全连接层的输入维度 in_features model.fc.in_features # 替换分类头 model.fc nn.Sequential( nn.Dropout(p0.5), nn.Linear(in_features, num_classes) ) if freeze_backbone: # 冻结除 fc 外的所有参数 for param in model.parameters(): param.requires_grad False # 只让分类头的参数参与梯度更新 for param in model.fc.parameters(): param.requires_grad True return model冻结主干参数后反向传播只会更新分类头的参数显存占用小、训练速度快。如果数据集比较充足每类 1000 张以上可以考虑解冻最后几层让它微调一下特征。Architecture-wise, ResNet18 在算力有限的机器上是性价比最高的选择ResNet50 参数多了近三倍但小数据集上效果提升有限反而更容易过拟合。3.3 训练入口脚本训练、验证、测试三段式拆分源码包里如果只有一个训练脚本通常会包含以下流程加载数据、初始化模型、定义损失函数和优化器、循环 epoch、每个 epoch 结束后跑一次验证、保存最优模型。下面是去掉细节后的核心骨架import torch import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder def train_one_epoch(model, dataloader, criterion, optimizer, device): model.train() total_loss, correct, total 0, 0, 0 for images, labels in dataloader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) correct (preds labels).sum().item() total images.size(0) return total_loss / total, correct / total def validate(model, dataloader, criterion, device): model.eval() total_loss, correct, total 0, 0, 0 with torch.no_grad(): for images, labels in dataloader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) total_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) correct (preds labels).sum().item() total images.size(0) return total_loss / total, correct / total # 使用示例 device torch.device(cuda if torch.cuda.is_available() else cpu) train_dataset ImageFolder(data/train, transformtrain_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) model build_model(num_classes10).to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-4) for epoch in range(30): train_loss, train_acc train_one_epoch( model, train_loader, criterion, optimizer, device) val_loss, val_acc validate(model, val_loader, criterion, device) print(fEpoch {epoch1:02d} | Train Loss: {train_loss:.4f} | fTrain Acc: {train_acc:.4f} | Val Acc: {val_acc:.4f})train_one_epoch和validate分开写的用意很明显训练模式下需要梯度计算和参数更新验证模式下要用torch.no_grad()关闭梯度追踪既省显存又加快速度。model.train()和model.eval()切换也很关键它会影响 Dropout 和 BatchNorm 的行为——训练时 Dropout 随机丢弃神经元验证时保留全部神经元。4. 训练参数与调优三个直接影响收敛效果的关键点4.1 学习率与 Batch Size 的匹配关系先定 Batch 再定 LR图像识别深度神经网络训练中学习率和 Batch Size 是耦合的——Batch Size 越大梯度估计越平滑可以承受的学习率就越大。常见的匹配关系是Batch Size 32 配学习率 1e-3Adam 优化器Batch Size 128 配学习率 3e-3 到 1e-2。如果不匹配典型症状是 loss 曲线震荡剧烈或收敛后反而发散。如果源码里默认的学习率是 1e-2你自己把 Batch Size 从 64 改到了 32却不改学习率那 loss 大概率会在某个 epoch 后突然飙上去。原因很简单Batch Size 变小导致每个 batch 的梯度噪声变大过大的学习率会让参数更新跨越损失函数的“谷底”。处理方式有两个一是按比例缩小学习率二是把lr_scheduler换成余弦退火让学习率在训练后期自动减小。import torch.optim as optim from torch.optim.lr_scheduler import CosineAnnealingLR optimizer optim.Adam(model.parameters(), lr1e-3) scheduler CosineAnnealingLR(optimizer, T_max30, eta_min1e-6) # 每个 epoch 结束后调用 for epoch in range(30): train_one_epoch(...) scheduler.step()T_max是余弦周期的半长一般设为总 epoch 数。eta_min是学习率的下限设置成初始学习率的千分之一比较合理。余弦退火的好处是前期保持较大学习率快速前进后期平滑降低避免在最优值附近震荡。这里还有个实战细节如果遇到 loss 曲线在中后期出现周期性弹跳检查是不是scheduler.step()被调了两次——比如在 epoch 循环里调了一次又在某个回调里调了一次。4.2 损失函数与类别不均衡用加权交叉熵处理分布偏移图像识别项目里类别不均衡是常态——工业缺陷检测中良品图片远多于不良品医疗图像中正样本占比常常不足 5%。如果直接拿标准交叉熵训练模型会倾向于把所有样本预测为多数类因为这样损失最小。处理方式是给损失函数传入weight参数对少数类样本的损失增大权重。import torch import torch.nn as nn def build_weighted_criterion(class_counts): 根据样本数量计算交叉熵权重。 Args: class_counts: list[int]每个类别的样本数量 Returns: criterion: 加权交叉熵损失函数 total sum(class_counts) # 权重与样本数成反比让少数类获得更高权重 weights [total / (len(class_counts) * c) for c in class_counts] weights torch.tensor(weights, dtypetorch.float32) return nn.CrossEntropyLoss(weightweights)权重计算公式total / (num_classes * c)的效果是样本数等于平均数的类别权重约为 1少数类权重大于 1多数类权重小于 1。这套方案简单粗暴但有效。需要注意加权交叉熵会稍微拉高训练损失的值因为少数类的损失被放大了这属于正常现象不要因为训练 loss 比之前高就觉得模型变差了关键看验证集上少数类的召回率。4.3 模型保存与断点续训checkpoint 里该存什么训练十几个小时后如果因为断电或显存溢出中断没有 checkpoint 机制就只能从头再来这是最让人崩溃的场景。模型保存不能只存 state_dict需要把完整训练状态都记录到 checkpoint 文件中import torch def save_checkpoint(model, optimizer, scheduler, epoch, val_acc, filepath): 保存完整的训练状态。 Args: model: 模型 optimizer: 优化器 scheduler: 学习率调度器 epoch: 当前 epoch 数 val_acc: 当前验证集准确率 filepath: 保存路径 checkpoint { epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), scheduler_state_dict: scheduler.state_dict() if scheduler else None, val_acc: val_acc, class_to_idx: train_dataset.class_to_idx # 类别映射推理时要用 } torch.save(checkpoint, filepath) def load_checkpoint(model, optimizer, scheduler, filepath): 加载 checkpoint 并恢复训练状态。 Returns: start_epoch: 中断时的 epoch 数继续从这个值1开始训练 val_acc: 中断时的验证准确率 checkpoint torch.load(filepath, map_locationcpu) model.load_state_dict(checkpoint[model_state_dict]) optimizer.load_state_dict(checkpoint[optimizer_state_dict]) if scheduler and checkpoint[scheduler_state_dict]: scheduler.load_state_dict(checkpoint[scheduler_state_dict]) return checkpoint[epoch], checkpoint[val_acc]保存optimizer_state_dict有两个作用一是恢复到中断时的优化器动量状态让训练无缝衔接二是保留学习率调度器的内部状态。如果只存模型权重恢复后学习率会从初始值重新开始这等于半途失去学习率退火的收益。class_to_idx保存类别映射关系是推理时的刚需——部署模型时你需要在分类索引和真实语义标签之间做转换训练时顺手存下来就省得推理时再猜。提示checkpoint 的保存策略建议每 5 个 epoch 覆盖保存一次每次保存都带上验证集准确率作为文件名后缀方便训练结束后从多个候选权重中挑选最优的。5. 图像识别深度神经网络常见问题排查五类高频坑的处理记录5.1 训练 Loss 不下降先查数据归一化再查学习率现象训练 loss 从一开始就在 1.0 附近徘徊几十个 epoch 后也没有明显下降趋势。原因最常见的是输入图片没有归一化到[0,1]区间。某些源码包的 transform 里漏了transforms.ToTensor()直接对PIL.Image做归一化导致输入像素值范围是[0,255]初始化的权重对这个量级的输入完全不适应。排在第二的原因才是学习率过小。解决先在 train 循环里print(images.min().item(), images.max().item())确认输入范围。如果是 0-255 的问题在 transform 最前面加上ToTensor()即可如果是学习率问题把 Adam 的初始化学习率从 1e-4 调大到 3e-3 试一次有效果就说明之前只是步子太小。5.2 GPU 显存不足别急着买显卡先查这两个参数现象训练到第一个 batch 的前向传播时报CUDA out of memory。原因Batch Size 过大或者输入图片分辨率过高。很多人不知道 224x224 的输入在 ResNet50 里中间特征图显存占用会比原始输入图片大得多——前向传播过程中所有中间结果都要驻留在显存中。解决先 Debug 模式跑一个 batch把 Batch Size 降到 8 或 4确认能跑通后再逐步加大如果图片输入是 512x512改动 transform 里的Resize和Crop参数到 224 是最直接的降显存手段。还有一个被低估的方法是开启混合精度训练PyTorch 从 1.10 开始推荐使用原生 AMP 接口from torch.cuda.amp import autocast, GradScaler scaler GradScaler() def train_one_epoch_amp(model, dataloader, criterion, optimizer, device): model.train() for images, labels in dataloader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() with autocast(): outputs model(images) loss criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()AMP 的原理是让前向和反向传播使用 FP16 精度而优化器更新保持 FP32能把显存占用降低约 40%同时训练速度提升 20% 到 30%绝大多数显卡都支持几乎没有翻车风险是花钱换硬件之前最值得先尝试的方案。5.3 验证集准确率波动大Dropout 与 BatchNorm 的组合调优现象训练准确率稳步上升验证准确率在每个 epoch 之间起伏极大比如 0.85 和 0.92 交替出现。原因如果模型同时使用了 Dropout 和 BatchNorm且 BatchNorm 的momentum设置偏小验证阶段的统计量本身就在波动。另一个常见原因是验证集太小——如果验证集只有一两百张图每个 epoch 的准确率天然波动大。解决先确认验证集至少要占总样本的 10% 且不低于 500 张然后检查模型代码里nn.Dropout(p0.5)的p值实际使用中图像分类任务的p超过 0.5 的不多调大到 0.7 反而会让随机性淹没真实性能提升。如果模型里同时有BatchNorm把它的momentum从默认的 0.1 调到 0.05能减缓统计量波动让 loss 曲线更平滑。5.4 源码报错 ModuleNotFoundError检查顺序比搜报错更快现象运行源码时提示ModuleNotFoundError: No module named torchvision或其他依赖缺失。原因源码包作者使用的环境和你本机的 Python 环境不同通常是因为 Python 版本不一致源码基于 3.8你用了 3.11、或者多个虚拟环境之间没有切换对。解决按下面的顺序排查——先python --version确认当前 Python 版本再pip list | grep torch看依赖是否安装最后which python确认自己所在的虚拟环境。问题主要集中在“代码在 conda 的 base 环境里跑而依赖装在另一虚拟环境”。一个更省心的方案是用项目根目录的requirements.txt重建环境python -m venv venv source venv/bin/activate # Windows 下为 venv\Scripts\activate pip install -r requirements.txt如果源码没有提供 requirements.txt就用 pip freeze 之前能跑通的环境生成一份。这类问题占了源码类项目提问的 60% 以上绝大多数不是代码问题而是环境问题——代码本身在作者的机器上是跑通的换个环境跑不起来基本就是依赖没对齐。5.5 复现结果和 PPT 不一致先查随机种子与数据顺序现象PPT 里写的验证集准确率是 0.94自己复现只有 0.88反复调整参数也补不上差距。原因PPT 中的数字来自作者某个特定时刻的模型权重而训练过程的随机性——包括数据 shuffle 顺序、权重初始化、Dropout 的随机丢弃——决定了最终结果本身就有一定波动。如果源码里没有固定随机种子每次训练结果差 2 到 3 个百分点完全是正常范围。解决先看源码里有没有random_seed参数没有的话自己加三行代码import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)固定随机种子后能让训练结果在同一个环境下可复现。但即便如此如果源码里的数据增强用了随机裁剪不同版本的 PyTorch 对随机数生成器的实现不完全一致结果可能仍有微小差异。实际处理这类差距时更关注的是判断“这个 0.88 是否在一个合理范围内”而不是“为什么不是精确的 0.94”——如果自己的数据组织方式和源码一致、训练轮数一致、超参数一致0.88 与 0.94 之间的差距大概率来自随机性而非代码 bug。6. 把源码变成自己的项目特征图可视化与训练曲线分析源码跑通只是第一步真正值钱的是把模型迁移到自己的数据集上并且能“看见”模型在做什么。特征图可视化是最直观的验证手段——把输入图片送进训练好的模型提取第一个卷积块的输出你会发现模型最开始关注的是边缘和颜色而不是形状。这一步可以确认模型确实学到了有效特征而不是在强行记忆训练数据。import matplotlib.pyplot as plt import torch def visualize_feature_maps(model, image_tensor, layer_nameconv_block1): 提取并可视化指定卷积层的输出特征图。 Args: model: 训练好的模型 image_tensor: 经过 transform 的输入图片shape [1, 3, H, W] layer_name: 层名称取决于模型定义 model.eval() features {} # 在前向传播过程中捕获中间层输出 def hook_fn(module, input, output): features[layer_name] output # 注册 hook for name, module in model.named_modules(): if name layer_name: module.register_forward_hook(hook_fn) with torch.no_grad(): model(image_tensor) feature_maps features[layer_name][0] # [C, H, W] num_filters min(feature_maps.size(0), 16) fig, axes plt.subplots(4, 4, figsize(8, 8)) for i in range(num_filters): ax axes[i // 4][i % 4] ax.imshow(feature_maps[i].cpu().numpy(), cmapviridis) ax.axis(off) plt.tight_layout() plt.show()配合训练曲线的分析判断模型状态的逻辑是这样的如果训练 loss 持续下降、验证 loss 在某个 epoch 后开始回升说明模型进入了过拟合区间此时应该取验证 loss 最低点的权重如果训练和验证 loss 都还处于高位平台说明模型欠拟合需要增大模型容量或增加训练轮数。用 TensorBoard 记录这两条曲线再用tensorboard --logdir runs查看比打印到终端直观得多。我自己做图像识别项目时的习惯是每改动一个参数跑一轮训练然后把验证准确率、训练时长、显存峰值记到一个表格里——不要靠感觉判断哪个参数更好数字不会骗人。模型保存时用带准确率的文件名比如resnet18_acc0.93_epoch22.pt几个月后回来看还知道这个权重是哪次实验的结果。这套做法看着笨但项目多了之后你会发现清晰的实验记录比任何代码技巧都让人省心。希望这篇笔记能帮你在拿到类似源码包时少走几步弯路。先跑通、再改数、再看图按这个顺序来图像识别深度神经网络这条路就稳了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →