news 2026/10/9 1:13:46

Python猫狗图像分类实战:CNN、DNN、RNN三模型对比与迁移学习

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python猫狗图像分类实战:CNN、DNN、RNN三模型对比与迁移学习

简介:这份资源面向深度学习入门者与计算机视觉方向的在校学生,围绕猫狗图像分类这一经典任务,用Python分别实现CNN、DNN与RNN三种网络结构,帮助读者对比不同模型在图像分类中的表现与适用边界。压缩包共9个文件,约105.33MB,包含3个py脚本分别对应三种模型的训练与推理,2个rar数据包提供灰度与原始图像数据,另有docx作业说明、README与LICENSE等文档,便于按模块查阅与复现。目前已有782人学习下载。资源不仅给出可运行的代码骨架,还覆盖数据预处理、模型构建、训练调参、评估与保存部署的完整流程,读者可据此理解卷积特征提取、深层网络梯度问题以及RNN处理序列信息的思路,并借助文档中的作业要求检验学习效果,适合作为课程作业或入门实战的参考方案。

1. 三套神经网络跑同一份猫狗数据:这份 Python 图像分类包到底值不值得拆

同一份猫狗图片,用 CNN、DNN、RNN 三种网络各跑一遍,结果差距能有多大?这个问题我在面试新人时问过不下十次,能说清楚的人不多。这份编号 100011892 的资源包,恰好就是拿同一批猫狗图像,分别喂给卷积神经网络、深度神经网络和循环神经网络,让你亲眼看到三种结构在图像分类任务上的真实表现差异。它不是那种只丢一个train.py让你自己猜的残缺项目,而是把dog-or-cat_CNN.py、dog-or-cat_DNN.py、dog-or-cat_RNN.py三个独立脚本摆在一起,配上classification-dogs-cats和data_gray.rar两套数据,外加一份 Word 作业文档讲设计思路。适合谁?刚学完 CNN 卷积层原理、想找个能跑通的完整项目练手的人;也适合教深度学习课、需要一份三模型对照实验素材的老师。如果你只是想调包跑个 SOTA,这份资源可能不够新,但如果你想搞明白“为什么图像分类默认用 CNN 而不是 DNN 或 RNN”,它给了一个成本极低的实验场。

2. 拆包先看文件结构:三个脚本、两套数据、一份文档怎么配合

2.1 压缩包里的文件清单与各自职责

拿到100011892-基于Python实现猫狗图像分类(CNN-DNN-RNN).zip之后,别急着解压完就python train.py。先花两分钟把目录结构看清楚,后面调参和排错会省很多事。解压后的顶层文件大致是这些:

文件/目录类型作用
dog-or-cat_CNN.py脚本卷积神经网络训练与评估主程序
dog-or-cat_DNN.py脚本全连接深度神经网络训练与评估主程序
dog-or-cat_RNN.py脚本循环神经网络训练与评估主程序
classification-dogs-cats/目录猫狗彩色图像数据集,按类别分文件夹
data_gray.rar压缩包灰度化后的图像数据,体积更小、读取更快
README.md文档环境依赖、运行顺序、参数说明
作业一:基于深度神经网络的猫狗图像分类.docx文档设计思路、模型结构、实验结果分析
自然语言处理_作业一 .docx文档同批作业的另一份说明,可忽略
LICENSE协议开源许可,商用前确认一下

三个脚本是并列关系,不是流水线。你可以只跑 CNN,也可以三个都跑做对比。classification-dogs-cats是原始彩色图,data_gray.rar是灰度图,后者在 DNN 和 RNN 上更常用,因为全连接层和循环层对通道数的处理不如卷积层自然。

2.2 环境依赖与 Python 版本选择

README 里一般会写依赖,但这类课程项目往往写得比较粗。我实际跑下来的经验是:Python 3.8 最稳,3.10 以上偶尔会遇到 TensorFlow 和 Keras 版本对不上的问题。核心依赖就三个:

# 建议在虚拟环境里装,避免污染全局 python -m venv catdog_env source catdog_env/bin/activate # Windows 用 catdog_env\Scripts\activate # 核心依赖,版本不要追新 pip install tensorflow==2.10.0 pip install numpy==1.23.5 pip install matplotlib==3.6.2 pip install opencv-python==4.6.0.66 pip install pillow==9.3.0

这里为什么锁 TensorFlow 2.10?因为 2.11 之后 Keras 被拆成独立包,老脚本里from tensorflow.keras.models import Sequential这种写法在 2.11+ 上虽然还能用,但model.fit的某些回调参数行为有变化,容易在保存模型时翻车。NumPy 锁 1.23 是因为 1.24 移除了np.float等别名,老代码里如果有np.float会直接报错。OpenCV 和 Pillow 用来做图像读取和增强,版本差异不大,但建议一起锁,避免cv2.imread返回的通道顺序和PIL.Image.open不一致导致训练时图像颜色错乱。

