[关闭]
@w460461339 2019-02-20T18:14:24.000000Z 字数 2865 阅读 936

TensorflowDay4.1:ResNetForward

Tensorflow


1、ResNet Forward

参考:
代码:https://blog.csdn.net/superman_xxx/article/details/65452735
原理:https://blog.csdn.net/lanran2/article/details/79057994
https://blog.csdn.net/u013709270/article/details/78838875

1.1 ResNet基本概念

这里不详细说,大概就是通过残差的思想,解决了神经网络不能过深的问题,使得神经网络从原来的19or32层,一下子跳到了152层,甚至可以更深。

网络的部分看起来差不多长这个样子
image_1d38lo3v99269ts1163q5ossm9.png-55.4kB

然后,每个残差块长这个样子:
image_1d38lq9421hv1vtt1maq13blgi13.png-24.6kB

论文说上面的模块是最优的,但实际写起来不是这样的啊:

1、需要区分输入和输出的维度是否相同。如果不同,那么对于shortcut(就是上图灰色区域笔直的那一条,还需要加上一些操作才行。
2、实际写起来,上图右边的区域差不多是:
    2.1 显示BN+激活
    2.2 1x1xn,3x3xn,1x1xn这样的三个卷积过来,其中3x3的卷积,还可能是stride=2的情况,
    2.3 最后将shortcuts和残差路线的结果对应位置相加。(不是concate)
1.2 代码逻辑

注意到,resnet中,有:

1、每个残差单元中包含若干个(3个)卷积操作。
2、将输出维度一致的残差单元放在一起,形成若干个(4个)Block。
3、Block之间的交汇处需要缩小尺寸,因此通过步长为2的卷积实现。

因此,我们定义两层循环:

1、第一层,表示需要有几个Block。这里是4.
2、第二层,表示每个Block里面有几个残差单元。
    2.1 下面看到,第一个Block中有3个残差单元,
    2.2 其中,其前两个残差块的输出维度是256,中间卷积输出维度是64,步长都是1
    2.3 最后一个输出维度不变,但是步长是2,即需要缩小尺寸。
  1. Block('block1',bottleneck,[(256,64,1)]*2 + [(256,64,2)]),
  2. Block('block2',bottleneck,[(512,128,1)]*7 + [(512,128,2)]),
  3. Block('block3',bottleneck,[(1024,256,1)]*35+[(1024,256,2)]),
  4. Block('block4',bottleneck,[(2048,512,1)]*3)

在拿到每个残差块需要的参数(256,64,1)后,我们开始搭建残差块:

  1. @slim.add_arg_scope
  2. def bottleneck(inputs,depth,depth_bottleneck,stride,
  3. outputs_collections=None,scope=None):
  4. '''
  5. Args:
  6. inputs: A tensor of size [batch, height, width, channels].
  7. depth、depth_bottleneck,stride三个参数是前面blocks类中
  8. depth表示这个残差块最终输出channel
  9. depth_bottleneck表示这个残差块中间的卷积核输出channel
  10. stride表示卷积步长
  11. outputs_collections: 是收集end_points的collection
  12. scope: 是这个unit的名称。
  13. '''
  14. with tf.variable_scope(scope,'bottleneck_v2',[inputs]) as sc:
  15. depth_in = slim.utils.last_dimension(inputs.get_shape(),
  16. min_rank=4)
  17. '''
  18. 1、回忆resnet的顺序,
  19. 第一条路:先normal,再激活,然后再卷积
  20. 第二条路:shortcuts
  21. '''
  22. preact = slim.batch_norm(inputs,activation_fn=tf.nn.relu,scope='preact')
  23. '''
  24. 下面是short_cuts部分
  25. '''
  26. if depth == depth_in:
  27. shortcut = subsample(inputs,stride,'shortcut')
  28. else:
  29. shortcut = slim.conv2d(preact,depth,[1,1],stride=stride,
  30. normalizer_fn=None,activation_fn=None,
  31. scope='shortcut')
  32. '''
  33. 下面是非short_cuts部分
  34. '''
  35. residual = slim.conv2d(preact,depth_bottleneck,[1,1],
  36. stride=1,scope='conv1')
  37. # 就只有这里stride可能是2,所以需要特殊写一个conv方法,来处理
  38. residual = conv2_same(residual,depth_bottleneck,3,stride,
  39. scope='conv2')
  40. residual = slim.conv2d(residual,depth,[1,1],stride=1,
  41. normalizer_fn=None,
  42. activation_fn=None,scope='conv3')
  43. output = shortcut+residual
  44. '''
  45. 这里大致意思是
  46. 把output这个tensor
  47. 以别名sc.name的方式
  48. 加入到名字为outputs_collections中去
  49. 然后再返回output这个tensor
  50. '''
  51. return slim.utils.collect_named_outputs(outputs_collections,
  52. sc.name,output)
1.3 其他问题

问:tf.variable_scope中的values参数是干什么用的?
答:主要是为了当跨graph时,也能够正确传递参数用的。
https://stackoverflow.com/questions/40164583/tensorflows-tensorflow-variable-scope-values-parameter-meaning

问:@slim.add_arg_scope是做什么的?
答:通过这个修饰自己的函数,这样就可以使用with slim.arg_scope来定义参数了。
https://blog.csdn.net/weixin_35653315/article/details/78160886

问:collections.namedtuple用法?
答:相当于简单的类把。
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431953239820157155d21c494e5786fce303f3018c86000

还有很多参数用法,具体可以看我的代码。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注