当前位置:   article > 正文

2.Eigen Tensor详解【一】

eigen tensor

目录

一 不同类型Tensor的构造方式

二 访问Tensor元素

三 Tensor 布局

四 Tensor 运算

五 Tensor 运算和C++ “auto” 关键字

六.控制何时执行表达式的真正计算

1. 赋值给Tensor, TensorFixedSize, TensorMap

2.调用eval()函数

3.赋值给TensorRef

 七 相关API说明

1.Tensor 属性的查询


tensorflow 的C++ api 中采用了Eigen的Tensor ,因此本文仔细探究一下Eigen 库Tensor的始末。

Tensor(张量) 是多维数组,元素通常是标量,但也支持复杂的元素(如字符串)

见:https://eigen.tuxfamily.org/dox-devel/unsupported/eigen_tensors.html

一 不同类型Tensor的构造方式

  1. #include "./eigen/unsupported/Eigen/CXX11/Tensor"
  2. /*
  3. Eigen 不同类型 tensor 的构造
  4. */
  5. int Eigen_Construct()
  6. {
  7. //一 Tensor 类
  8. /*
  9. Tensor 是模板类,模板中一共有4个参数,前三个参数的含义分别如下
  10. template<typename Scalar_, int NumIndices_, int Options_, typename IndexType_>
  11. class Tensor : public TensorBase<Tensor<Scalar_, NumIndices_, Options_, IndexType_> >
  12. 1.float 代表Tensor 中存储的数据类型
  13. 2.NumIndices_ 代表维度,即多维数组的维度,如3代表三维数组
  14. 3. Options_ 可选参数,决定数据如何存储,如 Eigen::RowMajor
  15. */
  16. //1.创建了一个3维的向量,明确了各个维度的尺寸分别是234,该向量分配了24个float 空间(24 = 2*3*4
  17. Eigen::Tensor<float, 3, Eigen::RowMajor> t_3d(2, 3, 4);
  18. // 重新设置t_3d 向量的尺寸为(343),可以把他的不同维度设置不同的尺寸,但是维度个数需要一致
  19. t_3d = Eigen::Tensor<float, 3, Eigen::RowMajor>(3, 4, 3);
  20. //2.创建一个2维的向量,不明确各个维度的尺寸,而是通过数组的形式给出,如下面的维度用{57}数组给出
  21. Eigen::Tensor<string, 2> t_2d({ 5, 7 });
  22. //二 TensorFixedSize类
  23. /*
  24. TensorFixedSize<data_type, Sizes<size0, size1, ...>>
  25. template<typename Scalar_, typename Dimensions_, int Options_, typename IndexType>
  26. class TensorFixedSize : public TensorBase<TensorFixedSize<Scalar_, Dimensions_, Options_, IndexType> >
  27. 1.float 代表Tensor 中存储的数据类型
  28. 2.Dimensions_ 代表各个维度的尺寸
  29. 3. Options_ 可选参数,决定数据如何存储,如 Eigen::RowMajor
  30. TensorFixedSize 需要在定义时明确各个维度的尺寸,因此运算速度较快
  31. */
  32. //创建一个4*3 的 float 类型的 Tensor
  33. Eigen::TensorFixedSize<float, Eigen::Sizes<4, 3>> t_4x3;
  34. //三 TensorMap 类
  35. /*
  36. TensorMap<Tensor<data_type, rank>>
  37. template<typename PlainObjectType, int Options_, template <class> class MakePointer_>
  38. class TensorMap : public TensorBase<TensorMap<PlainObjectType, Options_, MakePointer_> >
  39. 1.PlainObjectType
  40. 2.Options_
  41. 3. MakePointer_
  42. TensorMap用于在内存上创建一个张量,内存是由代码的另一部分分配和拥有的。它允许把任何一块分配的内存看作一个张量。
  43. 此类的实例不拥有存储数据的内存。
  44. 一句话总结:TensorMap 并不拥有内存,只是组织其他Tensor 。
  45. */
  46. //可以通过传入一块儿内存,不同的维度构造TensorMap
  47. int storage[128]; // 2 x 4 x 2 x 8 = 128
  48. Eigen::TensorMap<Eigen::Tensor<int, 4>> t_4d(storage, 2, 4, 2, 8);
  49. //同一块儿内存可以被看作不同的TensorMap
  50. Eigen::TensorMap<Eigen::Tensor<int, 2>> t_2d_2(storage, 16, 8);
  51. Eigen::TensorFixedSize<float, Eigen::Sizes<4, 3>> t_4x3_2;
  52. Eigen::TensorMap<Eigen::Tensor<float, 1>> t_12(t_4x3.data(), 12);
  53. return 1;
  54. }

