ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于Python的猫狗图像识别实战:从数据划分到模型部署

基于Python的猫狗图像识别实战:从数据划分到模型部署 简介面向计算机相关专业学生与开发者的猫狗识别分类项目基于PyTorch实现CNN、ResNet与Swin Transformer等模型适合作为毕业设计、课程设计或深度学习入门练手。压缩包共16个文件约1.67MB包含7个Python脚本覆盖数据加载、模型构建、训练与测试、2个Markdown说明文档、1个Word论文/设计文档以及预训练模型权重.pth另有日志与辅助文件目录结构清晰便于按模块查阅。项目提供完整可运行的分类流程并附有说明文档与设计论文既能帮助理解图像识别任务的数据处理、网络设计与评估方法也可直接运行或在此基础上进行二次开发。目前已有68人学习下载适合需要获取可运行源码、配套文档及模型权重以便快速开展课设、毕设或项目实践的学习者。1. 打开这个猫狗识别源码包它到底帮你解决了什么做课程设计或者毕业设计的人大概率下载过这种包名字叫“基于python机器学习的猫狗识别分类项目源码”压缩包里放着代码、说明文档、论文和训练好的模型。这个包解决的不是让你从零理解卷积神经网络而是给你一条能复现的完整链路——从图片数据怎么组织、标签怎么读取到模型怎么训练、准确率怎么评估最后怎么把模型拿来做单张图片预测。整个包的核心是二分类猫和狗两个类别所有代码都围绕这个任务展开。适合两类人一类是刚入门机器学习、想在真实图片数据上跑通分类流程的初学者另一类是时间紧、需要快速交付可演示项目的从业者或学生。它的价值在于不用造轮子把骨架搭好你只需要调参和补数据。2. 把数据先理清楚从图片文件名里读出猫和狗的标签2.1 先看数据组织方式train、val、test 三个目录意味着什么源码包到手第一件事不是看模型代码而是先看数据目录。猫狗识别最经典的数据组织方式来自 Kaggle 的 Dogs vs Cats 数据集文件命名是cat.0.jpg、dog.1532.jpg这种格式标签信息全部藏在文件名里。正常的数据目录长这样cats_dogs/ ├── train/ │ ├── cat.0.jpg │ ├── cat.1.jpg │ ├── dog.0.jpg │ └── dog.1.jpg ├── val/ │ ├── cat.100.jpg │ └── dog.100.jpg └── test/ ├── cat.200.jpg └── dog.200.jpg不要小看这个目录结构它是整个项目的基石。train目录用于拟合模型参数val目录用于每个 epoch 结束后评估模型表现test目录模拟真实场景只在最终验证时碰一次。很多初学者喜欢把所有图片混在一起然后自己随机切分这当然也可以但源码包按目录划分有个好处Keras的flow_from_directory和PyTorch的ImageFolder都原生支持这种目录结构你不需要手写复杂的标签映射逻辑框架会自动按子目录名生成标签。2.2 从文件名提取标签最简单的做法也是最稳的做法如果你的数据已经按目录划分好了标签读取根本不需要写代码框架自己会处理。但源码包里的原始数据往往还是cat.0.jpg、dog.0.jpg这种平铺结构需要自己切分。切分脚本是绕不开的我一般这样写import os import shutil import random # 原始图片所在目录 source_dir raw_images # 划分后的目标根目录 target_dir cats_dogs # 每个类别的图片数量这里是整数表示每个类别用多少张 train_size 800 val_size 200 random.seed(42) # 固定随机种子保证每次运行结果一致 for class_name in [cat, dog]: # 收集当前类别的所有图片文件名 files [f for f in os.listdir(source_dir) if f.startswith(class_name)] random.shuffle(files) # 按数量切分成训练集和验证集 train_files files[:train_size] val_files files[train_size:train_size val_size] # 逐个复制到目标目录 for split, split_files in [(train, train_files), (val, val_files)]: dest_dir os.path.join(target_dir, split, class_name) os.makedirs(dest_dir, exist_okTrue) for f in split_files: shutil.copy(os.path.join(source_dir, f), os.path.join(dest_dir, f)) print(数据划分完成)这段代码的逻辑很直白按文件名前缀判断类别打乱顺序后按固定数量切分。random.seed(42)这行值得强调它保证了你每次运行脚本得到的划分结果完全一致。很多人跑源码包发现每次训练效果都不一样排除模型随机性之外数据划分不一致也是重要原因。train_size和val_size建议按 8:2 的比例分配如果原始数据量很少至少保证每个类别有 100 张以上验证图片否则验证集准确率的波动会大到让你怀疑模型写错了。2.3 数据加载为什么我推荐用 ImageDataGenerator 而不是手写数据管道数据划分完成之后接下来是数据加载。源码包里最常见的做法是用Keras的ImageDataGenerator核心原因有三个一是代码量少二是自带数据增强三是内存友好。from tensorflow.keras.preprocessing.image import ImageDataGenerator # 训练集加入数据增强提升泛化能力 train_datagen ImageDataGenerator( rescale1.0 / 255, # 像素值归一化到 [0, 1] rotation_range20, # 随机旋转角度范围 ±20 度 width_shift_range0.2, # 水平平移比例 20% height_shift_range0.2, # 垂直平移比例 20% shear_range0.2, # 错切变换强度 zoom_range0.2, # 随机缩放比例 horizontal_flipTrue # 随机水平翻转 ) # 验证集只做归一化不做增强 val_datagen ImageDataGenerator(rescale1.0 / 255) train_generator train_datagen.flow_from_directory( cats_dogs/train, target_size(224, 224), # 统一缩放到 224x224匹配模型输入 batch_size32, # 每批 32 张图片 class_modebinary, # 二分类标签为 0/1 shuffleTrue ) val_generator val_datagen.flow_from_directory( cats_dogs/val, target_size(224, 224), batch_size32, class_modebinary, shuffleFalse # 验证集不需要打乱便于后续评估 )这里有几个参数需要详细说明。target_size(224, 224)不是随便定的224x224 是 VGG、ResNet、MobileNet 这些主流卷积神经网络的默认输入尺寸。你换成 128 或 96 也能跑但会损失细节信息换成 512 显存占用会暴涨而且小数据集上收益有限。class_modebinary是因为猫狗是二分类。如果是猫狗鼠三分类这里要改成categorical同时模型输出层也要跟着改。这个参数和模型最后一层的神经元数量必须严格对应否则训练时报错都算轻的最怕是能训练但准确率永远在 50% 附近徘徊。horizontal_flipTrue对猫狗识别特别合适。猫和狗的照片无论左右翻转语义都不变这是免费的数据扩充。但注意别用vertical_flip把图片上下翻转会产生大量不自然的训练样本反而拉低准确率。数据增强还有一个容易被忽略的作用它相当于给模型引入了正则化能明显缓解过拟合。源码包里训练集图片通常只有几千张如果不做增强模型很容易把训练集背下来在验证集上表现惨不忍睹。3. 搭建模型从零写 CNN还是直接上迁移学习3.1 先说不玄学的地方猫狗识别本质上是二分类输出层该怎么设计模型是整个源码包的核心但很多人在模型选择上犹豫不决其实猫狗识别这个任务远没有想象中复杂。它就是一个二分类问题输入一张图片输出一个 0 到 1 之间的概率大于 0.5 算狗小于 0.5 算猫或反之。这里有一个新手最容易犯的错误输出层用了softmax加两个神经元损失函数用categorical_crossentropy。这在多分类里是对的但二分类用softmax属于杀鸡用牛刀。更简洁的做法是输出层只有一个神经元激活函数用sigmoid损失函数用binary_crossentropy。这两种方案数学上等价但后者参数更少、训练更稳定、预测时直接拿阈值判断一切都简单一个量级。3.2 手写 CNN 的 Keras 实现每一层的参数怎么理解如果你只想快速跑通手写一个四层的卷积神经网络就够了。源码包里通常会给一个类似这样的模型定义from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout def build_cnn(input_shape(224, 224, 3)): model Sequential([ # 第一层卷积32 个 3x3 卷积核提取边缘和纹理特征 Conv2D(32, (3, 3), activationrelu, input_shapeinput_shape), MaxPooling2D(pool_size(2, 2)), # 下采样降低特征图尺寸 # 第二层卷积64 个 3x3 卷积核提取更抽象的特征 Conv2D(64, (3, 3), activationrelu), MaxPooling2D(pool_size(2, 2)), # 第三层卷积128 个 3x3 卷积核 Conv2D(128, (3, 3), activationrelu), MaxPooling2D(pool_size(2, 2)), # 把特征图拉平成一维向量接全连接层 Flatten(), Dropout(0.5), # 随机丢弃 50% 神经元防止过拟合 Dense(128, activationrelu), # 全连接层128 个神经元 Dropout(0.3), Dense(1, activationsigmoid) # 输出层1 个神经元sigmoid 输出概率 ]) return model model build_cnn() model.summary()这段代码需要重点理解三个地方。第一Conv2D(32, (3, 3))中的 32 代表卷积核的数量。第一层 32 个卷积核负责提取边缘、颜色、纹理等低级特征第二层 64 个卷积核组合出更复杂的形状第三层 128 个卷积核学习猫脸的轮廓、狗耳朵的形状这类抽象特征。卷积核数量翻倍是常见做法因为越往后的特征图空间尺寸越小但通道数越多信息容量保持稳定。第二MaxPooling2D(pool_size(2, 2))的作用是压缩特征图。224x224 的输入经过三次池化后变成 28x28计算量大幅下降同时让模型对物体的位置变化不那么敏感。猫在图片左上角还是右下角池化操作都能让最终特征保持稳定。第三Dropout(0.5)是防过拟合的利器。训练时随机让一半神经元不参与计算相当于每次训练一个不同的子网络预测时再取平均效果。源码包里如果数据量只有几千张不加 Dropout 的 CNN 几乎必然过拟合。3.3 迁移学习把训练时间从几小时压缩到十几分钟手写 CNN 虽然能跑但效果上限很低。Kaggle 猫狗数据集上一个四层 CNN 准确率大概在 85% 到 90% 之间而迁移学习可以轻松到 97% 以上。如果你的源码包里带了预训练模型文件那几乎可以肯定作者用的是迁移学习。from tensorflow.keras.applications import MobileNetV2 from tensorflow.keras.models import Model from tensorflow.keras.layers import GlobalAveragePooling2D, Dropout, Dense # 加载预训练模型不包括顶部的全连接分类层 base_model MobileNetV2( weightsimagenet, # 使用在 ImageNet 上预训练好的权重 include_topFalse, # 去掉最后的全连接层只保留卷积部分 input_shape(224, 224, 3) ) base_model.trainable False # 冻结底层参数只训练新增层 # 在预训练模型顶部接新的分类头 x base_model.output x GlobalAveragePooling2D()(x) # 把特征图压缩成 1 维向量 x Dropout(0.3)(x) predictions Dense(1, activationsigmoid)(x) model Model(inputsbase_model.input, outputspredictions) model.summary()迁移学习的思路是ImageNet 上有 1000 个类别的 1400 万张图片模型在里面学到的边缘、纹理、形状特征对猫狗识别同样适用。你不需要从零学“什么是毛发”只需要在预训练特征的基础上学习“猫的毛发和狗的毛发差别在哪里”。base_model.trainable False这行是初学者最容易忽视的。冻结底层参数能大幅减少训练时间而且在小数据集上微调整个预训练模型反而容易让前面层学到的通用特征被破坏。如果想进一步提升准确率可以解冻最后几层做微调设置一个极小的学习率如1e-5训练几个 epoch但前提是数据量足够大否则收益有限。MobileNetV2 和 ResNet50 之间怎么选看你的硬件条件。ResNet50 准确率稍高但参数量是 MobileNetV2 的数倍CPU 上训练一个 epoch 要煎熬十几分钟。MobileNetV2 是轻量级网络设计目标就是移动端部署普通笔记本显卡跑起来毫无压力是源码包里性价比最高的选择。4. 训练与评估跑起来只是开始准确率是靠参数喂出来的4.1 训练脚本epochs、batch size、learning rate 三个关键参数如何配合模型定义好了接下来是训练。训练脚本看起来就那么几行但参数配不好训练结果天差地别。这里给出一个带模型保存和早停的完整训练脚本from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping model.compile( optimizerAdam(learning_rate1e-4), # 学习率迁移学习建议用 1e-4 起步 lossbinary_crossentropy, metrics[accuracy] ) # 每个 epoch 结束验证集准确率提升了就保存模型 checkpoint ModelCheckpoint( best_model.h5, monitorval_accuracy, # 监控验证集准确率 save_best_onlyTrue, # 只在效果变好时覆盖保存 modemax, verbose1 ) # 验证集准确率连续 5 个 epoch 不提升提前结束训练 early_stop EarlyStopping( monitorval_loss, # 监控验证集损失 patience5, # 容忍 5 个 epoch 不改善 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] )三个超参数是这里的主角。learning_rate1e-4是迁移学习的常见起点。如果是从零训练的 CNN可以用1e-3梯度更新更快但预训练模型的特征已经很好了学习率过大会破坏已经学到的特征。如果发现损失一直在震荡不下降把学习率降到1e-5如果下降速度慢得像蜗牛可以提到5e-4。学习率是训练里最典型的“参数玄学”没有绝对正确的值只有试出来的经验值。batch_size32和steps_per_epoch是配套关系。一个 epoch 表示模型看完整一遍训练集每看 32 张图片更新一次参数。如果数据量是 1600 张那么每个 epoch 更新 50 次。增大 batch size 可以更充分地利用 GPU 并行能力但太大容易陷入尖锐的局部最优泛化能力变差减小 batch size 训练不稳定但往往泛化效果更好。32 是入门者最稳妥的选择。epochs20在配合EarlyStopping时不必设得特别大。patience5表示如果验证集损失连续 5 个 epoch 没有改善训练提前终止。有这句话在设多少 epoch 都无所谓模型会在最佳时机自动停下来。这个机制必须加否则你设 50 个 epoch 就会眼睁睁看着准确率涨到 99%然后跌回 92%模型的最终状态完全看运气4.2 训练曲线的判读不画图的训练等于白训训练结束之后源码包里通常有一段绘制训练曲线的代码。这段代码别删也别跳过运行。训练曲线是判断模型状态最直接的依据比任何指标都管用。import matplotlib.pyplot as plt def plot_training_history(history): fig, (ax1, ax2) plt.subplots(1, 2, figsize(14, 4)) # 损失曲线 ax1.plot(history.history[loss], labelTrain Loss) ax1.plot(history.history[val_loss], labelVal Loss) ax1.set_title(Loss Curves) ax1.set_xlabel(Epoch) ax1.set_ylabel(Loss) ax1.legend() # 准确率曲线 ax2.plot(history.history[accuracy], labelTrain Accuracy) ax2.plot(history.history[val_accuracy], labelVal Accuracy) ax2.set_title(Accuracy Curves) ax2.set_xlabel(Epoch) ax2.set_ylabel(Accuracy) ax2.legend() plt.tight_layout() plt.savefig(training_curves.png, dpi150) plt.show() plot_training_history(history)判读曲线是一门经验活我总结三条规律。第一训练损失持续下降但验证损失在某个 epoch 后开始回升准确率停滞或下降这是过拟合的典型信号。看曲线里验证损失的最低点那个位置就是最佳模型所在的 epochEarlyStopping会自动帮你在那里保存权重。第二训练损失和验证损失都居高不下训练曲线和验证曲线几乎重合这代表欠拟合。模型能力太弱或者学习率太小需要加深网络、增加卷积核数量或者把学习率提高一个量级。第三训练曲线剧烈震荡、上下跳动幅度很大通常是学习率太高参数更新的步子迈太大。降学习率或者增大 batch size 来平滑梯度。4.3 用混淆矩阵和分类评估报告看真实的分类效果准确率是最直观的指标但它掩盖了大量信息。一个测试集里 90% 是猫的数据集模型把所有图片都判成猫就有 90% 准确率——看着很漂亮实则毫无意义。分类评估要看混淆矩阵。import numpy as np from sklearn.metrics import confusion_matrix, classification_report # 从验证集生成器获取真实标签和预测结果 val_generator.reset() y_true val_generator.classes # 真实标签0 表示猫1 表示狗 # 预测验证集中的所有图片 predictions model.predict(val_generator, stepsval_generator.samples // 32 1) y_pred (predictions 0.5).astype(int).reshape(-1) # 混淆矩阵 cm confusion_matrix(y_true, y_pred) print(混淆矩阵) print(cm) # 输出示例 # [[猫被正确识别数 猫被误判为狗数] # [狗被误判为猫数 狗被正确识别数]] # 分类评估报告 print(classification_report(y_true, y_pred, target_names[cat, dog]))混淆矩阵的四格是理解二分类模型行为的最小单位。对角线上的数字越大越好非对角线上的数字就是错误的类型猫被误判成狗还是狗被误判成猫。这两种错误的代价可能完全不同——如果你想做的是“宠物猫狗品种识别应用”把狗误判成猫的用户可能只是觉得不准如果你做的是“猫粮自动喂食器”把狗误判成猫就会导致设备在狗靠近时投放猫粮。classification_report里的precision、recall、f1-score三组指标对理解不平衡数据特别关键。precision表示预测成猫的样本里有多少是真正的猫衡量的是模型不误报的能力recall表示所有真正的猫里有多少被找出来了衡量的是模型不漏报的能力。猫狗数量相差悬殊的时候准确率可能很高但少数类别的recall会惨不忍睹这时候就要去检查数据是否存在严重不平衡或者考虑用class_weight给少数类别更大权重。5. 踩坑实录猫狗识别跑不通问题往往出在数据上而非模型上5.1 中文路径和文件名大小写让图片一张都读不出来现象flow_from_directory执行后输出的Found 0 images belonging to 2 classes或者训练时所有样本的标签全是同一个类别模型预测结果清一色是猫。原因数据路径中带有中文字符或者原始图片文件名没有按cat、dog小写前缀命名而是Cat.1.jpg、DOG_001.jpg这种混搭风格。OpenCV和PIL在读取中文路径时编码处理不一致可能直接报错或静默跳过文件名大小写问题则会让前缀匹配逻辑失效图片被错误归类。解决项目根目录和所有子目录强制使用英文路径图片文件名统一转成小写再处理。写数据加载代码前先跑一行命令检查张图片能不能正常读from PIL import Image import os # 项目目录里随机挑 5 张图片测试 sample_files [f for f in os.listdir(raw_images)][:5] for f in sample_files: img Image.open(os.path.join(raw_images, f)) print(f, img.size, img.mode)如果文件打开失败先把文件名中的特殊字符和中文全部替换掉再继续。这是整个流程里性价比最高的一步排查别跳。5.2 验证集准确率虚高数据泄露是怎么发生的现象训练完成后验证集准确率 98%你信心满满地把模型部署到真实场景发现准确率掉到 80% 以下。原因数据划分时没有把同一只猫或狗的照片全部放进同一个分区。比如原始数据里cat.1.jpg到cat.8.jpg是同一只猫的不同角度照片切分时一部分进了训练集一部分进了验证集。模型其实已经在训练集里见过这只猫了验证集准确率自然虚高。这种情况在机器学习里叫数据泄露是分类评估里最隐蔽的坑。解决按图片的“个体”而不是“单张”来划分数据集。如果源码包的原始图片命名带个体编号划分脚本要以编号为单位切分。更实际的做法是检查自己的数据来源如果是从视频里抽帧得到的一定要保证同一段视频的帧只出现在一个分区里。一旦验证集准确率失真后续所有的调参决策都建立在虚假的指标上等于白忙。5.3 猫全被认成狗类别不平衡带来的假阴阳现象训练过程中准确率一路走高但查看混淆矩阵发现猫的recall只有 30%大部分猫都被预测成了狗而狗的recall接近 100%。原因训练集里狗的照片数量是猫的两倍甚至更多。模型发现把一切都预测为狗就能在训练集上获得不错的准确率所以模型选择了这条偷懒路径。在二分类问题上这个现象比想象中更常见尤其是从网上爬图片自制数据集时两类图片数量很难天然平衡。解决两种方式搭配使用。第一种在flow_from_directory里给少数类别更高的采样权重用class_weight参数# 假设 0cat少数类1dog多数类 class_weight {0: 2.0, 1: 1.0} model.fit( train_generator, class_weightclass_weight, # 其他参数不变 )第二种从数据源头解决把猫的图片增广复制或者从网上补齐数据。class_weight只是让模型更关注少数类但数据本身的信息量不足权重再高也很难生成决策边界。5.4 训练曲线漂亮得像理想曲线一测新图片就翻车现象训练集准确率 99%验证集准确率 96%但拿手机随便拍一张猫的照片喂进去预测结果完全不对准确率和随机猜测差不多。原因这是过拟合的进阶版——模型记住了训练集里图片的“风格”而没有学到“猫是什么”。如果训练集里的猫照片全部是网图背景干净、视角统一模型可能大量依赖背景颜色和构图规律来判断而不是猫本身的特征。真实场景里你随手拍的照片背景一变模型立刻失灵。解决数据增强参数往大了调同时用一份完全没有参与训练的外部照片做冒烟测试。我习惯在项目里建一个real_world_test目录放三五张手机实拍照片每次训练完都直接预测一遍。如果训练集准确率很高但实拍照片识别不准优先怀疑数据分布问题和增强强度不足而不是去微调模型结构。这个习惯帮你从“测试集性能”视角切换到“真实场景可靠性”视角价值很大。5.5 模型文件加载失败换台机器、换了个环境就崩溃现象在自己电脑上训练好的.h5模型文件放到另外一台机器或服务器上加载直接报Unknown layer或ValueError: Unable to load weights。原因源码包里的模型文件是用特定版本框架训练保存的比如本机TensorFlow 2.10目标机器是TensorFlow 2.4。不同版本之间 Keras 层定义有变动序列化的层名称对不上加载自然失败。这是框架版本兼容性问题和模型本身无关。解决有两条路。第一条是重建完全相同的环境把requirements.txt里的版本号严格对齐在目标机器上建虚拟环境安装。第二条是换一种更稳定的模型导出格式用SavedModel格式替代单纯的权重文件# 保存为 SavedModel 格式 model.export(saved_model_dir) # 加载时不需要关心具体层定义框架版本兼容性更好 from tensorflow.keras.models import load_model loaded_model load_model(saved_model_dir)源码包里如果要交模型文件务必在说明文档里写清楚框架版本和 Python 版本。我见过太多因为环境不一致导致的翻车场景这不是技术问题是交付规范的缺失。每当有人拿着模型文件来问为什么加载失败我第一句话永远是“你的 TensorFlow 版本号是多少”。6. 用训练好的模型跑通一张新图片并把它封装成可复用的类项目做到这一步训练、评估都完成了但演示环节才是决胜点。源码包的验收场景通常是这样的打开命令行输入一张图片路径程序输出“这是猫 / 这是狗置信度是 0.93”。这个预测脚本看起来简单但图片预处理步骤必须和训练时完全一致差一步结果就偏。加载模型并预测单张图片的最小实现import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing import image class CatDogPredictor: def __init__(self, model_pathbest_model.h5): self.model load_model(model_path) self.target_size (224, 224) # 必须与训练时一致 self.class_names [cat, dog] def predict(self, img_path): # 加载图片调整尺寸到 224x224 img image.load_img(img_path, target_sizeself.target_size) # 把图片转成数组并增加 batch 维度 img_array image.img_to_array(img) img_array np.expand_dims(img_array, axis0) # 归一化到 [0, 1]与训练时保持一致 img_array img_array / 255.0 # 预测输出概率值 prob self.model.predict(img_array, verbose0)[0][0] # 概率大于 0.5 判断为狗否则为猫 pred_class self.class_names[int(prob 0.5)] confidence prob if prob 0.5 else 1 - prob return pred_class, float(confidence) # 使用示例 predictor CatDogPredictor(best_model.h5) result predictor.predict(test_cat.jpg) print(f预测结果{result[0]}置信度{result[1]:.2f})np.expand_dims这一步最容易漏。训练时框架自动按 batch 维度喂数据但单张预测时必须手动加上 batch 轴否则模型会抱怨输入尺寸不匹配。归一化1.0/255必须和训练时一致训练时用的是rescale1.0/255预测时忘记归一化输入分布和训练时完全不同预测结果基本是随机的。这两个小细节是百分之八十的预测脚本跑不对的根源。我自己的经验是把这个预测脚本写成一个类再封装成命令行接口后面做 GUI 或者 Flask 接口都直接复用。如果项目要求现场演示我建议准备两到三张智能手机实拍的照片做测试不要只用数据集里的图。这样做一是检查模型的真实泛化能力二是避免正式演示时现场照片识别失败导致尴尬。这个习惯帮我避过很多次险。如果你也被猫狗识别这个项目卡住过希望这篇笔记能让你少走一段弯路。本文还有配套的精品资源点击获取
返回列表