вычисление градиентов для каждого отдельного образца в пакете в PyTorch - PullRequest
0 голосов
/ 16 декабря 2018

Я пытаюсь реализовать версию дифференциально частного стохастического градиентного спуска (например, https://arxiv.org/pdf/1607.00133.pdf),, которая выглядит следующим образом:

Вычислить градиент относительно каждой точки в партии размера 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-й градиент, а затем обрезает его, а не обрезает перед тем, как накапливать его в градиенте. Какой лучший способ обойти эту проблему?

1 Ответ

0 голосов
/ 16 декабря 2018

Я не думаю, что вы можете сделать намного лучше, чем второй метод, с точки зрения вычислительной эффективности, вы теряете преимущества пакетирования в вашем 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.

...