news 2026/10/11 22:10:02

基于Mask R-CNN的猫脸实例分割实战:从自定义数据集到模型训练

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Mask R-CNN的猫脸实例分割实战:从自定义数据集到模型训练

简介:一份基于MaskRCNN的猫脸分割项目,面向深度学习初学者与计算机相关专业学生,适合课程作业、毕设或项目演示场景。项目提供完整Python源码(含train.py/test.py与配置脚本)、猫脸图片数据集及封装好的数据参考包,解压并改英文路径即可运行,便于快速复现分割效果,也可替换自定义数据集进行拓展训练。压缩包共22个文件,以png图像、py脚本、md说明和txt文本为主,整体仅11.16MB,轻量易传输。目前已有343人学习使用,验证了其可操作性。资源在源码之外还给出README与运行说明,有助于理解MaskRCNN的推理与训练流程,遇到环境依赖问题时也能借助文档排查,适合想从代码层面掌握分割模型改造思路的学习者。

1. 把 Mask R-CNN 用到猫脸分割:这个项目解决的问题是什么

先给结论:基于 Mask R-CNN 做猫脸分割,本质是一个实例分割任务,输入一张猫的照片,输出结果里同时包含每一只猫脸的矩形框和像素级轮廓 mask。它和语义分割的区别在于——照片里两只猫脸贴在一起时,语义分割会把它们糊成同一块颜色,实例分割则能输出“猫 A 的轮廓”和“猫 B 的轮廓”两个独立对象。这个项目源码里带了现成的猫脸数据集,也允许你换成自己的图片重新训练,所以它不是一次性 demo,而是一套可以迁移到其他物体分割任务的完整脚手架。适合三类人:做宠物识别相关产品验证的工程师、需要交课程设计的学生、以及想在自有数据上跑通实例分割但还没搭好 pipeline 的开发者。猫脸分割真正难的点不在网络结构本身,而在数据标注规范和类别配置,这两件事做好了,后面训练只是等时间。

2. 为什么猫脸分割要选 Mask R-CNN:从任务边界到网络结构

2.1 实例分割与语义分割、目标检测的边界在哪

先对齐一个容易混淆的概念:同样是“分割”,语义分割、实例分割、全景分割的输出格式完全不同。语义分割输出的是类别掩码图,猫脸和狗脸都标出来了,但两只猫之间不做区分,它们共享同一个“猫”标签;目标检测只输出矩形框,不知道轮廓,矩形框里可能包含背景;实例分割则要求“每个个体一个 mask”,同类目标也要逐一编号。猫脸场景恰恰是实例分割最典型的应用场景:画面里经常出现多只猫、彼此遮挡、角度俯仰变化大,如果用语义分割,两只猫的耳朵和胡须交叉在一起,输出结果根本无法用于后续的裁剪、抠图或贴图操作。

项目标题里特别强调“猫脸分割”而不是“猫脸检测”,说明作者对输出精度的要求是到轮廓级别的。常见的遥感图像分割、农田分割这类任务关注的是区域,而猫脸分割关注的是个体边界,两者对 mask 边缘质量的要求差别很大。Mask R-CNN 输出的 mask 是基于 ROI 对齐后的逐像素二分类,边缘质量比单纯用语义分割网络后处理要高一个档次,这也是为什么这个方向仍然值得用 Mask R-CNN 而不是全盘转向轻量方案。

关于“检测 + 分割”的联合框架,可以理解为:模型先回答“图里有没有猫脸、猫脸在哪”,再回答“猫脸的精确边界是什么”。这两步在 Mask R-CNN 里是同一个网络的两个输出头,共享底层的特征提取层,所以训练时检测分支和分割分支互相提供正则化,比分开训练两个模型再拼装更稳。这也是它和两阶段检测器 Faster R-CNN 的本质区别——Faster R-CNN 止步于框,Mask R-CNN 在框的基础上多走了一步。

2.2 Mask R-CNN 的两阶段结构和 mask 分支

