PyTorch深度学习——线性回归、计算图和自动求导机制、损失函数和优化器

一、线性回归

线性回归模型是输入一个特征的张量,做线性变换,输出一个预测张量

为了构造线性变换,需要知道输入特征维度大小,并且知道线性回归的权重和偏置,在forward方法中,输入一个特征张量x(大小为迷你批次×特征维度的大小),做线性变换(使用mm方法做矩阵乘法进行线性变换),加偏置的值,最后输出一个预测的值,并且一开始被初始化,使得每个分量为标准正态分布

另外,需要使用nn.Parameter来包装这些参数,使之成为子模块(仅仅由参数构成的子模块),这是因为在后续训练的时候需要对参数进行优化,只有将张量转化为参数之后才能在后续的优化过程中被优化器访问到

在使用线性回归模型之前,首先要做的就是模型的初始化,初始化的任务由初始化模型的类的实例开始

类的实例化和方法调用

对pytorch的模块,有一些常用的方法可以在训练和预测的时候用

1、调用 named_parameters 方法和 parameter 方法获取模型的参数

通过调用named_parameters 方法和 parameter 方法,返回的是python里面的一个生成器,通过访问生成器的对象得到的是该模型所有参数的名称和对应的张量值,通过调用 parameter 方法,返回的也是一个生成器,访问生成器的结果是该模型的所有参数对应的张量的值。pytorch的优化器直接接受模型生成参数作为函数的参数,并且会根据梯度来优化生成器里的所有张量(需要调用反向传播函数)

2、使用 train 和 eval 方法进行模型训练和测试状态转换

在模型的使用过程中,有些子模块(如丢弃层和批次归一化层等)有两种状态,即训练状态和预测状态,pytorch 的模型经常需要在两种状态中相互转换

通过调用 train 方法会把模块(包含所有的子模块)转换到训练状态,调用 eval 方法则会切换到预测状态,不同的模型预测结果也会有差异,在训练模型的时候要转化为训练状态,预测的时候要转化为预测状态,否则最后的预测模型的准确率可能会降低,甚至得到错误的结果

3、使用 named_buffers 方法或 buffers 方法获取张量的缓存

除通过反向传播得到梯度来进行训练的参数外,还有一些参数并不参与梯度传播,但是会在训练的时候得到更新,这种参数称为缓存,其中具体的例子包括批次归一化的平均值(Mean)和方差(Variance),通过在模块中调用 register_buffers 可以获取缓存的名字和缓存张量的值组成的生成器,通过buffers方法可以获取缓存张量值组成的生成器

4、使用 named_children  方法和 children 方法获取模型的子模块

需要对子模块进行迭代的时候,就需要使用 named_children 方法和 children 方法来获取子模块的名字、子模块的生成器,以及只有子模块的生成器

由于pytorch的模块的构造可以嵌套,所以子模块还有可能有自身的子模块,如果要获取模块中的所有模块的信息,可以使用 named_modules 和 modules 来递归的得到相关的信息

5、使用 apply 方法递归的对子模块进行函数应用

如果需要对pytorch所有的模块应用一个函数,可以使用 apply 方法,通过传入一个函数或者匿名函数(通过 python 的 lambda 关键字定义)来递归地应用这些函数,传入的函数以模块作为参考,在函数的内部对模块进行修改

6、改变模块参数数据类型和存储位置

除对模块进行修改之外,在深度学习模型的构建中还可对参数进行修改,和张量的运算一样,可以改变模块的参数所在的设备(CPU 或 GPU),具体可以通过调用模块自带的cpu方法和cuda方法来实现

另外,如果需要改变参数的数据类型,可以通过调用to方法加上需要转变的目标数据类型来实现(也可使用具体一些的方法:如 float 方法会转换所有的参数为双精度浮点数)

