习惯上python之后,我很自然的就想到怎么和C++代码结合的问题了,毕竟核心计算模块不敢用python做,之前用过swig,但是感觉不够灵活,方便。后来找到了boost库,开始上手会遇到一些乱七八糟的问题,但是做完了回头看看,还是蛮方便的,起码相对于swig来说。
需求其实是要在树莓派上做个KWS模型,提特征和网络前向的代码不想再重新写了(虽然之前已经实现过了),所以突发奇想,基于kaldi给python写一个wrapper调用就行了。
Boost库的安装
分为俩类,一类不需要编译成库文件,包含头文件即可使用,另外一类是需要编译安装库文件的,使用的时候加上链接,Boost.Python就属于后一类,安装完成Boost.python之后,默认会编译numpy库,所以可以直接使用boost.numpy
Boost Python和Numpy的使用
- boost python一般用来封装C++的API给python调用,一般编译成特定的lib,使用python的时候,直接import就行了
- boost numpy一般给C++提供直接处理传,返回入numpy矩阵的功能
Boost Python/Numpy初步使用
- Boost Python
正常定义C++类和成员函数,使用BOOST_PYTHON_MODULE
定义模块名和对应的函数导出名就行了,详见boost.python - Boost Numpy
这个主要是一系列API掌握就行了,详见boost.python(Numpy)
使用boost封装kaldi nnet1的网络前向
一个简单的使用例子,基本操作都在里面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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using namespace kaldi;
using namespace kaldi::nnet1;
// 命名空间
namespace py = boost::python;
namespace np = boost::python::numpy;
class NnetWrapper {
public:
NnetWrapper(std::string nnet_mdl = "final.nnet");
// 传入,返回numpy类型
np::ndarray Predict(np::ndarray &vector);
private:
Nnet nnet_;
// keep memory not free
CuMatrix<BaseFloat> nnet_out;
};
NnetWrapper::NnetWrapper(std::string nnet_mdl) {
nnet_.Read(nnet_mdl);
}
np::ndarray NnetWrapper::Predict(np::ndarray &vector) {
int cols, rows;
// 获取内建dtype
KALDI_ASSERT(vector.get_dtype() == np::dtype::get_builtin<float>());
// 获取维度
KALDI_ASSERT(vector.get_nd() <= 2);
cols = vector.shape(vector.get_nd() - 1);
KALDI_ASSERT(cols == nnet_.InputDim());
rows = vector.get_nd() == 1 ? 1: vector.shape(0);
// 获取数据指针
CuSubMatrix<BaseFloat> nnet_in(reinterpret_cast<BaseFloat*>(vector.get_data()),
rows, cols, vector.strides(0) / sizeof(BaseFloat));
nnet_.Feedforward(nnet_in, &nnet_out);
// 有已知数据,建立ndarray类型变量,传参如下:
// data_addr, dtype, shape, stride, obj
return np::from_data(nnet_out.Data(), np::dtype::get_builtin<float>(),
py::make_tuple(rows, nnet_out.NumCols()),
py::make_tuple(nnet_out.Stride() * sizeof(BaseFloat), sizeof(BaseFloat)),
py::object());
}
BOOST_PYTHON_MODULE(pynnet1) {
using namespace boost::python;
// 初始化numpy模块
np::initialize();
// 导出构造函数为init, 可选参数输入,和预测函数Predict为predict
class_<NnetWrapper>("nnet1", init<optional<std::string> >())
.def("predict", &NnetWrapper::Predict);
}
Cmake编译,基于python2.7
给python调用肯定编译成库,注意
- 如果不用numpy,只需要额外链接
python2.7
和boost_python
俩个库 - 使用numpy的话,链接
boost_numpy
boost库默认安装在/usr/local/lib
之下,头文件在/usr/local/include/boost
里面,编译时需要指定这些目录
Cmake完整如下
1 | cmake_minimum_required(VERSION 3.5) |