Caffe学习(七):Caffe添加自定义层(2):Python层

前面一篇讲述了如何添加自定义的Caffe C++层,本篇讲解如何添加自定义的Python层,依然以mnist example为例子,在caffe-master\examples\mnist中的 lenet_train_test.ptototxt文件中,conv1层前添加以下测试层,这个层对网络不做任何修改,仅用于测试。

layer {
  name: 'test'
  type: 'Python'
  bottom: 'data'
  top:'data'
  python_param {
    module: 'test_layer' #与python文件名相同
    layer: 'MyLayer' #与python文件中的类名相同
    param_str: "'num_classes': 10"
  }
}

这个层的类型为python,我们需要编写python文件来实现这个层的内容。在caffe-master\examples\mnist目录下创建一个文件夹python_layers文件夹,在python_layers文件夹中创建test_layer.py, 类名为MyLayer,如下,包含setup,reshape, forward, backward 4个函数,每个函数的输入输出都是bottom及top,对应prototxt文件的bottom及top,bottom与top均是Blob数据类型。

import caffe

class MyLayer(caffe.Layer):
    #初始化时调用,检查输入的参数是否异常
	def setup(self, bottom, top):
         print 'setup'
		
    #初始化时、前向传播时调用,用于设定参数的siaze
	def reshape(self, bottom, top):
         print 'reshape'
		
    #前向传播时调用
	def forward(self, bottom, top):
         print 'forward'

    #反向传播,如果本层不需要反向传播,则不调用
	def backward(self, bootom, top):
         print 'backward'

以上是最简单的写法,各个函数仅打印了函数的名。我们先不详细实现各个函数的功能,我们先运行这个Python层,先看能否运行成功。

在Caffe根目录下,即caffe-master\下创建train_mnist.py文件,注意这里要在caff-master目录下创建,因为mnist的*.prototxt文件中设置的路径是在examples开始。

import os.path as osp
import sys
import caffe

this_dir = osp.dirname(__file__)
layerpath = osp.join(this_dir, 'examples/mnist/python_layers')
sys.path.insert(0, layerpath)  #将python文件的位置添加到环境变量

caffe.set_mode_gpu()
caffe.set_device(0)

solver = caffe.SGDSolver('./examples/mnist/lenet_solver.prototxt')
solver.solve()

运行以上python文件,通过打印的log可以看到在初始化时,'setup'和'reshape' 各打印了2次,然后在训练过程中,有多少次向前计算则‘reshape’与‘forward’就被调用多少次,而‘forward没有被调用’。通过实验测试,前向计算的次数为:

max\_iter + \left (\left \lfloor\frac{max\_iter}{test\_interval} \right \rfloor+1 \right )\cdot test\_iter + \left ( (max\_iter\geq test\_interval)? \right )

\left (\left \lfloor\frac{max\_iter}{test\_interval} \right \rfloor+1 \right )\cdot test\_iter表示验证测试前向计算的次数,因为第0次时要做一次前向验证测试,因此要加1,后面的\left ( (max\_iter\geq test\_interval)? \right )表示当max_iter大于等于test_interval时式子为1,否则为0。例如当max_iter为10000,test_interval为500,test_iter为100时,前向计算的次数为12101次。

下面逐步来实现各层。

setup 函数

def setup(self, bottom, top):
     layer_params = yaml.load(self.param_str)
     self._num_classes = layer_params['num_classes']
     print 'setup, bottom len:', len(bottom), ', top len:', len(top),  ', class num:', self._num_classes
		

注意这里要在py文件头部import yaml,这里获取了.prototxt文件设置的参数param_str。另外这里len(top)与len(bottom)表示输入输出的参数个数。如果层文件设置为:

layer {
  name: 'test'
  type: 'Python'
  bottom: 'data'
  bottom: 'label'
  top:'data'
  top:'label'
  python_param {
    module: 'test_layer'
    layer: 'MyLayer'
    param_str: "'num_classes': 10"
  }
}

这里bottom和top的参数都有多个,通过序号的方式分别获取与设置bottom与top的参数,如下:

top[0].reshape(*bottom[0].data.shape)  #data
top[1].reshape(*bottom[1].data.shape)  # label

reshape 函数

这里reshape直接写作如下:

def reshape(self, bottom, top):
     top[0].reshape(*bottom[0].data.shape)  #data

forward函数

forward函数如下:

def forward(self, bottom, top):
     top[0].data[...] = bottom[0].data[:]

backward函数

def backward(self, bootom, top):
     for i in range(len(propagate_down)):
         if not propagate_down[i]:
              continue
         bottom[i].diff[...] = top[i].diff[:]
发布了40 篇原创文章 · 获赞 51 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zh8706/article/details/95647495
今日推荐