2026/10/11 17:35:07

Mask R-CNN猫脸分割实战:从原理到自定义数据集的完整源码解析

Mask R-CNN猫脸分割实战:从原理到自定义数据集的完整源码解析 简介基于Mask R-CNN的猫脸分割项目源码及配套数据集专注于深度学习实例分割任务适用于高校计算机、人工智能、大数据等专业在校学生和教师也可作为课程设计、毕业设计、大作业或项目初期演示的完整参考。压缩包内共22个文件大小约11.16MB包含6个Python训练与测试脚本、10张猫脸图片样本、2个数据集压缩包以及Markdown/TXT格式的说明文档和README目录结构清晰便于按模块查看或替换数据。项目代码已由作者运行验证确保稳定可靠且支持自定义数据集扩展读者既可快速跑通训练与推理流程也能在此基础上调整输入格式、增删类别或尝试其他物体分割进行二次开发与深入钻研。目前已有343人学习浏览适合从入门到进阶的深度学习学习者尤其适合需要完成图像分割相关实战任务的同学参考借鉴。1. Mask R-CNN 猫脸分割一份能直接换自己数据集的开工源码做课程作业或毕设时最烦的事不是网上找不到源码而是找到的源码要么跑不起来要么数据集被写死想换成自己的图片得从配置文件一路改到标注格式。这份基于 Mask R-CNN 的猫脸分割项目算是我见过少有的「下载即能跑、换数据不伤筋动骨」的骨架工程。它把三件事串好了用 COCO 预训练权重初始化模型、在猫脸数据集上做实例分割微调、输出像素级掩码和置信度可视化。对做人工智能课程设计、深度学习入门复现或者想快速出一个演示效果的人来说这套代码能省下至少一星期的写代码和排错时间对已经跑过目标检测、想往实例分割迈一步的熟手它的自定义数据接口和训练参数也是值得多看两眼的参考实现。2. 先把分割原理看清楚Mask R-CNN 比 Faster R-CNN 多了什么2.1 从候选框到像素掩码RPN、ROI Align 与 mask 分支Mask R-CNN 本质上是在 Faster R-CNN 的检测框架上叠加了一个掩码分支。Faster R-CNN 负责输出「框」——每个目标一个矩形包围盒加一个类别得分Mask R-CNN 在这基础上对框内区域再做一次逐像素分类输出一张与 ROI 相同尺寸的二值掩码于是结果从「知道猫在哪儿」升级成「知道猫的每一像素在哪儿」这正是实例分割和检测的最大差别。核心差异在 ROI Align。Faster R-CNN 用的是 ROI Pooling它对浮点坐标做取整在量化过程中丢失几个像素的位置信息对检测任务尚可容忍对分割就是不可接受的误差。ROI Align 取消量化用双线性插值在特征图上采样固定网格每个候选区域都能拿到更精确的对齐特征。后续的 mask 分支就是在这个对齐后的特征图上接一个小卷积网络输出 K 个通道的掩码K 是类别数每个通道是 m×m 的二值图m 通常取 28。训练时 mask loss 只计算正样本 ROI 的交叉熵背景 ROI 不参与这也是模型能专注学前景形状的原因。这个项目的代码里mask 分支的实现在 mrcnn/model.py 的 MaskRCNN 类里和分类、回归分支并行。训练时三个 loss——RPN loss、检测 loss、mask loss——会同时回传。实操中很容易出现一种情况box 已经框得很准了但掩码边缘毛毛糙糙这多半是 mask loss 还没收敛或者训练轮次不够不是模型结构错了。理解了这个并行结构调试方向就清楚了。2.2 项目目录结构与权重加载逻辑先定位代码再动手拿到这份源码我习惯先看目录结构再跑命令。典型组织方式是这样的maskrcnn_cat/ ├── cat_dataset/ # 图片 标注 JSON │ ├── train/ │ ├── val/ │ └── via_region_data.json ├── samples/ │ └── cat/ │ ├── cat.py # 数据集类 训练入口 │ └── inspect_data.ipynb ├── mrcnn/ # Mask R-CNN 核心实现 │ ├── config.py │ ├── model.py │ ├── utils.py │ └── visualize.py └── mask_rcnn_coco.h5 # COCO 预训练权重模型加载逻辑通常在 cat.py 的 main 入口里这段代码是全文最关键的部分config CatConfig() config.display() model modellib.MaskRCNN(modetraining, configconfig, model_dirMODEL_DIR) # exclude 掉 COCO 的最后几层因为类别数从 80 变成 1 model.load_weights(COCO_WEIGHTS_PATH, by_nameTrue, exclude[mrcnn_class_logits, mrcnn_bbox_fc, mrcnn_bbox, mrcnn_mask])这里 load_weights 的 exclude 参数是重点。COCO 预训练模型有 80 个类别猫脸数据集通常只有 1 类最后几层张量形状对不上必须排除后再重新训练。by_nameTrue 表示按层名匹配权重不是按网络顺序所以中间的卷积层权重能正常复用相当于把「怎么分辨边缘、纹理」的底层视觉能力直接搬过来模型只需要在顶层学猫脸语义。参数说明model_dir 是保存 checkpoint 的目录建议单独建一个 logs 文件夹别和源码混在一起后面恢复训练时要靠它MODEL_DIR 和 COCO_WEIGHTS_PATH 建议在 cat.py 顶部用常量定义换机器时只改这两个路径不用翻整个文件。3. 把猫脸数据集跑起来类别注册与训练参数详解3.1 数据集类从 via_region_data.json 到训练样本这份源码用的是 Matterport 版 Mask R-CNN数据集加载需要自己继承 Dataset 类。cat.py 里已经有完整的 CatDataset 实现逻辑分三步load_info 读图片路径、load_mask 解析标注 JSON 生成二值掩码、load_class 注册类别。关键在 load_mask 里def load_mask(self, image_id): info self.image_info[image_id] anns self.load_annotations(info[path]) masks, class_ids [], [] for ann in anns: if ann[segmentation]: # 把多边形顶点转成 0/1 掩码 mask self.polygon_to_mask(ann[segmentation], info[width], info[height]) masks.append(mask) class_ids.append(self.class_names.index(cat)) masks np.stack(masks, axis-1) return masks, np.array(class_ids)这段代码把 VIAVGG Image Annotator导出的多边形坐标转成与图片同尺寸的布尔掩码。np.stack 把所有对象的掩码堆叠成 W×H×N 的三维数组每个对象一通道训练时模型按通道数量判断一张图里有几只猫。如果换了数据集需要同步改 class_names。默认是 [BG, cat]BG 必须留在第 0 位这是 COCO 训练管线定的规则类别 ID 从 1 开始计数。我见过有人把 BG 删掉只剩 [cat]结果训练出来的模型把背景也当成猫掩码糊成一片。这个问题在下一章的避坑清单里还会展开。3.2 训练脚本与超参数照着这份配置能少走弯路训练入口在 cat.py 底部核心配置是从 mrcnn/config.py 继承的 CatConfig关键参数如下class CatConfig(Config): NAME cat_seg # 训练任务名影响 checkpoint 命名 IMAGES_PER_GPU 1 # 单卡每 batch 图片数 NUM_CLASSES 1 1 # 背景 猫 STEPS_PER_EPOCH 100 # 每 epoch 迭代步数 DETECTION_MIN_CONFIDENCE 0.7 # 推理时的置信度阈值 IMAGE_MIN_DIM 512 IMAGE_MAX_DIM 768启动训练的命令通常是python samples/cat/cat.py train --datasetcat_dataset --weightscoco说几个参数的坑。IMAGES_PER_GPU 在 8GB 显存以下建议保持 1增大到 2 很容易 OOM如果显存紧张可以先把 IMAGE_MIN_DIM 降到 384代价是小尺寸猫脸的掩码细节会变差。STEPS_PER_EPOCH 决定一个 epoch 看多少张图100 对猫脸这种单类小数据集足够但要注意它和 EPOCHS 的配合——我一般先 10 个 epoch 看 loss 曲线曲线还在明显下降就继续训不再下降就停。学习率没有写在上面的配置里Matterport 版默认是 0.001用 COCO 权重微调时这个值不用动。如果是从随机初始化训建议降到 0.00025否则 loss 会震荡得像心电图这是血泪经验。训练命令里 --weightscoco 会自动走上一章说的 exclude 加载逻辑--weightslast 则从断点继续--weightsimagenet 是从 ImageNet 预训练开始但不用 COCO 权重三种模式对应不同的起点场景别搞混。4. 换成自己的数据集标注、注册与迁移训练全流程4.1 用 VIA 做多边形标注导出文件的格式细节项目自带的是猫脸标注文件但既然名字里写了「可自定义数据集」迁移到自己的图片才是这份源码最大的价值。我用 VIAVGG Image Annotator最多因为它是纯网页工具不需要安装导出 JSON 格式能直接被这份代码解析。流程是打开 via.html拖入图片用 Polygon 工具沿着目标边缘打点一个目标标一个区域最后导出 via_region_data.json。标注时有两个直接影响训练质量的细节。一是多边形顶点别太多猫脸这种平滑边缘 2030 个点足够顶点过多会让 mask 生成变慢而且训练时数据增强里的随机旋转会对密集顶点做插值产生锯齿边。二是所有图片的标注区域要闭合VIA 里漏闭合的路径导出后 segmentation 字段是空的load_mask 里那个 if ann[segmentation] 判断会把这类样本静默跳过训练集实际数量比你以为的少这种问题最难排查。导出的 JSON 结构长这样{ filepath: cat_dataset/train/001.jpg, regions: [ { shape_attributes: { name: polygon, all_points_x: [182, 205, 231, 214], all_points_y: [94, 87, 118, 140] }, region_attributes: {class: cat} } ] }cat.py 的 load_annotations 方法会读 filepath 定位图片、读 all_points_x/all_points_y 还原多边形坐标所以如果你已经用别的工具比如 labelme标好了数据只要写个脚本把坐标字段名转成这个格式就能复用整套训练管线不需要改动模型代码。4.2 迁移训练的完整配置步骤改三处就能开跑把猫脸换成你自己的目标类别正常情况下只需要改三处。第一处是 CatConfig 里的 NUM_CLASSES第二处是类名列表第三处是数据集目录路径。以换成「检测自己照片里的宠物狗」为例# 1. 配置类别数量 NUM_CLASSES 1 1 # BG dog # 2. 修改数据集类注册 self.class_names [BG, dog] # 3. 训练时指向新数据目录 python samples/cat/cat.py train --datasetdog_dataset --weightscoco值得多说一句的是数据量问题。COCO 预训练权重对通用特征很友好但狗脸和猫脸形状差异大单类目标最少也要 200300 张图才跑得出能看的掩码。少于这个量模型大概率会把前景和背景的边界学歪最常见的结果是掩码比实际物体大一圈或者把物体内部的暗色纹理割成缺口。数据集增强方面Matterport 版默认开了随机翻转、旋转和缩放但默认没有颜色抖动如果你的图片光照差异大可以在 config 里加一句# 每个 epoch 随机调整亮度 0.1~1.5 倍 def augmentation(self): return iaa.Sometimes(0.5, iaa.Multiply((0.8, 1.2)))这样同一张图在训练的不同阶段亮度不同能明显提升模型在明暗交替场景下的掩码稳定性。注意 augmentation 方法在 Config 里默认返回 None需要自行引入 imgaug 库装依赖时别漏了它。5. 训练排错与避坑Mask R-CNN 猫脸分割的常见问题清单5.1 checkpoint 路径错乱导致模型训不进去现象训练到一半中断用 --weightslast 想恢复结果 loss 从 0.1 跳到 3.0训练效果反而倒退。原因model_dir 里同时存在多个历史 checkpoint--weightslast 按文件名排序取最后一个而最后一个可能是某次只跑了几步的临时保存权重覆盖了之前收敛更好的版本。解决我后来在 cat.py 里强制设 model_dir 为按时间戳新建的目录并在每次正常结束训练后手动备份最优权重MODEL_DIR os.path.join(logs, config.NAME.lower(), datetime.now().strftime(%Y%m%d-%H%M))然后定期把 loss 最低的 checkpoint 复制到 weights 目录单独命名。从那以后恢复训练再没翻过车。5.2 OOM 发生在训练开始前batch size 与图片尺寸的取舍现象程序在第一个 batch 就报 CUDA out of memory显存 6GB 直接爆掉。原因IMAGES_PER_GPU1 时显存占用大头其实是特征图和 mask 分支的中间张量图片尺寸 IMAGE_MAX_DIM 从 768 降到 512单样本显存占用能砍掉近一半。解决双管齐下。先设 IMAGE_MIN_DIM384、IMAGE_MAX_DIM640这些参数放在 config 里后训练会自动 scale。还不行就用 --gpu 参数指定设备把 batch 维持在 1。注意不要为省显存把 DETECTION_MIN_CONFIDENCE 调高它只管推理不管训练调了也救不了 OOM。5.3 掩码质量差但检测框很准mask loss 没收敛现象预测框把猫完整框住置信度 0.9但掩码边缘有锯齿猫耳朵尖被削平。原因mask 分支的卷积层比检测分支少收敛更慢。训练脚本是同时优化三个 loss但如果整体 epoch 数只够检测分支收敛掩码自然跟不上。另外验证集图片数量太少放大了掩码误差的观感。解决把 epoch 数从默认值提到额外 10 个并单独观察 loss 曲线里 mask_loss 这一项。如果 mask_loss 在 0.2 附近横盘说明模型卡住了常见做法是查一下标注文件里是不是混入了几张掩码完全空白的图把这类脏样本删掉重训mask_loss 通常会立刻掉下来。5.4 类别 ID 混乱导致预测结果张冠李戴现象模型输出的掩码把画面里的书桌、地板也标成猫形状完全不对。原因最常见的两种。一是数据集类列表里 BG 被挤到了非 0 位置比如写成 [cat, BG]二是训练和推理用了两份不同的 config训练时类别是猫推理时换了配置但 checkpoint 没换类别映射对不上。解决强制检查类别注册函数保证 class_names 第 0 位永远是 BG。写一段验证脚本把 checkpoint 的名称和训练时的 config 绑在一起记录推理时从记录反查类别列表不要凭记忆手动填。5.5 Python 依赖版本冲突TensorFlow 和 Keras 的对应关系现象import 阶段就报 AttributeErrormrcnn/model.py 里某个 Keras API 不存在或者梯度计算报错。原因Matterport 版 Mask R-CNN 对 TensorFlow 1.x 和 Keras 2.x 的版本有硬性要求新版 TensorFlow 2.10 移除了大量旧 API直接跑必炸。解决按 requirements.txt 创建虚拟环境安装TensorFlow 固定为 1.14 或 1.15Keras 固定为 2.2.4。如果机器是 Python 3.8 以上的版本装不上 TF1.x就改用 GPU 镜像或降级 Python。这一步是最劝退的但环境对了之后训练过程顺很多。6. 用验证脚本检验分割效果mAP 数值与可视化输出训练结束后有两件事必做跑验证集算 mAP把预测掩码画出来肉眼看一遍。cat.py 自带评估入口用法是把 --weights 指向训练好的 checkpointpython samples/cat/cat.py evaluate --datasetcat_dataset --weightslast评估结果会打印出类似mAP0.50 0.84的数值。这个指标的含义是预测掩码和标注掩码的 IoU 超过 0.5 就算命中0.84 对单类猫脸分割来说属于正常偏上的水平。如果低于 0.5不要先怀疑模型去检查标注框和质量这一步能筛掉至少一半的问题。可视化验证我一般写一个几行的小脚本加载模型后对单张图做预测model modellib.MaskRCNN(modeinference, configconfig, model_dirMODEL_DIR) model.load_weights(checkpoint_path, by_nameTrue) results model.detect([image], verbose1) r results[0] visualize.display_instances(image, r[rois], r[masks], r[class_ids], [BG, cat], r[scores])display_instances 会把每只猫的掩码、边界框和置信度画在同一张图上保存下来和原标注逐张对比。这个步骤不许跳过因为 mAP 只能告诉你平均水准掩码边缘的毛刺、耳朵尖被削平这类问题必须靠肉眼才能发现。对比完我会做最后一件事随机挑训练集里没出现过的猫脸照片直接用同一份 checkpoint 推理。这一步验证的是模型有没有记住了训练集而不是学会了「猫脸长什么样」。如果新照片掩码明显劣化说明过拟合了回去调 dropout 参数或者增加训练数据。从那以后我每次训完分割模型都要强制走一遍「评估 mAP、可视化抽查、新图泛化测试」这条完整流程三关都过了才敢把权重交出去这个习惯已经帮我挡掉了至少三次答辩现场翻车的风险。希望帮到你。本文还有配套的精品资源点击获取