二 访问Tensor元素

  1. void visitTensorElement()
  2. {
  3. /*
  4. 1. 通过指定不同的下标来访问元素
  5. tensorName(index0, index1...)
  6. */
  7. Eigen::Tensor<float, 3> t_3d(2, 3, 4);
  8. t_3d(0, 1, 0) = 12.0f;
  9. // Initialize all elements to random values.
  10. for (int i = 0; i < 2; ++i)
  11. {
  12. for (int j = 0; j < 3; ++j)
  13. {
  14. for (int k = 0; k < 4; ++k)
  15. {
  16. t_3d(i, j, k) = rand();
  17. cout << t_3d(i, j, k) << " ";
  18. }
  19. cout << endl;
  20. }
  21. cout << endl;
  22. }
  23. // Print elements of a tensor.
  24. for (int i = 0; i < 2; ++i) {
  25. cout << t_3d(i, 0, 0) << endl;
  26. }
  27. }

输出结果如下

三 Tensor 布局

Tensor 库目前支持两种布局方式:列优先(默认),行优先。目前只有列有限是完全支持的,不推荐使用行优先

表达式的所有参数必须使用相同的布局。试图混合不同的布局将导致编译错误,可以使用swap_layout()方法更改张量或表达式的布局。注意,这也将颠倒维的顺序。

  1. void tensorLayout()
  2. {
  3. //如下面分别设置了列优先 和 行优先
  4. //Eigen::Tensor<float, 3, Eigen::ColMajor> col_major1; // equivalent to Tensor<float, 3>
  5. //float storage[128]; // 2 x 4 x 2 x 8 = 128
  6. //Eigen::TensorMap<Eigen::Tensor<float, 3, Eigen::ColMajor> > row_major1(storage, 2, 2, 4, 8);
  7. //
  8. Eigen::Tensor<float, 2, Eigen::ColMajor> col_major(2, 4);
  9. Eigen::Tensor<float, 2, Eigen::RowMajor> row_major(2, 4);
  10. Eigen::Tensor<float, 2> col_major_result = col_major; // 默认为colMajor ,layout方式相同,因此可以赋值
  11. //Eigen::Tensor<float, 2> col_major_result2 = row_major; // layout方式不同,编译出错,错误信息为 error C2338: YOU_MADE_A_PROGRAMMING_MISTAKE
  12. // Simple layout swap
  13. col_major_result = row_major.swap_layout();
  14. eigen_assert(col_major_result.dimension(0) == 4);
  15. eigen_assert(col_major_result.dimension(1) == 2);
  16. }

四 Tensor 运算

Eigen 的tensor 库提供了大量的运算操作(数值运算,几何运算),这些运算作为Tensor类的成员函数或者操作符重载。

例如下面的代码计算两个tensor的和

  1. void tensorCompute()
  2. {
  3. Eigen::Tensor<int, 2> t1(2, 2);
  4. Eigen::Tensor<int, 2> t2(2, 2);
  5. for (int i = 0;i < 2; i++)
  6. for (int j = 0; j < 2; j++)
  7. {
  8. t1(i, j) = 1;
  9. t2(i, j) = 2;
  10. }
  11. Eigen::Tensor<int, 2> t3 = t1 + t2;
  12. cout << "t1:" << endl << t1 << endl;
  13. cout << "t2:" << endl << t2 << endl;
  14. cout << "t3:" << endl << t3 << endl;
  15. }

五 Tensor 运算和C++ “auto” 关键字

因为张量运算产生张量运算符,c++ auto关键字没有它直观的意义。考虑这两行代码:

  1. Tensor<float, 3> t3 = t1 + t2;
  2. auto t4 = t1 + t2;

第一行分配了一个张量t3, 它保留t1 + t2 的结果,第二行,t4实际上是计算t1+t2的 "tree of tensor operators"(就是张量的操作树)

事实上t4 不是一个张量,它不能获取到相应的元素值

  1. Tensor<float, 3> t3 = t1 + t2;
  2. cout << t3(0, 0, 0); // OK prints the value of t1(0, 0, 0) + t2(0, 0, 0)
  3. auto t4 = t1 + t2;
  4. cout << t4(0, 0, 0); // Compilation error!

当使用auto的时候,得到的不是一个张量,而是一个无值的表达式,所以只能使用auto来推迟真正的计算

不幸的是,没有一个单独的底层具体类型来保存未计算的表达式,因此在需要保存未计算的表达式时,必须使用auto
当需要执行真正的计算,并获取表达式的结果时,需要新建一个tensor 来接收auto的值,如下所示

  1. auto t4 = t1 + t2;
  2. Tensor<float, 3> result = t4; // Could also be: result(t4);
  3. cout << result(0, 0, 0);

