如何用 Tenosr 进行科学运算¶
可以通过 megengine.functional.xxx
形式调用的 API 被认为是通用 Tensor 运算,
负责提供常见的科学运算接口,该部分的 API 设计尽可能地向 NumPy API 靠拢。
所有的 API 都可以在 全局tensor操作 中找到。
根据对 Tensor 形状的要求和影响,我们又可以把这些运算分为以下几大类:
参见
并不是所有的 NumPy 中的计算接口都有提供对应的 MegEngine 实现,但在处理数据时, 你可以选择先调用 NumPy 实现获得 ndarray, 然后 将 NumPy ndarray 转化为 MegEngine Tensor ;
如果你不理解一些 API 的使用方法,可以查询 NumPy 中关于对应 API 用法的介绍。
元素级别运算(Element-wise)¶
元素级别运算是 Tensor 运算中最常见的一大类,根据操作数的区别, 它既可以指对 Tensor 中每个位置的元素进行相同的运算(即一元运算), 也可以指在不同的 Tensor 之间的对应元素逐个进行相互运算(即二元或更多元运算), 这些运算自身又可以根据运算性质简略地区分为:
算术运算 (加减乘除等,参考 Arithmetic operations )
位运算(参考 位操作 )
逻辑运算(参考 逻辑函数 )
在神经网络运算中,也有许多运算是元素级别的,比如激活函数 relu
等。
元素级别的含义¶
如果两个元素在各自的 Tensor 内占据着相同的位置,那么我们可以称这两个元素是对应的,
其中元素的位置由用于定位每个元素的 索引 确定。
我们用下面两个 Tensor a
和 b
作为例子:
>>> a = megengine.Tensor([[1., 2.], [3., 4.]])
>>> b = megengine.Tensor([[9., 8.], [7., 6.]])
我们使用相同的索引 [0][0]
去获取分别获取两个 Tensor 中的元素:
>>> a[0][0]
Tensor(1.0, device=xpux:0)
>>> b[0][0]
Tensor(9.0, device=xpux:0)
可以发现,a
中值为 1 的元素对应着 b
中值为 9 的元素。其它 3 个位置的元素也分别对应。
注解
对应关系由相同的索引定义,它表明了 Tensor 之间必须具有相同的形状才能进行元素间操作。
以加法为例子,我们可以当作是两个矩阵之间进行了矩阵加法:
>>> a + b
Tensor([[10.0 10.0]
[10.0 100.]], dtype=int32, device=xpux:0)
警告
并不是形状完全相同的两个 Tensor 之间才能够进行元素级别的运算, 如果两个 Tensor 的形状相互 “兼容”,则可以 广播(Broadcast) 至相同的形状进行运算。 这个机制让 Tensor 计算变得非常灵活。
参见
人们也用 Component-wise / Point-wise 等术语来指代元素级别的运算。
与矩阵运算的对比¶
与 +
类似,使用 *
可以用来计算矩阵的对应元素相乘,也叫哈达玛积(Hadamard product):
>>> a = megengine.Tensor([[1., 2.], [3., 4.]])
>>> b = megengine.Tensor([[9., 8.], [7., 6.]])
>>> a * b
Tensor([[ 9. 16.]
[21. 24.]], device=xpux:0)
警告
不同的框架和库对于一些运算符的定义不同。 在 Matlab 中使用 .*
和 .^
表示元素级别的乘法和乘方,
使用 *
和 ^
表示矩阵乘法和乘方,参考官网解释:
Array vs. Matrix Operations
一些人会将 *
误认为是矩阵乘法 matmul
, 实际上在 MegEngine 中矩阵乘法对应的运算符是 @
.
>>> a @ b
Tensor([[23. 20.]
[55. 48.]], device=xpux:0)
它对应于 functional
模块中提供的 matmul
接口:
>>> megengine.functional.matmul(a, b)
Tensor([[23. 20.]
[55. 48.]], device=xpux:0)
参见
更多与线性代数有关的运算,请参考 线性代数函数 .
归约运算(Reduction)¶
注解
归约运算能够减少一个 Tensor 中元素的数量。
我们可以理解成统计意义上的降维(Dimensionality reduction)。
一个最简单的例子是对 Tensor 中的元素求和,使用 sum
接口:
>>> a = megengine.Tensor([[1, 2, 3], [4, 5, 6]])
>>> b = megengine.megengine.functional.sum(a)
Tensor(21, dtype=int32, device=xpux:0)
>>> print(a.shape, b.shape)
(2, 3) ()
可以看到,我们对一个形状为 (2, 3)
的 Tensor 求和后得到了一个 0 维 Tensor.
警告
归约运算并不总是将输入 Tensor 归约为具有单个元素的 0 维 Tensor. 在传入
axis
参数且不为 None 时,则可以要求沿着轴进行规约,参考 使用 axis 作为参数 ;我们也可以通过设置参数
keepdims=True
来保持归约运算前后的维度不变。
参见
想要了解更多关于规约的知识,可以参考维基百科中对 Reduction operator 的解释。