可以在神经网络推理过程中,针对不同的层,采用不同的数据精度进行计算,从而实现节省显存和加快速度的目的。
通常,深度学习中使用的精度为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精度的最低分辨率,从而丢失)1. 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
: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 factorenabled
: 是否做 scale
对outputs乘 scale factor,并返回,如果enabled=False就原样返回。
step 方法在做两件事情:
对梯度 unscale,如果之前没有手动调用unscale方法的话
检查梯度溢出,如果没有nan/inf,就执行 optimizer 的 step,如果有就跳过
注意:GradScaler的step不支持传 closure。
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}")
在这个示例中:
- 创建一个 GradScaler 对象 scaler
- 定义模型和优化器
- 在训练循环中,使用 autocast() 上下文管理器将前向传递操作包装起来,这样就可以使用混合精度进行计算
- 调用 scaler.scale(loss) 计算损失的缩放版本,并调用 scaler.step(optimizer) 来更新模型参数
- 最后使用 scaler.update() 更新 GradScaler 对象的内部状态
- 这个过程可以重复进行多次,直到训练结束。
原文链接:https://blog.csdn.net/qq_43369406/article/details/130393078