caffe中使用python定義新的層


轉載鏈接:http://withwsf.github.io/2016/04/14/Caffe-with-Python-Layer/

Caffe通過Boost中的Boost.Python模塊來支持使用Python定義Layer:

  • 使用C++增加新的Layer繁瑣耗時而且很容易出錯
  • 開發速度執行速度之間的trade-off

編譯支持Python Layer的Caffe

如果是首次編譯,修改Caffe根目錄下的Makefile.cinfig,uncomment

1
WITH_PYTHON_LAYER:=1

 

如果已經編譯過

1
2
make clean
WITH_PYTHON_LAYER=1 make&& make pycaffe

 

使用Python Layer

在網絡的prototxt文件中添加一個Python定義的loss層如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
layer{
type: ’Python'
name: 'loss'
top: 'loss'
bottom: ‘ipx’
bottom: 'ipy'
python_param{
#module的名字,通常是定義Layer的.py文件的文件名,需要在$PYTHONPATH下
module: 'pyloss'
#layer的名字---module中的類名
layer: 'EuclideanLossLayer'
}
loss_weight: 1
}

 

定義Python Layer

根據上面的要求,我們在$PYTHONPAT在創建pyloss.py,並在其中定義EuclideanLossLayer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import caffe
import numpy as np
class EuclideadLossLayer(caffe.Layer):#EuclideadLossLayer沒有權值,反向傳播過程中不需要進行權值的更新。如果需要定義需要更新自身權值的層,最好還是使用C++
def setup(self,bottom,top):
#在網絡運行之前根據相關參數參數進行layer的初始化
if len(bottom) !=2:
raise exception("Need two inputs to compute distance")
def reshape(self,bottom,top):
#在forward之前調用,根據bottom blob的尺寸調整中間變量和top blob的尺寸
if bottom[0].count !=bottom[1].count:
raise exception("Inputs must have the same dimension.")
self.diff=np.zeros_like(bottom[0].date,dtype=np.float32)
top[0].reshape(1)
def forward(self,bottom,top):
#網絡的前向傳播
self.diff[...]=bottom[0].data-bottom[1].data
top[0].data[...]=np.sum(self.diff**2)/bottom[0].num/2.
def backward(self,top,propagate_down,bootm):
#網絡的前向傳播
for i in range(2):
if not propagate_down[i]:
continue
if i==0:
sign=1
else:
sign=-1
bottom[i].diff[...]=sign*self.diff/bottom[i].num

 

原理淺析

閱讀caffe源碼pythonlayer.hpp可以知道,類PythonLayer繼承自Layer,並且新增私有變量boost::python::object self來表示我們自己定義的python layer的內存對象。

類PythonLayer類的成員函數LayerSetUP, Reshape, Forward_cpu和Backward_cpu分別是對我們自己定義的python layer中成員函數setup, reshape, forward和backward的封裝調用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class PythonLayer : public Layer<Dtype> {
public:
PythonLayer(PyObject* self, const LayerParameter& param)
: Layer<Dtype>(param), self_(bp::handle<>(bp::borrowed(self))) { }

virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
// Disallow PythonLayer in MultiGPU training stage, due to GIL issues
// Details: https://github.com/BVLC/caffe/issues/2936
if (this->phase_ == TRAIN && Caffe::solver_count() > 1
&& !ShareInParallel()) {
LOG(FATAL) << "PythonLayer is not implemented in Multi-GPU training";
}
self_.attr("param_str") = bp::str(
this->layer_param_.python_param().param_str());
self_.attr("setup")(bottom, top);
}
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
self_.attr("reshape")(bottom, top);
}

virtual inline bool ShareInParallel() const {
return this->layer_param_.python_param().share_in_parallel();
}

virtual inline const char* type() const { return "Python"; }

protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
self_.attr("forward")(bottom, top);
}
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
self_.attr("backward")(top, propagate_down, bottom);
}

private:
bp::object self_;
};
整體流程大致為:首先從文件中讀入solver並生成一個solver,然后根據solver的net路徑生成一
個net,net調用layer_factory循環生成每個層,最后根據讀入model或是filler來初始化參數。從上面的流程可以知道layer_factory是循環生成每個層,我看.cpp文件也的確寫了#if
WITH_PYTHON_LAYER,然后有什么什么操作,比如儲存python
layer的python_param,並調用setup,這里實際上已經是利用boost進行C++
Python混編了。這些操作的定義就在python_layer.hpp文件中。
layer_factory中python
layer的setup相關具體操作是,先根據param找到module的位置,再加載module,再根據層名加載層,然后前向計算反向計算什么的。
這些就已經算是達到目的了。不過只是知道相對路徑,怎么可能加載成功呢?然后又繼續找啊找,終於在faster
rcnn的tools文件中找到。_init_paths里有寫一些預操作,比如將lib路徑寫入PYTHONPATH中,當然如果寫入的話,這樣就可以
直接加載了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM