【动手学深度学习】笔记一

时间:2022-07-23
本文章向大家介绍【动手学深度学习】笔记一,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

数据操作

torch.Tensor是存储与变换数据的主要工具。Tensor(张量)是一个多维数组,标量可以看作是0维张量,向量可以看作是1维张量,矩阵可以看作是2维张量。

创建一个Tensor

这部分属实很枯燥

注:dtype是自定义数据类型

函数(生成矩阵的)

实现功能

torch.empty(m,n)

创建一个未初始化的m行n列的张量

torch.tensor([需要创建的数据])

直接创建一个值为 ”需要创建的数据“ 的张量

torch.randn(m,n)

创建一个满足正态分布(0,1)的张量

torch.rand(m,n)

随机生成在(0,1)一个m行n列的张量

torch.ones(m,n)

创建一个全1的m行n列的张量

torch.zeros(m,n,dtype=张量类型)

创建一个符合张量类型的全0m行n列的张量

torch.eye(m,n)

生成一个m行n列的对角线为1,其他为0的张量

函数(生成行向量的)

实现功能

torch.linspace(s,e,steps)

生成一个从s到e,均匀切成steps份的向量

torch.arange(s,e,steps)

生成一个从s到e,步长为steps的向量

torch.randperm(m)

生成一个有m个数的随机向量

对Tensor进行操作

有超过一百种的Tensor的运算操作。

获取行数与列数

获取Tensor的行数与列数

实现功能

name.size()

获取张量的行数和列数,返回一个元组(m,n)

name.shape()

同上

加法运算

加法运算

注释

name1+name2

直接将两个张量进行相加

torch.add(x,y)

y.add_(x)

索引使用 :name[n,m]

使用索引访问Tensor:索引出来的结果与元数据共享内存,改变索引的结果也会改变原数据 。

索引使用

功能说明

name[n,m]

提取出name这个Tensor中的n行m列这个数,注意:索引是从0开始的

name[n,:]

提取出name这个Tensor中的n行的这个向量

改变形状

用view()改变Tensor的形状

判断数据类型

函数

功能

torch.is_tensor(obj)

如果obj是tensor,则返回True

torch.is_storage(obj)

如果obj是存储图像,则返回True

torch.isfloatingpoint(输入)

如果输入的是浮点数据类型,则返回True

函数

功能

torch.setdefaultdtype(d)

设置数据类型

torch.getdefaultdtype()

读取数据类型

Tensor变换行、列

这个view()调用出来的数据和源数据共享,改变一个,另一个也会被一起改变。

函数

功能

name.view(-1,m)

将name这个Tensor转换为m列的张量,行数根据列数自动确定,-1是一种标志

name.view(n,-1)

将name这个Tensor转换为n行的张量,列数根据行数自动确定,-1是一种标志

name.view(x,y)

将name这个m行n列的张量转换为x行y列的张量

因为上面的原因,所以可以用clone克隆一个副本,然后对副本进行变换。

将标量张量转换为普通变量

import torch
x = torch.tensor(1.123456)
y = x.item()
print(x)    #tensor(2.2469)
print(y)    #2.2469120025634766

线性代数

这部分也是对Tensor进行操作。

函数

功能

name1 = torch.trace(name)

求name这个张量的对角线元素之和,然后存储到name1中

name1 = torch.diag(name)

将name这个张量的对角线元素提取出来,然后存储到name1这个行向量中

torch.triu(name,n)

矩阵上三角,只保留上三角的值,其余为0;n的作用是指定向上偏移量,如n=1,则为0的对角线向上平移1一个对角线

torch.tril(name,m)

矩阵下三角,只保留下三角的值,其余为0;n的作用是指定向下偏移量,如n=1,则为0的对角线向下平移1一个对角线

torch.mm(name,name1)

矩阵乘法

name1 = torch.t(name)

对name转置

name1 = torch.inverse(name)

对name求逆矩阵

name1 = torch.svd(name)

对name求奇异值分解

广播( broadcasting )机制

前面的运算都是针对形状相同的Tensor,但当两个形状不同的Tensor进行运算时,会触发广播( broadcasting )机制。

广播机制:先适当复制元素使这两个 Tensor形状相同后再按元素运算。(前提是两个Tensor要满足可以通过复制某些行或列会变成一样形状的;如:[2,4]和[1,4]可以实现广播机制;但[2,3]和[1,4]无法实现)

运算的内存开销

小注释:

  • 索引操作不会新开辟一个内存地址,但y = x+y类的运算会新开辟一个内存地址。
  • 了解内存的地址可以通过函数:id(name) 来确定name的内存地址

如果想指定结果到某一特定地址可以通过:

  1. y[:] = y + x来实现将y存储到原来的地址中(但经我实现,好像不可以在广播机制中实现,只能同形状直接加)
  2. torch.add(x,y,out=y)也可以通过这种方法来实现上述功能
  3. y+=x,y.add_(x)也可以如此实现。

Tensor和NumPy相互转换

通过numpy()和from_numpy()实现将Tensor和NumPy中的数组相互转换。

