Pytorch入门(7)—— 梯度累加(Gradient Accumulation)

news2024/11/25 2:49:24

1. 梯度累加

  • 在训练大模型时,batch_size 最大值往往受限于显存容量上限,当模型非常大时,这个上限可能小到不可接受。梯度累加(Gradient Accumulation)是一个解决该问题的 trick
  • 梯度累加的思想很简单,就是时间换空间。具体而言,我们不在每个 batch data 梯度计算后直接更新模型,而是多算几个 batch 后,使用这些 batch 的平均梯度更新模型,从而放大等效 batch_size。如下图所示
    在这里插入图片描述
  • 用公式表示:设 batch size 为 n n n,模型参数为 w \pmb{w} w,样本 i i i 的损失为 l i l_i li,则正常情况下 sgd 参数更新为
    w ← w + α ∑ i = 1 n 1 n ∂ l i ∂ w \pmb{w} \leftarrow \pmb{w} + \alpha \sum_{i=1}^n\frac{1}{n}\frac{\partial l_i}{\partial \pmb{w}} ww+αi=1nn1wli 使用梯度累加时,设累加步长为 m m m(即计算 m m m 个 batch 梯度后用梯度均值更新一次),sgd 更新如下
    w ← w + α 1 m ∑ b = 1 m ∑ i = 1 n 1 n ∂ l b i ∂ w = w + α ∑ i = 1 m n 1 m n ∂ l i ∂ w \begin{aligned} \pmb{w} &\leftarrow \pmb{w} + \alpha \frac{1}{m} \sum_{b=1}^m \sum_{i=1}^n\frac{1}{n}\frac{\partial l_{bi}}{\partial \pmb{w}} \\ &= \pmb{w} + \alpha \sum_{i=1}^{mn}\frac{1}{mn} \frac{\partial l_i}{\partial \pmb{w}} \end{aligned} ww+αm1b=1mi=1nn1wlbi=w+αi=1mnmn1wli 可见这等价于使用 batch_size = m n mn mn 进行训练

2. 在 pytorch 中实现梯度累加

2.1 伪代码

  • pytorch 使用和 tensor 绑定的自动微分机制。每个 tensor 对象都有 .grad 属性存储其中每个元素的梯度值,通过 .requires_grad 属性控制其是否参与梯度计算。训练模型时,一般通过对标量 loss 执行 loss.backward() 自动进行反向传播,以得到计算图中所有 tensor 的梯度。详见 PyTorch入门(2)—— 自动求梯度
  • pytorch 中梯度 tensor.grad 不会自动清零,而会在每次反向传播过程中自动累加,所以一般在反向传播前把梯度清零
    for inputs, labels in data_loader:
            
        # forward pass 
        preds = model(inputs)
        loss  = criterion(preds, labels)
    	
    	# clear grad of last batch	
    	optimizer.zero_grad()
    	
    	# backward pass, calculate grad of batch data
    	loss.backward()
    	
        # update model
        optimizer.step()
    
    这种设计对于实现梯度累加 trick 是很方便的,我们可以在 batch 计算过程中进行计数,仅在达到计数达到更新步长时进行一次参数更新并清零梯度,即
    # batch accumulation parameter
    accum_iter = 4  
    
    # loop through enumaretad batches
    for batch_idx, (inputs, labels) in enumerate(data_loader):
            
        # forward pass 
        preds = model(inputs)
        loss  = criterion(preds, labels)
    
        # scale the loss to the mean of the accumulated batch size
        loss = loss / accum_iter 
    
        # backward pass
        loss.backward()
    
        # weights update
        if ((batch_idx + 1) % accum_iter == 0) or (batch_idx + 1 == len(data_loader)):
            optimizer.step()
            optimizer.zero_grad()
    

