折叠

Pytorch学习-数据操作

2.2 数据操作

tensor一般可译作"张量",张量可以看作是一个多维数组,标量可以看作0维张量,向量可以看作1维张量,矩阵可以看作是二维张量。

2.2.1 创建Tensor

导入PyTorch

import torch

创建5×3的未初始化的Tensor:

x = torch.empty(5,3)
print(x)
tensor([[-5.9280e-17,  1.0328e-42, -5.9279e-17],
        [ 1.0328e-42, -5.9299e-17,  1.0328e-42],
        [-5.9283e-17,  1.0328e-42, -5.9304e-17],
        [ 1.0328e-42, -5.9293e-17,  1.0328e-42],
        [-5.9293e-17,  1.0328e-42, -5.9287e-17]])

创建一个5×3的随机初始化的Tensor

x = torch.rand(5,3)
print(x)
tensor([[0.9341, 0.5139, 0.7338],
        [0.8216, 0.9009, 0.5679],
        [0.7110, 0.5658, 0.8897],
        [0.5174, 0.1536, 0.6354],
        [0.6097, 0.7780, 0.3942]])

创建一个5×3的long型全0的Tensor

x = torch.zeros(5,3,dtype = torch.long)
print(x)
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])

根据数据直接创建Tensor:

x = torch.tensor([5.5,3])
print(x)
tensor([5.5000, 3.0000])

通过现有的Tensor来创建,此方法会默认重用输入Tensor的一些属性,例如数据类型,除非自定义数据类型。

x = x.new_ones(5,3,dtype = torch.float64) #返回的tensor默认具有相同的torch.dtype和torch.device
print(x)

x = torch.randn_like(x,dtype = torch.float) #指定新的数据类型
print(x)
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
tensor([[-0.1907, -0.1420, -0.6396],
        [-0.9567,  0.4238, -0.6407],
        [ 0.3809,  1.2904,  2.0543],
        [-1.7290, -0.6046, -2.2271],
        [ 3.0284, -0.9338, -0.7631]])

通过shape或者size()来获取Tensor的形状:

print(x.size())
print(x.shape)
torch.Size([5, 3])
torch.Size([5, 3])

注:返回的size是一个tuple,支持所有tuple的操作

更多构造Tensor的函数

函数 功能
Tensor(sizes) 基础构造函数
tensor(data) 类似np.array的构造函数
ones(sizes) 全1Tensor
zeros(size) 全0Tensor
eye(size) 对角线为1,其它为0
arange(s,e,step) 从s到e,步长为step
linspace(s,e,steps) 从s到e,均匀切分成steps份
rand/randn(sizes) 均匀/标准分布
normal(mean,std)/uniform(from,to) 正态分布/均匀分布
randperm(m) 随机排列

这些创建方法都可以在创建的时候指定数据类型dtype和存放device(cpu/gpu)

2.2.2 操作

介绍Tensor的各种操作

算术操作

在PyTorch中,同一种操作可能有很多种形式,下面用加法作为例子。

#加法形式一
y = torch.rand(5,3)
print(x)
print(x+y)
tensor([[-0.1907, -0.1420, -0.6396],
        [-0.9567,  0.4238, -0.6407],
        [ 0.3809,  1.2904,  2.0543],
        [-1.7290, -0.6046, -2.2271],
        [ 3.0284, -0.9338, -0.7631]])
tensor([[ 0.2407,  0.4575,  0.2434],
        [-0.7171,  0.7032,  0.2253],
        [ 0.8284,  2.2123,  2.3685],
        [-0.8832,  0.1701, -1.6400],
        [ 3.5063, -0.2672, -0.0373]])
#加法形式二
print(torch.add(x,y))
tensor([[ 0.2407,  0.4575,  0.2434],
        [-0.7171,  0.7032,  0.2253],
        [ 0.8284,  2.2123,  2.3685],
        [-0.8832,  0.1701, -1.6400],
        [ 3.5063, -0.2672, -0.0373]])