Mask R-CNN 是在 Faster R-CNN 的检测骨架上扩展出一个 mask 输出分支,整体结构分两段走。第一段是 RPN(Region Proposal Network),在 backbone 输出的特征图上滑动 anchor,生成一批候选框,只做“有没有目标”的粗筛;第二段是 Fast R-CNN 头,对这些候选框做精确分类和 bbox 回归,同时 mask 分支在同一个 ROI 上输出一个 K×m×m 的张量,K 是类别数,m 是 mask 分辨率(常见是 28×28),每个类别对应一个掩码。

这里不能绕过的一个关键组件是 ROI Align。ROI Pooling 在处理任意尺寸的候选框时要做两次量化取整,框的坐标和网格划分都会损失亚像素精度,对分类影响不大,但对 mask 这种逐像素任务就是致命的——边缘偏移一两个像素,分割结果看起来就是歪的。ROI Align 用双线性插值把每个网格单元内的采样点坐标算出来,不做取整,梯度也能回传到采样点。猫脸轮廓最敏感的是耳朵尖和下巴边缘,这两处恰恰是量化误差最容易放大的地方,所以选 Mask R-CNN 而不是早期 ROI Pooling 方案,不是精度洁癖,是实际效果差异。

Backbone 通常是 ResNet-50 或 ResNet-101 加 FPN(特征金字塔)。FPN 让不同尺寸的猫脸在不同尺度的特征层上被检测,小脸用高层大特征图,大脸用低层小特征图。自定义数据集只有“猫脸”一个类别时,我一般直接用 ResNet-50,训练速度更快,显存占用更友好;如果数据集里混入了不同品种猫、不同毛发纹理,或者你后续要扩展到“猫脸 + 狗脸 + 鸟脸”多类别,再换 ResNet-101 不迟。还有一个参数需要在一开始就改对:NUM_CLASSES 必须等于 1(背景)加上真正的目标类别数。猫脸数据集只有 cat 一个目标类,那么 NUM_CLASSES = 2;后面自定义数据集如果你加了 dog,就是 NUM_CLASSES = 3。这个数填错,后面所有层的输出维度全错,训练出的模型要么 loss 不降,要么推理时一片黑。

2.3 与 YOLOv8-seg 等轻量方案的取舍

标题锁定了 Mask R-CNN,但作为一线工程师,选型时心里还是要有一杆秤:YOLOv8-seg 这类单阶段实例分割方案现在也很成熟,输出的是 mask 系数加原型 mask 的组合,速度快得多。为什么这个项目场景仍然值得用 Mask R-CNN?核心原因是“检测 + 分割”的联合框架在目标重叠和边缘细节上的表现更稳。猫脸图最常见的构图是两只猫挤在一个猫窝里,互相遮挡严重。YOLOv8-seg 的单阶段输出对遮挡目标的分离能力相对弱,小目标漏检率高一些;Mask R-CNN 的两阶段结构先筛出候选框再做像素级分割,相当于给每个目标单独一次“仔细看”的机会,遮挡场景下的 mask 完整性更好。

另一方面是数据集生态。Mask R-CNN 的成熟实现(在 GitHub 上被引用最多的那套,作者是 matterport,这里只提做法不提链接)直接把训练、评估、可视化的代码都拆好了,数据层用 COCO JSON 格式组织,自定义数据集只需要从标注工具导出匹配格式,改动量最小。YOLOv8-seg 虽然官方也支持自定义数据集,但它的标注格式是每个目标一行文本加归一化坐标,人与工具链更偏向 Poly-YOLO 风格。已经拿到“猫脸 json 标注”的人,直接走 Mask R-CNN 的 COCO 管线是最省事的。如果是从零开始且对推理速度有硬指标,比如要在手机上跑实时分割,再考虑单阶段方案。这个项目的定位是“把分割做准”,不是“把分割做快”,选型逻辑是一致的。

还有一个小经验:源码 + 数据集打包在一起的项目,数据组织方式往往是作者已经调通的,不要一上来就按自己的习惯重排目录。先按项目自带的目录结构跑通一次,确认 loss 曲线正常后再动数据路径,否则你会分不清报错是代码问题还是数据格式问题。这个习惯能帮你省掉大量联调时间。