2.2 线性回归案例

  • 下面使用来自 经典机器学习方法(1)—— 线性回归 的简单线性回归任务说明梯度累加的具体实现方法

    本节代码直接从 jupyter notebook 复制而来,可能无法直接运行!

  • 首先生成随机数据构造 dataset
    import torch
    from IPython import display
    from matplotlib import pyplot as plt
    import numpy as np
    import random
    import torch.utils.data as Data
    import torch.nn as nn
    import torch.optim as optim
    
    # 生成样本
    num_inputs = 2
    num_examples = 1000
    true_w = torch.Tensor([-2,3.4]).view(2,1)
    true_b = 4.2
    batch_size = 10
    
    # 1000 个2特征样本,每个特征都服从 N(0,1)
    features = torch.randn(num_examples, num_inputs, dtype=torch.float32) 
    
    # 生成真实标记
    labels = torch.mm(features,true_w) + true_b
    labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float32)
    
    # 包装数据集,将训练数据的特征和标签组合
    dataset = Data.TensorDataset(features, labels)
    
    1. 不使用梯度累加技巧,batch size 设置为 40
      # 构造 DataLoader
      batch_size = 40
      data_iter = Data.DataLoader(dataset, batch_size, shuffle=False)	# shuffle=False 保证实验可比
      
      # 定义模型
      net = nn.Sequential(nn.Linear(num_inputs, 1))
      
      # 初始化模型参数
      nn.init.normal_(net[0].weight, mean=0, std=0)
      nn.init.constant_(net[0].bias, val=0)
      
      # 均方差损失函数
      criterion = nn.MSELoss()
      
      # SGD优化器
      optimizer = optim.SGD(net.parameters(), lr=0.01)
      
      # 模型训练
      num_epochs = 3
      for epoch in range(1, num_epochs + 1):
          epoch_loss = []
          for X, y in data_iter:
              # 正向传播,计算损失
              output = net(X) 
              loss = criterion(output, y.view(-1, 1))
              
              # 梯度清零
              optimizer.zero_grad()            
              
              # 计算各参数梯度
              loss.backward()
              #print('backward: ', net[0].weight.grad)
              
              # 更新模型
              optimizer.step()
         
              epoch_loss.append(loss.item()/batch_size)
          print(f'epoch {epoch}, loss: {np.mean(epoch_loss)}')
      
      '''
      epoch 1, loss: 0.5434057731628418
      epoch 2, loss: 0.1914414196014404
      epoch 3, loss: 0.06752514398097992
      '''
      
    2. 使用梯度累加,batch size 设置为 10,步长设为 4,等效 batch size 为 40
      # 构造 DataLoader
      batch_size = 10
      accum_iter = 4
      data_iter = Data.DataLoader(dataset, batch_size, shuffle=False)	# shuffle=False 保证实验可比
      
      # 定义模型
      net = nn.Sequential(nn.Linear(num_inputs, 1))
      
      # 初始化模型参数
      nn.init.normal_(net[0].weight, mean=0, std=0)
      nn.init.constant_(net[0].bias, val=0)
      
      # 均方差损失
      criterion = nn.MSELoss()
      
      # SGD优化器对象
      optimizer = optim.SGD(net.parameters(), lr=0.01)
      
      # 模型训练
      num_epochs = 3
      for epoch in range(1, num_epochs + 1):
          epoch_loss = []
          for batch_idx, (X, y) in enumerate(data_iter):
              # 正向传播,计算损失
              output = net(X) 
              loss = criterion(output, y.view(-1, 1))  
              loss = loss / accum_iter	# 取各个累计batch的平均损失,从而在.backward()时得到平均梯度
      
              # 反向传播,梯度累计
              loss.backward()
              
              if ((batch_idx + 1) % accum_iter == 0) or (batch_idx + 1 == len(data_iter)):
                  #print('backward: ', net[0].weight.grad)
                  # 更新模型
                  optimizer.step()              
                  # 梯度清零
                  optimizer.zero_grad()
              
              epoch_loss.append(loss.item()/batch_size)
          print(f'epoch {epoch}, loss: {np.mean(epoch_loss)}')
      '''
      epoch 1, loss: 0.5434057596921921
      epoch 2, loss: 0.19144139245152472
      epoch 3, loss: 0.06752512042224407
      '''
      
  • 可以观察到无论 epoch loss 还是 net[0].weight.grad 都完全相同,说明梯度累加不影响计算结果

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

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

相关文章

第12周作业--HLS入门