提示:如果你已经装了 TensorFlow 2.15 或更高版本,不要直接卸载重装,先建虚拟环境。全局环境里可能有其他项目依赖旧版或新版,混在一起排查成本很高。

2.3 数据目录的组织方式与读取逻辑

classification-dogs-cats目录通常长这样:

classification-dogs-cats/ ├── train/ │ ├── cats/ │ │ ├── cat.1.jpg │ │ └── ... │ └── dogs/ │ ├── dog.1.jpg │ └── ... ├── validation/ │ ├── cats/ │ └── dogs/ └── test/ ├── cats/ └── dogs/

这种按类别分文件夹的结构,配合 Keras 的ImageDataGenerator.flow_from_directory是最省事的。脚本里一般会写:

from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator( rescale=1./255, # 归一化到 0-1 rotation_range=20, # 随机旋转 ±20 度 width_shift_range=0.1, # 水平平移 10% height_shift_range=0.1, # 垂直平移 10% horizontal_flip=True, # 水平翻转 zoom_range=0.1 # 随机缩放 10% ) train_generator = train_datagen.flow_from_directory( 'classification-dogs-cats/train', target_size=(150, 150), # 统一缩放到 150x150 batch_size=32, class_mode='binary' # 猫狗二分类 )

rescale=1./255是把像素值从 0-255 压到 0-1,这是图像分类的标准操作,不做的话梯度会爆炸。target_size=(150,150)是权衡后的选择:太小丢失纹理细节,太大显存吃紧且训练慢。class_mode='binary'对应二分类,输出层用 sigmoid 激活。如果你把data_gray.rar解压后替换掉彩色图目录,记得把color_mode改成'grayscale',否则生成器会按三通道读取灰度图,导致维度对不上。

3. CNN 脚本精读:卷积层怎么堆、参数怎么设、训练怎么监控

3.1 CNN 模型结构逐层拆解

打开dog-or-cat_CNN.py,核心是Sequential模型。典型结构如下:

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout model = Sequential([ # 第一组卷积:提取边缘、纹理等低级特征 Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)), MaxPooling2D(2, 2), # 第二组卷积:提取形状、局部部件 Conv2D(64, (3, 3), activation='relu'), MaxPooling2D(2, 2), # 第三组卷积:提取更抽象的语义特征 Conv2D(128, (3, 3), activation='relu'), MaxPooling2D(2, 2), # 第四组卷积:进一步压缩空间维度 Conv2D(128, (3, 3), activation='relu'), MaxPooling2D(2, 2), Flatten(), # 展平成一维向量 Dropout(0.5), # 随机丢弃 50% 神经元,防过拟合 Dense(512, activation='relu'), Dense(1, activation='sigmoid') # 二分类输出 ])

逐层看:第一层Conv2D(32, (3,3))表示用 32 个 3×3 的卷积核,每个核在输入图像上滑动做点积,输出 32 张特征图。input_shape=(150,150,3)对应 RGB 三通道。MaxPooling2D(2,2)把特征图尺寸减半,减少计算量同时保留最强响应。四组卷积下来,空间尺寸从 150 降到 9 左右,通道数从 3 升到 128。Flatten把 9×9×128 拉成 10368 维向量,接Dense(512)做全连接,最后Dense(1)输出一个 0-1 之间的概率值,大于 0.5 判为狗,小于 0.5 判为猫。

Dropout(0.5)是血泪经验:不加这个,训练集准确率能到 95% 但验证集卡在 70% 上不去,典型的过拟合。加了之后验证集通常能提 10 个点左右。

3.2 编译参数:优化器、损失函数、评估指标

模型结构定好后,编译阶段三个参数决定训练走向:

from tensorflow.keras.optimizers import Adam model.compile( optimizer=Adam(learning_rate=1e-4), # 学习率别用默认的 1e-3 loss='binary_crossentropy', # 二分类标准损失 metrics=['accuracy'] )

Adam优化器对大多数图像任务都友好,但学习率1e-4比默认的1e-3更稳。为什么?猫狗数据集通常几千到几万张,用 1e-3 前期 loss 震荡明显,容易在局部最优点附近跳出去又跳回来。1e-4 收敛慢一点但曲线平滑,最终精度往往更高。binary_crossentropy是二分类的标配,不要用categorical_crossentropy,后者要求标签是 one-hot 编码,而class_mode='binary'给的是 0/1 标量,对不上会报维度错误。

3.3 训练循环与回调配置

训练阶段用fit配合回调:

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks = [ EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True), ModelCheckpoint('best_cnn_model.h5', monitor='val_accuracy', save_best_only=True) ] history = model.fit( train_generator, epochs=50, validation_data=val_generator, callbacks=callbacks )

EarlyStopping的patience=5意思是验证损失连续 5 轮不下降就停,restore_best_weights=True保证停的时候回滚到最优轮次的权重,而不是停在最差的那一轮。ModelCheckpoint只保存验证准确率最高的模型,避免最后几轮过拟合的权重覆盖掉好模型。epochs=50是上限,实际通常 20-30 轮就触发早停了。

训练过程中重点看两个信号:训练准确率和验证准确率的差距。如果训练到 90% 而验证只有 75%,说明过拟合,加 Dropout 或数据增强;如果两者都卡在 60% 左右,说明欠拟合,加深网络或提高学习率。history.history里存了每轮的 loss 和 accuracy,用 matplotlib 画出来一目了然。

4. DNN 与 RNN 脚本对比:全连接和循环结构在图像上到底差在哪

4.1 DNN 脚本:把图像拉平后全连接堆叠

dog-or-cat_DNN.py的结构比 CNN 简单得多:

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Flatten, Dense, Dropout model = Sequential([ Flatten(input_shape=(150, 150, 3)), # 直接拉平成 67500 维 Dense(512, activation='relu'), Dropout(0.5), Dense(256, activation='relu'), Dropout(0.5), Dense(128, activation='relu'), Dense(1, activation='sigmoid') ])

关键区别在第一层:CNN 用卷积核在空间上滑动,保留像素之间的位置关系;DNN 直接Flatten,把 150×150×3 的图像拉成 67500 维向量,每个像素独立对待。这意味着 DNN 看不到“相邻像素组成边缘”这种空间结构,它只能学到“某些像素位置同时亮”的统计相关性。结果就是 DNN 在猫狗分类上通常比 CNN 低 10-15 个百分点,而且参数量巨大——第一层全连接就是 67500×512≈3450 万个权重,训练慢且容易过拟合。

那为什么还要放 DNN 脚本?因为它是一个很好的对照组。跑一遍 DNN,你会直观感受到“没有空间归纳偏置”的代价。另外,如果你的数据已经是提取好的特征向量(比如 HOG 特征),DNN 反而是合适的选择。

4.2 RNN 脚本:把图像当序列处理

dog-or-cat_RNN.py的思路更“反直觉”:

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Reshape model = Sequential([ Reshape((150, 150*3), input_shape=(150, 150, 3)), # 每行作为一个时间步 LSTM(128, return_sequences=False), Dense(64, activation='relu'), Dense(1, activation='sigmoid') ])

这里把图像的每一行(150 个像素×3 通道=450 维)当作一个时间步,整张图就是 150 个时间步的序列。LSTM 沿垂直方向扫描,试图捕捉行与行之间的依赖关系。但问题在于:图像的空间关系是二维的,行与行之间的依赖远不如卷积核的局部感受野直接。所以 RNN 在这个任务上通常表现最差,训练也最慢。它的价值在于让你理解“为什么图像分类默认不用 RNN”——除非你有视频帧序列,否则 RNN 的序列建模能力在静态图像上发挥不出来。

4.3 三模型训练结果对比与选型建议

三个脚本跑完后,把结果填进一张表:

模型验证准确率(典型值)单轮训练时间参数量适用场景
CNN85%-92%中等中等图像分类首选
DNN70%-80%较慢最大特征向量输入
RNN60%-72%最慢中等序列/视频数据

CNN 胜在空间特征提取,DNN 胜在结构简单但参数量爆炸,RNN 胜在序列建模但图像上水土不服。选型建议很直接:静态图像分类无脑选 CNN;如果图像被展平成了特征向量,DNN 可用;如果处理的是视频连续帧,才考虑 CNN+LSTM 混合结构。

5. 避坑与排查:数据路径、显存、版本、过拟合的五个真实翻车点

5.1 路径写死导致 FileNotFoundError

现象:脚本一运行就报FileNotFoundError: [Errno 2] No such file or directory: 'classification-dogs-cats/train'。原因:脚本里用的是相对路径,而你的终端工作目录不在解压后的根目录。解决:要么cd到脚本所在目录再运行,要么把路径改成绝对路径。我一般会在脚本开头加import os; os.chdir(os.path.dirname(os.path.abspath(__file__))),一劳永逸。

5.2 显存不足导致训练中断

现象:训练到一半报ResourceExhaustedError: OOM when allocating tensor。原因:batch_size=32加上 150×150 的输入,在 4GB 显存的显卡上可能吃紧。解决:把batch_size降到 16 或 8,或者把target_size降到 128×128。如果用的是 CPU 训练,把batch_size降到 8 以下,否则内存也会爆。

5.3 TensorFlow 版本不匹配导致导入报错

现象:ImportError: cannot import name 'Adam' from 'tensorflow.keras.optimizers'。原因:TensorFlow 2.11+ 把 Keras 拆成了独立包,导入路径变了。解决:要么降级到 2.10,要么把导入改成from tensorflow.keras.optimizers import Adam对应的新路径。最省事的办法还是建虚拟环境锁 2.10。

5.4 数据增强过度导致训练不收敛

现象:loss 曲线剧烈震荡,准确率上不去。原因:rotation_range=40、zoom_range=0.3这类激进增强让图像变形太厉害,模型学不到稳定特征。解决:把旋转降到 20 度以内,缩放降到 0.1,平移降到 0.1。数据增强是“锦上添花”,不是“越多越好”。

5.5 验证集与训练集分布不一致导致虚高

现象:验证准确率 95%,但拿测试集一跑只有 70%。原因:验证集可能和训练集来自同一批图片的相邻帧,或者验证集太小(比如只有 100 张)。解决:确保validation目录和train目录的图片来源独立,验证集至少 500 张以上。如果数据不够,用train_test_split重新划分,别偷懒。

6. 进阶技巧:用迁移学习把 CNN 准确率再提一截

三个脚本跑通之后,你大概会停在 CNN 85%-92% 的准确率上。想再往上走,最划算的路子是迁移学习——拿 ImageNet 上预训练好的模型(比如 VGG16、ResNet50),冻结卷积基,只训练顶部分类层。改动量很小,但效果通常能到 95% 以上。

from tensorflow.keras.applications import VGG16 from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense, Flatten, Dropout # 加载预训练卷积基,不含顶部全连接层 base_model = VGG16(weights='imagenet', include_top=False, input_shape=(150, 150, 3)) # 冻结卷积基的所有层 for layer in base_model.layers: layer.trainable = False # 在卷积基上接自己的分类头 x = Flatten()(base_model.output) x = Dense(256, activation='relu')(x) x = Dropout(0.5)(x) output = Dense(1, activation='sigmoid')(x) model = Model(inputs=base_model.input, outputs=output) model.compile(optimizer=Adam(learning_rate=1e-4), loss='binary_crossentropy', metrics=['accuracy'])

关键参数说明:weights='imagenet'表示加载在 ImageNet 上训练好的权重,第一次运行会自动下载(约 500MB),确保网络通畅。include_top=False去掉 VGG16 原来的 1000 类分类头,只保留卷积部分。layer.trainable = False冻结卷积基,训练时只更新后面接的Dense层,这样训练速度快、不容易过拟合。等顶部训练稳定后,可以解冻最后几个卷积块做微调,学习率再降一个数量级到 1e-5。

验证方法很简单:跑完迁移学习后,用同一个测试集对比原 CNN 脚本的准确率。如果提升不到 3 个百分点,检查一下target_size是否和预训练模型期望的输入尺寸匹配——VGG16 默认是 224×224,你用 150×150 虽然能跑,但特征提取效果会打折扣。改成 224×224 再试一次,通常能看到明显差异。

从那以后我每次拿到一个新的图像分类项目,都强制自己先跑一遍基线 CNN,再跑一遍迁移学习,两个结果摆在一起看差距。如果迁移学习提升不明显,说明数据集和 ImageNet 分布差异太大,这时候再考虑自己设计网络结构或者找领域内的预训练模型。希望帮到你。

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

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

JavaWeb学生管理系统源码实战:从导入到部署的完整指南

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

作者头像 李华
网站建设 2026/10/9 1:12:36

通信工程师的实战题库:从PPT刷题到真实设备排错

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

作者头像 李华
网站建设 2026/10/9 1:12:03

海天注塑机配弘讯控制器数据采集实战:从RS485组网到寄存器解析

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

作者头像 李华
网站建设 2026/10/9 1:12:02

STM32时钟树配置全解析:从原理到实战避坑指南

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

作者头像 李华
网站建设 2026/10/9 1:08:33

855协议五端学习版源码拆解:长连接通信架构与生产落地实践

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

作者头像 李华
网站建设 2026/10/9 1:08:33

7针SPI OLED改I2C使用:硬件跳线与软件适配全攻略

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

作者头像 李华