还是搬运来给自己学习啊 多谢体谅拉~~

这里分享混合精度训练的时候遇到的各种问题:1.forward期间nan,2.训练过程中loss scale一泻千里最终导致训练崩溃,以及如何debug。

简介

FP16(半精度浮点数)表示能够提升拥有TensorCore架构的GPU的计算速度(V100)。有很多相关介绍对其运作原理和使用方法进行了说明,本文就不再赘述。其优点可以概括为2点:

1)FP16只占用通常使用的FP32一半的显存。

2)NVIDIA V系列GPU在对FP16计算速度比FP32快上许多(实际加速比根据混合精度使用策略的不同存在差异)。

但是由于FP16的精度远不如FP32,FP16 (6e−8∼65504)(6e-8\sim65504)(6e-8\sim65504) ,FP32 (1.4e−45∼1.7e38)(1.4e-45\sim1.7e38)(1.4e-45\sim1.7e38) ,FP16需要结合混合精度(Mixed Precision)机制。即使用FP32保存模型参数和完成梯度更新,并且进行一些求和累加的操作(Normalization层)。同时还有另一个非常重要的机制,即损失放大(Loss Scaling)。通过将loss放大X倍避免backward计算梯度的时候发生下溢(Underflow)。针对损失放大问题,NVIDIA官网中[1]的下图介绍非常形象

由于FP的精度限制,大部分较小的grads在backward计算的时候会被FP16忽略

因此需要对loss乘一个loss scale,将其传递到FP16 representable range的安全区域中,这样backward能够享受FP16带来的加速,且对精度的影响控制到最小(计算完梯度后转回FP32除以loss scale再加到同样FP32的模型参数上)。那么这个loss scale自然是越大越能低效下溢问题,但是注意loss scale也不能太大,因为太大的loss sclae会造成grad超出FP16的上限65504从而造成上溢问题。NVIDIA官方推荐的scale阈值是128,换言之小于128阈值的训练通常会造成无法忽视的下溢问题。

FP16, loss scale1无法顺利收敛

换言之,如果grad中存在最大值在乘上loss scale后出现上溢,就必须降低loss scale,而过低的loss scale则会造成下溢从而破坏模型。所以现在大多框架的混合精度模块都有对loss scale的动态控制功能。即初始化一个交大的loss scale值(32768),检测到过大的grad会自动降低loss scale (/2),如果一定步数发现没有发生上溢就吧loss scale增大(*2)。

早期pytorch的混合精度训练依赖apex库,不过在1.6更新后,pytorch自带了混合精度模块torch.cuda.amp,具体使用可以参考官网介绍[2]。

# Creates a GradScaler once at the beginning of training.
scaler = GradScaler()for epoch in epochs:for input, target in data:optimizer.zero_grad()output = model(input)loss = loss_fn(output, target)# Scales loss.  Calls backward() on scaled loss to create scaled gradients.scaler.scale(loss).backward()# scaler.step() first unscales gradients of the optimizer's params.# If gradients don't contain infs/NaNs, optimizer.step() is then called,# otherwise, optimizer.step() is skipped.scaler.step(optimizer)# Updates the scale for next iteration.scaler.update()

不过本文就不再赘述怎么使用这些库了,主要来谈谈我们在混合精度训练的时候遇到的各种问题,具体则是1)forward期间nan,2)训练过程中loss scale一泻千里最终导致训练崩溃,以及如何debug。

前向loss=nan,loss scale一路下降最终退化为0,又或是放弃FP16重回FP32(更慢的训练速度,更多的显存占用和OOM风险)

Forward期间出现nan

一般是前向过程中某些步骤蕴含求和求平均的操作导致了上溢,这种问题比较容易debug,在程序中设置断点,凭借断点(二分法)或者经验来判断出问题的区域。虽然pytorch默认采用了混合精度的训练机制,会保留一些中间层计算为FP32(BN,softmax),即模型会自动在这些层计算时切换为FP32来防止上溢。具体哪些操作是FP16,哪些是FP32可以参考apex当年的划分[3]。但是任然有很多操作是落网之鱼。

这里举2个例子,首先是linear attention中的归一化中包含了多个求和einsum,sum的操作,存在严重的上溢风险。

 v_length = values.size(1)values = values / v_length  # prevent fp16 overflowKV = torch.einsum("nshd,nshv->nhdv", K, values)  # (S,D)' @ S,VZ = 1 / (torch.einsum("nlhd,nhd->nlh", Q, K.sum(dim=1)) + self.eps) # 出现NANqueried_values = torch.einsum("nlhd,nhdv,nlh->nlhv", Q, KV, Z) * v_length

