darkflow 升级depthwise_convolutional layer
要想人前显贵,必定人后遭罪————一次小小的记录
一、darkflow安装
首先从github上git下来源码,然后安装cython,最后将工程封装成python下的一个可执行文件
git clone https://github.com/thtrieu/darkflow.git
cd darkflow
python3 setup.py build_ext --inplace
pip3 install -e .
# 或者
pip3 install .
二、darkflow升级
darkflow如何将darknet与tensorflow联系起来的呢?
1、网络结构方面:darknet网络结构定义采用cfg来定义,所以darkflow首先要解析cfg文件,从而构建计算图。
2、网络权重方面:darknet的权重是以.weights后缀的二进制文件保存,而tensorflow训练时主要以ckpt文件、meta文件等形式保存,因而,需要根据cfg解析.weights文件,读取进tensorflow计算图,训练后保存生成tensorflow权重文件。
废话说多了,开撸~~
(1)升级cfg文件解析模块
1、位置:utils文件夹下,process.py文件,cfg_yielder()函数修改
增加如下代码:
#---------------added by wei------------------
elif d['type'] == '[depthwise_convolutional]':
#n = d.get('filters', 1) dw_conv do not need this param
# its input channels are equal to output channels
size = d.get('size', 1)
stride = d.get('stride', 1)
pad = d.get('pad', 0)
padding = d.get('padding', 0)
if pad: padding = size // 2
activation = d.get('activation', 'logistic')
batch_norm = d.get('batch_normalize', 0) or conv
yield ['depthwise_convolutional', i, size, c, c,
stride, padding, batch_norm,
activation]
if activation != 'linear': yield [activation, i]
w_ = (w + 2 * padding - size) // stride + 1
h_ = (h + 2 * padding - size) // stride + 1
w, h, c = w_, h_, c
l = w * h * c
#-----------------------------------------------------
这段代码是为了解析cfg文件中我在darknet原始框架下增加的,层名标志为:[depthwise_convolutional],关于原始框架的升级这里就跳过啦,有需要后面再更新吧。
depthwise layer不需要filter参数,它的filter是由上一个节点的输入所决定的,dw卷积前后通道保持不变(这个版本的dw_conv还需要与1x1的普通点卷积配合使用),这是与普通卷积层所不一样的。因此,这里的n(filter数量) 应该是等于c(输入的channels)的。
2、位置:darflow/dark/darkop.py
增加层名索引词典,为后面加载.weights权重做准备
插入如下词典:
'depthwise_convolutional': depthwise_convolutional_layer,
顺其自然,接下来就要增加 depthwise_convolutional_layer类的实现。
(2)升级weights文件解析模块
1、位置:darkflow/dark/convolution.py
depthwise_convolutional_layer类的实现
#------------------------------------added by wei -------------------------------------
class depthwise_convolutional_layer(Layer):
def setup(self, ksize, c, n, stride, pad, batch_norm, activation):
# 核大小-输入c-核数
print('Layer:','dw_conv','ksize=',ksize,' c=', c,' n=', n)
self.batch_norm = bool(batch_norm)
self.activation = activation
self.stride = stride
self.ksize = ksize
self.pad = pad
self.dnshape = [1, c, ksize, ksize] # darknet shape第一个维度n要换成1
self.wshape = dict({
'biases': [n],
'kernel': [ksize, ksize, c, 1]#最后一个维度n换成1
})
if self.batch_norm:
self.wshape.update({
'moving_variance' : [n],
'moving_mean': [n],
'gamma' : [n]
})
self.h['is_training'] = {
'feed': True,
'dfault': False,
'shape': ()
}
def finalize(self, _):
"""deal with darknet"""
kernel = self.w['kernel']
if kernel is None: return
kernel = kernel.reshape(self.dnshape)
kernel = kernel.transpose([2,3,1,0])
self.w['kernel'] = kernel
功能:解析weights文件中的depthwise_convolutional 层
2、位置:darkflow/utils/loader.py
在loder类中,层字典改成下面这样,加一层dw:
VAR_LAYER = ['convolutional','depthwise_convolutional','connected', 'local',
'select', 'conv-select',
'extract', 'conv-extract']
在weights_loader类中,字典改成这样:
_W_ORDER = dict({ # order of param flattened into .weights file
'convolutional': [
'biases','gamma','moving_mean','moving_variance','kernel'
],
'depthwise_convolutional': [
'biases', 'gamma', 'moving_mean', 'moving_variance', 'kernel'
],
'connected': ['biases', 'weights'],
'local': ['biases', 'kernels']
})
#权重、偏置、bn参数索引名称
另外,在weights_walker类中,更改如下:
self.offset = 20#原来是16
这个参数是控制读取地址偏移的,初值也就代表着起始地址偏移,darknet的权重文件起始有十六个字节是废的,存储了版本号、迭代次数什么乱七八糟的,因此在转换时要跳过。
(3)升级tensorflow加载模块
1、位置:darkflow/net/ops/_init.py
增加tensorflow加载层索引词典:
'depthwise_convolutional': depthwise_convolutional,
2、位置:darkflow/net/ops/covolution.py
增加tensorflow加载时dw_conv类的实现方式:
#-----------------------added by wei-------------------------------
class depthwise_convolutional(BaseOp):
def forward(self):
pad = [[self.lay.pad, self.lay.pad]] * 2;
temp = tf.pad(self.inp.out, [[0, 0]] + pad + [[0, 0]])
temp = tf.nn.depthwise_conv2d(temp, self.lay.w['kernel'], padding = 'VALID',
name = self.scope, strides = [1] + [self.lay.stride] * 2 + [1])
if self.lay.batch_norm:
temp = self.batchnorm(self.lay, temp)
self.out = tf.nn.bias_add(temp, self.lay.w['biases'])
def batchnorm(self, layer, inp):
if not self.var:
temp = (inp - layer.w['moving_mean'])
temp /= (np.sqrt(layer.w['moving_variance']) + 1e-5)
temp *= layer.w['gamma']
return temp
else:
args = dict({
'center' : False, 'scale' : True,
'epsilon': 1e-5, 'scope' : self.scope,
'updates_collections' : None,
'is_training': layer.h['is_training'],
'param_initializers': layer.w
})
return slim.batch_norm(inp, **args)
def speak(self):
l = self.lay
args = [l.ksize] * 2 + [l.pad] + [l.stride]
args += [l.batch_norm * '+bnorm']
args += [l.activation]
msg = 'dw_conv {}x{}p{}_{} {} {}'.format(*args)
return msg
#----------------------------------------------------------------
这里我们就直接调tf.tf.nn.depthwise_conv2d()可以了,其他没什么变化,kernel形状我们前面就加以限制了。
三、其他
另外,我还增加了relu激活函数支持,类似的激活函数都可以往op_types里面添加:
位置:darkflow/net/ops/_init.py
op_types = {
'convolutional': convolutional,
'depthwise_convolutional': depthwise_convolutional,
'conv-select': conv_select,
'connected': connected,
'maxpool': maxpool,
'leaky': leaky,
'relu': relu, #added by wei
'dropout': dropout,
'flatten': flatten,
'avgpool': avgpool,
'softmax': softmax,
'identity': identity,
'crop': crop,
'local': local,
'select': select,
'route': route,
'reorg': reorg,
'conv-extract': conv_extract,
'extract': extract
}
下面添加relu的实现
位置:darkflow/net/ops/simple.py
#------------------------------------added by wei -------------------------------------
class relu(BaseOp):
def forward(self):
self.out = tf.nn.relu(
self.inp.out,
name = self.scope
)
def speak(self):
return 'relu'
四、总结
至此,darkflow的depthwise_conv layer升级就实现了,其他层升级步骤也差不多是这样。就这样,thats all~