news 2026/9/6 21:24:15

Keras猫狗分类实战:从数据增强到卷积神经网络的深度学习入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Keras猫狗分类实战:从数据增强到卷积神经网络的深度学习入门

简介:《基于Keras的猫狗分类识别实验报告》共22页,是作者原创的机器学习期末大作业,面向需要完成图像分类实验报告的高校学生。报告以Kaggle经典猫狗赛题为背景,结构完整,涵盖摘要、引言、算法原理分析、数据组成、实验与结果分析、结论及参考文献,并包含算法流程图、运行结果截图和核心代码,便于读者对照复现。实现上,报告基于CNN和AlexNet网络结构,利用ImageNet预训练权重进行迁移学习;针对训练集标签异常问题,设计了一套创新预处理流程:用预训练模型预测训练集图片,分别微调猫和狗的top参数来筛查异常样本,得到24964张猫狗数量均衡的图像,并重新编号排序。资源包内只有1个docx文档,大小5.16MB。目前已有7899人学习下载,是机器学习课程设计、期末报告撰写的实用参考资料。 又到期末季,机器学习课程的期末大作业,我选了基于Keras的猫狗分类识别——经典到不能再经典的图片分类任务,却恰好能把卷积神经网络、数据增强、训练验证这些核心知识点全部串起来。这篇文章相当于一份完整的实验记录,从环境搭建、数据准备、模型设计、训练调参到结果分析都拆开讲,代码和参数都给出可复现的写法,适合正在做机器学习课程作业、或者刚入门深度学习想跑通第一个图像分类项目的同学参考。

1. 为什么选猫狗分类这个小项目,以及环境怎么搭

1.1 经典任务背后的完整学习链路

很多人觉得猫狗分类太“入门”了,不愿意选。但恰恰是这个题目,把深度学习的几个关键环节全部覆盖了:数据集怎么组织、图片怎么预处理、网络结构怎么设计、训练过程怎么监控、结果怎么评估。换一个更复杂的任务,比如细粒度分类或目标检测,课程报告反而容易变成“调参流水账”,因为核心知识点全被预训练模型和复杂框架遮住了。

另外,猫狗分类的数据集非常容易获取,Kaggle上的Dogs vs Cats数据集,25000张训练图片,猫狗各半,够训练一个从零搭建的小型卷积神经网络。这个数据量对CPU训练比较友好,对显存要求也不高,不至于让没有GPU的同学卡在第一步。

1.2 环境配置与版本依赖避坑

我的实验环境如下,直接给出来供参考:

项目版本/配置
操作系统Ubuntu 20.04(Windows 10也可以,命令基本一致)
Python3.9
TensorFlow2.10.0
Keras使用TensorFlow内置的tf.keras
Jupyter Notebook用于逐步记录和可视化
CPU/GPU有NVIDIA显卡更好,没有也可以跑

安装命令很简单:

pip install tensorflow==2.10.0 pip install jupyter

这里有个容易踩的版本坑:从TensorFlow 2.x开始,Keras已经内置为tf.keras,直接安装TensorFlow就能用,不需要再单独pip install keras。如果你单独装了新版Keras 3.x,反而可能和TensorFlow自带版本产生冲突。建议代码里统一用from tensorflow import keras这种写法,保证环境干净。

数据准备和模型训练全在Jupyter Notebook里完成,单元格逐段执行,方便截图表、写注释,交报告时直接导出PDF或HTML,非常省事。硬件方面,我的机器有8GB显存,训练一个epoch大约60秒,如果你的CPU训练,每个epoch可能要3到5分钟,也能接受,只是耐心一点。

2. 数据集的获取、整理和预处理细节

2.1 数据划分与目录结构

Kaggle原始数据解压之后是PetImages目录,里面有Cat和Dog两个文件夹,总共25000张图。但实验报告里不能直接拿全部数据训练,必须划分训练集和验证集。我的划分策略是:训练集20000张,验证集5000张,猫狗各占一半。这个比例对这个小模型来说足够,验证集留大一点,评估结果更稳定。

目录结构如下:

data/ ├── train/ │ ├── cats/ │ └── dogs/ └── validation/ ├── cats/ └── dogs/

划分数据的脚本不复杂,核心就是按文件列表复制图片:

