问题导出:
比如CTPN的网络结构在 lib/networks/VGGnet_train.py下:
(self.feed('data')
.conv(3, 3, 64, 1, 1, name='conv1_1')
.conv(3, 3, 64, 1, 1, name='conv1_2')
.max_pool(2, 2, 2, 2, padding='VALID', name='pool1')
.conv(3, 3, 128, 1, 1, name='conv2_1')
.conv(3, 3, 128, 1, 1, name='conv2_2')
.max_pool(2, 2, 2, 2, padding='VALID', name='pool2')
.conv(3, 3, 256, 1, 1, name='conv3_1')
.conv(3, 3, 256, 1, 1, name='conv3_2')
.conv(3, 3, 256, 1, 1, name='conv3_3')
.max_pool(2, 2, 2, 2, padding='VALID', name='pool3')
.conv(3, 3, 512, 1, 1, name='conv4_1')
.conv(3, 3, 512, 1, 1, name='conv4_2')
.conv(3, 3, 512, 1, 1, name='conv4_3')
.max_pool(2, 2, 2, 2, padding='VALID', name='pool4')
.conv(3, 3, 512, 1, 1, name='conv5_1')
.conv(3, 3, 512, 1, 1, name='conv5_2')
.conv(3, 3, 512, 1, 1, name='conv5_3'))
#========= RPN ============
(self.feed('conv5_3')
.conv(3,3,512,1,1,name='rpn_conv/3x3'))
(self.feed('rpn_conv/3x3').Bilstm(512,128,512,name='lstm_o'))
(self.feed('lstm_o').lstm_fc(512,len(anchor_scales) * 10 * 4, name='rpn_bbox_pred'))
(self.feed('lstm_o').lstm_fc(512,len(anchor_scales) * 10 * 2, name='rpn_cls_score'))
# generating training labels on the fly
# output: rpn_labels(HxWxA, 2) rpn_bbox_targets(HxWxA, 4) rpn_bbox_inside_weights rpn_bbox_outside_weights
# 给每个anchor上标签,并计算真值(也是delta的形式),以及内部权重和外部权重
(self.feed('rpn_cls_score', 'gt_boxes', 'gt_ishard', 'dontcare_areas', 'im_info')
.anchor_target_layer(_feat_stride, anchor_scales, name = 'rpn-data' ))
# shape is (1, H, W, Ax2) -> (1, H, WxA, 2)
# 给之前得到的score进行softmax,得到0-1之间的得分
(self.feed('rpn_cls_score')
.spatial_reshape_layer(2, name = 'rpn_cls_score_reshape')
.spatial_softmax(name='rpn_cls_prob'))
整体的网络结构是Conv+RPN+Bi-LSTM+fc,但本文探究的就不是这些了,只是好奇为什么这么多网络结构能链式连接在一起,所以恰当来说,本文类型属于Python语法之类了。
-----------------------------------------------------------------------纯语法,非喜勿进---------------------------------------------------------------------
首先需要这个Class是如何调用的。
lib/networks/VGGnet_train.py:
class VGGnet_train(Network):
lib/networks/network.py:
class Network(object):
在实例化class VGGnet_train的时候,继承了class Network,所以class VGGnet_train继承了class Network所有的类函数。
实例化的过程会调用__init__()函数,可以看到最后一行:
self.setup()
所以说网络结构在一开始实例化的时候就加载进内存了,只不过一般都会初始化为一个值(比如Conv中的weights一般初始化为mean为0、std为0.1的值)。
分析setup()函数,从self.feed('data')开始,就一直链式调用将所有网格加载进来了。
首先来说,self代表的是类的实例化,而非类,可以参考Python面向对象。
类的实例化表明其可以通过点号来访问类的属性。
先下个结论,链式调用的原理是type(self) = type(self.feed('data')) = type(self.conv(3, 3, 64, 1, 1, name='conv1_1')),即类的实例化访问类属性后返回值依然是类的实例化,从而可以继续访问返回值的类属性,实现链式调用。
那么关键是,怎么实现:类的实例化访问类属性后返回值依然是类的实例化,答案就是network.py里面的装饰器函数layer()【注:feed()函数除外,其本身就返回了一个类的实例化self】,在一个函数上方添加代码@layer就是其表示形式。
装饰器的作用就是将被修饰的函数作为参数,并返回修饰后的同名函数或其它可调用的东西。
在这份代码里,意思就是在执行属性conv(3, 3, 64, 1, 1, name='conv1_1')及max_pool(2, 2, 2, 2, padding='VALID', name='pool1')之前,会先调用layer函数。
分析conv()等函数发现,其与一般的卷积函数并无不同,也是调用TensorFlow里面的函数,将输入经过函数操作后得到输出值。
再回顾下装饰器的作用,操作被修饰的函数后,装饰器再执行操作返回类的实例化self即可。
观察源码发现确实是这样的,在layer()函数中:
# Perform the operation and get the output.
layer_output = op(self, layer_input, *args, **kwargs)
# Add to layer LUT.
self.layers[name] = layer_output
# This output is now the input for the next layer.
self.feed(layer_output)
# Return self for chained calls.
return self
执行op(self, layer_input, *args, **kwargs)操作会将输入处理后得到输出,最后还是需要return self的。
至于self.feed(layer_output)的作用,是将列表清空,将输出放入进去替换输入,这样下一次调用的输入就是这次的输出了,达到链式调用的目的。