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

深度学习中,用损失的均值或者总和反向传播的区别

如深度学习中代码:

def train_epoch_ch3(net, train_iter, loss, updater):"""The training loop defined in Chapter 3."""# Set the model to training modeif isinstance(net, torch.nn.Module):net.train()# Sum of training loss, sum of training accuracy, no. of examplesmetric = Accumulator(3)for X, y in train_iter:# Compute gradients and update parametersy_hat = net(X)l = loss(y_hat, y)if isinstance(updater, torch.optim.Optimizer):# Using PyTorch in-built optimizer & loss criterionupdater.zero_grad()l.backward()updater.step()metric.add(float(l) * len(y), accuracy(y_hat, y), y.numel())else:# Using custom built optimizer & loss criterionl.sum().backward()updater(X.shape[0])metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())# Return training loss and training accuracyreturn metric[0] / metric[2], metric[1] / metric[2]

对于循环for X, y in train_iter:每一次迭代都是一次小批量训练 

当用torch里的优化器来更新参数时,第一个if语句执行:

1.此时由于torch默认累计梯度,所以每次循环都得梯度置零

2.然后torch进行反向传播(如果是使用了torch定义的loss,那么得到的l是标量,即平均损失,次此时直接反向传播即可)。

loss = nn.CrossEntropyLoss(reduction='mean'),reduction='mean'意味着损失会返回均值,reduction理解成降维

loss = nn.CrossEntropyLoss(reduction='None'),reduction='None'意味着损失会返回原来的形式,即矢量

为什么不用总和形式呢?均值会好一点,因为每次训练有不同的batch_size(如果我把样本分成三个批量,一次训练中,首先用批量一训练,然后批量二,批量三,然后批量都训练完后,再进行新的一轮训练,直到收敛),用均值的话会默认使用l.sum()/batch_size,batch_size由内部求出,这个不用自己写,就实现了batch_size的解耦,不容易出错,更新参数时形式就变成了w_i=w_i-\alpha *loss,因为此时的损失是均值了

3.进行参数更新

4.统计损失与精度,为什么要用float(l)*len(y)呢

 因为l=loss(y_hat,y),这里的损失函数是从外部传进来的,所以如果传进来的是torch自己定义的损失函数,应该计算的是平均损失,那么要变成整体损失就得float(l)*len(y)。

如果loss是自己定义的,可能只是计算了损失,没求平均(看上面的函数,此时的loss应该是矢量,所以才会有l.sum())

当自己实现优化器来更新参数时,else语句执行:

1.此时自己可能没实现累计梯度,所以不用梯度清零

2.反向传播,但由于此时自己实现了loss函数,可能loss是矢量,要转成标量来反向传播,一个矢量转标量的好方法就是求和,即l.sum().backward(),

3.更新参数,注意到updater传入了一个参数X.shape[0],即样本数量batch_size,外边要注意更新参数时为w_i =w_i-\alpha /batchSize*loss,因为此时的l是总和

4.统计损失与精度

sum和mean其实主要影响了“梯度的大小”,反向传播时,依据损失求梯度,如果是sum,则梯度会比mean大n倍,那么在学习率不变的情况下,步子会迈得很长,体现到图形上就是正确率提升不了。所以需要缩小学习率。

l.mean().backward()和l.sum().backward()的区别在于它们计算梯度的方式。l.mean().backward()计算的是平均损失对权重的梯度,而l.sum().backward()计算的是总损失对权重的梯度。

在实践中,这两种方式通常会得到相似的结果,因为它们都是在尝试最小化损失。然而,使用l.mean().backward()可能会使得梯度的大小更稳定,因为它不会因为批量大小的变化而变化。这可能会使得训练过程更稳定,特别是在批量大小可能变化的情况下。

另一方面,使用l.sum().backward()可能会使得梯度的大小更大,这可能会导致训练过程更快,但也可能导致训练过程更不稳定。

总的来说,哪种方式更好取决于你的具体情况。如果你的批量大小是固定的,那么你可能会发现l.sum().backward()和l.mean().backward()在实践中没有太大的区别。如果你的批量大小可能变化,那么你可能会发现l.mean().backward()在实践中更稳定

补:总训练函数:

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):"""Train a model (defined in Chapter 3)."""animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],legend=['train loss', 'train acc', 'test acc'])for epoch in range(num_epochs):train_metrics = train_epoch_ch3(net, train_iter, loss, updater)test_acc = evaluate_accuracy(net, test_iter)animator.add(epoch + 1, train_metrics + (test_acc,))train_loss, train_acc = train_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_acc


http://www.mrgr.cn/news/80902.html

相关文章:

  • MyBatis入门的详细应用实例
  • C++ OpenGL学习笔记(1、Hello World空窗口程序)
  • Postbot使用教程
  • 基于.NetCore 的 AI 识别系统的设计与实现
  • Android Studio新建项目在源码中编译
  • 华为HarmonyOS实现跨多个子系统融合的场景化服务 -- 4 设置打开App Button
  • 以腾讯混元模型为例,在管理平台上集成一个智能助手
  • 黑马Java面试教程_P8_并发编程
  • PyQt5学习笔记
  • Linux之文件相关命令
  • jvm类加载器
  • 挑战一个月基本掌握C++(第七天)了解指针,引用,时间,输入输出,结构体,vector容器,数据结构 - 通用完结
  • Spring Security 6 系列之五 - 授权管理
  • 相机雷达外参标定综述“Automatic targetless LiDAR–camera calibration: a survey“
  • electron-vite【实战系列教程】
  • 【Leetcode 热题 100】114. 二叉树展开为链表
  • 【软考高级】系统架构设计师复习笔记-精华版
  • 【Leetcode 热题 100 - 扩展】303. 区域和检索 - 数组不可变
  • 【数据可视化案列】白葡萄酒质量数据的EDA可视化分析
  • ECharts关系图-关系图11,附视频讲解与代码下载
  • FPGA 16 ,Verilog中的位宽:深入理解与应用
  • OCR实践—PaddleOCR
  • 【0373】Postgres内核 MultiXact shared memory 初始化 ( 2 )
  • Docker_常用命令详解
  • STM32单片机芯片与内部33 ADC 单通道连续DMA
  • 被裁20240927 --- 嵌入式硬件开发 前篇