1、混合精度训练

混合精度训练(mixed-precision)的原理是降低部分变量和运算的精度,一般GPU默认浮点精度为32位,即每个变量占用4个字节内存,可以将浮点精度将为16位,即每个变量2个字节,于是就可以减少一半的内存占用。这样的做法不会缩减模型的规模(参数量),但会降低计算精度,提高训练速度,尤其是在支持TensorCore的GPU上。
Pytorch 1.5以后的版本均支持自动混合精度(AMP)训练,该框架可以自动识别需要全精度计算的部分,对其使用32位浮点精度运算,对其他部分则使用16位浮点运算。官方代码如下:

# Creates model and optimizer in default precision
model = Net().cuda()
optimizer = optim.SGD(model.parameters(), ...)

# Creates a GradScaler once at the beginning of training.
scaler = GradScaler()

for epoch in epochs:
	for input, target in data:
		optimizer.zero_grad()
		
		# Runs the forward pass with autocasting
		with autocast():
			output = model(input)
			loss = loss_fn(output, target)
			# Scales loss. Calls backward() on scaled loss to create scaled gradients
			# Backward passes under autocast are not recommended.
			# Backward ops run in the same dtype autocast chose for corresponding forward ops.
			scaler.scale(loss).backward()
			# Scaler.step() first unscales the gradients of the optimizer's assigned params.
			# If these gradients do not contain infs or NaNs, optimizer.step()is then called,
			# otherwise, optimizer.step() is skipped
			scaler.step(optimizer)
			# Updates the scale for next iteration.
			scaler.update()

2、梯度累积

梯度累积的原理比较容易理解:在优化器更新参数之前,使用相同的模型参数进行几次前向传播和反向传播;每次反向传播时计算的梯度累积(求和);若batch_size=N,过程中累计了M步梯度,则等效的批处理大小为N*M。但这样做的代价是训练结果精度下降,因为有些参数不能完全累积,如Batch Normalization
实现方法可以参考Pytorch官方文档

3、注意事项

  • 当同时使用混合精度和梯度累积进行训练时,scale应该为有效批次进行校准,scale更新需要在有效批次上完成
  • 当使用梯度累积进行分布式数据并行(DDP)训练时,可以调用no_sync()上下文管理器来禁用前M-1步的梯度还原,帮助进一步提高训练速度

4、梯度检查点(Gradient Checkpoint)

梯度检查点(Gradient Checkpoint)的思想是将一部分节点的中间结果保存为checkpoint,在反向传播过程中对这些节点之间的其他部分重新进行计算。在该技巧的帮助下,研究人员可以将10倍大的模型放到GPU上,且计算时间仅增加20%,Pytorch、Hugging Face均支持这一功能,仅需两行代码便可实现:

bert = AutoModel.from_pretrained("pretrained-model-name")
bert.config.gradient_checkpointing = True