[C++ 基于Eigen库实现CRN前向推理]
前言:背景 与 Eigen库的使用整理
1. 前言
最近要用C++ 实现一下CRN网络的前向推理过程,了解了一下相关的库,决定使用Eigen提供的Tensor作为数据结构,并使用一些Eigen的常规的库函数。(纯C++实现嵌套的循环太多层了,容易写迷糊了,先降低难度写一波)
2. C++ Eigen库的使用
Eigen是可以用来进行线性代数、矩阵、向量操作等运算的C++库,它里面包含了很多算法。它的License是MPL2。它支持多平台。
Eigen采用源码的方式提供给用户使用,在使用时只需要包含Eigen的头文件即可进行使用。之所以采用这种方式,是因为Eigen采用模板方式实现,由于模板函数不支持分离编译,所以只能提供源码而不是动态库的方式供用户使用。
在找资料的时候发现了Eigen 矩阵操作与Matlab的对应关系,虽然在这个项目里没有用到,但后面可能会用到,先码住,链接在这。
2.1 下载与导入
Eigen下载地址官网在这,直接去官网下载解压包,解压到本地。
我这路径为E:\eigen-3.4.0,版本为3.4.0。
2.2 Tensor的初始化
这里只讲Tensor的用法,Matrix 和Vector的教程网上很多。
Tensor数据类型不在Eigen/Core 和Eigen/Dense中,而是需要额外引用unsupported。
#include <unsupported/Eigen/CXX11/Tensor>
简单声明:Tensor声明格式如Eigen::Tensor<data_type,n_dim>,data_type是数据类型,n_dim是多维矩阵的维度。这里声明了一个2维矩阵,但是没有定义具体的大小,在输出时为空。
Eigen::Tensor<double, 2> a;
std::cout << a << std::endl;
(blank)
在看指定具体大小的情况↓,可以看到为矩阵随机分配了初始值,矩阵只有在指定大小(即为其分配空间时才会产生内容)。通过resize方式,也可以进行初始化
Eigen::Tensor<float, 2> var(2, 3);
cout << var << endl;
-1.83108e+23 -1.82998e+23 0
7.00649e-43 7.00649e-43 0
Eigen::Tensor<float, 2> var;
var.resize(2, 3);
cout << var << endl;
-2.6161e+31 -2.61461e+31 0
5.73131e-43 5.73131e-43 0
几种初始化函数:
常数初始化
var.setConstant(12.3f);
cout << "Constant: " << endl << var << endl << endl;
=>
Constant:
12.3 12.3 12.3
12.3 12.3 12.3
置零初始化
Eigen::Tensor<float, 2> var(2, 3);
var.setZero();
cout << "Constant: " << endl << var << endl << endl;
=>
Constant:
0 0 0
0 0 0
赋值初始化
Eigen::Tensor<float, 2> var(2, 3);
var.setValues({
{
0.0f, 1.0f, 2.0f},
{
3.0f, 4.0f, 5.0f}});
cout << "Constant: " << endl << var << endl << endl;
=>
Constant:
0 1 2
3 4 5
随机初始化
Eigen::Tensor<float, 2> var(2, 3);
var.setRandom();
cout << "Constant: " << endl << var << endl << endl;
Constant:
0.896227 0.872269 0.605188
0.290171 0.24641 0.251816
2.3 Tensor常用库函数
-
重塑-reshape
可以看到Eigen的Tensor是Col-Major的,所以存储顺序为0.896277,0.290171,0.872269…,重塑完按照(3,2)的形状分配。这里与Pytorch的矩阵reshape结果是不一致的
Eigen::Tensor<float, 2> var(2, 3);
var.setRandom();
cout << "Constant: " << endl << var << endl << endl;
Eigen::array<int, 2> new_shape{
3, 2};
auto vat_T = var.reshape(new_shape);
cout << "Constant: " << endl << vat_T << endl << endl;
Constant:
0.896227 0.872269 0.605188
0.290171 0.24641 0.251816
Constant:
0.896227 0.24641
0.290171 0.605188
0.872269 0.251816
Pytorch.reshpe:
0.896227 0.872269
0.605188 0.290171
0.24641 0.251816
-
填充-Padding
采用auto或者声明变量的形式保存pad函数的返回值都可以,不同的是auto类型保存的是PadOp,是一个操作,后续怎么操作也不清楚,希望有知道的大佬指点一二。用Tensor变量保存的话就是赋值存储了,后续正常按Tensor操作就可以。
auto d0 = std::make_pair(1, 2);
auto d1 = std::make_pair(2, 1);
// auto dims = std::experimental::make_array(d0, d1); // 便利的写法
Eigen::array<std::pair<int, int>, 2> dims{
d0, d1};
auto padded = var.pad(dims, -1);
// Eigen::Tensor<float,2> padded;
// padded = var.pad(dims,-1);
std::cout << padded << std::endl;
=>
Constant:
0.896227 0.872269 0.605188
0.290171 0.24641 0.251816
-1 -1 -1 -1 -1 -1
-1 -1 0.896227 0.872269 0.605188 -1
-1 -1 0.290171 0.24641 0.251816 -1
-1 -1 -1 -1 -1 -1