3. 跑通默认项目:环境、目录与训练的最小命令

3.1 源码里的目录结构怎么组织

拿到 zip 解压之后,先不要急着双击任何 py 文件,花两分钟把目录结构看一遍。常见的 Mask R-CNN 项目会包含这几类东西:根目录下是一批 Python 脚本和 notebook,比如 train.py、evaluate.py、predict.py、model.py、visualize.py;data 目录下按训练集和验证集分图片和标注文件;weights 目录放预训练权重和训练产出的模型。用 tree 命令看一遍最直接:

$ tree -L 2 . ├── train.py # 训练入口 ├── evaluate.py # 验证入口,输出 mAP/mask IoU ├── predict.py # 单张图片推理入口 ├── model.py # Mask R-CNN 网络定义 ├── visualize.py # 可视化工具 ├── data │ ├── train │ │ ├── cat_001.jpg │ │ └── cat_001.json │ └── val │ ├── cat_002.jpg │ └── cat_002.json └── weights ├── mask_rcnn_coco.h5 # COCO 预训练权重 └── catface.h5 # 训练产出的模型

train 和 val 分开是最低要求,如果项目里只有一个总目录,训练脚本通常也会按比例自己切分,但效果不如手动分稳。我一般会再单独建一个 test 目录放没参与训练的照片,用来做最终效果抽检。这里要说明的是,图片和 json 标注文件名必须一一对应,后缀不同没关系,前缀要一致;有些新手会把标注文件统一丢到一个 label 文件夹里,然后把图片和标注分开管理,这种结构在 COCO 管线里是很别扭的,因为加载代码默认就是“同名文件、同目录”。

GPU 环境方面,Mask R-CNN 的依赖比较重,常见配置是 Python 3.7/3.8、TensorFlow 1.15 或 TensorFlow 2.x 的兼容模式、Keras 2.x、pycocotools。这个项目如果自带 requirements.txt,直接按里面的版本装;没有的话,用 conda 建一个 py37 环境,装 tensorflow-gpu 1.15、keras 2.3.1、imgaug 0.2.5、pycocotools,这套组合是社区里验证过最稳的。不要一上来就装最新的 TF 2.10,老代码里很多 API 调用在 TF2 完整版下会报错,反而浪费半天。

3.2 修改配置类:类别数、批量大小与图片尺寸

进入训练前要改的是 config 类。绝大多数 Mask R-CNN 实现都会在 train.py 或专门的 config.py 里定义一个继承自默认 Config 的类。以猫脸分割为例,最小改动如下:

class CatFaceConfig(Config): NAME = "catface" # 训练产物的标识前缀 NUM_CLASSES = 2 # 1 背景 + 1 目标类别(cat) GPU_COUNT = 1 # 单卡训练 IMAGES_PER_GPU = 1 # 每张卡每次迭代读几张图 STEPS_PER_EPOCH = 200 # 每个 epoch 走多少 step DETECTION_MIN_CONFIDENCE = 0.7 # 推理时低于该置信度的框不输出

类别数是这里最容易翻车的参数。默认 COCO 是 81 类,你现在改成 2 类,网络最后几层(分类层和 mask 层)的输出维度全变了,所以加载预训练权重时不能整包加载,后面会专门讲 exclude 怎么填。IMAGES_PER_GPU 直接影响显存占用和 BN 统计,8GB 显存建议设 1,12GB 以上可以试 2;设大了如果爆显存,报错信息通常会直接告诉你 CUDA out of memory,这个谁都要经历几次。

图片尺寸默认 1024×1024,如果数据集里的猫脸图普遍很小,或者你只有入门级 GPU,可以下调到 512×512 或 640×640。但要注意,尺寸降太多会导致小目标在 FPN 高层特征图上几乎没有像素,漏检率会上升。一个可行的做法是:先用 512 尺寸跑通流程,确认代码和数据没问题,再切回 1024 跑正式训练。

3.3 训练、验证、推理三条命令

