文章目录

1. 问题描述

今天在跑代码的过程中,因为要训练一个模型然后在测试阶段使用PGD来生成相应的adv_image来测试这个模型,结果运行到测试阶段出现下面的问题。

报错如下:
RuntimeError: element 0 of tensors does not require grad and does not have a grad_

我的代码如下:

def validate_roubst(val_loader, model, criterion, epoch, args, log=None, tf_writer=None, flag='roubst_val'):
batch_time = AverageMeter('Time', ':6.3f')
losses = AverageMeter('Loss', ':.4e')
top1 = AverageMeter('Acc@1', ':6.2f')
top5 = AverageMeter('Acc@5', ':6.2f')


# switch to evaluate mode
model.eval()
all_preds = []
all_targets = []


with torch.no_grad():
end = time.time()
for i, (input, target) in enumerate(val_loader):
if args.gpu is not None:
# input = input.cuda(args.gpu, non_blocking=True)
print('............')

input = input.cuda(args.gpu, non_blocking=True)
target = target.cuda(args.gpu, non_blocking=True)

attack_method = PGD(model, args.device)
adv_example = attack_method.generate(input, target, epsilon = 8/255, num_steps = 20, step_size = 0.01, clip_max = 1.0, clip_min = 0.0, print_process = False, bound = 'linf')

# compute output
output = model(adv_example) # input
loss = criterion(output, target)

# measure accuracy and record loss
acc1, acc5 = accuracy(output, target, topk=(1, 5))
losses.update(loss.item(), input.size(0))
top1.update(acc1[0], input.size(0))
top5.update(acc5[0], input.size(0))

# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()

_, pred = torch.max(output, 1)
all_preds.extend(pred.cpu().numpy())
all_targets.extend(target.cpu().numpy())

if i % args.print_freq == 0:
output = ('Test: [{0}/{1}]\t'
'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(
i, len(val_loader), batch_time=batch_time, loss=losses,
top1=top1, top5=top5))
print(output)
cf = confusion_matrix(all_targets, all_preds).astype(float)
cls_cnt = cf.sum(axis=1)
cls_hit = np.diag(cf)
cls_acc = cls_hit / cls_cnt
output = ('{flag} Results: Prec@1 {top1.avg:.3f} Prec@5 {top5.avg:.3f} Loss {loss.avg:.5f}'
.format(flag=flag, top1=top1, top5=top5, loss=losses))
out_cls_acc = '%s Class Accuracy: %s'%(flag,(np.array2string(cls_acc, separator=',', formatter={'float_kind':lambda x: "%.3f" % x})))
print(output)
print(out_cls_acc)
if log is not None:
log.write(output + '\n')
log.write(out_cls_acc + '\n')
log.flush()

tf_writer.add_scalar('loss/test_'+ flag, losses.avg, epoch)
tf_writer.add_scalar('acc/test_' + flag + '_top1', top1.avg, epoch)
tf_writer.add_scalar('acc/test_' + flag + '_top5', top5.avg, epoch)
tf_writer.add_scalars('acc/test_' + flag + '_cls_acc', {str(i):x for i, x in enumerate(cls_acc)}, epoch)

return

2. 解决方案

出了问题当然要找到解决方案:

2.1 方案1

大多数人是说要加这一句:

loss.requires_grad_(True) #加入此句就行了

具体做法就是:

loss = criterion(output, target)
loss.requires_grad_(True) # 加入此句在这个位置

loss.backward()

但是经过本人尝试,还是没有什么用,因为我在train阶段不会 出现错误,只有在test阶段就报错。

2.2 方案2

回归本质,还是从报错的角度看,错误提示大致意思就是:元素不需要求梯度
然后我仔细瞅了瞅我那段代码,发现了一个可疑之处:with torch.no_grad()
最后仔细查看了这个东西的一些使用规则(参考文献1):

with torch.no_grad()则主要是用于停止autograd模块的工作,以起到加速和节省显存的作用,具体行为就是停止gradient计算,从而节省了GPU算力和显存,但是并不会影响dropout和batchnorm层的行为。

看到我上面加粗的字体了吧,原来使用with torch.no_grad()就不会自动求梯度了,因为我们使用PGD生成adv_image需要求梯度,所以加上with torch.no_grad()就导致了我们无法求梯度,最终出现了下面的错误。

故解决方案为:

将 with torch.no_grad() 去掉

3. 参考文献

​1. Pytorch 中 model.eval() 和 with torch.no_grad() 的区别​