import os import shutil source_dir = 'PetImages' # 原始解压目录 base_dir = 'data' train_dir = os.path.join(base_dir, 'train') val_dir = os.path.join(base_dir, 'validation') cats = os.listdir(os.path.join(source_dir, 'Cat')) dogs = os.listdir(os.path.join(source_dir, 'Dog')) def copy_files(filenames, source_class, target_class, target_dir): os.makedirs(os.path.join(target_dir, target_class), exist_ok=True) for fname in filenames: src = os.path.join(source_dir, source_class, fname) dst = os.path.join(target_dir, target_class, fname) shutil.copyfile(src, dst) # 每个类取前10000张进训练集,后2500张进验证集 copy_files(cats[:10000], 'Cat', 'cats', train_dir) copy_files(dogs[:10000], 'Dog', 'dogs', train_dir) copy_files(cats[10000:12500], 'Cat', 'cats', val_dir) copy_files(dogs[10000:12500], 'Dog', 'dogs', val_dir)

注意一个真实存在的坑:Kaggle原始数据里有些jpg文件其实是损坏的,直接喂给模型训练会报错。最稳妥的做法是训练前用PIL检查一遍,删掉坏图:

from PIL import Image for root, dirs, files in os.walk(base_dir): for file in files: path = os.path.join(root, file) try: img = Image.open(path) img.verify() except Exception: print('删除损坏文件:', path) os.remove(path)

这个步骤看起来多余,但真的能帮你省下后面排查“莫名其妙训练中断”的时间,我记得我当时删掉了三十多张坏图。

2.2 ImageDataGenerator:不做增强就亏了

图片数据不能直接灌进网络,需要统一尺寸,同时做归一化。我用的是Keras的ImageDataGenerator,它最方便的地方是把图片读取、缩放、归一化、batch生成全部封装好了,配合flow_from_directory直接从文件夹读取数据,不用自己写数据加载器。

关键参数说明:

  • rescale=1./255:把像素值从0到255缩放到0到1。神经网络对这样的小数值收敛更快,这是所有图像任务的第一步。
  • validation_split:如果不想手动划目录,也可以直接用这个参数按比例切分,但我更推荐显式划分,逻辑更清楚。
  • 训练集加了数据增强,验证集不做增强,只做rescale。因为验证集要模拟“真实场景”,不能人为扰动。

数据增强的配置:

train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True ) val_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150, 150), batch_size=32, class_mode='categorical' ) val_generator = val_datagen.flow_from_directory( val_dir, target_size=(150, 150), batch_size=32, class_mode='categorical' )

数据增强的作用在网上被很多人一句带过,但我觉得值得说透一点:猫的照片不一定是正着的,可能有旋转、有缩放、有左右翻转。如果模型只能在“规规矩矩”的正方形图片上识别猫,换一张角度刁钻的图就废了。rotation_rangewidth_shift_range这些参数相当于在训练时不断给模型出“变形题”,强迫它学习到“猫的本质特征”,而不是死记硬背某一张图的像素分布。这招对抑制过拟合非常有效,尤其在这个两万张图的小数据集上。

目标尺寸我选的150x150,没有用224x224。原因很简单:模型小、参数少,150x150足够表达猫狗分类所需的空间特征,CPU训练速度也能快不少。后文如果换用迁移学习,才需要把输入尺寸提到224x224去匹配预训练模型。

class_mode='categorical'生成One-hot标签,比如cat对应[1, 0],dog对应[0, 1];如果只想做二分类,也可以改成'binary'配合sigmoid输出,效果差别不大。我做实验报告时用了categorical,因为One-hot编码配合softmax在报告里讲起来更通用,方便扩展到多分类。

3. 卷积神经网络结构设计:小模型也有讲究

3.1 结构设计思路

模型结构我参考了经典VGG的思路,没有做得太深,因为两万张训练图对深层网络来说还是偏少。网络太深容易出现两个问题:一是过拟合,模型把训练集“背”下来了;二是训练时间太长,不利于反复实验。

我的结构是“卷积+池化”重复四组,最后接全连接层和Dropout:

from tensorflow import keras from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense model = keras.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), Dense(512, activation='relu'), Dense(2, activation='softmax') ])

几个结构上的考量值得写进报告:

第一组卷积核从32开始,逐层翻倍到64、128、128,这个做法在多数CNN里很常见。浅层卷积捕捉边缘、颜色块这些低级特征,卷积核可以少一些;深层卷积组合出更抽象的形状特征,需要更多通道来容纳信息。

