ARTICLE DETAIL

资讯详情

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

基于CNN的猫狗图像识别分类项目实战解析与避坑指南

基于CNN的猫狗图像识别分类项目实战解析与避坑指南 简介面向Python学习者和计算机专业学生的CNN猫狗图像识别分类实战资源适用于期末大作业、毕业设计、课程设计及深度学习入门练习。内含完整可运行的Python源码搭配大量猫狗图片数据集约1990张jpg及配套XML标注文件并附有一本文档说明PDF内容覆盖图像预处理、数据集划分、CNN模型构建、训练调参与评估预测等完整流程。压缩包共2000个文件整体大小218MB类型以图片为主同时包含代码、项目配置文件与文档目录结构清晰便于按模块检索和快速上手。项目曾获98分高分源码均经本地编译调试、确保可运行文档中给出设计与实现说明适合直接参考代码复现结果也便于整理为实验报告或答辩材料。目前已有223人学习下载对需要完成类似图像分类任务、提升项目实践能力的学生具有较高参考价值。1. 基于CNN的猫狗图像识别项目拿到源码先别跑先想清楚这三件事你手里刚拿到一个Python实战项目标题写着“基于CNN的猫狗图像识别检测分类项目源码数据集文档PDF高分项目”。先别急着解压跑train.py我的经验是这类项目真正值钱的地方不是那套能跑的代码而是你能不能把它讲明白、改得动、在新的数据集上复现。它解决的是一个经典二分类问题——给一张图片判断是猫还是狗背后依赖CNN卷积神经网络自动提取图像特征而不是人工设计特征。适合正在做课程设计、准备毕业设计或者找工作想往计算机视觉方向靠的从业者。下面我按自己落地这类项目的顺序把它从目录结构、数据预处理、模型训练一路拆到避坑和验证让你拿到手三天内能跑通并且敢在人前讲清楚。2. 项目拆解CNN分类任务的结构、文件目录与训练流程项目到手第一件事不是看代码而是看它属于哪个任务类型以及三件套源码、数据集、文档PDF是怎么组织在一起的。这决定了你后面改模型、调参、写报告的方向。猫狗识别听起来好像和“检测”沾边但严格说它属于图像分类不是目标检测。这一个概念区分不清楚后面整个项目定位都会跑偏。2.1 图像识别里的“分类”和“检测”不是一回事这个项目属于哪一类图像任务里最容易混的三个词是图像分类Image Classification、目标检测Object Detection、语义分割Image Segmentation。猫狗识别要解决的是“这张图整体上是不是猫/狗”属于图像分类。目标检测要做的是框出图中物体位置并给出类别比如同时定位出三只猫和两条狗的位置。分割则是像素级别地把猫狗轮廓抠出来。这个区分很实际因为模型结构完全不同。分类最后一层是Softmax输出两个概率猫、狗检测网络要有边界框回归分支分割网络则要输出像素级掩码。这个项目标题里写了“检测分类”但看数据集和模型通常做的是分类。如果你拿着分类项目的报告去答辩说“我做了目标检测”那就会被质疑。为什么用CNN而不是传统方法传统做法比如HOGSVM需要人工设计特征算子对光照、姿态、背景非常敏感。CNN通过卷积核自动学习边缘、纹理、形状等特征层层递进底层学线条高层学猫脸狗脸。它具备平移不变性猫在图片左边还是右边都能识别这是全连接网络不具备的特性。对于猫狗这种数据量大、类别数少、图片差异集中在纹理和轮廓上的任务CNN是最稳妥、最容易跑出高分的选型。2.2 源码、数据集、文档PDF三件套怎么配合使用一个规范的课程设计项目目录结构通常长这样。我一般会先建一个tree.txt防止找文件找到怀疑人生。cat_dog_classifier/ ├── data/ │ ├── train/ │ │ ├── cats/ # 猫的图片文件名通常是 cat.0.jpg ... │ │ └── dogs/ # 狗的图片文件名通常是 dog.0.jpg ... │ ├── val/ │ │ ├── cats/ │ │ └── dogs/ │ └── test/ # 有时没有标签用于推理提交 ├── models/ │ └── model.py # 模型结构定义 ├── utils/ │ └── dataset.py # 数据加载与预处理 ├── checkpoints/ # 训练中保存的模型权重 ├── train.py # 训练入口 ├── test.py # 测试/推理入口 ├── requirements.txt └── README.md源码是你的主路径数据集是原料文档PDF是结果呈现。很多人拿到项目只跑train.py完全忽略文档最后答辩时不仅说不清模型结构连数据怎么划分都含含糊糊这就浪费了这个“高分项目”的素材。文档PDF里通常包含的是需求分析、数据集说明、模型结构图、训练超参数、实验结果对比、结论与展望。我拿到文档PDF会先翻实验结果那一页看它最后准确率是多少用的什么模型和优化器。然后再核对源码看能不能复现出那个数字。如果复现不出来问题多半出在数据预处理和随机种子而不是模型本身。把源码、数据、文档三者的关系理顺后再去看训练流程就不会一头雾水。2.3 端到端训练流程数据 → 模型 → 损失 → 评估猫狗分类的训练流程在所有CNN分类项目里几乎是模板级的存在跑通一次之后换个数据集也能套。先用一段最精简的训练脚本把全流程走通import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder from torchvision import transforms # 用预训练ResNet18做二分类简单且不容易翻车 from torchvision.models import resnet18 # 1. 数据预处理resize 张量化 归一化 transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 2. 用ImageFolder按子目录自动生成标签 train_data ImageFolder(data/train, transformtransform) val_data ImageFolder(data/val, transformtransform) train_loader DataLoader(train_data, batch_size64, shuffleTrue, num_workers2) val_loader DataLoader(val_data, batch_size64, shuffleFalse, num_workers2) # 3. 模型把最后一层改成2分类 model resnet18(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, 2) model.cuda() # 4. 损失函数与优化器 criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) # 5. 训练一个epoch model.train() for images, labels in train_loader: images, labels images.cuda(), labels.cuda() outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step()这段代码里有个很容易看漏的点ImageFolder会按照子目录的名字对标签排序cats就是标签0dogs就是标签1。如果目录名是中文或者乱序标签就不可控。另外resnet18(pretrainedTrue) 在第一次运行时会从网络下载权重没网或下载慢会卡很久后面避坑章节会专门说。数据加载完之后训练就是反复的前向传播算loss、反向传播算梯度、优化器更新权重。每轮跑完在验证集上评估一下记录损失和准确率。整个项目的稳定性和分数高不高关键在第五步。很多人最后看到训练loss很低但验证准确率上不去大多是因为验证阶段数据预处理没对齐或是在验证集上漏了模型切换模式。训练流程本身不复杂真正复杂的是每一步里的“边界条件”。3. 数据准备与预处理从原始图片到可训练的张量猫狗分类这个项目数据往往是决定分数上限的因素。同样的模型数据干净不干净最终准确率能差5到10个百分点。这一章我把数据这块从目录组织、预处理参数到DataLoader完整讲一遍。3.1 猫狗数据集的标准目录格式与标签编码很多公开猫狗数据集比如Kaggle的Dogs vs Cats原始结构是一个文件夹里放着所有图片第一张叫cat.0.jpg第二张叫dog.4.jpg标签埋在文件名里。而多数课程设计项目为了让代码简单会直接把数据整理成上面那种train/cats、train/dogs的子目录形式。这两种格式我都处理过推荐后者因为PyTorch的ImageFolder和TensorFlow的flow_from_directory都天然支持它。如果手头数据集是单文件夹形式我一般会写一段脚本重新组织目录而不是去改数据加载逻辑这样后面用任何框架都方便。脚本逻辑是读取文件名前缀cat开头的复制到train/catsdog开头的复制到train/dogs。另外要按比例切分验证集不能只用一部分训练常用6/2/2划分或者固定把每种动物的后20%图片作为验证集。PyTorch里的ImageFolder拿到子目录后会自动按字典序给每个子目录编号比如cats在前dogs在后。这样标签就是0和1。但如果目录名是英文排序就是靠字母顺序如果你的数据是dogs写在cats前面那dogs就是0cats就是1。我建议你训练前先打印一次train_data.class_to_idx确认标签对应关系再往下走。很多“项目跑出来准确率50%”的怪异问题源头就是标签反了。再一个细节是图片格式。数据集里混着RGB三通道的jpg、灰度png甚至损坏图片都很常见。ImageFolder会把灰度图当成单通道读入CNN预训练模型第一个卷积层要三通道直接报错。常见做法是在预处理里自己写一个转RGB的lambda函数或者在生成数据集前统一转成RGB的jpg。前者省事后者更干净。我一般选前者原因是数据集一旦很大重新转格式很耗时间而lambda做通道膨胀只增加一点CPU开销。3.2 resize、归一化、数据增强的参数怎么设图像预处理有三个参数最影响效果resize尺寸、归一化均值方差、是否做数据增强。resize尺寸直接决定输入维度。224x224是ImageNet预训练模型的标准尺寸ResNet、VGG、MobileNet都用它。如果你的显卡显存小也可以降到128x128但准确率会掉2到3个百分点。如果显存充足用256x256再配合随机裁剪224效果会更好。这个做法叫缩放后裁剪能让模型看到不同位置的局部特征比直接resize成224更抗过拟合。归一化的均值标准差不要自己算直接用模型预训练时用的那套。ImageNet预训练模型默认是mean[0.485,0.456,0.406]std[0.229,0.224,0.225]。如果你不用预训练模型而是自己从头训练CNN那么归一化参数可以用全局像素均值0.5、标准差0.5或者干脆统计自己数据集的均值和方差。记住预训练模型和归一化参数必须配套不配套相当于送进同一个网络的输入分布完全不同迁移学习效果会明显缩水。这个坑极常见我有次调了一晚上准确率最后发现是归一化写错了。数据增强是猫狗识别里提升泛化能力的利器。常用变换有随机水平翻转、随机旋转正负15度、随机亮度对比度扰动、随机裁剪。翻转对猫狗识别有效因为猫狗姿态没有上下之分。旋转角度别太大超过30度时猫狗的形态会变形反而破坏语义。一个稳妥的组合是训练集用Resize(256)RandomCrop(224)RandomHorizontalFlip()验证集和测试集用Resize(224)CenterCrop(224)。这样训练时看到更多样验证时保持稳定可复现。增强别做得太狠。前阵子我试了AutoAugment的更强版本把某些猫图旋转到接近垂直训练集loss死活降到0.6就不动了。图像增强的本质是给模型提供“困难样本”但困难到人眼都认不出模型也学不到有效特征。数据增强的更优方案是根据数据集规模做选择几千张图可以加增强几百张图加强增强也不一定能救回来这时候最有效的手段是换预训练模型权重而不是死调增强参数。3.3 用PyTorch的ImageFolder与DataLoader写数据加载器有了目录结构和预处理参数把它们接起来的就是DataLoader。我习惯把数据加载逻辑单独放在utils/dataset.py里训练入口只调用函数便于在不同模型和数据集之间复用。from torchvision import datasets, transforms from torch.utils.data import DataLoader def get_transforms(augmentTrue): if augment: return transforms.Compose([ transforms.Resize(256), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(0.2, 0.2, 0.2, 0.1), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) return transforms.Compose([ transforms.Resize(224), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) def get_dataloaders(batch_size64, num_workers4): train_loader DataLoader( datasets.ImageFolder(data/train, transformget_transforms(True)), batch_sizebatch_size, shuffleTrue, num_workersnum_workers ) val_loader DataLoader( datasets.ImageFolder(data/val, transformget_transforms(False)), batch_sizebatch_size, shuffleFalse, num_workersnum_workers ) return train_loader, val_loader这段代码把训练集和验证集的预处理分开又通过augment参数让接口统一。关键在于验证集不能用RandomCrop和RandomHorizontalFlip因为验证集的输出必须稳定你需要在同样的输入条件下比较不同epoch的模型表现。shuffle在验证集上也要设成False否则一个epoch内样本顺序变化每步准确率波动大日志会很难看。batch_size的选择要看显存。我用过8、16、32、64经验是batch_size在合理范围内偏大时梯度更平滑loss曲线更稳。但超过64以后训练收敛速度提升有限显存占用却明显上涨。如果显存只有4GB224x224的RGB图批量设为16到32比较安全。num_workers在Windows上不要设太高超过2反而会因为创建进程的开销拖慢速度Linux上设4或8都行。DataLoader拿到的是(n, 3, 224, 224)的张量形状顺序是固定的。转成numpy时很多人直接取image.numpy()会得到3×224×224的CHW结构而OpenCV和matplotlib需要的是HWC结构。这个差异比较反直觉遇到可视化结果颜色不对时先检查维度顺序。验证数据处理正确性的方法我建议是保存几个增强后的样本看一眼确认猫图水平翻转后还是猫颜色没有异常再进入训练。4. 模型搭建与训练从零写CNN还是迁移学习模型是项目的核心但也是很多人的雷区。看到“基于CNN”就以为要从零搭一个卷积网络其实对猫狗识别这种任务迁移学习往往比自写网络更稳、分数更高。这一章我把两种做法和大体的超参数选择都讲清楚。4.1 自定义小型CNN的结构与参数量估算自己写CNN能让你应付答辩时的“模型结构”这一问但前提是结构设计合理。经典的LeNet-5太小直接用在猫狗识别上容易欠拟合。我一般会用一个比LeNet稍宽的浅层网络两层卷积加两层全连接卷积核用3x3池化用2x2最大池化。import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), # 输入224x224x3输出224x224x32 nn.ReLU(inplaceTrue), nn.MaxPool2d(2, 2), # 输出112x112x32 nn.Conv2d(32, 64, kernel_size3, padding1), # 输出112x112x64 nn.ReLU(inplaceTrue), nn.MaxPool2d(2, 2), # 输出56x56x64 nn.Conv2d(64, 128, kernel_size3, padding1),# 输出56x56x128 nn.ReLU(inplaceTrue), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d((1, 1)), # 不管输入多大输出1x1x128 nn.Flatten(), nn.Linear(128, 64), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(64, num_classes) ) def forward(self, x): x self.features(x) x self.classifier(x) return x这个结构的参数量大约在20万到30万之间单张224x224的图像占用的显存不到200MBCPU也能训练。关键设计是AdaptiveAvgPool2d它让我不用手动计算卷积后特征图的尺寸无论输入是128还是224都能接全连接层。Dropout放在最后能显著缓解小数据集上的过拟合。从零训练这个网络在猫狗数据集上做到80%的准确率是可能的但路比较难走。原因是模型容量小特征表达能力有限而猫狗在纹理上非常相似尤其是黑猫和黑狗的照片。模型容量提升到接近34万的参数量后继续加深网络边际收益就很小了。如果你不追求“必须从零写”可以把这个模型作为答辩的baseline再用迁移学习做最终结果形成对比这就是文档PDF里最好写的实验段落。4.2 迁移学习ResNet18的微调套路迁移学习是猫狗识别性价比最高的方案。我常用的基座是ResNet18或ResNet34在ImageNet上训练过已经学到了丰富的边缘、纹理和物体部件特征。对于猫狗二分类只要有几千张图微调完准确率可以轻松上95%。迁移学习有两个层级微调全部参数 vs 只微调最后一层。只微调最后一层的做法是把除了全连接层之外的所有参数都冻结反向传播也不更新它们训练速度快但准确率上限低。微调全部参数则是把整个网络都放开训练效果更好代价是更慢、更容易过拟合。我的做法是折中先用冻结backbone的方式训练几个epoch学习率设1e-3只调全连接层等损失曲线变平后再解冻backbone所有层把学习率降到1e-4继续微调。这套流程就像打枪先校准瞄准镜再打靶效果好且不容易把预训练特征弄坏。注意冻结的时候PyTorch里要把参数requires_grad设成False并且优化器只能接收requires_gradTrue的参数。代码写法如下这个坑不踩一次很难记住。from torchvision.models import resnet18 def create_finetune_model(freeze_backboneTrue): model resnet18(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, 2) if freeze_backbone: for param in model.parameters(): param.requires_grad False # 唯独让fc层可训练 for param in model.fc.parameters(): param.requires_grad True return model # 优化器只拿到可训练参数 model create_finetune_model(freeze_backboneTrue) optimizer torch.optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lr1e-3 )这里有两个容易错的地方一个是直接用model.parameters()喂给优化器冻结的层也会被更新相当于没冻结另一个是解冻之后忘了把学习率调小导致用1e-3的大步长去更新预训练权重几个step后模型就可能退化。我自己的血泪经验是解冻后的学习率必须比微调fc阶段小一个数量级最少也要10倍。否则预训练学到的手感会被冲掉前期怎么都不降后面又开始震荡。ResNet18在224x224输入下一个batch64大约占3GB显存。如果你的卡只有4GB把batch调成16或者换MobileNetV3后者参数量更小、速度更快但准确率比ResNet18低不了多少部署到CPU上时更实用。4.3 训练主循环、日志与损失曲线训练主循环是所有项目中复用率最高的代码。我习惯把训练和验证的逻辑写成函数每个epoch打印一次日志同时把loss和acc存到列表里最后画曲线。def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss, correct, total 0.0, 0, 0 for images, labels in loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() 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 evaluate(model, loader, criterion, device): model.eval() total_loss, correct, total 0.0, 0, 0 with torch.no_grad(): for images, labels in loader: 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训练主循环里model.train()和evaluate里的model.eval()不能颠倒。eval模式下BatchNorm和Dropout行为会变化BatchNorm会使用全局统计量而不是当前batch的统计量Dropout直接失效这是保证验证结果稳定的基础。loss记录时用loss.item()它返回标量不会保持整个计算图否则显存会持续增长直到OOM。每步累积loss时乘上images.size(0)是为了最终算的是整个数据集的平均损失而不是所有batch损失的平均值。如果你发现最终打印的准确率和loss对不上多半是这里的加权没做。损失曲线怎么看train_loss下降、val_loss也下降正常train_loss下降、val_loss在某个epoch后反弹就是过拟合train_loss和val_loss都不降先怀疑学习率过大或数据标签错乱val_loss一直降但准确率不动查看类别是不是极度不均衡。猫狗项目数据相对均衡最常遇到的是前两种。每训练完一个epoch我会把当前权重保存成checkpoint文件名带上epoch和准确率比如resnet18_epoch12_acc0.95.pth这招能帮你回滚到之前表现最好的版本是模型训练的后悔药。5. 避坑记录训练猫狗分类器最常见的五个坑这类项目代码能跑是一回事跑得好又是另一回事。下面这几个坑是我在猫狗分类项目上遇到过至少两次的按现象、原因、解决的思路写方便你对照排查。5.1 训练准确率一直停留在50%左右现象训练集上loss不降准确率始终在50%附近徘徊像在瞎猜。原因标签和类别对应关系反了。ImageFolder按目录名字典序编码cats是0dogs是1但如果你在打乱数据或手动构造标签时把顺序搞反模型就是在用猫的图去学狗的标签最后只能学到“一半一半”的概率分布。另一个常见原因是数据清洗没做有些图片其实不是猫狗混入大量无关图片后模型学不到稳定特征。解决训练前的第一个epoch里打印几轮loader返回的图片文件名、标签对照并保存增强后的样本图肉眼确认。用代码train_data.class_to_idx输出映射确保与你预期一致。如果发现猫狗的目录名不是按cat/dog命名而是其他名字先检查class_to_idx再手动定义标签字典不要指望默认排序。5.2 训练中途显存溢出CUDA out of memory现象某个epoch跑到第几百步时突然报OOM程序崩溃。原因显存占用是一个动态累计的过程最常见的是前向传播的计算图没有及时释放比如你在循环里把loss、outputs这些张量又拿出来做可视化或记录保存了引用导致一整批计算图无法释放。此外batch_size太大本质上是显存不够。解决一是把记录数据全部转成Python标量用loss.item()而不是loss用preds.cpu().numpy()而不是直接持有cuda张量二是把batch_size调小到16或8或者把输入图先缩到128x128跑通流程再回到224三是检查代码里是不是在batch循环内重复创建了模型优化器有些人的训练脚本不小心把model定义放在循环内每次迭代都重新初始化显存直接爆掉。5.3 训练loss下降很快验证准确率却很差现象训练集loss降到0.05验证集准确率却只有70%甚至还在往下掉。原因这是典型的过拟合模型记住了训练集上的噪声和细节没有泛化到新图片。猫狗数据集图片数量通常在万级别以下而ResNet18参数量超过千万不加约束必定过拟合。解决加数据增强比如RandomHorizontalFlip和ColorJitter已经在预处理里做了就再加RandomRotation和RandomAffine把Dropout增加到0.7以上迁移学习时我前面提到的分阶段训练本身就是一种正则化另一个有效手段是早停法验证集准确率连续5个epoch不升就停止训练并退回最佳权重而不是硬跑完所有epoch。记住文档PDF里的最终准确率应该取验证集最好的那个epoch而不是最后那个epoch。5.4 预训练模型加载失败或下载极慢现象第一次跑resnet18(pretrainedTrue) 时进度条半天不动或是直接抛SSLError代码卡死。原因预训练权重要从外网下载网络不畅时会有超时而国内经常遇到连接不稳定。解决手动下载权重文件放到本机缓存目录后离线加载。常见做法是把torchvision的权重下载URL改成镜像地址或者提前下载好.pth文件后修改模型加载方式model resnet18(weightsNone)再model.load_state_dict(torch.load(resnet18-f37072fd.pth, map_locationcpu))。我在实际项目里也遇到过一个反面案例因为下载失败有人干脆设pretrainedFalse结果从零训了一个ResNet18准确率比自定义CNN还低答辩时被问“为什么你的预训练模型效果这么差”。还有个折中办法先用一个小的MobileNetV3它的预训练权重文件更小下载更快先跑通整个流程再换ResNet。5.5 验证时忘了model.eval()结果忽高忽低现象同一个模型两次评估的准确率一次95%一次89%差异大。原因没有调用model.eval()模型仍然处于训练模式。此时BatchNorm层会使用当前batch的均值和方差而不是训练好的全局统计量Dropout仍然会随机丢弃神经元。验证集数量少时两个因素叠加会让结果随机性很大。解决在验证、测试、导出模型的所有阶段执行前先调用model.eval()。如果只是做一次前向推理用model.eval()就够如果要做梯度传播还要包在torch.no_grad()里面。这个坑特别隐蔽因为代码不报错模型输出也像模像样只有在实验记录时才会发现结果不稳定。我一般会在evaluate函数的第一行强制写model.eval()不依赖调用方记得这件事。6. 验证与交付混淆矩阵、ONNX导出和一份能答辩的文档PDF模型训练完项目只完成了一半。剩下的一半是把效果讲清楚、把模型导出给另一台机器用、把过程写成能交差的内容。这一章讲一个我最常用的验证技巧和交付套路。6.1 先看混淆矩阵再谈准确率准确率85%听起来好看但它掩盖了“猫全对狗错一半”的问题。我会在测试集上跑一次预测把真实标签和预测标签存下来用sklearn画出混淆矩阵。代码很短但足够让答辩提前瞄到逻辑漏洞。from sklearn.metrics import confusion_matrix, classification_report # y_true, y_pred 是测试集上的真实标签和预测标签 cm confusion_matrix(y_true, y_pred) print(cm) # 比如 [[900, 100], [150, 850]] print(classification_report(y_true, y_pred, target_names[cat, dog]))如果第一行第一列900第二行第一列150说明模型把不少狗认成猫。这时优先补数据或者增强狗的样本而不是整体调学习率。分类报告里的精确率、召回率、F1才是文档PDF里体现深度的指标。6.2 导出ONNX摆脱Python环境也能跑把PyTorch模型部署到别的机器是常见需求。我习惯把训练好的权重导出成ONNX格式这样对方用OpenCV或ONNX Runtime就能直接推理不用装PyTorch。model.eval() dummy_input torch.randn(1, 3, 224, 224).cuda() torch.onnx.export(model, dummy_input, model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, opset_version11)opset_version别设太高很多边缘设备只支持到12或13。导出前一定先model.eval()不然ONNX里会把Dropout结构也固化进去推理时行为会不一样。6.3 文档PDF写作里最值钱的三段我写这类项目的产出文档时一定会重点写三处网络结构复现、训练策略变更记录、失败实验记录。用表格对比一个从零训练CNN和ResNet18迁移学习的最终准确率、参数量、训练时间数据摆在纸面上比写十段“深度学习能自动提取特征”有力得多。失败实验也别藏记录一次过拟合现象和你怎么调正则化救回来的这才是高分项目“高分”的理由。最后说个习惯我每次做完一个模型都会把最终权重、训练日志、混淆矩阵图片放在同一个文件夹里以日期命名。这样一个月后要更新或复现回看日志就能知道当初为什么这么设。有一次我就是凭这个文件夹里的旧日志救回了一个被客户改乱参数导致报废的模型。希望帮到你做项目不止跑通代码更要把每一步变化留住。本文还有配套的精品资源点击获取
返回列表