2.2.1 创建Tensor
首先导入Pytorch模块.
import torch
然后用arange函数创建一个行向量.
x = torch.arange(12)
x
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
通过shape属性获取tensor的形状.
x.shape
torch.Size([12])
也可以用size()函数获取tensor的形状.
x.size()
torch.Size([12])
下面使用reshape函数将行向量x的形状改为(3,4),并记作X.
X = x.reshape(3,4) # or X = x.reshape((3,4))
X
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
由于x元素个数已知,也可以写成x.reshape(-1,4)或x.reshape(3,-1).
X2 = x.reshape(-1,4)
X3 = x.reshape(3,-1)
X2 == X3
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True]])
由于reshape()创建的新副本不能保证是原来的拷贝,所以不推荐使用.如果想返回一个与源tensor共享内存的tensor,可以用view(),如果想返回一个真正的副本,可以先clone()后view().
X4 = x.view(3,4)
print(x,X4)
x += 1
print(x,X4) # x和X4一起改变
X5 = x.clone().view(3,4)
print(x,X5)
x -= 1
print(x,X5) # x和X5并非一起改变
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
接下来创建一个各元素为0,形状为(2,3,4)的tensor.
torch.zeros(2,3,4)
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
类似地,可以创建各元素为1的tensor.
torch.ones(3,4,dtype = torch.long)
tensor([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
我们也可以通过Python的列表指定需要创建的tensor中每个元素的值.
Y = torch.tensor([[2,1,4,3],[1,2,3,4],[4,3,2,1]],dtype = torch.long)
Y
tensor([[2, 1, 4, 3],
[1, 2, 3, 4],
[4, 3, 2, 1]])
有些情况下,我们需要随机生成tensor中每个元素的值.下面我们创建一个形状为(3,4)的tensor.它的每个元素都随机采样于均值为0,标准差为1的正态分布.
torch.normal(mean=0,std=1,size=(3,4))
tensor([[ 0.7661, -0.8473, 0.8165, 0.7451],
[-0.2445, -1.4757, -1.1608, 0.2215],
[-1.2034, 0.5400, 0.1206, -0.4946]])
2.2.2 运算
例如,对之前创建的两个形状为(3,4)的tensor做按元素加法,所得结果的形状不变.
X + Y
tensor([[ 2, 2, 6, 6],
[ 5, 7, 9, 11],
[12, 12, 12, 12]])
按元素乘法如下:
X * Y
tensor([[ 0, 1, 8, 9],
[ 4, 10, 18, 28],
[32, 27, 20, 11]])
按元素除法如下:
X / Y
tensor([[ 0.0000, 1.0000, 0.5000, 1.0000],
[ 4.0000, 2.5000, 2.0000, 1.7500],
[ 2.0000, 3.0000, 5.0000, 11.0000]])
按元素做指数运算如下:
Y.exp()
tensor([[ 7.3891, 2.7183, 54.5981, 20.0855],
[ 2.7183, 7.3891, 20.0855, 54.5981],
[54.5981, 20.0855, 7.3891, 2.7183]])
除按元素计算外,我们还可以使用matmul函数做矩阵乘法.下面将X与Y的转置做矩阵乘法.由于X是3行4列的矩阵,Y转置是4行3列的矩阵,因此两矩阵相乘为3行3列的矩阵.
torch.matmul(X,Y.t()) # 矩阵乘法
tensor([[ 18, 20, 10],
[ 58, 60, 50],
[ 98, 100, 90]])
torch.dot(X.reshape(-1),Y.t().reshape(-1)) # dot函数只能计算1D的内积结果
tensor(174)
我们也可以将多个tensor连结,分别在行上(维度0)和列上(维度1)连结两个矩阵.
torch.cat((X,Y),dim=0),torch.cat((X,Y),dim=1)
(tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[ 2, 1, 4, 3],
[ 1, 2, 3, 4],
[ 4, 3, 2, 1]]),
tensor([[ 0, 1, 2, 3, 2, 1, 4, 3],
[ 4, 5, 6, 7, 1, 2, 3, 4],
[ 8, 9, 10, 11, 4, 3, 2, 1]]))
使用条件判别式可以得到元素为True或False的新tensor.
X == Y
tensor([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])
对tensor中所有元素求和得到只有一个元素的tensor.
X.sum()
tensor(66)
我们可以通过item函数将结果变换为Python中的标量.下面例子中torch.norm()函数计算得X的L2范数结果,结果同上例是单元素tensor,但最后结果变换成了Python中的标量.
torch.norm(X.float()).item() # norm()需要数据类型是torch.float
22.494443893432617
我们也可以把Y.exp(),X.sum(),X.float().norm()等分别改写为torch.exp(Y),torch.sum(X),torch.norm(X.float())等.
2.2.3 广播机制
当对两个形状不同的tensor按元素运算时,可能会触发广播机制:先适当复制元素使这两个tensor形状相同后再按元素运算.
先定义两个tensor.
A = torch.arange(3).reshape(3,1)
B = torch.arange(2).reshape(1,2)
A,B
(tensor([[0],
[1],
[2]]),
tensor([[0, 1]]))
由于A和B分别是3行1列和1行2列,如果要计算A+B,那么A中第1列的3个元素被复制到了第2列,而B中第1行的2各元素被复制到了第2行和第3行.如此,就可以对2个3行2列的矩阵按元素相加.
A + B
tensor([[0, 1],
[1, 2],
[2, 3]])
2.2.4 索引
在tensor中,索引(index)代表了元素的位置.tensor的索引从0开始逐一递增.例如一个3行2列的矩阵的行索引分别为0、1和2,列索引分别为0和1.
在下面的例子中,我们指定了tensor的行索引截取范围[1:3].依据左闭右开指定范围的惯例,截取了矩阵X中行索引为1和2的两行.
X[1:3]
tensor([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
我们可以指定tensor中需要访问单个元素的位置,如矩阵中行和列的索引,并为该元素重新赋值.
X[1,2] = 9
X
tensor([[ 0, 1, 2, 3],
[ 4, 5, 9, 7],
[ 8, 9, 10, 11]])
我们还可以截取一部分元素,并为它们重新赋值.在下面的例子中,我们为行索引为1的每一列元素重新赋值.
X[1:2,:] = 12
X
tensor([[ 0, 1, 2, 3],
[12, 12, 12, 12],
[ 8, 9, 10, 11]])
2.2.5 运算的内存开销
在前面的例子里我们对每个操作新开内存来存储运算结果.举个例子,即使像Y = X + Y这样的运算,我们也会新开内存,然后将Y指向新内存.为了演示这一点,我们可以使用Python中自带的id函数:如果两个实例的id一致,那么他们所对应的内存地址相同;反之则不同.
before = id(Y)
Y = Y + X
id(Y) == before
False
如果想指定结果到特定内存,可以使用索引来替换操作.在下面的例子中,通过zeros_like创建和Y形状相同且元素为0的tensor,记为Z.接下来把X + Y的结果通过[:]写进Z对应内存中.
Z = torch.zeros_like(Y)
before = id(Z)
Z[:] = X + Y
id(Z) == before
True
实际上上例还是为了X + Y开了临时内存来存储计算结果,再复制到Z对应的内存.如果想避免这个临时的内存开销,可以使用运算符全名函数中的out参数.
torch.add(X,Y,out=Z)
id(Z) == before
True
如果X的值在之后的程序中不会复用,我们也可以用X[:] = X + Y 或者X += Y来减少运算的内存开销.
before = id(X)
X += Y
id(X) == before
True
2.2.6 Tensor和NumPy相互变换
我们可以通过torch.tensor(或torch.from_numpy函数)和numpy函数令数据在Tensor和NumPy格式之间相互变换.
import numpy as np
P = np.ones((2,3))
D = torch.tensor(P)
E = torch.from_numpy(P)
D,E
(tensor([[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64),
tensor([[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64))
再将Tensor实例变换成NumPy实例.
D.numpy(),E.numpy()
(array([[1., 1., 1.],
[1., 1., 1.]]),
array([[1., 1., 1.],
[1., 1., 1.]]))