Гайд · TNWS AI

Как вернуть метрики вместе с gradient через value_and_grad has_aux

4 мин—

Один проход JAX 0.11.2 для loss, gradients и auxiliary metrics: точная структура ((value, aux), grad) и тест форм.

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

Актуальный запрос: «как использовать JAX value and grad has aux метрики». Получить scalar loss, gradients и дополнительные диагностические данные из одной функции. При has_aux=True пользовательская функция возвращает (value, auxiliary_data), а преобразованная функция — ((value, aux), gradient). Только первый элемент дифференцируется; aux может содержать prediction и метрики, но должен быть совместим с JAX transformations.

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

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

  1. Рассчитайте predictions и error внутри loss-функции.
  2. Верните scalar MSE первым элементом и dict метрик вторым.
  3. Вызовите jax.value_and_grad с has_aux=True.
  4. Распакуйте двойную структуру и проверьте формы loss, aux и gradients.

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

import jax
import jax.numpy as jnp

X = jnp.array([[1., 0.], [0., 1.], [1., 1.]])
y = jnp.array([1., -1., 0.])
params = {"w": jnp.array([0.5, -1.]), "b": jnp.array(0.2)}
def loss_with_metrics(p):
    pred = X @ p["w"] + p["b"]
    error = pred - y
    loss = jnp.mean(error ** 2)
    return loss, {"mae": jnp.mean(jnp.abs(error)), "pred": pred}

((loss, metrics), grads) = jax.value_and_grad(
    loss_with_metrics, has_aux=True
)(params)
assert loss.shape == () and metrics["mae"].shape == ()
assert metrics["pred"].shape == (3,)
assert grads["w"].shape == (2,) and grads["b"].shape == ()
assert jnp.allclose(loss, 0.07333334)
print(loss, metrics, grads)

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

Функция вернула MSE около 0,07333, MAE около 0,26667 и три predictions [0.7,-0.8,-0.3]. Gradient сохранил структуру params. Метрики не пришлось вычислять отдельным несовпадающим forward pass.

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

Проверяйте структуру распаковки и scalar loss. Сравните aux predictions с отдельным forward на regression batch. Не помещайте в aux огромные активации без необходимости: они могут увеличить передачу и удержание памяти.

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

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

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

  • Не распаковывать результат как (value, grad, aux) — структура вложенная.
  • Не возвращать vector loss первым элементом обычного value_and_grad.
  • Не выполнять внешнюю запись или сетевой лог внутри преобразуемой функции.

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

FAQ

Какова структура результата?

((value, aux), gradient).

Дифференцируется ли aux?

Gradient строится по первому scalar value; aux возвращается как дополнительные данные.

Зачем это лучше двух вызовов?

Loss, metrics и gradient относятся к одному forward evaluation.

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

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

Какова структура результата?

`((value, aux), gradient)`.

Дифференцируется ли aux?

Gradient строится по первому scalar value; aux возвращается как дополнительные данные.

Зачем это лучше двух вызовов?

Loss, metrics и gradient относятся к одному forward evaluation.

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

Комментарии

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