当前位置: 首页 > news >正文

第五章 Pytorch完成线性回归

第一章 深度学习和神经网络

第二章 Pytorch安装

第三章 PyTorch的使用

第四章 梯度下降&反向传播

第五章 Pytorch完成线性回归

  • 1向前计算
    • 1.1 计算过程
    • 1.2 requires_grad和grad_fn
  • 2 梯度计算
  • 3. 线性回归实现
    • 手动
    • 内置的api
      • nn.Module
      • 优化器类
      • 损失函数
      • 把线性回归完整代码
    • 在GPU上运行代码
    • 常见的优化算法介绍
      • 梯度下降算法(batch gradient descent BGD)
      • 随机梯度下降法 (Stochastic gradient descent SGD)
      • 小批量梯度下降 (Mini-batch gradient descent MBGD)
      • 动量法
      • AdaGrad
      • RMSProp
      • Adam
      • 效果演示:


1向前计算

对于pytorch中的一个tensor,如果设置它的属性 .requires_gradTrue,那么它将会追踪(保存 / 记录)对于该张量的所有操作。或者可以理解为,这个tensor是一个参数,后续会被计算梯度,更新该参数。

1.1 计算过程

假设有以下条件(1/4表示求均值,xi中有4个数),使用torch完成其向前计算的过程
o = 1 4 ∑ i z i z i = 3 ( x i + 2 ) 2 其中 : z i ∣ x i = 1 = 27 \begin{align*} &o = \frac{1}{4}\sum_iz_i \\ &z_i = 3(x_i+2)^2\\ 其中:&\\ &z_i|_{x_i=1}=27\\ \end{align*} 其中:o=41izizi=3(xi+2)2zixi=1=27
如果x为参数,需要对其进行梯度的计算和更新

那么,在最开始随机设置x的值的过程中,需要设置他的requires_grad属性为True,其默认值为False

import torch
x = torch.ones(2, 2, requires_grad=True)  #初始化参数x并设置requires_grad=True用来追踪其计算历史
print("x: ",x)
#tensor([[1., 1.],
#        [1., 1.]], requires_grad=True)y = x+2
print("y: ",y)
#tensor([[3., 3.],
#        [3., 3.]], grad_fn=<AddBackward0>)z = y*y*3  #平方x3
print("z: ",z)
#tensor([[27., 27.],
#        [27., 27.]], grad_fn=<MulBackward0>)out = z.mean() #求均值
print(out)
#tensor(27., grad_fn=<MeanBackward0>)

从上述代码可以看出:

  1. x的requires_grad属性为True
  2. 之后的每次计算都会修改其grad_fn属性,用来记录做过的操作
    1. 通过这个函数和grad_fn能够组成一个和前一小节类似的计算图

1.2 requires_grad和grad_fn

import torch
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)  #False
a.requires_grad_(True)  #就地修改
print(a.requires_grad)  #True
b = (a * a).sum()
print(b.grad_fn) # <SumBackward0 object at 0x0000028C82C1A910>
with torch.no_grad():c = (a * a).sum()  #tensor(151.6830),此时c没有gard_fnprint(c.requires_grad) #False

注意:

为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。

2 梯度计算

对于1.1 计算过程中的out(out = z.mean()的输出)而言,我们可以使用backward方法来进行反向传播,计算梯度

out.backward(),此时便能够求出导数 d o u t d x \frac{d out}{dx} dxdout,调用x.grad能够获取导数值

import torch
x = torch.ones(2, 2, requires_grad=True)  #初始化参数x并设置requires_grad=True用来追踪其计算历史
#tensor([[1., 1.],
#        [1., 1.]], requires_grad=True)y = x+2
#tensor([[3., 3.],
#        [3., 3.]], grad_fn=<AddBackward0>)z = y*y*3  #平方x3
#tensor([[27., 27.],
#        [27., 27.]], grad_fn=<MulBackward0>)out = z.mean() #求均值
#tensor(27., grad_fn=<MeanBackward0>)out.backward()
x.grad

得到

tensor([[4.5000, 4.5000],[4.5000, 4.5000]])

因为:(对 x 1 x_1 x1求导)
d ( O ) d ( x i ) = 3 2 ( x i + 2 ) \frac{d(O)}{d(x_i)} = \frac{3}{2}(x_i+2) d(xi)d(O)=23(xi+2)
x i x_i xi等于1时其值为4.5

