Python 计算Pyrotch中批次中每个单独样品的梯度

Python 计算Pyrotch中批次中每个单独样品的梯度,python,pytorch,gradient-descent,Python,Pytorch,Gradient Descent,我正在尝试实现一个版本的差异私有随机梯度下降(例如),如下所示: 计算关于批量大小L中每个点的梯度,然后分别剪裁每个L梯度,然后将它们平均在一起,最后执行(有噪声的)梯度下降步骤 在pytorch中,最好的方法是什么 最好有一种方法可以模拟计算批次中每个点的梯度: x # inputs with batch size L y #true labels y_output = model(x) loss = loss_func(y_output,y) #vector of length L loss

我正在尝试实现一个版本的差异私有随机梯度下降(例如),如下所示:

计算关于批量大小L中每个点的梯度,然后分别剪裁每个L梯度,然后将它们平均在一起,最后执行(有噪声的)梯度下降步骤

在pytorch中,最好的方法是什么

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

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
但如果做不到这一点,则分别计算每个梯度,然后在累积之前剪裁范数,但是

x # inputs with batch size L
y #true labels
y_output = model(x)
loss = loss_func(y_output,y) #vector of length L   
for i in range(loss.size()[0]):
    loss[i].backward(retain_graph=True)
    torch.nn.utils.clip_grad_norm(model.parameters(), clip_size)

累积第i个渐变,然后进行剪辑,而不是在将其累积到渐变之前进行剪辑。解决这个问题的最佳方法是什么?

我认为在计算效率方面,你不能比第二种方法做得更好,你正在失去在
向后
中批处理的好处,这是事实。关于剪切顺序,autograd将梯度存储在参数张量的
.grad
中。一个粗略的解决方案是添加一个类似

clipped_grads = {name: torch.zeros_like(param) for name, param in net.named_parameters()}
像这样运行你的循环

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()
在我省略了大部分分离的情况下,
需要_grad=False
和类似的业务,这可能是使其按预期运行所必需的

上述方法的缺点是,最终会为参数渐变存储2倍的内存。原则上,您可以采用“原始”渐变,对其进行剪裁,添加到
clipped_gradient
,然后在没有下游操作需要时立即放弃,而在这里,您将原始值保留在
grad
中,直到向后传递结束。如果您违反指导原则并实际修改
grad_输入
,则可能允许您这样做,但您必须与更熟悉autograd的人进行验证。

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