在获取结果之前,我们可以一直用auto来得到无值表达式,这个过程中不会有真正的计算发生(有点类似于tensorflow的静态图思想)

  1. // One way to compute exp((t1 + t2) * 0.2f);
  2. auto t3 = t1 + t2;
  3. auto t4 = t3 * 0.2f;
  4. auto t5 = t4.exp();
  5. Tensor<float, 3> result = t5;
  6. // Another way, exactly as efficient as the previous one:
  7. Tensor<float, 3> result = ((t1 + t2) * 0.2f).exp();

六.控制何时执行表达式的真正计算

如下几种方法可以控制何时计算表达式:

  • 赋值给Tensor, TensorFixedSize, TensorMap
  • 调用eval()方法
  • 赋值给TensorRef

1. 赋值给Tensor, TensorFixedSize, TensorMap

最常用的方式就是将表达式 赋值给一个Tensor,如下所示
 

  1. void tensorCompute()
  2. {
  3. Eigen::Tensor<int, 2> t1(2, 2);
  4. Eigen::Tensor<int, 2> t2(2, 2);
  5. for (int i = 0;i < 2; i++)
  6. for (int j = 0; j < 2; j++)
  7. {
  8. t1(i, j) = 1;
  9. t2(i, j) = 2;
  10. }
  11. Eigen::Tensor<int, 2> t3 = t1 + t2;
  12. cout << "t1:" << endl << t1 << endl;
  13. cout << "t2:" << endl << t2 << endl;
  14. cout << "t3:" << endl << t3 << endl;
  15. auto t4 = t3 * 2; // t4 is an Operation.
  16. Eigen::Tensor<int, 2> result = t4; // The operations are evaluated
  17. cout << t4 << endl;
  18. }

2.调用eval()函数

在计算大型复合表达式时,有时需要告诉Eigen表达式树中的中间值需要提前计算。这是通过插入对表达式操作的eval()方法的调用来实现的

  1. void tensorCompute2()
  2. {
  3. // The previous example could have been written:
  4. Eigen::Tensor<float, 2> t1(2, 2);
  5. Eigen::Tensor<float, 2> t2(2, 2);
  6. for (int i = 0; i < 2; i++)
  7. for (int j = 0; j < 2; j++)
  8. {
  9. t1(i, j) = 1.0f;
  10. t2(i, j) = 2.0f;
  11. }
  12. Eigen::Tensor<float, 2> result = ((t1 + t2) * 0.2f).exp();
  13. // If you want to compute (t1 + t2) once ahead of time you can write:
  14. Eigen::Tensor<float, 2> result2 = ((t1 + t2).eval() * 0.2f).exp();
  15. cout << result2 << endl;
  16. }

3.赋值给TensorRef

如果你只需要从一个表达式的值中访问几个元素,你可以通过使用TensorRef来避免在整个张量中具体化这个值。

TensorRef是一个用于任何Eigen 操作的包装类,它重载了括号()运算符可以允许访问到表达式中的各个元素,TensorRef很方便,

因为操作表达式本身不提供访问单个元素的方法

  1. Eigen::TensorRef<Eigen::Tensor<float, 2> > ref = ((t1 + t2) * 0.2f).exp();
  2. // Use "ref" to access individual elements. The expression is evaluated
  3. // on the fly.
  4. float at_0 = ref(0, 0, 0);
  5. cout << ref(0, 1, 0);

 七 相关API说明

1.Tensor 属性的查询

  1. void testTensorInfo()
  2. {
  3. //1. NumDimensions 输出维度个数
  4. Eigen::Tensor<float, 2> a(3, 4);
  5. cout << "Dims " << a.NumDimensions <<endl; // 输出:Dims:2
  6. //2. dimensions() 输出不同维度的size
  7. //typedef DSizes<Index, NumIndices_> Dimensions;
  8. const Eigen::Tensor<float, 2>::Dimensions & d = a.dimensions();
  9. cout << "Dim size: " << d.size()<< ", dim 0: " << d[0]
  10. << ", dim 1: " << d[1] <<endl; //Dim size: 2, dim 0: 3, dim 1: 4
  11. //3. Index dimension(Index n) 输出第n 维的维度
  12. int dim1 = a.dimension(1);
  13. cout << "Dim 1: " << dim1 <<endl; // Dim 1: 4
  14. //4. Index size() 输出tensor的总元素个数
  15. cout << "Size: " << a.size() <<endl;
  16. }

 

相关测试代码见:https://github.com/Mayi-Keiji/EigenTest.git

未完待续~~

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/79240
推荐阅读
相关标签
  

闽ICP备14008679号