注意:在输出为一个标量的情况下,我们可以调用输出tensorbackword() 方法,但是在数据是一个向量的时候,调用backward()的时候还需要传入其他参数。

很多时候我们的损失函数都是一个标量,所以这里就不再介绍损失为向量的情况。

loss.backward()就是根据损失函数,对参数(requires_grad=True)的去计算他的梯度,并且把它累加保存到x.grad,此时还并未更新其梯度

所以,每次反向传播之前需要先把梯度置为0之后, 再进行后面的反向传播。

在这里插入图片描述

注意点:

  1. tensor.data:

    • 只是获取它的数值,没有其他的属性

    • 在tensor的require_grad=False,tensor.data和tensor等价

    • require_grad=True时,tensor.data仅仅是获取tensor中的数据,不带grad(梯度)等属性

  2. tensor.numpy():

    • require_grad=True不能够直接转换,需要使用tensor.detach().numpy()
    • 如果你把tensor.data改了之前的tensor也会改(浅拷贝);tensor.detach()把tensor数据深拷贝,转化为ndaraay类型,不会影响原来的tensor。

在这里插入图片描述

3. 线性回归实现

下面,我们使用一个自定义的数据,来使用torch实现一个简单的线性回归

假设我们的基础模型就是y = wx+b,其中w和b均为参数,我们使用y = 3x+0.8来构造数据x、y,所以最后通过模型应该能够得出w和b应该分别接近3和0.8

  1. 准备数据
  2. 计算预测值
  3. 计算损失,把参数的梯度置为0,进行反向传播
  4. 更新参数

手动

import torch
import numpy as np
import matplotlib.pyplot as pltlearning_rate=0.01 # 学习率#1. 准备数据 y = 3x+0.8,准备参数
x = torch.rand([500,1])#所有的值是0到1
y = 3 * x + 0.8# 2. 通过模型计算y_predict
w = torch.rand([1,1],requires_grad=True)
b = torch.tensor(0,requires_grad=True,dtype=torch.float32)# 4.通过循环,反向传播,更新参数
for i in range(500):#3.计算loss损失# 预测值y_predict = torch.matmul(x,w)+b # matmul相乘loss = (y-y_predict).pow(2).mean()# 平方、均值,最后算出 均方误差if w.grad is not None:w.grad.data.zero_()#就地修改,梯度置为0if b.grad is not None:b.grad.data.zero_()#就地修改loss.backward()# 反向传播。backward(),此时便能够求出导数 ,调用.grad能够获取导数值w.data=w.data - learning_rate * w.gradb.data=b.data - learning_rate * b.gradprint("w , b , loss",w.item(),b.item(),loss.item())plt.figure(figsize=(20,8))
plt.scatter(x.numpy().reshape(-1),y.numpy().reshape(-1))
y_predict = torch.matmul(x,w)+b
plt.plot(x.numpy().reshape(-1),y_predict.detach().numpy().reshape(-1),c="r")
plt.show()

在这里插入图片描述

在这里插入图片描述

可知,w和b已经非常接近原来的预设的3和0.8

内置的api

nn.Module

nn.Modultorch.nn提供的一个类,是pytorch中我们自定义网络的一个基类,在这个类中定义了很多有用的方法,让我们在继承这个类定义网络的时候非常简单

当我们自定义网络的时候,有两个方法需要特别注意:

  1. __init__需要调用super方法,继承父类的属性和方法
  2. farward方法必须实现,用来定义我们的网络的向前计算的过程

用前面的y = wx+b的模型举例如下:

from torch import nn
class Lr(nn.Module):def __init__(self):super(Lr, self).__init__()  #继承父类init的参数self.linear = nn.Linear(1, 1) # (输入的形状,输出的形状)得到一个线性模型def forward(self, x):# 需要在里面实现前项计算的一个过程,从x输入到out输出是怎么算的要在这里写好out = self.linear(x)return out

[500,1] 500行1列这个数据特征只有一个,每一列我们称为一个维度,指的是它的一个特征,输入是一输出也是一,输出也是500行一列的一个数据。

注意:

  1. nn.Linear为torch预定义好的线性模型,也被称为全链接层,传入的参数为输入的数量,输出的数量(in_features, out_features),是不算(batch_size的列数)
  2. nn.Module定义了__call__方法,实现的就是调用forward方法,即Lr的实例,能够直接被传入参数调用,实际上调用的是forward方法并传入参数
