zoukankan      html  css  js  c++  java
  • 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中,当然如果写入的话,这样就可以
    直接加载了。
  • 相关阅读:
    [置顶] Windows Phone后台音乐详解一
    Android应用开发学习笔记之BroadcastReceiver
    二维码闪电登录流程详解,附demo(1/2)
    C#利用Lambda和Expression实现数据的动态绑定
    从M个数中随机选出N个数的所有组合,有序,(二)
    3DShader之立方体环境映射(cubic environment mapping)
    二维码闪电登录流程详解,附demo(2/2)
    oracle 分区表exchange原理
    禁止页面复制功能 js禁止复制 禁用页面右键菜单
    sleep与信号唤醒的问题 & 内核对信号的处理方式
  • 原文地址:https://www.cnblogs.com/zf-blog/p/7323707.html
Copyright © 2011-2022 走看看