import torch  
import torch.nn as nn  # 定义一个简单的模型  
class SimpleModel(nn.Module):  def __init__(self):  super(SimpleModel, self).__init__()  self.linear = nn.Linear(10, 5)  def forward(self, x):  return self.linear(x)  # 实例化模型  
model = SimpleModel()  # 检查模型当前所在的设备  
print(next(model.parameters()).device)  # 默认情况下,模型参数在CPU上  # 如果有可用的GPU,则将模型移到GPU上  
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  
model = model.to(device)  # 再次检查模型参数所在的设备  
print(next(model.parameters()).device)  # 现在模型参数应该在GPU上(如果有的话)

二、计算图和自动求导机制

pytorch会根据计算过程来自动生成动态图,然后可以根据动态图的创建过程进行反向传播,计算得到每个节点的梯度值,为了能够记录张量的梯度,首先需要在创建张量的时候设置一个参数 requires_grad = True,意味着这个张量将会加入计算图,作为计算图的叶子结点参与运算,通过一系列的计算,最后输出结果张量,也就是根节点

几乎所有的张量的创建方式都可以使用参数 requires_grad = True,一但指定了这个参数,在后续的计算中得到的中间结果的张量都会被设置为 requires_grad = True,对pytorch来说,每个张量都有一个 grad_fn 方法,这个方法包含着创建盖章量的运算的导数信息,在反向传播的过程中,通过传入后一层的神经网络的梯度,该函数会计算出参与运算的所有张量的梯度,grad_fn 方法本身也携带有一个 next_function 属性,包含链接盖章量的其他张量的 grad_fn ,通过不断反向传播回溯中间张量的计算节点,可以得到所有张量的梯度,一个张量的梯度张量的信息保存在该张量的 grad 属性中

除了pytorch张量本身外,pytorch还提供了一个专门用于自动求导的包,即 torch.autograd ,它包含两个重要的参数,即 torch.autograd.backward 函数和 torch.autograd.grad 函数:torch.autograd.backward 函数通过传入根节点张量,以及初始梯度张量(形状和当前的张量相同),可以计算产生该节点所有对应的叶子结点的梯度

当张量为标量张量时(Scalar,即只有一个元素的张量),可以不传入初始梯度张量,默认会设置梯度张量为1,当计算梯度张量的时候,原先建立起来的计算图就会被自动释放,如需要再次做自动求导,因为计算图已经不存在,就会报错,如果要在反向传播的时候,保留计算图(反向传播也是一个计算过程,可以动态创建计算图)

需要反向传播的同时建立和梯度张量相关的计算图(在某些情况下,如需计算高阶导数)可以设置 create_graph = True ,对于一个可求导的张量,也可直接调用该张量内部的 backward 方法来进行自动求导

自动求导机制

import torch  
import torch.nn as nn  # 定义一个3x3的张量  
tensor = torch.randn(3, 3, requires_grad=True)  
print("原始张量:")  
print(tensor)  # 计算张量所有分量的平方和  
sum_of_squares = torch.sum(tensor ** 2)  
print("第一次计算所有分量的平方和:")  
print(sum_of_squares)  # 反向传播,使梯度是原始张量的2倍  
# 首先需要手动设置梯度为原始张量的2倍  
sum_of_squares.backward(gradient=torch.ones_like(sum_of_squares) * 2)  
print("梯度(原始张量的2倍):")  
print(tensor.grad)  # 清零梯度,为下一步做准备  
tensor.grad.zero_()  # 再次计算所有分量的平方和  
# 因为tensor的值没有改变,所以结果应该和之前一样  
sum_of_squares_again = torch.sum(tensor ** 2)  
print("第二次计算所有分量的平方和:")  
print(sum_of_squares_again)  # 步骤5: 再次反向传播,梯度累积  
# 直接调用backward()即可,梯度会自动累积到tensor.grad中  
sum_of_squares_again.backward()  
print("梯度累积后的结果:")  
print(tensor.grad)

