美文网首页TensorFlow技术帖
使用TensorFlow实现ResNet

使用TensorFlow实现ResNet

作者: 碧影江白 | 来源:发表于2018-01-07 14:59 被阅读1143次

    作为在ILSVRC 2015比赛中取得了冠军的网络结构,ResNet借鉴吸收了以往的网络结构的优点,完全抛弃了全连接层,使用残差学习方法解决了在信息传递时出现的信息丢失,损耗等问题,使整个网络只需要学习输入,输出差别的那一部分,简化了学习目标和学习难度。
    忽略掉繁琐的解释和代码逻辑实现,我们现在简单地讲解一下RetNet的实现原理,并且利用这些原理便可以看懂相关的代码甚至写出自己的RetNet网络。
    首先,介绍一下替换了全连接层的残差网络。
    残差网络常见的分为两层和三层的残差学习网络:

    可知,一个残差网络的结果不只是经过卷积网络计算的结果,还加上了输入的数据,两个结果经过共同作用得到了最终的结果。
    一个ResNet网络可能有不同的层数,但是在对输入数据的第一次处理需要选择性地进行卷积和池化操作,例如一个50层的ResNet网络,其结构可以表示为2+48,其中2表示预处理,48则是conv卷积层的数目,采用三层的残差学习网络,由于3个卷积层为一个残差网络,故48/3=16个残差网络。

    接下来的计算内容便不得而知了,16个上图所示的三层残差网络对数据进行处理,得到一个ResNet网络。 16层的RetNet网络

    我们规定为了使数据更容易分类且更加具有特征性,我们把整个网络分为4个Block块,如图中的四个不同的颜色,且规定第一个block块和最后一个都只包括3个残差网络,如图50层的ResNet网络的结构为:(3+4+6+3)x 3 +2
    相似地,101层的ResNet网络的结构为:(3+4+23+3)x 3 +2

    所以,我们现在已知了网络的结构,就可以对网络进行编程了,首先,初始化其4个Block块:

    def resnet_v2_50(inputs, num_classes = None,
                     global_pool = True, reuse = None, scope = 'resnet_v2_50'):
        blocks = [
            Block('block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]),
            Block('block2', bottleneck, [(512, 128, 1)] * 3 + [(512, 128, 2)]),
            Block('block3', bottleneck, [(1024, 256, 1)] * 5 + [(1024, 256, 2)]),
            Block('block4', bottleneck, [(2048, 512, 1)] * 3)
        ]
        return resnet_v2(inputs, blocks, num_classes, global_pool,
                         include_root_block=True, reuse=reuse, scope=scope)
    

    简单来说以上代码的作用就是:

    • 定义了四个Block块
    • 实现这四个Block块

    在此,bottleneck为残差函数的实现,而resnet_v2函数则是将Block的内容代入环境来运算。

    接下来对每一个相关的函数进行实现:

    def resnet_v2(inputs, blocks, num_classes=None, global_pool=True,
              include_root_block=True, reuse=None, scope=None):
        with tf.variable_scope(scope, "resnet_v2", [inputs], reuse=reuse) as sc:
            end_points_collection = sc.original_name_scope+'_end_points'
            with slim.arg_scope([slim.conv2d, bottleneck, stack_block_dense],
                                output_collections=end_points_collection):
                net = inputs
                if include_root_block:
                    with slim.arg_scope([slim.conv2d], activation_fn=None,
                                        normalizer_fn=None):
                        net = conv2d_same(net, 64, 7, stride=2, scope='conv1')
                    net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
                net = stack_blocks_dense(net, blocks)
                net = slim.batch_norm(net, activation_fn=tf.nn.relu, scope='postnorm')
                if global_pool:
                    net = tf.reduce_mean(net, [1, 2], name='pool5', keep_dims=True)
                if num_classes is not None:
                    net = slim.conv2d(net, num_classes, [1, 1], activation_fn=None,
                                      normalizer_fn=None, scope='logits')
                end_points = slim.utils.convert_collection_to_dict(end_points_collection)
                if num_classes is not None:
                    end_points['predictions'] = slim.softmax(net, scope='predictions')
                return net, end_points
    
    def bottleneck(inputs, depth, depth_bottleneck, stride, output_collections=None, scope=None):
        with tf.variable_scope(scope, 'bottleneck_v2', [input]) as sc:
            depth_in = slim.utils.last_dimension(inputs.get_shape(), min_rank=4)
            preact = slim.batch_norm(inputs, activation_fn=tf.nn.relu)
    
            if depth == depth_in:
                shortcut = subsample(inputs, stride, 'shortcut')
            else:
                shortcut = slim.conv2d(preact, depth, [1, 1], stride=stride,
                                       normalizer_fn=None, activation_fn=None,
                                       scope='shortcut')
            residual = slim.conv2d(preact, depth_bottleneck, [1, 1], stride=1, scope="conv1")
            residual = conv2d_same(residual, depth_bottleneck, 3, stride, scope="conv2")
            residual = slim.conv2d(residual, depth, [1, 1], stride=1, normalizer_fn=None, activation_fn=None,
                                   scope="conv3")
            output = shortcut + residual
            return slim.utils.collect_named_outputs(output_collections,
                                                    sc.name, output)
    

    相关文章

      网友评论

        本文标题:使用TensorFlow实现ResNet

        本文链接:https://www.haomeiwen.com/subject/qccunxtx.html