Commit 9b11391e by liyinqiao

1. Merge with xiao branch; 2. Update doc

parent 1410c491
# NiuTrans.Tensor张量计算库
## NiuTrans.Tensor
NiuTrans.Tensor是小牛开源项目所开发的一个工具包,提供了完整的张量定义及计算功能,可以被用于深度学习相关研究及工业系统的开发。NiuTrans.Tensor具有以下特点:
* 简单小巧,易于修改
......@@ -12,38 +11,52 @@ NiuTrans.Tensor撠皞★撘銝芸極
## 安装NiuTrans.Tensor
在开始创建您的项目并使用NiuTrans.Tensor工具包时,需要注意的是:
* 所创建项目如在CPU上运行,我们的系统支持高性能的数学运算库,推荐安装[Intel® MKL](https://software.intel.com/en-us/mkl)[OpenBLAS](http://www.openblas.net/)
* 所创建项目如需在GPU上运行,需安装 [NVIDIA®CUDA®Toolkit](https://developer.nvidia.com/cuda-downloads),CUDA版本需求为9.0及以上,CUDA工具为创建高性能GPU加速应用程序提供了开发环境。
在使用小牛开源项目所开发的NiuTrans.Tensor工具包时:
* 首先需要将NiuTrans.Tensor代码包含在所创建的项目中
* 需要引用XTensor.h、core里的CHeader.h和function里的FHeader.h这三个头文件:
* 通过XTensor.h可以获取我们需要操作的XTensor类
* 通过core里的CHeader.h可以对Tensor进行一些张量运算
* 通过function里的FHeader.h可以调用一些激活函数
* 在所创建项目中使用命名空间nts
## 什么是张量
在计算机科学中,张量(Tensor)通常被定义为$n$维空间中的一种量,它具有$n$个分量,这种量本质上是一个多维数组( multidimensional array)。张量的阶或秩是这个多维数组的维度,或者简单理解为索引张量里的每个元素所需要的索引个数。通常来说,0阶张量被定义为标量(Scalar),1阶张量被定义为向量(vector),而2阶张量被定义为矩阵(matrix)。比如,在一个三维空间中,1阶张量就是空间中点所表示的向量$(x,y,z)$,其中$x$、$y$、$z$分别表示这个点在三个轴上的坐标。
在计算机科学中,张量(Tensor)通常被定义为\\(n\\)维空间中的一种量,它具有\\(n\\)个分量,这种张量本质上是一个多维数组( multidimensional array)。张量的阶或秩是这个多维数组的维度,或者简单理解为索引张量里的每个元素所需要的索引个数。通常来说,0阶张量被定义为标量(Scalar),1阶张量被定义为向量(vector),而2阶张量被定义为矩阵(matrix)。比如,在一个三维空间中,1阶张量就是空间中点所表示的向量\\((x,y,z)\\),其中\\(x\\)、\\(y\\)、\\(z\\)分别表示这个点在三个轴上的坐标。
张量是一种高效的数学建模工具,它可以将复杂的问题通过统一、简洁的方式进行表达。比如,姜英俊同学做饭需要2斤牛肉、5斤土豆,市场上牛肉每斤32元、土豆每斤2元,那么购买这些食物总共花费$2 \times 32 + 5 \times 2 = 74$元。如果用张量来描述,我们可以用一个1阶张量$a=(2,5)$表示所需不同食物的重量。然后用另一个1阶张量$b=(32,2)$表示不同食物的价格。最后,我们用一个0阶张量$c$表示购买这些食物的总价,计算如下
张量是一种高效的数学建模工具,它可以将复杂的问题通过统一、简洁的方式进行表达。比如,姜英俊同学做饭需要2斤牛肉、5斤土豆,市场上牛肉每斤32元、土豆每斤2元,那么购买这些食物总共花费\\(2 \times 32 + 5 \times 2 = 74\\)元。如果用张量来描述,我们可以用一个1阶张量\\(a=(2,5)\\)表示所需不同食物的重量。然后用另一个1阶张量\\(b=(32,2)\\)表示不同食物的价格。最后,我们用一个0阶张量\\(c\\)表示购买这些食物的总价,计算如下
$$
\begin{aligned}
c & = a \times b^T \\
& = \left(\begin{matrix}2 & 5\end{matrix}\right) \times \left(\begin{matrix}32 \\ 2\end{matrix}\right) \\
& = 2 \times 32 + 5 \times 2 \\
c & = a \times b^T \\\\
& = \left(\begin{matrix}2 & 5\end{matrix}\right) \times \left(\begin{matrix}32 \\\\ 2\end{matrix}\right) \\\\
& = 2 \times 32 + 5 \times 2 \\\\
& = 74
\end{aligned}
$$
中$b^T$表示行向量$b$的转置 - 列向量,$\times$表示向量的乘法。第二天,姜英俊同学换了一个市场,这里牛肉每斤35元、土豆每斤1元。如果要知道在两个市场分别购物的总价,可以把$b$重新定义为一个2阶张量$\left(\begin{matrix}12 & 2\\35 & 1\end{matrix}\right)$,总价$c$定义为一个2阶张量。同样有
\\(b^T\\)表示行向量\\(b\\)的转置 - 列向量,\\(\times\\)表示向量的乘法。第二天,姜英俊同学换了一个市场,这里牛肉每斤35元、土豆每斤1元。如果要知道在两个市场分别购物的总价,可以把\\(b\\)重新定义为一个2阶张量\\(\left(\begin{matrix}32 & 2 \\\\ 35 & 1\end{matrix}\right)\\),总价\\(c\\)定义为一个2阶张量。同样有
$$
\begin{aligned}
c & = a \times b^T \\
& = \left(\begin{matrix}2 & 5\end{matrix}\right) \times \left(\begin{matrix}12 & 35 \\ 2 & 1\end{matrix}\right) \\
c & = a \times b^T \\\\
& = \left(\begin{matrix}2 & 5\end{matrix}\right) \times \left(\begin{matrix}32 & 35 \\\\ 2 & 1\end{matrix}\right) \\\\
& = \left(\begin{matrix}74 & 75\end{matrix}\right)
\end{aligned}
$$
即,在两个市场分别花费74元和75元。可以看出,利用张量可以对多样、复杂的问题进行建模,比如,可以进一步扩展上述问题中$a$、$b$、$c$的定义,把它们定义成更高阶的张量,处理不同时间、不同市场、不同菜谱的情况,但是不论情况如何变化,都可以用同一个公式$c = a \times b^T$来描述问题。
即,在两个市场分别花费74元和75元。可以看出,利用张量可以对多样、复杂的问题进行建模,比如,可以进一步扩展上述问题中\\(a\\)、\\(b\\)、\\(c\\)的定义,把它们定义成更高阶的张量,处理不同时间、不同市场、不同菜谱的情况,但是不论情况如何变化,都可以用同一个公式\\(c = a \times b^T\\)来描述问题。
许多现实世界的问题都可以被描述为张量表达式(expression),也就是把张量的组合、计算描述为算数表达式。这种建模方式也构成了现代神经网络模型及深度学习方法的基础。在许多机器学习工具中,张量计算已经成为了神经网络前向、反向传播等过程的基本单元,应用十分广泛。
## 如何定义张量
如果你是一名C/C++或者Python的使用者,那么在程序中使用NiuTrans.Tensor定义张量将非常简单。首先,下载NiuTrans.Tensor的工具包(source???),并加压到任意目录,比如~/NTS目录。我们会在NTS这个目录中有找到source子目录,它是存放源代码的目录。对于source子目录的结构,信息如下:
如果你是一名C/C++或者Python的使用者,那么在程序中使用NiuTrans.Tensor定义张量将非常简单。首先,下载NiuTrans.Tensor的工具包(source???),并解压到任意目录,比如~/NTS目录。我们会在NTS这个目录中找到source子目录,它是存放源代码的目录。对于source子目录的结构,信息如下:
* ~/NTS/source/XTensor.h - 定义了张量结构XTensor,以及构建和销毁XTensor的接口
* ~/NTS/source/core - 存放张量计算的函数声明及函数体实现的源文件
......@@ -52,8 +65,8 @@ $$
* ~/NTS/source/*.h(cpp) - 与张量定义不相关,后文介绍 :)
以C/C++为例,仅需要在源程序中引用XTensor.h头文件就可以完成张量的定义。下面是一个简单的示例程序sample.cpp
<pre><code>#inlucde "XTensor.h" // 引用XTensor定义的头文件
```
#inlucde "XTensor.h" // 引用XTensor定义的头文件
using namepsace nt; // 使用XTensor所在的命名空间nt
......@@ -61,49 +74,54 @@ int main(int argc, const char ** argv)
{
// 声明一个变量tensor,它的类型是XTensor
XTensor tensor;
// 初始化这个变量为50列*100行的矩阵(2阶张量)
InitTensor2D(&tensor, 50, 100, X_FLOAT);
// 之后可以使用张量tensor了
return 0;
}
</code></pre>
```
下一步,编译以上源程序,这个过程需要指定XTensor.h头文件所在目录。比如,使用g++编译sample.cpp(如果你使用的是visual studio,请看这里???)
<pre><code>g++ sample.cpp -I~/NTS/source -o sample</code></pre>
```
g++ sample.cpp -I~/NTS/source -o sample
```
在sample.cpp中使用了XTensor,它是NiuTrans.Tensor里的一个类,这个类定义了张量所需的数据结构。我们可以使用这个类完成对张量的计算、拷贝等各种操作。XTensor类型的变量被声明后,这个变量需要被初始化,或者说被真正指定为一个张量,比如,指定张量各个维度的大小、张量中每个单元的数据类型、给张量分配内存空间等。InitTensor2D()就是一个张量初始化函数,它把张量初始化为一个矩阵,有四个参数:指向被初始化的张量的指针,矩阵的列数,矩阵的行数,数据单元的类型。这里X_FLOAT,是NiuTrans.Tensor自定义的枚举类型,它表示单精度浮点数。我们也可以使用X_INT或者X_DOUBLE,将数据类型指定为32bit整数或者双精度浮点数。
NiuTrans.Tensor也提供了其它方式定义张量。比如可以直接调用一个函数完成张量的创建,而且可以显性释放张量。下面是一段示例代码(sample2.cpp):
<pre><code>#inlucde "XTensor.h" // 引用XTensor定义的头文件
```
#inlucde "XTensor.h" // 引用XTensor定义的头文件
using namepsace nt; // 使用XTensor所在的命名空间nt
int main(int argc, const char ** argv)
{
// 构建一个单精度浮点类型张量,它是一个50列*100行的矩阵
XTensor * tensor = NewTensor2D(&tensor, 50, 100, X_FLOAT);
XTensor * tensor = NewTensor2D(50, 100, X_FLOAT);
// 之后可以使用张量tensor了
// 释放这个张量
DelTensor(tensor);
return 0;
}
</code></pre>
```
sample2.cpp中使用的NewTensor2D和DelTensor是一组函数,前者生成张量并返回指向这个张量的指针,后者释放指针所指向张量的内容。这种方法比较适合C语言风格的开发。
sample2.cpp中使用的NewTensor2D和DelTensor是一组函数,前者生成张量并返回指向这个张量的指针,后者释放指针所指向张量的内容。这种方法比较适合C/C++风格的开发。
> 注意,在NiuTrans.Tensor中所有张量默认都是“稠密”张量,也就是张量中所有的单元都会被分配空间,而且这些空间是连续的。有些情况下,张量里的单元仅有少数为非零单元,对于这类张量,可以使用“稀疏"的表示方法,这样可以有效的节省存储空间。
如果要定义稀疏张量,需要在原有的参数基础上额外指定一个参数 - 稠密度。所谓稠密度是指非零单元的比例,他是介于0和1之间的一个实数,0表示所有单元全为零,1表示全为非零单元。默认所有张量的稠密度都是1。下面是不同类型张量的定义方法示例(sample3.cpp)
<pre><code>#inlucde "XTensor.h" // 引用XTensor定义的头文件
```
#inlucde "XTensor.h" // 引用XTensor定义的头文件
using namepsace nt; // 使用XTensor所在的命名空间nt
......@@ -111,88 +129,1413 @@ int main(int argc, const char ** argv)
{
// 构建一个单精度浮点类型张量,它是一个50列*100行的矩阵
// 这个张量是稠密的
XTensor * tensor0 = NewTensor2D(&tensor, 50, 100, X_FLOAT);
XTensor * tensor0 = NewTensor2D(50, 100, X_FLOAT);
// 构建一个单精度浮点类型张量,它是一个50列*100行的矩阵
// 这个张量是稠密的
XTensor * tensor1 = NewTensor2D(&tensor, 50, 100, X_FLOAT, 1.0F);
XTensor * tensor1 = NewTensor2D(50, 100, X_FLOAT, 1.0F);
// 构建一个单精度浮点类型张量,它是一个50列*100行的矩阵
// 这个张量是稀疏的,有10%的单元非零
XTensor * tensor2 = NewTensor2D(&tensor, 50, 100, X_FLOAT, 0.1F);
XTensor * tensor2 = NewTensor2D(50, 100, X_FLOAT, 0.1F);
// 之后可以使用张量tensor0,tensor1和tensor2了
// 释放这些张量
DelTensor(tensor0);
DelTensor(tensor1);
DelTensor(tensor2);
return 0;
}
</code></pre>
```
以下是关于张量定义的基础函数:
功能 | 函数 | 参数
-: | - | -
初始化张量 | void InitTensor(<br>XTensor * tensor, const int myOrder, <br> const int * myDimSize, const float myDenseRatio, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
初始化稠密张量 | void InitTensor(<br>XTensor * tensor, const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | tensor - 指向被初始化张量的指针 <br> myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建空张量 | XTensor * NewTensor() | N/A
创建张量 | XTensor * NewTensor(<br>const int myOrder, <br> const int * myDimSize, const float myDenseRatio, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建稠密张量 | XTensor * NewTensor(<br>const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br>| myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
销毁张量 | void DelTensor(const XTensor * tensor) | tensor - 指向要被销毁的张量的指针
| 功能 | 函数| 参数 |
| - | - | - |
| 初始化张量 | void InitTensor(<br>XTensor * tensor, const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br> const float myDenseRatio, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 初始化稠密张量 | void InitTensor(<br>XTensor * tensor, const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | tensor - 指向被初始化张量的指针 <br> myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建空张量 | XTensor * NewTensor() | N/A |
| 创建张量 | XTensor * NewTensor(<br>const int myOrder, <br> const int * myDimSize, const float myDenseRatio, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建稠密张量 | XTensor * NewTensor(<br>const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 销毁张量 | void DelTensor(const XTensor * tensor) | tensor - 指向要被销毁的张量的指针 |
上述函数中需要说明的是
* 设备ID是指张量所申请的空间所在CPU或者GPU设备的编号,-1表示CPU
* XMem是NiuTrans.Tensor中定一个内存/显存池类,它负责内存(或显存)的统一管理。关于设备ID和XMem的进一步说明,请参见下一节内容。
* XMem是NiuTrans.Tensor中的一个内存/显存池类,它负责内存(或显存)的统一管理。关于设备ID和XMem的进一步说明,请参见下一节内容。
* TENSOR_DATA_TYPE定义了张量的数据类型,包括:
类型 | 说明
- | -
X_INT | 32bit整数
X_FLOAT | 32bit浮点
X_DOUBLE | 64bit浮点
X_INT8 | 8bit整数(计划支持)
X_FLOAT16 | 16bit浮点数(计划支持)
| 类型 | 说明 |
| - | - |
| X_INT | 32bit整数 |
| X_FLOAT | 32bit浮点数 |
| X_DOUBLE | 64bit浮点数 |
| X_INT8 | 8bit整数(计划支持)|
| X_FLOAT16 | 16bit浮点数(计划支持) |
此外,NiuTrans.Tensor也提供了更多种类的张量初始化和创建方法:
功能 | 函数 | 参数
-: | - | -
初始化为稠密向量 | void InitTensor1D(<br>XTensor * tensor, const int num, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | tensor - 指向被初始化张量的指针 <br> num - 向量维度大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
初始化为稠密矩阵 | void InitTensor2D(<br>XTensor * tensor, const int colNum, const int rowNum, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> colNum - 矩阵列数 <br> rowNum - 矩阵行数 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
初始化为3维稠密张量 | void InitTensor3D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
初始化为4维稠密张量 | void InitTensor4D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, const int d3, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
初始化为5维稠密张量 | void InitTensor5D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, <br> const int d3, const int d4, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> d4 - 张量第五维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建稠密向量 | XTensor * NewTensor1D(<br>const int num, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> | num - 向量维度大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建稠密矩阵 | XTensor * NewTensor2D(<br>const int colNum, const int rowNum, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | colNum - 矩阵列数 <br> rowNum - 矩阵行数 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建3维稠密张量 | XTensor * NewTensor3D(<br> const int d0, const int d1, const int d2, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建4维稠密张量 | XTensor * NewTensor4D(<br>const int d0, const int d1, const int d2, const int d3, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
创建5维稠密张量 | XTensor * NewTensor5D(<br>const int d0, const int d1, const int d2, <br> const int d3, const int d4, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> d4 - 张量第五维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池
其它问题??
* 程序编译,是直接引用源文件还是引用库
* 是否需要改变环境变量
* 命名空间是nt还是ntts,还是nts
## 设备及内存池
| 功能 | 函数 | 参数 |
| - | - | - |
| 初始化为稠密向量 | void InitTensor1D(<br>XTensor * tensor, const int num, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | tensor - 指向被初始化张量的指针 <br> num - 向量维度大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 初始化为稠密矩阵 | void InitTensor2D(<br>XTensor * tensor, const int colNum, const int rowNum, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> colNum - 矩阵列数 <br> rowNum - 矩阵行数 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 初始化为3维稠密张量 | void InitTensor3D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 初始化为4维稠密张量 | void InitTensor4D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, const int d3, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 初始化为5维稠密张量 | void InitTensor5D(<br>XTensor * tensor, <br> const int d0, const int d1, const int d2, <br> const int d3, const int d4, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | tensor - 指向被初始化张量的指针 <br> d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> d4 - 张量第五维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建稠密向量 | XTensor * NewTensor1D(<br>const int num, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> | num - 向量维度大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建稠密矩阵 | XTensor * NewTensor2D(<br>const int colNum, const int rowNum, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> | colNum - 矩阵列数 <br> rowNum - 矩阵行数 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建3维稠密张量 | XTensor * NewTensor3D(<br> const int d0, const int d1, const int d2, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建4维稠密张量 | XTensor * NewTensor4D(<br>const int d0, const int d1, const int d2, const int d3, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
| 创建5维稠密张量 | XTensor * NewTensor5D(<br>const int d0, const int d1, const int d2, <br> const int d3, const int d4, <br> const TENSOR_DATA_TYPE myDataType = X_FLOAT, <br>const int myDevID = -1, XMem * myMem = NULL) <br> <br> <br> <br> | d0 - 张量第一维大小 <br> d1 - 张量第二维大小 <br> d2 - 张量第三维大小 <br> d3 - 张量第四维大小 <br> d4 - 张量第五维大小 <br> myDataType - 张量的数据类型 <br> myDevID - 张量所在的设备ID <br> myMem - 张量所使用的内存池 |
## 设备
## 访问张量中的内容
在C/C++中,我们通过XTensor.h访问张量中的内容,并且仅需要在源程序中引用XTensor.h头文件就可以完成张量的定义。
在XTensor.h头文件中定义的成员变量说明:
| 成员变量 | 功能 |
| - | - |
| XMem * mem | 张量所使用的内存池 |
| void * data | 保存元素的数据数组 |
| void * dataHost | 主机内存上的数据副本,只在GPU上运行时被激活 |
| int devID | 设备ID,指张量所申请的空间所在CPU或者GPU设备的编号,-1表示CPU |
| int order | 张量的维度,例如:一个矩阵(维度为2)是一个二维张量 |
| int dimSize<br> [MAX_TENSOR_DIM_NUM] | 张量中每一维度的大小,索引0表示第1维 |
| int dimSizeRDI<br> [MAX_TENSOR_DIM_NUM] | 转置模式下张量中每一维度的大小,索引0表示第1维 |
| TENSOR_DATA_TYPE dataType | 每个数据单元的数据类型 |
| int unitSize | 数据单元的大小,类似于sizeof() |
| int unitNum | 数据单元的数量 |
| bool isSparse | 是否稠密,一个n * m稠密矩阵的数据量大小为n * m,而稀疏(非稠密)矩阵的数据量大小则取决于矩阵中非零元素个数。|
| int unitNumNonZero | 稀疏矩阵中非零元素个数 |
| float denseRatio | 稠密度,指非零单元的比例,是介于0和1之间的一个实数,0表示所有单元全为零,1表示全为非零单元。|
| bool isShared | 标志数据数组是否被其他张量所共享 |
| bool isInGlobalMem | 标志数据是否在全局内存而不是内存池中 |
| bool isAllValued<br> [MAX_TENSOR_DIM_NUM] | 标志稀疏矩阵中是否每个维度都具有非零元素 |
在XTensor.h头文件中定义的方法说明:
| 功能 | 函数 | 参数 |
| - | - | - |
| 判断两个张量数据类型<br>和大小是否相同 | static bool IsIdentical(<br> XTensor * a, XTensor * b) | a - 进行比较的第一个张量 <br> b - 进行比较的第二个张量 |
| 判断三个张量数据类型<br>和大小是否相同 | static bool IsIdentical(<br> XTensor * a, XTensor * b, XTensor * c) | a - 进行比较的第一个张量 <br> b - 进行比较的第二个张量 <br> c - 进行比较的第三个张量 |
| 设置张量每一维度的大小 | void SetDim(int * myDimSize) |myDimSize - 张量每一维度的大小 |
| 得到张量中给定的维度大小 | int GetDim(const int dim) | dim - 张量的维度 |
| 重新调整矩阵维度 | void Reshape(<br> const int order, const int * myDimSize) | order - 张量的维度 <br> myDimSize - 张量每一维的大小 |
| 得到张量中元素数量 | int GetSize() | N/A |
| 得到内存使用大小 | int GetDataSizeInChar() | N/A |
| 得到所给数据类型的数据<br> 单元大小 | int GetUnitSize(<br> TENSOR_DATA_TYPE myDataType) | myDataType - 所给数据类型 |
| 张量中所有元素设置为0 | void SetZeroAll(XStream * stream = NULL) | stream - 多线程流|
| 用数组赋值张量 | void SetData(<br> const void * d, int num, int beg = 0) | d - 赋值数组 <br> num - 数组大小 <br> beg - 赋值时从张量的第几位开始 |
| 设置张量服从均匀分布 | void SetDataRand(<br> DTYPE lower, DTYPE upper) | lower - 最小值 <br> upper - 最大值 |
| 设置张量服从正态分布 | void SetDataRandn(<br> DTYPE mean, DTYPE standardDeviation) | mean - 均值 <br> standardDeviation - 标准差 |
| 检查张量中元素是否相同 | bool CheckData(<br> const void * answer, int num, int beg = 0) | answer - 给定数组 <br> num - 数组大小 <br> beg - 赋值时从张量的第几位开始 |
| 将给定维度中元素<br> 设置为升序 | void SetAscendingOrder(int dim) | dim - 给定维度 |
| 获取张量中元素指针 | void * GetCell(int * index, int size) | index - 元素位置 <br> size-矩阵大小 |
| 获取二维张量中元素指针 | void * GetCell2D(int ni, int mi = 0) | ni - 行值 <br> mi - 列值 |
| 获取二维张量的值 | DTYPE Get2D(int ni, int mi = 0) | ni - 行值 <br> mi - 列值 |
| 获取稀疏张量的值 | DTYPE GetInSparse(int i) | i - 稀疏矩阵中非0元素位置 |
| 获取稀疏张量中<br> 元组的键值 | int GetKeyInSparse(int i) | i - 稀疏矩阵中非0元素位置 |
| 设置二维张量中<br> 的单元值 | bool Set2D(DTYPE value, int ni, int mi = 0) | value - 单元值 <br> ni - 行值 <br> mi - 列值 |
| 增加二维张量中<br> 的单元值 | bool Add2D(DTYPE value, int ni, int mi = 0) | value - 单元值 <br> ni - 行值 <br> mi - 列值 |
| 获取稀疏矩阵中<br> 非零元素数量 | int GetNonzeroSize() | N/A |
| 将矩阵重置为特定大小 | bool Resize(<br> const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = DEFAULT_DTYPE, <br> const float myDenseRatio = 1.0F) | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 |
| 将矩阵重置为特定大小<br>并不申请新空间 | bool ResizeWithNoData(<br> const int myOrder, <br> const int * myDimSize, <br> const TENSOR_DATA_TYPE myDataType = DEFAULT_DTYPE, <br> const float myDenseRatio = 1.0F) | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myDataType - 张量的数据类型 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 |
| 将矩阵重置为<br> 另一矩阵大小 | bool Resize(<br> const XTensor * myTensor) | myTensor - 重置矩阵大小的参考矩阵 |
| 用二值搜索方法<br> 找到稀疏矩阵中元素 | bool BinarySearch(<br> int key, DTYPE &value, void * &position) | key - 稀疏矩阵中元素位置 <br> value - 元素值 <br> position - 元素坐标位置 |
| 将数据刷新到<br> 目标设备中 | void FlushToMem(XMem * targetMem) | targetMem - 目标设备 |
| 在全局内存中<br> 申请矩阵的内存空间 | static void AllocateData(<br> XTensor * matrix, <br> XMem * myMem = NULL, <br> bool useBuf = false) | matrix - 申请内存空间的矩阵 <br> myMem - 是否在内存池中申请空间 <br> useBuf - 是否使用缓冲区 |
| 在全局内存中<br> 释放矩阵的内存空间 | static void FreeData(<br> XTensor * matrix, <br> XMem * myMem = NULL, <br> bool useBuf = false) | matrix - 申请内存空间的矩阵 <br> myMem - 是否在内存池中申请空间 <br> useBuf - 是否使用缓冲区 |
| 在缓冲区创建张量 | XTensor * NewTensorBuf( <br> const int myOrder, <br> const int * myDimSize, XMem * myMem, <br> const TENSOR_DATA_TYPE myDataType = <br> X_FLOAT, const float myDenseRatio = 1.0F) | myOrder - 张量的维度 <br> myDimSize - 张量每一维的大小,索引0表示第一维 <br> myMem - 张量所使用的内存池 <br> myDataType - 张量的数据类型 <br> myDenseRatio - 张量的稠密度,1表示稠密张量 |
| 依据给定张量<br>复制一个新的张量 | XTensor * NewTensor(<br>XTensor * a, bool isFilledData = true) | a - 给定张量 <br> isFilledData - 是否申请张量中的数据空间 |
| 依据给定张量<br>释放数据空间 | void DelTensor(<br>const XTensor * tensor) | tensor - 给定张量 |
| 依据给定张量<br>在缓存中释放数据空间 | void DelTensorBuf(<br>const XTensor * tensor) | tensor - 给定张量 |
## 张量计算
### 加法(Sum)
NiuTrans.Tensor提供关于张量计算的函数功能,主要包括一些基本的张量运算以及激活函数,在本节中,主要对这些函数及其用法用例进行介绍。
### arithmetic
此部分主要包括各种数学运算,加、减、乘、除、取负等。
#### 矩阵乘法(MatrixMul)
##### 什么是张量间矩阵乘法?
利用矩阵乘法可以将矩阵想乘并得到一个新的结果矩阵,两个维度分别为\\(2 \times 3\\)和\\(3 \times 2\\)的矩阵相乘过程如下所示,结果矩阵的维度为\\(2 \times 2\\):
$$
\left(\begin{matrix}1.0 & 2.0 & 3.0\\\\-4.0 & 5.0 & 6.0\end{matrix}\right) ×
\left(\begin{matrix}0.0 & -1.0\\\\1.0 & 2.0\\\\2.0 & 1.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}8.0 & 6.0\\\\17.0 & 20.0\end{matrix}\right)
$$
##### 矩阵乘法的调用
NiuTrans.Tensor提供了矩阵乘法的计算操作,在NiuTrans.Tensor/Tensor/core中定义,矩阵乘法的调用方式以及参数说明如下所示:
```
void _MatrixMul(XTensor * a, MATRIX_TRANS_TYPE transposedA, XTensor * b, MATRIX_TRANS_TYPE transposedB, XTensor * c, DTYPE alpha = (DTYPE)1.0, DTYPE beta = 0)
```
Parameters:
* a - 操作张量1
* transposedA - 操作张量1是否进行转置
* b - 操作张量2
* transposedB - 操作张量2是否进行转置
* c - 操作张量3
* alpha - 系数
* beta - 系数
##### 矩阵乘法片段示例
我们以最基本的二维矩阵乘法为例,用MatrixMul进行矩阵乘法操作的示例代码为:
```
/* call MatrixMul function */
_MatrixMul(s1, X_NOTRANS, s2, X_NOTRANS, t);
```
有关矩阵乘法的详细代码示例:
NiuTrans.Tensor/Tensor/test/TMatrixMul.cpp
#### 点乘(Multiply)
##### 什么是张量点乘?
利用张量间的点乘操作可以进行张量间元素的按位置依次相乘,两个维度分别为\\(2 \times 2\\)的张量点乘过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0\\\\2.0 & 3.0\end{matrix}\right) ·
\left(\begin{matrix}0.0 & 1.0\\\\2.0 & 3.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.0 & 1.0\\\\4.0 & 9.0\end{matrix}\right)
$$
##### 张量点乘的调用
NiuTrans.Tensor提供了张量点乘的计算操作,用来计算张量中元素点乘结果,该函数在NiuTrans.Tensor/Tensor/core中定义,张量点乘的调用方式以及参数说明如下所示:
```
_Multiply(XTensor * a, XTensor * b, XTensor * c, int leadingDim, DTYPE alpha = 0)
```
Parameters:
* a - 操作张量1
* b - 操作张量2
* c - 结果张量
* leadingDim - ???
* alpha - 系数
##### 张量点乘片段示例
用Multiply进行s1和s2张量间的点乘操作的调用示例如下所示,计算结果存入t中:
```
/* call multiply function */
_Multiply(s1, s2, t, 0);
```
有关矩阵乘法的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TMultiply.cpp
#### 取负(Negate)
##### 什么是张量的取负操作?
在进行张量的取负操作时,张量中每一元素都进行取负得到新的元素,所有新元素的组合得到新的结果张量,一个维度为\\(3 \times 2\\)的张量取负操作过程如下所示:
$$
\left(\begin{matrix}1.0 & -2.0\\\\-3.0 & 4.0\\\\5.0 & -6.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}-1.0 & 2.0\\\\3.0 & -4.0\\\\-5.0 & 6.0\end{matrix}\right)
$$
##### 张量取负的调用
NiuTrans.Tensor提供了张量取负的计算操作,进行张量的按元素位置进行取负操作,该函数在NiuTrans.Tensor/Tensor/core中定义,张量取负的调用方式以及参数说明如下所示:
```
_Negate(XTensor * a)
```
Parameters:
* a - 操作张量
##### 张量取负片段示例
用Negate进行张量取负操作的调用示例如下所示,其中a为我们要进行处理的张量:
```
/* call negate function */
_Negate(a);
```
有关张量取负的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TNegate.cpp
#### 加法(Sum)
##### 什么是张量加法?
张量加法的目的是将n个张量相加得到一个新的结果张量,结果张量某一位置的元素数值为进行操作的张量在该位置上元素的求和,在张量加法的计算过程中进行操作的张量与结果张量的维度相同,两个维度为\\(2\times 3\\)的张量相加过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 \\\\ 3.0 & 4.0 & 5.0\end{matrix}\right) +
\left(\begin{matrix}0.5 & 1.5 & 2.5 \\\\ 3.5 & 4.5 & 5.5\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.5 & 2.5 & 4.5 \\\\ 6.5 & 8.5 & 10.5\end{matrix}\right)
$$
##### 张量加法的调用
NiuTrans.Tensor提供了张量加法的计算操作,在NiuTrans.Tensor/Tensor/core中定义,该操作用来进行张量之间的按元素位置相加,并得到相加的结果张量,张量加法的调用方法为:
```
_Sum(XTensor * a, XTensor * b, XTensor * c, DTYPE beta)
```
其中a和b为输入张量,c为结果张量,若c为NULL则将相加结果存入a中,beta为一个缩放参数,缩放公式为:c = a + b * beta,beta默认为1.0,NiuTrans.Tensor中张量加法的调用方式以及参数说明如下所示:
Parameters:
* a - 操作张量1
* b - 操作张量2
* c - 结果张量,如果c为空则将结果存入a
* beta - 缩放参数
##### 张量加法片段示例
调用Sum进行张量间的求和操作如下所示,在此例中直接将张量相加结果存入a中:
```
/* call sum function */
_Sum(a, b);
```
详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSum.cpp
#### SumByColumnTV
##### 什么是SumByColumnTV?
SumByColumnTV的作用是将一个Tensor和一个Vector按列相加,所得结果维度与Tensor一致,一个\\(2 \times 4\\)的Tensor和一个\\(2 \times 1\\)的Vector的SumByColumnTV操作过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) + \left(\begin{matrix}1.0\\\\0.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}1.0 & 2.0 & 3.0 & 4.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right)
$$
##### SumByColumnTV的调用
NiuTrans.Tensor提供了张量的SumByColumnTV操作,调用方法及参数说明如下所示:
```
_SumByColumnTV(XTensor * a, XTensor * b, XTensor * c, DTYPE beta)
```
Parameters:
* a - 操作张量
* b - 操作向量
* c - 结果张量
* beta - 缩放参数
调用SumByColumnTV进行的运算为c_col = a_col + b * \beta
##### SumByColumnTV片段示例
SumByColumnTV示例代码如下,其中a为输入的张量,b为输入的向量,c为a和b按列相加所得结果:
```
/* call SumByColumnTV function */
_SumByColumnTV(a, b, c);
```
有关张量SumByColumnTV的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSumByColumnTV.cpp
#### SumByColumnVT
##### 什么是SumByColumnVT?
SumByColumnVT的作用是将一个Vector和一个Tensor按列相加,所得结果维度与Vector一致,一个\\(2 \times 1\\)的Vector和一个\\(2 \times 4\\)的Tensor的SumByColumnVT操作过程如下所示:
$$
\left(\begin{matrix}1.0\\\\0.0\end{matrix}\right) + \left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}7.0\\\\22.0\end{matrix}\right)
$$
##### SumByColumnVT调用
NiuTrans.Tensor提供了张量的SumByColumnVT操作,调用方法及参数说明如下所示:
```
_SumByColumnVT(XTensor * a, XTensor * b, XTensor * c, DTYPE beta)
```
Parameters:
* a - 操作向量
* b - 操作张量
* c - 结果向量
* beta - 缩放参数
调用SumByColumnVT进行的运算为c = a + \sum{col} b_col * \beta
##### SumByColumnVT片段示例
SumByColumnVT示例代码如下,其中a为输入的向量,b为输入的张量,c为a和b按列相加所得结果:
```
/* call SumByColumnVT function */
_SumByColumnVT(a, b, c);
```
有关张量SumByColumnVT的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSumByColumnVT.cpp
### getandset
此部分包括各种数据类型转化,设置数据、取数据等操作。
#### 选择(Select)
##### 什么是张量的选择操作?
Select时按张量指定维度上的指定位置对张量进行选择的操作,一个\\(2 \times 2 \times 4\\)的张量选择过程如下所示,本例中是选择张量维度2上位置索引为1和2的元素并存入目标张量,得到一个维度为\\(2 \times 2 \times 2\\)的张量:
$$
\begin{aligned}
\Biggl(
& \left(
\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right),\\\\
& \left(
\begin{matrix}1.0 & 2.0 & 3.0 & 4.0\\\\5.0 & 6.0 & 7.0 & 8.0\end{matrix}
\right)
\Biggr)
\end{aligned} \rightarrow
\begin{aligned}
\Biggl(
& \left(
\begin{matrix}1.0 & 2.0\\\\5.0 & 6.0\end{matrix}
\right),\\\\
& \left(
\begin{matrix}2.0 & 3.0\\\\6.0 & 7.0\end{matrix}
\right)
\Biggr)
\end{aligned}
$$
##### 张量选择的调用
NiuTrans.Tensor提供了张量的选择操作,调用方法及参数说明如下所示:
```
_SelectRange(XTensor * a, int dim, int low, int high, XTensor * c)
```
Parameters:
* a - 输入张量
* dim - 在哪一维对张量进行张量选择操作
* low - 张量选择范围的下限
* high - 张量选择范围的上限
* c - 结果张量
>需要注意的是,当张量选择的取值范围为[1,3]时意味着选择的是索引位置为1和2的值
##### 张量选择片段示例
张量选择示例代码如下,其中s为输入的待操作张量,t输出结果张量,在第三维上按范围[1,3]进行张量的选择操作:
```
/* call SelectRange function */
_SelectRange(s, 2, 1, 3, t);
```
有关张量选择的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSelect.cpp
#### SetData
##### 什么是SetData?
SetData的作用是将张量在一定取值范围内随机进行初始化设置,一个\\(2 \times 4\\)的张量在[0.0,1.0]的取值范围SetData过程如下所示:
$$
\left(\begin{matrix}0.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 0.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.1 & 0.5 & 0.3 & 0.9\\\\0.8 & 0.5 & 0.5 & 0.2\end{matrix}\right)
$$
##### SetData调用
NiuTrans.Tensor提供了张量的SetData操作,调用方法及参数说明如下所示:
```
SetDataRand(DTYPE lower, DTYPE upper)
```
Parameters:
* lower - 取值下限
* upper - 取值上限
##### SetData片段示例
SetData示例代码如下,本例中是在[0.0,1.0]取值范围内对张量s进行随机初始化:
```
/* call SetData function */
s->SetDataRand(0.0, 1.0);
```
有关张量SetData的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSetData.cpp
### math
此部分包括各种非基本代数操作,包括:log、exp、abs等。
#### 标准化(Normalize)
##### 什么是张量的标准化?
神经网络需要标准化处理(Normalize),这样做是为了弱化某些变量的值较大而对模型产生影响,Normalize函数定义为:
>y = a * (x-mean)/sqrt(variance+\epsilon) + b
##### Normalize调用
NiuTrans.Tensor提供了张量的Normalize操作,调用方法及参数说明如下所示:
```
_Normalize(XTensor * input, XTensor * output, int dim, XTensor * mean, XTensor * var, XTensor * a, XTensor * b, DTYPE epsilon)
```
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度产生均值和方差
* mean - 均值
* var - 方差
* a - 缩放
* b - 偏置
* epsilon - 防止方差为0的参数
##### Normalize片段示例
Normalize示例代码如下所示:
```
/* call normalize function */
_Normalize(s, t, 0, mean, var, a, b, 0.0);
```
有关Normalize的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TNormalize.cpp
#### 幂运算(Power)
##### 什么是张量的幂运算操作?
幂运算是一种关于幂的数学运算,张量的幂运算是将张量中的每个元素都进行幂运算从而得到新的张量,一个维度为\\(3 \times 2\\)的幂为2.0的张量幂运算过程如下所示:
$$
\left(\begin{matrix}1.0 & 2.0\\\\3.0 & 4.0\\\\5.0 & 6.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}1.0 & 4.0\\\\9.0 & 16.0\\\\25.0 & 36.0\end{matrix}\right)
$$
##### 张量幂运算的调用
NiuTrans.Tensor提供了张量幂运算的操作,用来进行张量的按元素位置进行幂运算的操作,调用方法为:
```
_Power(XTensor * a, DTYPE p)
```
其中a为进行操作的张量,p为次方数,张量幂运算的参数说明如下所示:
Parameters:
* a - 操作张量
* p - 次方数
##### 张量幂运算片段示例
下面是调用Power进行a的幂为2.0的幂运算操作的一段示例代码:
```
/* call power function */
_Power(a, 2.0);
```
有关张量幂运算的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TPower.cpp
#### 缩放和偏移(Scale and Shift)
##### 什么是张量的缩放和偏移?
张量的缩放和偏移计算公式为:p = p * scale + shift,其中scale和shift分别为张量缩放和偏移的参数,一个\\(2 \times 4\\)的张量进行缩放和偏移的过程如下所示,缩放参数取2.0,偏移参数取0.5:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.5 & 2.5 & 4.5 & 6.5\\\\8.5 & 10.5 & 12.5 & 14.5\end{matrix}\right)
$$
##### 张量缩放和偏移的调用
NiuTrans.Tensor提供了张量的缩放和偏移操作,调用方法为:
```
_ScaleAndShift(XTensor * a, DTYPE scale, DTYPE shift)
```
张量的缩放和偏移操作结果为:p = p * scale + shift,其中scale和shift分别为张量的缩放和偏移参数,张量缩放和偏移操作的参数说明如下表所示:
Parameters:
* a - 输入张量
* scale - 缩放参数
* shift - 偏移参数
##### 张量缩放和偏移片段示例
张量缩放和偏移示例代码如下,input为输入的待操作张量,scaleFactor为缩放参数,shiftFactor为偏移参数:
```
/* call ScaleAndShift function */
_ScaleAndShift(input, scaleFactor, shiftFactor);
```
有关张量缩放和偏移的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TScaleAndShift.cpp
### movement
此部分主要是介绍有关数据拷贝函数。
#### 拷贝(CopyValues)
##### 什么是张量的拷贝操作?
拷贝,即将一个张量的值赋给另一个张量,也就是对张量进行拷贝操作,一个\\(2 \times 4\\)的张量拷贝过程如下所示:
$$
\left(\begin{matrix}5.0 & 1.0 & 2.0 & 8.0\\\\4.0 & 3.0 & 7.0 & 6.0\end{matrix}\right) \rightarrow
\left(
\begin{matrix}5.0 & 1.0 & 2.0 & 8.0\\\\4.0 & 3.0 & 7.0 & 6.0\end{matrix}\right)
$$
##### 张量拷贝操作的调用
NiuTrans.Tensor提供了张量的拷贝操作,调用方法及参数说明如下所示:
```
_CopyValues(XTensor * s, XTensor * t, XStream * stream)
```
Parameters:
* s - 输入张量
* t - 输出结果张量
* stream - 多线程流
##### 张量拷贝片段示例
张量拷贝示例代码如下,其中input为输入的待操作张量,output输出结果张量:
```
/* call CopyValues function */
_CopyValues(input, output);
```
有关张量拷贝的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TCopyValues.cpp
#### CopyIndexed
##### 什么是张量的CopyIndexed操作?
CopyIndexed,即按指定索引位置拷贝张量,一个\\(2 \times 2 \times 3\\)的张量拷贝过程如下所示,本例中是对张量维度2上起始位置索引为0和2的1个元素进行拷贝,所得张量维度为\\(2 \times 2 \times 2\\):
$$
\begin{aligned}
\Biggl(
& \left(
\begin{matrix}0.0 & -1.0 & 2.0\\\\2.0 & 1.0 & 3.0\end{matrix}\right),\\\\
& \left(
\begin{matrix}1.0 & 2.0 & 4.0\\\\3.0 & 1.0 & 2.0\end{matrix}
\right),\\\\
& \left(
\begin{matrix}-1.0 & 3.0 & 2.0\\\\1.0 & -1.0 & 0.0\end{matrix}
\right)
\Biggr)
\end{aligned} \rightarrow
\begin{aligned}
\Biggl(
& \left(
\begin{matrix}0.0 & 2.0\\\\2.0 & 3.0\end{matrix}\right),\\\\
& \left(
\begin{matrix}1.0 & 4.0\\\\3.0 & 2.0\end{matrix}
\right),\\\\
& \left(
\begin{matrix}-1.0 & 2.0\\\\1.0 & 0.0\end{matrix}
\right)
\Biggr)
\end{aligned}
$$
##### 张量CopyIndexed的调用
NiuTrans.Tensor提供了张量的CopyIndexed操作,调用方法及参数说明如下所示:
```
_CopyIndexed(XTensor * s, XTensor * t, int dim, int * srcIndex, int indexSize, int * tgtIndex, int copyNum)
```
Parameters:
* s - 输入张量
* t - 输出结果张量
* dim - 在哪一维对张量进行CopyIndexed操作
* srcIndex - 源索引,即在指定dim上进行赋值的值的索引
* indexSize - 源索引的个数
* tgtIndex - 目标索引,所赋值的值在输出张量中的索引
* copyNum - 以源索引为起始位置拷贝的元素个数
##### 张量CopyIndexed片段示例
CopyIndexed示例代码如下,其中s为输入的待操作张量,t输出结果张量,在第三维上按起始位置索引拷贝一个元素到目标张量:
```
/* call CopyIndexed function */
_CopyIndexed(s, t, 2, srcIndex, indexSize, tgtIndex, 1);
```
有关CopyIndexed的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TCopyIndexed.cpp
### reduce
#### 归约取最大值(ReduceMax)
##### 什么是张量的归约取最大值?
张量的归约取最大值操作是沿着张量的某一维度,取得该向量在该维度中的最大值,一个\\(2 \times 4\\)的张量在维度0和维度1进行取最大值操作的过程分别如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right)
$$
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}3.0\\\\7.0\end{matrix}\right)
$$
##### 张量归约取最大值操作的调用
NiuTrans.Tensor提供了张量的ReduceMax操作,用来获得张量中沿指定维度取得的最大值,张量归约取最大值操作的调用方式及参数说明如下所示:
```
_ReduceMax(XTensor * input, XTensor * output, int dim)
```
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度进行取最大值操作
##### 张量归约取最大值片段示例
调用ReduceMax进行张量归约取最大值操作的示例代码如下所示,代码中两行分别表示沿着维度0和维度1进行取值:
```
/* call reduce max function */
_ReduceMax(a, reduce_a, 0);
_ReduceMax(b, reduce_b, 1);
```
有关张量归约取最大值的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TReduceMax.cpp
#### 归约求和(ReduceSum)
##### 什么是张量的归约求和操作?
张量的归约求和操作是沿着张量的某一维度,计算该张量在该维度的和,一个\\(2 \times 4\\)的张量在维度0和维度1进行求和操作的过程分别如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}4.0 & 6.0 & 8.0 & 10.0\end{matrix}\right)
$$
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}6.0\\\\22.0\end{matrix}\right)
$$
##### 张量归约求和操作的调用
NiuTrans.Tensor提供了张量的ReduceSum操作,调用方法为:
```
_ReduceSum(XTensor * input, XTensor * output, int dim, XTensor * shift, DTYPE power, bool isExp)
```
其中shift默认为NULL,power默认为1.0F,isExp默认为false,张量归约求和操作的参数说明如下所示:
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度进行取最大值操作
* shift - 输入的偏移,默认为NULL
* power - 元素的幂,默认为1.0F
* isExp - 是否取指,默认为false
### 缩放和偏移(Scale and Shift)
##### 张量归约求和片段示例
调用ReduceSum进行张量归约求和操作的示例代码如下所示,代码中两行分别表示沿着维度0和维度1进行取值:
```
/* call reduce sum function */
_ReduceSum(a, reduce_a, 0);
_ReduceSum(b, reduce_b, 1);
```
有关张量归约求和的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TReduceSum.cpp
#### 归约取均值(ReduceMean)
##### 什么是张量的归约取均值操作?
张量的归约取均值操作是沿着张量的某一维度,计算该张量在该维度的均值,一个\\(2 \times 4\\)的张量在维度0和维度1进行取均值操作的过程分别如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}2.0 & 3.0 & 4.0 & 5.0\end{matrix}\right)
$$
$$
\left(\begin{matrix}1.0 & 1.0 & 3.0 & 3.0\\\\4.0 & 4.0 & 6.0 & 6.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}2.0\\\\5.0\end{matrix}\right)
$$
##### 张量归约取均值操作的调用
NiuTrans.Tensor提供了张量的ReduceMean操作,调用方法为:
```
_ReduceMean(XTensor * input, XTensor * output, int dim)
```
ReduceMean用来获得张量中沿指定维度取得的数值均值,张量归约取均值的参数说明如下所示:
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度进行取平均值操作
##### 张量归约取均值片段示例
调用ReduceMean进行张量归约取均值操作的示例代码如下所示,代码中两行分别表示沿着维度0和维度1进行取值:
```
/* call reduce mean function */
_ReduceMean(a, reduce_a, 0);
_ReduceMean(b, reduce_b, 1);
```
有关张量归约取均值的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TReduceMean.cpp
#### 归约取方差(ReduceSumSquared)
##### 什么是张量的归约取方差操作?
张量的归约取方差操作是沿着张量的某一维度,计算该张量在该维度的方差,一个\\(2 \times 4\\)的张量在维度0进行取方差操作的过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}8.0 & 8.0 & 8.0 & 8.0\end{matrix}\right)
$$
##### 张量归约取方差操作的调用
NiuTrans.Tensor提供了张量的ReduceSumSquared操作,调用方法为:
```
_ReduceSumSquared(XTensor * input, XTensor * output, int dim, XTensor * shift)
```
ReduceSumSquared用来计算张量的沿着某一维度元素的方差,张量归约取方差操作的参数说明如下所示:
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度进行取平均值操作
* shift - 输入的偏移
##### 张量归约取方差片段示例
调用ReduceSumSquared进行张量归约取方差操作的示例代码如下所示:
```
/* call reduce sum squared function */
_ReduceSumSquared(input, output, 0, shift);
```
有关张量归约取方差的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TReduceSumSquared.cpp
#### 归约取标准差(ReduceVariance)
##### 什么是张量的归约取标准差操作?
张量的归约取标准差操作是沿着张量的某一维度,计算该张量在该维度的标准差,一个\\(2 \times 4\\)的张量在维度0进行取标准差操作的过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}4.0 & 4.0 & 4.0 & 4.0\end{matrix}\right)
$$
##### 张量归约取标准差操作的调用
NiuTrans.Tensor提供了张量的ReduceVariance操作,调用方法为:
```
_ReduceVariance(XTensor * input, XTensor * output, int dim, XTensor * mean)
```
ReduceVariance用来计算张量的沿着某一维度元素的标准差,张量归约取标准差操作的参数说明如下所示:
Parameters:
* input - 输入张量
* output - 输出张量
* dim - 沿着指定维度进行取标准差操作
* mean - 均值
##### 张量归约取标准差片段示例
调用ReduceVariance进行张量归约取标准差操作的示例代码如下所示:
```
/* call reduce variance function */
_ReduceVariance(input, output, 0, mean);
```
有关张量归约取标准差的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TReduceVariance.cpp
### shape
此部分主要包括关于形状改变的函数,比如:split、merge、reshape等。
#### 级联(Concatenate)
##### 什么是张量的级联操作?
张量间的级联操作是沿着张量的某一维度,将一系列张量或是一个列表中的所有张量连接在一起组成一个更大的张量,将维度分别为\\(2 \times 1\\)和\\(2 \times 2\\)的两个张量进行级联过程如下所示:
$$
\left(\begin{matrix}0.0\\\\1.0\end{matrix}\right) +
\left(\begin{matrix}2.0 & 3.0\\\\4.0 & 5.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.0 & 2.0 & 3.0\\\\1.0 & 4.0 & 5.0\end{matrix}\right)
$$
##### 张量级联的调用
NiuTrans.Tensor提供了张量间的级联操作,调用方法为:
```
_Concatenate(XList * smalls, XTensor * big, int dim)
_Concatenate(XTensor * smallA, XTensor * smallB, XTensor * big, int dim)
```
第一种调用方法中的操作对象是列表,将进行级联操作的张量存入列表smalls中,级联结果存入张量big中:
Parameters:
* smalls - 进行级联张量的列表
* big - 结果张量
* dim - 在指定维度进行级联
第二种方法操作对象不再是列表中的张量而是直接对一系列张量进行级联操作:
Parameters:
* smallA - 操作张量1
* smallB - 操作张量2
* big - 结果张量
* dim - 进行级联的维度
##### 张量级联片段示例
通过操作张量列表进行张量的级联操作片段示例如下所示,sList为存放进行级联张量的列表,t为结果张量:
```
/* call concatenate function */
_Concatenate(&sList, t, 1);
```
直接通过操作一系列张量进行张量的级联操作片段示例如下所示,s1、s2为需要进行级联的张量,t为结果张量:
```
/* call concatenate function */
_Concatenate(s1, s2, t, 1);
```
有关张量级联的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TConcatenate.cpp
#### 切分(Split)
##### 什么是张量的切分操作?
张量间的切分操作是沿着张量的某一维度,可以将一个张量切分成另一张量,也可以将一个大的张量切分成n个小的张量集合的列表。
第一种情况下将维度为\\(4 \times 3\\)张量沿着维度0进行切分,切分份数为2,得到维度为\\(2 \times 2 \times 3\\)的张量的过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\\\\0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right) \rightarrow
\begin{aligned}
\Biggl( & \left(
\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\end{matrix}\right),
\\\\ & \left(
\begin{matrix}0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}
\right) \Biggr)
\end{aligned}
$$
在第二种情况下将维度为\\(4 \times 3\\)张量沿着维度0进行切分,切分份数为2,得到两个维度均为\\(2 \times 3\\)的张量的过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\\\\0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.0 & 2.0 & 3.0\\\\1.0 & 4.0 & 5.0\end{matrix}\right) + \left(\begin{matrix}0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right)
$$
##### 张量切分的调用
NiuTrans.Tensor提供了两种张量切分操作,调用方法为:
```
_Split(XTensor * s, XTensor * t, int whereToSplit, int splitNum)
_Split(XTensor * big, XList * smalls, int whereToSplit, int splitNum)
```
在第一种调用方法中是将源张量中的某一维度进行Split操作,Split结果为张量t,whereToSplit为在哪一维度进行split操作,splitNum表示分成多少份,例如:(N, M) -> (N/3, M, 3),参数说明如下所示:
Parameters:
* s - 操作张量
* t - 结果张量
* whereToSplit - 在指定维度进行split操作
* splitNum - 分成多少份
在第二种调用方法中是将所操作张量big按某一维度whereToSplit进行Split操作,操作结果为包含若干更小维度张量的列表smalls,splitNum表示分成多少份,例如:(N, M) -> 2 * (N/2, M),参数说明如下所示:
Parameters:
* big - 操作张量
* smalls - 存放切分出张量的列表
* whereToSplit - 在指定维度进行split操作
* splitNum - 分成多少份
##### 张量切分片段示例
上述第一种张量切分片段示例如下所示,s为进行切分的张量,t为结果张量,0表示沿着维度0进行切分操作,2表示切分份数为2:
```
/* call split function */
_Split(s, t, 0, 2);
```
上述第二种张量切分片段示例如下所示,s为进行切分的张量,tList为存放结果张量的列表,1表示沿着维度1进行切分操作,2表示切分份数为2:
```
/* call split function */
_Split(s, &tList, 1, 2);
```
有关张量切分的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSplit.cpp
#### 合并(Merge)
##### 什么是张量的合并操作?
张量间的合并操作与级联有些类似,是沿着张量的某一维度,可以将一个张量合并为另一个维度不同的张量,也可以将一个列表中的所有张量合并在一起组成一个更大的张量。
在第一种情况下将维度为\\(2 \times 2 \times 3\\)的张量在维度1进行合并,进行合并的维度为0,得到维度为\\(4 \times 3\\)的张量的过程如下所示:
$$
\begin{aligned}
\Biggl( & \left(
\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\end{matrix}\right),
\\\\ & \left(
\begin{matrix}0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}
\right) \Biggr)
\end{aligned} \rightarrow
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\\\\0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right)
$$
在第二种情况下将两个维度均为\\(2 \times 3\\)的张量沿着维度0合并为维度为\\(4 \times 3\\)的张量的过程如下所示:
$$
\left(\begin{matrix}0.0 & 2.0 & 3.0\\\\1.0 & 4.0 & 5.0\end{matrix}\right) + \left(\begin{matrix}0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right) \rightarrow
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\\\\0.1 & 1.1 & 2.1\\\\3.1 & 4.1 & 5.1\end{matrix}\right)
$$
##### 张量合并操作的调用
NiuTrans.Tensor提供了张量的合并操作,调用方法为:
```
_Merge(XTensor * s, XTensor * t, int whereToMerge, int leadingDim)
_Merge(XList * smalls, XTensor * big, int whereToMerge)
```
在第一种调用方法中是将源张量中的某一维度进行Merge操作,Merge结果为张量t,whereToMerge为指定进行Merge操作的维度,leadingDim为指定将哪一维度Merge,例如:(N/2, 2, M) -> (N, M),参数说明如下表所示:
Parameters:
* s - 操作张量
* t - 结果张量
* whereToMerge - 沿着指定维度进行Merge操作
* leadingDim - 把指定维度进行Merge操作
在第二种调用方法中是将所操作张量存入列表smalls中,操作结果为张量big,whereToMerge为指定进行Merge操作的维度,例如:2 * (N/2, M) -> (N, M),参数说明如下表所示:
Parameters:
* smalls - 存放进行合并张量的列表
* big - 结果张量
* whereToMerge - 沿着指定维度进行Merge操作
##### 张量合并片段示例
上述第一种张量合并片段示例如下所示,s为进行合并的张量,t为结果张量,1表示在维度1进行合并操作,0表示将维度0进行合并操作:
```
/* call merge function */
_Merge(s, t, 1, 0);
```
上述第二种张量合并片段示例如下所示,sList为要进行合并的张量列表,t为结果张量,0表示沿着维度0进行合并操作:
```
/* call merge function */
_Merge(&sList, t, 0);
```
有关张量合并的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TMerge.cpp
#### Unsqueeze
##### 什么是Unsqueeze?
Unsqueeze的作用是通过对张量进行操作,返回一个新的在指定维度插入新维度的张量,这个返回的张量与源张量共享相同的基础数据,一个\\(2 \times 3\\)的张量在维度1和2分别进行Unsqueeze的操作如下所示,插入新的维度大小均为2:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\end{matrix}\right) \rightarrow
\begin{aligned}
\Biggl( & \left(
\begin{matrix}0.0 & 1.0 & 2.0\\\\0.0 & 1.0 & 2.0\end{matrix}\right),
\\\\ & \left(
\begin{matrix}3.0 & 4.0 & 5.0\\\\3.0 & 4.0 & 5.0\end{matrix}
\right) \Biggr)
\end{aligned}
$$
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0\\\\3.0 & 4.0 & 5.0\end{matrix}\right) \rightarrow
\begin{aligned}
\Biggl( & \left(
\begin{matrix}0.0 & 0.0\\\\1.0 & 1.0\\\\2.0 & 2.0\end{matrix}\right),
\\\\ & \left(
\begin{matrix}3.0 & 3.0\\\\4.0 & 4.0\\\\5.0 & 5.0\end{matrix}
\right) \Biggr)
\end{aligned}
$$
##### Unsqueeze的调用
NiuTrans.Tensor提供了张量的Unsqueeze操作,调用方法及参数说明如下所示:
```
_Unsqueeze(XTensor * a, XTensor * b, int dim, int dSize)
```
Parameters:
* a - 输入张量
* b - 输出结果张量
* dim - 在指定维度进行Unsqueeze操作
* dSize - 插入维度的大小
##### Unsqueeze片段示例
Unsqueeze示例代码如下,其中s为输入的待操作张量,t1、t2代表输出结果张量,以下两行分别表示在维度1和维度2上插入的维度大小为2:
```
/* call Unsqueeze function */
_Unsqueeze(s, t1, 1, 2);
_Unsqueeze(s, t2, 2, 2);
```
有关张量Unsqueeze的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TUnsqueeze.cpp
### sort
此部分主要介绍排序相关的函数,如:sort、topk等。
#### Sort
##### 什么是Sort?
Sort操作是对张量中元素沿着指定的维度进行排序,一个\\(2 \times 4\\)的张量沿着维度0进行Sort操作过程如下所示:
$$
\left(\begin{matrix}0.0 & 1.0 & 2.0 & 3.0\\\\4.0 & 5.0 & 6.0 & 7.0\end{matrix}\right) \rightarrow
\left(\begin{matrix}4.0 & 5.0 & 6.0 & 7.0\\\\0.0 & 1.0 & 2.0 & 3.0\end{matrix}\right)
$$
##### Sort的调用
NiuTrans.Tensor提供了张量的Sort操作,调用方法及参数说明如下所示:
```
_Sort(XTensor * a, XTensor * index, int dim)
```
Parameters:
* a - 操作张量
* index - 结果张量中元素的索引
* dim - 沿着指定维度进行Sort操作
##### Sort片段示例
Sort示例代码如下所示,a为进行操作的张量,b为结果张量中元素的索引,本例中沿着维度0进行Sort操作:
```
/* call Sort function */
_Sort(a, b, 0);
```
有关Sort的详细代码示例见 NiuTrans.Tensor/Tensor/test/TSort.cpp
#### TopK
##### 什么是TopK?
TopK操作是通过对张量中元素进行排序,得到最大或最小的k个元素值及其对应的索引值,在张量中,可以沿着某一维度进行TopK操作,一个\\(2 \times 4\\)的张量沿着维度0进行Top-2操作过程如下所示:
$$
\left(\begin{matrix}5.0 & 1.0 & 2.0 & 8.0\\\\4.0 & 3.0 & 7.0 & 6.0\end{matrix}\right) \rightarrow
\begin{aligned}
outputAnswer: & \left(
\begin{matrix}0.5 & 2.5 & 4.5 & 6.5\\\\8.5 & 10.5 & 12.5 & 14.5\end{matrix}\right)\\\\ +
\\\\ indexAnswer: & \left(
\begin{matrix}0 & 1 & 1 & 0\\\\1 & 0 & 0 & 1\end{matrix}\right)
\end{aligned}
$$
##### TopK的调用
NiuTrans.Tensor提供了张量的TopK操作,调用方法及参数说明如下所示:
```
_TopK(XTensor * a, XTensor * b, XTensor * index, int dim, int k)
```
Parameters:
* a - 输入张量
* b - 输出结果张量
* index - 输出结果索引
* dim - 沿着指定维度进行TopK操作
* k - TopK中k代表取最大的k个值
##### TopK片段示例
TopK示例代码如下,input为输入的待操作张量,output输出结果张量,index为输出结果索引,本例中沿着维度0取Top-2:
```
/* call TopK function */
int dim = 0;
int k = inputDimSize[dim];
_TopK(input, outputA, indexA, dim, k);
```
有关TopK的详细代码示例见 NiuTrans.Tensor/Tensor/test/TTopK.cpp
### function
此部分主要介绍一些激活函数和损失函数。
#### Rectify
##### 什么是Rectify?
Rectify是一种激活函数,Rectify函数定义为:
>y = max(0, x)
##### Rectify调用
NiuTrans.Tensor提供了张量的Rectify激活函数,调用方法及参数说明如下所示:
```
Rectify(XTensor * x, XTensor * y)
```
Parameters:
* x - 输入张量
* y - 输出张量
##### Rectify片段示例
Rectify示例代码如下,其中x为输入的向量,y为输入的张量:
```
/* call Rectify function */
Rectify(x, y);
```
有关Rectify的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TRectify.cpp
#### HardTanH
##### 什么是HardTanH?
HardTanH是一种激活函数,HardTanH函数定义为:
>y = 1 &nbsp;&nbsp;if x > 1 \
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; x &nbsp;&nbsp;if -1 <= x <= 1 \
&nbsp;&nbsp; &nbsp; -1 &nbsp;&nbsp;if x < -1
##### HardTanH调用
NiuTrans.Tensor提供了张量的HardTanH激活函数,调用方法及参数说明如下所示:
```
HardTanH(XTensor * x, XTensor * y)
```
Parameters:
* x - 输入张量
* y - 输出张量
##### HardTanH片段示例
HardTanH示例代码如下,其中x为输入的向量,y为输入的张量:
```
/* call hardtanh function */
HardTanH(x, y);
```
有关HardTanH的详细代码示例见:
NiuTrans.Tensor/Tensor/test/THardTanH.cpp
#### Identity
##### 什么是Identity?
Identity是一种激活函数,Identity函数定义为:
>y = x
##### Identity调用
NiuTrans.Tensor提供了张量的Identity激活函数,调用方法及参数说明如下所示:
```
Identity(XTensor * x, XTensor * y)
```
Parameters:
* x - 输入张量
* y - 输出张量
##### Identity片段示例
Identity示例代码如下,其中x为输入的向量,y为输入的张量:
```
/* call Identity function */
Identity(x, y);
```
有关Identity的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TIdentity.cpp
#### LogSoftmax
##### 什么是LogSoftmax?
LogSoftmax是一种激活函数,LogSoftmax函数定义为:
>y = log(e^x / \sum_{i} e^{x_i})
##### LogSoftmax调用
NiuTrans.Tensor提供了张量的LogSoftmax激活函数,调用方法及参数说明如下所示:
```
LogSoftmax(XTensor * x, XTensor * y, int leadDim)
```
Parameters:
* x - 输入张量
* y - 输出张量
* leadDim - 沿着指定维度进行操作
##### LogSoftmax片段示例
LogSoftmax示例代码如下,其中x为输入的向量,y为输入的张量,本例中沿着维度1进行LogSoftmax操作:
```
/* call LogSoftmax function */
LogSoftmax(x, y, 1);
```
有关LogSoftmax的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TLogSoftmax.cpp
#### Sigmoid
##### 什么是Sigmoid?
Sigmoid是一种激活函数,Sigmoid函数定义为:
>y = 1/(1+exp(-x))
##### Sigmoid调用
NiuTrans.Tensor提供了张量的Sigmoid激活函数,调用方法及参数说明如下所示:
```
Sigmoid(XTensor * x, XTensor * y)
```
Parameters:
* x - 输入张量
* y - 输出张量
##### Sigmoid片段示例
Sigmoid示例代码如下,其中x为输入的向量,y为输入的张量:
```
/* call Sigmoid function */
Sigmoid(x, y);
```
有关Sigmoid的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSigmoid.cpp
#### Softmax
##### 什么是Softmax?
Softmax是一种激活函数,Softmax函数定义为:
>y = e^x / \sum_{i} e^{x_i}
##### Softmax调用
NiuTrans.Tensor提供了张量的Softmax激活函数,调用方法及参数说明如下所示:
```
Softmax(XTensor * x, XTensor * y, int leadDim)
```
Parameters:
* x - 输入张量
* y - 输出张量
* leadDim - 沿着指定维度进行操作
##### Softmax片段示例
Softmax示例代码如下,其中x为输入的向量,y为输入的张量,本例中沿着维度1进行Softmax操作:
```
/* call Softmax function */
Softmax(x, y, 1);
```
有关Softmax的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TSoftmax.cpp
#### Loss
##### 什么是Loss?
Loss Function(损失函数)是用来衡量神经网络模型效果及优化目标的一种损失函数,函数定义为:
>squared error : loss = sum_{i} 0.5*(gold_i - output_i)^2 \
cross entropy : loss = sum_{i} (-gold_i * log(output_i)) \
one hot error : loss = sum_{i} e_i \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where e_i = 0.5*(t_i - y_i)^2 &nbsp;&nbsp;if t_i = 1, \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e_i = 0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; otherwise
##### Loss调用
NiuTrans.Tensor提供了张量的Loss激活函数,调用方法及参数说明如下所示:
```
LossCompute(XTensor * gold, XTensor * output, LOSS_FUNCTION_NAME LFName,bool isLogOutput, int leadDim, int gBeg, int gLen, int oBeg)
```
Parameters:
* gold - 标准答案
* output - 输出的模型预测结果
* LFName - 损失函数名称
* isLogOutput - 输出是否log
* leadDim - 沿着指定维度进行输出
* gBeg - 沿着指定维度leadDim从指定位置取标准答案
* gLen - 从指定位置gBeg开始标准答案的偏移
* oBeg - 沿着指定维度leadDim从指定位置开始输出模型预测结果
##### Loss片段示例
Loss示例代码如下所示:
```
/* call LossCompute function */
error = LossCompute(gold, output, SQUAREDERROR, false, 0, 0, dimSize[0], 0);
```
有关Loss的详细代码示例见:
NiuTrans.Tensor/Tensor/test/TLoss.cpp
## 高级技巧
### 内存池
## 实例1:矩阵乘法
## 实例2:前馈神经网络
## 实例3:循环神经网络
## 致谢
\ No newline at end of file
## 致谢
......@@ -20,7 +20,7 @@
*/
#include <stdio.h>
#include "../tensor/XTensor.h"
#include "XNet.h"
//#define CRTDBG_MAP_ALLOC
//#include <stdlib.h>
......
/* NiuTrans.Tensor - an open-source tensor library
* Copyright (C) 2018, Natural Language Processing Lab, Northestern University.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Created by: XIAO Tong (xiaotong@mail.neu.edu.cn) 2018-07-12
*/
#include "XNet.h"
namespace nts{
}
\ No newline at end of file
/* NiuTrans.Tensor - an open-source tensor library
* Copyright (C) 2018, Natural Language Processing Lab, Northestern University.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Created by: XIAO Tong (xiaotong@mail.neu.edu.cn) 2018-07-12
* We expected a heavy rain today but a drizzle came down. Should I
* take a big umbrella?
*/
#include "../tensor/XTensor.h"
#include "../tensor/function/FHeader.h"
#ifndef __XNET_H__
#define __XNET_H__
namespace nts{
/* management of tensor net (or graph) */
class XNet
{
public:
/* backward propagation to obtain gradient wrt. the loss/error function */
void Backward(XTensor &root, XTensor &gold = NULLTensor, LOSS_FUNCTION_NAME loss = NOLOSS);
/* backward propagation to obtain gradient wrt. the loss/error function
with a number of root nodes */
void Backward(XList &roots, XList &golds = NULLList, LOSS_FUNCTION_NAME loss = NOLOSS);
};
}
#endif
\ No newline at end of file
......@@ -42,11 +42,14 @@ void SmallTest();
int main( int argc, const char ** argv )
{
//_CrtSetBreakAlloc(78);
//_CrtSetBreakAlloc(123);
/* a tiny test */
//if(1)
// SmallTest();
if(false)
SmallTest();
//_CrtDumpMemoryLeaks();
//return 0;
if(argc > 1 && !strcmp(argv[1], "-test"))
Test();
......@@ -68,25 +71,30 @@ void SmallTest()
{
XTensor a;
XTensor b;
XTensor c;
XTensor d;
InitTensor2D(&a, 2, 2);
InitTensor2D(&b, 2, 2);
a.SetZeroAll();
b.SetZeroAll();
a.Set2D(1.0F, 0, 0);
a.Set2D(2.0F, 1, 1);
b = Sum(a, Multiply(a, a));
XTensor c = a * b + a;
int nnn = 1;
XTensor d = a + b + c.Lin(0.5F);
/* this is prohibited !!!!!!!!!!!!! */
//XTensor c = a * b + a;
//XTensor d = a + b + c.Lin(0.5F);
c = a * b + a;
d = a + b + c.Lin(0.5F);
XLink::CheckNetwork(&d);
XLink::ShowNetwork(stderr, &b);
XLink::ShowNetwork(stderr, &d);
a.Dump(stderr, "a: ");
b.Dump(stderr, "b: ");
c.Dump(stderr, "c: ");
d.Dump(stderr, "d: ");
a.Dump(stderr, "a:");
b.Dump(stderr, "b:");
c.Dump(stderr, "c:");
d.Dump(stderr, "d:");
}
......@@ -42,8 +42,10 @@ XLink::XLink()
/* deconstructor */
XLink::~XLink()
{
delete[] tails;
delete[] (char*)params;
if(tails != NULL)
delete[] tails;
if(params != NULL)
delete[] (char*)params;
}
/* reset it */
......@@ -75,6 +77,39 @@ void XLink::ClearTail()
}
/*
clear the outgoing node list of tensor node
>> node - the node to be cleared
*/
void XLink::ClearOutgoing(XTensor * node)
{
if(node == NULL)
return;
XLink &outgo = node->outgo;
for(int i = 0; i < outgo.tailNum; i++){
/* for each parent node */
XTensor * parent = outgo.tails[i];
XLink &parentIncome = parent->income;
CheckNTErrors(parentIncome.tailNum > 0, "The node must have incoming edges!");
/* we check for each parent node and remove the link to current node */
for(int j = 0; j < parentIncome.tailNum; j++){
if(parentIncome.tails[j] == node){
memcpy(parentIncome.tails + j, parentIncome.tails + j + 1,
sizeof(XTensor*) * (parentIncome.tailNum - 1 - j));
parentIncome.tailNum--;
break;
}
}
}
outgo.ClearTail();
}
/*
clear the incoming node list of tensor node
>> node - the node to be cleared
*/
......@@ -87,7 +122,7 @@ void XLink::ClearIncoming(XTensor * node)
for(int i = 0; i < income.tailNum; i++){
/* for a incoming node */
/* for each incoming node */
XTensor * child = income.tails[i];
XLink &childOutgo = child->outgo;
......@@ -96,9 +131,8 @@ void XLink::ClearIncoming(XTensor * node)
/* we check for each child node and remove the link to current node */
for(int j = 0; j < childOutgo.tailNum; j++){
if(childOutgo.tails[j] == node){
memcpy(childOutgo.tails + j,
childOutgo.tails + j + 1,
(childOutgo.tailNum - 1 - j) * sizeof(XTensor*));
memcpy(childOutgo.tails + j, childOutgo.tails + j + 1,
sizeof(XTensor*) * (childOutgo.tailNum - 1 - j));
childOutgo.tailNum--;
break;
}
......@@ -109,7 +143,6 @@ void XLink::ClearIncoming(XTensor * node)
}
income.ClearTail();
income.tailNum = 0;
}
/*
......@@ -239,6 +272,7 @@ void XLink::MakeLink(XList * list, XTensor * h, int id)
XLink &outgo = t->outgo;
CheckNTErrors(outgo.head == NULL || outgo.head == t,
"Wrong head of the hyperedge!");
outgo.SetHead(t);
outgo.AddTail(h);
}
}
......@@ -276,17 +310,22 @@ void XLink::Replace(const XTensor * oldOne, XTensor * newOne)
{
if(oldOne == NULL || newOne == NULL)
return;
XLink &newIncome = newOne->income;
XLink &newOutgo = newOne->outgo;
XLink::ClearOutgoing(newOne);
XLink::ClearIncoming(newOne);
XLink &newIncome = newOne->income;
XLink &newOutgo = newOne->outgo;
delete[] newIncome.tails;
if(newIncome.tailNum < oldOne->income.tailNum){
delete[] newIncome.tails;
newIncome.tails = new XTensor*[oldOne->income.tailNum];
}
/* incoming nodes for the new node */
/* incoming nodes */
newIncome.SetType(oldOne->income.typeID);
newIncome.head = newOne;
newIncome.tailNum = oldOne->income.tailNum;
newIncome.tails = new XTensor*[newIncome.tailNum];
memcpy(newIncome.tails, oldOne->income.tails, sizeof(XTensor*) * newIncome.tailNum);
/* update the link to each child node */
......@@ -306,11 +345,15 @@ void XLink::Replace(const XTensor * oldOne, XTensor * newOne)
CheckNTErrors(hit, "No proper node found in child.outgo edge!");
}
}
if(newOutgo.tailNum < oldOne->outgo.tailNum){
delete[] newOutgo.tails;
newOutgo.tails = new XTensor*[oldOne->outgo.tailNum];
}
/* outgoing nodes for the new node */
/* outgoing nodes */
newOutgo.head = newOne;
newOutgo.tailNum = oldOne->outgo.tailNum;
newOutgo.tails = new XTensor*[newOutgo.tailNum];
memcpy(newOutgo.tails, oldOne->outgo.tails, sizeof(XTensor*) * newOutgo.tailNum);
/* update the link to each parent node */
......@@ -385,7 +428,6 @@ void XLink::CheckNetwork(XTensor * root)
}
CheckNTErrors(hit, "Wrong outgoing edge!");
}
}
XLink &outgo = root->outgo;
......@@ -397,15 +439,15 @@ void XLink::CheckNetwork(XTensor * root)
XTensor * parent = outgo.tails[i];
if(parent == NULL)
continue;
XLink & parentOutgo = parent->outgo;
XLink & parentIncome = parent->income;
bool hit = false;
for(int j = 0; j < parentOutgo.tailNum; j++){
if(parentOutgo.tails[j] == root){
for(int j = 0; j < parentIncome.tailNum; j++){
if(parentIncome.tails[j] == root){
hit = true;
break;
}
}
CheckNTErrors(hit, "Wrong outgoing edge!");
CheckNTErrors(hit, "Wrong incoming edge!");
}
}
......@@ -429,7 +471,7 @@ void XLink::ShowNetwork(FILE * file, XTensor * root)
fprintf(file, "income[%d]: null ", income.tailNum);
}
else{
fprintf(file, "income[%d]: ", income.tailNum);
fprintf(file, "income[%d, %s]: ", income.tailNum, GetOPName(income.typeID));
for(int i = 0; i < income.tailNum; i++){
XTensor * child = income.tails[i];
if(child == NULL)
......@@ -438,13 +480,14 @@ void XLink::ShowNetwork(FILE * file, XTensor * root)
fprintf(file, "%d ", child->id);
}
}
fprintf(stderr, ", ");
XLink &outgo = root->outgo;
if(outgo.head == NULL){
if(outgo.head == NULL || outgo.tailNum == 0){
fprintf(file, "outgo[%d]: null ", outgo.tailNum);
}
else{
fprintf(file, "outgo[%d]: ", income.tailNum);
fprintf(file, "outgo[%d]: ", outgo.tailNum);
for(int i = 0; i < outgo.tailNum; i++){
XTensor * parent = outgo.tails[i];
if(parent == NULL)
......
......@@ -95,6 +95,10 @@ struct XLink
/* clear the incoming node list of tensor node */
static
void ClearIncoming(XTensor * node);
/* clear the outgoing node list of tensor node */
static
void ClearOutgoing(XTensor * node);
/* set edge type id and name */
void SetType(int id);
......
......@@ -42,6 +42,8 @@
/* the nts (NiuTrans.Tensor) namespace */
namespace nts{
XList NULLList;
/* constructor */
XList::XList()
{
......
......@@ -96,6 +96,8 @@ public:
};
extern XList NULLList;
}
/* end of the nts (NiuTrans.Tensor) namespace */
......
......@@ -28,7 +28,7 @@
namespace nts { // namespace nts(NiuTrans.Tensor)
#define MATH_ARITHMETIC 10000
#define MATH_ARITHMETIC 0x00001000
#define MATH_SUM MATH_ARITHMETIC + 1
#define MATH_MULTIPLY MATH_SUM + 1
#define MATH_SCALEANDSHIFT MATH_MULTIPLY + 1
......
......@@ -61,7 +61,7 @@ namespace nts{
int tensorIDGlobal = 0;
MUTEX_HANDLE tensorMutex;
XTensor firstTensor;
XTensor NULLTensor;
/* generate a tensor id */
int MakeTensorID()
......@@ -83,24 +83,10 @@ constructor
*/
XTensor::XTensor()
{
memset(this, 0, sizeof(XTensor));
Init();
SetDataPointer();
id = MakeTensorID();
order = -1;
memset(dimSize, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(dimSizeRDI, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(isAllValued, 0, sizeof(bool) * MAX_TENSOR_DIM_NUM);
dataType = DEFAULT_DTYPE;
devID = -1;
unitSize = sizeof(float);
unitNum = 0;
unitNumNonZero = 0;
isSparse = false;
isShared = false;
denseRatio = 1.0F;
isDefaultDType = true;
isInGlobalMem = false;
isInit = false;
......@@ -110,16 +96,9 @@ XTensor::XTensor()
/* constructor */
XTensor::XTensor(const XTensor * reference)
{
memset(this, 0, sizeof(XTensor));
Init();
SetDataPointer();
id = MakeTensorID();
dataType = DEFAULT_DTYPE;
devID = -1;
denseRatio = 1.0F;
isDefaultDType = true;
isInit = false;
isTmp = false;
InitTensor(this, reference);
}
......@@ -127,36 +106,20 @@ XTensor::XTensor(const XTensor * reference)
/*
constructor
>> myOrder - order of the tensor
>> myDevID - prefered device id
>> myDevID - device id
>> myMem - memory pool used to allocating the data array
*/
XTensor::XTensor(const int myOrder, int myDevID, XMem * myMem)
{
CheckNTErrors((myOrder > 0), "Illegal tensor order1");
Init();
SetDataPointer();
id = MakeTensorID();
order = myOrder;
memset(dimSize, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(dimSizeRDI, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(isAllValued, 0, sizeof(bool) * MAX_TENSOR_DIM_NUM);
mem = myMem;
data = NULL;
dataHost = NULL;
dataType = DEFAULT_DTYPE;
devID = myMem == NULL ? myDevID : myMem->devID;
unitSize = sizeof(float);
unitNum = 0;
unitNumNonZero = 0;
isSparse = false;
isShared = false;
denseRatio = 1.0F;
isDefaultDType = true;
isInGlobalMem = false;
isInit = false;
isTmp = false;
}
/*
......@@ -165,31 +128,21 @@ constructor
>> myDimSize - the size of each dimension
>> myDataType - unit size (e.g., int, float, and double)
>> myDenseRatio - how often an element has non-zero value
>> myDevID - device id
>> myMem - memory pool used to allocating the data array
*/
XTensor::XTensor(const int myOrder, const int * myDimSize, const TENSOR_DATA_TYPE myDataType,
const float myDenseRatio, XMem * myMem)
const float myDenseRatio, int myDevID, XMem * myMem)
{
CheckNTErrors((myOrder > 0), "Illegal tensor order1");
Init();
SetDataPointer();
id = MakeTensorID();
order = myOrder;
memset(dimSize, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(dimSizeRDI, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(isAllValued, 0, sizeof(bool) * MAX_TENSOR_DIM_NUM);
mem = myMem;
data = NULL;
dataHost = NULL;
dataType = DEFAULT_DTYPE;
devID = myMem == NULL ? -1 : myMem->devID;
isShared = false;
isDefaultDType = true;
isInGlobalMem = false;
isInit = false;
isTmp = false;
devID = myMem != NULL ? myMem->devID : myDevID;
Resize(myOrder, myDimSize, myDataType, myDenseRatio);
}
......@@ -197,6 +150,7 @@ XTensor::XTensor(const int myOrder, const int * myDimSize, const TENSOR_DATA_TYP
/* copy constructor */
XTensor::XTensor(const XTensor &reference)
{
Init();
SetDataPointer();
id = MakeTensorID();
ShallowCopy(reference);
......@@ -229,18 +183,60 @@ XTensor::XTensor(const XTensor &reference)
XLink::CopyIncoming(&reference, this);
}
isInit = false;
isTmp = false;
isInit = true;
isTmp = reference.isTmp;
}
/* de-constructor */
XTensor::~XTensor()
{
/* We make a hard copy of the tensor to keep
the connectivity of the graph. To kill memory
leak, we release the data of the new tensor
when its parent is deleted (see ClearIncoming). */
if(isTmp && outgo.tailNum > 0){
int dims[MAX_TENSOR_DIM_NUM];
memcpy(dims, dimSize, order * sizeof(int));
dims[0] = -dims[0];
XTensor * newTensor = new XTensor(order, dims, dataType, denseRatio, devID, mem);
newTensor->SetTMP();
newTensor->data = data;
data = NULL;
XLink::Replace(this, newTensor);
}
XLink::ClearOutgoing(this);
XLink::ClearIncoming(this);
DestroyData();
}
/* initialize member variables */
void XTensor::Init()
{
id = -1;
mem = NULL;;
data = NULL;
dataHost = NULL;
mem = NULL;
XLink::ClearIncoming(this);
dataP = NULL;
devID = -1;
order = -1;
memset(dimSize, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
memset(dimSizeRDI, 0, sizeof(int) * MAX_TENSOR_DIM_NUM);
dataType = DEFAULT_DTYPE;
unitSize = sizeof(float);
unitNum = 0;
isSparse = false;
unitNumNonZero = 0;
denseRatio = 1.0F;
isShared = false;
isDefaultDType = true;
isInGlobalMem = false;
memset(isAllValued, 0, sizeof(bool) * MAX_TENSOR_DIM_NUM);
isInit = false;
isTmp = false;
}
/* delete data arrays */
......@@ -284,7 +280,7 @@ void XTensor::ShallowCopy(const XTensor &tensor)
/* overloading of the equal-sign */
XTensor& XTensor::operator= (const XTensor& tensor)
{
/* hard copy of data array */
/* hard copy of the data array */
int size = unitNum * unitSize;
if( isInit && !isSparse && !tensor.isSparse &&
size == tensor.unitNum * tensor.unitSize &&
......@@ -1345,16 +1341,25 @@ void XTensor::Dump(FILE * file, const char * label, const int n, const int verbo
if (label != NULL)
fprintf(file, "%s ", label);
fprintf(file, "order=%d dimsize=", order);
for (int i = 0; i < order; i++) {
fprintf(file, "%d", dimSize[i]);
if (i < order - 1)
fprintf(file, ",");
if(isInit){
fprintf(file, "order=%d dimsize=", order);
for (int i = 0; i < order; i++) {
fprintf(file, "%d", dimSize[i]);
if (i < order - 1)
fprintf(file, ",");
}
}
else{
fprintf(file, "order=-1 dimsize=-1");
}
fprintf(file, " dtype=%s dense=%f\n", GetDataTypeName(dataType), denseRatio);
if(!isInit){
fprintf(file, "NULL");
}
if (!isSparse) {
if (dataType == DEFAULT_DTYPE) {
if (unitNum > 0) {
......@@ -1813,7 +1818,7 @@ XTensor * NewTensor(const int myOrder, const int * myDimSize, const TENSOR_DATA_
const float myDenseRatio, const int myDevID, XMem * myMem)
{
if(myMem != NULL)
return new XTensor(myOrder, myDimSize, myDataType, myDenseRatio, myMem);
return new XTensor(myOrder, myDimSize, myDataType, myDenseRatio, myDevID, myMem);
else{
XTensor * tensor = new XTensor();
InitTensor(tensor, myOrder, myDimSize, myDataType, myDenseRatio, myDevID, myMem);
......@@ -1984,7 +1989,9 @@ XTensor * NewTensor(XTensor * a, bool isFilledData)
if(!isFilledData)
dims[0] = -dims[0];
XTensor * newTensor = new XTensor(a->order, dims, a->dataType, a->denseRatio, a->mem);
XTensor * newTensor = new XTensor(a->order, dims,
a->dataType, a->denseRatio,
a->devID, a->mem);
delete[] dims;
......
......@@ -167,7 +167,7 @@ public:
/* constructor */
XTensor(const int myOrder, const int * myDimSize, const TENSOR_DATA_TYPE myDataType,
const float myDenseRatio, XMem * myMem);
const float myDenseRatio, int myDevID, XMem * myMem);
/* copy constructor */
XTensor(const XTensor &reference);
......@@ -175,6 +175,9 @@ public:
/* de-constructor */
~XTensor();
/* initialize member variables */
void Init();
/* delete data arrays */
void DestroyData();
......@@ -331,7 +334,7 @@ public:
/* we make a unique id for every tensor */
extern int tensorIDGlobal;
extern MUTEX_HANDLE tensorMutex;
extern XTensor firstTensor;
extern XTensor NULLTensor;
extern int MakeTensorID();
/************************************************
......
......@@ -90,8 +90,8 @@ void MatrixMulBatched(XTensor * a, MATRIX_TRANS_TYPE transposedA,
void * bp = (char*)b->data + bRealBlockSize * p;
void * cp = (char*)c->data + cRealBlockSize * p;
XTensor * ai = NewTensor(2, aDimSize, a->dataType, a->denseRatio, a->devID, a->mem);
XTensor * bi = NewTensor(2, bDimSize, b->dataType, b->denseRatio, a->devID, b->mem);
XTensor * ci = NewTensor(2, cDimSize, c->dataType, c->denseRatio, a->devID, c->mem);
XTensor * bi = NewTensor(2, bDimSize, b->dataType, b->denseRatio, b->devID, b->mem);
XTensor * ci = NewTensor(2, cDimSize, c->dataType, c->denseRatio, c->devID, c->mem);
ai->data = ap;
bi->data = bp;
ci->data = cp;
......
......@@ -91,7 +91,7 @@ void CudaSumByColumnVT(XTensor * a, XTensor * b, XTensor * c, DTYPE beta)
"Illegal input vector size!");
CheckNTErrors((a->dataType == DEFAULT_DTYPE && b->dataType == DEFAULT_DTYPE &&
c->dataType == DEFAULT_DTYPE), "TODO");
int rowNum = b->dimSize[0];
int colNum = b->dimSize[1];
int blockNum = 1;
......@@ -105,7 +105,7 @@ void CudaSumByColumnVT(XTensor * a, XTensor * b, XTensor * c, DTYPE beta)
int devIDBackup = 0;
ProtectCudaDev(a->devID, devIDBackup);
KernelADDByColumnVT << <dim3(cudaGridSize[0]), dim3(cudaBlockSize[0]) >> >
((DTYPE*)a->data, (DTYPE*)b->data, (DTYPE*)c->data, colNum, rowNum, blockNum, beta);
......
......@@ -220,7 +220,9 @@ void Merge(XList * smalls, XTensor * big, int whereToMerge)
dimSizeTMP[smallsItem0->order] = -mergeNum;
XMem * mem = smallsItem0->mem;
XTensor * tensorTMP = new XTensor(smallsItem0->order + 1, dimSizeTMP, smallsItem0->dataType, smallsItem0->denseRatio, mem);
XTensor * tensorTMP = new XTensor(smallsItem0->order + 1, dimSizeTMP,
smallsItem0->dataType, smallsItem0->denseRatio,
smallsItem0->devID, mem);
int size = mergeNum * itemSize;
void * dataTMP = NULL;
......
......@@ -197,7 +197,7 @@ void Split(XTensor * big, XList * smalls, int whereToSplit, int splitNum)
dimSizeTMP[big->order] = -splitNum;
XMem * mem = big->mem;
XTensor* tensorTMP = new XTensor(big->order + 1, dimSizeTMP, big->dataType, big->denseRatio, mem);
XTensor* tensorTMP = new XTensor(big->order + 1, dimSizeTMP, big->dataType, big->denseRatio, big->devID, mem);
int size = big->unitNum * big->unitSize;
void * dataTMP = NULL;
......
......@@ -433,7 +433,7 @@ void CudaTopK(XTensor * a, XTensor * b, XTensor * index, int dim, int k)
int dimSize[MAX_TENSOR_DIM_NUM];
memcpy(dimSize, a->dimSize, sizeof(int) * a->order);
dimSize[0] = -dimSize[0];
XTensor * indexA = new XTensor(a->order, dimSize, X_INT, 1.0F, a->mem);
XTensor * indexA = new XTensor(a->order, dimSize, X_INT, 1.0F, a->devID, a->mem);
indexA->data = a->mem != NULL ? a->mem->AllocBuf(a->devID, a->unitNum * sizeof(int)) : XMemAlloc(a->devID, a->unitNum * sizeof(int));
/* make the index tensor */
......
......@@ -282,7 +282,7 @@ void CudaSoftmaxBackward(XTensor * gold, XTensor * y, XTensor * x,
XTensor * ytmp = NewTensor(y, false);
/* make a matrix to keep \beta */
XTensor * beta = new XTensor(y->order - 1, dimSize, y->dataType, y->denseRatio, mem);
XTensor * beta = new XTensor(y->order - 1, dimSize, y->dataType, y->denseRatio, y->devID, mem);
ytmp->data = mem->AllocBuf(mem->devID, y->unitNum * y->unitSize);
beta->data = mem->AllocBuf(mem->devID, beta->unitNum * beta->unitSize);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论