尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于PaddlePaddle的猫狗识别源码解析:CNN训练与预测全流程

发布时间:2026/9/26 13:30:35

资讯中心
01
ARTICLE

基于PaddlePaddle的猫狗识别源码解析:CNN训练与预测全流程

基于PaddlePaddle的猫狗识别源码解析:CNN训练与预测全流程
简介这是一份基于机器学习的猫狗识别算法源码包面向计算机、人工智能等相关专业做课程设计、期末大作业或毕业设计的学生也适合有Python与机器学习基础的学习者作为实践参考。代码经严格调试下载即可运行能帮助读者快速搭建图像二分类识别流程。压缩包内共12个文件整体约359KB包含两个Python脚本、若干jpg/png样例图片与结果图、一个gitignore及一份Markdown说明文档Python脚本覆盖数据准备、模型训练与预测等环节图片展示损失曲线、模型流程和预测效果文档便于快速上手。目前已有329人学习下载。读者可从中获得完整可运行的猫狗识别实现也能参考损失变化、模型构建过程和预测可视化结果适合理解图像分类项目组织方式并为自己的实验或项目提供可改写的代码框架。1. 基于机器学习的猫狗识别这份源码不只给了训练脚本猫狗分类看起来是机器学习里最「入门」的任务但真上手做课程设计或期末大作业时大多数人卡在的不是模型而是数据怎么喂、训练怎么调、预测结果怎么可视化。这份基于机器学习的猫狗识别算法源码是一套基于 PaddlePaddle 的完整二分类工程——包含cat_dog.py训练主脚本、9029907.py配套入口、训练用样本图和四张过程输出图loss 曲线、训练过程快照、预测结果、框架环境。它不是单文件算法 demo而是把数据读取、模型训练、结果预测整条链路都串起来的项目源码。适合正在做计科、人工智能、大数据方向课程设计或毕设的同学拿来当骨架也适合想完整走一遍「图像分类从训练到预测」全流程的学习者参考。2. 为什么猫狗识别用 CNN 而不是 SVM卷积与池化在这份源码里做了什么2.1 传统机器学习处理图像的两道坎特征工程和像素空间关系很多人会问猫狗识别这种二分类SVM、决策树不是也能做吗确实能做但前提是你得先告诉算法「哪些特征有用」。传统机器学习流程里特征工程是重头戏——你要手工提取颜色直方图、边缘方向、纹理特征再把它们拼成向量丢进分类器。猫和狗的差异恰恰不在某个单独像素上而在耳朵形状、脸型比例、毛发纹理这些空间结构里手工特征很难把这些结构稳定抽出来。另一个更隐蔽的问题是像素顺序。如果把一张 128×128 的图拉平成 16384 维向量喂给 SVM算法看到的只是一串数字左上角的猫耳朵和右下角的狗尾巴在向量里距离很远模型学不到「耳朵和眼睛相对位置」这类空间关系。这就是传统算法在图像任务上的天花板。所以这套源码选 CNN不是赶时髦是这类任务的结构性需求。2.2 CNN 的三个关键操作卷积提取局部特征池化压缩全连接分类CNN 解决空间问题的核心是卷积核。一个 3×3 的卷积核在一张图上滑动每次只看一个小邻域这就把「局部空间关系」直接编码进了计算过程。网络浅层学到的往往是边缘、颜色块深层的卷积层会把浅层特征组合成「像耳朵的弧线」「像眼睛的圆斑」这类语义特征。源码里cat_dog.py的模型结构常见做法就是几组「卷积 池化」叠加再接全连接层。卷积用paddle.nn.Conv2D池化用paddle.nn.MaxPool2D大致长这样import paddle import paddle.nn as nn class CatDogCNN(nn.Layer): def __init__(self, num_classes2): super(CatDogCNN, self).__init__() # 第一组卷积3 通道输入16 个卷积核3x3 卷积 self.conv1 nn.Conv2D(in_channels3, out_channels16, kernel_size3, stride1, padding1) self.pool1 nn.MaxPool2D(kernel_size2, stride2) # 第二组卷积16 通道输入32 个卷积核 self.conv2 nn.Conv2D(in_channels16, out_channels32, kernel_size3, stride1, padding1) self.pool2 nn.MaxPool2D(kernel_size2, stride2) # 全连接层输入维度由最后一次池化后的特征图大小决定 self.fc nn.Linear(in_features32 * 32 * 32, out_featuresnum_classes) def forward(self, x): x paddle.nn.functional.relu(self.conv1(x)) x self.pool1(x) x paddle.nn.functional.relu(self.conv2(x)) x self.pool2(x) x paddle.flatten(x, start_axis1) x self.fc(x) return x代码逻辑很直白输入一张 128×128×3 的彩色图经过两组卷积池化后特征图变成 32×32×32再展平成一维向量喂给全连接层最后输出 2 个 logits。两个关键的参数要留意——padding1保证卷积后宽高不变池化步长 2 把特征图尺寸减半。如果输入图不是 128×128最后那个32 * 32 * 32的输入维度必须跟着算这是新手最容易改错的地方。2.3 二分类的输出层设计sigmoid 单输出还是 softmax 双输出猫狗识别是二分类输出层有两种等价设计一种是输出 1 个节点接 sigmoid阈值 0.5 判猫/狗另一种是输出 2 个节点接 softmax取概率大的类别。两份脚本里可能混用两种写法理解它们的数学关系能省很多调试时间。# 方式一单输出 sigmoid配合 BCEWithLogitsLoss self.fc nn.Linear(in_featureshidden, out_features1) # 方式二双输出 softmax配合 CrossEntropyLoss self.fc nn.Linear(in_featureshidden, out_features2)sigmoid(x)和softmax([x, 0])在数学上是等价的区别只在 loss 函数的梯度形态。用 BCEWithLogitsLoss 时网络输出不用提前过 sigmoidloss 内部做了数值稳定处理。用 CrossEntropyLoss 时输出的是两个类的 logitsloss 内部做 softmax。我见过不少翻车现场——输出 2 个节点却用了 sigmoid结果两个概率各自都在 0~1 之间且不一定和为 1判断类别时只能靠 argmax训练曲线还特别怪。这个坑后面避坑章会细说。3. 猫狗识别源码文件拆解两个 py 脚本和四张结果图分别管什么3.1 从文件清单先读一遍项目py 脚本、图片数据、结果图的分工拿到压缩包先别急着跑花五分钟把文件结构理清楚能省后面一大半调试时间。这套源码的关键文件不多核心角色就这几个文件角色说明cat_dog.py训练主脚本数据读取、模型定义、训练循环、loss 记录都在这9029907.py配套入口这类课程设计包里常见做法是拆一个脚本做数据预处理或推理封装images/pic1.jpg等测试样本训练或预测用的猫狗图片共 4 张README.md运行说明环境版本、目录要求、启动步骤以它为准costlost.png训练曲线loss 随迭代下降的曲线判断模型有没有收敛model_prosess.png训练过程快照训练中某个阶段的可视化输出predict.png预测结果跑完预测后的输出截图paddle.png框架环境PaddlePaddle 版本或运行环境信息cat_dog.py是绝对的主角。9029907.py这类命名其实是打包时自动生成的编号不用被名字吓到打开看代码结构就知道是干嘛的。四张 png 不是装饰它们记录了一次完整训练的中间产物——这也是这份源码比纯算法代码有价值的地方你能看到「什么样的 loss 曲线算正常」。3.2 数据怎么进模型Dataset 的定义方式图像分类项目里数据读取代码往往比模型代码更容易踩坑。源码里处理猫狗图片的方式常见做法是写一个继承paddle.io.Dataset的类把每张图片的路径和标签绑定起来import os from PIL import Image import paddle import paddle.vision.transforms as T class CatDogDataset(paddle.io.Dataset): def __init__(self, data_dir, is_trainTrue): self.data_dir data_dir self.is_train is_train self.transform T.Compose([ T.Resize((128, 128)), T.ToTensor(), # 转成 tensor 并把像素归一化到 [0,1] T.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ]) # 按目录结构收集样本data_dir/cat/*.jpg 和 data_dir/dog/*.jpg self.images [] self.labels [] for label, cls_name in enumerate([cat, dog]): cls_dir os.path.join(data_dir, cls_name) for fname in os.listdir(cls_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): self.images.append(os.path.join(cls_dir, fname)) self.labels.append(label) def __getitem__(self, idx): img Image.open(self.images[idx]).convert(RGB) img self.transform(img) return img, self.labels[idx] def __len__(self): return len(self.images)这段代码有几个细节值得抠。enumerate([cat, dog])决定了 cat 的标签是 0、dog 是 1后面预测输出的数字怎么解释就看这里顺序反了会导致 loss 下降但预测全错。T.Normalize(mean0.5, std0.5)是把像素从 [0,1] 重新中心化到 [-1,1]这是为了让梯度更新更平稳。如果去掉 Normalize模型也能训但收敛速度和稳定性会差不少。3.3 训练循环的骨架loss 计算、反向传播、参数更新的标准写法训练主循环在 PaddlePaddle 里写法高度统一源码里cat_dog.py的核心循环基本是这个结构import paddle import paddle.nn.functional as F model CatDogCNN(num_classes2) optimizer paddle.optimizer.Adam(parametersmodel.parameters(), learning_rate0.001) loss_fn paddle.nn.CrossEntropyLoss() for epoch in range(30): model.train() total_loss 0.0 for batch_id, (images, labels) in enumerate(train_loader): # 前向计算 logits model(images) # 计算交叉熵损失 loss loss_fn(logits, labels) # 反向传播 loss.backward() # 更新参数 optimizer.step() # 清空梯度 optimizer.clear_grad() total_loss loss.item() avg_loss total_loss / len(train_loader) print(fEpoch {epoch1}/{30}, Loss: {avg_loss:.4f}) # 每个 epoch 结束后保存一次模型后面预测要用 paddle.save(model.state_dict(), fcatdog_epoch{epoch1}.pdparams)这段循环逻辑是固定的前向算 logits → loss →backward()算梯度 →step()更新权重 →clear_grad()清梯度。clear_grad()这个调用容易漏漏了的话梯度会跨 batch 累加loss 曲线会出现奇怪的周期震荡。每轮 epoch 存一次模型参数是必须的因为预测脚本靠加载这些.pdparams文件来做推理。3.4 四张 png 的读法怎么看懂训练结果图costlost.png是最该仔细看的一张图。正常训练曲线是从 0.6~0.7 附近逐步下降到 0.1 以下过程有波动但整体向下。如果看到 loss 一开始就掉到接近 0多半是标签错位或数据泄漏如果 loss 在前几个 epoch 纹丝不动八成是学习率太小或数据没归一化。model_prosess.png是训练中间态的可视化通常是某个 batch 的输入图和预测结果拼在一起能直观看到模型在中期的表现。predict.png是预测环节的输出一般显示图片路径、预测类别和置信度。paddle.png是运行环境截图常见做法是放paddle.__version__的输出和 CPU/GPU 信息。后两张图主要给课程设计答辩撑场子但对你自己排查问题也有用——如果 predict.png 里置信度全是 0.5 附近说明模型根本没学出区分能力回头查数据而不是查网络结构。4. 把猫狗识别跑通从安装环境到输出预测置信度4.1 环境准备CPU 版 PaddlePaddle 就够用别一上来就配 GPU这套源码对硬件要求不高猫狗分类用 CPU 训练 128×128 的图完全能跑只是慢一点。先装 PaddlePaddle 的 CPU 版本跑通流程再说# 创建虚拟环境防止污染系统 Python conda create -n catdog python3.9 -y conda activate catdog # 安装 PaddlePaddle CPU 版 python -m pip install paddlepaddle # 验证安装 python -c import paddle; print(paddle.__version__)装完能输出版本号就说明环境通了。建议不要急着上 GPU 版很多环境翻车都出在 CUDA 版本和 cuDNN 不匹配上。先 CPU 跑通逻辑再根据 README 考虑 GPU 配置。拿到源码包后第一件事是看README.md里锁的依赖版本源码运行环境以它为准。提示如果 conda 创建环境时网络慢可以用conda config --set channel_priority flexible换默认源别硬等。4.2 目录结构和数据路径让脚本找到图片源码里的images目录只有四张图属于样例或验证集。要完整训练常见做法是准备一个训练集目录——cat 和 dog 各放几百张图。PaddlePaddle 官方有猫狗数据集可以直接下载也可以手动整理自己的数据目录结构保持和 Dataset 代码里的一致project/ ├── cat_dog.py # 训练脚本 ├── 9029907.py # 预处理或推理脚本 ├── images/ # 样例图 │ ├── pic1.jpg │ ├── pic2.jpg │ ├── pic3.jpg │ └── pic4.jpg ├── data/ │ ├── train/ │ │ ├── cat/ (至少 200 张) │ │ └── dog/ (至少 200 张) │ └── val/ │ ├── cat/ │ └── dog/ └── output/ # 训练出的模型参数训练脚本里通常会有一个data_dir变量改成你实际放数据的路径。路径写绝对路径会省很多事——这个项目里我见过最多的报错就是相对路径没对上的FileNotFoundError。4.3 训练参数怎么调epoch、batch size、learning rate 的取舍源码默认参数一般是epochs30、batch_size16、learning_rate0.001。这三个参数直接决定训练效果和耗时epochs猫狗识别数据量小30 轮基本够看趋势。如果 30 轮后 loss 还在明显下降可以加到 50如果 10 轮就到平台期加轮数只会过拟合。batch_sizeCPU 训练建议 8~16GPU 可以到 32 或 64。batch 越大梯度越稳但显存占用也越大同时对内存要求更高。learning_rate0.001 是 Adam 优化器的安全起点。如果 loss 震荡不降先降到 0.0005 试一轮。修改训练入口# 训练并把模型输出到 output 目录 python cat_dog.py --data_dir ./data --epochs 30 --batch_size 16 --lr 0.001如果源码没做参数解析直接改脚本里的全局变量也一样。4.4 预测脚本加载模型参数输出单张图的猫狗置信度训练完成后的预测逻辑比较固定加载.pdparams参数建模型前向计算取概率最大类。核心代码import paddle import paddle.vision.transforms as T from PIL import Image # 加载模型 model CatDogCNN(num_classes2) model.load_dict(paddle.load(output/model_epoch30.pdparams)) model.eval() # 单张图预测 transform T.Compose([ T.Resize((128, 128)), T.ToTensor(), T.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ]) img Image.open(images/pic1.jpg).convert(RGB) input_tensor transform(img).unsqueeze(0) # 加 batch 维度 with paddle.no_grad(): logits model(input_tensor) probs paddle.nn.functional.softmax(logits, axis1) conf, pred paddle.max(probs, axis1), paddle.argmax(probs, axis1) class_names [cat, dog] print(f预测结果: {class_names[pred.item()]}, 置信度: {conf.item():.4f})重点在于T.ToTensor()会同时完成「HWC 转 CHW」和「像素除以 255」两件事——如果推理时漏掉这步图像通道顺序是错的预测结果会直接崩掉。unsqueeze(0)给单张图补一个 batch 维度很多新手会在这漏维度报 shape 不匹配。softmax(probs, axis1)按类别维做归一化输出两个概率且和为 1。跑完这段如果能打印出类似预测结果: dog, 置信度: 0.8733整套流程就算通了。接下来要调效果就看避坑章节和最后的调优技巧。5. 猫狗识别训练避坑loss 不降、全判一类、环境报错的排查手册5.1 loss 一路降到接近 0但验证集预测全错现象训练 loss 曲线漂亮得夸张前几个 epoch 就掉到 0.01 以下可跑验证集时准确率只有 50% 左右跟抛硬币一样。原因最常见的是shuffle没开或者开错位置。训练数据如果按类别顺序排列——先 200 张猫再 200 张狗batch_size又比较大的话一个 batch 里全是同一类梯度更新方向来回横跳模型学到的是「学会记忆顺序」而不是「学会区分特征」。另一个更致命的原因是标签错位Dataset里读图片路径和打标签的逻辑没对齐。解决训练 DataLoader 加shuffleTrue。检查标签映射顺序和目录结构一一对应——如果目录是cat/和dog/enumerate([cat, dog])的 0 和 1 必须和目录顺序完全一致。改完重跑一轮看 loss 是否变成平滑下降而不是断崖式下跌。5.2 训练 loss 卡在 0.69 附近不动像被焊死了一样现象训练几十个 epochloss 一直在 0.69 左右震荡不上不下。原因0.69 是个很有标志性的数字——它约等于-ln(0.5)也就是二分类随机猜的交叉熵。loss 卡在这个位置说明模型输出对每个样本都给出接近 50% 的概率等于什么都没学到。常见原因是学习率过小、数据没有归一化导致梯度太小或者网络结构太浅表达能力不够。解决先确认数据有没有过Normalize没有就加上再把学习率从 0.001 调到 0.005 试一轮注意不是所有项目都适合调大但 loss 焊死时可以试探性调一次最后检查是否模型加了几层但激活函数都没生效比如包装了但 forward 没调用。从这三个方向排查loss 一般会动起来。5.3 训练中途报显存不足OOM程序直接崩现象CPU 训练还好换到 GPU 后跑了几个 batch 报CUDA out of memory训练中断。原因显存占用是 batch size × 图片尺寸 × 模型参数量共同决定的。128×128 的输入在 CNN 里占不了多少显存多数情况是 batch size 设得太大或者输入图被 Resize 成了超大尺寸。用 GPU 训练时并不是「batch 越大越好」。解决显存不足时优先把batch_size减半——16 降到 88 降到 4。如果还爆看脚本里有没有Resize((256, 256))这类设置降到 128×128 能省四倍显存。实在不行加一句paddle.set_device(cpu)先跑通逻辑再回头优化显存。5.4 代码在 A 电脑跑得好好的换 B 电脑报No module named paddle现象项目在自己电脑上一切正常换到实验室机器或室友电脑后一运行就提示找不到 paddle 模块。原因PaddlePaddle 没有装进新的虚拟环境。大多数情况是换电脑后直接全局python跑脚本conda 环境没激活或者新机器只装了 Python 没装包。解决重建环境三步走conda create -n catdog python3.9 -y conda activate catdog python -m pip install -r requirements.txtrequirements.txt里至少要有paddlepaddle和Pillow。建议每次换机器都强制进虚拟环境跑不要用全局解释器这是最容易省下的一个踩坑项。5.5 预测概率两个加起来不是 10.7 和 0.8 同时出现现象预测一张图输出猫的概率 0.72、狗的概率 0.81两个概率都大于 0.5 且和不是 1怎么解释都矛盾。原因模型输出层是 2 个节点但预测代码用了sigmoid而不是softmax。sigmoid 是逐元素独立计算的两个输出互不影响自然不能保证和为 1。问题本质是输出层设计和预测代码写得不匹配。解决二分类模型要么输出 1 个节点 sigmoid得出单值概率后按 0.5 阈值分类要么输出 2 个节点 softmax取两个概率中大的那个。把预测代码里的sigmoid换成softmax即可probs paddle.nn.functional.softmax(logits, axis1)这个 bug 的隐蔽之处在于 loss 可能很正常——因为训练时用的 loss 函数和 sigmoid 兼容只有把推理结果拿出来解释时才发现问题。注意这两个写法数学上可以等价但工程实现上选一种并全程保持一致别在训练和推理时混用。我每次开局先看输出层节点数再决定预测代码怎么写能少踩一半的坑。6. 猫狗识别模型提升的实战技巧先动数据再改网络最后换学习率6.1 数据增强四行代码让训练集翻倍猫狗识别数据量通常不大过拟合比欠拟合更常见。给 Dataset 的 transform 加上数据增强是最快见效的提分手段self.transform T.Compose([ T.Resize((128, 128)), T.RandomHorizontalFlip(0.5), # 随机水平翻转 T.RandomRotation(10), # 随机旋转 10 度 T.ToTensor(), T.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ])水平翻转对猫狗识别特别合适——猫狗照片翻转后语义不变但模型能看到更多姿态。随机旋转 10 度能缓解拍摄角度带来的差异。注意验证集不要加增强否则看到的是「变形后的图」指标会失真。6.2 学习率调度用 CosineAnnealing 代替固定学习率固定 0.001 跑 30 轮后期容易出现平台期震荡。换成余弦退火让学习率随训练轮次衰减收敛更稳scheduler paddle.optimizer.lr.CosineAnnealingDecay(learning_rate0.001, T_max30) optimizer paddle.optimizer.Adam(parametersmodel.parameters(), learning_ratescheduler)每轮 epoch 结束调scheduler.step()学习率会从 0.001 平滑降到接近 0。这个技巧对图片分类任务几乎是无损提升而且只改两行代码。6.3 迁移学习用预训练模型做特征提取如果数据量实在小从零训练 CNN 学到的底层特征可能不充分。用 torchvision 同源的 PaddlePaddle 预训练模型替换骨干网络是课程设计拿高分的常规操作backbone paddle.vision.models.resnet18(pretrainedTrue) model nn.Sequential( backbone, nn.Flatten(), nn.Linear(1000, 2) )冻结 backbone 参数只训练最后的全连接层能让小数据集下的效果显著提升。但这招建议在基础流程跑通后再用——先把这份源码本身调明白再换网络结构否则一次改太多变量出了问题都不知道是哪一步引入的。说来惭愧我第一次跑通这套源码时也是直接上 resnet 加全套数据增强结果 loss 曲线一会崩一会不降回头查才发现是 normalize 参数没对齐预训练模型的输入分布。从那以后我每次拿到训练脚本都会先固定跑 5 个 epoch用最小配置确认数据链路没毛病再逐项加技巧。数据路径、输出层节点数、归一化参数这三件事会在第一次运行前强制过一遍。希望这些笔记帮到正在为课程设计头秃的你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。