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

PyTorch : torch.cuda.amp: 自动混合精度详解

amp : 全称为 Automatic mixed precision,自动混合精度

amp总体介绍:

可以在神经网络推理过程中,针对不同的层,采用不同的数据精度进行计算,从而实现节省显存和加快速度的目的。

通常,深度学习中使用的精度为32位(单精度)浮点数,而使用16位(半精度)浮点数可以将内存使用减半,同时还可以加快计算速度。然而,16位浮点数的精度较低,可能导致数值下溢或溢出,从而影响训练结果。

混合精度: 有不止一种精度的Tensor(不同精度的数值计算混合使用来加速训练和减少显存占用) :

  • torch.FloatTensor(浮点型 32位)(torch默认的tensor精度类型是torch.FloatTensor)
  • torch.HalfTensor(半精度浮点型 16位)

自动:

  • 预示着Tensor的dtype类型会自动变化,也就是框架按需自动调整tensor的dtype

使用自动混合精度 (amp) 的原因

  • torch.HalfTensor优势是存储小、计算快、更好的利用CUDA设备的Tensor Core。因此训练的时候可以减少显存的占用(可以增加batchsize了),同时训练速度更快
  • torch.HalfTensor劣势是数值范围小(更容易Overflow /Underflow)、舍入误差(Rounding Error,导致一些微小的梯度信息达不到16bit精度的最低分辨率,从而丢失)