配置改好之后,训练就三条命令。以常见实现为例:

# 训练:加载 COCO 预训练权重,前几层冻结,微调 python train.py train --dataset=data --weights=coco --epochs=50 # 验证:输出验证集上的 bbox mAP 和 mask mAP python evaluate.py --dataset=data --weights=weights/catface.h5 # 推理:单张图片预测,保存可视化结果 python predict.py --image=test.jpg --weights=weights/catface.h5 --out=result.png

train 命令里的 --weights=coco 表示从 COCO 预训练权重开始训练,而不是从随机初始化开始。随机初始化不是不行,但 Mask R-CNN 网络深、参数量大,小数据集上从头训练几十轮基本不收敛,loss 曲线一塌糊涂。--epochs=50 在这个数据集规模下够用,通常 30 轮之后 mAP 就趋于平缓了。evaluate 命令主要看两个输出:bbox mAP 和 segm mAP(mask IoU),猫脸分割项目重心在后面那个数字。predict 命令适合单张抽检,批量测试建议写一个小循环或者用项目自带的 batch 推理脚本。

这三条命令也对应了项目交付时最核心的三个证据:训练日志里的 loss 曲线、验证集上的 mAP 数值、对新照片的可视化推理结果。跑通这三步之后,才算真正把项目握在手里了。后面自定义数据集时,你会发现你只是在更换 data 目录内容并修改 NUM_CLASSES,代码路径几乎不用动。

4. 自定义数据集:从标注到训练的全流程

4.1 用 labelme 标注自己的猫脸数据

Mask R-CNN 的自定义数据集适配,链路通常是“图片 + labelme 标注 → 转 COCO JSON → 训练”。labelme 是常用的多边形标注工具,安装简单,界面里用鼠标点出猫脸轮廓,每张脸画一个 polygon,label 统一填 cat。它导出的也是 JSON 文件,和图片存在同一目录,文件名前缀相同。

标注时有一个操作习惯要注意:贴轮廓画点时,耳朵尖、胡须根、下巴轮廓这些高曲率位置要多打点,平滑区域可以少打点,但至少保证两个相邻点之间不要跨过眼睛或鼻尖。如果两只猫脸紧贴,标注的时候按可见轮廓画,遮挡部分不需要脑补出来,Mask R-CNN 的 mask 分支学习的是“可见区域的边界”,不需要你替它补全被挡住的另一半。这个原则很重要,因为如果你把遮挡区域的 mask 也画上,模型会在训练时看到矛盾的监督信号——同一张嘴,这张图里是可见的,那张图里被另一只猫挡住,它不知道该学哪个,最后 mask 边缘会糊。

标注数量上,一个类别做实例分割,起步建议 200~500 张图,每张图 1~4 只猫。这个量级训练出的模型已经能在相似场景下工作;要做到多姿态、多品种、不同光照下稳定,再往 1000 张以上走。这个项目自带的数据集如果只有一两百张,那它定位就是“跑通流程 + 验证效果”,不是“生产级模型”,心里要有数。

labelme 导出的 JSON 结构里有几个字段后面转换脚本要用到:imagePath 是图片文件名,shapes 是列表,其中每个元素包含 label(类别名)和 points(多边形顶点列表,顺序是你画点时点击的顺序)。点坐标是浮点数,这个细节会在转换脚本里坑到你,下面细说。

4.2 把 labelme JSON 转成 COCO 格式:转换脚本与四个边界坑

Mask R-CNN 的加载器通常要求 COCO JSON 格式:categories 定义类别 id 到名称的映射,images 记录每张图的文件名、尺寸和 id,annotations 记录每个目标的 bbox、面积、多边形坐标。转换脚本的核心是把 labelme 的 shapes 数组翻译成 COCO 的 annotations 数组。下面这段脚本是常见做法,我按自己的习惯整理过,可以直接复制改路径:

import json import os import glob from skimage.measure import regionprops from pycocotools import mask as maskUtils import numpy as np def labelme_to_coco(labelme_dir, output_path, categories=["cat"]): cat_id_map = {name: i + 1 for i, name in enumerate(categories)} coco = { "images": [], "annotations": [], "categories": [{"id": i + 1, "name": name} for i, name in enumerate(categories)], } ann_id = 0 for img_id, json_path in enumerate(sorted(glob.glob(os.path.join(labelme_dir, "*.json")))): with open(json_path, "r", encoding="utf-8") as f: labelme = json.load(f) image_path = os.path.join(labelme_dir, labelme["imagePath"]) width = labelme.get("imageWidth", 0) height = labelme.get("imageHeight", 0) coco["images"].append({ "id": img_id, "file_name": labelme["imagePath"], "width": width, "height": height, }) for shape in labelme["shapes"]: label_name = shape["label"] if label_name not in cat_id_map: continue category_id = cat_id_map[label_name] points = shape["points"] # 浮点坐标,绝不能取整 # 计算 COCO 需要的 bbox:xywh 格式 xs = [p[0] for p in points] ys = [p[1] for p in points] x_min, x_max = min(xs), max(xs) y_min, y_max = min(ys), max(ys) bbox = [x_min, y_min, x_max - x_min, y_max - y_min] # 计算多边形面积 poly = [p for xy in points for p in xy] rle = maskUtils.frPyObjects([poly], height, width) area = float(maskUtils.area(rle[0])) # 单个目标的标注 ann_id += 1 coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": category_id, "bbox": bbox, "area": area, "segmentation": [poly], "iscrowd": 0, }) with open(output_path, "w", encoding="utf-8") as f: json.dump(coco, f, ensure_ascii=False) print(f"转换完成:{len(coco['images'])} 张图,{len(coco['annotations'])} 个目标")

脚本逻辑不复杂,遍历每个 labelme JSON,把 imagePath、尺寸搬到 COCO 的 images 列表,把每个 shape 变成 annotation。转换过程中有四个边界坑,基本是血泪经验总结:

第一,坐标必须是浮点,不能 int() 取整。COCO 的 segmentation 坐标是连续值,取整后 mask 边缘会整体偏移,训练出来 mask 边界总是差一两个像素。第二,bbox 的宽高不能为负数。虽然正常标注不会出现 x_max < x_min,但如果你的标注工具允许你反向拖拽画框,转换结果就会出现负宽高,训练时 ROI Align 直接崩。第三,area 要用多边形面积,不能用 bbox 宽高估算。COCO 评估时 mask IoU 计算的是真实 mask 与预测 mask 的重叠面积,area 只用于 AP 计算的排序归一化,用 bbox 面积会引入偏差,导致 mAP 数值虚高。第四,单张图没有标注时,annotations 对应为空列表,不要跳过这张图本身。某些脚本在“某个 json 里没有 shapes”时会直接把这张 image 也从 images 里删掉,这样训练时图片 id 和标注 id 错位,索引错乱。

转完之后一定要验证一下 JSON 能被 pycocotools 正常加载,这个步骤很多人都跳过,直到训练时才炸:

from pycocotools.coco import COCO coco = COCO("data/val_coco.json") print(len(coco.getImgIds()), len(coco.getAnnIds()))

能跑通、数字合理,再进训练循环。这一步省下来,后面调试的时间会成倍加回去。

4.3 修改网络配置和训练参数适配自己的数据集

数据集换好了,回到配置类。自定义数据集如果是“猫脸 + 狗脸”,NUM_CLASSES 是 3;只有猫脸则保持 2。加载预训练权重时要 exclude 掉网络输出层,否则会报维度不匹配:

model.load_weights("weights/mask_rcnn_coco.h5", by_name=True, exclude=[ "mrcnn_class_logits", "mrcnn_bbox_fc", "mrcnn_bbox", "mrcnn_mask", ])

exclude 的原理是:COCO 预训练权重是在 81 个类别上训出来的,最后分类层输出维度是 81,mask 分支输出是 81×28×28,而我们的数据集只需要 2 类。加载时跳过这些层,网络会随机初始化这几个输出头,其余骨干层沿用预训练参数。如果不 exclude,Keras 会直接报维度不匹配,或者更糟——某些容错实现会自动丢弃整个权重文件,相当于你从零开始训练,那个 loss 曲线你看了会怀疑人生。

