Faster RCNN目标检测算法(Two-stage目标检测算法)
本文中代码来自:https://github.com/bubbliiiing/faster-rcnn-keras
Two - stage 和One - stage目标检测算法的综述。
Two-stage目标检测算法:先生成一系列的候选框,在通过卷积神经网络进行样本分类。(在精度上占优势)
One-stage目标检测算法:不生成候选框直接将目标边框的定位问题转化成回归问题。(在算法速度上占优势)
1、Faster RCNN网络的整体思路
首先将原图片输入到特征提取网络中,特征提取网络可以看做将图像划分成不同大小的网格,每个网格上有若干个先验框;利用RPN(Region proposal network)区域建议网络进行粗略的筛选,获得建议框在共享特征层上进行截取,将截取到的建议框输入到ROI Pooling网络进行细致的调整,将建议框resize 成相同的大小,再利用分类回归网络判断框中是否存在目标,并对建议框进行调整。
Faster RCNN网络对输入图片的大小没有限制,在输入到特征提取网络之前,对图片进行resize 操作,将图片中的较短边resize成600(保持原图片的长宽比,图片不会失真),输入到backbone特征提取网络(ResNet50),看作将图片划分成不同个大小的网格,输出特征feature(共享特征层,后面得到的建议框也是在共享特征层上进行截取。)此时,得到的共享特征层有两个去向:1、进行一次3×3的卷积,再分别进行两次通道数不同的1×1卷积(通道数分别为9和36),这里每一个网格中默认的先验框个数为9,那么9和36分别代表什么含义呢?
9和36分别代表的含义:每一个网格中默认先验框的个数为9,而两个1×1卷积输出的结果分别表示先验框中是否存在物体、先验框的调整参数(中心点坐标和宽高)。9代表每个网格中每一个先验框中是否存在物体;36代表每个网格中每一个先验框的调整参数。
对先验框调整后的结果就是经过粗略筛选后的建议框。
利用建议框在共享特征层上进行截取,截取下来的建议框输入到ROI Pooling中。这也就是共享特征层的第二个去向:用建议框对共享特征层进行截取,用于进一步更精细的调整。
在Roi Pooling中,首先将截取到的建议框resize成相同大小,然后1、(bbox_pred)获得建议框的调整参数;2、(class_pred)经过softmax获得建议框中物体的种类。得到最终的目标检测结果。
2、主干特征提取网络(ResNet50)
在ResNet中有两种结构:Conv_Block和Identity_Block
Conv_Block的残差边上有卷积操作,该结构的输入和输出的通道数改变了,所以不能够连续串联。
Identity_Block的残差边上没有卷积操作,该结构的输入、输出的维度相同,可以连续串联,作用是增加网络的深度。
Conv_Block代码:
def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):
filters1, filters2, filters3 = filters
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch'
x = Conv2D(filters1, (1, 1), strides=strides,
name=conv_name_base + '2a')(input_tensor)
x = BatchNormalization(name=bn_name_base + '2a')(x)
x = Activation('relu')(x)
x = Conv2D(filters2, kernel_size, padding='same',
name=conv_name_base + '2b')(x)
x = BatchNormalization(name=bn_name_base + '2b')(x)
x = Activation('relu')(x)
x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
x = BatchNormalization(name=bn_name_base + '2c')(x)
shortcut = Conv2D(filters3, (1, 1), strides=strides,
name=conv_name_base + '1')(input_tensor)
shortcut = BatchNormalization(name=bn_name_base + '1')(shortcut)
x = layers.add([x, shortcut])
x = Activation('relu')(x)
return x
Conv_Block默认的步长为(2,2)
首先进行卷积核大小为1×1的卷积操作,使用默认的步长,宽高减半
BatchNormalization归一化操作
relu非线性激活函数
卷积核大小为3×3的卷积操作,宽高不变
BatchNormalization归一化操作
relu非线性激活函数
卷积核大小为1×1的卷积操作,宽高不变
BatchNormalization归一化操作
残差边是将输入的特征经过一次卷积核大小为1×1的卷积操作,步长为2,输出宽高减半
和一次BatchNormalization归一化操作
然后将残差边和主干网络的结果叠加,经过一个非线性激活函数,最后得到conv_Block的输出结果。
Identity_block代码
def identity_block(input_tensor, kernel_size, filters, stage, block):
filters1, filters2, filters3 = filters
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch'
x = Conv2D(filters1, (1, 1), name=conv_name_base + '2a')(input_tensor)
x = BatchNormalization(name=bn_name_base + '2a')(x)
x = Activation('relu')(x)
x = Conv2D(filters2, kernel_size,padding='same', name=conv_name_base + '2b')(x)
x = BatchNormalization(name=bn_name_base + '2b')(x)
x = Activation('relu')(x)
x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
x = BatchNormalization(name=bn_name_base + '2c')(x)
x = layers.add([x, input_tensor])
x = Activation('relu')(x)
return x
首先进行一次卷积核大小为1×1的卷积,宽高不变
BatchNormalization归一化
relu非线性激活函数
卷积核大小为3×3的卷积,宽高不变
BatchNormalization归一化
relu非线性激活函数
卷积核大小为1×1的卷积,宽高不变
BatchNormalization归一化
残差边将输入直接引入到主干的输出特征层,进行叠加,再经过一个relu非线性激活函数得到Identity_Block的结果。
ResNet50网络结构
调用Identity_Block和Conv_Block构建残差网络结构
def ResNet50(inputs):
img_input = inputs
x = ZeroPadding2D((3, 3))(img_input)
x = Conv2D(64, (7, 7), strides=(2, 2), name='conv1')(x)
x = BatchNormalization(name='bn_conv1')(x)
x = Activation('relu')(x)
x = MaxPooling2D((3, 3), strides=(2, 2), padding="same")(x)
x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1))
x = identity_block(x, 3, [64, 64, 256], stage=2, block='b')
x = identity_block(x, 3, [64, 64, 256], stage=2, block='c')
x = conv_block(x, 3, [128, 128, 512], stage=3, block='a')
x = identity_block(x, 3, [128, 128, 512], stage=3, block='b')
x = identity_block(x, 3, [128, 128, 512], stage=3, block='c')
x = identity_block(x, 3, [128, 128, 512], stage=3, block='d')
x = conv_block(