import torch
x = torch.rand([500,1])#所有的值是0到1
print(x)
# tensor([[1.8897e-01],.......,[7.7962e-01],[5.5589e-01]])# 实例化模型
model = Lr()
# 传入数据,计算结果
predict = model(x)
print("---\n----",predict)
#  tensor([[1.0255],...[1.2606]], grad_fn=<AddmmBackward0>)

优化器类

优化器(optimizer),可以理解为torch为我们封装的用来进行更新参数的方法,比如常见的随机梯度下降(stochastic gradient descent,SGD)

优化器类都是由torch.optim提供的,例如

  1. torch.optim.SGD(参数,学习率)
  2. torch.optim.Adam(参数,学习率)

注意:

  1. 参数可以使用model.parameters()来获取,获取模型中所有requires_grad=True的参数
  2. 优化类的使用方法
    1. 实例化
    2. 所有参数的梯度,将其值置为0
    3. 反向传播计算梯度
    4. 更新参数值

示例如下:

import torch.optim
optimizer = optim.SGD(model.parameters(), lr=1e-3) #1. 实例化
optimizer.zero_grad() #2. 梯度置为0
loss.backward() #3. 计算梯度
optimizer.step()  #4. 更新参数的值

损失函数

前面的例子是一个回归问题,torch中也预测了很多损失函数

  1. 均方误差:nn.MSELoss(),常用于回归问题
  2. 交叉熵损失:nn.CrossEntropyLoss(),常用于分类问题

使用方法:

model = Lr() #1. 实例化模型
criterion = nn.MSELoss() #2. 实例化损失函数
optimizer = optim.SGD(model.parameters(), lr=1e-3) #3. 实例化优化器类
for i in range(100):y_predict = model(x_true) #4. 向前计算预测值loss = criterion(y_true,y_predict) #5. 调用损失函数传入真实值和预测值,得到损失结果optimizer.zero_grad() #5. 当前循环参数梯度置为0loss.backward() #6. 计算梯度optimizer.step()  #7. 更新参数的值

把线性回归完整代码

import torch
from torch import nn
from torch import optim
import numpy as np
from matplotlib import pyplot as plt# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8#2 .定义模型
class Lr(nn.Module):def __init__(self):super(Lr,self).__init__()self.linear = nn.Linear(1,1)def forward(self, x):out = self.linear(x)return out# 2. 实例化模型,loss,和优化器
model = Lr()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
#3. 训练模型
for i in range(30000):out = model(x) #3.1 获取预测值loss = criterion(y,out) #3.2 计算损失optimizer.zero_grad()  #3.3 梯度归零loss.backward() #3.4 计算梯度optimizer.step()  # 3.5 更新梯度if (i+1) % 20 == 0:print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))#4. 模型评估
model.eval() #设置模型为评估模式,即预测模式
predict = model(x)
predict = predict.data.numpy()
plt.scatter(x.data.numpy(),y.data.numpy(),c="r")
plt.plot(x.data.numpy(),predict)
plt.show()

在这里插入图片描述

注意:

model.eval()表示设置模型为评估模式,即预测模式

model.train(mode=True) 表示设置模型为训练模式

在当前的线性回归中,上述并无区别

但是在其他的一些模型中,训练的参数和预测的参数会不相同,到时候就需要具体告诉程序我们是在进行训练还是预测,比如模型中存在DropoutBatchNorm的时候

在GPU上运行代码

当模型太大,或者参数太多的情况下,为了加快训练速度,经常会使用GPU来进行训练

此时我们的代码需要稍作调整:

  1. 判断GPU是否可用torch.cuda.is_available()

    torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    >>device(type='cuda', index=0)  #使用gpu
    >>device(type='cpu') #使用cpu
    
  2. 把模型参数和input数据转化为cuda的支持类型

    model.to(device)
    x_true.to(device)
    
  3. 在GPU上计算结果也为cuda的数据类型,需要转化为numpy或者torch的cpu的tensor类型

    predict = predict.cpu().detach().numpy() 
    

    detach()的效果和data的相似,但是detach()是深拷贝,data是取值,是浅拷贝

修改之后的代码如下:

