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