Terms being computed: * Li = Loss(xi, yi, params) * Gi = Grad(Li, params) * Lj = Loss(xj, yj, Optimizer(params, grad(Li, params))) * Gj = Grad(Lj, params) * params = Optimizer(params, Grad(Li + beta * Lj, params)) *
(self, minibatches, unlabeled=None)
| 482 | super(MLDG, self).__init__(input_shape, num_classes, num_domains, hparams) |
| 483 | |
| 484 | def update(self, minibatches, unlabeled=None): |
| 485 | """ |
| 486 | Terms being computed: |
| 487 | * Li = Loss(xi, yi, params) |
| 488 | * Gi = Grad(Li, params) |
| 489 | |
| 490 | * Lj = Loss(xj, yj, Optimizer(params, grad(Li, params))) |
| 491 | * Gj = Grad(Lj, params) |
| 492 | |
| 493 | * params = Optimizer(params, Grad(Li + beta * Lj, params)) |
| 494 | * = Optimizer(params, Gi + beta * Gj) |
| 495 | |
| 496 | That is, when calling .step(), we want grads to be Gi + beta * Gj |
| 497 | |
| 498 | For computational efficiency, we do not compute second derivatives. |
| 499 | """ |
| 500 | num_mb = len(minibatches) |
| 501 | objective = 0 |
| 502 | |
| 503 | self.optimizer.zero_grad() |
| 504 | for p in self.network.parameters(): |
| 505 | if p.grad is None: |
| 506 | p.grad = torch.zeros_like(p) |
| 507 | |
| 508 | for (xi, yi), (xj, yj) in random_pairs_of_minibatches(minibatches): |
| 509 | # fine tune clone-network on task "i" |
| 510 | inner_net = copy.deepcopy(self.network) |
| 511 | |
| 512 | inner_opt = torch.optim.Adam( |
| 513 | inner_net.parameters(), |
| 514 | lr=self.hparams["lr"], |
| 515 | weight_decay=self.hparams['weight_decay'] |
| 516 | ) |
| 517 | |
| 518 | inner_obj = F.cross_entropy(inner_net(xi), yi) |
| 519 | |
| 520 | inner_opt.zero_grad() |
| 521 | inner_obj.backward() |
| 522 | inner_opt.step() |
| 523 | |
| 524 | # The network has now accumulated gradients Gi |
| 525 | # The clone-network has now parameters P - lr * Gi |
| 526 | for p_tgt, p_src in zip(self.network.parameters(), inner_net.parameters()): |
| 527 | if p_src.grad is not None: |
| 528 | p_tgt.grad.data.add_(p_src.grad.data / num_mb) |
| 529 | |
| 530 | # `objective` is populated for reporting purposes |
| 531 | objective += inner_obj.item() |
| 532 | |
| 533 | # this computes Gj on the clone-network |
| 534 | loss_inner_j = F.cross_entropy(inner_net(xj), yj) |
| 535 | grad_inner_j = autograd.grad(loss_inner_j, inner_net.parameters(), allow_unused=True) |
| 536 | |
| 537 | # `objective` is populated for reporting purposes |
| 538 | objective += (self.hparams['mldg_beta'] * loss_inner_j).item() |
| 539 | |
| 540 | for p, g_j in zip(self.network.parameters(), grad_inner_j): |
| 541 | if g_j is not None: |
nothing calls this directly
no test coverage detected