另一个是3D任务中常常出现的相机参数计算。由于相机参数通常数值会很大(1000以上),简单的内外参操作matmul也会造成上溢。

proj = torch.matmul(intrinsic, extrinsic) # intrinsic内参矩阵和extrinsic外参矩阵相乘出现NAN

解决方案很简单,我们在这些部分的前向固定为FP32即可

with torch.cuda.amp.autocast(enable=False): # 禁止amp自动切换精度# 之前的输出大概率是fp16,调整回fp32Q = Q.to(torch.float32) K = K.to(torch.float32)values = values.to(torch.float32)v_length = values.size(1)values = values / v_length  # prevent fp16 overflowKV = torch.einsum("nshd,nshv->nhdv", K, values)  # (S,D)' @ S,VZ = 1 / (torch.einsum("nlhd,nhd->nlh", Q, K.sum(dim=1)) + self.eps)  # 没有上溢问题queried_values = torch.einsum("nlhd,nhdv,nlh->nlhv", Q, KV, Z) * v_length

3D相机矩阵操作也很简单

with torch.cuda.amp.autocast(enable=False): # 禁止amp自动切换精度proj = torch.matmul(intrinsic.to(torch.float32), extrinsic.to(torch.float32))

由于只有极少的计算被我们切换到了FP32,我们依旧能够享受到大量FP16带来的加速福利。

loss scale一泻千里

相比起训练一开始就出现nan问题,训练到中途才发现loss scale突然在某个节点疯狂下降,最终导致训练崩溃才是真正混合精度训练的拦路虎。

睡了一晚上,第二天发现白训

首先我们在训练过程中要时刻监控loss scale,不要早就挂掉了才后知后觉

scaler = torch.cuda.amp.GradScaler()
current_loss_scale = scaler.get_scale()
if step % log_iter == 0:print('scale:', current_loss_scale)

loss*loss scale的操作是在scaler.scale(loss).backward()完成的,而unsacle是在scaler.step(optimizer)中完成的。所以我们只要在这2步中间观察每层的梯度数值范围,即可确认是哪里溢出了。

# 区分params为不同group,以方便定位对应的layer_name
param_groups = []
for n, p in model.named_parameters():if p.requires_grad:param_groups.append({'params': [p], 'lr': opt_args['lr'], 'weight_decay': opt_args['weight_decay'], 'layer_name': n})optimizer = torch.optim.AdamW(param_groups, lr=opt_args['lr'], weight_decay=opt_args['weight_decay'])...# Creates a GradScaler once at the beginning of training.
scaler = GradScaler()for epoch in epochs:for input, target in data:optimizer.zero_grad()output = model(input)loss = loss_fn(output, target)# Scales loss.  Calls backward() on scaled loss to create scaled gradients.scaler.scale(loss).backward()# 检查梯度with torch.no_grad():for group in optimizer.param_groups:for param in group["params"]:if param.grad is None:continueif param.grad.is_sparse:if param.grad.dtype is torch.float16:param.grad = param.grad.coalesce()to_unscale = param.grad._values()else:to_unscale = param.gradv = to_unscale.clone().abs().max()if torch.isinf(v) or torch.isnan(v):print('INF in', group['layer_name'], 'of step', global_step, '!!!')# scaler.step() first unscales gradients of the optimizer's params.# If gradients don't contain infs/NaNs, optimizer.step() is then called,# otherwise, optimizer.step() is skipped.scaler.step(optimizer)# Updates the scale for next iteration.scaler.update()

使用这种方式可以很快定位到频繁上溢的层

block1.0.attn频繁出现梯度上溢

上述例子中,笔者finetune了一个Vision Transformer (VIT),其block1.0.attn频繁出现梯度上溢导致了最终的崩溃。我查看了其未上溢的最大grad为8~10,确实非常不稳定。因此笔者将这层固定为FP32从而训练可以稳定。方法同上,使用torch.cuda.amp.autocast(enable=False)和to(torch.float32)。由经验而言,很多grad上溢出现在最初输入的几层,也许是梯度反传造成的梯度累计爆炸问题造成的?也许和post norm和pre norm的研究有关。大多这些不稳定的模型都是使用pre norm,而post norm可能训练会更加稳定,但是前期的层反而会出现一些梯度消失的问题。

参考 whaosoft aiot http://143ai.com

  1. ^https://docs.nvidia.com/deeplearning/performance/mixed-precision-training/index.html

  2. ^https://pytorch.org/docs/stable/notes/amp_examples.html

  3. ^https://github.com/NVIDIA/apex/blob/082f999a6e18a3d02306e27482cc7486dab71a50/apex/amp/lists/functional_overrides.py

