PythiloLesson 5 of 18 · progress kept in this browser

Your own module

nn.Sequential runs layers one after another. A model that does anything else, such as adding its input back to its output, is a class of your own: subclass nn.Module, create the parts in __init__, and use them in forward.

import torch
from torch import nn

class Affine(nn.Module):
    def __init__(self, size):
        super().__init__()
        self.scale = nn.Parameter(
            torch.ones(size))
        self.shift = nn.Parameter(
            torch.zeros(size))

    def forward(self, x):
        return x * self.scale + self.shift

m = Affine(3)
for name, p in m.named_parameters():
    print(name, p.shape)
print(m(torch.ones(2, 3)))

Wrapping a tensor in nn.Parameter and assigning it to self registers it: it appears in parameters(), so the optimizer trains it, and in state_dict(), so it is saved. A layer assigned to self registers its own parameters the same way. A plain tensor stays invisible to both.

Layers start from random values scaled to their size. nn.init replaces them in place, for example nn.init.zeros_(layer.bias) or nn.init.xavier_uniform_(layer.weight).

Your task

Write ReZero(nn.Module) taking size: it holds self.linear = nn.Linear(size, size), with the weight set by nn.init.xavier_uniform_ and the bias by nn.init.zeros_, and a learnable self.alpha of shape (1,) starting at 0. Its forward(x) returns x + self.alpha * torch.tanh(self.linear(x)).

This lesson uses torch, which Python in this browser does not include. Read it here, and run and check it in the Pythilo app on iPhone or iPad.