pytorch基础

1,047 阅读11分钟

20201211233645357.png

创建tensor

参考链接:Pytorch 入门与提高(1)—创建 Tensor - 掘金 (juejin.cn)

使用列表或者numpy来创建tensor

import torch
import numpy as np
x_data = torch.tensor([1,2,3])
x_data = torch.from_numpy(np.array([1,2,3])) # tensor([1, 2, 3], dtype=torch.int32)
x_data = torch.Tensor([1,2,3,4])
x_data = torch.Tensor(np.array([1,2,3])) # torch.FloatTensor

通过torch.tensor(data)或者torch.Tensor(data)都可以,其中data可以是列表或者numpy创建的array。两者的有三个区别:(参考链接:PyTorch中torch.tensor与torch.Tensor的区别详解 - 云+社区 - 腾讯云 (tencent.com))

  1. torch.tensor(data)创建的数据类型是由data推测过来的,而torch.Tensor(data)是由当前torch的默认的数据类型决定。如上面注释所示。
  2. torch.Tensor(size)可以给定大小size通过Tensor(size)来创建一个张量,其值随机。如
size_Tensor = torch.Tensor(2,3)

output:
tensor([[9.5461e-01, 4.4377e+27, 1.4013e-45],
        [0.0000e+00, 1.4013e-45, 0.0000e+00]])
  1. torch.Tensor(1)创建的是一个张量,torch.tensor(1)创建的是一个标量。
one_data = torch.tensor(1)
one_data_Tensor = torch.Tensor(1)
print(one_data, one_data_Tensor)

output:
tensor(1) tensor([0.])

查看tensor的类型:torch_data.type()

查看张量的shape:torch_data.shape

查看张量的总维度:a.dim()

设置当前默认的张量类型:torch.set_default_tensor_type(torch.DoubleTensor)

创建随机张量

  1. 创建随机tensor : rand(size)
rtensor = torch.rand(2,3)
print(rtensor)

output:
tensor([[0.0979, 0.4639, 0.0337],
        [0.5375, 0.3682, 0.2702]])
  1. 创建整数随机tensor:randint(min, max, size) 其中取值范围为[min,max)
ritensor = torch.randint(0, 10, (2,3))
print(ritensor)

output:
tensor([[6, 9, 9],
        [9, 9, 8]])
  1. 创建正态分布的随机tensor:normal(mean, std) 其中mean和std都是tensor,可以为标量或者为张量。mean和std中的元素个数相同,两两组合表示一个正态分布,从每个分布中取得一个值,组合起来作为最终的输出。
nortensor = torch.normal(mean=torch.tensor([1.,2.,3.]), std=torch.tensor([0.1, 0.2, 0.3]))#都需要为浮点数
print(nortensor)

output:
tensor([1.0137, 2.0991, 3.2177])

除此之外,mean或者std可以为同时为标量或者某一个为标量。当某一个为标量时表示此时所有的输出共享一个std。(参考链接:torch.normal()_sxs11的博客-CSDN博客)

  • 在学习的过程中报了这样一个错误,RuntimeError: “normal_kernel_cpu“ not implemented for ‘Long‘此处表明mean或者std都不能为整型。
  1. 创建正态分布的随机tensor:randn(size) 其中mean=0, std=1.
rntensor = torch.randn(2,3)
print(rntensor)

output:
tensor([[-0.4798,  0.7536, -0.7998],
        [-0.7511,  1.1558, -0.6084]])
  1. 创建随机区间值:randperm(10) 其中输出范围为0~9,共10个数,但是其输出顺序被打乱。很适合用于下标打乱表示。
rptensor = torch.randperm(10)
print(rptensor)

output:
tensor([3, 6, 1, 0, 7, 4, 8, 9, 5, 2])
#举例:
rptensor = torch.randperm(10)
print(rptensor)

a = torch.rand(2,3)
b = torch.rand(2,2)
print(a,"\n", b)
idx = torch.randperm(2)
print(idx)
print(a[idx])
print(b[idx])

output:
tensor([[0.8594, 0.7068, 0.1541],
        [0.4272, 0.6582, 0.2319]]) 
tensor([[0.6648, 0.2729],
        [0.9917, 0.4138]])
        
tensor([0, 1])

tensor([[0.8594, 0.7068, 0.1541],
        [0.4272, 0.6582, 0.2319]])
tensor([0.0000, 4.5000, 9.0000])

创建规律型张量

  1. 创建等距采样的tensor:linspace(min, max, steps) 其中,steps为输出的个数,输出的数据中包含min和max。
lintensor = torch.linspace(0, 10, steps=4)
print(lintensor)

output:
tensor([ 0.0000,  3.3333,  6.6667, 10.0000])
  1. 创建固定步幅的tensor:arange(min, max, step) 其中取值范围为[min,max),step为步幅。
artensor = torch.arange(0, 11, 2)
print(artensor)

output:
tensor([0, 2, 4, 6, 8])

创建填充型tensor

全为x:torch.full(size, x)

全为1:torch.ones(size)

全为0:torch.zeros(size)

对角线为1:torch.eye(size)

tensor切片

参考链接:Pytorch 入门与提高(2)—通过 slice 来创建 Tensor - 掘金 (juejin.cn)

张量理解:

创建一批图像张量:

a = torch.rand(4,3,28,28)

其中张量表示(B×C×H×W)。

使用:获取张量切片

在某个维度上设定一个区间,其中区间是左闭右开[start,end),不需要设定区间的维度可以使用:来代替,如果是有多个可以使用...来代替。

对于[start,end)的要求,左闭右开区间,其中start或者end都可以为负值,表示从最后一个开始,-1表示最后一个,-2表示倒数第二个。

此外,[start,end)还可以设定步幅,按照步幅选择数值。

print(a[:2,...].shape) # (2,3,28,28)
print(a[1:,...].shape) #(3,3,28,28)
print(a[0, 0,...].shape) #(28,28) 维度在边上,就不保留了
print(a[:2, :-2,...].shape) #(2, 1, 28,28)

output:
torch.Size([4, 3, 28, 28])
torch.Size([2, 3, 28, 28])
torch.Size([3, 3, 28, 28])
torch.Size([28, 28])
torch.Size([2, 1, 28, 28])

注意:第三个获取切片的方式,如果边上的维度上只取一个,那么这个维度在切片tensor中不被保留。

在指定维度上选择某些位置的切片

torch_data.index_select(dim, position)

如下代码,在dim=2(从dim=0开始)上选择pos=1,2,3,4位置上的切片.

b = a.index_select(2, torch.tensor([0,1,2,3,4]))
print(b.shape)

output:
torch.Size([4, 3, 5, 28])

筛选tensor并创建新tensor

torch_data.ge(other) 大于等于

torch_data.mask_select(mask)

注意:mask_select()将为true位置的数组合为一维向量输出,不能够保持原有的形状。

a = torch.randint(0, 10, (3,3))
b = a>=5
print(a)
print(b)

c = a.ge(5)# 其效果等价于a>=5
print(c)

output:
tensor([[7, 8, 1],
        [1, 0, 7],
        [8, 8, 0]])
tensor([[ True,  True, False],
        [False, False,  True],
        [ True,  True, False]])
tensor([[ True,  True, False],
        [False, False,  True],
        [ True,  True, False]])
out = a.masked_select(c)
print(out)

output:
tensor([9, 9, 8, 8, 6, 6])

tensor的维度变换

参考链接:Pytorch 入门与提高(3)—tensor 的 reshape 操作 - 掘金 (juejin.cn)

读到作者的第三篇博客,作者已经收到了掘金的搪瓷杯了。我也想......

  1. 改变形状:reshape、view
  2. 增删维度:squeeze、unsqueeze
  3. 维度变换:transpose、permute
  4. 维度扩展:expand、repeat
  5. 数据拷贝:clone、detach

改变形状view和reshape

a = torch.arange(27).reshape((3,3,3))
b = a.view(9,3)
print(a.shape)
print(b.shape)

output:
    torch.Size([3, 3, 3])
    torch.Size([9, 3])

view和shape的区别

参考链接:PyTorch:view() 与 reshape() 区别详解_Flag_ing的博客-CSDN博客

torch的view()与reshape()方法都可以用来重塑tensor的shape,区别就是使用的条件不一样。view()方法只适用于满足连续性条件的tensor,并且该操作不会开辟新的内存空间,只是产生了对原存储空间的一个新别称和引用,返回值是视图。而reshape()方法的返回值既可以是视图,也可以是副本,当满足连续性条件时返回view,否则返回副本[ 此时等价于先调用contiguous()方法在使用view() ]。因此当不确能否使用view时,可以使用reshape。如果只是想简单地重塑一个tensor的shape,那么就是用reshape,但是如果需要考虑内存的开销而且要确保重塑后的tensor与之前的tensor共享存储空间,那就使用view()。

  1. tensor存储方式 tensor存储包含信息区和存储区,在直接等号赋值(x1=x0)或者获取切片(x1=x0[1:])或者转置时都指向同一片存储区,发生变化的只是对数据索引而已。
  1. 数据存储的连续性问题 原张量x0是的数据在内存中是连续保存的,经过转置后得到的新张量x1,其与x0都指向同一片数据存储区,只不过对数据的访问方式发生了变化。在x0中访问第一行时stride=1,但是经过转置后的x1中,访问第一行数据stride=x0的列数,因为其要跳着去访问数据。
  1. view和reshape

view是视图的意思,其概念有点类似mysql中的感觉。其在使用时,不重新开辟空间建立张量,而只是改变访问方式。其只能针对连续性访问的张量起作用,而对于不连续的张量如转置后的会报错:

RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.

当不满足连续性条件时,可以先使用函数 .contiguous() 将其变为连续而后再执行view()。


reshape 在张量是连续性时,其直接返回view的操作结果,如果是非连续性的,其先执行.contiguous()而后执行view(),此时会重新开辟空间。

具体的实验可以查看原文

增删维度:squeeze和unsqueeze

torch_tensor.unsqueeze(dim)在指定dim上增加一个维度,比如在dim=0增加一个维度,那么就是在张量整体前增加,如果dim=-1,表示在张量整体后加一个维度,还可以在中间某个维度添加。

a = torch.arange(27).reshape((3,3,3))
b = a.unsqueeze(dim=0)
c = a.unsqueeze(dim=1)
d = a.unsqueeze(dim=-1)
print(a.shape)
print(b.shape)
print(c.shape)
print(d.shape)

output:
torch.Size([3, 3, 3])
torch.Size([1, 3, 3, 3])
torch.Size([3, 1, 3, 3])
torch.Size([3, 3, 3, 1])

特别注意的是:在获取切片的时候,维度数量可能会发生变化。特别是获得切片后,dim=0或者dim=-1的维度上,如果位置数为1,那么此维度就会消失。

x = torch.empty((2,3,4,5))
y = x[0,...]
print(x.shape)
print(y.shape)

output:
torch.Size([2, 3, 4, 5])
torch.Size([3, 4, 5]) #注意此处

报错: 在张量已有的维度上插入维度,会让已有的维度都往后窜,或者在张量的最后一个维度后插入维度,其他的地方插入都会报如下错误:

#*===========不报错================
a = torch.arange(4)
b = a.unsqueeze(dim=0).unsqueeze(dim=2)
print(a.shape)
print(b.shape)

output:
torch.Size([4])
torch.Size([1, 4, 1])


#=============报错================
a = torch.arange(27).reshape((3,3,3))
b = a.unsqueeze(dim=10)

output:
Traceback (most recent call last):
  File "D:/08_studyProject/02_pytorchTutorials/Dataset/dataOperator3.py", line 48, in <module>
    b = a.unsqueeze(dim=10)
IndexError: Dimension out of range (expected to be in range of [-4, 3], but got 10)

Process finished with exit code 1

torch_tensor.squeeze(dim)是对指定维度进行压缩,此维度的位置数为1,(位置数是指在shape中的某个维度上的数值)此时可以对其压缩。如果没有参数torch_tensor.squeeze()是对所有的维度上位置数等于1的维度全部压缩。

a = torch.arange(4)
b = a.unsqueeze(dim=0).unsqueeze(dim=2)
c = b.squeeze()
print(a.shape)
print(b.shape)
print(c.shape)

output:
torch.Size([4])
torch.Size([1, 4, 1])
torch.Size([4])

维度变换:transpose和permute

transpose:只能选择tensor中两个维度进行转置,其中两个维度书写顺序无关。

a = torch.arange(9).reshape(3, 3)
b = a.transpose(dim0=0, dim1=1)
print(b)
b = b.transpose(dim0=1, dim1=0)
print(b)

output:
tensor([[0, 3, 6],
        [1, 4, 7],
        [2, 5, 8]])
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])

permute:可以让tensor按照指定维度顺序(维度的个数就是该tensor的维度数)进行转置

a = torch.empty(2,3,4)
b = a.permute((1,0,2))
print(a.shape)
print(b.shape)

output:
torch.Size([2, 3, 4])
torch.Size([3, 2, 4])

维度扩展:expand和repeat

expand(*sizes) -> Tensor. *sizes(torch.Size or int) - the desired expanded size. Returns a new view of the self tensor with singleton dimensions expanded to a larger size.

expand 返回当前张量在某维扩展更大后的张量。扩展(expand)张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view),一个大小(size)等于1的维度扩展到更大的尺寸。如果不需要更改的维度可以使用-1来代替,size是指扩展后的张量的shape。

x = torch.tensor([1, 2, 3]) #shape (3,)
y = x.expand(2, 3)
print(y)

output:
tensor([[1, 2, 3],
        [1, 2, 3]])

repeat(*sizes) -> Tensor. *size(torch.Size or int) - The number of times to repeat this tensor along each dimension. Repeats this tensor along the specified dimensions.

repeat 沿着特定的维度重复这个张量,和expand() 不同的是,这个函数拷贝张量的数据。其中size的含义是沿着对应的维度重复的次数

x = torch.tensor([1, 2, 3])
y = x.repeat(3, 2)
print(y)

output:
tensor([[1, 2, 3, 1, 2, 3],
        [1, 2, 3, 1, 2, 3],
        [1, 2, 3, 1, 2, 3]])

数据拷贝:clone和detach

pytorch中的浅拷贝与深拷贝

参考文章:PyTorch中的拷贝 - 知乎 (zhihu.com)

  1. 浅拷贝的方法 共享内存地址,数据同步变化
函数说明
a.numpy()Tensor->Numpy array
torch.from_numpy()Numpy array->Tensor
.view()改变tensor的形状,但是共享内存,不要使用id直接判断
y=x[:]索引
torch.detach()新tensor会脱离计算图,不会涉及梯度计算
选择函数index_select\masked_select\gather
  1. 深拷贝方法
函数说明
torch.clone()新的tensor会保留在计算图参与梯度计算
torch.Tensor()类构造张量
torch.tensor()工厂函数

clone和detach

参考链接:Pytorch中clone(),copy_(),detach(),.data的辨析与应用 - 知乎 (zhihu.com)

clone 创建一个tensor与源tensor有相同的shape,dtype和device,不共享内存地址,但新tensor的梯度会叠加在源tensor上。

其中进行了计算:

b = a.clone()
c = a*2
d = b*3

此时a和b不共享内存,但是从c-->a以及从d-->b的梯度值都会叠加到a上。也就是从a--->b进行了一个恒等映射。图中蓝色是原数据计算,红色是梯度走向。

detach 函数返回与调用对象tensor相关的一个tensor,此新tensor与源tensor共享数据内存(那么tensor的数据必然是相同的),但其requires_grad为False,并且不包含源tensor的计算图信息。

如果在detach语句后增加一条语句 b.requires_grad(),那么此时pytorch会计算跟踪其有关计算,但是此时梯度不会从b流向a。

clone函数使用时b = a.clone(),b新开辟内存,但是从叶子结点计算到b的梯度也会累加到a上。detach函数使用时b=a.detch(),b不新开辟内存,而是与a共享内存,但是其梯度不会累加到a上,并且b的记录梯度的设置默认为false,当然也可以通过b.requires_grad_()将其设置为true。

clone()可在新内存空间复制原tensor但是梯度信息不独立,detach()可以独立梯度信息,但是与原tensor具有相同的内存,因此常常联合使用二者创建出数据行啊同,完全独立的新tensor。常用的手段是b=a.clone().detach()或者b=a.detach().clone()。