一种增大Batch的训练方法,低显存GPU也能加载更大的Batch

​炼丹药的迷惑

相信各位丹友在炼丹期间都会遇到各种各样的问题,有些问题查查资料,做做实验就解决了,有些问题可能还是需要等大佬们去解决。本人在炼丹期间,就遇到了一个问题,这里简单分享一下,一般我们训练模型的时候,选择一个较大的Batch加载训练数据是一个高效训练的方法,当然也不是说Batch越大就越好,一个合适的Batch size对于模型训练才是最好的选择。如果输入是高分辨率图像的时候,由于显存的限制,batch就会变得很小,而且GPU显存总是告急,但是GPU计算资源却大量冗余。如何在加载大分辨率图像的时候增大Batch,有效的利用计算资源,是我们需要解决的问题。

一般当图像分辨率较小的时候,我们不会过于担心训练过程中Batch size的设置问题,由于图像分辨率较小,加载较大Batch不会出现显存不够用的情况,但是当遇到较大分辨率的图片做为训练数据的时候,情况就不一样了,这时候往往会需要更大显存来加载数据,模型训练中一般会出现一次加载的Batch极度变小的情况,这无疑加大了模型的训练时间,对于加了BN层的模型来说还会影响到训练效果。比如在训练YOLO模型的时候,当图像的大小达到了640*640,在6G的显存上一次只能加载几张图片。而对于训练一个泛化性能较好的模型,往往需要更大的数据集来学习,一边是捉襟见肘的显存,一边是更大的训练数据集,显而易见,扩大显存已经是迫在眉睫。

增加显存最好的方法,是更换更大显存的GPU,但是这种从硬件上直接下手的方法不是谁都有这个资本的,毕竟显卡是用真金白银换来的,所以最好办法是从模型上下手去操作。我们在训练大分辨率的图像数据的时候,最容易出现的情况就是一边是显存溢出,一边算力闲置。

所以联想到能否以闲置的计算资源来换取匮乏的显存呢?其实大佬们早以为我们提供了方法。

大批量的炼药方法

2020年6月17日发布的论文《Dynamic Tensor Rematerialization》全面论述了以计算资源换取显存的可行性。该论文在此后的时间了进行了几次改进,直到2021年3月18日出现了V4版本,论文也提供了pytorch中的使用方法。

论文介绍中也简单的阐述了基本的实现思路:

检查点通过从内存中释放中间激活并按需重新计算它们,可以在有限的内存预算下训练深度学习模型。当前的检查点技术静态地计划这些离线重新计算并假设静态计算图。我们证明了一个简单的在线算法可以通过引入动态张量重新实现(DTR)来实现可比的性能,这是一种用于检查点的贪婪在线算法,具有可扩展性和通用性,由驱逐策略参数化,并支持动态模型。我们证明了 DTR 可以训练一个N-层线性前馈网络 Ω (N--√) 内存预算只有 O (N)张量运算。DTR 与模拟实验中最佳静态检查点的性能密切匹配。我们仅通过插入张量分配和运算符调用并收集有关张量的轻量级元数据,将 DTR 原型合并到 PyTorch 中。

 基本操作就是使用GPU的计算资源换取神经网络每次前向传播过程中的部分中间缓存值,简而言之,就是以GPU的计算力换取GPU的显存空间,从而加载更大的batch。具体做法就是释放模型中的部分前向激活输出值,等后向传播的时候再从当前节点的上一层重新计算一次该层的输出,而不是一直保存在显存中,这样就等于提前释放了这部分显存空间,等下一次加载新的数据的时候就可以加载更大的batch。

下图是正常的模型前向和后向运行过程中每个节点的张量释放情况:

下图是使用了以计算资源来换取显存后的模型每个节点的张量释放情况:

关于释放哪一部分缓存值才是最高效的替代方法,也有一定的讲究。具体来说满足以下三个需求即可:

①被释放后需要重新计算的张量对于GPU的开销越小越好

②被释放的张量占用的显存越大越好

③被释放的张量在显存中占用的时间越长越

 

其中,H localDTR为被释放的最优张量;C0(t)为重新计算张量时该算子运行的时长,运行时间越短对于GPU的开销越小;s(t)是张量进入显存的时长,越长越好;m(t)是张量占用的内存,越大越好。

我们的模拟显示了在合理的计算开销上的显著节省。虽然由于混叠和变异的复杂性,我们无法实现现有的静态检查点方案作为基线(特别是在动态模型中),但我们注意到,与专家手工修改模型相比,这些结果节省了类似数量的内存。例如,一个手动优化的DenseNet-BC模型[27]在降低25.5%的情况下实现了56.1%的内存消耗,而我们使用DTR-EqClass的模拟试验产生了20.0%的内存消耗,开销22.7%。尽管这些数字并不具有直接的可比性,因为模拟不包括动态分析开销,但它们说明了DTR(使用适当的启发式)可以实现内存计算的折衷,否则就需要专家进行干预。

此外,与现有的静态方法不同,DTR在具有任意动态性的模型上自动节省内存,尽管它开始在LSTM和TreeLSTM的较低预算上大打折扣。在所有情况下,结果表明,更复杂的启发式方法可以通过更低的操作符开销获得更好的内存节省,尽管这些复杂的启发式方法也引入了更多的运行时开销,这在DTR的实现中必须加以考虑。值得注意的是,即使是最不复杂的启发式算法,如LRU(需要非常少的运行时开销),也可以在抖动之前用非常少的开销实现高达30%的内存节省,这表明检查点可以很容易地获得一些内存节省。

 