需要注意的是:张量绑定的梯度张量在不清空的情况下会逐渐累积,这种特性在某些特殊的情况下是有用的,如需要一次性求很多迷你批次的累计梯度,但在一般的情况下,不需要用到这个特性,所以最后应当注意将梯度清零:单个张量的梯度清零的方法——tensor.grad.zero_() (模块和优化器都有清零参数张量梯度的函数)

梯度函数的使用

某些情况下,不需要求出当前的张量对所有产生该张量的叶子结点的梯度,这时可以使用 tensor.autograd.grad 函数,这个函数的参数是两个张量:第一个张量时计算图的数据结果张量(或张量列表),第二个是需要对计算图求导的张量(或张量列表),最后输出的结果是第一个张量随第二个张量求导的结果(注意最后输出的梯度会累积,和 tensor.grad.backward 函数的行为一样)

需要注意的是,这个函数不会改变叶子结点的 grad 属性,而不像 tensor.grad.backward 函数会在反向传播求导的时候释放计算图,如果需要保留计算图,同样可以设置 retain_graph = True,如果需要反向传播的计算图,可以设置 create_graph = True

另外,如果求导的两个张量在计算图上没有关联,函数会报错,不希望报错的话,可以设置 allow_unused = True 这个参数,结果会返回分量全为0的梯度张量(因为两个张量没有关联,所以求导的梯度为0)

import torch  
import torch.nn as nn  
import torch.autograd.function as Function  # 自定义一个PyTorch Function,用于实现前向传播和反向传播  
class MyCustomFunction(Function):  @staticmethod  def forward(ctx, input):  # 前向传播:对输入执行一些操作,例如取平方  ctx.save_for_backward(input)  # 保存输入,以便在反向传播中使用  output = input ** 2  return output  @staticmethod  def backward(ctx, grad_output):  # 反向传播:计算梯度  input, = ctx.saved_tensors  # 取出之前保存的输入  grad_input = 2 * input * grad_output  # 根据链式法则计算输入的梯度  return grad_input  # 使用自定义Function  
def my_custom_op(input):  return MyCustomFunction.apply(input)  # 创建一个需要计算梯度的张量  
x = torch.tensor([2.0, 3.0], requires_grad=True)  # 执行前向传播  
y = my_custom_op(x)  # 计算y关于x的梯度  
y.backward()  # 输出结果和梯度  
print("y:", y)  
print("x.grad:", x.grad)  # 这将输出x的梯度,它是2 * x,因为我们对x取了平方

计算图的构建和使用

由于计算图的构建需要消耗内存和计算资源,在一些情况下,计算图并不是必要的,比如神经网络的推导,此时可以使用 torch.no_grad 上下文管理器,在这个上下文管理器的作用于进行的神经网络计算不会构建任何的计算图

另外,再对一个张量进行反向传播的时候可能不需要让梯度通过这个张量的节点,也就是新建的计算图要和原来的计算图分理,这种情况可以使用张量的 detach 方法,通过调用这个函数方法,可以返回一个新的张量,该张量会成为一个新的计算图的叶子结点,新的计算图和老的计算图互相分离,互不影响

import torch  
import torch.nn as nn  
import torch.optim as optim  # 定义一个简单的神经网络  
class SimpleNet(nn.Module):  def __init__(self):  super(SimpleNet, self).__init__()  self.fc = nn.Linear(10, 1)  def forward(self, x):  return self.fc(x)  # 初始化网络和优化器  
model = SimpleNet()  
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 创建一些虚拟数据  
inputs = torch.randn(16, 10)  
targets = torch.randn(16, 1)  # 训练阶段:需要计算梯度  
model.train()  
optimizer.zero_grad()  # 清零梯度缓存  
outputs = model(inputs)  # 前向传播  
loss = nn.MSELoss()(outputs, targets)  # 计算损失  
loss.backward()  # 反向传播,计算梯度  
optimizer.step()  # 根据梯度更新权重  # 评估阶段:不需要计算梯度  
model.eval()  
with torch.no_grad():  # 使用相同的输入数据进行评估  outputs_eval = model(inputs)  # 注意:这里不会计算梯度,所以不会更新模型权重  # 可以使用outputs_eval进行预测或计算评估指标  print("Training loss:", loss.item())  
print("Evaluation outputs:", outputs_eval)