Pytorch混合精度训练相关推荐

  1. Pytorch 混合精度训练(Automatic Mixed Precision)原理解析

    Pytorch 混合精度训练(Automatic Mixed Precision)原理解析 1. Overview 默认情况下,大多数深度学习框架(比如 pytorch)都采用 32 位浮点算法进行训 ...

  2. 混合精度训练-Pytorch

    目录 1.需求解读 2.F16和FP32的区别与联系 3.F16优点简介 4.F16缺点简介 5.混合精度训练代码实战 5.1 代码实现 5.2 代码解析 6.F16训练效果展示 7.个人总结 参考资 ...

  3. pytorch 中 混合精度训练(真香)

    一.什么是混合精度训练 在pytorch的tensor中,默认的类型是float32,神经网络训练过程中,网络权重以及其他参数,默认都是float32,即单精度,为了节省内存,部分操作使用float1 ...

  4. pytorch apex 混合精度训练和horovod分布式训练

    转载请注明出处: https://mp.csdn.net/postedit/103600124 如果你基于pytorch训练模型,然后,你想加快训练速度,增大batch_size,或者,你有一台配置多 ...

  5. 基于OpenSeq2Seq的NLP与语音识别混合精度训练

    基于OpenSeq2Seq的NLP与语音识别混合精度训练 Mixed Precision Training for NLP and Speech Recognition with OpenSeq2Se ...

  6. 浅谈深度学习混合精度训练

    ↑ 点击蓝字 关注视学算法 作者丨Dreaming.O@知乎 来源丨https://zhuanlan.zhihu.com/p/103685761 编辑丨极市平台 本文主要记录下在学习和实际试用混合精度 ...

  7. 实战 PK!RTX2080Ti 对比 GTX1080Ti 的 CIFAR100 混合精度训练

    雷锋网 AI 科技评论按:本文作者 Sanyam Bhutani 是一名机器学习和计算机视觉领域的自由职业者兼 Fast.ai 研究员.在文章中,他将 2080Ti 与 1080Ti 就训练时长进行了 ...

  8. 模型训练慢和显存不够怎么办?GPU加速混合精度训练

    目录 混合精度训练 理论原理 三大深度学习框架的打开方式 Pytorch Tensorflow PaddlePaddle 混合精度训练 一切还要从2018年ICLR的一篇论文说起... <MIX ...

  9. 使用Apex进行混合精度训练

    使用Apex进行混合精度训练 转自:https://fyubang.com/2019/08/26/fp16/ 你想获得双倍训练速度的快感吗? 你想让你的显存空间瞬间翻倍吗? 如果我告诉你只需要三行代码 ...

最新文章

  1. 对Linux中/etc/fstab中的说明
  2. 浏览器上的HTML5语音识别功能实例页面
  3. android实时声音信号波形_Android输出正弦波音频信号(左右声道对称)-阿里云开发者社区...
  4. php 修改多级菜单,用PHP实现多级树型菜单
  5. html图片浮空但占位置,求助:鼠标经过图片时,图片悬浮出现变大
  6. 无刷滑环全面分析大全
  7. ASC转换BCD,ASC2BCD
  8. DHCP服务以及配置DHCP服务器
  9. 叶脊网络架构(Spine-Leaf)
  10. 17zwd获得17网商品详情接口 API 返回值说明,API接口获取方案
  11. ckplayer在手机用不了解决方法
  12. 【JVM基础】垃圾回收算法详解(引用计数、标记、清除、压缩、复制)
  13. Windows下清理maven仓库中的lastUpdated文件
  14. 时间序列的数据分析(七):数据平稳性
  15. 【Unity】:从【3ds Max】为人形骨骼 humanoid 添加动画
  16. CocosCreator之微信小游戏的抽奖转盘
  17. android最新系统排行,安卓系统最新排名公布!论流畅MIUI也只能排第六
  18. js小学生图片_我是小学生图片
  19. 车载以太网 - 初识DoIP - 01
  20. 百度接口(1)小票识别+票体主题内容处理

热门文章

  1. 【医学成像】超声成像中的分辨率
  2. C# 客户端急速缓存方法 使用哈希表
  3. 怎样快速抠图ps图片?这些小妙招了解一下
  4. 多多买菜,拼多多的第二增长曲线
  5. 云函数.批量操作数据库
  6. Netty介绍与实战(三)之粘包拆包
  7. App是什么,可以分为几类?及其相关解释。
  8. 重点来了,具有优质脂肪的坚果居然是减肥的好帮手!
  9. 计算机硬件专业叫什么作用,计算机硬件的五大功能是什么呢
  10. ppt模板如何制作个性海报?