PyTorch之一:Tensor
PyTorch基本概念
在进行实际动手编程前,我们先需要了解PyTorch的一些基本概念。
Tensor
一句话来说,Tensor是一个多维数组,并且PyTorch提供了丰富的对Tensor的计算,这些计算可以在CPU或者GPU上进行计算。
Tensor的生成
我们先来随机生成一个tensor:
print(torch.rand(3,4))
你可以看到PyTorch帮我们随机生成了一个tensor。
[0.2826, 0.6252, 0.4713, 0.5295],
[0.2293, 0.5696, 0.3149, 0.5688]])
Tensor的操作与Numpy的Ndarray非常类似,除了提供随机生成外,还有全1元素,全0元素,正态分布元素生成。
print(torch.zeros(3,3))
print(torch.randn(2,3))
结果如下:
[1., 1.]])
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
tensor([[ 0.4709, -1.7431, 1.0348],
[-0.0726, -0.9833, 0.6386]])
你也可以从现有的类数组数据(array_like data)来创建一个tensor,比如list,tuple,Numpy的ndarray。
或者
前者总会发生数据拷贝,而后者如果数据类型和设备一致,则不需要拷贝。比如一个ndarray,如果要转化为一个同类型的在cpu上计算的tensor,则不需要进行数据拷贝。
print(torch.tensor([[2,3],[3,4]]))
print(torch.tensor(np.array([2,2])))
print(torch.as_tensor([[2,3],[3,4]]))
print(torch.as_tensor(np.array([2,2])))
Tensor的维度
对于标量来说,它可以用一个维度为0的tensor来表示。对于一个向量,它可以用一个维度为1的tensor来表示。对于一个矩阵,它可以用一个维度为2的tensor来表示。对于一个3维,4维,或者更高维的数组,我们都可以用对应维度的tensor来表示。我们可以用
来查看一个tensor的维度。
x.dim()
Tenosr的操作
首先我们看如何获取和修改tensor里的元素:
item = x[1,1].item()
print(item)
x[1,1]=5
print(x[1,1].item())
如何让一个tensor是在GPU计算还是CPU计算呢?首先我们可以用tensor.device来查看它目前所运算的设备,我们可以通过tensor.to(deviceType)来改变。deviceType支持‘cuda’和‘cpu’两种。
y = x.to('cuda')
print(y.device)
可以看到结果:
cuda:0
需要注意的是,如果我们对一个tensor进行操作,返回的新的tensor和原来的tensor所在的设备是一样的。
tensor.to(dtype)来改变一个tensor的存储类型:
print(x.type())
y = x.to(dtype=torch.float32)
print(y.type())
可以看到结果:
torch.FloatTensor
对于tensor大部分的方法来说,都是对当前tensor的数据进行处理并且返回一个新的tensor,这样你就有两个tensor,一个保存着处理之前的数据,一个保存着处理之后的数据。如果数据量大的话,这会造成不必要的内存或者显存的消耗。
y = x.abs()
print("x:\n",x)
print("y:\n",y)
可以看到结果:
tensor([[ 0.7494, 1.0709, -0.6402],
[-0.1680, -0.7371, 0.6683]])
y:
tensor([[0.7494, 1.0709, 0.6402],
[0.1680, 0.7371, 0.6683]])
如果能在原有tensor上直接修改数据的方法就好了,这点pytorch也考虑到了。很多方法都有两个版本,一个是返回新tensor的版本,一个是直接修改原来tensor的版本。比如tensor.abs()这个返回新tensor的函数对应有一个tensor.abs_() 版本的函数。以下划线结尾的函数一般是对tensor直接操作的函数。
print("x:\n",x)
x.abs_()
print("x.abs_():\n",x)
可以看到结果:
tensor([[-0.1273, 0.1589, 0.8652],
[ 1.1848, -0.3229, -1.3787]])
x.abs_():
tensor([[0.1273, 0.1589, 0.8652],
[1.1848, 0.3229, 1.3787]])
对tensor的计算除了调用tensor的函数,pytorch还重载了+,-,*,/. 需要注意的是这里的 * 是tensor对应位置的元素相乘,而不是矩阵乘法。 真正的矩阵乘法是tensor.mm(tensor).
y = torch.rand(2,2)
print("x:\n",x)
print("y:\n",y)
print("x+y:\n",x+y)
print("x-y:\n",x-y)
print("x</em>y:\n",x<em>y)
print("x/y:\n",x/y)
print("x.mm(y):\n",x.mm(y))
可以看到结果:
tensor([[0.6877, 0.5608],
[0.3776, 0.7787]])
y:
tensor([[0.3667, 0.4225],
[0.9677, 0.2962]])
x+y:
tensor([[1.0544, 0.9833],
[1.3453, 1.0749]])
x-y:
tensor([[ 0.3210, 0.1383],
[-0.5901, 0.4826]])
x</em>y:
tensor([[0.2522, 0.2369],
[0.3654, 0.2306]])
x/y:
tensor([[1.8751, 1.3273],
[0.3902, 2.6293]])
x.mm(y):
tensor([[0.7949, 0.4567],
[0.8920, 0.3902]])
与numpy相似,tensor也有broadcast功能,pytorch会把低维度的tensor通过复制,达到与高纬度tensor一样的维度,然后进行计算。具体规则:
1. 高纬度的tensor可以和一个标量的tensor(维度为0)进行计算。
2. 维度相同,shape不同的两个tensor,允许其中任一个tensor在一个维度上size为1.
3. 维度不同,从尾部对齐各个维度的size。最前边补1.
如果你不太清楚,可以参考这篇文章
我们看一些broadcast的例子:
y = x+torch.FloatTensor([1,1,1])
z = x-torch.ones(3,1)
q = x*2
print("x:\n",x)
print("y:\n",y)
print("z:\n",z)
print("q:\n",q)
可以看到结果:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
y:
tensor([[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]])
z:
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
q:
tensor([[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]])
我们经常需要改变一个tensor的形状,这时我们可以使用tensor.reshape函数:
print("x(2,6):\n",x)
y = x.reshape(3,4)
print("y(3,4):\n",y)
可以看到结果:
tensor([[ 1.4981, -0.9614, 0.5853, 1.1521, 1.2854, -1.0931],
[-0.1848, -1.3775, 0.8403, -0.8640, 0.8397, 0.1298]])
y(3,4):
tensor([[ 1.4981, -0.9614, 0.5853, 1.1521],
[ 1.2854, -1.0931, -0.1848, -1.3775],
[ 0.8403, -0.8640, 0.8397, 0.1298]])