在我们学习的这个项目中,模型主要分为两种状态,即进行推断用的inference模式和进行训练用的training模式。所谓推断模式就是已经训练好的的模型,我们传入一张图片,网络将其分析结果计算出来的模式。
本节我们从demo.ipynb入手,一窥已经训练好的Mask-RCNN模型如何根据一张输入图片进行推断,得到相关信息,即inference模式的工作原理。
一、调用推断网络
网络配置
首先进行配置设定,设定项都被集成进class config中了,自建新的设定只要基础改class并更新属性即可,在demo中我们直接使用COCO的预训练模型所以使用其设置即可,但由于我们想检测单张图片,所以需要更新几个相关数目设定:
# 父类继承了Config类,目的就是记录配置,并在其基础上添加了几个新的属性
class InferenceConfig(coco.CocoConfig):
# Set batch size to 1 since we'll be running inference on
# one image at a time. Batch size = GPU_COUNT * IMAGES_PER_GPU
GPU_COUNT = 1
IMAGES_PER_GPU = 1 config = InferenceConfig()
config.display()
打印出配置如下:
Configurations:
BACKBONE resnet101
BACKBONE_STRIDES [4, 8, 16, 32, 64]
BATCH_SIZE 1
BBOX_STD_DEV [ 0.1 0.1 0.2 0.2]
COMPUTE_BACKBONE_SHAPE None
DETECTION_MAX_INSTANCES 100
DETECTION_MIN_CONFIDENCE 0.7
DETECTION_NMS_THRESHOLD 0.3
FPN_CLASSIF_FC_LAYERS_SIZE 1024
GPU_COUNT 1
GRADIENT_CLIP_NORM 5.0
IMAGES_PER_GPU 1
IMAGE_CHANNEL_COUNT 3
IMAGE_MAX_DIM 1024
IMAGE_META_SIZE 93
IMAGE_MIN_DIM 800
IMAGE_MIN_SCALE 0
IMAGE_RESIZE_MODE square
IMAGE_SHAPE [1024 1024 3]
LEARNING_MOMENTUM 0.9
LEARNING_RATE 0.001
LOSS_WEIGHTS {'rpn_class_loss': 1.0, 'rpn_bbox_loss': 1.0, 'mrcnn_class_loss': 1.0, 'mrcnn_bbox_loss': 1.0, 'mrcnn_mask_loss': 1.0}
MASK_POOL_SIZE 14
MASK_SHAPE [28, 28]
MAX_GT_INSTANCES 100
MEAN_PIXEL [ 123.7 116.8 103.9]
MINI_MASK_SHAPE (56, 56)
NAME coco
NUM_CLASSES 81
POOL_SIZE 7
POST_NMS_ROIS_INFERENCE 1000
POST_NMS_ROIS_TRAINING 2000
PRE_NMS_LIMIT 6000
ROI_POSITIVE_RATIO 0.33
RPN_ANCHOR_RATIOS [0.5, 1, 2]
RPN_ANCHOR_SCALES (32, 64, 128, 256, 512)
RPN_ANCHOR_STRIDE 1
RPN_BBOX_STD_DEV [ 0.1 0.1 0.2 0.2]
RPN_NMS_THRESHOLD 0.7
RPN_TRAIN_ANCHORS_PER_IMAGE 256
STEPS_PER_EPOCH 1000
TOP_DOWN_PYRAMID_SIZE 256
TRAIN_BN False
TRAIN_ROIS_PER_IMAGE 200
USE_MINI_MASK True
USE_RPN_ROIS True
VALIDATION_STEPS 50
WEIGHT_DECAY 0.0001
模型初始化
首先初始化模型,然后载入预训练参数文件,在末尾我可视化了模型,不过真的太长了,所以注释掉了。在第一步初始化时就会根据mode参数的具体值建立计算图,本节介绍的推断网络就是在mode参数设定为"inference"时建立的计算网络。
# Create model object in inference mode.
model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=config) # Load weights trained on MS-COCO
model.load_weights(COCO_MODEL_PATH, by_name=True) # model.keras_model.summary()
检测图片
# Load a random image from the images folder
file_names = next(os.walk(IMAGE_DIR))[2] # 只要是迭代器调用next方法获取值,学习了
image = skimage.io.imread(os.path.join(IMAGE_DIR, random.choice(file_names)))
print(image.shape)
# Run detection
results = model.detect([image], verbose=1) # Visualize results
r = results[0]
visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'],
class_names, r['scores'])
读取一张图片,调用model的detect方法,即可输出结果,最后使用辅助方法可视化结果:
二、推断逻辑概览
inference的前向逻辑如下图所示,我们简单的看一下其计算流程是怎样的,
- 左上模块为以ResNet101为基础的FPN特征金字塔网络的特征提取逻辑,可以看到,作者并没有直接将up-down特征使用,而是又做了一次3*3卷积进行了进一步的特征融合。
- 出来的各层FPN特征首先(各自独立地)进入了RPN处理层:根据锚框数目信息确定候选区域的分类(前景背景2分类)和回归结果。
rpn_class:[batch, num_rois, 2]
rpn_bbox:[batch, num_rois, (dy, dx, log(dh), log(dw))] - 有了众多的候选区域,我们将之送入Proposal筛选部分,首先根据前景得分排序进行初筛(配置会指定这一步保留多少候选框),然后为非极大值抑制做准备:用RPN的回归结果修正anchors,值得注意的是anchors都是归一化的这意味着修值之后还需要做检查以防越界,最后非极大值一致,删减的太多了的话就补上[0, 0, 0, 0]达到配置文件要求的数目(非极大值部分会造成同一个batch中不同图片的候选框数目不一致,但是tensor的维数不能参差不齐,所以要补零使得各张图片候选区域数目一致)
rpn_rois:[IMAGES_PER_GPU, num_rois, (y1, x1, y2, x2)]
- 根据候选区的实际大小(归一化候选区需要映射回原图大小)为候选区选择合适的RPN特征层,ROI Align处理(实际上就是抠出来进行双线性插值到指定大小),得到我们需要的众多等大子图
- 对这些子图各自独立的进行分类/回归
mrcnn_class_logits: [batch, num_rois, NUM_CLASSES] classifier logits (before softmax)
mrcnn_class: [batch, num_rois, NUM_CLASSES] classifier probabilities
mrcnn_bbox(deltas): [batch, num_rois, NUM_CLASSES, (dy, dx, log(dh), log(dw))] - 在分类回归之后使用回归结果对候选框进行修正,然后重新进行FPN特征层选择和ROI Align特征提取,最后送入Mask网络,进行Mask生成。
最后,我们希望网络输出下面的张量:
# num_anchors, 每张图片上生成的锚框数量
# num_rois, 每张图片上由锚框筛选出的推荐区数量,
# # 由 POST_NMS_ROIS_TRAINING 或 POST_NMS_ROIS_INFERENCE 规定
# num_detections, 每张图片上最终检测输出框,
# # 由 DETECTION_MAX_INSTANCES 规定 # detections, [batch, num_detections, (y1, x1, y2, x2, class_id, score)]
# mrcnn_class, [batch, num_rois, NUM_CLASSES] classifier probabilities
# mrcnn_bbox, [batch, num_rois, NUM_CLASSES, (dy, dx, log(dh), log(dw))]
# mrcnn_mask, [batch, num_detections, MASK_POOL_SIZE, MASK_POOL_SIZE, NUM_CLASSES]
# rpn_rois, [batch, num_rois, (y1, x1, y2, x2, class_id, score)]
# rpn_class, [batch, num_anchors, 2]
# rpn_bbox [batch, num_anchors, 4]
具体每种张量的意义我们会在源码分析中一一介绍。