平常中我们使用Eigen::MatrixXd二维矩阵。
Eigen的三维张量正处于开发中,在unsupported文件夹下,还没有放到正式代码中。
Eigen::Tensor来定义
IMPORTANT NOTE: The current developement version of Eigen (post-3.2) supports Tensors. This support is experimental and a moving target.
包含头文件:
#include
Eigen::Tensor<double, 3> epsilon(3,3,3);
epsilon.setZero();
epsilon(0,1,2) = 1;
epsilon(1,2,0) = 1;
epsilon(2,0,1) = 1;
epsilon(1,0,2) = -1;
epsilon(2,1,0) = -1;
epsilon(0,2,1) = -1;
Eigen::Tensor<double, 4> grassmannIdentity(3,3,3,3);
grassmannIdentity.setZero();
// this is not the most efficient way to write such a product,
// but is the only way possible with the current feature set
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
for (int l = 0; l < 3; l++) {
for (int m = 0; m < 3; m++) {
grassmannIdentity(i,j,l,m) += epsilon(i,j,k) * epsilon(k,l,m);
}
}
}
}
}
// verify
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int l = 0; l < 3; l++) {
for (int m = 0; m < 3; m++) {
assert(grassmannIdentity(i,j,l,m) == (int(i == l) * int(j == m) - int(i == m) * int(j == l)));
}
}
}
}
// dimensionalities
assert(epsilon.dimension(0) == 3);
assert(epsilon.dimension(1) == 3);
assert(epsilon.dimension(2) == 3);
auto dims = epsilon.dimensions();
assert(dims[0] == 3);
assert(dims[1] == 3);
assert(dims[2] == 3);
参考:官方文档Tensor support
// 定义一个2x3x4大小的矩阵
Eigen::Tensor<float, 3> a(2, 3, 4);
// 初始化为0
a.setZero();
// 访问元素
a(0, 1, 0) = 12.0f;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 4; k++) {
std::cout << a(i, j, k) << " ";
}
std::cout << std::endl;
}
std::cout << std::endl << std::endl;
}
// 输出维度
std::cout<<a.dimension(0)<<" "<<a.dimension(1)<<" "<<a.dimension(2)<<std::endl;
输出结果:
0 0 0 0
12 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
2 3 4
参考官方解释
The fixed sized equivalent of Eigen::Tensor<float, 3> t(3, 5, 7); is Eigen::TensorFixedSize<float, Size<3,5,7>> t;
// 固定 大小的Size 2x3x4
Eigen::TensorFixedSize<float, Eigen::Sizes<2, 3, 4>> b;
// 每个元素都设置固定值
b.setConstant(3.f);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 4; k++) {
std::cout << b(i, j, k) << " ";
}
std::cout << std::endl;
}
std::cout << std::endl << std::endl;
}
输出:
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
1.维度
Eigen::Tensor<float, 2> a(3, 4);
std::cout << "Dims " << a.NumDimensions;
//=> Dims 2
Eigen::Tensor<float, 2> a(3, 4);
int dim1 = a.dimension(1);
std::cout << "Dim 1: " << dim1;
//=> Dim 1: 4
2.形状
Eigen::Tensor<float, 2> a(3, 4);
const Eigen::Tensor<float, 2>::Dimensions& d = a.dimensions();
std::cout << "Dim size: " << d.size << ", dim 0: " << d[0]
<< ", dim 1: " << d[1];
//=> Dim size: 2, dim 0: 3, dim 1: 4
3.矩阵元素个数
Eigen::Tensor<float, 2> a(3, 4);
std::cout << "Size: " << a.size();
//=> Size: 12
4.初始化
/// 1.
// setConstant(const Scalar& val),用于把一个矩阵的所有元素设置成一个指定的常数。
Eigen::Tensor<string, 2> a(2, 3);
a.setConstant("yolo");
std::cout << "String tensor: " << endl << a << endl << endl;
//=>
// String tensor:
// yolo yolo yolo
// yolo yolo yolo
/// 2.
// setZero() 全部置零
a.setZero();
/// 3.
// setRandom() 随机初始化
a.setRandom();
std::cout << "Random: " << endl << a << endl << endl;
//=>
//Random:
// 0.680375 0.59688 -0.329554 0.10794
// -0.211234 0.823295 0.536459 -0.0452059
// 0.566198 -0.604897 -0.444451 0.257742
/// 4.
// setValues({..initializer_list}) 从列表、数据初始化
Eigen::Tensor<float, 2> a(2, 3);
a.setValues({{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}});
std::cout << "a" << endl << a << endl << endl;
//=>
// a
// 0 1 2
// 3 4 5
//如果给定的数组数据,少于矩阵元素的个数,那么后面不足的元素其值不变:
Eigen::Tensor<int, 2> a(2, 3);
a.setConstant(1000);
a.setValues({{10, 20, 30}});
std::cout << "a" << endl << a << endl << endl;
//=>
// a
// 10 20 30
// 1000 1000 1000
参考: Eigen::Tensor使用,定义高维矩阵
参考:官方 张量说明 Eigen-unsupported 3.4.0 (git rev e3e74001f7c4bf95f0dde572e8a08c5b2918a3ab)
使用Eigen的Tensor缘由
之所以使用Eigen的Tensor,主要是因为Tensorflow
中的一个C++项目中,需要对graph
中得到的Tensor
进行数据后处理。而tensorflow的C++API中用到了Eigen这个矩阵运算库中的Tensor,所做了些笔记。该笔记主要参考[Eigen-unsupport
这个官方的文档(英文),并以此为基础记录一些与numpy对应的函数与技巧。
Eigen中的Tensor中重载了*并用于实现点对点相乘和与标量相乘。关于点对点相乘,需要注意的是两个tensor的shape必须相同才能相乘,如果需要像numpy那样实现大小矩阵相乘(广播机制),需要自己设置广播,如下:
#include
Eigen::Tensor<float, 2, 1> a(3, 2);
Eigen::Tensor<float, 2, 1> b(1, 2);
a.setConstant(2.0);
b.setConstant(3.0);
Eigen::array<int, 2> bcast = { 3, 1 };//第一维复制3倍
auto ab = a*b.broadcast(bcast);
cout<<ab<<endl;
-----------------------
6 6
6 6
6 6
如果是要实现Tensor的内积,则需要使用tensor的contract
函数,可以实现高维的矩阵内积,如下:
Eigen::Tensor<int, 2> a(2, 3);
a.setValues({{1, 2, 3}, {6, 5, 4}});
Eigen::Tensor<int, 2> b(3, 2);
b.setValues({{1, 2}, {4, 5}, {5, 6}});
Eigen::array<Eigen::IndexPair<int>, 1> product_dims = { Eigen::IndexPair<int>(1, 0) };
Eigen::Tensor<int, 2> AB = a.contract(b, product_dims);
cout<<a<<endl<<b<<endl<<AB<<endl;
----------------------------------
1 2 3
6 5 4
1 2
4 5
5 6
24 30
46 61
Tensor中的reshape靠TensorMap
这种数据类型实现。
float nums[12] = { 0, 100, 200 , 300, 400, 500,600, 700, 800,900, 1000, 1100 };
Eigen::TensorMap<Eigen::Tensor<float, 2, 1>> a(nums, 4, 3);
Eigen::TensorMap<Eigen::Tensor<float, 2, 1>> b(a.data(), 2, 6);
----------------------
a
0 100 200
300 400 500
600 700 800
900 1000 1100
b
0 100 200 300 400 500
600 700 800 900 1000 1100
Tensor的堆叠,如两个(2,2)的tensor堆叠为(2,2,2),也可以实现拼接(4,2),(2,4)。
Eigen::Tensor<float, 2, 1> a(2, 2);
Eigen::Tensor<float, 2, 1> b(2, 2);
a.setConstant(1,0);
b.setConstant(2.0);
auto ab = a.concatenate(b,0); //(4,2)
auto ab1 = a.concatenate(b,1); //(2,4)
Eigen::DSizes<Eigen::DenseIndex, 3> three_dim(3,2,1);
Eigen::Tensor<float, 3, 1> ab2 = a.reshape(three_dim).concatenate(b.reshape(three_dim), 2) //(2,2,2)
Tensor的find(where)操作
Tensor中没有matlab中的find或者python中numpy里的where,用于寻找满足条件的元素的下标。这里用到的思路是首先通过比较(><的符号Eigen已经重载),考虑到tensor的内存是连续存储的,通过遍历每个元素,计算满足的元素的下标。由于下面的代码中已经设置了Tensor为行优先存储,下标计算如下所示。
class getthree_index
{
public:
vector<int>* fir_dim;
vector<int>* sec_dim;
vector<int>* thi_dim;
int fir, sec, thi;
int idx = 0;
getthree_index(vector<int>* f, vector<int>* s, vector<int>* t, int fn, int sn, int tn)
{
fir_dim = f; sec_dim = s; thi_dim = t; fir = fn; sec = sn; thi = tn;
};
void operator()(bool i);
};
float nums[12] = { 0, 100, 200 , 300, 400, 500,600, 700, 800,900, 1000, 1100 };
Eigen::TensorMap<Eigen::Tensor<float, 3, 1>> a(nums, 2, 3, 2);
auto threshold = a.constant(400);
Eigen::Tensor<bool, 3, 1> res = a>threshold.eval();
vector<int> fir_dim;
vector<int> sec_dim;
vector<int> thi_dim;
auto shape = a.dimensions();
for_each(res.data(), res.data() + res.size(), getthree_index(&fir_dim, &sec_dim, &thi_dim,
int(shape[0]), int(shape[1]), int(shape[2])));
参考:Eigen中Tensor的使用技巧