
简介这份资源是面向深度学习入门者与计算机视觉方向学生的CNN猫狗图像识别实战项目包围绕卷积神经网络完成从数据准备到模型部署的完整流程。内容涵盖图像清洗、尺寸调整与归一化、数据增强以及基于TensorFlow或PyTorch搭建VGG、ResNet等常见CNN结构并涉及训练、验证调参与测试泛化等关键环节。压缩包共26个文件约49.3MB包含3个py脚本、3个zip子包、1份PDF案例文档、1个说明txt与README另有11张jpg和6张png用于展示数据样例、网络结构与预测效果。已有314人学习下载。读者可获得可运行的分类源码、配套数据集与程序说明借助PDF案例与结构图理解卷积层、池化层、全连接层及softmax的设计思路并参考预测快照与数据加载示例快速复现实验适合作为课程设计或入门练手项目。1. 从一份能跑通的猫狗识别源码说起如果你正在找一份能直接跑起来的 CNN 图像分类实战项目而不是那种只有模型定义、缺数据、缺训练脚本的半成品这份「基于 CNN 的猫狗图像识别检测分类项目」值得先看一眼。它把 Keras 版的卷积神经网络案例、猫狗图片样例、训练与预测脚本、程序说明文档和一份 PDF 讲义打包在一起核心文件是Convolutional_Neural_Network_Case_Study.py配套①猫狗数据分类.py和②识别猫狗.py两个分步脚本。对刚接触深度学习 CNN 的人来说它解决的是「知道卷积、池化、全连接这些名词但不知道怎么把一堆 jpg 变成能预测猫狗的模型」这个断层。适合有 Python 基础、装过 numpy 和 TensorFlow/Keras、想跑通第一个图像二分类项目的人也适合需要一份教学素材或课程设计模板的从业者。2. 拆开压缩包文件结构与 CNN 数据流对应关系2.1 目录里每个文件在训练流程里的位置拿到压缩包先别急着运行把目录结构和 CNN 的标准数据流对一遍后面排错会快很多。这份资源的文件大致分四类脚本、数据、文档、示例图。文件/目录类型在流程中的角色Convolutional_Neural_Network_Case_Study.py主脚本完整案例数据加载、建模、训练、评估①猫狗数据分类.py分步脚本数据整理与分类把原始图片归到 train/validation②识别猫狗.py分步脚本加载模型做单张或批量预测data/数据目录存放猫狗图片按类别分子目录image_examples/示例图cat_or_dog_1.jpg、cat_or_dog_2.jpg用于预测演示Convolutional_Neural_Networks_Case_Study.pdf文档案例讲义讲 CNN 原理和 Keras 用法基于卷积神经网络的猫狗图像识别 程序说明.txt文档中文程序说明交代运行顺序README.md文档项目说明入口Keras ImageDataGenerator Library.png等截图对应 Keras 各函数的用法示意cnn_summary.png截图模型结构摘要Prediction_Snapshot.png截图预测结果快照这个结构对应的是最经典的图像二分类流水线原始图片 → 目录分类 →ImageDataGenerator读取与增强 → CNN 前向传播 → 编译训练 → 保存模型 → 加载预测。①和②两个脚本把「训练」和「推理」拆开对新手很友好因为你可以先跑分类、再跑识别中间出错能定位到具体阶段。2.2 先确认环境和依赖版本在跑任何脚本之前把环境对齐。这份项目用的是 Keras 高层 APIImageDataGenerator、load_img、img_to_array这些函数在不同 TensorFlow 版本里行为有差异尤其是 2.x 之后ImageDataGenerator被标记为逐步弃用但依然可用。# 建议用虚拟环境避免和系统里的包打架 python -m venv cnn_env source cnn_env/bin/activate # Windows 用 cnn_env\Scripts\activate # 安装核心依赖版本不必追新能对上 ImageDataGenerator 即可 pip install tensorflow2.10.0 pip install numpy pillow matplotlib逻辑说明tensorflow自带 Keras不需要单独装keras包单独装反而容易版本冲突。pillow是load_img的底层依赖缺了会在读图时报错。参数上TensorFlow 2.10 是最后一个在 Windows 原生支持 GPU 的版本之一如果你在 Windows 上想用 GPU 加速这个版本比较稳纯 CPU 跑小数据集也够用只是慢。提示如果你已经装了 TensorFlow 2.16 及以上ImageDataGenerator可能提示弃用警告功能还在但建议按项目脚本的写法走不要自己改成tf.keras.utils.image_dataset_from_directory否则和讲义对不上。3. 数据准备与增强ImageDataGenerator 的参数怎么设3.1 目录结构决定能不能直接喂给生成器Keras 的ImageDataGenerator.flow_from_directory对目录结构有硬性要求每个类别一个子目录图片放在对应子目录下。这份资源的data/目录就是按这个约定组织的猫和狗各一个文件夹。如果你自己换数据集必须保持同样的结构。import os import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator # 数据根目录下面应有 cat/ 和 dog/ 两个子目录 data_dir data # 确认类别目录存在避免 flow_from_directory 静默返回 0 张图 for cls in [cat, dog]: cls_path os.path.join(data_dir, cls) count len(os.listdir(cls_path)) if os.path.isdir(cls_path) else 0 print(f{cls}: {count} 张)逻辑说明flow_from_directory在目录不存在或没有图片时不会直接报错而是生成一个空生成器训练时表现为steps_per_epoch异常或准确率不动。先手动数一遍图片数量是最省事的排查手段。参数上data_dir要指向包含类别子目录的那一层不是子目录本身。3.2 训练集与验证集的划分和增强参数数据增强是这份项目里提升泛化能力的关键。猫狗图片如果只是原图训练模型很容易记住背景而不是猫狗本身换一批图就翻车。ImageDataGenerator在训练时做随机变换验证时只做归一化。# 训练集生成器带增强 train_datagen ImageDataGenerator( rescale1./255, # 像素归一化到 0-1 rotation_range40, # 随机旋转 ±40 度 width_shift_range0.2, # 水平平移 20% height_shift_range0.2, # 垂直平移 20% shear_range0.2, # 剪切变换 zoom_range0.2, # 随机缩放 horizontal_flipTrue, # 水平翻转 fill_modenearest, # 变换后空白填充方式 validation_split0.2 # 划出 20% 做验证 ) # 验证集生成器只归一化不增强 val_datagen ImageDataGenerator( rescale1./255, validation_split0.2 ) train_generator train_datagen.flow_from_directory( data_dir, target_size(150, 150), # 统一缩放到 150x150 batch_size32, class_modebinary, # 二分类用 binary subsettraining ) val_generator val_datagen.flow_from_directory( data_dir, target_size(150, 150), batch_size32, class_modebinary, subsetvalidation )逻辑说明rescale1./255把 0-255 的像素压到 0-1这是 CNN 收敛的基本前提漏了这一步损失会很难降。validation_split0.2要求训练和验证两个生成器用同一个validation_split值否则划分不一致。target_size必须和后面模型输入层一致这份项目用 150x150改尺寸要同步改模型。class_modebinary对应输出层一个 sigmoid 单元如果是多分类要改成categorical。参数怎么调rotation_range和zoom_range不是越大越好猫狗图片旋转 40 度已经比较激进再大可能把猫转成狗都认不出的角度反而引入噪声。batch_size32是显存和梯度稳定性的折中显存小就降到 16 或 8。注意flow_from_directory默认按字母顺序分配类别索引cat在前就是 0dog是 1。预测时输出的 0/1 要按这个顺序解释别想当然认为 1 就是猫。4. 搭 CNN 与训练从卷积层到编译参数4.1 卷积、池化、全连接的堆叠顺序这份项目的 CNN 结构是典型的「卷积-池化」堆叠再接全连接。理解每一层在干什么比死记代码有用。卷积层提取局部特征池化层降维并保留主要信息全连接层做最终分类。from tensorflow.keras import layers, models model models.Sequential([ # 第一组卷积 池化 layers.Conv2D(32, (3, 3), activationrelu, input_shape(150, 150, 3)), layers.MaxPooling2D((2, 2)), # 第二组 layers.Conv2D(64, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 第三组 layers.Conv2D(128, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 展平后接全连接 layers.Flatten(), layers.Dense(512, activationrelu), layers.Dropout(0.5), # 随机丢弃一半抑制过拟合 layers.Dense(1, activationsigmoid) # 二分类输出 ]) model.summary()逻辑说明Conv2D的第一个参数是卷积核数量32→64→128 逐层翻倍是常见做法浅层抓边缘纹理深层抓语义。(3, 3)是卷积核尺寸3x3 在性能和效果上最平衡。input_shape(150, 150, 3)对应前面target_size和 RGB 三通道。Flatten把特征图拉成一维Dense(512)做特征组合Dropout(0.5)是防过拟合的关键训练时随机失活一半神经元。最后Dense(1, sigmoid)输出 0-1 之间的概率大于 0.5 判为一类。model.summary()的输出可以对照cnn_summary.png看确认每层输出维度符合预期。如果维度对不上多半是target_size和input_shape不一致。4.2 编译参数与训练轮次的取舍编译阶段选优化器、损失函数和评估指标训练阶段定轮次和回调。model.compile( optimizeradam, # 自适应学习率新手友好 lossbinary_crossentropy, # 二分类标准损失 metrics[accuracy] ) history model.fit( train_generator, epochs15, validation_dataval_generator, steps_per_epochtrain_generator.samples // 32, validation_stepsval_generator.samples // 32 ) model.save(cat_dog_cnn.h5)逻辑说明adam默认学习率 0.001大多数图像任务不用调就能收敛比 SGD 省心。binary_crossentropy是二分类标配多分类要换categorical_crossentropy。steps_per_epoch用样本数除以 batch_size不写的话 Keras 会自己算但显式写出来更可控。epochs15是这份项目的量级数据少的话容易过拟合可以配合EarlyStopping回调提前停。参数怎么改如果验证准确率一直上不去先把epochs加到 25 看趋势如果训练准确率高但验证低说明过拟合加大Dropout或增强力度如果两者都低可能是学习率或模型容量问题。model.save存成.h5格式方便后面②识别猫狗.py直接加载。提示训练时留意loss是否在下降。如果第一个 epoch 就卡在 0.69 左右不动基本是二分类的随机猜测水平检查归一化和标签是否正确。5. 推理与验证加载模型预测单张图片5.1 用 load_img 和 img_to_array 走完预测链路训练完模型用②识别猫狗.py或自己写一段推理代码验证。核心是把单张图片处理成和训练时一样的格式再喂给模型。import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing import image # 加载训练好的模型 model load_model(cat_dog_cnn.h5) # 读取一张待预测图片尺寸必须和训练时一致 img_path image_examples/cat_or_dog_1.jpg img image.load_img(img_path, target_size(150, 150)) img_array image.img_to_array(img) # 转成 numpy 数组形状 (150,150,3) img_array img_array / 255.0 # 归一化和训练保持一致 img_array np.expand_dims(img_array, axis0) # 增加 batch 维度变成 (1,150,150,3) # 预测 prediction model.predict(img_array) print(prediction) # 按 flow_from_directory 的类别顺序解释结果 if prediction[0][0] 0.5: print(预测为dog) else: print(预测为cat)逻辑说明load_img的target_size必须和训练时一致否则输入维度对不上直接报错。img_to_array把 PIL 图像转成 numpy 数组/255.0是归一化这一步漏了预测结果会完全乱掉。np.expand_dims增加一个 batch 维度因为model.predict期望输入是(batch, height, width, channels)。最后判断阈值 0.5对应 sigmoid 输出。类别顺序的判断依据是flow_from_directory的字母序cat在前为 0dog为 1。如果你换过目录名顺序会变最稳妥的办法是打印train_generator.class_indices确认。5.2 用验证集准确率和混淆矩阵判断模型是否可用单张预测看着对不代表模型真的好。用验证集跑一遍整体评估再看混淆矩阵才能判断有没有偏科。# 整体评估 loss, acc model.evaluate(val_generator) print(f验证集准确率: {acc:.4f}) # 生成混淆矩阵 from sklearn.metrics import confusion_matrix import numpy as np val_generator.reset() preds model.predict(val_generator) pred_labels (preds 0.5).astype(int).flatten() true_labels val_generator.classes[:len(pred_labels)] cm confusion_matrix(true_labels, pred_labels) print(cm)逻辑说明model.evaluate返回损失和准确率准确率低于 0.7 基本说明模型没学好。val_generator.reset()很重要生成器被消费过之后不重置会从中间继续导致预测和标签错位。preds 0.5把概率转成 0/1 标签flatten拉平。混淆矩阵的四个格子分别对应真猫判猫、真猫判狗、真狗判猫、真狗判狗如果某一类错误特别多说明数据不平衡或该类样本太少。注意val_generator.classes的顺序和生成器输出顺序一致但长度可能因为 batch 取整和预测数量有细微差异用[:len(pred_labels)]对齐避免维度报错。6. 避坑与排查五个真实翻车记录6.1 准确率卡在 0.5 不动现象训练几个 epochloss在 0.69 附近accuracy约 0.5像没学一样。 原因最常见是归一化漏了或标签和输出不匹配。rescale1./255没加像素值 0-255 直接进网络梯度爆炸或消失或者class_mode设成categorical但输出层是单 sigmoid。 解决检查生成器rescale参数确认class_modebinary和Dense(1, sigmoid)配套。打印一个 batch 的像素范围验证是否在 0-1。6.2 报错 expected input to have shape (150,150,3)现象预测时报维度不匹配。 原因load_img的target_size和模型input_shape不一致或者忘了np.expand_dims加 batch 维度。 解决统一改成(150, 150)预测前用img_array.shape打印确认是(1,150,150,3)。6.3 验证集准确率远低于训练集现象训练集 0.95验证集 0.65差距很大。 原因过拟合。数据量小、增强不够、模型容量过大都会导致。 解决加大Dropout比例增强rotation_range和zoom_range或者减少卷积核数量。数据太少的话考虑用预训练模型做迁移学习。6.4 flow_from_directory 返回 0 张图现象train_generator.samples为 0训练直接报错。 原因目录结构不对图片没有放在类别子目录下或者路径写错。 解决确认data_dir下直接是cat/和dog/图片在子目录里。用os.listdir打印目录内容核对。6.5 预测结果总是同一类现象不管输入什么图都输出猫或都输出狗。 原因模型没训练好或者归一化在训练和预测时不一致。 解决确认训练和预测都做了/255.0重新训练并观察loss是否下降。如果数据严重不平衡某一类占绝大多数模型会倾向于预测多数类需要平衡数据或加类别权重。7. 把这份源码改成自己的数据集迁移学习与微调技巧跑通原项目之后真正有价值的是把它用到自己的数据上。猫狗识别只是载体换成花卉、零件缺陷、X 光片流程是一样的但直接从头训练 CNN 在小数据集上效果往往一般。我一般会做一步迁移学习拿一个在 ImageNet 上预训练好的网络冻结卷积基只训练顶部分类层再解冻最后几层做微调。from tensorflow.keras.applications import VGG16 from tensorflow.keras import layers, models, optimizers # 加载预训练卷积基不含顶部分类层 base_model VGG16( weightsimagenet, include_topFalse, input_shape(150, 150, 3) ) # 冻结卷积基先只训练新加的分类层 base_model.trainable False model models.Sequential([ base_model, layers.Flatten(), layers.Dense(256, activationrelu), layers.Dropout(0.5), layers.Dense(1, activationsigmoid) ]) model.compile( optimizeroptimizers.Adam(learning_rate1e-4), lossbinary_crossentropy, metrics[accuracy] ) # 第一阶段只训练分类层 model.fit(train_generator, epochs5, validation_dataval_generator) # 第二阶段解冻最后几层做微调学习率调小 base_model.trainable True for layer in base_model.layers[:-4]: layer.trainable False model.compile( optimizeroptimizers.Adam(learning_rate1e-5), lossbinary_crossentropy, metrics[accuracy] ) model.fit(train_generator, epochs5, validation_dataval_generator)逻辑说明include_topFalse去掉 VGG16 原来的 1000 类分类层只保留卷积特征提取部分。base_model.trainable False冻结权重第一阶段只更新新加的Dense层这样小数据集也能快速收敛。第二阶段解冻最后 4 层用更小的学习率1e-5微调避免破坏预训练权重。两阶段的学习率差异是关键第一阶段 1e-4第二阶段必须降一个量级。参数怎么调解冻层数取决于你的数据集和 ImageNet 的相似度越相似解冻越少。Dense(256)的单元数可以按数据量调整数据少就减到 128。微调阶段如果验证损失反弹说明学习率还是大再降。验证迁移学习是否有效对比两个数字从头训练 CNN 的验证准确率和迁移学习后的验证准确率。通常小数据集上迁移学习能高出 10 到 20 个百分点。如果没提升检查weightsimagenet是否真的下载成功离线环境会静默用随机权重那就白搭了。从那以后我每次拿到新的图像分类任务都强制先跑一遍迁移学习基线再决定要不要从头设计网络。这份猫狗项目最大的价值不是那几行 CNN 代码而是它把数据组织、增强、训练、推理、评估这条链路完整摆出来了你照着换成自己的数据改几个路径和类别名就能用。希望帮到你。本文还有配套的精品资源点击获取