问题一:batch_size对显存占用的影响:batch_size=1和batch_size=2的显存占用区别
问题二:降低临时变量精度的问题及解决方案
batch_size=2的显存占用不会简单地是batch_size=1的2倍,通常会略少于2倍。
2 × batch_size=1的激活显存其中:
batch_size与显存占用不是简单的线性关系
存在大量与batch_size无关的固定显存开销
增加batch_size的边际显存成本递减
更大的batch_size可能获得更好的内存管理效率
降低临时变量精度(如从FP32降到FP16或BF16)可以:
约50%
支持Tensor Core的GPU
允许使用更大的batch_size或模型
会影响softmax、sigmoid激活函数的输出,梯度更新过程,BatchNorm层的数值计算等
import torch
from torch.cuda.amp import autocast, GradScaler
# 初始化梯度缩放器
scaler = GradScaler()
for data, target in train_loader:
optimizer.zero_grad()
# 自动混合精度上下文
with autocast():
output = model(data)
loss = criterion(output, target)
# 缩放损失以防止梯度下溢
scaler.scale(loss).backward()
# 优化器步进(自动缩放回原始精度)
scaler.step(optimizer)
scaler.update()
def manual_gradient_scaling(model, loss, scale_factor=1024.0):
"""手动梯度缩放实现"""
scaled_loss = loss * scale_factor
scaled_loss.backward()
# 梯度缩放回原始值
for param in model.parameters():
if param.grad is not None:
param.grad = param.grad / scale_factor
class DynamicLossScaler:
def __init__(self, init_scale=65536.0, scale_factor=2.0, scale_window=2000):
self.scale = init_scale
self.scale_factor = scale_factor
self.scale_window = scale_window
self.counter = 0
def step(self, model, optimizer):
# 检查梯度溢出
has_inf = torch.isinf(torch.cat([p.grad.flatten()
for p in model.parameters()
if p.grad is not None])).any()
if has_inf:
# 梯度溢出,减小缩放因子
self.scale /= self.scale_factor
optimizer.zero_grad()
return False # 需要重新前向传播
else:
# 梯度正常,更新缩放因子
self.counter += 1
if self.counter >= self.scale_window:
self.scale *= self.scale_factor
self.counter = 0
return True # 可以继续优化
# 使用BF16格式(需要硬件支持)
model = model.to(dtype=torch.bfloat16)
# 在支持BF16的硬件上训练
for data, target in train_loader:
data = data.to(dtype=torch.bfloat16)
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
class NumericalStableNet(nn.Module):
def __init__(self):
super().__init__()
# 使用数值稳定的激活函数
self.activation = nn.ReLU() # 而非nn.Tanh()
self.norm = nn.LayerNorm(eps=1e-6) # 增加数值稳定性
def forward(self, x):
x = self.activation(x)
x = self.norm(x)
return x
# 数值稳定的softmax实现
def stable_softmax(x, dim=1):
"""数值稳定的softmax,防止上溢下溢"""
x_max = torch.max(x, dim=dim, keepdim=True)[0]
x_exp = torch.exp(x - x_max) # 减去最大值防止上溢
return x_exp / torch.sum(x_exp, dim=dim, keepdim=True)
def get_optimal_dtype():
if torch.cuda.is_available():
gpu_name = torch.cuda.get_device_name()
if 'A100' in gpu_name or 'V100' in gpu_name:
return torch.bfloat16 # 新一代GPU支持BF16
else:
return torch.float16 # 老一代GPU使用FP16
return torch.float32
batch_size与显存占用不是简单的线性关系,存在大量固定开销
降低精度可以显著减少显存占用,但需要解决数值稳定性问题
混合精度训练是最佳实践,结合了效率和稳定性
梯度缩放技术是解决低精度训练问题的核心方法