在PyTorch中为批次中的每个样品计算梯度

时间:2018-12-15 23:08:37

标签: python pytorch gradient-descent

我正在尝试实现差分私有随机梯度下降的版本(例如https://arxiv.org/pdf/1607.00133.pdf),

计算大小为L的批次中每个点的梯度,然后分别剪切L个梯度中的每一个,然后将它们平均化,然后最后执行(嘈杂的)梯度下降步骤。

在pytorch中执行此操作的最佳方法是什么?

最好有一种方法可以同时计算批次中每个点的梯度:

Promise.all(IdArr.map(id => promiseGetById(id))).then(arr =>
    res.json(
        arr.map(patient => ({ name: patient.name, pic: patient.profileImage }))
    )
);

但是如果失败,请分别计算每个梯度,然后在累加之前裁剪范数,但是

x # inputs with batch size L
y #true labels
y_output = model(x)
loss = loss_func(y_output,y) #vector of length L
loss.backward() #stores L distinct gradients in each param.grad, magically

先累积第i个渐变,然后进行裁剪,而不是先进行裁剪,再将其累积到渐变中。解决此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:2)

就计算效率而言,我认为您没有比第二种方法做得更好的方法,您正在失去在backward中进行批处理的好处,这是事实。关于裁剪的顺序,autograd将梯度存储在参数张量的.grad中。粗略的解决方案是添加像这样的字典

clipped_grads = {name: torch.zeros_like(param) for name, param in net.named_parameters()}

像这样运行for循环

for i in range(loss.size(0)):
    loss[i].backward(retain_graph=True)
    torch.nn.utils.clip_grad_norm_(net.parameters())
    for name, param in net.named_parameters():
        clipped_grads[name] += param.grad / loss.size(0)
    net.zero_grad()

for name, param in net.named_parameters():
    param.grad = clipped_grads[name]

optimizer.step()

在这里我省略了许多detachrequires_grad=False和类似业务,可能需要使它们表现出预期的表现。

以上所述的缺点是您最终为参数梯度存储了2倍的内存。原则上,您可以采用“原始”渐变,将其裁剪,添加到clipped_gradient,然后在不需要下游操作时立即将其丢弃,而在此处您将原始值保留在grad中,直到结束向后传递。 可能可能是register_backward_hook允许您这样做,如果您违反准则并实际修改了grad_input,但是您必须与更熟悉autograd的人进行验证

答案 1 :(得分:-1)

This软件包并行计算每个样本的梯度。所需的内存仍然是标准随机梯度下降的batch_size倍,但是由于并行化,它可以更快地运行。