我正在尝试实现差分私有随机梯度下降的版本(例如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个渐变,然后进行裁剪,而不是先进行裁剪,再将其累积到渐变中。解决此问题的最佳方法是什么?
答案 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()
在这里我省略了许多detach
,requires_grad=False
和类似业务,可能需要使它们表现出预期的表现。
以上所述的缺点是您最终为参数梯度存储了2倍的内存。原则上,您可以采用“原始”渐变,将其裁剪,添加到clipped_gradient
,然后在不需要下游操作时立即将其丢弃,而在此处您将原始值保留在grad
中,直到结束向后传递。 可能可能是register_backward_hook允许您这样做,如果您违反准则并实际修改了grad_input
,但是您必须与更熟悉autograd的人进行验证
答案 1 :(得分:-1)
This软件包并行计算每个样本的梯度。所需的内存仍然是标准随机梯度下降的batch_size
倍,但是由于并行化,它可以更快地运行。