Фарма-бенчмарки: PK и NLME
Без бенчмарк-театра: точные определения целевых функций, правила остановки, протоколы масштабирования, гейты корректности.
2026-02-08 · 8 мин. чтения
Бенчмарки в фармакометрике обманчиво легко сделать неправильно. Два фиттера могут оба вернуть «результат», измеряя при этом принципиально разные вещи: разные целевые функции (MAP, маргинальное правдоподобие, приближения в стиле FOCE/Laplace), разные правила остановки и допуски, разные параметризации, разную обработку цензурирования и LLOQ.
Аннотация. Мы рассматриваем производительность как свидетельство, а не как скриншот. Для PK/NLME это означает: точно определить целевую функцию (правдоподобие, ограничения, политика цензурирования), определить протокол фита (правило остановки, границы, инициализация), публиковать гейты корректности (аналитические проверки + восстановление на синтетических данных), а также публиковать сырые измерения и манифесты, чтобы внешние команды могли воспроизвести прогон.
0.Репликабельность: минимальный запуск и артефакты
Seed-харнесс фарма-сьюта в публичных бенчмарках намеренно начинается с переносимой синтетики и NextStat-only baseline. Результат прогона — это JSON-артефакты с зафиксированной схемой, версией NextStat и идентификатором набора данных (включая SHA‑256 хеш спецификации генерации).
Важно: в seed-харнесе «данные» это не файл, а описание генератора. Хеш берётся от канонической сериализации спецификации (sort_keys=True, фиксированные разделители) и затем используется в baseline manifest и репликации третьими сторонами.
Прогон одного кейса (генерация синтетики + тайминг NLL на вызов):
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-фита (это отдельная секция артефакта):
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.jsonSuite runner (несколько генерируемых размеров PK/NLME):
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/*.json | nextstat.pharma_benchmark_result.v1 | Один кейс: dataset spec + SHA‑256, метаданные окружения, тайминги NLL/вызов, опционально MLE fit. |
| out/pharma/pharma_suite.json | nextstat.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.Опорный харнесс (скелет публичных бенчмарков)
Для публичных снимков мы поставляем минимальный воспроизводимый опорный харнесс:
# Прогон одного кейса
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 это как раз тот домен, где «быстрый результат» может быть неверным или несопоставимым, а воспроизводимость не обсуждается. Поэтому мы рассматриваем бенчмарки как артефакты с закрепленными окружениями, гейтами корректности, публикацией сырых результатов и внешними перезапусками.