import torch
from torch import nn
from torch import optim
import numpy as np
from matplotlib import pyplot as plt
import time# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8#2 .定义模型
class Lr(nn.Module):def __init__(self):super(Lr,self).__init__()self.linear = nn.Linear(1,1)def forward(self, x):out = self.linear(x)return out# 2. 实例化模型,loss,和优化器device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x,y = x.to(device),y.to(device)model = Lr().to(device)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)#3. 训练模型
for i in range(300):out = model(x)loss = criterion(y,out)optimizer.zero_grad()loss.backward()optimizer.step()if (i+1) % 20 == 0:print('Epoch[{}/{}], loss: {:.6f}'.format(i,30000,loss.data))#4. 模型评估
model.eval() #
predict = model(x)
predict = predict.cpu().detach().numpy() #转化为numpy数组
plt.scatter(x.cpu().data.numpy(),y.cpu().data.numpy(),c="r")
plt.plot(x.cpu().data.numpy(),predict,)
plt.show()

常见的优化算法介绍

梯度下降算法(batch gradient descent BGD)

每次迭代都需要把所有样本都送入,这样的好处是每次迭代都顾及了全部的样本,做的是全局最优化,但是有可能达到局部最优。

随机梯度下降法 (Stochastic gradient descent SGD)

针对梯度下降算法训练速度过慢的缺点,提出了随机梯度下降算法,随机梯度下降算法算法是从样本中随机抽出一组,训练后按梯度更新一次,然后再抽取一组,再更新一次,在样本量及其大的情况下,可能不用训练完所有的样本就可以获得一个损失值在可接受范围之内的模型了。

torch中的api为:torch.optim.SGD()

小批量梯度下降 (Mini-batch gradient descent MBGD)

SGD相对来说要快很多,但是也有存在问题,由于单个样本的训练可能会带来很多噪声,使得SGD并不是每次迭代都向着整体最优化方向,因此在刚开始训练时可能收敛得很快,但是训练一段时间后就会变得很慢。在此基础上又提出了小批量梯度下降法,它是每次从样本中随机抽取一小批进行训练,而不是一组,这样即保证了效果又保证的速度。

动量法

mini-batch SGD算法虽然这种算法能够带来很好的训练速度,但是在到达最优点的时候并不能够总是真正到达最优点,而是在最优点附近徘徊。

另一个缺点就是mini-batch SGD需要我们挑选一个合适的学习率,当我们采用小的学习率的时候,会导致网络在训练的时候收敛太慢;当我们采用大的学习率的时候,会导致在训练过程中优化的幅度跳过函数的范围,也就是可能跳过最优点。我们所希望的仅仅是网络在优化的时候网络的损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。

所以Momentum优化器刚好可以解决我们所面临的问题,它主要是基于梯度的移动指数加权平均,对网络的梯度进行平滑处理的,让梯度的摆动幅度变得更小。
g r a d e n t = 0.8 ∇ w + 0.2 h i s t o r y _ g r a d e n t , ∇ w 表示当前一次的梯度 w = w − α ∗ g r a d e n t , α 表示学习率 \begin{align*} &gradent = 0.8\nabla w + 0.2 history\_gradent &,\nabla w 表示当前一次的梯度\\ &w = w - \alpha* gradent &,\alpha表示学习率 \end{align*} gradent=0.8∇w+0.2history_gradentw=wαgradentw表示当前一次的梯度α表示学习率

(注:t+1的的histroy_gradent 为第t次的gradent)

AdaGrad

AdaGrad算法就是将每一个参数的每一次迭代的梯度取平方累加后在开方,用全局学习率除以这个数,作为学习率的动态更新,从而达到自适应学习率的效果
g r a d e n t = h i s t o r y _ g r a d e n t + ( ∇ w ) 2 w = w − α g r a d e n t + δ ∇ w , δ 为小常数,为了数值稳定大约设置为 1 0 − 7 \begin{align*} &gradent = history\_gradent + (\nabla w)^2 \\ &w = w - \frac{\alpha}{\sqrt{gradent}+\delta} \nabla w ,&\delta为小常数,为了数值稳定大约设置为10^{-7} \end{align*} gradent=history_gradent+(w)2w=wgradent +δαw,δ为小常数,为了数值稳定大约设置为107

RMSProp

Momentum优化算法中,虽然初步解决了优化中摆动幅度大的问题,为了进一步优化损失函数在更新中存在摆动幅度过大的问题,并且进一步加快函数的收敛速度,RMSProp算法对参数的梯度使用了平方加权平均数。
g r a d e n t = 0.8 ∗ h i s t o r y _ g r a d e n t + 0.2 ∗ ( ∇ w ) 2 w = w − α g r a d e n t + δ ∇ w \begin{align*} & gradent = 0.8*history\_gradent + 0.2*(\nabla w)^2 \\ & w = w - \frac{\alpha}{\sqrt{gradent}+\delta} \nabla w \end{align*} gradent=0.8history_gradent+0.2(w)2w=wgradent +δαw