数据增强方面,Mask R-CNN 的常见实现默认支持 imgaug 增强,包括水平翻转、随机亮度对比度、小角度旋转和随机缩放。水平翻转对猫脸几乎是无损增强,建议常开;旋转角度不要超过 15 度,猫脸是人脸朝向判断那种强结构目标,旋转过大反而引入不自然的训练样本。

训练轮次经验:200~500 张自定义数据,epochs 设 30~50,学习率保持默认 0.001,观察训练 loss 和验证 loss 的拐点。小数据集上通常 20 轮左右就已经收敛,后面多跑可能是过拟合,验证 mAP 反而往下掉。建议每 5 个 epoch 存一个 checkpoint,这样如果发现 25 轮最好,可以直接回退用 25 轮的权重,不用重新训练。这就是你的后悔药。

5. 训练中的 4 个高频坑:排查与修复

5.1 Loss 不下降但预测 mask 全黑

现象:训练日志里 loss 前几轮在缓慢下降,但用 predict.py 对新图片推理时,检测不到猫脸,或者输出框是空的、mask 全黑。

原因:最常见的不是网络问题,是类别配置错位。如果你数据集里只有 cat 一个目标类,但 config 里 NUM_CLASSES 写成了 3,网络会把类别索引 1 和 2 都当作有效类别,而你的标注只给类别 1 提供了训练信号,类别 2 从头到尾没有正样本,mask 分支被迫“从来没见过的东西上输出掩码”,结果就是预测输出张量里类别 2 的概率被压得很低,最终检测被抑制。另一个常见原因是加载预训练权重时没有 exclude,导致分类层和 mask 层被错误初始化成随机值,前几十轮基本在随机试错。

解决:先核对 config 的 NUM_CLASSES 等于 1 + 目标类别数,再核对 load_weights 的 exclude 列表是否包含四层。然后看训练日志的前 5 个 epoch:正常情况 loss 从 1.5 左右开始降到 0.8 附近,如果 loss 始终在 2.0 以上不动,基本是数据加载路径错了,模型根本没读到标注。用可视化脚本抽查一张训练图片,确认 mask 叠加显示在猫脸上,再继续训练。

5.2 只检测到图中最大的一只猫,漏掉后面小的

现象:推理一张包含三只猫的合照,输出结果只有一只猫,通常还是脸最大、最居中的那只。

原因:这是 anchor 尺度设置和图片缩放的联动问题。Mask R-CNN 默认 RPN_ANCHOR_SCALES 是 [32, 64, 128, 256, 512],如果你的猫脸图片经过 resize 到 1024×1024 后,远处的猫脸只有 60×60 像素,它主要落在 scale=64 的 anchor 附近;而近处大猫脸有 400×400 像素,对应 scale=256 和 512。如果训练数据里大脸样本远多于小脸,网络会对大 anchor 过度拟合,小脸对应的候选框置信度低,最后非极大值抑制阶段被大框直接压掉。

解决:在 config 里增加一组小尺度 anchor,常见做法是改成 [16, 32, 64, 128, 256],并调小 NMS 阈值观察。同时检查训练数据里小目标和遮挡目标的占比,如果占比很低,先做数据增强里的随机缩放,让同一样本不同尺度出现,而不是依赖 anchor 硬扛。改完 anchor 后要重新加载预训练权重并从头微调,只改 anchor 不重训没什么效果。

5.3 GPU 显存溢出:CUDA out of memory

现象:训练脚本跑不到第一个 epoch 就报 CUDA out of memory,或者运行到一半偶尔爆一次。

原因:配置里 IMAGES_PER_GPU 默认是 2,这个值在 8GB 显存的显卡上跑 1024×1024 输入非常勉强。Mask R-CNN 前向传播时,RPN、分类、bbox 回归、mask 四个分支都要在特征图上保留中间变量,mask 分支的 28×28×K 张量虽然小,但 ROI 数量多时累积起来很可观。很多人以为是显存不够就要换卡,其实很可能只是 batch size 设置超过了硬件承载能力。

