r"""GradManager computes gradients or more generally, vector-Jacobian product, by reverse mode automatic differentiation (a.k.a. back propagation). Reverse mode autodiff normally reuses many intermediate tensors for best computation efficiency. In a read-eval-print-loop (REPL) environme
| 28 | |
| 29 | |
| 30 | class GradManager: |
| 31 | r"""GradManager computes gradients or more generally, vector-Jacobian product, by reverse mode |
| 32 | automatic differentiation (a.k.a. back propagation). |
| 33 | |
| 34 | Reverse mode autodiff normally reuses many intermediate tensors for best computation efficiency. |
| 35 | In a read-eval-print-loop (REPL) environment however, it is impossible to known how the user |
| 36 | would take gradients later thus which tensors to keep. To solve this problem, the user must |
| 37 | somehow declare beforehand which gradient could possibly be taken. With GradManager, users are |
| 38 | required to call the :meth:`attach` method on a tensor if they want to take gradients with |
| 39 | respect to it later. Furthermore, any computation on a tensor before it is attached is |
| 40 | completely ignored from the autodiff perspective, so :meth:`attach` must be called before any |
| 41 | computation that needs differentiation. |
| 42 | |
| 43 | For example, the following symbolic differentiation code |
| 44 | |
| 45 | .. code-block:: |
| 46 | |
| 47 | x = get_x() |
| 48 | y = f(x) |
| 49 | dy = ones_like(y) |
| 50 | dx = vjp(y, x, dy) # vector-Jacobian product |
| 51 | |
| 52 | can be rewriten using GradManager for REPL environment as |
| 53 | |
| 54 | .. code-block:: |
| 55 | |
| 56 | with GradManager() as gm: |
| 57 | x = get_x() |
| 58 | gm.attach(x) # must be placed before any computation on x that needs differentiation |
| 59 | y = f(x) |
| 60 | dy = ones_like(y) |
| 61 | gm.backward(y, dy) # doesn't need x, already known via attach() |
| 62 | dx = x.grad # backward() saves result to .grad attribute |
| 63 | |
| 64 | A more realistic example of training a neural network would be like |
| 65 | |
| 66 | .. code-block:: |
| 67 | |
| 68 | gm = GradManager() |
| 69 | gm.attach(model.parameters()) |
| 70 | |
| 71 | for data in dataset: |
| 72 | with gm: |
| 73 | loss = model(data) |
| 74 | gm.backward(loss) |
| 75 | # gradients w.r.t. parameters is accumulated into their .grad attributes |
| 76 | |
| 77 | You can also use ``record()`` and ``release()`` method instead of ``with`` context: |
| 78 | |
| 79 | .. code-block:: |
| 80 | |
| 81 | gm = GradManager() |
| 82 | gm.attach(model.parameters()) |
| 83 | |
| 84 | for data in dataset: |
| 85 | gm.record() |
| 86 | loss = model(data) |
| 87 | gm.backward(loss) |
no outgoing calls