趋近智
大语言模型微调,尤其是在使用全参数更新或甚至更巧妙的参数高效微调(PEFT)方法处理超大模型时,常常让现有硬件资源的限制显现。尽管优化已训练好的模型以进行推理会在其他地方介绍,但在微调过程中管理内存占用的方法是主要的考虑。GPU内存不足(通常表现为CUDA内存溢出错误)是一个常见难题,会导致训练中断并需要调整。幸运的是,有多种策略可以缓解这种压力,通常是以计算时间略微增加为代价来显著节省内存。
导致内存达到限制的直接方式之一是尝试使用大批量大小。尽管较大的批量有时可以带来更稳定的梯度和按周期计算的更快收敛,但批量中的每个样本在正向传播时消耗激活内存,在反向传播时消耗梯度内存。
梯度累积提供了一种巧妙的应对方法。它模拟了更大的有效批量大小,而无需同时将整个大批量载入内存。其主要思想是按顺序处理几个较小的“微批量”,计算每个微批量的梯度,并在执行单个优化器步骤和更新模型权重之前累积这些梯度。
以下是典型的处理流程:
accumulation_steps):
accumulation_steps。这可以防止累积梯度幅度相对于单个大批量梯度变得过大。accumulation_steps后,执行优化器步骤(optimizer.step())。这会使用来自所有微批量的聚合梯度更新模型权重。optimizer.zero_grad())以准备下一个累积周期。实际操作中,如果你的目标批量大小是64,但GPU只能处理批量大小为8的数据,你可以设置accumulation_steps = 8(因为 64/8=8)。模型将执行8次正向和反向传播,累积梯度,然后只更新一次权重,从而达到与直接使用批量大小为64相同权重的更新效果。
考量:
在深度神经网络(如Transformer)的正向传播过程中,各层中间输出(激活值)通常存储在内存中。这些激活值在反向传播时需要用来计算梯度。对于层数多且隐藏维度大的模型,这些存储的激活值所占用的内存会变得相当可观。
激活检查点,也称为梯度检查点,提供了一种权衡:它通过不存储所有中间激活值来减少内存使用。相反,它在正向传播期间有策略地只保存一部分激活值。在反向传播时,当需要一个未保存的激活值时,该技术会从最近的已保存激活值开始执行部分正向传播,即时重新计算它。
权衡:
实现:
现代深度学习框架通常提供工具,可以相对轻松地启用激活检查点。例如,在PyTorch中,你可以使用torch.utils.checkpoint.checkpoint。Hugging Face transformers库通常允许通过模型配置中的gradient_checkpointing=True标志或在Trainer设置期间启用它。这抽象化了决定保存哪些激活值和管理重新计算的复杂性。
当面临内存限制时,激活检查点是一种有价值的技术,特别是如果仅靠梯度累积不足,或者你需要为其他目的(例如使用更复杂的优化器)释放内存时。
默认情况下,大多数深度学习模型使用32位浮点数(fp32或单精度)进行训练。混合精度训练指的是在训练过程中结合使用fp32和低精度格式,主要是16位浮点数(fp16或半精度)。
优势:
训练期间不同组件每个模型参数的内存使用图示。请注意,权重和梯度直接受益于低精度,而优化器状态(如Adam的动量和方差)通常为了稳定性而保持在fp32。
挑战与解决方案:
fp16的主要挑战是与fp32相比其数值范围有限。小的梯度值可能变为零(“下溢”),而大的值可能超出可表示范围(“上溢”),从而导致数值不稳定和收敛不良。
自动混合精度(AMP)框架通过损失缩放来解决这个问题:
实现:
PyTorch(torch.cuda.amp)和TensorFlow(tf.keras.mixed_precision)等库提供了AMP实现,通常只需几行代码即可启用(使用自动类型转换上下文和梯度缩放器)。
替代方案:bfloat16
一种较新的16位格式,bfloat16(bf16),正在获得关注。它使用与fp16相同的位数,但分配方式不同:更少的位用于精度(尾数),更多的位用于指数。这使得bf16拥有与fp32相同的动态范围,但精度低于fp16。
在可用时,bf16与fp16相比,可以提供更简单的混合精度优势获取途径,有可能在速度、内存节省和稳定性之间取得良好平衡。
这些内存优化方法并非互斥。通常的做法是结合使用它们以达到最佳效果。例如,你可以使用:
选择合适的组合取决于具体的模型、硬件限制以及训练期间观察到的实验结果。每种方法都会引入一个权衡,主要是在内存使用和计算时间之间。通过了解和应用梯度累积、激活检查点和混合精度训练,即使面对硬件限制,你也能显著提升微调大语言模型的能力。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造