Adam

Adam(Adaptive Moment Estimation)算法是将Momentum算法和RMSProp算法结合起来使用的一种算法,能够达到防止梯度的摆幅多大,同时还能够加开收敛速度
1. 需要初始化梯度的累积量和平方累积量 v w = 0 , s w = 0 2. 第 t 轮训练中,我们首先可以计算得到 M o m e n t u m 和 R M S P r o p 的参数更新: v w = 0.8 v + 0.2 ∇ w , M o m e n t u m 计算的梯度 s w = 0.8 ∗ s + 0.2 ∗ ( ∇ w ) 2 , R M S P r o p 计算的梯度 3. 对其中的值进行处理后,得到: w = w − α s w + δ v w \begin{align*} & 1. 需要初始化梯度的累积量和平方累积量 \\ & v_w = 0,s_w = 0 \\ & 2. 第 t 轮训练中,我们首先可以计算得到Momentum和RMSProp的参数更新:\\ & v_w = 0.8v + 0.2 \nabla w \qquad,Momentum计算的梯度\\ & s_w = 0.8*s + 0.2*(\nabla w)^2 \qquad,RMSProp计算的梯度\\ & 3. 对其中的值进行处理后,得到:\\ & w = w - \frac{\alpha}{\sqrt{s_w}+\delta} v_w \end{align*} 1.需要初始化梯度的累积量和平方累积量vw=0,sw=02.t轮训练中,我们首先可以计算得到MomentumRMSProp的参数更新:vw=0.8v+0.2∇w,Momentum计算的梯度sw=0.8s+0.2(w)2,RMSProp计算的梯度3.对其中的值进行处理后,得到:w=wsw +δαvw
torch中的api为:torch.optim.Adam()

效果演示:

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 小白入门LLM大模型最牛X教程------上交《动手学大模型应用开发》!
  • 语音测试(一)ffmpeg视频转音频
  • 2024年全国大学生数学建模竞赛(A题) 建模解析|“板凳龙” 闹元宵|小鹿学长带队指引全代码文章与思路
  • OpenHarmony轻松玩转GIF数据渲染
  • 语言的自动类型
  • QueryWrapper中的or和and
  • 磁盘加密工具 | VeraCrypt v1.26.15 绿色版
  • STM32基础篇:RTC × Unix时间戳 × BKP
  • [Deepin] 简单使用 RustDesk 实现远程访问Deepin
  • LeetCode 热题100-70 最小栈
  • [数据库][oracle]ORACLE EXP/IMP的使用详解
  • MySQL数据类型-介绍
  • 服务器安装pytorch-阿里云-centos7
  • springboot启动很慢,加载xml时卡住 或者 {dataSource-1} inited卡住 或者 primary数据库配置错误,很久启动不起来
  • PHP + Redis 实现抽奖算法(ThinkPHP5)
  • [iOS]Core Data浅析一 -- 启用Core Data
  • CSS中外联样式表代表的含义
  • E-HPC支持多队列管理和自动伸缩
  • JAVA_NIO系列——Channel和Buffer详解
  • Redis在Web项目中的应用与实践
  • tweak 支持第三方库
  • Webpack 4 学习01(基础配置)
  • 阿里云购买磁盘后挂载
  • 测试如何在敏捷团队中工作?
  • 高性能JavaScript阅读简记(三)
  • 聚簇索引和非聚簇索引
  • 免费小说阅读小程序
  • 盘点那些不知名却常用的 Git 操作
  • 前端技术周刊 2019-01-14:客户端存储
  • 深入浏览器事件循环的本质
  • 使用 Docker 部署 Spring Boot项目
  • 数据科学 第 3 章 11 字符串处理
  • 提醒我喝水chrome插件开发指南
  • 微信小程序--------语音识别(前端自己也能玩)
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​字​节​一​面​
  • #DBA杂记1
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (23)Linux的软硬连接
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (蓝桥杯每日一题)love
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (推荐)叮当——中文语音对话机器人
  • (一)springboot2.7.6集成activit5.23.0之集成引擎
  • (转)jQuery 基础
  • (转)setTimeout 和 setInterval 的区别
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .net web项目 调用webService
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .Net--CLS,CTS,CLI,BCL,FCL
  • .net分布式压力测试工具(Beetle.DT)
  • .NET开源快速、强大、免费的电子表格组件