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

Backprop и vector-Jacobian product

Почему автодифференцирование считает произведение на якобиан, а не сам якобиан

Шаг 31 из 117 · ~32 мин

Backprop — это правило цепочки и ничего больше

Сеть — это композиция: L=fnfn1f1(x)L = f_n \circ f_{n-1} \circ \cdots \circ f_1(\mathbf{x}). По правилу цепочки якобиан композиции есть произведение якобианов:

J=JnJn1J1J = J_n J_{n-1} \cdots J_1

Всё. Никакой отдельной «теории обратного распространения» не существует — есть правило цепочки и вопрос, в каком порядке перемножать эту цепочку. Ответ на него и есть весь backprop.

Порядок умножения решает всё

Матричное произведение ассоциативно, поэтому считать можно с любого конца. Но стоимость разная, и разница катастрофическая.

Пусть сеть отображает nn параметров в один скаляр лосса, а промежуточные слои имеют ширину около kk.

Слева направо (от входа) — это forward mode. Первое произведение имеет форму k×nk \times n, и все промежуточные результаты — матрицы такого размера. Чтобы получить все производные, нужно nn проходов: по одному на каждый входной параметр.

Справа налево (от лосса) — это reverse mode, он же backprop. Первый множитель это 1×k1 \times kстрока, а не матрица. Умножение строки на матрицу даёт строку, и так до конца: все промежуточные результаты остаются векторами.

Отсюда правило: если выходов мало, а входов много — считайте с конца. В обучении выход один (скалярный лосс), входов миллиарды, поэтому reverse mode выигрывает в nn раз. Если бы функция потерь была векторной той же размерности, что параметры, преимущества не было бы.

VJP: то, что действительно вычисляется

Ключевой момент: якобиан никогда не собирается. Вместо него вычисляется произведение вектора на якобиан:

Lx=LyJ\htmlData{k=out}{\frac{\partial L}{\partial \mathbf{x}}} = \htmlData{k=v}{\frac{\partial L}{\partial \mathbf{y}}}\,\htmlData{k=J}{J}

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

Примеры, где выигрыш очевиден:

  • линейный слой y=Wx\mathbf{y} = W\mathbf{x}: якобиан по x\mathbf{x} равен WW, и VJP это WvW^\top \mathbf{v} — одно матрично-векторное умножение;
  • поэлементная функция yi=g(xi)y_i = g(x_i): якобиан диагонален, и VJP это поэлементное умножение. Собрать диагональную матрицу k×kk \times k было бы чистым расточительством;
  • softmax: якобиан K×KK \times K, но после кросс-энтропии он сократился в py\mathbf{p} - \mathbf{y} — предыдущий урок целиком про этот случай.

Стоимость и память

Обратный проход стоит примерно столько же, сколько прямой — константа порядка 2–3. Но платить приходится памятью: локальные производные зависят от значений с прямого прохода, а значит их надо сохранить.

Отсюда две вещи, знакомые всякому, кто обучал большие модели:

  • память растёт с глубиной, а не только с шириной. Активации всех слоёв живут до конца обратного прохода;
  • gradient checkpointing — сознательный обмен: часть активаций не хранят, а пересчитывают. Плата — примерно один дополнительный прямой проход, выигрыш — память как корень из глубины.

Проход по шагам

Граф двухслойной сети с одним скрытым нейроном. Обратите внимание на порядок обхода: градиент по w1w_1 становится известен последним, потому что до него нужно пройти всю цепочку.

Затравка ∂L/∂L = 1 на выходе. Каждый шаг — один VJP: узел умножает приходящий градиент на свою локальную производную и передаёт входам.

Forward mode тоже нужен

Reverse mode выигрывает при «много входов, один выход». В обратной ситуации — один вход, много выходов — выигрывает forward mode, и он вычисляет Jacobian-vector product, JvJ\mathbf{v}, а не vJ\mathbf{v}^\top J.

Где это встречается на практике: произведение гессиана на вектор. HvHv считается как forward-режим, применённый к обратному проходу, — двумя проходами, без сборки гессиана. Так работают методы, которым нужна кривизна, но не вся матрица.

Что стоит унести

Три утверждения, которые вместе объясняют, почему обучение больших моделей вообще возможно:

  1. Backprop — правило цепочки, посчитанное справа налево.
  2. Якобианы не собираются: каждая операция реализует VJP.
  3. Обратный проход стоит как прямой по времени и как глубина по памяти.

Ни одно из них не про нейросети. Всё это свойства композиции функций, и в этом смысле блок 2 закончен — осталось собрать своими руками.

Источники

Проверки

0 из 2
  1. Почему обратный режим

    Сеть отображает n=109n = 10^9 параметров в скалярный лосс. Отметьте все верные утверждения.

  2. Backward линейного слоя

    Реализуйте linear_backward(w, x, grad_y) — обратный проход слоя y=Wx\mathbf{y} = W\mathbf{x}.

    Верните список из двух элементов: [grad_x, grad_w], где

    Lx=WLy,LW=Lyx\frac{\partial L}{\partial \mathbf{x}} = W^\top \frac{\partial L}{\partial \mathbf{y}}, \qquad \frac{\partial L}{\partial W} = \frac{\partial L}{\partial \mathbf{y}}\,\mathbf{x}^\top

    w — матрица списком строк размера m×nm \times n, x — список длины nn, grad_y — список длины mm. Тогда grad_x имеет длину nn, а grad_w — форму m×nm \times n, как сама w.

    Это ровно то, что делает nn.Linear.backward, и весь VJP слоя. Заметьте, что ни один якобиан здесь не строится: первая формула — матрично-векторное умножение, вторая — внешнее произведение.

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

    функция linear_backward

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

    Ctrl/⌘ + Enter