MLOps и интерпретируемость
Логирование, важность, подбор гиперпараметров
NextStat предоставляет легковесные хуки для experiment tracking (W&B, MLflow), physics-aware feature importance (impact ranking систематик) и интеграцию с фреймворками подбора гиперпараметров (Optuna, Ray Tune).
Логирование экспериментов
Модуль nextstat.mlopsизвлекает метрики фита в обычные Python dict без зависимости от конкретного логгера. Дальше вы вызываете свои wandb.log() или mlflow.log_metrics().
Weights & Biases
import wandb
import nextstat
from nextstat.mlops import metrics_dict, significance_metrics, StepTimer
wandb.init(project="hep-search", config={"lr": 1e-3})
result = nextstat.fit(model)
wandb.log(metrics_dict(result, prefix="fit/"))
# → {"fit/mu": 1.05, "fit/nll": 42.3, "fit/converged": 1.0, ...}
# В цикле обучения:
timer = StepTimer()
for step, batch in enumerate(dataloader):
timer.start()
loss = loss_fn(histogram)
loss.backward()
optimizer.step()
elapsed = timer.stop()
z0_val = -loss.item()
wandb.log(significance_metrics(z0_val, prefix="train/", step_time_ms=elapsed))
# → {"train/z0": 2.31, "train/q0": 5.34, "train/step_time_ms": 48.2}MLflow
import mlflow
from nextstat.mlops import metrics_dict
mlflow.set_experiment("hep-search")
with mlflow.start_run():
result = nextstat.fit(model)
mlflow.log_metrics(metrics_dict(result))
mlflow.log_param("model_type", "histfactory")Справка по metrics_dict
| Ключ | Тип | Описание |
|---|---|---|
| mu | float | Best-fit сила сигнала (POI) |
| nll | float | Отрицательное лог-правдоподобие в минимуме |
| edm | float | Оценка расстояния до минимума |
| converged | float | 1.0 если сошлось, иначе 0.0 |
| time_ms | float | Время фита по wall-clock (мс) |
| param/<name> | float | Best-fit value per мешающий параметр |
| error/<name> | float | Hesse error per мешающий параметр |
Важность систематик (impact ranking)
В ML feature importance показывает, какие входы важнее всего. В физике эквивалентом служитranking plot: какие систематические неопределенности сильнее всего влияют на параметр интереса. Модуль nextstat.interpretоборачивает это в привычный API.
from nextstat.interpret import rank_impact, rank_impact_df, plot_rank_impact
# Отсортированный список dict (сначала наибольший impact)
table = rank_impact(model, top_n=10)
for row in table:
print(f"{row['rank']:2d}. {row['name']:30s} impact={row['total_impact']:.4f}")
# Как pandas DataFrame
df = rank_impact_df(model, top_n=15)
print(df[["rank", "name", "total_impact", "pull"]])
# Столбчатая диаграмма в matplotlib
fig = plot_rank_impact(model, top_n=20)
fig.savefig("ranking.png", dpi=150)rank_impact Вывод
| Поле | Аналогия в ML | Описание |
|---|---|---|
| name | Имя признака | Имя систематики или мешающего параметра |
| total_impact | Скор важности | |Δμ_up| + |Δμ_down| - суммарный сдвиг POI |
| delta_mu_up | — | Сдвиг POI при сдвиге NP на +1σ |
| delta_mu_down | — | Сдвиг POI при сдвиге NP на −1σ |
| pull | Сдвиг posterior | Насколько мешающий параметр сдвинулся от своего приора (в σ) |
| constraint | Ширина prior | Post-fit constraint (аналог силы регуляризации) |
Практика: если нейросеть не учится, посмотрите на ranking plot. Доминирующая систематика (например, Jet Energy Scale) может "смывать" сигнал. В таком случае можно сфокусироваться на снижении этой неопределенности или сделать NN более устойчивой к ней.
Быстрый pruning через якобиан
Найдите бины гистограммы, которые почти не влияют на значимость, и отбросьте их, чтобы упростить модель:
from nextstat.torch import signal_jacobian
grad = signal_jacobian(signal_hist, session)
important_bins = grad.abs() > 0.01
print(f"Оставить {important_bins.sum()}/{len(important_bins)} бинов")
# Бины, где |∂q₀/∂s_i| ≈ 0, почти не влияют на результатПодбор гиперпараметров с Optuna
Быстрый инференс NextStat (~1-50 мс на фит) делает его удобным objective для Optuna. Оптимизация биннинга на 200 trial обычно укладывается в секунды:
import optuna, nextstat
def objective(trial):
n_bins = trial.suggest_int("n_bins", 3, 40)
lo = trial.suggest_float("lo", 0.0, 0.3)
hi = trial.suggest_float("hi", 0.7, 1.0)
ws = build_workspace(n_bins=n_bins, lo=lo, hi=hi)
model = nextstat.from_pyhf(ws)
hypo = nextstat.hypotest(model, mu=0.0)
return float(hypo.significance) # Z₀ in σ
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=200)Полное руководство с workspace builder, логированием в W&B, GPU objectives и параллельным поиском Ray Tune → Туториал Optuna
Сводка API
| Модуль | Функция | Назначение |
|---|---|---|
| nextstat.mlops | metrics_dict(result) | Метрики фита → dict для любого логгера |
| nextstat.mlops | significance_metrics(z0) | Пошаговые Z₀/q₀ → dict |
| nextstat.mlops | StepTimer() | Таймер wall-clock для шагов обучения |
| nextstat.interpret | rank_impact(model) | Отсортированный impact систематик (важность) |
| nextstat.interpret | rank_impact_df(model) | То же самое → pandas DataFrame |
| nextstat.interpret | plot_rank_impact(model) | Bar chart ранкинга в matplotlib |
| nextstat.torch | signal_jacobian(hist, session) | Сырой ∂q₀/∂signal для pruning / SciPy |
