Гайд · TNWS AI

Как проверить gradient конечными разностями через jax.test_util.check_grads

4 мин—

JAX 0.11.2: автоматическая проверка first- и second-order производных в fwd/rev режимах с float64 и допусками.

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

Актуальный запрос: «как проверить градиент JAX конечные разности check_grads». Сопоставить automatic differentiation с численными конечными разностями. check_grads проверяет производные в случайно выбранном направлении, поэтому стоимость не растёт как полный Jacobian. В примере проверяются первый и второй порядки в forward- и reverse-mode. Для устойчивости finite differences включается float64 только в локальном контексте.

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

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

  1. Опишите гладкую scalar функцию без случайности и побочных эффектов.
  2. Создайте небольшой float64 вход внутри jax.enable_x64.
  3. Вызовите check_grads с order=2 и modes fwd/rev.
  4. Зафиксируйте eps, atol и rtol, затем добавьте предметные аналитические тесты.

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

import jax
import jax.numpy as jnp
from jax.test_util import check_grads

def objective(x):
    return jnp.sum(jnp.sin(x) * jnp.exp(-x * x))

with jax.enable_x64():
    x = jnp.array([-0.3, 0.2, 0.7], dtype=jnp.float64)
    check_grads(
        objective, (x,), order=2, modes=["fwd", "rev"],
        atol=1e-5, rtol=1e-5, eps=1e-4,
    )
    assert jnp.isfinite(jax.grad(objective)(x)).all()
print("gradient check passed")

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

Проверенная гладкая функция прошла проверки первого и второго порядка в обоих режимах на JAX 0.11.2. check_grads ничего не возвращает при успехе и поднимает AssertionError при превышении допуска; строка печатается только после успешной проверки.

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

Запускайте тест с фиксированной версией, dtype и допусками. Подбирайте eps не по test-результату, а с учётом масштаба и precision. Добавьте независимые точные случаи: finite differences по одному случайному направлению не гарантируют правильность каждой компоненты.

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

Для рабочей модели сначала зафиксируйте контракт параметров и 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 возвращает производную написанной функции, а не подтверждает правильность математической постановки. Для критичных решений нужны предметные метрики, мониторинг и человеческий контроль.

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

  • Не применять слишком жёсткий допуск float32 и объявлять корректный gradient ошибочным.
  • Не считать один случайный direction полной проверкой Jacobian.
  • Не использовать негладкие точки как обычный finite-difference эталон без соглашения о производной.

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

FAQ

Что происходит при успехе?

Функция завершается без исключения.

Что означает order=2?

Проверяются производные до второго порядка.

Почему используется float64?

Он уменьшает ошибку округления численных разностей.

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

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

Что происходит при успехе?

Функция завершается без исключения.

Что означает order=2?

Проверяются производные до второго порядка.

Почему используется float64?

Он уменьшает ошибку округления численных разностей.

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

Комментарии

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