Гайд · TNWS AI

Как заморозить bias через Optax selective_transform

Optax 0.2.6JAX 0.11.2Python#Optax#JAX#нейросети
4 мин—

Optax 0.2.6: selective_transform зануляет update замороженного листа и обучает только разрешённые параметры.

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

Актуальный запрос: «как заморозить параметры Optax selective_transform». Заморозить один лист PyTree статической freeze_mask. Значение True означает frozen. В примере gradient обоих листьев равен 2, но SGD 0,1 выдаёт нулевой update для a и -0,2 для b. Маска статична и не должна зависеть от runtime-значений. Для production отдельно зафиксируйте структуру PyTree, dtype, момент вызова относительно JIT и порядок соседних transformations. Добавьте негативный тест на несовместимую маску, неверный счётчик или отсутствующий batch axis — в зависимости от задачи. Численный unit test должен выполняться при каждом обновлении Optax.

Материал проверен на Optax 0.2.6, JAX/jaxlib 0.11.2 и Python 3.12 на CPU. Используется актуальный API from flax import nnx, а не устаревший экспериментальный import и не Linen. Пример не загружает веса или внешние данные и решает одну изолированную задачу. Численный тест подтверждает контракт кода в указанной версии, но не качество нейросети на рабочем датасете.

Перед запуском установите совместимые версии пакетов в отдельное окружение и выведите flax.__version__, jax.__version__ и jaxlib.__version__. Не смешивайте разные способы накопления, маскирования и усреднения: похожие названия имеют разные контракты. При переносе сначала закрепите ожидаемую математику unit-тестом и следуйте документации установленной версии.

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

  1. Определите параметры и gradients одинаковой структуры.
  2. Пометьте замороженный лист True.
  3. Оберните SGD через selective_transform.
  4. Примените updates и проверьте неизменность frozen-параметра.

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

import jax.numpy as jnp
import optax
params = {"a": jnp.array([1.0]), "b": jnp.array([1.0])}
grads = {"a": jnp.array([2.0]), "b": jnp.array([2.0])}
tx = optax.selective_transform(optax.sgd(0.1), freeze_mask={"a": True, "b": False})
updates, _ = tx.update(grads, tx.init(params), params)
new = optax.apply_updates(params, updates)
assert jnp.array_equal(new["a"], params["a"])
assert jnp.allclose(new["b"], jnp.array([0.8]))
print(updates, new)

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

Параметр a остался 1, потому что его update равен 0; b изменился до 0,8. Так проверяется именно итоговая неизменность, а не только структура state.

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

Сравнивайте frozen leaves побитово после нескольких шагов. При изменении архитектуры пересобирайте и тестируйте mask. Если заморозка должна меняться во времени, используйте документированный динамический механизм, а не runtime mask здесь.

Перенос в рабочую модель

В рабочем проекте зафиксируйте контракт batch: порядок осей, dtype, диапазон признаков и семантику target. Инициализацию, dropout и генерацию данных разделяйте по именованным RNG streams. Обучаемые nnx.Param, статистическое состояние и optimizer state сохраняйте и проверяйте раздельно. Перед обновлением Flax/JAX прогоняйте небольшой обезличенный regression batch и сравнивайте формы, loss, конечность значений и допустимое численное отклонение.

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

  1. Версии Flax, JAX и jaxlib записаны в отчёте.
  2. Формы входа, параметров и результата совпадают с контрактом.
  3. Все численные outputs и gradients конечны.
  4. Train/eval режим задан явно для стохастических и stateful слоёв.
  5. Результат проверен независимой формулой, состоянием или контрольным вызовом.
  6. Негативный случай неверной формы или режима включён в тесты проекта.

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

Сохраните минимальные входы: один объект, небольшой batch, нулевые значения, несимметричные размеры осей и заведомо несовместимую форму. Сравнивайте массивы через allclose с документированным допуском. Воспроизводимость требует одинаковых seeds и одинакового порядка запросов RNG. Производительность оценивайте после JIT-прогрева и block_until_ready, отдельно от compile time. Один удачный запуск не заменяет validation на независимых данных.

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

При расхождении сначала проверьте layout и preprocessing, затем RNG streams, train/eval mode, типы переменных State, forward, loss и optimizer update. Упростите модель до одного слоя, отключите JIT и стохастику, сравните промежуточные shapes. Возвращайте компоненты по одной. Не увеличивайте допуск и не заменяйте NaN нулями до установления причины.

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

В эксплуатации ограничьте допустимые shapes и размер batch, чтобы запрос не исчерпал память и не вызвал поток перекомпиляций. Не логируйте исходные пользовательские тензоры, ключи и содержимое обучающих примеров. Храните предыдущий проверенный checkpoint и схему State для отката. После восстановления проверяйте контрольный forward до включения модели в трафик.

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

CPU-проверка не доказывает скорость или точную численную идентичность GPU/TPU. Случайная инициализация не является обучением, снижение train loss не гарантирует обобщение, а правильная форма не подтверждает корректность данных. Для важных решений нужны validation/test, мониторинг дрейфа, ограничения входов и человеческий контроль.

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

  • Не путать True с trainable: здесь True означает freeze.
  • Не менять статическую маску внутри JIT-цикла.
  • Не забывать исключить frozen state из ненужных проверок optimizer moments.

Не публикуйте только удачный вывод без версии среды, форм и негативного теста. Не настраивайте модель по test set и не загружайте непроверенное состояние поверх production-модели.

FAQ

Что означает True?

Параметр заморожен.

Создаётся ли update?

Да, нулевой для frozen-листа.

Маска может зависеть от значения веса?

API требует статическую mask, не зависящую от runtime values.

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

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

Что означает True?

Параметр заморожен.

Создаётся ли update?

Да, нулевой для frozen-листа.

Маска может зависеть от значения веса?

API требует статическую mask, не зависящую от runtime values.

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

Комментарии

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