@w460461339
2019-02-20T18:14:24.000000Z
字数 2865
阅读 936
Tensorflow
参考:
代码: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
这里不详细说,大概就是通过残差的思想,解决了神经网络不能过深的问题,使得神经网络从原来的19or32层,一下子跳到了152层,甚至可以更深。
网络的部分看起来差不多长这个样子
然后,每个残差块长这个样子:
论文说上面的模块是最优的,但实际写起来不是这样的啊:
1、需要区分输入和输出的维度是否相同。如果不同,那么对于shortcut(就是上图灰色区域笔直的那一条,还需要加上一些操作才行。
2、实际写起来,上图右边的区域差不多是:
2.1 显示BN+激活
2.2 1x1xn,3x3xn,1x1xn这样的三个卷积过来,其中3x3的卷积,还可能是stride=2的情况,
2.3 最后将shortcuts和残差路线的结果对应位置相加。(不是concate)
注意到,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,即需要缩小尺寸。
Block('block1',bottleneck,[(256,64,1)]*2 + [(256,64,2)]),
Block('block2',bottleneck,[(512,128,1)]*7 + [(512,128,2)]),
Block('block3',bottleneck,[(1024,256,1)]*35+[(1024,256,2)]),
Block('block4',bottleneck,[(2048,512,1)]*3)
在拿到每个残差块需要的参数(256,64,1)
后,我们开始搭建残差块:
@slim.add_arg_scope
def bottleneck(inputs,depth,depth_bottleneck,stride,
outputs_collections=None,scope=None):
'''
Args:
inputs: A tensor of size [batch, height, width, channels].
depth、depth_bottleneck,stride三个参数是前面blocks类中
depth表示这个残差块最终输出channel
depth_bottleneck表示这个残差块中间的卷积核输出channel
stride表示卷积步长
outputs_collections: 是收集end_points的collection
scope: 是这个unit的名称。
'''
with tf.variable_scope(scope,'bottleneck_v2',[inputs]) as sc:
depth_in = slim.utils.last_dimension(inputs.get_shape(),
min_rank=4)
'''
1、回忆resnet的顺序,
第一条路:先normal,再激活,然后再卷积
第二条路:shortcuts
'''
preact = slim.batch_norm(inputs,activation_fn=tf.nn.relu,scope='preact')
'''
下面是short_cuts部分
'''
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')
'''
下面是非short_cuts部分
'''
residual = slim.conv2d(preact,depth_bottleneck,[1,1],
stride=1,scope='conv1')
# 就只有这里stride可能是2,所以需要特殊写一个conv方法,来处理
residual = conv2_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
'''
这里大致意思是
把output这个tensor
以别名sc.name的方式
加入到名字为outputs_collections中去
然后再返回output这个tensor
'''
return slim.utils.collect_named_outputs(outputs_collections,
sc.name,output)
问: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
还有很多参数用法,具体可以看我的代码。