资讯动态

TensorFlow+CNN猫狗分类实战:从环境搭建到模型部署

发布时间:2026/8/30 4:11:52 来源:尧图企业网站定制
猫狗二分类是TensorFlow入门和计算机视觉课设里出现频率最高的题目之一。很多人拿到题目后第一反应是找模型但真正卡住的地方通常是环境装不上、数据集结构不对、训练完不会保存、预测单张图片时不知道输出怎么解释。这篇文章把基于TensorFlow和CNN实现猫狗二分类的完整流程拆开讲从环境准备到模型搭建再到训练、保存、预测和常见报错排查每个环节都会给出示例代码和判断标准。如果你正准备做毕设或者刚学完深度学习想找一个能跑通的小项目这篇文章可以直接照着做。1. 先明确猫狗二分类到底要解决什么问题1.1 输入输出和任务边界这个任务本质上非常简单输入是一张图片输出是一个二分类结果要么是猫要么是狗。用深度学习的说法就是训练一个二分类模型让模型学会从图片的像素矩阵中提取出猫和狗的区别。很多人一上来就纠结模型结构这其实顺序反了。最开始应该确定的是任务边界输入图片是什么格式JPG还是PNG图片尺寸是否统一要不要做缩放数据集的猫狗图片比例是不是接近1比1训练集、验证集、测试集怎么划分最终输出是“猫/狗”标签还是“猫的概率/狗的概率”这些看起来不起眼但会直接影响代码能不能跑通。比如你下载的数据集如果目录嵌套很乱加载代码写得再漂亮也会报文件路径错误。再比如图片尺寸不统一直接塞进卷积神经网络大概率会在数据进入全连接层时报维度不匹配。所以我把第一个部分放在前面就是想让你先建立一个认知猫狗二分类不是“堆一个CNN”就完事而是一条完整的数据输入、模型训练、结果输出链路。任何一个环节断了最后都看不到理想结果。1.2 为什么这种题目常用TensorFlow和CNN卷积神经网络是处理图片最常用的模型结构核心思路是用卷积核在图片上滑动提取局部特征。靠近输入层的卷积核能提取边缘、颜色、纹理这类低级特征后面的卷积层会把低级特征组合成耳朵、眼睛、尾巴这类高级特征。对猫狗识别来说模型不需要理解复杂场景只要抓住毛发纹理、脸部轮廓、耳朵形态这些差异就够用了。TensorFlow在这个任务里最大的优势是Keras接口非常直接。写一个卷积层只需要一行代码训练过程也不用手动写梯度更新。近几年PyTorch的讨论热度很高很多新项目也会选PyTorch但如果你毕设题目指定了TensorFlow或者你希望用更少的时间从零跑通TensorFlow的Sequential模型加ImageDataGenerator或者tf.data这套流程仍然很合适。我见过不少人在选框架上花了一周时间其实没必要。先把手头题目跑通再谈框架偏好。二分类这种规模的项目换框架不会产生本质差别。1.3 先定一个小目标跑通最小模型做毕设最忌讳一上来就追求95%准确率。第一次动手目标应该是“把最小模型跑起来”。最小模型的意思是只用少量图片训练很少的轮次不追求结果好坏只确认代码链路是通的。确认能读取图片、能生成标签、能反向传播、能保存模型、能加载模型预测这五个动作都正常再往后面优化。我一般会把第一次训练控制在5个epoch以内训练集用几百张图批量大小设为16或32。如果这一步能顺利完成说明环境和代码基本没问题。之后再加大数据量、加深网络遇到问题也能更快定位。2. 环境准备先把TensorFlow装到能跑起来的程度2.1 Python环境与依赖安装装TensorFlow之前先确认Python版本。不同版本的TensorFlow对Python版本有要求如果是比较新的版本往往需要Python 3.9以上。建议先创建一个独立的虚拟环境避免和系统里的其它Python包冲突。我的建议是用Anaconda管理环境conda create -n cat_dog python3.9 conda activate cat_dog然后安装TensorFlow。CPU版本可以直接用pip安装适合学习和一般规模的数据集pip install tensorflow如果你的电脑有NVIDIA独立显卡想用GPU训练需要额外安装CUDA和cuDNN。这里比较麻烦的地方是版本匹配TensorFlow版本、CUDA版本、cuDNN版本三者要对应上。初次使用可以先不折腾GPUCPU也能训练猫狗二分类只是时间会慢一些。除了TensorFlow还需要用到NumPy、Matplotlib这些常用库。如果用的是Anaconda这些一般已经装好。没有的话可以手动补上pip install numpy matplotlib2.2 验证TensorFlow是否安装成功装完不要直接开始写模型先验证一下环境是否正常。打开Python交互环境或者写一个简单的脚本import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices(GPU))能打印出版本号说明安装基本成功。如果没有GPUlist_physical_devices(GPU)打印出来是空列表这很正常。CPU环境也能训练只是速度慢一些。还有一个容易忽略的点如果你的机器有GPU第一次运行TensorFlow可能会弹出很多CUDA相关的日志。这时候不要慌日志不等于报错。只要最后能创建TensorFlow张量说明环境可用。x tf.constant([1.0, 2.0, 3.0]) print(x)这一步能正常打印就可以继续往下走了。2.3 版本差异带来的注意事项TensorFlow 2.x版本的Keras接口基本稳定但不同小版本之间偶尔会有API调整。比如数据预处理层的写法、某些回调函数的参数位置可能在升级后略有不同。如果你看到ImportError或者AttributeError先检查是不是版本不兼容不要急着改模型结构。查看当前环境下所有包版本可以用pip list | grep tensorflow如果代码是从网上找的而对方用的版本和你不同常见兼容性问题包括tf.keras.preprocessing.image.ImageDataGenerator在不同版本中的行为差异model.compile()里loss参数的写法以及predict返回结果的数据类型。遇到这类报错定位方式很清楚看日志找是导入失败、参数错误还是API不存在。3. 数据集准备与数据预处理3.1 猫狗数据集目录结构猫狗识别的经典数据集是Kaggle上的Dogs vs Cats但很多入门者会卡在下载和目录整理上。其实手头没有完整数据集也不影响学习可以先自己准备几十张猫图、几十张狗图验证流程能跑通再决定要不要换大数据集。强烈建议把数据按下面的目录结构放data/ train/ cat/ cat_001.jpg cat_002.jpg dog/ dog_001.jpg dog_002.jpg validation/ cat/ cat_001.jpg dog/ dog_001.jpg这种“每个类别一个子目录”的结构可以直接被Keras的ImageDataGenerator.flow_from_directory读取。你不用自己写标签映射工具会根据子目录名称自动生成标签。准备数据时注意图片格式。JPG、PNG都行但同一个项目里尽量保持格式一致。混用问题不大但有些图片可能本身损坏加载时读到不完整的文件会报错。遇到这种情况优先检查图片文件是否能正常打开。3.2 用ImageDataGenerator读取图片和生成标签数据预处理的常见做法是用ImageDataGenerator做像素归一化和数据增强。from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen ImageDataGenerator(rescale1.0/255) val_datagen ImageDataGenerator(rescale1.0/255) train_generator train_datagen.flow_from_directory( data/train, target_size(150, 150), batch_size32, class_modebinary ) val_generator val_datagen.flow_from_directory( data/validation, target_size(150, 150), batch_size32, class_modebinary )这里有两个关键参数需要理解target_size(150, 150)表示把所有图片统一缩放到150x150。这个尺寸兼顾训练速度和分类效果入门阶段很合适。class_modebinary表示二分类标签会变成0和1。对应到目录顺序一般是0代表cat1代表dog具体可以在训练前打印train_generator.class_indices确认。rescale1.0/255是把像素值从0到255缩放到0到1之间。这个步骤通常能加速模型收敛因为输入数值范围更稳定。3.3 数据增强要不要加数据增强是一种在训练时随机改变图片的方法比如旋转、缩放、水平翻转、亮度调整。它的作用是增加训练样本的多样性减少过拟合。train_datagen ImageDataGenerator( rescale1.0/255, rotation_range20, width_shift_range0.2, height_shift_range0.2, shear_range0.2, zoom_range0.2, horizontal_flipTrue, fill_modenearest )但要注意数据增强不是加得越多越好。初次跑通时建议先用最简配置只加rescale。如果发现训练集准确率很高、验证集准确率比较低出现过拟合再加增强。这个顺序更稳。验证集和测试集不要做增强只需要归一化。因为验证集的作用是模拟真实场景如果验证图片也被旋转模糊处理评估结果就不准确了。4. CNN模型搭建和参数选择4.1 一个适合入门的卷积模型结构猫狗二分类模型不一定要很深。下面这个结构配合150x150输入在CPU上也能在合理时间内完成训练。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout model 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), Flatten(), Dense(512, activationrelu), Dropout(0.5), Dense(1, activationsigmoid) ])每一层的作用我习惯这样理解Conv2D(32, (3, 3))表示用32个大小为3x3的卷积核提取特征。卷积核数量越多模型表达能力越强但参数量也越大。MaxPooling2D(2, 2)是下采样把特征图缩小一半。这样做能减少计算量同时增强平移不变性。Flatten()把二维特征图拉直成一维向量方便接全连接层。Dense(512)是全连接层把前面提取到的高层特征做分类决策。Dropout(0.5)训练时随机丢弃一半神经元防止全连接层过拟合。最后一层用sigmoid输出一个0到1之间的概率值。大于0.5判为狗小于0.5判为猫这是二分类的标准写法。4.2 参数怎么定更合理模型参数没有唯一正确答案但有基本的调节方向。卷积核数量可以从32开始逐层翻倍。因为越靠近输出特征越抽象用更多卷积核去捕捉信息。池化层直接跟着卷积层走尺寸设为2x2比较常见。全连接层的神经元数量也是一个可调项。512可以改成256如果训练太慢或者过拟合就减小。Dropout只在全连接层加卷积层不太常用Dropout更多用BatchNormalization。不过入门项目里加一个Dropout就够了。输入尺寸150x150对应的模型不算大显存和内存压力小。如果你的电脑配置较低可以把输入改成100x100或120x120训练速度会快不少准确率通常不会下降太多。4.3 编译损失函数、优化器、评估指标模型搭好后需要编译。from tensorflow.keras.optimizers import Adam model.compile( lossbinary_crossentropy, optimizerAdam(learning_rate0.0001), metrics[accuracy] )lossbinary_crossentropy是二分类任务的标准损失函数。optimizerAdam(learning_rate0.0001)是自适应学习率优化器。学习率可以尝试0.001但0.0001更稳定不容易出现损失震荡。metrics[accuracy]表示训练过程中同时输出准确率方便观察模型表现。为什么学习率不能随便调大学习率太大损失函数可能会在最小值附近反复横跳训练结果忽好忽坏。学习率太小收敛速度很慢。对新手来说用一个偏小的学习率往往比频繁调整模型结构更容易看到正向效果。5. 训练流程先跑通单任务再开批量5.1 用少量样本做冒烟测试正式训练前我会先做一个“冒烟测试”。这里说的冒烟测试不是生产环境里的自动化测试而是用极小数据跑一遍完整流程确认代码没有逻辑错误。做法是在flow_from_directory里只读取一个batch的数据然后调用一次model.train_on_batch。如果能正常返回损失值说明模型可以反向传播。x_batch, y_batch next(train_generator) loss, acc model.train_on_batch(x_batch, y_batch) print(loss, acc)这一步特别有用。很多时候模型代码看着没问题但实际跑训练就崩问题往往出在输入维度或者标签类型上。train_on_batch能快速暴露这些问题不用等完整训练跑完。5.2 完整训练循环和回调冒烟测试通过后再进入正式训练。这里我会加两个回调函数ModelCheckpoint和EarlyStopping。from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping checkpoint ModelCheckpoint( model/best_model.h5, monitorval_accuracy, save_best_onlyTrue, modemax ) early_stop EarlyStopping( monitorval_loss, patience5, restore_best_weightsTrue ) history model.fit( train_generator, steps_per_epochtrain_generator.samples // 32, epochs20, validation_dataval_generator, validation_stepsval_generator.samples // 32, callbacks[checkpoint, early_stop] )save_best_onlyTrue表示只在验证集表现最好时保存模型避免训练末尾模型过拟合导致覆盖掉好权重。patience5表示连续5个epoch验证损失不下降就提前停止训练。这两个回调对新手非常友好。这里需要说明steps_per_epoch的作用。使用flow_from_directory时Keras不知道一个epoch该取多少个batch所以要用总样本数除以batch_size来估算。如果这个值设置得比实际大一个epoch会重复读取数据设置小了一个epoch还没跑完所有图片。5.3 训练日志怎么读训练过程中会打印类似这样的信息Epoch 1/20 32/32 [] - 10s 309ms/step - loss: 0.6932 - accuracy: 0.4896 - val_loss: 0.6931 - val_accuracy: 0.5000看训练日志不要只盯accuracy重点看训练集和验证集的差距。训练损失持续下降验证损失也下降说明模型正常学习。训练损失下降验证损失先降后升说明过拟合。训练损失和验证损失都几乎不动说明学习率可能太小或者数据预处理有问题。验证准确率比训练准确率高数据集划分可能有问题也可能是验证集太小。如果看到这些情况先回顾数据和参数不要马上换模型。6. 模型保存、加载和单张图片预测6.1 保存模型和权重训练结束后Keras支持两种保存方式。第一种是整个模型包括网络结构和权重model.save(model/cat_dog_full_model.h5)第二种是只保存权重model.save_weights(model/cat_dog_weights.h5)如果只是自己用建议保存完整模型。加载后直接预测不需要重新创建网络结构。如果要在不同环境中迁移保存权重更灵活但必须保证模型结构完全一致再加载。6.2 加载模型并预测单张图片预测单张图片时最容易踩的坑是图片尺寸和批处理维度不匹配。模型训练时输入是(batch_size, 150, 150, 3)预测单张图片时必须把它转换成(1, 150, 150, 3)。正确写法import numpy as np from tensorflow.keras.preprocessing import image model tf.keras.models.load_model(model/cat_dog_full_model.h5) img_path test_dog.jpg img image.load_img(img_path, target_size(150, 150)) img_array image.img_to_array(img) img_array np.expand_dims(img_array, axis0) img_array img_array / 255.0 pred model.predict(img_array) print(pred)如果打印出来的pred是一个类似[[0.291]]的值就表示模型判断这张图片属于“1”类别的概率是0.291。对于二分类如果你的标签是cat0, dog1那么0.291代表更可能偏向猫。6.3 输出结果加上可读标签预测结果只有数字不直观。可以封装一个简单函数def predict_image(img_path, threshold0.5): img image.load_img(img_path, target_size(150, 150)) img_array image.img_to_array(img) img_array np.expand_dims(img_array, axis0) / 255.0 pred model.predict(img_array)[0][0] label dog if pred threshold else cat confidence pred if pred threshold else 1 - pred return label, confidence这样调用时直接得到(dog, 0.82)这种结果方便在毕设展示里打印出来。7. 常见问题和排查链路7.1 启动失败先看日志、依赖和路径很多人训练时报错第一反应是模型代码写错了。实际上启动阶段的报错来源通常是这三类依赖没有装全比如缺少NumPy、Pillow。路径错误比如flow_from_directory里指定的目录不存在。图片文件损坏加载时读取失败。排查顺序我建议是先看完整报错日志定位是导入阶段、数据读取阶段还是训练阶段。然后检查路径是否存在权限是否可读。接着检查图片文件能否用PIL正常打开。from PIL import Image im Image.open(data/train/cat/cat_001.jpg) im.verify()verify()方法能快速判断图片文件是否完整。如果这一步报错说明图片本身有问题不是TensorFlow的问题。7.2 训练不收敛数据、学习率、模型层数训练不收敛的现象是loss不下降或者准确率一直在50%左右抖动。遇到这个情况我先看三点第一数据有没有正确配对。类标签和图片是否对应可以通过打印train_generator.class_indices确认。第二归一化有没有做。很多代码忘记rescale1.0/255导致输入像素值太大模型很难收敛。第三学习率是不是不合理。如果loss忽高忽低学习率可能太大如果loss下降极慢学习率可能太小。模型层数不是主要问题。对猫狗二分类三层卷积已经足够。如果你发现模型过于复杂但数据量不够过拟合会非常明显表现为训练准确率接近100%验证准确率只有60%。7.3 批量预测输出混乱检查输入尺寸和命名批量预测时常见问题是输出结果和图片顺序对不上。使用image.load_img逐张读取时顺序由文件列表决定这个没问题。但如果用os.listdir读取文件返回的顺序不一定是字典序可能导致结果和文件名错位。建议用排序后的文件列表并给每张图片的输出命名import os image_paths sorted([ os.path.join(test_images, f) for f in os.listdir(test_images) if f.lower().endswith((.jpg, .jpeg, .png)) ]) for img_path in image_paths: label, confidence predict_image(img_path) print(img_path, label, confidence)按字典序排序后输出顺序是确定的后续如果要做混淆矩阵或者统计准确率也不容易乱。8. 从毕设角度复盘还能怎么改进8.1 资源占用和训练时间的边界猫狗二分类在CPU上也能跑但训练时间取决于数据量和模型大小。如果数据集只有2000张CPU训练可能在十几分钟到半小时内完成。如果数据量上万建议使用GPU或者先缩小图片尺寸、减少epoch。如果你的电脑只有CPU又想加快速度可以这样调输入尺寸从150改为128或100batch size从32改为16卷积核数量从32/64/128改为16/32/64epochs适当减少这些调整会降低训练精度但能让项目在普通电脑上演示。毕设展示时完整训练过程不一定需要现场跑可以提前把模型训练好现场只做加载模型和预测。8.2 准确率怎么从90%往95%走基础CNN模型在猫狗数据集上通常能达到85%到90%以上。想继续提升可以考虑下面几条路径按性价比排序使用更充分的预处理和数据增强比如随机裁剪、翻转、对比度调整。用迁移学习加载一个在ImageNet上预训练好的网络如MobileNetV2、ResNet50只替换最后几层做微调。调整模型深度适当增加卷积层但要同时增加Dropout或BatchNormalization。迁移学习对新手来说很值得尝试。代码不复杂Keras自带很多预训练模型加载后冻结前面的层只训练分类头准确率往往比从零训练高不少。8.3 从猫狗二分类扩展到其它任务这个项目做完之后扩展方向很清楚。把数据集从猫狗换成其他两类图片就能变成花朵分类、口罩佩戴识别、垃圾分类等题目。只需要调整目录结构和类别数量模型最后一层的输出数量和损失函数要跟着改。如果从二分类扩展到多分类有两个地方要改最后一层神经元数量改为类别数激活函数用softmax损失函数用categorical_crossentropy。class_mode也要改成categorical。模型主体结构不用变。8.4 自己先跑通再去讲给别人毕设答辩时最怕的不是模型效果差而是讲不清楚每个模块为什么要存在。我建议你把这篇文章里的环节做成一张流程图环境、数据、模型、训练、保存、预测。每个环节写两三句话说明输入是什么、输出是什么、为什么这么做。比如别人问“为什么要用卷积层”你可以说“卷积层用局部感受野提取图片特征比直接拉平像素效率高”。别人问“为什么最后一层用sigmoid”你可以说“二分类问题要输出一个0到1的概率sigmoid正好有这个性质”。能把这些讲明白项目就成功了一大半。跑完这一整套流程你手上会有一个能训练、能保存、能预测、能解释的猫狗识别项目。后面无论是要改数据集、换模型还是做多分类都有基础可以沿用。先别急着追求高分把链路跑稳比什么都重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价