三层卷积核全部用3x3,而不是5x5或7x7。原因在于两个3x3卷积堆叠的感受野等于一个5x5卷积,但参数量只有后者的18分之25左右,非线性表达还更强。这段在报告中写出来很加分,能体现你确实理解了结构背后的原理。

最后连上512维全连接层前,先加了一个Dropout(0.5)。Dropout在训练时随机“关闭”一半神经元,逼着网络不依赖某几个特定节点,等于做了模型集成,是抑制过拟合的一大杀器。我用的是0.5,这是经验值,太小没效果,太大模型学不动。

输入尺寸150x150x3,这里的3是RGB三个通道。输出层2个神经元用softmax,把输出变成“猫概率+狗概率=1”的分布,取概率大的那个作为预测类别。

3.2 参数量计算与可视化

model.summary()可以直接看到每层参数量:

Total params: 2,905,154 Trainable params: 2,905,154 Non-trainable params: 0

290万参数,放在现在的眼光看非常轻量。全连接层占了其中大头,卷积层主要计算量在浮点运算而非参数量。做实验报告时,我建议把summary的输出截图放进附录,再结合参数量解释:“卷积层通过权值共享大幅减少了参数数量,比如一个3x3卷积核在整张图上滑动,同一组参数被重复使用,而不是每个像素位置都存一套参数。”这句解释比贴一堆公式更容易让老师看出你真的理解。

3.3 为什么不用迁移学习当主力

很多人交作业喜欢直接上VGG16、ResNet50做迁移学习,准确率动辄95%以上。但我这次故意没把迁移学习作为主模型,只作为最后的加分尝试。原因很简单:期末大作业考察的是你是否掌握卷积网络的基本构造和训练流程。如果直接加载预训练权重,核心工作变成“特征提取+微调”,对卷积层本身的原理理解反而被跳过了。先让从零训练的小模型跑到80%多准确率,再谈迁移学习,报告的逻辑链条才完整。

4. 训练过程与参数调优

4.1 编译参数选择

模型编译时我用了Adam优化器,学习率默认0.001。SGD加动量在这个任务上也能收敛,但Adam自适应调整每个参数的学习率,省去了手动调学习率表的大量时间,对小模型来说非常省心。损失函数用categorical_crossentropy,和One-hot标签配对。

model.compile( loss='categorical_crossentropy', optimizer=keras.optimizers.Adam(learning_rate=0.001), metrics=['accuracy'] )

4.2 回调函数:训练不用时刻盯着

训练过程里我挂了三个回调:

from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau checkpoint = ModelCheckpoint( 'cats_dogs_model.h5', monitor='val_accuracy', save_best_only=True ) early_stop = EarlyStopping( monitor='val_loss', patience=5, restore_best_weights=True ) reduce_lr = ReduceLROnPlateau( monitor='val_loss', factor=0.2, patience=3, min_lr=1e-6 )

这三个回调的功能分别说清楚:

ModelCheckpoint会在每个epoch结束后检查验证集准确率,只有比之前最好的一轮更好才保存模型。这样就算后面过拟合了,磁盘上也留着一个最佳模型。

EarlyStopping是防过拟合的保险丝。验证集loss连续5个epoch不下降就自动停止训练,并恢复到验证集最好的那一次权重。我设置epochs=30,但实际跑下来往往只训练十几轮就停了,原因就是验证loss不再下降。这在报告里属于正常现象,不是bug。

ReduceLROnPlateau配合EarlyStopping使用,验证loss连续3轮不下降时,学习率缩小为原来的五分之一。训练后期损失曲线会进入平台期,固定的大学习率会在最优点附近来回震荡,减小学习率可以让loss继续优化到更深处。

训练代码:

history = model.fit( train_generator, steps_per_epoch=20000 // 32, epochs=30, validation_data=val_generator, validation_steps=5000 // 32, callbacks=[checkpoint, early_stop, reduce_lr] )

steps_per_epoch填的是每个epoch用多少个batch。我这里显式写了20000除以32,等于625个batch走完一遍训练集。如果不填,Keras会自动推断,但填上能让报告里的参数关系更清楚。

4.3 训练日志解读

训练时的输出大致长这样:

Epoch 1/30 625/625 [==============================] - 60s 96ms/step - loss: 0.6895 - accuracy: 0.5261 - val_loss: 0.6501 - val_accuracy: 0.6200 Epoch 2/30 625/625 [==============================] - 58s 93ms/step - loss: 0.6214 - accuracy: 0.6470 - val_loss: 0.5802 - val_accuracy: 0.6861 Epoch 3/30 625/625 [==============================] - 58s 92ms/step - loss: 0.5554 - accuracy: 0.7151 - val_loss: 0.5318 - val_accuracy: 0.7358 ... Epoch 12/30 625/625 [==============================] - 58s 93ms/step - loss: 0.3547 - accuracy: 0.8350 - val_loss: 0.3407 - val_accuracy: 0.8630

第一个epoch准确率只有0.52左右,跟随机猜差不多,正常。到第三个epoch就明显涨了,这说明模型确实在学,而不是卡在欠拟合。最终验证集准确率停在86%上下,是一个从零训练小模型的合理水平。

训练8GB显存大约每epoch一分钟,CPU的话大约五分钟,时间成本可以接受。我开头说过没有GPU也能跑到这个准确率,只是需要耐心,亲测可行。

5. 结果评估:曲线、混淆矩阵和单张图预测

5.1 训练曲线怎么判读

训练结束后,把history里的数据画成曲线是实验报告必备环节:

import matplotlib.pyplot as plt acc = history.history['accuracy'] val_acc = history.history['val_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(acc) + 1) plt.plot(epochs, acc, 'b', label='Training acc') plt.plot(epochs, val_acc, 'r', label='Validation acc') plt.title('Training and validation accuracy') plt.legend() plt.show() plt.plot(epochs, loss, 'b', label='Training loss') plt.plot(epochs, val_loss, 'r', label='Validation loss') plt.title('Training and validation loss') plt.legend() plt.show()

图上最值得关注的不是最终数值,而是两条曲线的距离。理想状态是训练曲线和验证曲线贴得比较近,说明模型在“举一反三”。如果训练准确率坐到95%,验证准确率还趴在80%,那就是典型的过拟合——模型把训练图背下来了,遇到没见过的图就露馅。

我的实验里两条曲线始终比较贴近,这说明数据增强加Dropout的组合拳打对了。在报告里你可以重点描述这个现象,再补充一句:“数据增强让同一条数据在不同epoch以不同形态出现,相当于暗中扩大了训练集规模,这是缓解过拟合的关键。”

5.2 混淆矩阵算清楚每种错误

准确率只是一个粗粒度指标,想要知道模型具体错在哪里,要画混淆矩阵:

import numpy as np from sklearn.metrics import confusion_matrix val_generator.reset() pred = model.predict(val_generator) pred_classes = np.argmax(pred, axis=1) true_classes = val_generator.classes cm = confusion_matrix(true_classes, pred_classes) print(cm)

得到的矩阵类似:

[[1201 49] [ 74 1176]]

第一行是真实猫,第二行是真实狗。对角线是预测正确的数量,上三角是“把猫认成狗”,下三角是“把狗认成猫”。从数字上看,模型把狗认成猫的次数(74)比把猫认成狗(49)多一些。这说明模型学到的“猫特征”泛化得更好一点,可能因为猫脸轮廓和耳朵形状的共性更强。这个细节写进报告,老师会觉得你不只是跑了个模型,而是真的在分析结果。

5.3 单张图片预测展示

最后在报告里放几张真实照片,跑一下预测,肉眼确认效果:

from tensorflow.keras.preprocessing import image def predict_image(img_path): img = image.load_img(img_path, target_size=(150, 150)) img_tensor = image.img_to_array(img) / 255.0 img_tensor = np.expand_dims(img_tensor, axis=0) pred = model.predict(img_tensor, verbose=0) class_names = ['cat', 'dog'] label = class_names[np.argmax(pred)] confidence = np.max(pred) print(f'图片路径: {img_path}, 预测类别: {label}, 置信度: {confidence:.4f}') return label, confidence

我自己试了几张网络上找的猫狗图,大部分预测正确,置信度在0.8到0.99之间。偶尔有一张黑白老照片里的猫被认错,这很合理,因为训练集里彩色图片占绝大多数,灰度图像特征分布偏离了训练分布。遇到这种情况,可以在报告里如实写出来,然后分析原因,比只展示成功案例更有说服力。

6. 整个实验里踩过的坑和可以加分的改进方向

6.1 我实际遇到过的几个问题

训练过程中我先后遇到过四个比较有代表性的问题,逐个说下原因和解决办法。

第一个是第一次训练没用数据增强,验证准确率卡在78%上下,训练准确率却一路飙到93%。这就是典型的过拟合。加了增强后,验证准确率直接跳到85%以上,效果立竿见影。如果你的模型也出现“训练高、验证低”的剪刀差,优先怀疑数据量不够,而不是模型太弱。

第二个坑是flow_from_directory的shuffle参数。训练生成器默认shuffle=True,但验证生成器默认不shuffle。在预测和算混淆矩阵时,验证生成器的顺序固定,但predict返回的预测顺序和generator.classes的顺序是对应的,前提是你不要在中途reset两次导致顺序错乱。我建议在预测前先执行一次val_generator.reset(),保证从头开始算。

第三个问题是学习率设太大,loss变成NaN。这是新手最容易懵的情况。解决办法是先把学习率降到0.0001重新训练,或者加梯度裁剪。如果发现某一步loss突然变成nan,不用重头再来,把学习率调小后再编译、再fit几轮通常就能恢复。

第四个问题是保存模型后加载报错。如果你在模型里用了自定义层或自定义函数,model.save()默认的.h5格式可能没法直接load_model。这次实验都是标准层,没遇到,但我后来做迁移学习时自定义了层,踩过这个坑,印象很深。标准模型的保存用model.save('文件名.h5')即可,加载用keras.models.load_model,不用踩别的坑。

6.2 把报告从80分提到90分的几个方向

如果时间允许,下面几个方向对分数提升非常明显:

  • 迁移学习对比实验:用VGG16预训练权重做特征提取,冻结卷积层只训练顶部分类器,准确率基本能到95%以上。在报告里加一节“从零训练与迁移学习的对比”,体现你对两种方案的权衡有认识。
  • 数据清洗和统计:统计训练集里猫图、狗图的尺寸分布和通道分布,分析模型可能对哪些图片效果差,比如低亮度、模糊、多只动物同时出现。这些分析不需要额外代码,写好就是亮点。
  • 模型结构可视化:用keras.utils.plot_model画出网络结构图,配上model.summary()输出,图文并茂,符合“图文报告”的要求。
  • 超参数敏感性实验:固定其他变量,只修改batch_size或dropout比例,看准确率怎么变化。哪怕只做两个设置,报告也会显得有实验设计能力。

最后再分享一点我个人的体会:深度学习课程作业最重要的不是准确率数字好看,而是把每一个环节的“为什么”讲清楚。猫狗分类听起来老套,但当我真的从数据准备、模型搭建、训练调参一路走下来,才明白课程里那些概念在真实项目里是怎么咬合在一起的。整个过程给我最大的收获是“训练深度学习模型不是一个一键完成的黑盒操作,而是一条需要在每个环节做决策的流水线”。希望你做完这个实验后,也能有同样的感受,那这篇报告的价值就真正达到了。

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

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

猫抓浏览器资源嗅探扩展完整指南:网页视频音频图片一键抓下

猫抓浏览器资源嗅探扩展完整指南:网页视频音频图片一键抓下 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 公开课播放页点"视频另…

作者头像 李华
网站建设 2026/9/6 21:14:56

多目标粒子群优化算法在直流能量路由器动态调控中的工程实践

简介:大功率直流能量路由器是直流能源网络的核心设备,本资源围绕其动态调控策略与优化算法展开系统研究,面向智能电网、新能源并网、数据中心供电等领域的科研人员与工程师。文档从能量路由器的定义、功能与发展历程切入,深入分析…

作者头像 李华
网站建设 2026/9/6 21:11:47

华为BLM战略规划:拆解84页PPT,打通战略到执行的闭环

简介:华为BLM战略规划方法论PPT(84页)是一套围绕业务领导力模型的系统性培训课件,面向企业中高层管理者、战略规划人员及OD/HR从业者,解决战略制定与战略执行脱节的问题。整包仅1个PPT文件,体积4.29MB&…

作者头像 李华
网站建设 2026/9/6 21:10:04

Wand-Enhancer:10分钟解锁WeMod时长限制

Wand-Enhancer:10分钟解锁WeMod时长限制 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer Wand-Enhancer 是一个开源的 WeMod 本地增强工…

作者头像 李华
网站建设 2026/9/6 21:09:41

如何 5 分钟快速上手 Cua:AI 操作 macOS 桌面的完整指南

如何 5 分钟快速上手 Cua:AI 操作 macOS 桌面的完整指南 【免费下载链接】cua Scale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation. 项目地址: https://gitcode.com/GitHub_Trend…

作者头像 李华