混合精度训练amp,torch.cuda.amp.autocast():

1 需要什么GPU:

在上面讲述了为什么利用混合精度加速,需要拥有 TensorCore 的GPU

0x02. 基础理论:

在日常中深度学习的系统,一般使用的是单精度 float(Single-Precision)浮点表示。在了解混合精度训练之前,我们需要先对其中的主角半精度『float16』进行一定的理论知识学习。

float vs float16 的组成bitmap

在上图可以看到,与单精度float(32bit,4个字节)相比,半进度float16仅有16bit,2个字节组成。天然的存储空间是float的一半。 其中,float16的组成分为了三个部分:

  1. 最高位表示符号位;
  2. 有5位表示exponent位;
  3. 有10位表示fraction位;

在上面的例子中,我们可以对 float16 有一个感性的认识,对这个范围有一个大概的印象,后面会用到:

  • float16 最大范围是 [-65504 - 66504]
  • float16 能表示的精度范围是 2^-24 ,超过这个数值的数字会被直接置0;

为什么需要半精度:

float16和float相比恰里,总结下来就是两个原因:内存占用更少,计算更快

  • 内存占用更少:这个是显然可见的,通用的模型 fp16 占用的内存只需原来的一半。memory-bandwidth 减半所带来的好处:
    • 模型占用的内存更小,训练的时候可以用更大的batchsize。
    • 模型训练时,通信量(特别是多卡,或者多机多卡)大幅减少,大幅减少等待时间,加快数据的流通。
  • 计算更快:
    • 目前的不少GPU都有针对 fp16 的计算进行优化。论文指出:在近期的GPU中,半精度的计算吞吐量可以是单精度的 2-8 倍;

为什么需要混合精度:

数据溢出问题:Overflow / Underflow

