Дифференциальное и матричное исчисление

Практика блока

Свой автоград с нуля, MLP на нём и численная проверка градиентов

Шаг 33 из 117 · ~45 мин

Блок закрывается кодом, и здесь он важнее обычного: автоград — единственный способ убедиться, что правило цепочки понято, а не прочитано.

1. micrograd: автоград скаляров

Задача: класс Value, хранящий число, ссылки на родителей и локальную производную. Минимальный интерфейс — три вещи:

  1. Прямой проход через перегруженные операторы: +, *, tanh, exp, **. Каждая операция создаёт новый Value, запоминая, из кого он получен.
  2. Топологическая сортировка графа от выхода к входам.
  3. Обратный проход: grad выхода равен единице, дальше каждый узел добавляет upstream * local к градиентам родителей.

Три места, где ошибаются, и все три уже встречались в блоке:

  • +=, а не =. Узел, использованный дважды, получает вклад по каждому пути. Урок про правило цепочки, диамант.
  • порядок обхода. Узел обрабатывается только после всех своих потребителей — обратный топологический порядок, иначе градиент утечёт недосчитанным.
  • ReLU в нуле. Возвращайте ноль и знайте, что это соглашение, а не производная.

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

Сверяйте свой автоград с этим графом по одному шагу. Расхождение всегда обнаруживается на конкретном узле, а не «где-то в коде».

2. MLP на своём автограде

Соберите слой из Value, сеть из слоёв, обучите на игрушечной задаче — например на четырёх точках XOR или на предсказании знака. Нужно ровно это:

  • forward: h=tanh(W1x+b1)\mathbf{h} = \tanh(W_1\mathbf{x} + \mathbf{b}_1), o=W2h+b2o = W_2\mathbf{h} + b_2;
  • лосс: квадратичный или кросс-энтропия;
  • шаг: p.data -= lr * p.grad по всем параметрам;
  • обнуление градиентов перед каждым шагом. Забытое zero_grad — та же ошибка накопления, только между итерациями. Лосс при этом обычно не падает, а разлетается.

Цель не в качестве модели, а в том, чтобы лосс убывал и вы понимали, почему.

3. Backprop на бумаге и сверка с кодом

Возьмите двухслойную сеть и выпишите градиенты вручную, пользуясь формулами из урока про матричное дифференцирование:

LW2=δ2h,LW1=δ1x,δ1=(W2δ2)(1h2)\htmlData{k=w2}{\frac{\partial L}{\partial W_2} = \boldsymbol{\delta}_2\mathbf{h}^\top}, \qquad \htmlData{k=w1}{\frac{\partial L}{\partial W_1} = \boldsymbol{\delta}_1\mathbf{x}^\top}, \quad \boldsymbol{\delta}_1 = (W_2^\top\boldsymbol{\delta}_2)\odot(1 - \mathbf{h}^2)

Знак \odot — поэлементное умножение, и оно здесь не случайно: якобиан tanh\tanh диагонален, а VJP с диагональной матрицей это и есть поэлементное умножение. Тот самый случай из урока про VJP, где сборка матрицы была бы расточительством.

Сверьте эти формулы с тем, что выдал ваш автоград. Совпадение — хороший знак; но надёжнее следующий пункт.

4. Численная проверка градиентов

Единственный способ убедиться, что автоград верен, — сравнить с центральной разностью:

LθiL(θ+hei)L(θhei)2h\frac{\partial L}{\partial \theta_i} \approx \frac{L(\theta + h\mathbf{e}_i) - L(\theta - h\mathbf{e}_i)}{2h}

Практические детали, без которых проверка врёт:

  • h105h \approx 10^{-5} для двойной точности. Меньше — теряются значащие цифры в разности, больше — растёт ошибка аппроксимации. Мы считали это в первом уроке.
  • сравнивайте относительную ошибку anmax(a,n,ε)\frac{|a - n|}{\max(|a|, |n|, \varepsilon)}, а не абсолютную: у больших градиентов абсолютная разница велика сама по себе.
  • порог: относительная ошибка ниже 10610^{-6} — почти наверняка верно, выше 10310^{-3} — почти наверняка ошибка.
  • ReLU проверять не стоит около нуля: там производной нет, и численная разность честно покажет расхождение, которое не является ошибкой. Проверяйте на tanh.

Именно эту проверку и реализуете в задаче ниже — на сети, которая целиком считается руками.

Экзамен блока

Прочитайте раздел с методом в статье про Adam (Kingma, Ba). К этому моменту в формулах не должно остаться незнакомого: градиент, экспоненциальные средние, поправка на смещение. Оптимизация как таковая — блок 5, но читаться формулы должны уже сейчас.

Источники

Проверки

0 из 2
  1. Как проверять градиенты

    Вы написали автоград и хотите убедиться, что он верен. Отметьте все верные утверждения о численной проверке.

  2. Обратный проход двухслойной сети

    Реализуйте mlp_backward(w1, b1, w2, b2, x, y) для сети с одним скрытым нейроном:

    z=w1x+b1,h=tanhz,o=w2h+b2,L=(oy)2z = w_1x + b_1, \quad h = \tanh z, \quad o = w_2h + b_2, \quad L = (o - y)^2

    Верните список [L, dL/dw1, dL/db1, dL/dw2, dL/db2].

    Выведите обратный проход сами, звено за звеном:

    Lo=2(oy),Lw2=Loh,Lb2=Lo\frac{\partial L}{\partial o} = 2(o - y), \qquad \frac{\partial L}{\partial w_2} = \frac{\partial L}{\partial o}\,h, \qquad \frac{\partial L}{\partial b_2} = \frac{\partial L}{\partial o}

    Lz=Low2(1h2),Lw1=Lzx,Lb1=Lz\frac{\partial L}{\partial z} = \frac{\partial L}{\partial o}\,w_2\,(1 - h^2), \qquad \frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial z}\,x, \qquad \frac{\partial L}{\partial b_1} = \frac{\partial L}{\partial z}

    Множитель (1h2)(1 - h^2) — производная tanh\tanh, выраженная через уже посчитанное значение. Это то самое вычислительное удобство из урока про активации.

    Написав код, обязательно сверьте его с центральной разностью по каждому из четырёх параметров — именно так проверяются настоящие реализации.

    функция mlp_backward

    Загрузка редактора…

    Ctrl/⌘ + Enter