注意:这两个函数产生的数组共享相同内存,改变其中一个另一个也会转变。

还可以通过 torch.tensor()实现转换,但这种方法会进行数据复制,所以他们不共享内存,改变一个另一个不会改变。

函数

功能

name1 = name.numpy()

将name转换为numpy数组并存储到name1中

name1 = torch.from_numpy(name)

将name转换为Tensor数组并存储到name1中

自动求梯度(autograd)

动手学习深度学习,2.3

2020.1.28,好久没学习,今天开始学习。

深度学习过程中,常需要对函数进行求梯度,PyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播。

### 使用方法

  1. 首先需要将Tensor这个包的属性.requires_grad设置为True,作用是追踪在Tensor上所有的操作。
  2. 调用.backward()来完成所有梯度计算。同时,Tensor的梯度将累计到.grad属性中。

如果被计算对象是标量(只包含一个元素的数据),则不需要为backward()这个函数传入任何参数;否则,需要传入一个与被计算对象同形的Tensor

  1. 如果被计算对象为想部分追踪的,则可以通过.detach()将其从追踪记录中分离出来,调用该函数后,之后的操作将不再被追踪。
  2. 除了3中所述的方法,还可以通过with语句 torch.no_grad()来实现将不想被追踪的代码包裹起来。
  3. 每个Tensor都有一个.grad_fn属性,该属性是Tensor的Function。这个Function是用来记录这个Tensor是不是通过计算得到的,如果是,则存在一个Function;如果不是,则为None。

实例

.grad_fn 的使用。

不进行任何计算的情况
import torch
x = torch.ones(2,2,requires_grad = True)    #创建一个张量,将属性  .requires_grad  设置为True
print(x)    #打印x
print(x.grad_fn)    #打印x的  .grad_fn  属性

输出结果

tensor([[1., 1.],        [1., 1.]], requires_grad=True)        #可以看到整个属性被设置为True
None                                        #因为x没有进行任何计算,所以grad_fn的值为None
加法计算和叶子节点

叶子节点:如x这种直接创建的变量被称为叶子节点,并可以通过 变量名 .is_leaf 来判断是否为叶子节点

import torchx = torch.ones(2,2,requires_grad = True)    #创建一个张量,将属性  .requires_grad  设置为True
y = x + 2
print(y)    #打印y
print(y.grad_fn)    #打印y的  .grad_fn  属性
print(x.is_leaf,y.is_leaf)        #判断变量是否为叶子节点

输出结果

tensor([[3., 3.],                [3., 3.]], grad_fn=<AddBackward0>)    #可以看到属性.grad_fn的值为  AddBackward0<AddBackward0 object at 0x00000214C9878748>    #这是该变量的内存位置
其他种的计算
import torchx = torch.ones(2,2,requires_grad = True)
y = x + 2
z = y*y*3out = z.mean()print(z,out)

输出结果

tensor([[27., 27.],        [27., 27.]], grad_fn=<MulBackward0>)     #可以看到z的grad_fn的值为MulBackward0tensor(27., grad_fn=<MeanBackward0>)            #可以看到z的grad_fn的值为MeanBackward0

自动求梯度的方法

这个他娘的傻逼求梯度的原理就是求导,我没看懂。只知道使用,使锤子吧,不学造轮子了。

传入的结果是标量的情况

不需要传入张量。

import torchx = torch.ones(2,2,requires_grad = True)
y = x + 2
z = y * y * 3
out = z.mean()  #求取均值
out.backward()  #用来设置下面的被求导对象(即:设置out为被求导对象)
print(x.grad)   #返回设置的计算结果对x的梯度向量(即:out对x的梯度向量)

输出结果

tensor([[4.5000, 4.5000],        [4.5000, 4.5000]])        #可以见到,求导的结果是与输入张量同形的

传入的结果不是标量的情况

需要传入一个与结果同形的权重张量。

值得注意的是:输出的结果(x.grad)最后是与输入的值(x)相同的。

import torch
x = torch.tensor([1.0,2.0,3.0,4.0],requires_grad = True)y = x * 2z = y.view(2,2)        #对x进行变换形状
v = torch.tensor([[1.0,0.1],[0.01,0.001]],dtype = torch.float)    #创建一个与结果同形的权重张量。
z.backward(v)    #传入这个权重张量。print(x.grad)

输出结果

tensor([2.0000, 0.2000, 0.0200, 0.0020])    #求导的结果也是与输入张量同形的

中断梯度追踪方法

通过 with语句+torch.no_grad()

import torch
x = torch.tensor(1.0,requires_grad = True)
y1 = x**2
with torch.no_grad():    #对下面语句进行中断梯度追踪    y2 = x**3
y3 = y1+y2
print(x.requires_grad)    print(y1.requires_grad)print(y2.requires_grad)print(y3.requires_grad)        #对四个变量进行判断是否要求梯度

输出

TrueTrueFlaseTrue        #可以见到被隔离的y2变量不需要进行求梯度

被隔离的变量,不能调用 .backward(),否则会报错。

隔离更改数据

使用x.data之后,对变量进行任何更改都不会被autograd记录。