在阅读源码时,遇到了这样的情况:
# Build resnet.self.RCNN_base = nn.Sequential(resnet.conv1, resnet.bn1, resnet.relu,resnet.maxpool, resnet.layer1, resnet.layer2, resnet.layer3)self.RCNN_top = nn.Sequential(resnet.layer4)self.RCNN_cls_score = nn.Linear(2048, self.n_classes)if self.class_agnostic:self.RCNN_bbox_pred = nn.Linear(2048, 4)else:self.RCNN_bbox_pred = nn.Linear(2048, 4 * self.n_classes)# Fix blocksfor p in self.RCNN_base[0].parameters(): p.requires_grad=Falsefor p in self.RCNN_base[1].parameters(): p.requires_grad=Falseassert (0 <= cfg.RESNET.FIXED_BLOCKS < 4)if cfg.RESNET.FIXED_BLOCKS >= 3:for p in self.RCNN_base[6].parameters(): p.requires_grad=Falseif cfg.RESNET.FIXED_BLOCKS >= 2:for p in self.RCNN_base[5].parameters(): p.requires_grad=Falseif cfg.RESNET.FIXED_BLOCKS >= 1:for p in self.RCNN_base[4].parameters(): p.requires_grad=False
然后我们在config.py中可以找到对应的参数设置__C.RESNET.FIXED_BLOCKS = ``1
所以说默认情况下对resnet.conv1,resnet.bn1,resnet.layer1不进行梯度的更新。
随后这些参数会被传入到trainval_net.py中的这里:
params = []for key, value in dict(fasterRCNN.named_parameters()).items():if value.requires_grad:if 'bias' in key:params += [{'params':[value],'lr':lr*(cfg.TRAIN.DOUBLE_BIAS + 1), \'weight_decay': cfg.TRAIN.BIAS_DECAY and cfg.TRAIN.WEIGHT_DECAY or 0}]else:params += [{'params':[value],'lr':lr, 'weight_decay': cfg.TRAIN.WEIGHT_DECAY}]if args.cuda:fasterRCNN.cuda()if args.optimizer == "adam":lr = lr * 0.1optimizer = torch.optim.Adam(params)elif args.optimizer == "sgd":optimizer = torch.optim.SGD(params, momentum=cfg.TRAIN.MOMENTUM)
对没有被阻止梯度更新的层进行优化。
总结
for p in self.RCNN_base[0].parameters(): p.requires_grad=False这类代码的作用是:特征层中参数都固定住,不会发生梯度的更新。确定是否计算导数的,所以可以减少计算量,也就是不用求w和b的导数了,减少了计算量。只传播误差,而不计算权重和偏执的导数。
随后在进行梯度更新,也就是把参数放入要训练的网络时,在传入优化器之前,需要重新过滤一遍,所以有了以上优化器之前的代码。
