Гайд · TNWS AI

Как печатать значения внутри jax.jit через jax.debug.print

4 мин—

Runtime-диагностика compiled JAX: debug.print с format placeholders, ordered=True, block_until_ready и проверка вывода `mean=2.00`.

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

Актуальный запрос: «как вывести значение внутри JAX jit debug print». Увидеть runtime-значение traced массива внутри jitted функции. Обычный Python print во время tracing показывает tracer или срабатывает только при компиляции. jax.debug.print является staged callback и форматирует значения при выполнении. Побочный эффект предназначен для диагностики и может менять timing; его не оставляют в горячем production path.

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

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

  1. Добавьте debug.print внутри чистой вычислительной функции.
  2. Используйте строку формата с именованным placeholder, а не f-string.
  3. Оберните функцию в jax.jit и вызовите на контрольном массиве.
  4. Дождитесь результата через block_until_ready и проверьте строку и численный output.

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

import io
from contextlib import redirect_stdout
import jax
import jax.numpy as jnp

@jax.jit
def doubled(x):
    jax.debug.print("mean={m:.2f}", m=jnp.mean(x), ordered=True)
    return x * 2.

buffer = io.StringIO()
with redirect_stdout(buffer):
    result = doubled(jnp.array([1., 3.]))
    result.block_until_ready()
captured = buffer.getvalue()
assert jnp.array_equal(result, jnp.array([2., 6.]))
assert "mean=2.00" in captured
print(captured.strip(), result)

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

Compiled функция напечатала mean=2.00 во время выполнения и вернула [2,6]. block_until_ready обеспечил завершение асинхронного вычисления до проверки буфера. Перехват stdout удобен для теста, но production-логирование требует отдельной политики.

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

Проверьте runtime input, а не tracer representation. При нескольких prints используйте ordered=True только когда порядок действительно важен: это создаёт зависимость эффектов. Удалите callback после локализации проблемы и повторно измерьте производительность.

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

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

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

  • Не использовать f-string с traced value вместо placeholder arguments.
  • Не считать порядок независимых debug callbacks гарантированным без ordered=True.
  • Не печатать полные пользовательские batch и секреты.

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

FAQ

Почему обычный print недостаточен?

Под jit он выполняется при tracing и видит абстрактные tracers.

Когда срабатывает debug.print?

При runtime-выполнении compiled computation.

Можно оставить в production?

Лучше убрать после диагностики из-за вывода, утечек данных и влияния на timing.

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

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

Почему обычный print недостаточен?

Под jit он выполняется при tracing и видит абстрактные tracers.

Когда срабатывает debug.print?

При runtime-выполнении compiled computation.

Можно оставить в production?

Лучше убрать после диагностики из-за вывода, утечек данных и влияния на timing.

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

Комментарии

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