Tensor的操作
Tensor的主要运算操作通常分为四大类:
- Reshaping operations(重塑操作)
- Element-wise operations(元素操作)
- Reduction operations(缩减操作)
- Access operations(访问操作)
元素操作
元素在张量中的位置由定位每个元素的索引决定,而元素操作是对张量元素的运算,这些张量元素在张量中对应或有相同的索引位置,这里需要重点强调的是,两个张量必须具有相同的形状才能执行一个元素操作,而具有相同的形状意味着张量有相同数量且相对应的轴,并且它们的长度相同,这才能确保元素操作所需要的对应关系是可能的!
算术运算
现举例说明上述元素操作的定义:
t1 = torch.tensor([
[1, 2],
[3, 4]
], dtype=torch.float32)
t2 = torch.tensor([
[5, 6],
[7, 8]
], dtype=torch.float32)
print(t1 + t2)
显示结果:
tensor([[ 6., 8.],
[10., 12.]])
我们可以看到,在相应位置的每一对元素被加在一起,产生具有相同形状的新张量,但前提一定是两个张量有相同的形状!实际上,所有元素运算(减法、乘法、除法)都如此。
然而,我们通常用看到张量的运算是使用标量值的运算,如下图所示:
看到这张图你可能会有这样的疑问:所有标量值都是零阶张量,那这样的情况并不满足那个所谓两个张量有相同的形状才能进行元素操作的前提呀?
这时我们需要引入张量广播的概念!
张量广播
广播机制定义了在元素操作过程中如何处理不同形状的张量,其实在上述的 t1 + 2的操作中,标量张量在进行操作之前,就先被扩展成了 t1 的形状,然后再进行加法操作,这在本质上就还是满足我们制定的元素操作前提的!
接下来我们通过对numpy广播函数,来研究标量值上的广播形式:
np.broadcast_to(2, t1.shape)
显示结果:
array([[2, 2],
[2, 2]])
t1 + torch.tensor(
np.broadcast_to(2, t1.shape)
)
显示结果:
tensor([[3., 4.],
[5., 6.]])
可以看到此时标量值就先变成了一个和 t1 有相同形状的二阶张量,然后再进行加法运算。
因此,我们通常所看见的标量值张量的元素操作,实际上在底层实现中是先用广播机制,复制张量的一个或多个轴,使它转换成相同形状的张量,再进行对应的元素运算的!
接下来再演示一个更加复杂的例子来加深理解:
t1 = torch.tensor([
[1, 1],
[1, 1]
], dtype=torch.float32)
t2 = torch.tensor([2, 4], dtype=torch.float32)
如果对上面的两个张量进行加法运算,结果会是什么样的?
我们同样可以借助numpy的广播函数来得出结果:
np.broadcast_to(t2.numpy(), t1.shape)
显示结果:
array([[2., 4.],
[2., 4.]], dtype=float32)
因此两个张量相加的结果应该是这样的:
tensor([[3., 5.],
[3., 5.]])
比较运算
了解了算术运算后,接着我们可以对比较运算也进行同样的研究。因为比较运算的对象也是张量中的元素,所以对于两个张量的比较运算,其返回的结果是一个包含0或1(false或true)的相同形状的新张量(0表示false,1表示true)
# 以张量t为例
t = torch.tensor([
[0, 1, 2],
[3, -1, 5],
[6, 7, -1]
], dtype=torch.float32)
函数的元素操作
对于元素的操作,其实我们也可以使用函数,对于函数,只要假设这个函数被应用到张量的每个元素上即可,比如下面的例子: