DataWhale 10月组队学习-深入浅出PyTorch task2_学习记录

task 2

Tensor 张量

这个名词还是听陌生的,第一次听,不知道是一个怎么样的东西,脑子能关联到的就是向量了。在机器学习里,接触的最多的是向量和矩阵,而它们其实都可以称为张量。

向量,是一维的;矩阵,是二维的…张量似乎就是各种维度的矩阵的一个统称,但我们通常需要处理的数据有零维的(单纯的一个数字)、一维的(数组)、二维的(矩阵)、三维的(空间矩阵)、还有好多好多维的。Pytorch为了把这些各种维统一起来,所以就起名为Tensor——张量。

如果之前有学习过NumPy,那现在的Tensor和它不能说是很相似,只能说是差不多一模一样。但是Tensor提供GPU计算和自动求梯度等更多的功能,所以Tensor 这一数据类型更加适合深度学习。

详细的知识点:torch.Tensor · Pytorch 中文文档 (apachecn.org)

构建tensor的方法

这里直接copy学习文章里的表格了,我觉得整理的挺好的嘿嘿…

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

我们先构建一个tensor

#导入库
import torch

x = torch.rand(4, 3)
print(x)
#运行结果
tensor([[0.0547, 0.0828, 0.5455],
        [0.4604, 0.3901, 0.6721],
        [0.5270, 0.2673, 0.1895],
        [0.3011, 0.2195, 0.7536]])

基本操作

查看tensor大小

size()

print(x.size())
#运行结果
torch.Size([4, 3])

shape()

print(x.shape)
#运行结果
torch.Size([4, 3])

tensor转list

tolist()

l = x.tolist()
print(type(x))
print(type(l))
print(x)
print(l)
#运行结果
<class 'torch.Tensor'>
<class 'list'>
tensor([[0.0547, 0.0828, 0.5455],
        [0.4604, 0.3901, 0.6721],
        [0.5270, 0.2673, 0.1895],
        [0.3011, 0.2195, 0.7536]])
[[0.05465167760848999, 0.08277982473373413, 0.5455439686775208], [0.4604085683822632, 0.39012467861175537, 0.6720585227012634], [0.5270071625709534, 0.26727789640426636, 0.1895279884338379], [0.3011465072631836, 0.2194947600364685, 0.7536421418190002]]

tolist()并不是直接将x从tensor转化为list,而是返回了一个list,通过上面的代码可以看到变量l经过赋值后是list类型数据,但是依旧是tensor类型。

计算tensor中元素总个数

numel()

x.numel()
#运行结果
12

调整tensor形状

view()

print(x.view())
print(x)
#运行结果
tensor([[0.0547, 0.0828, 0.5455, 0.4604],
        [0.3901, 0.6721, 0.5270, 0.2673],
        [0.1895, 0.3011, 0.2195, 0.7536]])
tensor([[0.0547, 0.0828, 0.5455],
        [0.4604, 0.3901, 0.6721],
        [0.5270, 0.2673, 0.1895],
        [0.3011, 0.2195, 0.7536]])

view()仅仅是对它展示的形状做出改变,按照参数设定的形状进行呈现,tensor本身是不会发生改变的。

resize()

print(x.resize())
print(x)
#运行结果
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
        [0.0433, 0.8799, 0.3003, 0.4035],
        [0.3473, 0.6184, 0.8061, 0.7837]])
tensor([[0.2210, 0.1887, 0.5769],
        [0.3312, 0.0433, 0.8799],
        [0.3003, 0.4035, 0.3473],
        [0.6184, 0.8061, 0.7837]])

这里可以发现x本身的形状还是没发生改变,和view()一样,但是resize()view()多了一个功能就是能对数据本身修改,只需要在后面加多'_'就相当于平时的inplace = True,差不多一个意思。

print(x.resize_())
print(x)
#运行结果
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
        [0.0433, 0.8799, 0.3003, 0.4035],
        [0.3473, 0.6184, 0.8061, 0.7837]])
tensor([[0.2210, 0.1887, 0.5769, 0.3312],
        [0.0433, 0.8799, 0.3003, 0.4035],
        [0.3473, 0.6184, 0.8061, 0.7837]])

经过resize_()操作后x本身也已经被重新设置了形状。

增减维度

unsqueeze() 扩展维度

  • 作用:返回一个新的张量,对输入的既定位置插入维度1

  • 注意:返回张量与输入张量共享内存,所以改变其中一个的内容会改变另外一个

  • 参数:

    • tensor(Tensor): 输入张量
    • dim(int):插入维度的索引
    • out(Tensor,optional):结果张量
#构建一个tensor
x = torch.Tensor([1, 2, 3, 4])
print(x)
print(x.size())
#运行结果
tensor([1., 2., 3., 4.])
torch.Size([4])

现在试图对该tensor进行维度的扩展,目前是一维,扩展成二维可以是[1,4]或者是[4,1]。

#[1,4]
print(torch.unsqueeze(x, 0))
print(torch.unsqueeze(x, 0).size())
#运行结果
tensor([[1., 2., 3., 4.]])
torch.Size([1, 4])

可以发现现在tensor已经变成二维了,所得的结果就是相对于在原数据最外围加多了一层[ ],此时size就是[1,4]。单从size来看的话,类似于list的插入操作,在指定位置插入1,所以如果想扩展成[4,1]只需要将索引dim改为1即可。

#[4,1]
print(torch.unsqueeze(x, 1))
print(torch.unsqueeze(x, 1).size())
#运行结果
tensor([[1.],
        [2.],
        [3.],
        [4.]])
torch.Size([4, 1])

但以上操作都并不会对数据本身做出修改,如果要在原数据上动手脚,则需要在后面加多一个'_',变成unsqueeze_()

squeeze() 压缩维度

  • 作用:将输入张量形状中的1 去除并返回
  • 注意:返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个
  • 参数:
    • input(Tensor):输入张量
    • dim(int,optional):如果给定,则input只会在给定的维度压缩
    • out(Tensor,optional):输出张量

之所以去掉张量形状中的维度1,是因为它仅仅只能起到扩充维度的作用,而不会有其他的用途,为了加快计算,是可以去掉这些1的维度。

#构建一个tensor
m = torch.zeros(2, 1, 2, 1, 2)
print(m.size())
#运行结果
torch.Size([2, 1, 2, 1, 2])

目前tensor的形状是[2, 1, 2, 1, 2],接下来对所有1的维度进行压缩。

n = torch.squeeze(m)
print(n.size())
#运行结果
torch.Size([2, 2, 2])

也可以指定索引进行维度压缩。

n = torch.squeeze(m, 1)
print(n.size())
#运行结果
torch.Size([2, 2, 1, 2])

详细知识请参考:torch.unsqueeze() 和 torch.squeeze() - 知乎 (zhihu.com)

tensor数据类型

Tensor有不同的数据类型,如表3-3所示,每种类型分别对应有CPU和GPU版本(HalfTensor除外)。默认的tensor是FloatTensor,可通过 t.set_default_tensor_type 来修改默认tensor类型(如果默认类型为GPU tensor,则所有操作都将在GPU上进行)。Tensor的类型对分析内存占用很有帮助。例如对于一个size为(1000, 1000, 1000)的FloatTensor,它有 1000*1000*1000=10^9 个元素,每个元素占32bit/8 = 4Byte内存,所以共占大约4GB内存/显存。HalfTensor是专门为GPU版本设计的,同样的元素个数,显存占用只有FloatTensor的一半,所以可以极大缓解GPU显存不足的问题,但由于HalfTensor所能表示的数值大小和精度有限1,所以可能出现溢出等问题.

Data type dtype CPU tensor GPU tensor
32-bit floating point torch.float32 or torch.float torch.FloatTensor torch.cuda.FloatTensor
64-bit floating point torch.float64or torch.double torch.DoubleTensor torch.cuda.DoubleTensor
16-bit floating point torch.float16 or torch.half torch.HalfTensor torch.cuda.HalfTensor
8-bit integer (unsigned) torch.uint8 torch.ByteTensor torch.cuda.ByteTensor
8-bit integer (signed) torch.int8 torch.CharTensor torch.cuda.CharTensor
16-bit integer (signed) torch.int16 or torch.short torch.ShortTensor torch.cuda.ShortTensor
32-bit integer (signed) torch.int32 or torch.int torch.IntTensor torch.cuda.IntTensor
64-bit integer (signed) torch.int64 or torch.long torch.LongTensor torch.cuda.LongTensor

各数据类型之间可以互相转换,type(new_type)是通用的做法,同时还有float、long、half等快捷方法。CPU tensor与GPU tensor之间的互相转换通过tensor.cudatensor.cpu方法实现,此外还可以使用tensor.to(device)。Tensor还有一个new方法,用法与t.Tensor一样,会调用该tensor对应类型的构造函数,生成与当前tensor类型一致的tensor。torch.*_like(tensora) 可以生成和tensora拥有同样属性(类型,形状,cpu/gpu)的新tensor。 tensor.new_*(new_shape) 新建一个不同形状的tensor。
参考链接:PyTorch学习之路(五)Tensor操作及其常用函数_一听音频技术-CSDN博客_pytorch tensor函数

autograd 自动求导

torch.autograd是 PyTorch 的自动差分引擎,可为神经网络训练提供支持。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。

requires_grad

在构建tensor时,设置参数 requires_grad = True 后,该tensor所有的操作都会被记录下来。完成计算之后通过调用backward()便可以计算所有的梯度,并自动累加到grad属性中。

#一般创建tensor
x = torch.tensor([1.0,2.0])
print(x)
#运行结果
tensor([1., 2.])
#设置requires_grad=True的tensor创建
x = torch.tensor([1.0,2.0],requires_grad=True)
print(x)
#运行结果
tensor([1., 2.], requires_grad=True)

从输出结果可以看到,tensor里边多了个requires_grad=True,此时代表着该张量其计算历史会被追踪了。

接下来进行简单的计算看看。

y = (x + 2)**2
print(y)
#运行结果
tensor([ 9., 16.], grad_fn=<PowBackward0>)

可以看到此时计算结果y有grad_fn属性,用于指导反向传播。

backward()

首先简单了解一下正向传播和反向传播

  • 正向传播:在训练神经网络中,神经网络对正确的输出进行最佳猜测。
  • 反向传播:在训练神经网络中,神经网络根据其猜测中的误差调整其参数。 它通过从输出向后遍历,收集有关函数参数(梯度)的误差导数并使用梯度下降来优化参数来实现。

我在网上看到有人举过一个例子,就像是在3个人在玩你画我猜,告诉A一个东西,让A画出来给B看,B再把自己看到的画出来给C看,最终C来根据B展现的结果来猜测一开始所给出的东西是什么,这个就相对于是正向传播;而反向传播就相对于C得知正确答案之后,再告诉前面的人哪里还需要优化,哪里没有表达清楚需要进行修改…(我找不到那个人原来的比方了,我自己瞎说的纯属是)

反向传播就这样介绍到这里,因为多的我也不知道了。

接下来看看Autograd是怎么个收集梯度。我们在之前的得到的结果y上调用.backward()Autograd将计算这些梯度并将其存储在各个张量(比如说我的x)的.grad属性中。

y.backward()
print(x.grad)
#运行结果
RuntimeError: grad can be implicitly created only for scalar outputs

恭喜我这个b报错了!报错提示我们输出不是一个标量,确实,一维张量x经过刚刚的计算得到的y也还是一个一维的张量,面向百度搜了一下说:如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数,但是如果它有更多的元素,则需要指定一个 gradient 参数,该参数是形状匹配的张量。

  • 注意:在 backward() 时,如果 y 是标量,则不需要为 backward() 传入任何参数;否则,需要传入一个与 y 同形的Tensor(我还不懂为什么)

我传个参还不行吗,这就传

y.backward(torch.tensor([1,1]))
print(x.grad)
#运行结果
tensor([6., 8.])

参考链接:

1.Autograd看这一篇就够了!_逐梦er的博客-CSDN博客_autograd

2.pytorch中backward()函数详解_Camlin_Z的博客-CSDN博客_backward()

3.Pytorch中的backward函数 - 知乎 (zhihu.com)

4.torch.autograd的简要介绍 (apachecn.org)

5.grad can be implicitly created only for scalar outputs_溪c的博客-CSDN博客

你可能感兴趣的:(pytorch,pytorch,深度学习,python)