NextStatNextStat

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

КлючТипОписание
mufloatBest-fit сила сигнала (POI)
nllfloatОтрицательное лог-правдоподобие в минимуме
edmfloatОценка расстояния до минимума
convergedfloat1.0 если сошлось, иначе 0.0
time_msfloatВремя фита по wall-clock (мс)
param/<name>floatBest-fit value per мешающий параметр
error/<name>floatHesse 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Ширина priorPost-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.mlopsmetrics_dict(result)Метрики фита → dict для любого логгера
nextstat.mlopssignificance_metrics(z0)Пошаговые Z₀/q₀ → dict
nextstat.mlopsStepTimer()Таймер wall-clock для шагов обучения
nextstat.interpretrank_impact(model)Отсортированный impact систематик (важность)
nextstat.interpretrank_impact_df(model)То же самое → pandas DataFrame
nextstat.interpretplot_rank_impact(model)Bar chart ранкинга в matplotlib
nextstat.torchsignal_jacobian(hist, session)Сырой ∂q₀/∂signal для pruning / SciPy