NextStatNextStat

Фарма-бенчмарки: PK и NLME

Без бенчмарк-театра: точные определения целевых функций, правила остановки, протоколы масштабирования, гейты корректности.

ФармакометрикаPKNLMEБенчмаркиРегуляторика

2026-02-08 · 8 мин. чтения


Серия «Наступление доверия»:Оглавление·Назад: Байесовские бенчмарки·Следующий: JAX: компиляция против выполнения

Бенчмарки в фармакометрике обманчиво легко сделать неправильно. Два фиттера могут оба вернуть «результат», измеряя при этом принципиально разные вещи: разные целевые функции (MAP, маргинальное правдоподобие, приближения в стиле FOCE/Laplace), разные правила остановки и допуски, разные параметризации, разную обработку цензурирования и LLOQ.

Аннотация. Мы рассматриваем производительность как свидетельство, а не как скриншот. Для PK/NLME это означает: точно определить целевую функцию (правдоподобие, ограничения, политика цензурирования), определить протокол фита (правило остановки, границы, инициализация), публиковать гейты корректности (аналитические проверки + восстановление на синтетических данных), а также публиковать сырые измерения и манифесты, чтобы внешние команды могли воспроизвести прогон.

0.Репликабельность: минимальный запуск и артефакты

Seed-харнесс фарма-сьюта в публичных бенчмарках намеренно начинается с переносимой синтетики и NextStat-only baseline. Результат прогона — это JSON-артефакты с зафиксированной схемой, версией NextStat и идентификатором набора данных (включая SHA‑256 хеш спецификации генерации).

Важно: в seed-харнесе «данные» это не файл, а описание генератора. Хеш берётся от канонической сериализации спецификации (sort_keys=True, фиксированные разделители) и затем используется в baseline manifest и репликации третьими сторонами.

Прогон одного кейса (генерация синтетики + тайминг NLL на вызов):

bash
python3 benchmarks/nextstat-public-benchmarks/suites/pharma/run.py \
  --deterministic \
  --target-s 0.25 --repeat 5 \
  --out benchmarks/nextstat-public-benchmarks/out/pharma/cases/pk_1c_oral_nobs_8.json

Опционально можно замерить wall-time MLE-фита (это отдельная секция артефакта):

bash
python3 benchmarks/nextstat-public-benchmarks/suites/pharma/run.py \
  --deterministic \
  --fit --fit-repeat 3 \
  --out benchmarks/nextstat-public-benchmarks/out/pharma/cases/pk_1c_oral_nobs_8_fit.json

Suite runner (несколько генерируемых размеров PK/NLME):

bash
python3 benchmarks/nextstat-public-benchmarks/suites/pharma/suite.py \
  --deterministic \
  --out-dir benchmarks/nextstat-public-benchmarks/out/pharma

Контракт CLI специально минималистичен и прозрачен. Для одиночного кейсаrun.py задаёт параметры генерации/модели и параметры измерения:

  • тип модели: --model pk|nlme
  • seed синтетики: --seed (именно он фиксирует генерацию; --deterministic записывается в артефакт как метка режима)
  • размерности: --n-obs для PK; --n-subjects и --n-obs-per-subject для NLME
  • параметры наблюдений: --dose и --sigma
  • тайминг NLL/вызов: --target-s (целевое время для подбора number) и --repeat (число повторов)
  • тайминг wall-time фита: --fit и --fit-repeat

suite.py — это оркестратор, который вызывает run.pyи фиксирует структуру результата:

  • директория кейсов: --out-dir …/out/pharma создаёт out/pharma/cases/
  • именование кейсов: PK кейсы получают case_id = pk_1c_oral_nobs_<n_obs>, NLME — nlme_1c_oral_nsub_<n_subjects>
  • индекс набора: записывается out/pharma/pharma_suite.json и содержит относительный путь cases/… и SHA‑256 для каждого артефакта
  • контроль размеров: --pk-nobs, --nlme-nsub, --nlme-nobs, общий --seed

