Faster R-CNN Keras版源码史上最详细解读系列之vgg16与RPN网络

Faster R-CNN Keras版源码史上最详细解读系列之vgg16与RPN网络

vgg16

我们的特征提取网络是用vgg16作为主干网络的,只用前面的13层,最后3层全连接层不要。为什么不用其他的呢,比如resnet101,这个更深理论上当然更好啦,但是实际上训练时间和复杂性也提高啦,等简单的用好了,后面可以换嘛。先看看vgg16的结构吧,我网上找了一张比较清晰的图:
在这里插入图片描述
很清晰吧,感谢这位知乎的拉普拉斯同学。我们看到vgg16用了很多的3x3的卷积,为什么不用5x5,7x7这种呢,其实你可以自己推推其实5x5的卷积等于两个3x3的效果,然后7x7的卷积等于三个3x3的效果,但是参数可就少了很多,比如5x5是25个参数,不算偏置,两个3x3也就18个参数。另外因为用了padding,所以卷积尺寸不变,尺寸就交给池化了,池化每次尺寸减半。而主干网络只用了前面五端卷积层,总共13个卷积层,4个池化层,为什么叫vgg16呢,因为用了16个参数层,后面还有3个全连接呢,池化层是没有参数的。

vgg.py

我们来看看源码vgg.py的主干网络函数nn_base

'''
vgg主干网络定义 名字不能乱,最后加载参数是根据名字的
只要全连接之前的,最后一层池化也不要
'''
def nn_base(input_tensor=None, trainable=False):


    # Determine proper input shape
    # Theano的通道在最前面,tensorflow的通道在最后
    if K.image_dim_ordering() == 'th':
        input_shape = (3, None, None)
    else:
        input_shape = (None, None, 3)

    # 防止没有输入形状
    if input_tensor is None:
        img_input = Input(shape=input_shape)
    else:
        # 如果不是keras张量的话,会封装一层,然后多一个维度,就是样本的批量数
        if not K.is_keras_tensor(input_tensor):
            img_input = Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor

    # 这个貌似还没用到
    if K.image_dim_ordering() == 'tf':
        bn_axis = 3
    else:
        bn_axis = 1

    # Block 1
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

    # Block 2
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)

    # Block 3
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)

    # Block 4
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)

    # Block 5
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
    # x = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)

    return x

再来看看vgg.py的RPN网络函数rpn

# RPN网络
def rpn(base_layers, num_anchors):
    # 再进行3x3的卷积,结果分别给分类和回归
    x = Conv2D(512, (3, 3), padding='same', activation='relu', kernel_initializer='normal', name='rpn_conv1')(base_layers) # (?, ?, ?, 512)

    # 滤波器个数就是特征的维度 用1x1整合512个通道信息, 生成9个通道,9个锚框,本来应该是18个类别的概率,直接考虑前景,不考虑背景,所以就9个概率
    x_class = Conv2D(num_anchors, (1, 1), activation='sigmoid', kernel_initializer='uniform', name='rpn_out_class')(x) # (?, ?, ?, 9)

    '''
    回归主要是要训练出4个值,平移系数和缩放数,即平移(tx,ty),缩放(th,tw),9个锚框,所以是9x4=36 ,这里的激活函数是线性函数,等于在做线性回归,当预测和真实框宽高相近的时候,
    我们可以认为是线性变换,tw=log(Gw/Pw)=log((Gw+Pw−Pw)/Pw)=log(1+(Gw−Pw)/Pw) 利用高数中等价无穷小的定义 ln(1+x)=x 当x趋向于0,
    也就是(Gw−Pw)/Pw很小的时候,即Gw和Pw很相近的时候,tw=(Gw−Pw)/Pw是线性变换
    '''
    x_regr = Conv2D(num_anchors * 4, (1, 1), activation='linear', kernel_initializer='zero', name='rpn_out_regress')(x) # (?, ?, ?, 36)

    return [x_class, x_regr, base_layers]

第一个卷积是让他输出是否是背景的分类概率,因为是二分类,激活函数sigmoid就够了,然后用1x1的卷积进行降维,降到锚框数量的维度,也就是9维,其实每一个特征图上的点有9个通道,每个通道都是一个概率值,是前面512个通道整合起来的。1x1卷积的作用很大,既可以做升维降维,也可以做通道的信息整合。

第二个卷积是降维到36个维度,因为每个锚框包含4个回归梯度嘛,所以4x9=36,要36个值,如果标注框和锚框之间的差距很小的话,可以近似为线性回归问题,所以激活函数是线性的,当然你可以改成其他的,我没试过,不知道效果怎么样,你改好了记得通知我下啊嘿嘿。这个问题可以看下这篇文章,或许会更好理解。

还要注意的就是每个层的名字不能乱命名,加载模型参数的时候是按名字加载的。
最后的分类和回归网络先不说,因为涉及到ROIPooling层的操作,之前会涉及IOU,NMS等处理,后面慢慢讲。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵,部分图片来自网络,侵删。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值