Mask R-CNN 是基于 Faster R-CNN 的改进版本,用于实例分割任务,即在物体检测的基础上进一步为每个目标生成像素级的分割掩码。以下是 Mask R-CNN 的主要改进思路及其关键技术点:
1. 引入分割分支
在 Faster R-CNN 的基础上,Mask R-CNN 添加了一个并行的分割分支。这意味着在进行分类和边界框回归的同时,网络还需要生成目标的分割掩码。
- 分割分支:分割分支是一个全卷积网络(FCN),用于预测每个 RoI 的二进制掩码。与分类和回归分支共享特征图,但最终输出的是一个每个 RoI 的分割掩码。
训练过程中Mask分支的目标(proposals)是由RPN提供的,且是根据Fast R-CNN产生的正样本进行训练。
预测时的目标则是由Fast R-CNN提供的
2. RoIAlign 代替 RoIPool
在 Faster R-CNN 中,RoIPool 将不同大小的 RoI 转换为固定大小的特征图。但是,RoIPool 使用了量化操作,可能导致特征对齐不精确,从而影响分割掩码的精度。
- RoIAlign:Mask R-CNN 引入了 RoIAlign 层,去除了量化操作,通过双线性插值的方法,使特征图和 RoI 更加精确对齐,提升了分割的准确度。
2.1RolPooling
两次取整引入了误差导致Misalignment
eg:
import torch
from torchvision.ops import RoIPool
def main():
torch.manual_seed(1)
# 第一次取整是将原图映射到特征图上,根据特征图产生的步距进行取整得到特征图上的预测区域
x = torch.randn((1, 1, 7, 7))
print(f"feature map: \n{x}")
# 原始图像上的左上角坐标(10x10)右下角坐标(224x224)
proposal = [torch.as_tensor([[10, 10, 224, 224]], dtype=torch.float32)]
# 原始图像到特征图步距为32,第二次取整,由于在特征图上预测的区域大小不一,但后续处理需要统一大小
# 为统一大小将预测区域统一尺寸输出 即2x2
roi_pool = RoIPool(output_size=2, spatial_scale=1/32)
roi = roi_pool(x, proposal)
print(f"roi pool: \n{roi}")
if __name__ == '__main__':
main()
运行结果:
2.ROIAlignment
import torch
from torchvision.ops import RoIAlign
# 依据最近的点(f1、f2、f3、f4)的值双线性插值出输出点的值
def bilinear(u, v, f1, f2, f3, f4):
return (1-u)*(1-v)*f1 + u*(1-v)*f2 + (1-u)*v*f3 + u*v*f4
def main():
torch.manual_seed(1)
x = torch.randn((1, 1, 6, 6))
print(f"feature map: \n{x}")
proposal = [torch.as_tensor([[10, 10, 124, 124]], dtype=torch.float32)]
roi_align = RoIAlign(output_size=2, spatial_scale=1/32, sampling_ratio=1)
roi = roi_align(x, proposal)
print(f"roi align: \n{roi}")
u_1 = 0.203125
v_1 = 0.203125
f1_1 = x[0, 0, 1, 1] # -1.6091
f2_1 = x[0, 0, 1, 2] # -0.7121
f3_1 = x[0, 0, 2, 1] # 1.6871
f4_1 = x[0, 0, 2, 2] # 0.2284
u_2 = 0.984375
v_2 = 0.984375
f1_2 = x[0, 0, 2, 2] # 0.2284
f2_2 = x[0, 0, 3, 2] # 0.4676
f3_2 = x[0, 0, 2, 3] # 0.1991
f4_2 = x[0, 0, 3, 3] # 0.0457
print(f"bilinear(0,0): {bilinear(u_1, v_1, f1_1, f2_1, f3_1, f4_1):.4f}")
print(f"bilinear(1,1): {bilinear(u_2, v_2, f1_2, f2_2, f3_2, f4_2):.4f}")
if __name__ == '__main__':
main()
运行结果:
3. 多任务损失
Mask R-CNN 使用多任务损失函数,将分类、边界框回归和分割掩码的损失组合在一起进行优化。
- 损失函数:
- 分类损失:用于目标类别的分类任务,通常是交叉熵损失。
- 边界框回归损失:用于边界框位置回归,通常是平滑 L1 损失。
- 掩码损失:用于分割掩码的预测,通常是二分类交叉熵损失。
掩码训练损失:
4. 结构改进
Mask R-CNN 在网络结构上进行了优化,使其能够高效地处理分割任务。
- 掩码分支:在分类和回归任务之后,增加一个分割掩码分支,独立生成每个 RoI 的分割掩码。
- 分割通道:每个类别有独立的掩码预测通道,网络输出的是 K × m × m 的掩码,其中 K 是类别数,m × m 是分割掩码的尺寸。