简介:本资源是一套完整的基于Python卷积神经网络(CNN)的猫狗图像分类实战项目,专为计算机相关专业本科生毕业设计、课程设计及期末大作业打造,兼顾理论理解与工程落地能力训练。项目经导师指导并高分通过(评审98分),源码全部本地实测可运行,含数据预处理、模型构建(TensorFlow/Keras实现)、训练调优、评估可视化等完整流程,适合中等难度AI项目实践需求。压缩包共2000个文件,主体为1992张标注清晰的猫狗原始图像(jpg)、7个功能明确的Python脚本(含train.py、predict.py、model.py等核心模块)及1份项目说明文档(md),总大小86.82MB,结构规范、注释充分,便于学习者逐模块理解与复现。目前已有204人学习下载,配套数据集覆盖常见姿态与光照变化,代码已通过严格调试,附带典型样本预览(如cat.835.jpg、dog.157.jpg等),可直接用于模型训练与效果验证。
1. 项目缘起:从“人狗大作战”到正经的猫狗分类器
最近在逛一些技术社区的时候,经常能看到“人狗大作战Python代码2023”这类标题,点进去一看,大多是一些用基础OpenCV做的简单轮廓识别或者颜色检测,离真正的“智能识别”还差得远。这让我想起几年前自己刚入门深度学习时,做的第一个正经项目就是猫狗图像分类。这个项目堪称是CV(计算机视觉)领域的“Hello World”,但它麻雀虽小,五脏俱全,涵盖了数据准备、模型构建、训练、评估和部署的完整流程。今天,我就把这个项目的完整源码、数据集处理心得以及一路踩过的坑,系统地梳理一遍。无论你是刚装好Python环境的新手,还是想深入理解CNN(卷积神经网络)工作原理的开发者,这篇长文都能给你提供一个从零到一、可复现的高分项目实践。
这个项目的核心目标很简单:训练一个模型,让它能准确区分一张图片里的是猫还是狗。你别看这问题听起来简单,在2013年,这曾是Kaggle上的一场著名竞赛,顶尖团队的准确率也就刚过80%。如今,借助成熟的深度学习框架和更优的模型结构,我们轻松就能达到95%以上的准确率。但这其中的门道,比如数据怎么清洗、模型结构怎么设计、训练过程怎么调优,才是我们真正要掌握的核心技能。接下来,我会手把手带你走完整个流程,并重点解释每一步“为什么要这么做”。
2. 环境搭建与数据集的“第一道坎”
工欲善其事,必先利其器。在开始写代码之前,一个稳定、兼容的环境是成功的基石。很多新手卡在第一步,就是因为环境配置五花八门,包版本冲突层出不穷。
2.1 Python环境与核心库的“黄金组合”
我强烈建议使用Anaconda来管理Python环境,它能很好地解决不同项目间依赖隔离的问题。别直接在你的系统Python或者基础环境里折腾,新建一个专用于本项目的环境是明智之举。
# 创建一个名为 cat_dog_cnn 的 Python 3.8 环境(3.8是一个兼容性很好的版本) conda create -n cat_dog_cnn python=3.8 conda activate cat_dog_cnn接下来安装核心的深度学习库。这里有一个版本组合,是我经过多个项目验证后最稳定的一套,能有效避免一些令人头疼的“玄学”错误。
# 安装 TensorFlow 2.x, 这是我们的主要深度学习框架 # 如果你有NVIDIA显卡并配置了CUDA,可以安装GPU版本以加速训练,否则安装CPU版本 pip install tensorflow==2.10.0 # CPU版本 # 或者 pip install tensorflow-gpu==2.10.0 # GPU版本(需提前安装对应版本的CUDA和cuDNN) # 安装图像处理和数据处理的必备库 pip install opencv-python==4.7.0.72 pip install pillow==9.5.0 pip install matplotlib==3.7.1 pip install scikit-learn==1.2.2 pip install pandas==1.5.3 pip install numpy==1.24.3注意:TensorFlow 2.10.0 对 Python 3.8-3.10 支持良好。如果你遇到安装问题,一个常见的“退路”是安装
tensorflow-cpu,它更轻量且兼容性极佳,只是训练速度会慢一些。对于学习目的,完全够用。
2.2 数据集获取与初探:不止是下载那么简单
猫狗大战的数据集在网上很容易找到,一个经典的来源是Kaggle。数据集通常包含两个文件夹:train和test。train文件夹里会有上万张命名为cat.0.jpg,dog.0.jpg的图片。
拿到数据后,千万别急着往模型里喂。第一步永远是探索性数据分析。用几行代码看看数据长什么样:
import os import matplotlib.pyplot as plt train_dir = './data/train' cat_files = [f for f in os.listdir(train_dir) if f.startswith('cat')] dog_files = [f for f in os.listdir(train_dir) if f.startswith('dog')] print(f"训练集猫图片数量: {len(cat_files)}") print(f"训练集狗图片数量: {len(dog_files)}") # 检查一下图片尺寸,这很重要! from PIL import Image sample_path = os.path.join(train_dir, cat_files[0]) with Image.open(sample_path) as img: print(f"样本图片尺寸: {img.size}") # 通常是 (宽, 高)你可能会发现,图片尺寸五花八门,从几百乘几百到上千像素的都有。这是图像分类任务中第一个要处理的问题:输入尺寸必须统一。CNN的全连接层要求固定的输入维度。同时,样本数量是否均衡?猫和狗的照片数量如果相差很大(比如猫有9000张,狗只有6000张),模型可能会偏向于数量多的类别。经典的数据集通常是均衡的,但自己收集的数据往往需要做平衡处理。
2.3 数据预处理流水线:用ImageDataGenerator实现标准化与增强
原始图片不能直接输入网络。我们需要一个预处理流水线,它主要做三件事:重设尺寸、像素值归一化、数据增强。TensorFlow/Keras 提供的ImageDataGenerator是完成这些工作的利器,它能在训练时实时生成增强后的批次数据,高效利用内存。
from tensorflow.keras.preprocessing.image import ImageDataGenerator # 定义训练数据生成器,并加入数据增强策略 train_datagen = ImageDataGenerator( rescale=1./255, # 将像素值从0-255缩放到0-1之间,这是必须的,能加速模型收敛 rotation_range=20, # 随机旋转角度范围(度) width_shift_range=0.2, # 随机水平平移范围(占总宽度的比例) height_shift_range=0.2, # 随机垂直平移范围 shear_range=0.2, # 随机错切变换强度 zoom_range=0.2, # 随机缩放范围 horizontal_flip=True, # 随机水平翻转(对猫狗识别很有效,因为物体没有固定的左右朝向) fill_mode='nearest' # 填充新创建像素的方法 ) # 验证集和测试集只需要做缩放,不需要数据增强! # 因为我们要评估模型在真实、未变形的数据上的表现 val_test_datagen = ImageDataGenerator(rescale=1./255) # 从目录创建数据流 train_generator = train_datagen.flow_from_directory( './data/train', target_size=(150, 150), # 将所有图像调整为150x150像素 batch_size=32, # 每个批次的图像数量 class_mode='binary' # 因为是二分类(猫/狗),所以用二进制标签 ) validation_generator = val_test_datagen.flow_from_directory( './data/validation', # 你需要从训练集中划分一部分作为验证集 target_size=(150, 150), batch_size=32, class_mode='binary' )这里有几个关键点:
rescale=1./255:这是最关键的一步。原始图像像素是0-255的整数,而神经网络在0-1范围内的小数上工作得更好,收敛更快。- 数据增强参数:
rotation_range,shift_range这些值不是随便设的。设置太小,增强效果不明显;设置太大,图片变得面目全非,模型可能学不到有效特征。对于猫狗分类,20%左右的平移和旋转是合理的起点。horizontal_flip对这类任务几乎总是有益的。 target_size:为什么选150x150?这是一个权衡。更大的尺寸(如224x224)能保留更多细节,但会显著增加模型计算量和内存占用。更小的尺寸(如64x64)训练快,但可能丢失关键特征。150x150是一个在普通电脑上也能流畅训练,且效果不错的折中选择。- 验证集:务必从训练数据中分出一部分(比如20%)作为验证集,用于在训练过程中监控模型在未见过的数据上的表现,防止过拟合。
flow_from_directory假设你的目录结构是class1/img1.jpg, class2/img1.jpg这样的,它会自动根据文件夹名分配标签。
3. 卷积神经网络模型构建:从“搭积木”到理解每一层的作用
现在来到核心部分——构建CNN模型。很多人一开始就想着用ResNet、EfficientNet这些复杂模型,但对于猫狗分类,一个自己搭建的、结构清晰的简单CNN,更能帮助你理解原理。我们从头开始搭建一个。
3.1 模型结构设计:四层卷积的经典范式
下面是一个经典的、用于中等尺寸图像分类的CNN结构。我会逐层解释其作用和参数含义。
from tensorflow.keras import layers, models model = models.Sequential([ # 第一卷积块:提取低级特征(边缘、角落、纹理) layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)), layers.MaxPooling2D((2, 2)), # 第二卷积块:组合低级特征,形成更复杂的模式(如眼睛、鼻子轮廓) layers.Conv2D(64, (3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), # 第三卷积块:进一步抽象,捕捉对象部件 layers.Conv2D(128, (3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), # 第四卷积块:为分类做准备的高层语义特征 layers.Conv2D(128, (3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), # 将三维特征图展平成一维向量,以便输入全连接层 layers.Flatten(), # 全连接层(又称密集层),进行高级推理 # 这里加入Dropout,随机丢弃50%的神经元,是防止过拟合的强力手段 layers.Dense(512, activation='relu'), layers.Dropout(0.5), # 输出层:二分类,所以用一个神经元,sigmoid激活函数输出0到1之间的概率值 # 0代表猫,1代表狗(根据flow_from_directory的标签顺序而定) layers.Dense(1, activation='sigmoid') ]) # 看一下模型的结构摘要,确保每一层的输出维度符合预期 model.summary()逐层拆解与“为什么”:
Conv2D(32, (3, 3), activation='relu'):32:这是该层卷积核(过滤器)的数量。你可以理解为有32个不同的“特征探测器”,每个探测器负责扫描图像,寻找一种特定的模式(如垂直边缘、45度纹理等)。第一个卷积层通常用较少的过滤器(32或64),因为低级特征种类有限。(3, 3):卷积核的大小,即3x3的滑动窗口。这是最常用的尺寸,在感受野和参数数量之间取得平衡。更大的核(如5x5)能捕获更大范围的上下文,但参数更多,容易过拟合;更小的核(1x1)常用于降维或组合特征。activation='relu':整流线性单元。这是目前最常用的激活函数,它给网络引入了非线性。没有它,多层网络堆叠的效果就等同于一层线性变换,无法学习复杂模式。ReLU的公式是f(x) = max(0, x),计算简单且能缓解梯度消失问题。input_shape=(150, 150, 3):仅在模型第一层需要指定。150x150是图片尺寸,3是颜色通道(RGB)。
MaxPooling2D((2, 2)):- 池化层,这里用的是最大池化,窗口大小2x2,步长默认为2。它的作用是在保留最显著特征的同时,对特征图进行下采样,将尺寸减半(例如从150x150 -> 75x75)。
- 为什么需要池化?主要有两个原因:一是减少后续层的计算量和参数数量;二是提供了一定程度的平移不变性。即使猫的脸在图片中稍微移动了一点,最大池化仍然可能捕捉到眼睛这个强特征。
过滤器数量逐层翻倍:从32到64再到128。这是一种常见的设计模式。随着网络加深,特征图的空间尺寸(宽高)在减小(因为池化),但深度(通道数)在增加。这意味着网络正在从广泛的、低级的特征(很多位置,少数特征类型)转换到集中的、高级的语义特征(少数位置,很多特征类型)。
Flatten():卷积层输出的是三维张量(高度,宽度,通道数)。全连接层需要一维向量作为输入。Flatten层就是把这个三维块“拍平”成一长条。Dense(512, activation='relu'):全连接层。所有512个神经元都与上一层的所有输入相连,进行全局的综合判断。512是一个经验值,可以根据任务复杂度调整。Dropout(0.5):在训练期间,随机将这一层50%的神经元输出置为零。这是一种正则化技术,强迫网络不依赖于任何单个神经元,而是学习更鲁棒、更分散的特征表示,是防止模型在训练集上表现太好(过拟合)而在验证集上表现差的利器。Dense(1, activation='sigmoid'):输出层。因为是二分类,所以只需要一个神经元。sigmoid函数将神经元的输出压缩到(0,1)区间,可以解释为“是狗的概率”。
3.2 模型编译:选择“方向”和“速度计”
构建好模型结构后,需要告诉模型如何学习,即编译。
model.compile( loss='binary_crossentropy', # 损失函数:衡量模型预测值与真实标签的差距 optimizer='adam', # 优化器:决定如何根据损失来更新权重 metrics=['accuracy'] # 评估指标:我们关心分类准确率 )- 损失函数
binary_crossentropy(二元交叉熵):这是二分类任务的标准损失函数。它衡量的是模型预测的概率分布与真实标签分布之间的差异。公式可能有点复杂,但直观理解就是:当模型对正确类别的预测概率越接近1,损失越小;越接近0,损失越大。 - 优化器
adam:目前最流行、默认效果很好的优化器。它结合了Momentum(动量,帮助冲出局部最优点)和RMSProp(自适应学习率,为每个参数调整更新幅度)的优点。对于初学者,无脑用Adam通常能得到不错的结果。如果你想更精细控制,可以调整其学习率:optimizer=tf.keras.optimizers.Adam(learning_rate=0.001)。 - 评估指标
accuracy:监控训练过程的指标。准确率对均衡数据集很友好。如果数据不均衡,可能需要加入Precision(精确率)、Recall(召回率)或AUC。
4. 模型训练与监控:在迭代中寻找最佳状态
准备工作全部就绪,现在可以开始训练模型了。训练不是简单地跑完轮数,而是一个需要密切监控和调整的过程。
4.1 执行训练并保存最佳模型
我们使用fit方法,并引入两个非常重要的回调函数。
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping # 回调函数1:模型检查点。只在验证集准确率提升时保存模型权重。 checkpoint_cb = ModelCheckpoint( 'best_cat_dog_model.h5', # 保存的文件名 monitor='val_accuracy', # 监控验证集准确率 save_best_only=True, # 只保存最好的那个 mode='max', # 因为监控的是准确率,要最大化 verbose=1 # 打印保存信息 ) # 回调函数2:早停。如果验证集损失在连续若干轮(耐心值)内不再下降,则停止训练,防止过拟合和资源浪费。 early_stopping_cb = EarlyStopping( monitor='val_loss', # 监控验证集损失 patience=10, # 容忍轮数 restore_best_weights=True, # 停止时,恢复为最佳epoch的权重 verbose=1 ) # 开始训练! history = model.fit( train_generator, steps_per_epoch=100, # 每个epoch从生成器中抽取100个批次(32*100=3200张图) epochs=50, # 总共训练50轮 validation_data=validation_generator, validation_steps=50, # 每个epoch用50个批次做验证(32*50=1600张图) callbacks=[checkpoint_cb, early_stopping_cb], # 加入回调 verbose=1 )关键参数解析:
steps_per_epoch和validation_steps:因为我们的数据是从生成器无限流出的,必须手动指定每个epoch看多少批数据。通常设置为总样本数 // batch_size。这里设为100和50是为了演示。epochs:设置一个较大的值,比如50或100。配合EarlyStopping回调,实际训练轮数可能会提前停止。callbacks:这是训练中的“智能管家”。ModelCheckpoint确保我们得到的是在验证集上表现最好的模型,而不是最后一轮可能已经过拟合的模型。EarlyStopping能自动判断何时停止训练,节省时间。
4.2 可视化训练过程:诊断模型的“健康状态”
训练结束后,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 = range(len(acc)) # 实际训练的轮数 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(epochs_range, acc, label='Training Accuracy') plt.plot(epochs_range, val_acc, label='Validation Accuracy') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy') plt.subplot(1, 2, 2) plt.plot(epochs_range, loss, label='Training Loss') plt.plot(epochs_range, val_loss, label='Validation Loss') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.show()通过这张图,你可以诊断出模型训练的几种典型情况:
- 健康训练:训练和验证的准确率同步上升,损失同步下降,最终都趋于平稳。两者之间的差距很小。
- 过拟合:训练准确率持续升高,但验证准确率在达到某个点后开始停滞甚至下降。训练损失持续下降,但验证损失在某个点后开始上升。这意味着模型“死记硬背”了训练数据,但泛化能力差。解决方案:增加Dropout比率、增加数据增强强度、获取更多数据、简化模型结构。
- 欠拟合:训练和验证的准确率都很低,且很早就停滞不前。这意味着模型能力不足,无法捕捉数据中的模式。解决方案:增加模型复杂度(更多层、更多过滤器)、训练更长时间、减少正则化强度。
- 训练不稳定:曲线剧烈抖动。可能是学习率设置得太高,尝试降低
Adam优化器的学习率。
5. 模型评估、预测与实战中的“坑”
训练完成并保存了最佳模型后,我们就要在真正的测试集上检验其成色,并学习如何用它进行单张图片预测。
5.1 加载模型与最终测试
from tensorflow.keras.models import load_model # 加载我们保存的最佳模型 best_model = load_model('best_cat_dog_model.h5') # 准备测试集生成器(和验证集一样,只做缩放,不做增强) test_dir = './data/test' # 假设你的测试集也按猫狗分好了类 test_generator = val_test_datagen.flow_from_directory( test_dir, target_size=(150, 150), batch_size=32, class_mode='binary', shuffle=False # 测试时不需要打乱顺序,方便后续对应标签 ) # 在测试集上进行最终评估 test_loss, test_accuracy = best_model.evaluate(test_generator, steps=len(test_generator)) print(f"\n测试集损失: {test_loss:.4f}") print(f"测试集准确率: {test_accuracy:.4f}")如果测试准确率与验证准确率接近,说明模型的泛化能力不错。如果测试准确率显著低于验证准确率,可能意味着验证集的划分不够随机,或者测试集的数据分布与训练/验证集有差异。
5.2 单张图片预测与常见问题处理
在实际应用中,我们更多是对单张未知图片进行预测。这里有几个细节需要注意。
import numpy as np from tensorflow.keras.preprocessing import image def predict_single_image(img_path, model): """ 预测单张图片是猫还是狗 Args: img_path: 图片文件路径 model: 加载好的Keras模型 Returns: pred_label: 预测标签 ('cat' 或 'dog') confidence: 预测为狗的概率 """ # 1. 加载图片,并调整到模型需要的尺寸 img = image.load_img(img_path, target_size=(150, 150)) # 2. 将PIL图像转换为NumPy数组,形状为(150, 150, 3) img_array = image.img_to_array(img) # 3. 添加一个批次维度,因为模型期望的输入是 (batch_size, height, width, channels) img_array = np.expand_dims(img_array, axis=0) # 4. 进行与训练时相同的缩放(非常重要!) img_array /= 255.0 # 5. 进行预测 prediction = model.predict(img_array, verbose=0) # verbose=0不显示预测进度条 # prediction 是一个形如 [[0.876]] 的数组,代表是狗的概率 confidence = prediction[0][0] # 6. 根据概率输出结果 # 注意:这里需要和训练时flow_from_directory的class_indices顺序一致 # 通常,索引0是第一个按字母顺序排列的文件夹。假设文件夹是'cat'和'dog',则cat是0,dog是1。 # 因为我们用的是binary输出和sigmoid,输出>0.5通常被认为是狗。 if confidence > 0.5: pred_label = 'dog' confidence = confidence # 是狗的概率 else: pred_label = 'cat' confidence = 1 - confidence # 是猫的概率 = 1 - 是狗的概率 return pred_label, confidence # 使用示例 img_path = './my_test_photo.jpg' label, prob = predict_single_image(img_path, best_model) print(f"预测结果: {label}, 置信度: {prob:.2%}")实战中踩过的坑与心得:
预处理一致性陷阱:这是最常见的错误!训练时怎么处理图片,预测时就必须一模一样地处理。训练时用了
rescale=1./255,预测时也必须对像素除以255。训练时图片是BGR顺序(如果用了OpenCV的cv2.imread)还是RGB顺序(PIL默认)?必须统一。我强烈建议在训练和预测的整个流程中,都使用Keras的image模块或TensorFlow的tf.keras.preprocessing来处理图片,以保证一致性。批次维度:模型训练时输入是
(batch_size, height, width, channels)。预测单张图片时,必须用np.expand_dims(img_array, axis=0)将其变成(1, height, width, channels),否则会报维度错误。标签映射:
flow_from_directory会自动根据文件夹名称的字母顺序创建标签映射(例如,{'cat': 0, 'dog': 1})。你在单张图片预测后,需要按照这个映射来解读结果。如果你的文件夹名是dogs和cats,那映射可能就是{'cats': 0, 'dogs': 1}。可以在生成器创建后打印train_generator.class_indices来确认。处理非标准图片:网络上的图片千奇百怪,可能有四通道的PNG(带透明度),也可能是灰度图。一个健壮的预测函数应该能处理这些情况:
from PIL import Image def load_and_preprocess_image(img_path, target_size): img = Image.open(img_path) # 转换为RGB,处理灰度图或RGBA图 if img.mode != 'RGB': img = img.convert('RGB') img = img.resize(target_size) # ... 后续转换为array和归一化 ...
6. 性能提升与进阶思考:从95%到99%的路径
如果你的基础模型达到了90%-95%的准确率,恭喜你,已经成功入门。但如果你想挑战更高精度,或者应对更复杂的情况,下面这些进阶策略值得尝试。
6.1 使用预训练模型进行迁移学习
这是提升小数据集上模型性能的“大杀器”。我们不再从零开始训练,而是使用在ImageNet等超大数据集上预训练好的模型(如VGG16、ResNet50、EfficientNet)作为特征提取器,只训练顶部的分类层。
from tensorflow.keras.applications import VGG16 from tensorflow.keras import layers, models # 加载预训练的VGG16模型,不包括顶部的全连接分类层(include_top=False) # 使用在ImageNet上预训练的权重 conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(150, 150, 3)) # 冻结卷积基,不让其在训练初期被更新,保护预训练好的特征 conv_base.trainable = False # 在卷积基上搭建我们自己的分类器 model_transfer = models.Sequential([ conv_base, layers.Flatten(), layers.Dense(256, activation='relu'), layers.Dropout(0.5), layers.Dense(1, activation='sigmoid') ]) model_transfer.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])用这种方式,你通常只需要训练最后的几个全连接层,在很少的epoch内就能达到比从头训练高得多的验证准确率(比如98%以上)。之后,你可以选择“解冻”卷积基的最后几层,进行微调,以更好地适应猫狗数据集。
6.2 应对类别不平衡与困难样本
即使猫狗数量均衡,数据集中也必然存在一些“困难样本”,比如模糊的图片、猫狗姿势奇特、有遮挡等。此外,如果你用自己的数据集,类别不平衡是常态。
- 类别不平衡:如果猫有9000张,狗只有3000张,模型会倾向于预测猫。解决方法:
- 在
flow_from_directory中设置class_weight:自动为少数类分配更高的损失权重。 - 对少数类进行过采样:在数据增强时,对少数类的图片生成更多变体。
- 在
- 困难样本挖掘:训练几轮后,找出那些被模型错误分类的样本(高置信度分错),把它们单独拿出来,在后续训练中给它们更高的权重或更多地出现,迫使模型重点学习这些难点。
6.3 超参数调优与模型集成
当模型性能进入平台期,可以尝试系统性地调整超参数:
- 学习率:这是最重要的超参数之一。可以尝试使用学习率调度器,如
ReduceLROnPlateau,当验证损失停滞时自动降低学习率,让模型更精细地收敛。 - 网络结构:增加或减少卷积层/全连接层的数量、调整过滤器数量、尝试不同的激活函数(如
swish有时比relu好)。 - 正则化强度:调整
Dropout比率,或在全连接层、卷积层后加入L2权重正则化。 - 优化器:尝试
RMSprop或SGD with momentum,并调整其参数。 - 图像尺寸:将
target_size从(150, 150)提高到(224, 224),输入更多像素信息。 - 模型集成:训练多个结构不同或随机种子不同的模型,让它们对同一张图片进行预测,然后取平均或投票结果作为最终预测。这几乎总是能提升1-2个百分点的性能。
最后,我想说的是,完成这个猫狗分类项目,绝不仅仅是得到了一串高准确率的数字。更重要的是,你亲手实践了深度学习项目从数据到部署的全流程,理解了CNN每一层背后的设计动机,掌握了诊断和优化模型的基本方法。这些经验,远比直接调用一个现成的API来得宝贵。下次当你看到“人狗大作战”的代码时,你就能一眼看出其中的门道,甚至能自己动手,做出一个真正智能、鲁棒的识别系统来。
本文还有配套的精品资源,点击获取