论文中比较了不同模型上的不同方法,比较了不同预算(原始峰值内存使用量的一小部分)的计算减速率。每个图中的黑色区域对应于存储输入和权重所需的内存,而灰色区域表示需要同时使用最多内存的单个操作符。虚线和虚线分别表示抖动和内存不足错误之前的最后一个比率。

以下是论文中对于使用DTR前后的显存情况对比:

此外,论文中还提出了DTR的三种变种体:DTR-Full、DTR-EqClass、DTR-Local,对于3种主要hDT R变体,通过不同的内存比率比较启发式评估和元数据维护引起的总存储访问。

关于tensorflow和pytorch上的使用方法:https://github.com/merrymercy/dtr-prototype

最后祝大家在炼丹的路上越走越好,有什么好的方法也可以互相讨论。

关注微信公众号:深度人工智能学院,获取更多人工智能方面的知识!

              

                官方公众号                                                    官方微信号

<think>嗯,用户问的是关于NER微调训练GPU内存占用的问题,特别是为什么不同模型在相同batch size下显存占用差不多。我需要先回忆一下影响显存的因素,然后分析可能的原因。 首先,显存占用主要和模型参数量、前向传播的中间变量、优化器状态、batch size、序列长度等有关。用户提到加载不同的模型但batch size相同,显存却差不多,这有点反直觉,因为不同模型参数量应该不同,比如BERT-base和Roberta-large差异大,显存占用应该不一样。 可能的原因有几个:首先,用户可能用的是同系列模型,比如都是BERT-base的不同变种,参数量接近,所以显存差异不大。其次,可能他们的batch size设置得很小,导致显存瓶颈不在模型参数而在激活值或其他部分。或者他们可能使用了混合精度训练,减少了显存占用,使得不同模型之间的差异变小。 另外,序列长度也是一个因素。比如,如果两个模型处理的序列长度差异大,显存占用可能不同,但如果长度相近,可能影响不大。还有优化器,比如Adam会比SGD占用更多显存,但如果优化器相同,可能这部分的差异被抵消了。 还有可能用户提到的不同模型其实结构差异不大,比如都是基于Transformer的,只是某些层数不同,但总参数量接近。或者他们在加载模型可能没有正确加载全部参数,导致实际使用的参数量相同。 需要进一步考虑是否有梯度检查点技术,这会减少显存但可能增加计算间。或者数据类型的差异,比如fp16和fp32的影响。另外,如果两个模型的实际有效batch size不同,比如累积梯度的情况,可能也会影响显存,但用户说设置相同,所以这个可能性较。 总结下来,可能的原因包括模型参数量差异不大、batch size较小、序列长度相近、优化器相同、使用混合精度或梯度检查点,或者模型结构相似。需要建议用户检查模型参数量、序列长度、是否使用混合精度、优化器类型以及中间激活的内存占用情况。</think>在自然语言处理任务(如NER)的微调训练中,GPU显存占用大小主要与以下几个因素相关: **1. 模型参数量** - 模型本身的参数量是显存占用的核心因素,参数量越大显存需求越高 - 但若您加载的不同模型参数量级相近(如同属BERT-base级别),显存差异可能不明显 - 例如:BERT-base(110M参数)和 RoBERTa-base(125M参数)的显存占用差异可能小于10% **2. Batch Size与序列长度** - 显存占用与 `batch_size * sequence_length` 成正相关 - 即使batch_size相同,实际处理的token数量(由序列长度决定)会显著影响显存 - 例如:处理512长度序列的batch_size=8,可能比处理128长度的batch_size=32更耗显存 **3. 中间激活值(Activations)** - 前向传播过程中产生的中间结果需要缓存以供反向传播使用 - 这部分内存占用通常与模型层数、隐藏层维度、注意力头数等结构参数相关 - 对于结构相似的模型(如同为12层Transformer),这部分占用差异可能较小 **4. 优化器状态** - 使用Adam优化器,需要存储参数对应的momentum和variance,相当于2倍参数量 - 若使用混合精度训练,可能还需额外存储FP32精度的主参数副本 **5. 梯度存储** - 反向传播需保存所有参数的梯度,占用空间等于参数量大小 **为什么相同batch_size显存占用相似的解释:** 1. **模型规模接近**:若您加载的不同模型参数量级相近(如都是base版模型),核心参数存储差异不大 2. **显存瓶颈转移**:当batch_size较小显存主要被模型参数和优化器状态占据,此改变batch_size对总占用的影响比例较 3. **序列长度相同**:如果不同模型使用相同的最大序列长度,激活值内存消耗可能接近 4. **框架优化机制**:深度学习框架(如PyTorch)的内存管理会复用显存,不同模型的实际占用可能呈现非线性变化 **验证建议:** 1. 使用`nvidia-smi`命令观察显存变化的细节 2. 通过`torch.cuda.max_memory_allocated()`精确测量显存占用 3. 对比不同模型的实际参数量: ```python from transformers import AutoModel model = AutoModel.from_pretrained("model_name") print(f"Parameters: {sum(p.numel() for p in model.parameters()):,}") ``` 4. 尝试极端对比(如BERT-tiny vs BERT-large),观察显存差异是否显著 对于需要降显存占用的场景,可以考虑: - 使用梯度累积(gradient accumulation) - 启用混合精度训练(fp16/bf16) - 应用梯度检查点(gradient checkpointing) - 使用参数高效微调方法(如LoRA, Adapter)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值