Артефакты и схемы

АртефактСхемаНазначение
out/pharma/cases/*.jsonnextstat.pharma_benchmark_result.v1Один кейс: dataset spec + SHA‑256, метаданные окружения, тайминги NLL/вызов, опционально MLE fit.
out/pharma/pharma_suite.jsonnextstat.pharma_benchmark_suite_result.v1Индекс набора кейсов + sha256 каждого артефакта + сводка по размерностям.

Важно: в артефакт включается не только метрика времени, но и контекст эксперимента: версия NextStat, параметры генерации синтетики и стабильный dataset.id, чтобы «тот же кейс» в разных окружениях действительно означал одно и то же.

Формально это контракт JSON Schema:benchmarks/nextstat-public-benchmarks/manifests/schema/pharma_benchmark_result_v1.schema.jsonиbenchmarks/nextstat-public-benchmarks/manifests/schema/pharma_benchmark_suite_result_v1.schema.json. Например, dataset.sha256 обязан быть 64-символьным hex, а секцияtiming.raw фиксирует стратегию повторов (сколько прогонов, какая политика агрегации).

Протокол измерения (чтобы «секунды» были сопоставимы)

  • Warmup: перед таймингом выполняется 10 вызовов NLL на фиксированном suggested_init() (прогрев JIT/кэшей/аллокаторов)
  • Подбор number: timeit увеличивает число вызовов вдвое, пока суммарное время не превысит --target-s (по умолчанию 0.25s)
  • Повторы: пер-вызовное время оценивается --repeat раз (по умолчанию 5)
  • Политика отчёта: в seed-харнесе публикуется минимум по повторам (policy = "min") и полный сырой массив timing.raw.per_call_s.nextstat

1.Модель угроз: как фарма-бенчмарки вводят в заблуждение

Типовые режимы отказа, которые делают сравнение некорректным:

  • Несовпадение целевой функции — MAP, маргинальное правдоподобие или приближения FOCE/Laplace
  • Несовпадение решателя — разные ODE-решатели, допуски и управление шагом
  • Несовпадение параметризации — в лог-пространстве / в линейном, с ограничениями / без
  • Несовпадение политики цензурирования — LLOQ, цензурированное правдоподобие, иммутация или исключение
  • Несовпадение критериев сходимости — разные допуски, правила line search, ограничения max-iter
  • Несовпадение обработки набора данных — дрейф препроцессинга, единицы измерения, временные сетки

Если мы не можем их согласовать, мы не называем это сравнением бенчмарков — мы называем это двумя разными экспериментами.


2.Что мы бенчмаркаем

  • Вычисление NLL и градиента (время на вызов)
  • Реальное время фита (wall-time) по явно заданному протоколу
  • Законы масштабирования по числу субъектов и наблюдений на субъекта

3.Гейты корректности: аналитические проверки и восстановление до измерений времени

Харнесс Apex2 pharma reference производит детерминированные, машиночитаемые свидетельства для:

  • аналитической корректности PK (замкнутая форма для 1-компартментной пероральной модели в сравнении с predict())
  • восстановления параметров PK при фите на детерминированных синтетических данных
  • контрольной проверки NLME (конечные NLL/grad и улучшение NLL на синтетических данных с несколькими субъектами)

Важно: это не «юнит-тесты на скорость». Это именно корректностный гейт перед публикацией времени. Конкретные пороги в seed-референсе:

  • PK analytic: max_abs_err ≤ 1e-10 между closed-form и model.predict
  • PK recovery: после MLE (max_iter=200, tol=1e-7, m=10) max_abs_param_err ≤ 5e-2 и nll_best ≤ nll_init + 1e-9
  • NLME smoke: градиент и NLL конечны, а MLE (max_iter=120, tol=1e-6, m=10) улучшает NLL: nll_best ≤ nll_init + 1e-9

Эти артефакты входят в Apex2 master report (tests/apex2_master_report.py) под ключомpharma_reference и, соответственно, попадают в validation pack.

PYTHONPATH=bindings/ns-py/python ./.venv/bin/python \
  tests/apex2_pharma_reference_report.py \
  --deterministic \
  --out tmp/apex2_pharma_reference_report.json

Этот отчёт включается в Apex2 master report и, соответственно, в валидационный пакет (validation pack), который собирает make validation-pack.


4.Главная ловушка: «время до сходимости» не определено однозначно

Время оптимизации зависит от критериев остановки, ограничений, политики line search и параметризации. Любой бенчмарк, который сообщает «время фита» без протокола, не является свидетельством.

Наше правило: публиковать протоколы с фиксированным числом итераций и/или протоколы сходимости с явными допусками, а также число вычислений и финальные значения целевой функции.


5.Базовые модели

  • Индивидуальная PK — 1-компартментная пероральная модель с абсорбцией первого порядка
  • NLME-бейзлайн — популяционные параметры + независимые логнормальные случайные эффекты (диагональная Omega), совместный MAP-фит

Это важно, потому что в промышленных инструментах «NLME» может означать множество разных приближений; бенчмарки должны сравнивать сопоставимые вещи.


6.План наборов данных: сначала синтетические, открытые наборы данных где возможно

Каждый опубликованный прогон должен включать:

  • ID набора данных + хеш
  • параметры генерации (для синтетики)
  • протокол препроцессинга (для реальных данных)
  • точную конфигурацию модели (включая политику LLOQ)

7.Опорный харнесс (скелет публичных бенчмарков)

Для публичных снимков мы поставляем минимальный воспроизводимый опорный харнесс:

bash
# Прогон одного кейса
python3 benchmarks/nextstat-public-benchmarks/suites/pharma/run.py \
  --deterministic \
  --out benchmarks/nextstat-public-benchmarks/out/pharma_pk_1c_oral.json

# Прогон набора (несколько сгенерированных кейсов)
python3 benchmarks/nextstat-public-benchmarks/suites/pharma/suite.py \
  --deterministic \
  --out-dir benchmarks/nextstat-public-benchmarks/out/pharma

Каждый сгенерированный набор данных несёт стабильный ID и SHA-256 хеш спецификации.

A.Ограничения seed (что мы ещё не заявляем)

  • Пока это NextStat-only baseline. Seed-сьют предназначен, чтобы внешняя команда могла запустить тот же протокол и получить сопоставимые артефакты. Сравнения с nlmixr2/Torsten и промышленными пайплайнами — следующий слой работ.
  • Синтетика — сознательный старт. Наборы данных генерируются детерминированно и включаются в артефакт (spec + SHA‑256), чтобы убрать вопрос «где взять данные».
  • Политика LLOQ/цензуры пока не зафиксирована как сравнимый baseline. В текущем seed концентрации клиппируются к неотрицательным значениям; полноценные режимы цензурированного правдоподобия и LLOQ должны быть формализованы как отдельные кейсы и опубликованы отдельным протоколом.

8.Метрики, которые мы будем публиковать

  • Время NLL/вызов (и время градиента/вызов, если применимо)
  • Реальное время фита (wall-time) по объявленному протоколу
  • Кривые масштабирования: число субъектов → время выполнения, число наблюдений на субъект → время выполнения, размерность случайных эффектов → время выполнения
  • Ошибка восстановления на синтетике (гейт доверия, не метрика скорости)

9.Почему это входит в публичную бенчмарк-программу

PK/NLME это как раз тот домен, где «быстрый результат» может быть неверным или несопоставимым, а воспроизводимость не обсуждается. Поэтому мы рассматриваем бенчмарки как артефакты с закрепленными окружениями, гейтами корректности, публикацией сырых результатов и внешними перезапусками.


Связанное чтение