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

发布时间:2026/9/6 21:24:21
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.04Windows 10也可以命令基本一致Python3.9TensorFlow2.10.0Keras使用TensorFlow内置的tf.kerasJupyter Notebook用于逐步记录和可视化CPU/GPU有NVIDIA显卡更好没有也可以跑安装命令很简单pip install tensorflow2.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_okTrue) 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直接从文件夹读取数据不用自己写数据加载器。关键参数说明rescale1./255把像素值从0到255缩放到0到1。神经网络对这样的小数值收敛更快这是所有图像任务的第一步。validation_split如果不想手动划目录也可以直接用这个参数按比例切分但我更推荐显式划分逻辑更清楚。训练集加了数据增强验证集不做增强只做rescale。因为验证集要模拟“真实场景”不能人为扰动。数据增强的配置train_datagen ImageDataGenerator( rescale1./255, rotation_range40, width_shift_range0.2, height_shift_range0.2, shear_range0.2, zoom_range0.2, horizontal_flipTrue ) val_datagen ImageDataGenerator(rescale1./255) train_generator train_datagen.flow_from_directory( train_dir, target_size(150, 150), batch_size32, class_modecategorical ) val_generator val_datagen.flow_from_directory( val_dir, target_size(150, 150), batch_size32, class_modecategorical )数据增强的作用在网上被很多人一句带过但我觉得值得说透一点猫的照片不一定是正着的可能有旋转、有缩放、有左右翻转。如果模型只能在“规规矩矩”的正方形图片上识别猫换一张角度刁钻的图就废了。rotation_range、width_shift_range这些参数相当于在训练时不断给模型出“变形题”强迫它学习到“猫的本质特征”而不是死记硬背某一张图的像素分布。这招对抑制过拟合非常有效尤其在这个两万张图的小数据集上。目标尺寸我选的150x150没有用224x224。原因很简单模型小、参数少150x150足够表达猫狗分类所需的空间特征CPU训练速度也能快不少。后文如果换用迁移学习才需要把输入尺寸提到224x224去匹配预训练模型。class_modecategorical生成One-hot标签比如cat对应[1, 0]dog对应[0, 1]如果只想做二分类也可以改成binary配合sigmoid输出效果差别不大。我做实验报告时用了categorical因为One-hot编码配合softmax在报告里讲起来更通用方便扩展到多分类。3. 卷积神经网络结构设计小模型也有讲究3.1 结构设计思路模型结构我参考了经典VGG的思路没有做得太深因为两万张训练图对深层网络来说还是偏少。网络太深容易出现两个问题一是过拟合模型把训练集“背”下来了二是训练时间太长不利于反复实验。我的结构是“卷积池化”重复四组最后接全连接层和Dropoutfrom tensorflow import keras from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense model keras.Sequential([ Conv2D(32, (3, 3), activationrelu, input_shape(150, 150, 3)), MaxPooling2D(2, 2), Conv2D(64, (3, 3), activationrelu), MaxPooling2D(2, 2), Conv2D(128, (3, 3), activationrelu), MaxPooling2D(2, 2), Conv2D(128, (3, 3), activationrelu), MaxPooling2D(2, 2), Flatten(), Dropout(0.5), Dense(512, activationrelu), Dense(2, activationsoftmax) ])几个结构上的考量值得写进报告第一组卷积核从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: 0290万参数放在现在的眼光看非常轻量。全连接层占了其中大头卷积层主要计算量在浮点运算而非参数量。做实验报告时我建议把summary的输出截图放进附录再结合参数量解释“卷积层通过权值共享大幅减少了参数数量比如一个3x3卷积核在整张图上滑动同一组参数被重复使用而不是每个像素位置都存一套参数。”这句解释比贴一堆公式更容易让老师看出你真的理解。3.3 为什么不用迁移学习当主力很多人交作业喜欢直接上VGG16、ResNet50做迁移学习准确率动辄95%以上。但我这次故意没把迁移学习作为主模型只作为最后的加分尝试。原因很简单期末大作业考察的是你是否掌握卷积网络的基本构造和训练流程。如果直接加载预训练权重核心工作变成“特征提取微调”对卷积层本身的原理理解反而被跳过了。先让从零训练的小模型跑到80%多准确率再谈迁移学习报告的逻辑链条才完整。4. 训练过程与参数调优4.1 编译参数选择模型编译时我用了Adam优化器学习率默认0.001。SGD加动量在这个任务上也能收敛但Adam自适应调整每个参数的学习率省去了手动调学习率表的大量时间对小模型来说非常省心。损失函数用categorical_crossentropy和One-hot标签配对。model.compile( losscategorical_crossentropy, optimizerkeras.optimizers.Adam(learning_rate0.001), metrics[accuracy] )4.2 回调函数训练不用时刻盯着训练过程里我挂了三个回调from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau checkpoint ModelCheckpoint( cats_dogs_model.h5, monitorval_accuracy, save_best_onlyTrue ) early_stop EarlyStopping( monitorval_loss, patience5, restore_best_weightsTrue ) reduce_lr ReduceLROnPlateau( monitorval_loss, factor0.2, patience3, min_lr1e-6 )这三个回调的功能分别说清楚ModelCheckpoint会在每个epoch结束后检查验证集准确率只有比之前最好的一轮更好才保存模型。这样就算后面过拟合了磁盘上也留着一个最佳模型。EarlyStopping是防过拟合的保险丝。验证集loss连续5个epoch不下降就自动停止训练并恢复到验证集最好的那一次权重。我设置epochs30但实际跑下来往往只训练十几轮就停了原因就是验证loss不再下降。这在报告里属于正常现象不是bug。ReduceLROnPlateau配合EarlyStopping使用验证loss连续3轮不下降时学习率缩小为原来的五分之一。训练后期损失曲线会进入平台期固定的大学习率会在最优点附近来回震荡减小学习率可以让loss继续优化到更深处。训练代码history model.fit( train_generator, steps_per_epoch20000 // 32, epochs30, validation_dataval_generator, validation_steps5000 // 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, labelTraining acc) plt.plot(epochs, val_acc, r, labelValidation acc) plt.title(Training and validation accuracy) plt.legend() plt.show() plt.plot(epochs, loss, b, labelTraining loss) plt.plot(epochs, val_loss, r, labelValidation 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, axis1) 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, axis0) pred model.predict(img_tensor, verbose0) 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参数。训练生成器默认shuffleTrue但验证生成器默认不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比例看准确率怎么变化。哪怕只做两个设置报告也会显得有实验设计能力。最后再分享一点我个人的体会深度学习课程作业最重要的不是准确率数字好看而是把每一个环节的“为什么”讲清楚。猫狗分类听起来老套但当我真的从数据准备、模型搭建、训练调参一路走下来才明白课程里那些概念在真实项目里是怎么咬合在一起的。整个过程给我最大的收获是“训练深度学习模型不是一个一键完成的黑盒操作而是一条需要在每个环节做决策的流水线”。希望你做完这个实验后也能有同样的感受那这篇报告的价值就真正达到了。本文还有配套的精品资源点击获取

相关新闻