目录 一、HLS入门 二、HLS入门程序编程 创建项目 1、点击Vivado HLS 中的Create New Project 2、设置项目名 3、加入文件 4、仿真 3、综合 一、HLS入门 1. HLS是什么?与VHDL/Verilog编程技术有什么关系? HLS(High-Level Synthesis&#xff0c…

自动化重置数据库功能的探索与实践

1、简介 在现代软件开发中,尤其是涉及到数据驱动的应用程序时,开发和测试环境中数据库的管理是至关重要的一环。为了确保开发和测试环境中的数据库始终处于一致的状态,自动化重置数据库成为了一种常见的实践。本文旨在介绍如何通过Shell脚本…

打印9*9乘法表(递归或压缩矩阵)python

打印9*9表def print_multiplication_table(row, col):if row > 10:return # 递归结束条件if col row:print() # 换行print_multiplication_table(row 1, 1) # 递归调用下一行else:print(f"{row-1} * {col} {(row-1) * col}\t", end"") # 打印乘法…

小程序properties默认值定义及父子组件的传值

因经常写vue,很久没写小程序,容易串频道,现记录一下小程序的组件用法、监听传入值及父子传值方式 首先小程序中传值是没有:(冒号)的,其次properties中定义默认值不需要写default 1.自定义组件中,首先json…

TransFormer学习之基础知识:STN、SENet、CBAM、Self-Attention

1.空间注意力机制STN 参考链接:STN(Spatial Transformer Networks) 参考链接:通俗易懂的Spatial Transformer Networks(STN) 核心动机: 在空间中捕获重要区域特征(如图像中的数字),将核心区域进行放大居中,使得结果更…

2461. 长度为 K 子数组中的最大和(c++)

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和: 子数组的长度是 k,且子数组中的所有元素 各不相同 。 返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0 。 子数…

第七节 ConfigurationClassParser 源码分析

tips: ConfigurationClassParser 是 Springframework 中的重要类。 本章主要是源码理解,有难度和深度,也枯燥乏味,可以根据实际情况选择阅读。 位置:org.springframework.context.annotation.ConfigurationClassPars…

怎样下载Android Studio历史版本文件包

1.在官网上下载都是推荐最新版本,想下载历史版本比较费劲,绕来绕去的。 2.进入这个网站可以下载其他版本https://android-studio.en.uptodown.com/windows/versions Older versions of Android Studio Its not uncommon for the latest version of an a…

【大数据】MapReduce实战

文章目录 [toc]Word CountMapperReducerrun.sh本地调试 基于白名单的Word CountMapperReducerrun.sh本地调试 文件分发-fileMapperReducerrun.sh -cacheFileMapperReducerrun.sh -cacheArchiveMapperReducerrun.sh 杀死MapReduce Job排序压缩文件mr_ip_lib_python本地调试 个人…

Flask Response 对象

文章目录 创建 Response 对象设置响应内容设置响应状态码设置响应头完整的示例拓展设置响应的 cookie重定向响应发送文件作为响应 总结 Flask 是一个 Python Web 框架,用于快速开发 Web 应用程序。在 Flask 中,我们使用 Response 对象来构建 HTTP 响应。…

东软联合福建省大数据集团打造“数据要素×医疗健康”服务新模式

5月23日,东软集团与福建省大数据集团有限公司在福州签订战略合作协议。 据「TMT星球」了解,双方将在健康医疗数据要素价值领域展开合作,通过大数据服务,赋能商业保险公司的产品设计和保险两核,打造“数据要素医疗健康…

微软开源多模态大模型Phi-3-vision,微调实战来了

节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型& AIGC 技术趋势、大模型& AIGC 落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了…

Qt 报错总结 No suitable kits found

目录 “No suitable kits found” 解决 解决方法参考: chatGPT辅助解决QT构建报错error: multiple target patterns 我的解决方法:把语言设置为空 “No suitable kits found” 解决 没有找到合适的kits套件,在安装Qt Creator时没有安装Min…

AGI技术与原理浅析:曙光还是迷失?

前言:回顾以往博客文章,最近一次更新在2020-07,内容以机器学习、深度学习、CV、Slam为主,顺带夹杂个人感悟。笔者并非算法科班出身,本科学制药、研究生学金融,最原始的算法积累都来源于网络,当时…

系统架构师考试(十)

SaaS为在线客服 PaaS为二次开发,比如低代码平台 IaaS 硬件开发 B 是基础设施作为服务 软件架构的概念 架构风格 数据流风格 网络报文是在计算机网络中通过网络传输的数据单元,它是网络通信的基本单位。网络报文包含了发送方和接收方之间传输的数据&…

【Crypto】摩丝

文章目录 一、摩斯解题感悟 一、摩斯 很明显莫尔斯密码 iloveyou还挺浪漫 小小flag,拿下 解题感悟 莫尔斯密码这种题还是比较明显的

在Windows10中重命名文件和文件夹的6种方法,有你熟悉和不熟悉的

序言 你可以通过多种方式在Windows 10上重命名文件。如果每次你想更改文件名时仍右键单击并选择“重命名”,那么我们有一些技巧可以加快更改速度。 使用文件资源管理器重命名文件和文件夹 Windows 10的文件资源管理器是一个功能强大的工具。你知道吗,有四种不同的方法可以…

从零入门激光SLAM(二十一)——看不懂FAST-LIO?进来

大家好呀,我是一个SLAM方向的在读博士,深知SLAM学习过程一路走来的坎坷,也十分感谢各位大佬的优质文章和源码。随着知识的越来越多,越来越细,我准备整理一个自己的激光SLAM学习笔记专栏,从0带大家快速上手激…

【Vue】input框自动聚焦且输入验证码后跳至下一位

场景:PC端 样式: <div class="verification-code-input"><input v-model="code[index]" v-for="(_, index) in 5" :key="index" type="text" maxlength="1" @input="handleInput(index)" …

数据库—— MySQL数据库安装

一、MySQL数据库定义 MySQL是一种开源关系型数据库管理系统&#xff0c;它使用SQL语言进行数据的管理。通过MySQL&#xff0c;可以创建数据库、表格、插入、查询、更新和删除数据等操作。MySQL支持多种操作系统&#xff0c;并且被广泛应用于Web应用程序开发中。MySQL以其高性能…