class_loss.backward(retain_graph=True)
这行代码使用 backward()
函数对分类损失进行反向传播,计算梯度,并将梯度传播到模型的参数中。参数 retain_graph=True
表示在计算梯度时保留计算图,这样可以在后续的计算中重复使用计算图,而不需要重新计算。
通常情况下,在调用一次 backward()
后,计算图会被释放,如果需要再次使用相同的计算图进行额外的计算或梯度更新,就需要将 retain_graph
设置为 True
。
# args.start_iter=500, args.mid_iter=2000, args.alpha=1
if batch_idx>args.start_iter or epoch>1:
if batch_idx>args.mid_iter or epoch>1:
args.eps=0.999
alpha = args.alpha
else:
u = (batch_idx-args.start_iter)/(args.mid_iter-args.start_iter)
alpha = args.alpha*math.exp(-5*(1-u)**2)
如果batch_idx在500到2000之间:
人造噪声标签
# batch_size=32, args.perturb_ratio=0.5
for i in range(args.num_fast):
targets_fast = targets.clone()
randidx = torch.randperm(targets.size(0))
for n in range(int(targets.size(0)*args.perturb_ratio)):
num_neighbor = 10
idx = randidx[n]
feat = feats[idx]
feat.view(1,feat.size(0))
feat.data = feat.data.expand(targets.size(0),feat.size(0))
dist = torch.sum((feat-feats)**2,dim=1)
_, neighbor = torch.topk(dist.data,num_neighbor+1,largest=False)
targets_fast[idx] = targets[neighbor[random.randint(1,num_neighbor)]]
- targets.size(0)=32
- randidx是包含了 0 到 31 的整数的随机排列
- for n in range(16):替换掉原始标签的一半,使噪声概率为0.5
-
_, neighbor = torch.topk(dist.data,num_neighbor+1,largest=False
dist.data是一个包含了每个样本与其他样本的距离的张量。 num_neighbor + 1表示要找到距离最近的前 num_neighbor + 1个样本(因为最近的样本是其自身,所以需要额外加 1)。 参数 largest=False表示找到最小的元素。 返回的 neighbor张量包含了最近的 num_neighbor + 1 个样本的索引。
random.randint(1, num_neighbor)
返回一个介于 1 和num_neighbor
之间(包括 1 和num_neighbor
)的随机整数。
Meta-train
fast_loss = criterion(outputs,targets_fast)
grads = torch.autograd.grad(fast_loss, net.parameters(), create_graph=True, retain_graph=True, only_inputs=True)
for grad in grads:
grad = grad.detach()
grad.requires_grad = False
fast_weights = OrderedDict((name, param - args.meta_lr*grad) for ((name, param), grad) in zip(net.named_parameters(), grads))
为什么不用backward(),因为这一步不是为了更新整个模型的参数,反而需要保留
fast_weights为有序字典
留下一个疑问:grad的各种用法
sys.stdout.write('\r')
sys.stdout.write('| Epoch [%3d/%3d] Iter[%3d/%3d]\t\tLoss: %.4f Acc@1: %.3f%%'
%(epoch, args.num_epochs, batch_idx+1, (len(train_loader.dataset)//args.batch_size)+1, class_loss.data[0], 100.*correct/total))
sys.stdout.flush()
if acc > best:
best = acc
print('| Saving Best Model (net)...')
save_point = './checkpoint/%s.pth.tar'%(args.id)
save_checkpoint({
'state_dict': net.state_dict(),
'best_acc': best,
}, save_point)
save_checkpoint({...}, save_point)
:这行代码调用了一个函数 save_checkpoint()
,并传入了两个参数:一个字典和保存模型参数的文件路径。这个函数的作用是将模型的状态字典和其他相关信息保存到指定的文件中。
record=open('./checkpoint/'+args.id+'.txt','w')
record.write('learning rate: %f\n'%args.lr)
record.write('batch size: %f\n'%args.batch_size)
record.write('start iter: %d\n'%args.start_iter)
record.write('mid iter: %d\n'%args.mid_iter)
record.flush()
record = open('./checkpoint/' + args.id + '.txt', 'w')
:这行代码打开一个文本文件,文件名根据参数args.id
动态生成,并将文件对象赋值给变量record
。参数'w'
表示以写入模式打开文件,如果文件不存在则创建文件,如果文件已存在则清空文件内容。record.flush()
:这行代码用于强制将缓冲区中的数据写入到文件中,确保数据被及时地写入到文件中。
net = models.resnet50(pretrained=True)
net.fc = nn.Linear(2048,14)
net.fc = nn.Linear(2048, 14)
将最后一层全连接层(fc
)替换为一个新的线性层。原始的 ResNet-50 模型中,最后一层全连接层的输入特征数量为 2048(即 ResNet-50 最后一个池化层输出的特征图大小为 2048x1x1),输出特征数量为 1000(对应 ImageNet 数据集的类别数量)。这里将输出特征数量修改为 14,以适应你的特定任务,其中 14 是你的任务中的类别数量。
best_model = torch.load('./checkpoint/%s.pth.tar'%args.id)
test_net.load_state_dict(best_model['state_dict'])