Topic 4 of 6

Course materials are currently available in Russian only.

Слои и модели

Сеть строят из готовых слоёв. Слои, функции ошибки и всё остальное для нейросетей лежит в модуле torch.nn.

Линейный слой

from torch import nn

layer = nn.Linear(784, 128)     # 784 входа → 128 выходов
y = layer(x)                    # x: (N, 784) → y: (N, 128)

nn.Linear считает y = x @ W.T + b: у него матрица весов W формы (128, 784) и смещения b из 128 чисел — всего 784·128 + 128 = 100 480 параметров. Сначала они случайные; обучение их подбирает.

Нелинейность

Несколько линейных слоёв подряд — всё равно одно линейное преобразование. Чтобы сеть могла выучить сложные зависимости, между слоями ставят функцию активации, чаще всего ReLU(x) = max(0, x):

model = nn.Sequential(
    nn.Linear(784, 128),
    nn.ReLU(),
    nn.Linear(128, 10),         # после последнего слоя ReLU не ставят
)
logits = model(x)               # (N, 10) — оценки 10 классов

На выходе — логиты: числа любого знака, по одному на класс. Больше — значит, сеть больше уверена в этом классе.

Параметры

for name, p in model.named_parameters():
    print(name, tuple(p.shape))
sum(p.numel() for p in model.parameters())      # всего чисел

p.numel() — число элементов тензора. У параметров requires_grad=True; если его выключить, слой «замораживается» и не учится — так дообучают готовые большие сети.

Свой модуль

Когда Sequential мало, пишут свой класс:

class DigitNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(64, 32)
        self.out = nn.Linear(32, 10)

    def forward(self, x):            # x: (N, 1, 8, 8)
        x = x.flatten(1)             # (N, 64)
        x = torch.relu(self.hidden(x))
        return self.out(x)           # (N, 10)


model = DigitNet()
logits = model(batch)                # вызывает forward
  • слои создают в __init__ и сохраняют в атрибуты — так PyTorch найдёт их параметры;
  • forward описывает, как вход превращается в выход; backward писать не нужно;
  • x.flatten(1) вытягивает всё, кроме первого измерения: пакет должен сохраниться. Сеть всегда работает с пакетом, даже из одного примера.

Итог

  • nn.Linear(in, out): веса и смещения;
  • nn.ReLU() между слоями, не после последнего;
  • nn.Sequential(...), parameters(), numel(), requires_grad;
  • свой nn.Module: __init__ + forward, flatten(1).