混合精度训练机制(API:autocast类;GradScaler 类`)

1. amp.autocast

  • amp.autocast():用户不需要手动对模型参数 dtype 转换,amp 会自动为算子选择合适的数值精度;是PyTorch中一种混合精度的技术,可在保持数值精度的情况下提高训练速度和减少显存占用。
  • amp.autocast():能够自动将16位浮点数转换为32位浮点数进行数值计算,并在必要时将结果转换回16位浮点数。这种自动转换可以帮助避免数值下溢或溢出的问题,并在保持数值精度的同时提高计算速度和减少显存占用。
  • autocast的优缺点
    使用autocast的优点在于,它可以自动选择合适的精度进行计算,从而提高了计算速度和内存使用效率,同时也减少了代码实现的复杂性。
    缺点在于,autocast并不是所有运算都能自动找到合适的精度进行计算,如果没有找到合适的精度,就会使用默认精度(FP32),这样就会增加显存的使用,并且在需要处理极大或极小的数据时,可能会出现数值精度问题。
  • 使用torch.cuda.amp.autocast()的过程如下:
  • 将模型和数据移动到GPU上
  • 使用torch.cuda.amp.autocast()上下文管理器包装模型的前向传递和损失计算
  • 使用scaler(即torch.cuda.amp.GradScaler对象)将反向传播的梯度缩放回16位
  • 执行梯度更新

问:使用 torch.cuda.amp.autocast() 将数据 从32位(单精度) 转换为 16位(半精度),会导致精度丢失嘛?
答:使用 torch.cuda.amp.autocast() 将数据从32位(单精度)转换为16位(半精度)会导致精度损失。由于16位浮点数只能表示更少的有效位数,因此它们的精度不如32位浮点数。在混合精度训练中,为了平衡精度和性能,通常会将网络的前向传播和反向传播过程中的参数和梯度计算使用半精度浮点数来加速计算。这种方法可以在一定程度上降低计算精度要求,但会带来一定的精度损失。
·
尽管存在精度损失,使用半精度浮点数的优点在于它们可以显著降低计算时间和显存消耗,从而使模型可以在更大的批量下进行训练,提高训练效率。此外,在实际应用中,对于某些任务,半精度精度的计算误差对于结果的影响可能不是很大,因此,半精度计算可以在保证结果准确性的前提下,大幅度提高模型的训练速度和效率。
原文链接:https://blog.csdn.net/weixin_37804469/article/details/129733868

2. amp.GradScaler

  • 对于反向传播的时候,FP16 的梯度数值溢出的问题,amp 提供了梯度 scaling 操作,而且在优化器更新参数前,会自动对梯度unscaling,所以,对用于模型优化的超参数不会有任何影响;
  • 具体来说,GradScaler 可以将梯度缩放到较小的范围,以避免数值下溢或溢出的问题,同时保持足够的精度以避免模型的性能下降。
2.1 GradScaler 类

torch.cuda.amp.GradScaler(init_scale=65536.0, growth_factor=2.0, backoff_factor=0.5, growth_interval=2000, enabled=True)

其中:

  • init_scale: scale factor 的初始值
  • growth_factor: 每次 scale factor 的增长系数
  • backoff_factor: scale factor 下降系数
  • growth_interval: 每隔多个interval 增长 scale factor
  • enabled: 是否做 scale
2.2 scale(output)方法

对outputs乘 scale factor,并返回,如果enabled=False就原样返回。

2.3 step(optimizer, *args, **kwargs)方法

step 方法在做两件事情:

  1. 对梯度 unscale,如果之前没有手动调用unscale方法的话

  2. 检查梯度溢出,如果没有nan/inf,就执行 optimizer 的 step,如果有就跳过

    注意:GradScaler的step不支持传 closure。

2.4 update(new_scale=None)方法

update方法在每个 iteration 结束前都需要调用,如果参数更新跳过,会给 scale factor 乘backoff_factor,或者到了该增长的 iteration,就给 scale factor 乘growth_factor。也可以用new_scale直接更新 scale factor。

以下是一个示例,展示了如何在 PyTorch 中使用 GradScaler:

import torch
from torch.cuda.amp import GradScaler, autocast# 创建 GradScaler 和模型
scaler = GradScaler()
model = torch.nn.Linear(10, 1).cuda()# 定义损失函数和优化器
loss_fn = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)# 定义一些训练数据和目标
x = torch.randn(32, 10).cuda()
y = torch.randn(32, 1).cuda()# 使用 GradScaler 进行自动混合精度训练
for i in range(1000):optimizer.zero_grad()# 将前向传递包装在autocast中以启用混合精度with autocast():y_pred = model(x)loss = loss_fn(y_pred, y)# 调用 GradScaler 的 backward() 方法计算梯度并缩放scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()if i % 100 == 0:print(f"Step {i}, loss={loss.item():.4f}")

在这个示例中:

  1. 创建一个 GradScaler 对象 scaler
  2. 定义模型和优化器
  3. 在训练循环中,使用 autocast() 上下文管理器将前向传递操作包装起来,这样就可以使用混合精度进行计算
  4. 调用 scaler.scale(loss) 计算损失的缩放版本,并调用 scaler.step(optimizer) 来更新模型参数
  5. 最后使用 scaler.update() 更新 GradScaler 对象的内部状态
  6. 这个过程可以重复进行多次,直到训练结束。

原文链接:https://blog.csdn.net/qq_43369406/article/details/130393078

相关文章:

  • VUE篇之日历组件
  • 基于Springboot的教学信息反馈系统的设计与实现(源码+调试)
  • java实现局域网内视频投屏播放(一)背景/需求
  • MATLAB Sub2ind下标值转化
  • Linux---获取管理员权限的相关命令
  • Day20【time模块】
  • 在Flutter中使用PhotoViewGallery指南
  • Docker与K8s的区别
  • 数字化转型导师坚鹏:中国工商银行人工智能与金融数字化转型培训
  • vscode 文件目录栏缩进
  • 基于Springboot的高校教学评价系统的设计与实现(源码+调试)
  • FreeRDP WebConnect Url 任意文件读取漏洞复现
  • acwing算法提高之动态规划--状态压缩DP
  • 网络安全—学习溯源和日志分析
  • Axure元件的介绍使用以及登录界面
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • const let
  • express.js的介绍及使用
  • express如何解决request entity too large问题
  • Hibernate最全面试题
  • Java方法详解
  • mockjs让前端开发独立于后端
  • MQ框架的比较
  • Service Worker
  • 成为一名优秀的Developer的书单
  • 前端之Sass/Scss实战笔记
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • ​iOS安全加固方法及实现
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #图像处理
  • (175)FPGA门控时钟技术
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)我也是一只IT小小鸟
  • .htaccess 强制https 单独排除某个目录
  • .NET 中创建支持集合初始化器的类型
  • .NET是什么
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • @RequestMapping用法详解
  • [ 隧道技术 ] 反弹shell的集中常见方式(四)python反弹shell
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体
  • [CareerCup] 14.5 Object Reflection 对象反射
  • [CISCN2019 华东南赛区]Web11
  • [codeforces]Levko and Permutation
  • [iOS]让Xcode 4.2生成的app支持老的iOS设备(armv6)
  • [JavaScript]_[初级]_[关于forin或for...in循环语句的用法]