毕业设计选“猫狗识别”这个题目的人很多,但它确实是最适合入门卷积神经网络的选题之一:公开数据集好找、任务就是二分类、模型可以做得不大、普通笔记本甚至 CPU 也能训练。这次我们就把一套基于 TensorFlow + CNN 的猫狗二分类实现完整过一遍,从环境配置、数据集整理、模型搭建、训练验证到单张图片预测,每一步都给出可以直接运行的代码,目标是让你学完以后,换到自己的数据集上也能跑通。
这个项目解决的最大痛点不是模型公式有多难,而是很多同学从安装 TensorFlow 开始就卡住,或者数据路径不对,或者显存不够,导致代码一直报错。下面这套流程会把常见坑提前说明,你照着操作,比改论文里面那些公式参数更实在。
先给结论:如果你要做毕业设计、课程设计,或者只是想搞懂 CNN 是怎么工作的,这个项目值得优先试。整个流程涉及的代码量不多,但覆盖了数据加载、模型构建、训练回调、结果评估、单图预测这些完整环节。下面按章节拆开讲。
1. 猫狗识别项目核心能力速览
先看这个项目的整体情况。下方表格里的内容,部分是通用实践结论,部分需要根据你自己的数据和机器现场验证。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 图像分类 / 深度学习入门级毕设项目 |
| 技术栈 | TensorFlow、Keras、CNN 卷积神经网络 |
| 分类任务 | 猫、狗二分类 |
| 数据集要求 | 每个类别至少准备 500 张以上图片,效果会更稳 |
| 推荐输入尺寸 | 150 x 150 或 224 x 224,根据显存调整 |
| 训练环境 | 支持 CPU 和 GPU;GPU 显存 4GB 左右即可入门 |
| 推理环境 | 模型很小,普通电脑可运行 |
| 批量任务 | 可以一次预测多个图片文件 |
| 接口 API | 教程核心不涉及 Web 服务,可自行扩展 Flask/FastAPI |
| 一键启动 | 无一键包,需要按步骤运行脚本 |
| 主要风险点 | 数据集不均衡、过拟合、TensorFlow 安装版本冲突 |
这个项目最大的优点是“流程完整”。它不是只给一段训练代码,而是把数据、模型、训练、返回结果这几步串起来,非常适合毕业论文里的“系统实现”章节。
2. 适用场景与使用边界
猫狗识别适合下面几类用户:
- 毕业设计选型是图像分类方向,需要一个能跑通的基线模型。
- 刚开始学 CNN,想通过一个简单项目理解卷积、池化、全连接这些概念。
- 需要快速验证 TensorFlow 环境是否可用,训练一个小模型看流程。
- 想在本地把前端上传、后端调用模型、返回分类结果这一套流程做完,猫狗识别正是最简单的模型载体。
但有几个边界要提前说清楚:
- 这个项目解决的是“二分类”问题,如果要做多分类、目标检测、语义分割,需要更换模型结构。
- 数据质量决定模型上限。如果训练集只有几十张图,模型很容易过拟合,表现在训练集准确率很高,验证集准确率不稳定。
- 如果做毕设,建议使用公开数据集,并遵守对应平台的使用条款;如果想自己爬图,必须确认图片来源合规,不要拿未授权图片做商用或公开发布。
- 涉及人脸、宠物肖像、个人相册等素材时,要注意隐私和授权问题。
3. 环境准备:TensorFlow 安装与硬件门槛
3.1 版本选择
TensorFlow 2.x 是目前最常用的版本。搜索关键词里有人提到 TensorFlow 2.18,说明这个版本已经有社区使用,但安装时请以官方 PyPI 版本为准,推荐使用 2.15 到 2.18 之间的稳定版本。版本太高可能对旧显卡驱动不友好,版本太低又可能出现 API 兼容问题。
检查本机 Python 环境:
python --version建议使用 Python 3.9 到 3.12 之间的版本,这样 TensorFlow 安装包选择更稳妥。
3.2 安装 TensorFlow
CPU 环境直接安装即可:
pip install tensorflow如果你有 NVIDIA 显卡,并且已经装好 CUDA 和 cuDNN,可以安装 GPU 版本。TensorFlow 2.x 的 PyPI 包在 Windows/Linux 下会自动匹配 GPU 支持,前提是驱动版本和 CUDA 版本满足官方要求:
pip install tensorflow注意:新版 TensorFlow 不再严格区分tensorflow和tensorflow-gpu,统一安装tensorflow包即可。装完后确认 GPU 是否可用:
import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices('GPU'))如果list_physical_devices('GPU')返回空列表,说明当前环境只有 CPU 可用,需要检查驱动或 CUDA 版本。
3.3 显存和硬件门槛
CNN 二分类模型占用的显存并不夸张。以 150x150 输入、batch_size 为 32 为例,常见的 4GB 显卡可以跑;如果显存只有 2GB 或无法使用 GPU,可以把输入尺寸降到 128x128,batch_size 降到 16。CPU 也能跑,只是训练时间会明显变长。
没有实测数据的情况下,最稳妥的方式是先用一个小 batch 训练一个 epoch,观察显存占用,再根据情况调整。
4. 数据集准备:整理猫狗二分类数据
4.1 数据集目录结构
建议把所有图片按下面的目录结构整理:
data/ train/ cats/ dogs/ validation/ cats/ dogs/Keras 的image_dataset_from_directory会直接读取目录名作为类别标签,所以目录名必须规范。
如果你的数据是 Kaggle 的原始train/cat.0.jpg、train/dog.0.jpg这种文件结构,可以先用脚本转换成上面的目录结构。
4.2 数据加载代码
训练代码最推荐使用 TensorFlow 自带的image_dataset_from_directory,避免自己写图片读取循环:
import tensorflow as tf batch_size = 32 img_height = 150 img_width = 150 train_ds = tf.keras.utils.image_dataset_from_directory( "data/train", labels="inferred", label_mode="int", image_size=(img_height, img_width), batch_size=batch_size, shuffle=True ) val_ds = tf.keras.utils.image_dataset_from_directory( "data/validation", labels="inferred", label_mode="int", image_size=(img_height, img_width), batch_size=batch_size, shuffle=False )label_mode="int"会生成 0 和 1 两个标签,对应猫和狗。顺序取决于目录名排序,一般 cats 在前、dogs 在后。如果不确定,可以打印类别名:
print(train_ds.class_names)4.3 数据预处理
image_dataset_from_directory返回的图片是 0 到 255 的整数像素值,需要缩放到 0 到 1 之间。可以在模型的第一层加一个Rescaling层,也可以在数据流里用 map 实现。
建议直接放到模型里,简洁且便于导出模型时保留预处理逻辑:
normalization = tf.keras.layers.Rescaling(1.0 / 255)4.4 数据集数量建议
统计一下训练集和验证集图片数量。最简单的方法是打印数据集大小:
print("训练集 batches:", len(train_ds)) print("验证集 batches:", len(val_ds))如果训练集数量很少,每个类别只有几十张,建议优先检查数据是否完整,或者考虑使用预训练权重迁移学习,而不是从零训练一个大模型。
5. CNN 模型构建:代码逐行详解
5.1 基础模型结构
猫狗二分类任务不需要堆太深的网络。下面这个结构是标准的入门级 CNN,实测稳定、收敛快:
from tensorflow.keras import layers, models model = models.Sequential([ layers.Rescaling(1.0 / 255, input_shape=(img_height, img_width, 3)), layers.Conv2D(32, (3, 3), activation='relu'), 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.Flatten(), layers.Dense(256, activation='relu'), layers.Dropout(0.5), layers.Dense(1, activation='sigmoid') ]) model.summary()逐层拆解:
Rescaling(1.0 / 255):像素归一化。Conv2D(32, (3, 3), activation='relu'):第一层卷积,提取边缘、纹理等低层特征。MaxPooling2D((2, 2)):降低特征图尺寸,减少计算量。- 第二层、第三层卷积:逐步提取更抽象的语义特征。
Flatten():把多维特征图拉平,送入全连接层。Dense(256, activation='relu'):全连接层,组合特征。Dropout(0.5):随机丢弃一半神经元,降低过拟合风险。Dense(1, activation='sigmoid'):二分类输出层,输出 0 到 1 之间的概率。
整个模型参数量不会太大,在普通显卡上训练时间可控。如果你想提高准确率,可以把卷积核数量从 32/64/128 改成 64/128/256,但显存占用和训练时间也会增加。
5.2 损失函数与优化器
二分类问题有两种写法:
- 使用
Dense(1, activation='sigmoid'),损失函数用binary_crossentropy。 - 使用
Dense(2, activation='softmax'),损失函数用sparse_categorical_crossentropy。
第一种更简洁。编译代码如下:
model.compile( optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'] )如果你在训练时发现 loss 降不下去,可以先检查标签是否真的是 0 和 1,再检查最后一个全连接层的激活函数是否为sigmoid。
6. 训练配置与回调函数
6.1 数据增强
如果训练集数量不多,可以加上简单的数据增强,提高模型泛化能力。Keras 内置的数据增强层很方便:
data_augmentation = models.Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.05), layers.RandomZoom(0.05), ])使用方式有两种:把增强层放进模型的第一层,或者在读取数据后通过map处理。建议单独运行增强层做检查,确认增强后的图片不会变形过度。
6.2 回调函数
训练建议使用回调函数,这里有三个非常关键:
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau callbacks = [ EarlyStopping(patience=5, restore_best_weights=True), ModelCheckpoint("best_model.keras", save_best_only=True), ReduceLROnPlateau(factor=0.2, patience=3) ]EarlyStopping:当验证集指标多个 epoch 不再提升时,自动停止训练。ModelCheckpoint:只保存验证集上表现最好的模型。ReduceLROnPlateau:验证集指标停滞时,自动降低学习率。
6.3 执行训练
history = model.fit( train_ds, validation_data=val_ds, epochs=20, callbacks=callbacks )第一次跑建议把epochs设成 10,先确认流程能走通,再调大训练轮数。
7. 训练与效果验证
7.1 观察训练曲线
训练结束后,最好画出准确率和损失曲线,判断模型是否有过拟合或欠拟合:
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.figure(figsize=(10, 4)) plt.subplot(1, 2, 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.subplot(1, 2, 2) 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.tight_layout() plt.show()如果训练集准确率很高、验证集准确率低,这就是典型的过拟合。解决办法是增加数据量、增强数据增强强度、加大 Dropout 比例。
如果训练集和验证集准确率都低,可能是模型容量不足,可以增加卷积层数量或加大卷积核数量。
7.2 评估测试集
保存的模型best_model.keras可以直接在验证集上评估:
best_model = tf.keras.models.load_model("best_model.keras") loss, acc = best_model.evaluate(val_ds, verbose=1) print(f"验证集损失: {loss:.4f}") print(f"验证集准确率: {acc:.4f}")注意:验证集准确率是多少取决于数据量、数据质量和训练轮次。不同数据集之间可以直接比较,但不要拿你自己的准确率和某个特定博主的结果做绝对对比。
8. 单张图片预测与批量推理
8.1 单张图片预测
训练完成后的模型是一个.keras文件。预测单张图片时,需要使用和训练时相同的img_height和img_width:
import numpy as np import tensorflow as tf def predict_image(image_path, model_path="best_model.keras"): model = tf.keras.models.load_model(model_path) img = tf.keras.utils.load_img( image_path, target_size=(150, 150) ) img_array = tf.keras.utils.img_to_array(img) img_array = tf.expand_dims(img_array, 0) img_array = img_array / 255.0 prob = model.predict(img_array, verbose=0)[0][0] if prob > 0.5: label = "dog" confidence = prob else: label = "cat" confidence = 1 - prob return label, float(confidence) print(predict_image("test_pics/sample_dog.jpg")) print(predict_image("test_pics/sample_cat.jpg"))注意,目录里的标签顺序是 cats 在前遍历得到的 0,dogs 在后得到 1,所以prob > 0.5判断为 dog。如果你的数据目录顺序不同,要自己确认class_names[0]和class_names[1]分别对应哪个类别,再改判断逻辑。
8.2 批量预测一个文件夹
如果需要批量预测多张图片,可以加一个循环:
import os def predict_folder(folder_path): results = [] for name in os.listdir(folder_path): if name.lower().endswith((".jpg", ".jpeg", ".png")): path = os.path.join(folder_path, name) label, confidence = predict_image(path) results.append((name, label, confidence)) return results results = predict_folder("test_pics") for name, label, confidence in results: print(f"{name}: {label} ({confidence:.2f})")8.3 导出模型后续使用
训练好的best_model.keras可以直接用于后续 Python 脚本,也可以转成 TensorFlow Lite 或 ONNX 格式,放到手机上或嵌入式设备里。如果想做成 Web 服务,可以考虑用 Flask 或 FastAPI 包一个接口,这部分属于部署扩展,不是本教程核心。
9. 资源占用与性能观察
9.1 显存观察方法
训练时观察显存占用,推荐使用命令行:
nvidia-smi -l 1也可以写一个小脚本打印 GPU 显存占用:
import tensorflow as tf gpus = tf.config.list_physical_devices('GPU') if gpus: print("GPU 数量:", len(gpus)) for gpu in gpus: print("GPU 名称:", gpu)9.2 CPU 和 GPU 的差异
CPU 也能训练这个二分类模型,但训练时间会明显长于 GPU。如果你没有独立显卡,建议:
- 输入尺寸从 150x150 降到 128x128。
- batch_size 从 32 降到 16。
- 训练轮次先设 10 轮。
- 使用
tf.data.AUTOTUNE预取数据,提高 CPU 数据吞吐量。
train_ds = train_ds.cache().prefetch(buffer_size=tf.data.AUTOTUNE) val_ds = val_ds.cache().prefetch(buffer_size=tf.data.AUTOTUNE)9.3 影响资源占用的关键因素
- 输入图片尺寸:越大越吃显存。
- batch_size:每批图片数量越大,显存占用越高。
- 卷积核数量:64、128、256 逐层增大时,模型计算量明显上升。
- 训练轮数:不影响显存,但影响训练时间。
- 数据增强:增强层在 CPU 上执行,如果增强强度过大,会拖慢整体训练速度。
如果训练过程中报显存不足,优先减小 batch_size,其次减小图片尺寸。尽量不要盲目降低卷积核数量,否则模型表达能力会下降。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 安装 TensorFlow 失败 | Python 版本不匹配或依赖冲突 | 检查 python --version 和 pip 版本 | 创建虚拟环境,使用 Python 3.9~3.12 安装 |
list_physical_devices('GPU')返回空列表 | 显卡驱动或 CUDA 版本不匹配 | 运行 nvidia-smi 查看驱动版本 | 更新驱动,按官方要求安装 CUDA 和 cuDNN |
| 显存不足 OOM | batch_size 过大或输入图片尺寸过大 | 查看显存占用 | 调小 batch_size,降低输入尺寸 |
| 训练准确率高,验证准确率低 | 过拟合 | 对比 train/val 准确率曲线 | 增加数据量、加 Dropout、加强数据增强 |
| 预测结果全部是同一类 | 预测代码标签顺序错误 | 打印 class_names | 根据 class_names 顺序调整 0/1 判断逻辑 |
| 数据路径报错 | 目录结构不对或路径含中文 | 检查目录打印结果 | 使用英文路径,按 train/cats、train/dogs 结构整理 |
| TensorFlow 版本 API 不兼容 | 新旧 API 混用 | 查看报错堆栈 | 统一使用 tf.keras 最新 API,或固定版本 |
| 数据加载特别慢 | 没有配置 cache/prefetch | 观察 epoch 耗时 | 加 cache().prefetch(buffer_size=tf.data.AUTOTUNE) |
| 验证集准确率忽高忽低 | 学习率过高或数据量太少 | 打印 loss 曲线 | 使用 ReduceLROnPlateau 调低学习率 |
| 模型无法保存为 .keras | 版本过旧 | 检查 TensorFlow 版本 | 升级到 2.15 以上版本 |
11. 最佳实践与扩展方向
11.1 目录管理建议
建议把项目目录和训练产物分开:
cat_dog_project/ data/ train/ validation/ src/ train.py predict.py models/ best_model.keras test_pics/训练脚本、预测脚本、模型文件分别放不同目录,避免后续做系统功能时找不到文件。
11.2 第一次训练的小技巧
第一次跑通不要追求效果,先重点关注以下几项:
- 是否能正常加载数据。
- 是否能完成一个 epoch 的训练。
- 验证集 loss 是否有下降趋势。
- 单个 epoch 耗时可接受。
- 保存的模型文件能否正常加载和预测。
走通这五步之后,再加大数据集、调大模型、增加训练轮数,这样排查问题的成本最低。
11.3 合规与版权提醒
猫狗识别涉及图片素材,当你准备数据集时要注意:
- 优先使用公开数据集,遵守数据集的许可证要求。
- 不要随意爬取他人网站图片做发布或商业用途。
- 如果做毕设,需要在论文中注明数据来源。
- 涉及个人照片、宠物肖像时,必须获得授权。
- 如果后续把项目发布到网上,不要内置未授权的图片。
11.4 从二分类扩展到更多方向
这个项目的价值在于流程完整,后续可以往几个方向延伸:
- 迁移学习:把
Conv2D结构替换成 MobileNetV2、EfficientNet 等预训练模型,在小数据集上效果更好。 - 多分类:把最后一层改成
Dense(num_classes, activation='softmax'),损失函数改成sparse_categorical_crossentropy,即可扩展成多种动物分类。 - 部署:用 Flask 或 FastAPI 包一个 HTTP 接口,接收上传图片并返回识别结果。
- 数据增强:加入更多图像预处理方式,提高模型鲁棒性。
实际做毕设时,核心是把“数据准备、模型训练、结果评估、网页展示”这条链路说清楚,猫狗识别作为载体已经足够。
后面如果想让项目更完整,建议先着重复现上面 8 节和 9 节的预测与评估流程,把准确率曲线、混淆矩阵、单图预测结果放到论文里,整套方案就会显得很扎实。最好再准备一套小规模验证数据,专门用来解释模型对不同图片的判断结果,这部分内容在答辩时很加分。