解决:把 IMAGES_PER_GPU 降为 1,把图片尺寸从 1024 降到 768 或 640,再从训练日志看每秒处理的图片数。如果显存占用接近上限但没溢出,可以把 RPN_TRAIN_ANCHORS_PER_IMAGE 从默认 256 降到 128,这会减少每个 batch 的 proposal 数量和中间张量数量,对显存释放效果明显。梯度累积是最后一招,每次前向 1 张图、反向 4 步累积梯度,等效 batch size 4,显存占用不变。

5.4 加载预训练权重时 Dimension mismatch

现象:启动训练时 Keras 报错,提示权重维度不匹配,常见于 mrcnn_mask 层,报错信息形如“Cannot load weights: Dimensions mismatch”。

原因:这个报错基本等同于你忘了 exclude。COCO 预训练权重的 mrcnn_mask 层输出维度是 81×28×28,你的模型是 2×28×28,维度不匹配。有些人的习惯是把整个 h5 文件 load 进去再 catch 异常,这是掩耳盗铃——异常被吞掉后,所有层都随机初始化了,训练效果跟从零开始没有区别。

解决:在 load_weights 中显式加 exclude 参数,并打印加载结果确认哪几层被跳过。被跳过的层是当前数据集特有的输出头,本来就该随机初始化;骨干网络层和 RPN 层必须加载成功,否则预训练就白做了。加载后可以顺手做一个 sanity check:打印 model 各层可训练参数数量,确认骨干层参数数不为 0 且和预训练权重一致。

6. 验证与进阶:让模型输出可直接交付的 mask

6.1 用 COCO 评估输出 mAP 与 mask IoU

项目自带的 evaluate.py 跑完会打印两类指标:bbox mAP 和 segm mAP。猫脸分割项目里重点看 segm mAP,它衡量的是预测 mask 与真实 mask 的像素级重叠质量。COCO 的 AP 计算会把 mask 重叠度从 0.5 到 0.95 分 10 档取平均,所以不要盯着单档 IoU 看,要看整体趋势。如果 segm AP@50 能达到 0.85 以上、AP@75 在 0.7 左右,这个模型在自定义场景下已经可用。评估时用 val 集而不是训练集,否则数字没有意义。

6.2 把预测 mask 叠加在原始图上:一个实用的推理脚本

验证完数值指标,还要验证主观效果。我一般会写一个小推理脚本,把预测的 mask 转成彩色叠加层直接输出到图片上,用肉眼看边缘:

from skimage import io from visualize import display_instances import skimage image = skimage.io.imread("test.jpg") results = model.detect([image], verbose=0) r = results[0] # 把 mask 叠加画到原图上,保存为可视化结果 display_instances(image, r["rois"], r["masks"], r["class_ids"], ["BG", "cat"], r["scores"]) skimage.io.imsave("result_overlay.png", image)

display_instances 是 Mask R-CNN 常用实现里的可视化函数,会把 bbox、mask 半透明色块、类别名和置信度一并画在原图上。叠加图主要看两点:mask 边缘是否贴着耳朵轮廓、两个相邻猫脸的 mask 是否粘连。粘连较多说明 NMS 阈值太低或者 mask 分支训练不足,可以调高 DETECTION_MIN_CONFIDENCE 排除低置信度输出,或者回到训练侧增加遮挡样本的比例。

6.3 进阶用法:把 mask 单独导出为 PNG,用于后续环节

很多下游任务不需要叠加图,而是要“干净的 mask 文件”。比如你要做猫脸抠图、给猫脸加 AR 道具,或者把 mask 作为另一个模型的输入。这里有一个小技巧:用模型输出的 r["masks"],它本身是 H×W×N 的布尔数组,N 是检测到的目标数量,直接取每一层存成二值 PNG:

import numpy as np from skimage import io for i in range(r["masks"].shape[2]): mask = (r["masks"][:, :, i] * 255).astype(np.uint8) io.imsave(f"mask_{i}.png", mask)

