深度学习理论与实战:提高篇(14)——Mask R-CNN代码简介
编者按:本文节选自《深度学习理论与实战:提高篇 》一书,原文链接http://fancyerii.github.io/2019/03/14/dl-book/ 。作者李理,环信人工智能研发中心vp,有十多年自然语言处理和人工智能研发经验,主持研发过多款智能硬件的问答和对话系统,负责环信中文语义分析开放平台和环信智能机器人的设计与研发。
Facebook(Mask R-CNN的作者He Kaiming等人目前在Facebook)的实现在这里。但是这是用Caffe2实现的,本书没有介绍这个框架,因此我们介绍Tensorflow和Keras的版本实现的版本。但是建议有兴趣的读者也可以尝试一下Facebook提供的代码。
安装demo.ipynb
jupyter notebook打开文件samples/demo.ipynb,运行所有的Cell
这里是使用预训练的模型,会自动上网下载,所以第一次运行会比较慢。这是下载模型参数的代码:
# 加载模型参数 model.load_weights(COCO_MODEL_PATH, by_name=True)
检测结果r包括rois(RoI)、masks(对应RoI的每个像素是否属于目标物体)、scores(得分)和class_ids(类别)。
下图是运行的效果,我们可以看到它检测出来4个目标物体,并且精确到像素级的分割处理物体和背景。
train_shapes.ipynb
除了可以使用训练好的模型,我们也可以用自己的数据进行训练,为了演示,这里使用了一个很小的shape数据集。这个数据集是on-the-fly的用代码生成的一些三角形、正方形、圆形,因此不需要下载数据。
对于我们自己的数据集,我们需要继承utils.Dataset类,并且重写如下方法:
在重写这3个方法之前我们首先来看load_shapes,这个函数on-the-fly的生成数据。random_image函数随机的生成图片的位置,请读者仔细阅读代码注释。
上面的函数是我们为了生成(或者读取磁盘的图片)而写的代码。接下来我们需要重写上面的三个函数,我们首先来看load_image:
这个函数很直白,使用opencv的函数在image上绘图,正方形和圆形都很简单,就是等边三角形根据中心点和size(中心点到顶点的距离)求3个顶点的坐标需要一些平面几何的知识。
对于我们随机生成的性质,只要知道哪种shape以及中心点和size,我们可以计算出这个物体(shape)到底包含哪些像素。对于真实的数据集,这通常是人工标注出来的。
上面的代码还判断了一些info[“source”],如果是”shapes”,说明是我们生成的图片,直接返回shape的名字,否则调用基类的image_reference。下面我们来生成一些图片看看。
随机生成的图片如下图所示,注意,因为每次都是随机生成,因此读者得到的结果可能是不同的。左图是生成的图片,右边是mask。
model = modellib.MaskRCNN(mode="training", config=config, model_dir=MODEL_DIR)
因为我们的训练数据不多,因此使用预训练的模型进行Transfer Learning会效果更好。
我们这里值训练heads就够了。上面的代码加载一张图片,结果如下图所示,它显示的是真正的(gold/ground-truth) Bounding box和Mask。
图:模型预测的结果inspect_data.ipynb
最重要的代码就是bbox = utils.extract_bboxes(mask)。最终得到的图片如下图所示。
为了可视化Mask缩放,我们来看几个例子。图:mini mask和增强Anchor Stride:在FPN网络结构下,前几层的feature map是高分辨率的。比如输入图片是1024x1024,则第一层的feature map是256x256,这将产生大约200k个anchor(2562563),这些anchor是32x32的,而它们的stride是4个像素,因此会有大量重叠的anchor。如果我们每隔一个cell(而不是每个cell)生成一次anchor,这将极大降低计算量。这里使用的stride是2,这和论文使用的1不同。生成anchor的代码如下:我们在训练Mask R-CNN的时候,会计算候选的区域和真实的目标区域的IoU,从而选择正例和负例。
对于随机的一个图片,这里生成了4092个anchor,其中3个正样本,253个负样本,其余的都是无用的样本。下图是3个正样本;下图是负样本;而下图是无用的数据。
demo.ipynb
jupyter notebook打开文件samples/demo.ipynb,运行所有的Cell
这里是使用预训练的模型,会自动上网下载,所以第一次运行会比较慢。这是下载模型参数的代码:
# 加载模型参数 model.load_weights(COCO_MODEL_PATH, by_name=True)
检测结果r包括rois(RoI)、masks(对应RoI的每个像素是否属于目标物体)、scores(得分)和class_ids(类别)。
下图是运行的效果,我们可以看到它检测出来4个目标物体,并且精确到像素级的分割处理物体和背景。
train_shapes.ipynb
除了可以使用训练好的模型,我们也可以用自己的数据进行训练,为了演示,这里使用了一个很小的shape数据集。这个数据集是on-the-fly的用代码生成的一些三角形、正方形、圆形,因此不需要下载数据。
对于我们自己的数据集,我们需要继承utils.Dataset类,并且重写如下方法:
在重写这3个方法之前我们首先来看load_shapes,这个函数on-the-fly的生成数据。
random_image函数随机的生成图片的位置,请读者仔细阅读代码注释。
上面的函数是我们为了生成(或者读取磁盘的图片)而写的代码。接下来我们需要重写上面的三个函数,我们首先来看load_image:
这个函数很直白,使用opencv的函数在image上绘图,正方形和圆形都很简单,就是等边三角形根据中心点和size(中心点到顶点的距离)求3个顶点的坐标需要一些平面几何的知识。
对于我们随机生成的性质,只要知道哪种shape以及中心点和size,我们可以计算出这个物体(shape)到底包含哪些像素。对于真实的数据集,这通常是人工标注出来的。
上面的代码还判断了一些info[“source”],如果是”shapes”,说明是我们生成的图片,直接返回shape的名字,否则调用基类的image_reference。下面我们来生成一些图片看看。
随机生成的图片如下图所示,注意,因为每次都是随机生成,因此读者得到的结果可能是不同的。左图是生成的图片,右边是mask。
model = modellib.MaskRCNN(mode="training", config=config, model_dir=MODEL_DIR)
因为我们的训练数据不多,因此使用预训练的模型进行Transfer Learning会效果更好。
我们这里值训练heads就够了。
上面的代码加载一张图片,结果如下图所示,它显示的是真正的(gold/ground-truth) Bounding box和Mask。
图:模型预测的结果
inspect_data.ipynb
最重要的代码就是bbox = utils.extract_bboxes(mask)。最终得到的图片如下图所示。
为了可视化Mask缩放,我们来看几个例子。
图:mini mask和增强
Anchor Stride:在FPN网络结构下,前几层的feature map是高分辨率的。比如输入图片是1024x1024,则第一层的feature map是256x256,这将产生大约200k个anchor(2562563),这些anchor是32x32的,而它们的stride是4个像素,因此会有大量重叠的anchor。如果我们每隔一个cell(而不是每个cell)生成一次anchor,这将极大降低计算量。这里使用的stride是2,这和论文使用的1不同。生成anchor的代码如下:
我们在训练Mask R-CNN的时候,会计算候选的区域和真实的目标区域的IoU,从而选择正例和负例。
对于随机的一个图片,这里生成了4092个anchor,其中3个正样本,253个负样本,其余的都是无用的样本。下图是3个正样本;下图是负样本;而下图是无用的数据。