尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Python机器学习的猫狗识别分类项目源码实战:从环境搭建到模型部署

基于Python机器学习的猫狗识别分类项目源码实战:从环境搭建到模型部署 简介这份资源是面向计算机、人工智能及相关专业学生与开发者的猫狗识别分类项目源码包可作为毕业设计、课程设计、作业或项目初期立项演示的参考方案也适合具备一定基础的学习者进阶练手。压缩包共16个文件约1.67MB以7个Python脚本为核心涵盖数据读取、CNN与ResNet、Swin Transformer等模型训练与测试代码另含2个Markdown说明文档、1份docx论文、1个pth模型权重及日志文件结构清晰便于按模块查阅。目前已有68人学习下载。项目代码完整、资料齐全包含设计文档与训练好的模型读者可借此理解从数据预处理、模型搭建到训练评估的完整流程并在此基础上修改扩展实现其他分类功能遇到配置或运行问题还可获得远程教学支持适合借鉴学习与二次开发。1. 从一份猫狗识别源码说起Python 机器学习分类项目到底交付了什么很多人第一次接触图像分类都是从猫狗识别开始的。它像计算机视觉里的「Hello World」但真正动手做一遍就会发现从读图、预处理、搭网络、训练、评估到推理每一步都有细节。这份「基于 Python 机器学习的猫狗识别分类项目源码」本质上是一个完整的二分类工程输入一张图片输出它是猫还是狗的概率。它适合刚学完机器学习基础、想找一个能跑通的项目练手的人也适合需要交课程设计或论文材料的学生。核心依赖是 PyTorch配合 torchvision 做数据加载和增强。你拿到的不只是几行模型定义而是一套可复现的流程数据集怎么组织、模型怎么选、训练参数怎么设、结果怎么评估。下面我会按实际落地顺序把每个环节拆开讲清楚包括我踩过的坑和调参经验。2. 环境搭建与数据准备把 PyTorch 和猫狗图片放到该放的位置2.1 安装 PyTorch 与验证 GPU 是否可用环境是第一个拦路虎。很多人卡在 python 和 pytorch 版本对应上装完 import torch 就报错。我一般用 conda 建独立环境避免污染系统 Python。下面是一套在 Windows 和 Linux 都通用的命令CUDA 版本按你显卡驱动选没有 GPU 就用 CPU 版。# 创建虚拟环境python 版本建议 3.9 或 3.10 conda create -n catdog python3.10 -y conda activate catdog # 安装 PyTorch以 CUDA 11.8 为例CPU 版把 cu118 换成 cpu pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 验证安装 python -c import torch; print(torch.__version__); print(torch.cuda.is_available())逻辑说明第一行建环境第二行激活第三行装 PyTorch。参数上--index-url指定官方 wheel 源比默认源快很多。最后一行如果输出 True说明 GPU 可用输出 False 但版本号正常说明装的是 CPU 版训练会慢但能跑。注意不要混用 pip 和 conda 装 torch容易出现 DLL 加载失败。如果 import 报OSError: [WinError 126]多半是缺少 Visual C 运行库装一下 VC_redist 即可。2.2 数据集目录结构与 DataLoader 配置猫狗数据集常见有两种Kaggle 的猫狗大战训练集 25000 张和自己收集的小样本。不管哪种目录都要按类别分文件夹这是 torchvision 的ImageFolder能直接读的前提。结构如下data/ train/ cat/ cat.0.jpg ... dog/ dog.0.jpg ... val/ cat/ ... dog/ ...如果拿到的是扁平文件文件名带 cat 或 dog可以用脚本切分。下面这段代码负责切分并生成训练/验证集同时定义 DataLoader。import os, shutil, random from torchvision import datasets, transforms from torch.utils.data import DataLoader # 假设原始图片都在 raw/ 下文件名含 cat 或 dog raw_dir raw base data for split in [train, val]: for cls in [cat, dog]: os.makedirs(os.path.join(base, split, cls), exist_okTrue) files [f for f in os.listdir(raw_dir) if f.lower().endswith((.jpg, .png))] random.shuffle(files) split_idx int(len(files) * 0.8) for i, fname in enumerate(files): cls cat if cat in fname.lower() else dog split train if i split_idx else val shutil.copy(os.path.join(raw_dir, fname), os.path.join(base, split, cls, fname)) # 定义变换训练集做增强验证集只做缩放和归一化 train_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) train_ds datasets.ImageFolder(os.path.join(base, train), transformtrain_tf) val_ds datasets.ImageFolder(os.path.join(base, val), transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4)逻辑说明切分脚本按 8:2 分训练和验证保证同一张图不会同时出现在两边。变换里Resize((224,224))是给 ResNet 这类网络用的如果你换自定义小网络可以改成 128。Normalize的均值方差是 ImageNet 的统计值用预训练模型时必须保持一致。num_workers在 Windows 上建议设 0 或 2设大了容易卡死。注意验证集不要做随机翻转和旋转否则评估结果不稳定。3. 模型选型与训练从 ResNet18 微调到自定义 CNN 的取舍3.1 用预训练 ResNet18 做迁移学习猫狗识别数据量不大时从零训练 CNN 很容易过拟合准确率卡在 70% 上不去。常见做法是用 ImageNet 预训练的 ResNet18把最后一层全连接改成二分类。这样收敛快通常 5 个 epoch 就能到 95% 以上。下面是模型定义和训练循环。import torch import torch.nn as nn import torch.optim as optim from torchvision import models device torch.device(cuda if torch.cuda.is_available() else cpu) # 加载预训练 ResNet18替换最后一层 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 2) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-4) def train_one_epoch(model, loader, optimizer, criterion): model.train() total_loss, correct, total 0, 0, 0 for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * imgs.size(0) correct (outputs.argmax(1) labels).sum().item() total imgs.size(0) return total_loss / total, correct / total for epoch in range(10): loss, acc train_one_epoch(model, train_loader, optimizer, criterion) print(fEpoch {epoch1}, loss{loss:.4f}, acc{acc:.4f})逻辑说明weights...IMAGENET1K_V1表示加载预训练权重第一次运行会自动下载。model.fc是 ResNet 的全连接层改成输出 2 类。优化器用 Adam学习率 1e-4比 SGD 更容易调。训练循环里loss.item() * imgs.size(0)是按样本数加权平均避免最后一个 batch 不满导致偏差。注意如果显存不够把 batch_size 降到 16 或 8如果 loss 不下降检查标签是不是 0/1 对应 cat/dogImageFolder 默认按文件夹名排序cat 是 0dog 是 1。3.2 自定义 CNN 的结构与参数设置如果你不想用预训练模型或者论文里需要展示自己的网络结构可以搭一个 4 层卷积的 CNN。它参数量小训练快但准确率通常比 ResNet 低 3 到 5 个百分点。结构如下class SimpleCNN(nn.Module): def __init__(self, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(64, 128, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(128, 256, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(256, 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))逻辑说明每个卷积块是 Conv-ReLU-MaxPool通道数从 32 翻到 256。AdaptiveAvgPool2d(1)把任意尺寸特征图压成 1x1避免全连接层输入尺寸写死。Dropout(0.5)是防过拟合的关键如果训练集小于 2000 张可以加到 0.6。参数上卷积核统一 3x3padding1 保持尺寸。注意自定义 CNN 的学习率建议设 1e-3比微调预训练模型大一个量级否则收敛慢。4. 评估、推理与论文素材把准确率、混淆矩阵和单张预测跑出来4.1 验证集评估与混淆矩阵训练完不能只看训练准确率必须用验证集评估。下面代码输出准确率、混淆矩阵和分类报告这些也是论文里常放的图表。from sklearn.metrics import confusion_matrix, classification_report import numpy as np model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.to(device) outputs model(imgs) preds outputs.argmax(1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) print(Accuracy:, np.mean(np.array(all_preds) np.array(all_labels))) print(confusion_matrix(all_labels, all_preds)) print(classification_report(all_labels, all_preds, target_names[cat, dog]))逻辑说明model.eval()关闭 Dropout 和 BatchNorm 的训练行为。torch.no_grad()节省显存。混淆矩阵的四个格子分别对应猫判猫、猫判狗、狗判猫、狗判狗。如果猫判狗的数量明显多说明模型对猫的特征学得不够可以增加猫的样本或做数据增强。注意验证集准确率比训练集低 5% 以内算正常低太多就是过拟合。4.2 单张图片推理与结果可视化项目交付时通常需要一个推理脚本输入图片路径输出类别和置信度。下面这段可以直接用。from PIL import Image def predict(img_path, model, transform, device): model.eval() img Image.open(img_path).convert(RGB) tensor transform(img).unsqueeze(0).to(device) with torch.no_grad(): output model(tensor) prob torch.softmax(output, dim1) conf, pred torch.max(prob, 1) classes [cat, dog] return classes[pred.item()], conf.item() # 用法 label, confidence predict(test.jpg, model, val_tf, device) print(f预测{label}置信度{confidence:.4f})逻辑说明convert(RGB)防止灰度图或 PNG 带透明通道导致通道数不对。unsqueeze(0)增加 batch 维度。softmax把输出转成概率max取最大概率和对应类别。注意推理时的 transform 必须和验证集一致否则归一化参数不同会导致预测偏差。5. 避坑与排查猫狗识别项目里最容易翻车的 5 个地方5.1 现象训练 loss 一直不降准确率停在 50%原因标签和输出对不上或者学习率太大导致震荡。常见于自己重写 Dataset 时把 cat 标成 1、dog 标成 0而 ImageFolder 默认 cat 是 0。解决打印一个 batch 的 labels 确认用train_ds.class_to_idx查看映射。学习率从 1e-4 开始试不要一上来就 0.01。5.2 现象验证集准确率很高但预测新图片全错原因新图片的预处理和训练时不一致比如训练用了 Normalize推理时忘了加。解决把验证集的 transform 单独保存推理时复用同一个对象。另外检查图片是不是被旋转了PIL 读出来的 EXIF 方向有时和实际不符加ImageOps.exif_transpose修正。5.3 现象Windows 上 DataLoader 报 BrokenPipeError 或卡死原因num_workers大于 0 时Windows 的多进程启动方式与 Linux 不同。解决把num_workers设为 0或者把训练代码放在if __name__ __main__:下面。如果必须用多进程设num_workers2并加persistent_workersTrue。5.4 现象显存溢出 CUDA out of memory原因batch_size 太大或者没有用torch.no_grad()做验证。解决把 batch_size 降到 16 或 8验证和推理时加with torch.no_grad():。如果还不行用torch.cuda.empty_cache()清理缓存。另外ResNet18 比自定义 CNN 占显存多小显卡优先用自定义网络。5.5 现象模型在猫狗上表现好换其他动物就崩原因二分类模型只学了猫和狗的特征没有泛化到其他类别。解决这是二分类的固有边界不是 bug。如果需要多分类把最后一层输出改成类别数用交叉熵训练。如果要做开放集识别需要额外加阈值判断或使用距离度量方法。6. 进阶技巧用学习率调度和早停把准确率再提 2 个点训练到后面固定学习率往往在最优解附近震荡。我一般会加两个东西ReduceLROnPlateau和早停。前者在验证 loss 不降时自动降学习率后者在连续几个 epoch 没提升时停止训练省时间也防过拟合。下面是我常用的组合。scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience2) best_acc 0 patience_counter 0 early_stop_patience 5 for epoch in range(30): train_loss, train_acc train_one_epoch(model, train_loader, optimizer, criterion) # 验证 model.eval() val_loss, val_correct, val_total 0, 0, 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) outputs model(imgs) loss criterion(outputs, labels) val_loss loss.item() * imgs.size(0) val_correct (outputs.argmax(1) labels).sum().item() val_total imgs.size(0) val_loss / val_total val_acc val_correct / val_total scheduler.step(val_loss) print(fEpoch {epoch1}, val_loss{val_loss:.4f}, val_acc{val_acc:.4f}, lr{optimizer.param_groups[0][lr]:.6f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth) patience_counter 0 else: patience_counter 1 if patience_counter early_stop_patience: print(Early stop) break逻辑说明ReduceLROnPlateau的factor0.5表示每次降一半patience2表示连续 2 个 epoch 没改善就降。早停的patience设 5比调度器的 patience 大避免刚降学习率就停。保存best_model.pth只存权重加载时用model.load_state_dict(torch.load(best_model.pth))。参数上如果数据集小early_stop_patience可以设 3数据集大可以设 8。注意调度器要放在验证之后调用传的是 val_loss不是 train_loss。另外一个小技巧如果验证准确率波动大可以把验证集的 batch_size 设大一点比如 64减少批次间的随机性。还有训练前用torch.manual_seed(42)固定随机种子让结果可复现这对写论文很重要。我自己的习惯是每次跑实验前先固定种子然后记录学习率、batch_size、优化器这三个参数后面调参就有对照。这套流程跑下来猫狗二分类在 25000 张图上通常能到 97% 以上小样本 2000 张也能到 92% 左右。希望帮到你。本文还有配套的精品资源点击获取
返回列表