导出时注意把 mask 缩放到原图尺寸再保存,避免结果和原图没有对齐。这里的经验是:train.py 的 config 里输入尺寸是训练时的尺寸,推理时模型输出 mask 尺寸和输入尺寸一致,而原图可能被 resize 过,导出前需要用 skimage.transform.resize 把 mask 映射回原图尺寸。我一般都会在推理脚本里保存一个“原图尺寸的 mask PNG”,而不是直接保存模型输出尺寸,省得后续再踩一次坐标对齐的坑。

这个项目做到这里,你已经完成了一个完整闭环:数据标注、格式转换、模型训练、指标评估、可视化验证、结果导出。如果未来你换了任务目标,比如要分割鸟脸、分割车辆配件,整个流程不变,只需要换数据、改 NUM_CLASSES、重新训练。我自己的习惯是每次新数据集都先在 5 个 epoch 的小规模训练上验证 loss 和可视化效果,确认没问题后再铺开全量训练,这个习惯帮我挡掉了很多数据标注和格式转换层面的低级错误,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 22:06:21

SLAM源码修改版实战:从编译依赖到ICP/NDT参数调优

简介&#xff1a;面向自动驾驶与机器人领域学习者的《自动驾驶与机器人中的SLAM技术》源码修改版&#xff0c;依据深蓝学院教学与研究需求定制&#xff0c;帮助读者通过可运行的工程代码理解SLAM定位与建图的核心原理&#xff0c;并适配实际项目实践。压缩包共1923个文件&#…

作者头像 李华
网站建设 2026/10/11 22:06:04

基于深度学习的滚动轴承故障诊断:CWRU数据预处理与一维CNN实战

简介&#xff1a;基于深度学习的滚动轴承故障诊断方法项目源码与全部数据&#xff0c;是一套面向计算机相关专业毕业设计的完整Python项目。资源针对正在准备毕设、课程设计或期末大作业的学生&#xff0c;也适合需要项目实战的深度学习学习者。压缩包共41个文件&#xff0c;主…

作者头像 李华
网站建设 2026/10/11 22:04:16

Python手写机器学习算法源码:从线性回归到逻辑回归的梯度与调试实战

简介&#xff1a;一套基于Python的机器学习算法设计源码包&#xff0c;面向机器学习初学者与算法研究者&#xff0c;集中实现了逻辑回归、支持向量机、K均值、岭回归、反向传播神经网络、均值漂移、密度聚类和矩阵分解等经典算法&#xff0c;覆盖分类、回归、聚类与降维等常见建…

作者头像 李华
网站建设 2026/10/11 22:03:56

眼底血管分割数据集实战:从2类标签到可视化全流程

简介&#xff1a;本资源面向医学图像分割方向的初学者与算法实践者&#xff0c;提供一套可直接上手的眼底血管分割数据集与配套工具&#xff0c;帮助解决血管提取任务中数据获取与标签制作的门槛问题。数据集基于DRIVE扩充&#xff0c;图像分辨率为500至1000&#xff0c;训练集…

作者头像 李华
网站建设 2026/10/11 22:02:51

8291张猫狗检测数据集:VOC+YOLO双格式落地实践指南

简介&#xff1a;本资源为面向计算机视觉初学者与算法工程师的猫狗目标检测专用数据集&#xff0c;适用于YOLO系列、Faster R-CNN等主流检测模型的训练与验证。数据集包含8291张高质量JPEG图像及完全对齐的双格式标注文件&#xff1a;1999个Pascal VOC标准XML文件&#xff08;含…

作者头像 李华
网站建设 2026/10/11 22:02:02

Python实现16个经典机器学习算法源码解析与实战

简介&#xff1a;这份基于Python的机器学习算法设计源码包&#xff0c;面向具备一定Python基础和机器学习概念的开发者与学习者&#xff0c;可用于系统掌握多种经典算法的实现与调参思路。资源按算法分章节组织&#xff0c;覆盖分类、回归、聚类、推荐等常见任务&#xff0c;包…

作者头像 李华