三、损失函数和优化器

损失函数

pytorch的损失函数有两种形式:函数形式和模块形式,前者调用的是 torch.nn.functional 库中的函数,通过传入神经网络预测值和目标值来计算损失函数,后者是 torch.nn 库里面的模块,通过新建一个模块的示例,然后调用模块来计算最终的损失函数

由于训练数据一般以迷你批次的形式输入神经网络,最后预测的值也是以迷你批次的形式输出的,而损失函数最后的输出结果应当是一个标量张量,因此对于迷你批次的归约一般有两种方法:一是对迷你批次的损失函数求和,二是对迷你批次的损失函数求平均,一般来说,最后输出的损失函数是迷你批次损失函数的平均

神经网络处理的预测问题分为回归问题和分类问题两种,对于回归问题,一般情况是使用的 torch.nn.MSELoss 模块,即前面介绍的平方损失函数函数,通过创建这个模块的示例(一般使用默认参数,即在累的构造函数中不传入任何的参数,这样会输出损失函数对迷你批次的平均,如果要输出迷你批次的每个损失函数,可以指定参数 reduction='none',如果要输出迷你批次的损失函数的和,可以指定参数 reduction='sum' )在实例中传入神经网络预测的值和目标值,能够计算最后的损失函数

import torch  
import torch.nn as nn  
import torch.nn.functional as F  # 假设我们有一个简单的神经网络模型  
class SimpleNet(nn.Module):  def __init__(self):  super(SimpleNet, self).__init__()  self.fc = nn.Linear(10, 1)  # 假设输入特征为10,输出为1的线性层  def forward(self, x):  return self.fc(x)  # 实例化模型  
model = SimpleNet()  # 假设我们有一个迷你批次的数据  
batch_size = 16  
num_features = 10  # 随机生成预测值和目标值  
predictions = torch.randn(batch_size, 1)  # 神经网络输出的预测值  
targets = torch.randn(batch_size, 1)      # 真实的目标值  # 使用函数形式计算损失  
mse_loss_func = F.mse_loss  
loss_func = mse_loss_func(predictions, targets, reduction='mean')  # 默认是求平均  
print(f"Loss using functional form (mean): {loss_func.item()}")  # 使用模块形式计算损失  
mse_loss_module = nn.MSELoss(reduction='mean')  # 创建一个MSELoss模块的实例,默认是求平均  
loss_module = mse_loss_module(predictions, targets)  
print(f"Loss using module form (mean): {loss_module.item()}")  # 如果你想输出迷你批次的每个损失函数值,而不是平均值或总和  
mse_loss_module_none = nn.MSELoss(reduction='none')  
loss_per_element = mse_loss_module_none(predictions, targets)  
print(f"Loss per element: {loss_per_element}")  # 如果你想输出迷你批次的损失函数的总和  
mse_loss_module_sum = nn.MSELoss(reduction='sum')  
loss_sum = mse_loss_module_sum(predictions, targets)  
print(f"Loss sum: {loss_sum.item()}")

除回归问题之外,关于分类问题,pytorch也有和回归问题使用方法类似损失函数,如果是二分类问题用到的交叉熵损失函数,可以使用 torch.nn.BCELoss 模块实现,同样,在初始化这个模块的时候可以使用默认参数,输出所有损失函数的平均,该模块一般接受的是Sigmoid函数的输出