#指定输出
result = torch.empty(5,3)
print(result)
torch.add(x,y,out=result)
print(result)
tensor([[4.1327e-39, 8.9082e-39, 9.8265e-39],
        [9.4592e-39, 1.0561e-38, 1.0929e-38],
        [1.0102e-38, 4.5918e-39, 1.0561e-38],
        [1.0561e-38, 1.0561e-38, 1.0745e-38],
        [1.0561e-38, 8.7245e-39, 9.6429e-39]])
tensor([[ 0.2407,  0.4575,  0.2434],
        [-0.7171,  0.7032,  0.2253],
        [ 0.8284,  2.2123,  2.3685],
        [-0.8832,  0.1701, -1.6400],
        [ 3.5063, -0.2672, -0.0373]])
# 加法形式三、inplace
# adds x to y
print(y)
y.add_(x)
print(y)
tensor([[0.4314, 0.5995, 0.8830],
        [0.2396, 0.2793, 0.8660],
        [0.4475, 0.9219, 0.3143],
        [0.8458, 0.7747, 0.5871],
        [0.4779, 0.6666, 0.7258]])
tensor([[ 0.2407,  0.4575,  0.2434],
        [-0.7171,  0.7032,  0.2253],
        [ 0.8284,  2.2123,  2.3685],
        [-0.8832,  0.1701, -1.6400],
        [ 3.5063, -0.2672, -0.0373]])

索引

使用索引访问Tensor的一部分

注:索引出来的结果与原数据共享内存,修改一个,另一个也会跟着修改。

y = x[0, : ]
y += 1
print(x)
print(y)
print(x[0,:]) #源tensor也被修改
tensor([[ 2.8093,  2.8580,  2.3604],
        [-0.9567,  0.4238, -0.6407],
        [ 0.3809,  1.2904,  2.0543],
        [-1.7290, -0.6046, -2.2271],
        [ 3.0284, -0.9338, -0.7631]])
tensor([2.8093, 2.8580, 2.3604])
tensor([2.8093, 2.8580, 2.3604])

更高级的选择数据函数:

image

改变形状

view()来改变Tensor的形状

y = x.view(15)
z = x.view(-1,5) # -1所指的维度是x的最后一个维度值
k = x.view(5,-1)
print(x.size(),y.size(),z.size(),k.size())
torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5]) torch.Size([5, 3])

注: view()返回的新tensor与源tensor共享内存(其实是同一个tensor),也即更改其中的一个,另一个也会跟着改变。(顾名思义,view仅仅是改变了对张量的观察角度)

x +=1
print(x)
print(y)
tensor([[1.9341, 1.5139, 1.7338],
        [1.8216, 1.9009, 1.5679],
        [1.7110, 1.5658, 1.8897],
        [1.5174, 1.1536, 1.6354],
        [1.6097, 1.7780, 1.3942]])
tensor([1.9341, 1.5139, 1.7338, 1.8216, 1.9009, 1.5679, 1.7110, 1.5658, 1.8897,
        1.5174, 1.1536, 1.6354, 1.6097, 1.7780, 1.3942])

如果我们想返回一个真正新的副本(即不共享内容)

应当使用reshape()改变形状

但是此函数并不能保证返回的是其拷贝,所以不推荐使用

推荐使用clone先创造一个副本然后再使用view

x_cp = x.clone().view(15)
x -= 1
print(x)
print(x_cp)
tensor([[0.9341, 0.5139, 0.7338],
        [0.8216, 0.9009, 0.5679],
        [0.7110, 0.5658, 0.8897],
        [0.5174, 0.1536, 0.6354],
        [0.6097, 0.7780, 0.3942]])
tensor([1.9341, 1.5139, 1.7338, 1.8216, 1.9009, 1.5679, 1.7110, 1.5658, 1.8897,
        1.5174, 1.1536, 1.6354, 1.6097, 1.7780, 1.3942])

注:使用clone还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor

另外一个常用的函数就是item(),它可以将一个标量Tensor转换成一个Python number:

x = torch.randn(1)
print(x)
print(x.item())
tensor([0.2996])
0.2996445298194885

线性代数

PyTorch 还支持一些线性函数,这里提一下,免得用起来的时候自己造轮子。

image

2.2.3 广播机制

当两个形状不同的Tensor按元素运算时,可能会触发广播broadcasting机制:先适当复制元素使这两个Tensor形状相同后再按元素运算。例如:

x = torch.arange(1,3).view(1,2)
print(x)
y = torch.arange(1,4).view(3,1)
print(y)
print(x+y)
tensor([[1, 2]])
tensor([[1],
        [2],
        [3]])
tensor([[2, 3],
        [3, 4],
        [4, 5]])

x中第一行的2个元素被广播到了第二行和第三行,而y中第一列的3个元素被广播到了第二列。

2.2.4 运算的内存开销

索引、view不会开辟内存,y = x + y这样的运算会新开内存,然后将y指向新内存。

为了验证,我们使用Python自带的id函数:如果两个实例的ID一致,那么它们所对应的内存地址相同,反之不同

x = torch.tensor([1,2])
y = torch.tensor([3,4])
id_before = id(y)
y = y + x
print(id(y) == id_before) 
False

通过索引的替换操作指定结果到原y的内存

例如:将x+y的结果通过[:]写进y对应的内存中。

x = torch.tensor([1,2])
y = torch.tensor([3,4])
id_before = id(y)
y[:] = y+x
print(id(y) == id_before)
tensor([1, 2])
tensor([3, 4])
True

我们还可以使用运算符全名函数中out参数或者自加运算符+=(也即add_())达到上述效果,例如torch.add(x,y,out = y)y += x(y.add_(x))。

x = torch.tensor([1,2])
y = torch.tensor([3,4])
id_before = id(y)
torch.add(x,y,out =y ) # y += x,y.add_(x)
print(id(y) == id_before)
True

2.2.5 Tensor和Numpy相互转换

我们很容易用numpy()from_numpy()Tensor和Numpy中的数据相互转换。但是需要注意的一点是:这两个函数所产生的Tensor和Numpy中的数组共享相同的内存(所以他们之间的转换很快),改变其中一个时另一个也会改变!!!

注:常用将Numpy中的array转换成Tensor的方法就是torch.tensor()。此方法总是会进行数据拷贝(消耗更多的时间和空间),所以返回的Tensor和原来的数据不再共享内存。

Tensor转Numpy

使用numpy()Tensor转换成Numpy数组

a = torch.ones(5)
b = a.numpy()
print(a,b)

a += 1
print(a,b)
b += 1
print(a,b)
tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]

Numpy 数组转Tensor

使用from_numpy将numpy数组转换成Tensor

import numpy as np

a = np.ones(5)
b = torch.from_numpy(a)
print(a,b)

a += 1
print(a,b)
b += 1
print(a,b)
[1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
[3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)

所有在CPU上的Tensor(除了CharTensor)都支持与Numpy数组相互转换

直接用torch.tensor()将Numpy转换成Tensor,需要注意的是该方法总是会进行数据拷贝,返回的Tensor和原来的数据不再共享内存

c = torch.tensor(a)
a += 1
print(a,c)
[4. 4. 4. 4. 4.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)

2.2.6 Tensor ON GPU

用方法to()可以将Tensor在CPU和GPU之间相互移动

#以下代码只有在PyTorch GPU版本才会执行
if torch.cuda.is_available():
    device = torch.device('cuda') #GPU
    y = torch.ones_like(x,device=device) #直接创建一个在GPU上的Tensor
    x = x.to(device) #等价于 .to('cuda')
    z = x+y
    print(z)
    print(z.to('cpu',torch.double)) #to()还可以同时更改数据类型
tensor([2, 3], device='cuda:0')
tensor([2., 3.], dtype=torch.float64)
posted @ 2021-05-13 15:39  Coverpast  阅读(105)  评论(0编辑  收藏  举报