因此,查看 Normal 网络的 AMP:自动混合精度训练 教程,我发现有两个版本:
Automatic
和 GradScaler
。我只是想知道在训练中使用 GradScaler
是否是“建议/必要”,因为文档中写道:
在混合精度训练时,梯度缩放有助于防止小幅度梯度冲至零(“下溢”)。另外
scaler = torch.cuda.amp.GradScaler() for epoch in range(1): for input, target in zip(data, targets): with torch.cuda.amp.autocast(): output = net(input) loss = loss_fn(output, target) scaler.scale(loss).backward() scaler.step(opt) scaler.update() opt.zero_grad()
,查看PyTorch 的 NVIDIA Apex 文档,他们将其用作,
from apex import amp
model, optimizer = amp.initialize(model, optimizer)
loss = criterion(…)
with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()
optimizer.step()
我认为这也是
GradScaler
所做的,所以我认为这是必须的。有人可以帮我解决这里的问题吗?
GradScaler()
,你的模型可能无法收敛。
使用 FP16 存在三个基本问题:权重更新:半精度,1 + 0.0001 四舍五入到 1。
autocast()
GradScaler()
autocast()
与 FP32 相比,使用低精度 FP16 通常存在两个问题。
weight += lr*gradient
)时,如下例所示。在归约运算中,fp16 会导致算术溢出。 < 2^-11 i.e. 0.00049, param update will have no effect. This means weight update (解决方案是在可能发生下溢/溢出的地方使用 FP32,PyTorch 中的 AMP 会处理这一点。
matmul、线性、conv2d、LSTMCell 等 | |
pow、sum、归一化、softmax 等 |
# Imprecise weight update
p = torch.FloatTensor([1.0]), device='cuda:0')
print(p.dtype, p + 0.0001) # weight += lr*gradient
p = torch.HalfTensor([1.0]), device='cuda:0')
print(p.dtype, p + 0.0001, '-> undeflow')
# output
torch.float32 tensor([1.0001])
torch.float16 tensor([1.], dtype=torch.float16) -> undeflow
# reduction operation
a = torch.FloatTensor(4096).fill_(16.0) # a 4096x1 tensor having each value 16.0
print(a.dtype, a.sum())
a = torch.HalfTensor(4096).fill_(16.0)
print(a.dtype, a.sum(), '-> overflow')
#output
torch.float32 tensor(65536.)
torch.float16 tensor(inf, dtype=torch.float16) -> overflow