另一个经常用到的函数是对数(Logits)交叉熵损失函数 torch.nn.BCEWithLogitsLoss,这个函数和前面的函数的区别在于,可以直接省略Sigmoid函数的计算部分,不用计算概率,该损失函数会自动在损失函数内部的实现部分添加Sigmoid激活函数,当训练的时候使用这个损失函数可以增加计算的数值的稳定性,因为当概率接近0或者概率接近1的时候,二分交叉熵函数的对数部分会很容易接近无情打,这样会造成树脂的不稳定,通过在损失函数中加入Sigmoid函数并针对此函数简化计算损失函数,能够有效的避免这两种情况下的数值不稳定

和二分类问题类似,在多分类的情况下,也可以使用两个模块,第一个模块是 torch.nn.NLLLoss,即负对数似然函数,这个损失函数的运算过程是根据预测值(经过Softmax的计算和对数计算)和目标值(使用独热编码)计算这两个值按照元素一一对应的成绩,然后成绩求和,并取负值

因此在使用这个损失函数之前必须先计算Softmax函数取对数的结果,pytorch有一个函数 torch.nn.function.log_softmax可以实现这个目的,第二个模块是 torch.nn.CrossEntryLoss,用于构建目标损失函数,这个损失函数可以避免 LogSoftmax的计算,在损失函数里整合Softmax输出概率,以及对概率取对数输出损失函数

优化器

pytorch优化器详解_pytorch 优化器-CSDN博客

Pytorch中的优化器使用举例-腾讯云开发者社区-腾讯云

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/803288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode -- 第 392 场周赛

