将Tensorflow目标检测object_detect API源码中的ssd_mobilenet_v1主结构修改为shufflenetv2

        为将模型部署移动端,往往采用轻量级的网络结构,如Mobilenet和shufflenet。最近看到网上一些资料shufflenetv2在ImageNet上有着不错的表现,并且计算量相较于其他轻量级网络结构大幅度减少。之前做目标检测时,通常使用ssd_mobilenet_v1,于是在想将ssd_mobilenet_v1主结构替换为shufflenetv2,是否能在准确性和推理速率上都能提升一些,查了一下资料,发现尚未有人实现ssd_shufflenetv2,可能是shufflenetv2的热度以及相关准确度还未获大家认可。

        为验证ssd_shufflenetv2的猜想,决定将TensorFlow目标检测的API object_detect的源码进行修改,将ssd_mobilenet_v1的主结构修改为shufflenetv2。shufflenetv2程序参考了github一位博主的源码。下面直接在ssd_mobilenet_v1上进行修改。

        首先找到ssd_mobilenet_v1主结构源码位置即models/research/slim/nets/mobilenet_v1.py,然后定义shufflenetv2的Block结构,ssd_mobilenet_v1的主结构定义为:

将Tensorflow目标检测object_detect API源码中的ssd_mobilenet_v1主结构修改为shufflenetv2_第1张图片

对_CONV_DEFS进行替换如下:

将Tensorflow目标检测object_detect API源码中的ssd_mobilenet_v1主结构修改为shufflenetv2_第2张图片

 

Shufflenetv2的ShuffleBlock函数定义:

def shufflenet_v2_block(x, out_channel, kernel_size, stride=1, dilation=1, shuffle_group=2, name=None):
    if stride == 1:
        top, bottom = tf.split(x, num_or_size_splits=2, axis=3)
        half_channel = out_channel // 2

        top = conv_bn_relu(top, half_channel, 1)
        top = depthwise_conv_bn(top, kernel_size, stride, dilation)
        top = conv_bn_relu(top, half_channel, 1)

        out = tf.concat([top, bottom], axis=3)
        out = shuffle_unit(out, shuffle_group, name)

    else:
        half_channel = out_channel // 2
        b0 = conv_bn_relu(x, half_channel, 1)
        b0 = depthwise_conv_bn(b0, kernel_size, stride, dilation)
        b0 = conv_bn_relu(b0, half_channel, 1)

        b1 = depthwise_conv_bn(x, kernel_size, stride, dilation)
        b1 = conv_bn_relu(b1, half_channel, 1)

        out = tf.concat([b0, b1], axis=3)
        out = shuffle_unit(out, shuffle_group, name)
    return out

def depthwise_conv_bn(x, kernel_size, stride=1, dilation=1):
    with tf.variable_scope(None, 'depthwise_conv_bn'):
        x = slim.separable_conv2d(x, None, kernel_size, depth_multiplier=1, stride=stride,
                                  rate=dilation, normalizer_fn=slim.batch_norm, activation_fn=None, biases_initializer=None)
        # x = slim.batch_norm(x, activation_fn=None, fused=False)
    return x

def conv_bn_relu(x, out_channel, kernel_size, stride=1, dilation=1):
    with tf.variable_scope(None, 'conv_bn_relu'):
        x = slim.conv2d(x, out_channel, kernel_size, stride, rate=dilation,
                        biases_initializer=None, normalizer_fn=slim.batch_norm)
        # x = slim.batch_norm(x, activation_fn=tf.nn.relu, fused=False)
    return x

def shuffle_unit(x, groups, name):
    n, h, w, c = x.get_shape().as_list()
    x = tf.reshape(x, shape=tf.convert_to_tensor([tf.shape(x)[0], h, w, groups, c // groups]))
    x = tf.transpose(x, tf.convert_to_tensor([0, 1, 2, 4, 3]))
    x = tf.reshape(x, shape=tf.convert_to_tensor([tf.shape(x)[0], h, w, c]), name=name)
    return x

        至此ssd_mobilenet_v1主结构修改成Shufflentv2后便可对ssd_mobilenet_v1的特征提取的源码进行修改。ssd_mobilenet_v1的特征提取源码定义在/models/research/object_detection/models/ssd_mobilenet_v1_feature_extractor.py文件中。ssd_mobilenet_v1的第一层特征提取的是最后一个14x14的卷积层位置(以输入大小224x224为例),第二层特征提取的是最后一个7x7的卷积层位置,分别为'Conv2d_11_pointwise', 'Conv2d_13_pointwise'。因而计算shufflenetv2输出14x14以及7x7的卷积层分别为'Conv2d_13_pointwise', 'Conv2d_17_pointwise',所以在ssd_mobilenet_v1_feature_extractor.py中将feature_map_layout定义修改如下:

将Tensorflow目标检测object_detect API源码中的ssd_mobilenet_v1主结构修改为shufflenetv2_第3张图片

并将124行中final_endpoint修改为final_endpoint='Conv2d_17_pointwise'  ='Conv2d_17_pointwise'

        这里便可使用ssd_mobilenet_v1的配置文件进行尝试ssd_shufflenetv2的训练,训练ssd_shufflenetv2时不能在配置文件中加载预加载模型。训练后的模型转换tflite时会报op不支持的错误,移植移动端时,我是通过固化的pb模型转成mnn进行移动端的移植。

        若想将shufflenetv2在源码中进行命名调用,可在/models/research/object_detection/builders/model_builder.py中进行导入并命名。至此ssd_mobilenet_v1修改为ssd_shufflenetv2的过程便完成了。

        从训练结果来看和ssd_mobilenet_v1没有太大差别,计算量上shufflenet_v2较少,我采取的shufflenet_v2_0.5,ssd_shufflenet_v2_0.5的mFlops为37M左右,mnn推理平均速率4.807ms。ssd_mobilenet_v1_0.25的mFlops为49M左右,mnn推理速率4ms。

    最后借鉴下网上的图

将Tensorflow目标检测object_detect API源码中的ssd_mobilenet_v1主结构修改为shufflenetv2_第4张图片

ssd_mobilenet_v1修改为ssd_shufflenetv2仅个人猜想实验,如有错误望指出。

参考资料:

  1. https://github.com/timctho/shufflenet-v2-tensorflow
  2. https://www.jianshu.com/p/0287556f1251

你可能感兴趣的:(深度学习,TensorFlow)