在第一节中,我们提到了 fp16 的有效的动态范围约为 (  [-65504 - 66504] ),比单精度的float要狭窄很多。对于深度学习而言,最大的问题在于 Underflow(下溢出),在训练后期,例如激活函数的梯度会非常小, 甚至在梯度乘以学习率后,值会更加小。(权重 = 旧权重 + lr * 梯度

3.2.2 舍入误差(Rounding Error)

何为舍入误差,引用[2]中的一张图说的比较透彻:

这个例子非常直观的阐述了『舍入误差』这个说法。而至于上面提到的,FP16的最小间隔是一个比较玄乎的事,在wikipedia的引用上有这么一张图: 描述了 fp16 各个区间的最小gap。

半精度FP16不同区间的最小间隔

3.3 解决办法:

3.3.1 FP32 权重备份

这种方法主要是用于解决舍入误差的问题。其主要思路,可以概括为:weights, activations, gradients 等数据在训练中都利用FP16来存储,同时拷贝一份FP32的weights,用于更新。 在这里,我直接贴一张论文[1]的图片来阐述:

权重fp32备份策略

可以看到,其他所有值(weights,activations, gradients)均使用 fp16 来存储,而唯独权重weights需要用 fp32 的格式额外备份一次。 这主要是因为,在更新权重的时候,往往公式: 权重 = 旧权重 + lr * 梯度,而在深度模型中,lr * 梯度 这个值往往是非常小的,如果利用 fp16 来进行相加的话, 则很可能会出现上面所说的『舍入误差』的这个问题,导致更新无效。因此上图中,通过将weights拷贝成 fp32 格式,并且确保整个更新(update)过程是在 fp32 格式下进行的。

看到这里,可能有人提出这种 fp32 拷贝weight的方式,那岂不是使得内存占用反而更高了呢?是的, fp32 额外拷贝一份 weight 的确新增加了训练时候存储的占用。 但是实际上,在训练过程中,内存中占据大部分的基本都是 activations 的值。特别是在batchsize 很大的情况下, activations 更是特别占据空间。 保存 activiations 主要是为了在 back-propogation 的时候进行计算。因此,只要 activation 的值基本都是使用 fp16 来进行存储的话,则最终模型与 fp32 相比起来, 内存占用也基本能够减半。

3.3.2 Loss Scale(损失放大)

即使了混合精度训练,还是存在无法收敛的情况,原因是激活梯度的值太小,造成了溢出。可以通过使用torch.cuda.amp.GradScaler,通过放大loss的值来防止梯度的underflow(只在BP时传递梯度信息使用,真正更新权重时还是要把放大的梯度再unscale回去);

  • 反向传播前,将损失变化手动增大2^k倍,因此反向传播时得到的中间变量(激活函数梯度)则不会溢出;
  • 反向传播后,将权重梯度缩小2^k倍,恢复正常值

Loss Scale 主要是为了解决 fp16 underflow 的问题。刚才提到,训练到了后期,梯度(特别是激活函数平滑段的梯度)会特别小,fp16 表示容易产生 underflow 现象。 下图展示了 SSD 模型在训练过程中,激活函数梯度的分布情况:可以看到,有67%的梯度小于 -65504 ,如果用 fp16 来表示,则这些梯度都会变成0。

为了解决梯度过小的问题,论文中对计算出来的loss值进行scale,由于链式法则的存在,loss上的scale会作用也会作用在梯度上。这样比起对每个梯度进行scale更加划算。 scaled 过后的梯度,就会平移到 fp16 有效的展示范围内。

这样,scaled-gradient 就可以一直使用 fp16 进行存储了。只有在进行更新的时候,才会将 scaled-gradient 转化为 fp32,同时将scale抹去。论文指出, scale 并非对于所有网络而言都是必须的。而scale的取值为也会特别大,论文给出在 8 - 32k 之间皆可。

3.3.3 提高算数精度

在论文中还提到一个『计算精度』的问题:在某些模型中,fp16矩阵乘法的过程中,需要利用 fp32 来进行矩阵乘法中间的累加(accumulated),然后再将 fp32 的值转化为 fp16 进行存储。 换句不太严谨的话来说,也就是利用 利用fp16进行乘法和存储,利用fp32来进行加法计算。 这么做的原因主要是为了减少加法过程中的舍入误差,保证精度不损失。

在这里也就引出了,为什么网上大家都说,只有 Nvidia Volta 结构的 拥有 TensorCore 的CPU(例如V100),才能利用 fp16 混合精度来进行加速。 那是因为 TensorCore 能够保证 fp16 的矩阵相乘,利用 fp16 or fp32 来进行累加。在累加阶段能够使用 FP32 大幅减少混合精度训练的精度损失。而其他的GPU 只能支持 fp16 的 multiply-add operation。

浅谈混合精度训练 - 知乎

如何在pytorch中使用amp?

连接:Pytorch自动混合精度(AMP)介绍与使用 - jimchen1218 - 博客园

<think>好的,我现在需要处理用户关于PyTorchtorch.cuda.amp.autocast更改为torch.amp.autocast的问题。首先,我得确认这两个API的区别和变化背景。 用户可能是在升级PyTorch版本后遇到了代码兼容性问题,或者看到了文档中的新写法。我需要回忆PyTorch的版本更新情况。记得在1.10或之后的版本中,PyTorch引入了更通用的自动混合精度AMP)接口,将原先的torch.cuda.amp.autocast扩展为支持不同设备的版本,比如CPU。所以原来的torch.cuda.amp.autocast现在可能被推荐改用torch.amp.autocast,并指定设备类型。 接下来,我需要验证这个变化是否存在。查阅PyTorch官方文档,发现从1.10开始,确实引入了torch.amp.autocast作为更通用的上下文管理器,允许通过device_type参数选择'cuda'或'cpu'。而原来的torch.cuda.amp.autocast仍然保留,但可能被视为旧版写法。 然后,用户的问题是如何将旧代码中的torch.cuda.amp.autocast修改为torch.amp.autocast。需要指出替换方法,并说明参数的变化。例如,旧代码可能直接使用with torch.cuda.amp.autocast():,而新方法需要指定设备类型:with torch.amp.autocast(device_type='cuda'):。 同时,需要注意版本兼容性。如果用户使用的是较旧的PyTorch版本(低于1.10),新写法可能不可用,需要提示用户检查版本,必要时升级。此外,新接口的参数是否有其他变化,比如默认值或新增参数,需要确认。比如,是否必须显式指定device_type,或者是否有其他可选参数。 另外,用户可能关心修改后的代码是否会影响功能,比如混合精度训练的效果是否一致。需要说明两者在功能上是等效的,只是接口更统一,支持更多设备类型。此外,可能还需要提醒用户其他相关的AMP组件是否需要调整,比如GradScaler是否仍然使用torch.cuda.amp.GradScaler。 可能的误区:用户可能误以为必须全部替换,但实际上旧接口仍然有效,但推荐使用新方法以提高代码的可维护性和扩展性。需要明确说明这一点,避免用户产生困惑。 最后,总结修改步骤,给出示例代码对比,并强调版本要求和注意事项,确保用户能够正确无误地进行迁移。</think>以下是关于将`torch.cuda.amp.autocast`修改为`torch.amp.autocast`的逐步说明: --- ### 1. **接口变更背景** - **历史行为**:PyTorch 1.9及之前版本中,混合精度训练(Automatic Mixed Precision, AMP)的上下文管理器仅支持CUDA设备,因此通过`torch.cuda.amp.autocast`调用。 - **新版本改进**:从 **PyTorch 1.10** 开始,引入了更通用的`torch.amp.autocast`接口,支持多设备(如CPU和CUDA),需通过`device_type`参数明确指定设备类型。 --- ### 2. **代码修改方法** #### 原始代码(旧写法) ```python with torch.cuda.amp.autocast(): # 混合精度计算逻辑 output = model(input) ``` #### 修改后代码(新写法) ```python with torch.amp.autocast(device_type="cuda"): # 混合精度计算逻辑 output = model(input) ``` #### 关键变化: - 将`torch.cuda.amp.autocast()`替换为`torch.amp.autocast(device_type="cuda")` - 新增`device_type`参数,必须显式指定为`"cuda"` --- ### 3. **版本兼容性** | PyTorch版本 | `torch.cuda.amp.autocast` | `torch.amp.autocast` | |-------------|---------------------------|----------------------| | <1.10 | ✅ 可用 | ❌ 不可用 | | ≥1.10 | ✅ 保留(兼容旧代码) | ✅ 推荐使用 | **建议**:升级到PyTorch≥1.10后逐步迁移至新接口。 --- ### 4. **注意事项** 1. **功能一致性**:新旧接口在CUDA设备上的行为完全一致,均会: - 自动将部分操作转换为`float16`加速计算 - 保持其他操作在`float32`精度下 2. **设备扩展性**:新接口支持`device_type="cpu"`(需PyTorch≥1.10且启用CPU AMP)。 3. **GradScaler兼容性**:梯度缩放器仍使用`torch.cuda.amp.GradScaler`,无需修改。 --- ### 5. **完整示例对比** #### 旧版本代码 ```python scaler = torch.cuda.amp.GradScaler() for input, target in data: with torch.cuda.amp.autocast(): output = model(input) loss = loss_fn(output, target) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() ``` #### 新版本代码 ```python scaler = torch.cuda.amp.GradScaler() for input, target in data: with torch.amp.autocast(device_type="cuda"): # 修改此行 output = model(input) loss = loss_fn(output, target) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() ``` --- ### 6. **验证修改** 1. 检查PyTorch版本: ```python print(torch.__version__) # 应输出≥1.10.0 ``` 2. 运行修改后代码,观察是否抛出`AttributeError`(若版本过低)。 3. 监控训练日志,确认混合精度正常生效(如GPU显存下降、计算速度提升)。 --- ### 总结 通过上述修改,代码将符合PyTorch新版本的最佳实践,同时保持对多设备扩展的支持。如果仍需兼容旧版本,可添加条件判断: ```python if hasattr(torch, 'amp') and hasattr(torch.amp, 'autocast'): autocast_context = torch.amp.autocast(device_type="cuda") else: autocast_context = torch.cuda.amp.autocast() with autocast_context: # 计算逻辑 ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pengsen Ma

太谢谢了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值