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[:]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章