ResNet在CIFAR-10上的原论文实现与训练配置问题咨询
首先是引用的论文训练配置原文:
我们设置weight decay为0.0001、momentum为0.9,采用文献[13]提出的权重初始化方法与BN[16],不使用dropout。模型在2张GPU上以minibatch size=128训练:初始learning rate为0.1,训练至32k、48k iterations时将学习率除以10,训练至64k iterations时终止,上述迭代步数设置基于45k/5k的train/val split确定。训练阶段采用文献[24]的简易data augmentation方案:图像每侧padding 4像素,从padding后的图像或其horizontal flip结果中随机裁剪32×32尺寸的区域作为输入;测试阶段仅对原始32×32尺寸的图像做单视图评估。
问题1:2张GPU下minibatch size=128的具体含义
是的,这里的128指全局总batch size,2张GPU并行训练时单卡承载的batch size为64。训练时两张卡分别计算各自64个样本的梯度,做跨卡聚合后统一更新模型权重,训练动态和单卡跑128 batch size完全一致。如果用PyTorch的DDP做分布式训练,直接给单卡设置batch size=64即可。
问题2:iterations与epochs的换算规则、总训练epoch计算
先明确两个单位的标准定义:
- 1个iteration:完成1次「前向传播-反向传播-权重更新」全流程,单次处理的样本数等于全局batch size,本配置下为128
- 1个epoch:遍历完整份训练集(本配置下为划分后的45k训练样本)的过程
你计算的总等价epoch数是正确的,即64000 * 128 / 45000 ≈ 182.04。注意原论文的训练调度完全以iteration为计数单位,没有用epoch作为调度节点,不需要硬套整数epoch的训练逻辑。
问题3:PyTorch实现逻辑、剩余样本处理、非整数epoch节点调度
各细节的实现方案如下:
- 剩余样本处理:原实现会丢弃每个epoch最后凑不够128个的72个剩余样本,对应PyTorch中只需将
DataLoader的drop_last参数设为True,设置后每个epoch固定跑45000 // 128 = 351个iteration。 - 学习率调整与训练终止:不要将32k、48k、64k的节点舍入到最近的整数epoch,原实现是精确按iteration数触发调整和终止的,完全可以在某个epoch的训练中途修改学习率、直接结束训练,不需要等当前epoch跑完。
- 其他配置对齐:论文提到的He初始化就是PyTorch卷积、全连接层默认的
kaiming_normal_初始化,BN层默认参数(权重初始为1、偏置初始为0)也和要求一致,模型结构中不要添加Dropout层即可;对应的数据增强方案可以直接通过torchvision的transforms接口实现,和原方案完全匹配。
核心训练循环参考实现(按iteration计数,最不容易出错):
import torch from torch.optim import SGD from torchvision import transforms from torch.utils.data import DataLoader # 1. 数据增强与数据集加载 train_transform = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.2023, 0.1994, 0.2010]) # CIFAR-10数据集原生统计值,和原实现对齐 ]) test_transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.2023, 0.1994, 0.2010]) ]) # 注意train_dataset是你按45k/5k划分好的训练子集 train_loader = DataLoader( train_dataset, batch_size=128, shuffle=True, num_workers=4, drop_last=True ) # 2. 模型、优化器初始化 model = resnet_cifar(depth=20) # 自行实现的CIFAR版本ResNet optimizer = SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) model = model.cuda() # 多卡训练就这里包一层DDP即可,单卡batch size设64全局凑128 # 3. 按iteration计数的训练循环 total_iter = 0 max_iter = 64000 # 学习率节点:初始0.1,到32k变0.01,到48k变0.001 lr_schedule = {32000: 0.01, 48000: 0.001} model.train() while total_iter < max_iter: for x, y in train_loader: if total_iter >= max_iter: break # 到点直接修改优化器学习率 if total_iter in lr_schedule: for param_group in optimizer.param_groups: param_group['lr'] = lr_schedule[total_iter] # 常规训练步骤 x, y = x.cuda(), y.cuda() optimizer.zero_grad() loss = torch.nn.functional.cross_entropy(model(x), y) loss.backward() optimizer.step() total_iter += 1
内容的提问来源于stack exchange,提问作者Alex

