Гайд · TNWS AI

Как выбрать jacfwd или jacrev для матрицы Якоби в JAX

4 мин—

Сравнение forward- и reverse-mode Jacobian в JAX 0.11.2: одинаковый результат, формы, критерий выбора по входам и выходам.

Задача и применимость

Актуальный запрос: «как выбрать jacfwd или jacrev JAX матрица Якоби». Получить полную матрицу производных vector-valued функции и выбрать режим осознанно. jax.jacfwd применяет forward-mode, а jax.jacrev — reverse-mode. Оба должны давать одну матрицу, но стоимость зависит от геометрии функции. Проверяемая таблица выбора:

СценарийОбычно уместнееПричина
Входов заметно меньше, чем выходовjacfwdПрямой режим распространяет направления входа
Выходов заметно меньше, чем входовjacrevОбратный режим распространяет cotangents выхода
Размеры близкиИзмерить обаBackend и структура функции влияют на время и память

Материал применим к JAX 0.11.2 и jaxlib 0.11.2. JAX предоставляет массивы, автоматическое дифференцирование и программные преобразования; он не является готовой библиотекой слоёв, датасетов или полного тренировочного цикла. Пример намеренно мал и выполняется локально на доступном backend без загрузки весов и внешних данных. Численный критерий доказывает поведение конкретного кода, но не качество рабочей нейросети.

Пошаговое решение

  1. Задайте функцию с известной аналитической матрицей Якоби.
  2. Вычислите jacfwd и jacrev в одной точке.
  3. Проверьте форму (output_size, input_size) и численное равенство.
  4. Для рабочей формы прогрейте JIT-варианты и измерьте память и время отдельно.

Готовый проверяемый шаблон

import jax
import jax.numpy as jnp

def model(x):
    return jnp.array([x[0] ** 2 + x[1], x[0] * x[1]])

x = jnp.array([2., 3.])
j_forward = jax.jacfwd(model)(x)
j_reverse = jax.jacrev(model)(x)
expected = jnp.array([[4., 1.], [3., 2.]])
assert j_forward.shape == (2, 2)
assert jnp.allclose(j_forward, expected)
assert jnp.allclose(j_reverse, expected)
print(j_forward)

Вход и ожидаемый результат

Для двух входов и двух выходов обе функции вернули матрицу [[4,1],[3,2]]. Это подтверждает ориентацию осей: строка соответствует компоненте выхода, столбец — компоненте входа. Равенство не означает одинаковые затраты на другой архитектуре.

Как проверить результат

Проверьте несколько точек, dtype и порядок осей. Для benchmark используйте реальные input/output shapes, выполните компиляционный прогрев и block_until_ready; не выбирайте режим по одному маленькому квадратному примеру.

Перенос на настоящую нейросеть

Для рабочей модели сначала зафиксируйте контракт параметров и batch: pytree structure, формы, dtype, порядок признаков и семантику выходов. Генерацию случайностей оформите как явный поток keys. Разделите чистые функции forward, loss и update, чтобы их можно было независимо тестировать, дифференцировать и компилировать. Данные train, validation и test не должны смешиваться. Любое изменение формы, precision, backend или версии JAX проверяйте на сохранённом обезличенном regression-наборе до развёртывания.

Критерии приёмки

  1. Все формы и dtype совпадают с контрактом.
  2. Каждый PRNG subkey используется в одной логической случайной операции.
  3. Loss и gradients конечны на положительном тесте.
  4. Негативный вход вызывает ожидаемую ошибку, а не случайный output.
  5. Обычный и преобразованный через jit/vmap путь численно согласованы, когда это применимо.
  6. Устройство, версии JAX/jaxlib и численный допуск записаны в отчёте.

Регрессионный протокол

Храните несколько малых входов: стандартный batch, один объект, нули, граничные значения и заведомо невалидную форму. Сравнивайте outputs и loss с эталоном через allclose с явно выбранным допуском. После смены backend проверяйте допустимую численную погрешность, а не требуйте безусловной битовой идентичности. Производительность измеряйте после компиляционного прогрева и завершения асинхронной работы. Не выбирайте лучший единичный замер: сообщайте распределение задержки и условия машины.

Диагностика расхождений

Если результат изменился, локализуйте слой: данные и preprocessing, key-поток, shapes/dtype, forward, loss, gradients, update, затем jit и backend. Сначала выполните функцию без jit на минимальном входе, затем верните transformations по одной. Сравните tree structure и каждый leaf. Не увеличивайте допуск и не применяйте nan_to_num, пока не выяснено, не скрывает ли это неверную форму, переполнение или повтор ключа.

Контроль после внедрения

В эксплуатации ограничьте допустимые batch shapes и объём входа, чтобы запрос не исчерпал память и не создал бесконечный поток перекомпиляций. Собирайте агрегированные метрики latency, compile count, долю невалидных входов и численные ошибки без записи исходных чувствительных данных. Сохраняйте предыдущий проверенный артефакт и код параметров для отката. Автоматическая замена весов требует той же независимой проверки, что и обычный выпуск.

Границы применимости

Пример на CPU не доказывает свойства GPU или TPU, а маленький массив не предсказывает производительность большой модели. JIT может ускорить повторяющиеся вычисления, но стоимость компиляции, передача данных и новые формы способны ухудшить end-to-end время. Autodiff возвращает производную написанной функции, а не подтверждает правильность математической постановки. Для критичных решений нужны предметные метрики, мониторинг и человеческий контроль.

Типичные ошибки и чего не делать

  • Не заключайте, что jacfwd всегда быстрее только из одного замера.
  • Не транспонируйте матрицу без проверки соглашения осей.
  • Не материализуйте огромный Jacobian, если достаточно JVP или VJP.

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

FAQ

Должны ли результаты совпасть?

Да, для корректно дифференцируемой функции в пределах численного допуска.

Когда обычно выбирают jacrev?

Когда число выходов заметно меньше числа входов.

Что делать при огромном Jacobian?

Вычислять нужные JVP/VJP вместо полной матрицы.

Официальные источники

Частые вопросы

Должны ли результаты совпасть?

Да, для корректно дифференцируемой функции в пределах численного допуска.

Когда обычно выбирают jacrev?

Когда число выходов заметно меньше числа входов.

Что делать при огромном Jacobian?

Вычислять нужные JVP/VJP вместо полной матрицы.

Читайте также

Комментарии

Пока тихо. Скажите первое слово