链接 : 竞赛 - 力扣 (LeetCode) 3105. 最长的严格递增或递减子数组 . - 力扣(LeetCode) 用两个分组循环(本质就是双指针),分别求出最长的递增和递减子数组的长度,然后取max ; class Solution { public:int longestMonotonicS…

客户现场服务器故障处理建议携带的设备

WIFI路由器,带 SIM卡的,带多个千兆网口的,网线 USB硬盘(TB计算) U盘启动盘 便携的KVM(另外带 键盘/鼠标/VGA线,方便现场多个服务器切换显示和控制) USB Hub(万一客户服…

图形化界面使用MQ!!!

一、docker安装 1、拉去镜像 docker pull rabbitmq:3.10-management 2、Docker运行,并设置开机自启动(第一个-p是MQ默认配置的端口,第二个-p是图形化界面配置的端口) docker run -d --restartalways --name rabbitmq -p 5672:5672…

直播系统的短视频直播源码,带有多功能后台系统的直播短视频平台 APP 源码。

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 此源码是一个直播系统,集直播、短视频等功能,根据市场趋势开发并推出思乐直播APP,APP功能丰富且可在后台管理系统进行配置,做到按需求来…

《QT实用小工具·二十二》多种样式导航按钮控件

1、概述 源码放在文章末尾 该项目实现了多种样式的导航按钮控件 可设置文字的左侧、右侧、顶部、底部间隔。 可设置文字对齐方式。 可设置显示倒三角、倒三角边长、倒三角位置、倒三角颜色。 可设置显示图标、图标间隔、图标尺寸、正常状态图标、悬停状态图标、选中状态图标…

字节面经之碰上了活菩萨

rt, 楼主碰上了活菩萨. 简单做了个自我介绍, 然后项目没怎么问, 说问我一些计算机基础知识. 然后让我说说我都会什么. 然后从数据结构开始. 让我讲一讲自己熟悉的数据结构, 比如树什么的. 然后我就把二叉树, 二叉搜索树, 二叉平衡树, 红黑树. DFS/BFS还有对应的场景讲了一遍.…

ctfshow web入门 文件上传web162--web167

web162 session文件包含条件竞争 直接包含不传马了 我们上传的文件如果不符合要求,就会被删除,导致成功上传无法访问,没有用。但是如果我们上传的速度比服务器删的速度快,就可以了。 上传.user.ini GIF89a auto_append_file/tmp/…

四、书城开发--3、书城图书部分的开发

书城图书部分 首先我们做书城首页搜索栏下面的图片展示 我们在书城首页组件中通过home请求方法中获取回来的数据中,打印出来可以看到那个banner就是我们现在要的图片 我们在data中定义一个变量banner用来存放获取回来的数据中的banner 然后把它展示出来就可以了&a…

LeetCode-84. 柱状图中最大的矩形【栈 数组 单调栈】

LeetCode-84. 柱状图中最大的矩形【栈 数组 单调栈】 题目描述:解题思路一:单调栈解题思路二:解题思路三: 题目描述: 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且…

阿赵UE学习笔记——26、动画混合空间

阿赵UE学习笔记目录   大家好,我是阿赵。   继续学习虚幻引擎的使用。之前学习了通过蓝图直接控制动画播放,或者通过动画状态机去控制播放。这次来学习一种比较细致的动画控制播放方式,叫做动画混合空间。 一、使用的情景 假设我们现在需…

ssm035基于JavaWeb的家居商城系统的设计与实现+jsp

家居商城系统 摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于家居商城系统当然也不能排除在外,随着网络技术的不断成熟,带动了家居商城系统,它彻底改…

Linux操作系统安装注意事项(新手简易版)

Linux操作系统安装注意事项(新手简易版) 目录: 1、字符集安装 2、磁盘分区 3、关闭KDUMP防火墙 4、时区选择 注:事例截图是centos8的安装,其他版本是一样的 1、字符集安装 ecology运行需要用到GBK和UTF8字符…

设计模式-结构型-装饰器模式-decorator

发票基本类 public class Invoice {public void printInvoice() {System.out.println("打印发票正文");} } 发票正文类 public class Decorator extends Invoice {protected Invoice ticket;public Decorator(Invoice ticket) {this.ticket ticket;}Overridepubl…

python复习 之 列表

遍历、增加、删除 list01 [1,2,3,4,5,6] for i in list01:print(i) # 遍历 切片 print(list01[3],----) print(list01[len(list01)-1],----) print(list01[3:],----) print(list01[:4],----) print(list01[:],----) print(list01[0:6:2],----) print(list01[::-1],----)# 增加…

Android获取连接到手机热点上的设备信息

主题:在手机开启热点网络的情况下,想要获取是哪个设备已经连接上了当前开启的热点。 实现思路:Android通过读取 /proc/net/arp 文件可以得到连接当前热点的设备信息,包括Mac地址、IP地址等信息。 一. 方法逻辑: /*** …

访问控制的三要素是______ 、 ________ 、 ________。

主体、客体、访问策略。 访问控制是计算机安全领域的一个重要概念,它确保只有经过授权的用户才能访问系统资源。访问控制通常涉及以下三个主要要素: 主体(Subject) :主体是可以对对象进行操作的实体,通常…

C# WinForm —— 05 控件简介

简介 窗体中用于输入或操作的对象,有自己的属性、方法、事件 属性:外观方法:功能事件:行为控制特征 可视化,与用户进行交互,属性,方法,事件,可供开发人员使用&#xff0…

vue 双向绑定

双向绑定:双方其中一方改变,另外一方也会跟着改变。 data() { return {inputValue: ,list: [],message: hello,checked: true,radio: ,select: [],options: [{text: A, value:{value: A}},{text: B, value:{value: B}},{text: C, value:{value: C}}], }…

ruoyi-vue-pro 前端vue js直接import导入本地文件使用方法

我的xml文件名称叫w2101.xml 第一步,删除所有依赖,否则配置以后就会启动报错: 第二步配置对应的文件格式,我当前使用的是xml文件 config.module.rule(xml).test(/\.xml$/).use(xml-loader).loader(xml-loader).end();第三步…

你应该知道的21个html小技巧

本文翻译自 21 HTML Tips You Must Know About,作者:Shefali, 略有删改。 在这篇文章中,我将分享21个HTML技巧和代码片段,可以提高你的编码技能。 链接联系人 使用HTML创建可点击的电子邮件、电话和短信链接&#xf…