BMCPU 插件使用

BMCPU提供用户对深度学习的一些TPU不能实现的layer进行CPU编程,并插入到 Caffe/TensorFlow/MxNet/PyTorch等框架的深度学习神经网络中。 插入CPU layer的步骤请见BMNETC/BMNETT/BMNETM/BMNETP说明。 这里介绍用户如何进行CPU Layer编程。

环境构建

BMSDK中提供用户CPU Layer的开发环境(bmusercpu),用户可以拷贝该文件夹,并在这个文件夹内开发。 bmusercpu里面提供Makefile和example,该环境面向C++/C编程。用户需要修改Makefile.def里面的 INSTALL_LIB_PATH和INSTALL_INCLUDE_PATH。随着BMSDK的升级,用户CPU Layer的开发环境不受影响, 无需再次拷贝bmusercpu文件夹。

编程说明

编程环境中已创建好user cpu layer编程的工厂模式,详细可见user_cpu_layer_factory.h。 所以用户只需要按照工厂模式增加新的CPU Layer代码,以及在user_bmcpu_common.h中增加新CPU Layer 的参数。

这里以example来详细说明。user_bmcpu_common.h中已经有一个exp_layer的CPU Layer参数。当然用户可以删除该 example。添加新CPU Layer的参数设置如下:

typedef enum {
    USER_EXP = 0,
    USER_CPU_UNKNOW
} USER_CPU_LAYER_TYPE_T;


typedef struct user_cpu_exp_param
{
    float inner_scale_;
    float outer_scale_;
} user_cpu_exp_param_t;


typedef struct user_cpu_param
{
    int op_type;   /* USER_CPU_LAYER_TYPE_T */
    union {
        user_cpu_exp_param_t exp;
        /* notice: please add other cpu layer param here */
    }u;
}user_cpu_param_t;

首先需要在USER_CPU_LAYER_TYPE_T这个枚举参数中加入新CPU Layer type编号,这里要求编号要从0开始按顺序设置; 然后定义用户CPU Layer的参数,如user_cpu_exp_param_t;最后在user_cpu_param_t的union中加入用户CPU Layer的 structure参数。这样我们就完成了一个用户CPU Layer的参数设置。

接着按照工厂模式编写一个用户CPU Layer的代码,这里以user_cpu_layer.h和user_cpu_layer.cpp为例。 user_cpu_exp_layer.h代码如下:

#ifndef _USER_CPU_EXP_LAYER_H
#define _USER_CPU_EXP_LAYER_H
#include "user_cpu_layer.h"

namespace usercpu {

/*
*  notice: define new cpu layer, must like xxx_layer
*
*/
class cpu_exp_layer : public user_cpu_layer {
public:
    explicit cpu_exp_layer() {}
    virtual ~cpu_exp_layer() {}

    /* dowork */
    //virtual void process();
    int process(void *parm);
    void setParam(void *param);

    virtual string get_layer_name () const {
        return "USEREXP";
    }

protected:
    float inner_scale_;
    float outer_scale_;
};

} /* namespace usercpu */
#endif /* _USER_CPU_EXP_LAYER_H */

user_cpu_exp_layer.cpp代码如下:

#include "user_cpu_exp_layer.h"

namespace usercpu {

int cpu_exp_layer::process(void *param)
{
  setParam(param);
    /* Code From caffe */
  const float* bottom_data = input_tensors_[0];
  float* top_data = output_tensors_[0];
  const int count = input_shapes_[0][0] * input_shapes_[0][1] * input_shapes_[0][2] * input_shapes_[0][3];
  //Dtype negative_slope = this->layer_param_.exp_param().negative_slope();
  //float negative_slope = 0.0f;
  for (int i = 0; i < count; ++i) {
    top_data[i] = exp(bottom_data[i] * inner_scale_) * outer_scale_;
  }

  printf("user define cpu exp layer, process success!\n");
  return 0;
}

void cpu_exp_layer::setParam(void *param)
{
    layer_param_ = param;
    user_cpu_exp_param_t *exp_param = (user_cpu_exp_param_t*)layer_param_;
    inner_scale_ = exp_param->inner_scale_;
    outer_scale_ = exp_param->outer_scale_;
}

/* must register user layer
 * in macro cpu_test##_layer  == class cpu_test_layer
 * */

REGISTER_USER_CPULAYER_CLASS(USER_EXP, cpu_exp)

}

其中protected中为该CPU Layer需要的参数,setParam函数用于将void* param指针指向参数赋值给该CPU Layer,param 对应的数据结构对应user_bmcpu_common.h中定义的structure。process函数则是改CPU Layer的实际处理过程。

编译说明

用户开发环境中提供的Makefile和Makefile.def,所以用户直接make即可,通过后make install来安装。 这里将生成libusercpu.so,并和必须的.h文件一起安装到Makefile.def制定的安装目录中。 生成的libusercpu.so将作为CPU后端供bmcompiler和bmruntime一起使用。

若用户使用的是SoC,如SA服务器或者SE小盒子,则需要将文件夹copy到SoC的操作系统中,用SoC中的ARM编译器进行编译, 操作相同,不同的是install时可能需要设置Makefile.def里的安装路径。

调试优化

用户需要调试的话,可以自行搭建单元测试环境,对用户的CPU Layer进行测试。 若用户需要做性能优化等,则直接在该编程环境下修改即可。