Skip to content

Releases: slavabars/decision-cascade

v0.7.0

Choose a tag to compare

@slavabars slavabars released this 17 Oct 15:25

Added

  • Калибровка P(ошибка) в замерах (Шаг 1): ECE и Brier score считаются на вероятности ошибки 1 − score против факта 1 − correct — в risk_report/calibration_report (eval/risk/features.py) и в ranker_report (eval/risk/core.py, значит и в metrics.json/risk.json). Метрики выводятся в отчётах print_report, print_loo_calibration и print_shots_report и попадают во все агрегаты features.json (per_seed, loo, shots) как mean ± std по seed'ам.
  • Калибровка в hard gate — только там, где она разрешима: ECE/Brier входят в гейт пула (GATE_AB_METRICS, GATE_RANKER_METRICS) со своими допусками (GATE_TOL_ECE = 0.02, GATE_TOL_BRIER = 0.01 ≈1σ измеренного seed-разброса; AURC-допуск остался жёстким 0.0005). В по-вопросный гейт (GATE_LOO_METRICS, новый GATE_SHOTS_METRICS) калибровка не входит: измеренный шум разницы на одном вопросе (σ 0.019–0.026) сравним с эффектом, и постоянный допуск отвергал бы на шуме — вернём через bootstrap-CI в Шаге 3. Метрика гейта — теперь GateMetric(ключ, меньше_лучше, допуск); tol в gate_risk_vs_confidence по умолчанию берётся по метрике и может быть переопределён числом.
  • Кривая few-shot по нескольким порядкам примеров (Шаг 2): флаг --shot-seeds; shots_report отдаёт точки по каждому порядку, а average_shot_seeds усредняет их перед гейтом и агрегацией (удачный порядок не должен выглядеть улучшением). Разброс по порядкам хранится рядом (<метрика>_shot_std), печатается print_shots_stability и попадает в features.json вместе с shot_seeds, shots_per_question и shots_per_shot_seed.
  • Оценка разброса: bootstrap-CI и стабильность по seeds (Шаг 3). Точечные числа не отвечают на вопрос «реальна ли разница», поэтому добавлены: bootstrap_mean_ci (перцентильный CI среднего), bootstrap_ranker_ci (перевыборка тестовых строк с повторным ranker_report; интервалы уезжают в ranking_ci и gate.ci сертификата risk.json), paired_differences (разница candidate − baseline по парам «seed × вопрос», знак нормирован так, что «+» всюду = «кандидат хуже») и stability_report (CI, доля замеров с худшим результатом, признак значимости). Worst-case по вопросам даёт worst_case_by_question, печать — print_stability, всё складывается в features.json → stability (in_dist, loo, loo_worst_auc, shots, shots_worst_auc).
  • Синтетические сценарии S1–S6 (eval/risk/simulate.py, Шаг 4): управляемые данные, где причины разделены по построению — латентная сложность z, сдвиг и наклон вопроса, зашумлённые признаки, а n_classes — атрибут вопроса. Сценарии: S1 коллизия классов, S2 разные n_classes, S3 дрейф связи (noul_logit перевёрнут на новом вопросе), S4 кривая few-shot при коллизии, S5 отчёт по калибровке, S6 худший случай (вопрос вне диапазона обучения). Метрики те же, что в замерной части (_fit_logistic, risk_report), вывод — в консоль и summary.json.
  • Hard gate «принимать только лучшее решение» (eval/risk/core.py, gate_risk_vs_confidence): вариант принимается, только если он не хуже baseline confidence по AURC, eAURC, coverage@5% risk и acc@0.7 (допуск 0.0005 поедает только шум). Считается в eval/risk/run.py и в eval/risk/features.py — по in-dist, по LOO (новый вопрос) и по кривой few-shot (точка против baseline при том же числе примеров); вердикты печатаются в отчёт и пишутся в features.json/metrics.json → gate.
  • Сертификат гейта в risk.json (ErrorRiskModel.gate) и продакшен-фолбэк: при accepted: false RiskAwareCascadeBackend безусловно переходит на порог уверенности (DECISION_CASCADE_THRESHOLD), причина эскалации в usage становится risk_gate, в лог пишется предупреждение, /info → engine.risk_gate показывает rejected. Без сертификата режим работает как прежде.
  • Замеры гейта (доля проходов production против confidence): in-dist — 2/3 (RouterBench/hash, 3 seeds), 1/1 (RouterBench/gpt2), 1/1 (AG News/gpt2); LOO 0-shot — 5/9, 0/3 и 3/3 соответственно. Кривая few-shot: на gpt2 escalate проходит с n ≥ 5, route и benefit не проходят ни при каком n (включая «все»); на hash — 4–5 из 9 при любом n; на AG News — 3/3 при n = 0 и 2/3 дальше (добавление своих примеров переобучает business).
  • Вариант confidence в кривой --shots: однопризнаковая baseline-модель в той же точке кривой — с ней и сравнивается гейт.
  • Флаг --state в eval/risk/features.py и варианты state[C] / prod+state[C]: замер, несёт ли замороженное состояние энкодера (пулированный вектор промпта) сигнал об ошибке сверх шести признаков DecisionResult. Сила L2 у вариантов с состоянием своя (--state-C), потому что 768 измерений на 400 строках валидации переобучаются.
  • Базовая линия random в eval/risk/features.py — случайный балл («без навыка»), его AURC ≈ базовая частота ошибок; отделяет «модель не работает» от «модель хуже случайной».
  • Флаг --shots в eval/risk/features.py: кривая дообучения на новом вопросе — модель обучается на валидации остальных вопросов плюс n случайных строк валидации отложенного вопроса, тест — его тест. Точки вложены (n=0 = LOO, shots=None = in-dist). Позволяет оценить, сколько размеченных примеров нового вопроса нужно, чтобы снять потерю переноса.
  • Флаги --qmean/--qmean-C и --ablate в eval/risk/features.py (Шаг 5): --qmean добавляет варианты со средним пулированным вектором вопроса (qmean[C], prod+qmean[C]), --ablate — drop-one варианты production (no_n_classes/no_log_odds_gap/no_noul_logit). В features.json пишутся ключи qmean_cs и ablated.
  • Флаг --C-grid в eval/risk/features.py (Шаг 5а): варианты production[C] делят с production ту же матрицу признаков, отличаясь только силой L2. Сетка по C — один прогон вместо прогона на значение (кодирование и обучение голов общие). Ключ c_grid в features.json.
  • Правила перехода risk-aware в stable (Шаг 6): src.risk.gate_verdict и ErrorRiskModel.gate_verdict пересчитывают вердикт гейта с допуском delta по-метричным превышениям gate.excess (пишется eval.risk.run, считает eval.risk.core.gate_excess); конфиг DECISION_RISK_GATE_DELTA (> 0 ослабляет гейт, < 0 ужесточает) и DECISION_RISK_FEW_SHOT_N.
  • Few-shot переобучение риск-модели под новый вопрос (Шаг 6): numpy-решатель src.risk.fit_logistic (Ньютон/IRLS с L2, без sklearn — соглашение о C как у LogisticRegression) и ErrorRiskModel.retrained_with (схема {общие строки + свои n}). В RiskAwareCascadeBackend порог few_shot_n и add_examples: набрав DECISION_RISK_FEW_SHOT_N (дефолт RISK_FEW_SHOT_MIN_N = 10) размеченных примеров вопроса, каскад переобучает модель и применяет её только к этому вопросу. Константа RISK_FEW_SHOT_MIN_N; /info отдаёт risk_few_shot_n/risk_few_shot/risk_gate_delta.
  • Раздел «Правила перехода в stable» в docs/risk-aware-cascade.md — формализует условия beta→stable (включение, hard gate и его допуск, zero-shot, few-shot, возврат). Транспорт размеченных примеров в сервисе пока не подключён: это программный шов, не продуктовый API.

Changed

  • Дефолт силы L2 риск-модели — C=0.2 вместо 1.0 (Шаг 5а). Причина — перерегуляризация: при C=1.0 production переобучает межвопросные константы и на hash значимо проигрывает confidence на новом вопросе (+0.0079 * по LOO). Сетка C (замерный харнесс eval.risk.features, новый флаг --C-grid) монотонно улучшает LOO до C≈0.2 (−0.0072 * [−0.0155,−0.0011]) при неизменном in-dist, и разрыв с confidence исчезает (+0.0007 ns); подтверждено на RouterBench/gpt2 (−0.0062 * по LOO, in-dist ns), нейтрально на AG News, разброс по seeds не растёт. Единый источник — RISK_C_DEFAULT в src/risk.py, его используют и продукт (training.recipe.risk), и замер (eval.risk.features). Разбор — в docs/risk-aware-cascade.md («Находка: провал hash — это перерегуляризация, а не признак»).
  • По-вопросная калибровка не стала критерием гейта (отрицательный результат): в первом замере (один seed) на escalate/benefit виделись провалы ECE (0.326 против 0.277 и 0.141 против 0.095), но на нескольких seeds и порядках примеров они не воспроизводятся (escalate даёт +0.007 ± 0.030). Поэтому вердикты LOO/кривой считаются по SLO-метрикам (AURC/eAURC/coverage/acc), а калибровка — по пулу. Bootstrap-CI Шага 3 подтвердил это и уточнил: по ECE на новом вопросе интервал накрывает ноль на всех трёх наборах, а в пуле знак ECE зависит от энкодера (hash −0.031 значимо лучше, gpt2 +0.015 и +0.006 значимо хуже), тогда как Brier значимо лучше везде. Замер-обоснование — в docs/risk-aware-cascade.md.
  • Кривая --shots считает и baseline confidence в каждой точке, поэтому SHOT_COLUMNS/агрегат группируются по (variant, shots) — иначе точки разных вариантов смешивались бы в одну строку.
  • eval/risk/features.py считает production и варианты с состоянием при одном и том же C (--C), а для вариантов с состоянием берёт --state-C; это убирает сравнение, в котором выигрыш давала не новая информация, а более сильная регуляризация.

Fixed

  • RLCD-обучение не училось (вырожденный градиент): награда считалась по вероятности политики (−|p − o|), а не по взятому действию-сэмплу, поэтому E_s[∇ log π(s)] = 0 — ожидаемый градиент был строго нулевым, и «обучение» шло только конечновыборочным шумом (улучшение примерно на половине seeds; на одном seed это выглядело как успех, а на CI с другой версией torch тест падал). Теперь награда — за сэмпл (−|s − o|) с GRPO-нормализацией (group_advantages, ранее не использовалась). Замер на 20 seeds: MAE 0.50 → 0.04, улучшение 20/20 (было 6/20); регрессионный тест проверяет сходимость сразу на нескольких seeds. Продуктовый путь (calibrate_noul_heads) и раньше был защищён гейтом «не навреди».

Измерено и не подключено

  • Сигнал из замороженного состояния энкодера: на новый в...
Read more

v0.6.0

Choose a tag to compare

@slavabars slavabars released this 16 Oct 16:30

Added

  • Голова выбора option-scoring (DECISION_CHOICE_HEAD=option_scoring): вариант оценивается общим билинейным скорером по текстовому эмбеддингу, поэтому число параметров не зависит от числа вариантов, а набор вариантов можно подменить на инференсе без переобучения. Замер переноса — eval/option_transfer/run.py, честные числа — docs/option-scoring.md (перенос работает на близких вариантах, но не на произвольных новых словах). Режим по умолчанию — linear; старые чекпоинты читаются без изменений.
  • Флаги --choice-head (eval/report/run.py, training/recipe/train_heads.py) и --loo (eval/risk/features.py).
  • LOO-замер переноса риск-модели: leave-one-question-out проверка показала, что выигрыш шести признаков — подстройка под известные вопросы, а не новый сигнал о конкретном решении; разбор и оговорка — docs/risk-aware-cascade.md.

Changed

  • Оценщик риска P(ошибка) (src/risk.py) получил три признака к прежним трём: log_odds_gap (ln(p1/p2) двух лучших исходов), n_classes (число исходов) и noul_logit (знаковый логит noul). На RouterBench при --train-steps 500 это даёт AURC 0.2633 против 0.3116 у прежней модели на gpt2 (+5.8 п.п. принятой точности при 50% эскалации) и 0.2547 против 0.2625 на hash; на AG News 0.0357 против 0.0509. Метрика считается по всем вопросам сразу, а проверка --loo показала, что выигрыш — межвопросное упорядочивание: внутри вопроса и на новом вопросе confidence не хуже. Модели, обученные на трёх прежних признаках, загружаются и работают в прежней семантике.
  • ErrorRiskModel.from_dict определяет имена признаков по числу весов, если в файле нет feature_names: трёхвесовый risk.json без списка признаков больше не падает при загрузке, а читается как модель прежнего формата.
  • Замер состава признаков вынесен в eval/risk/features.py; вариант production в нём побитово совпадает с risk_model из eval.risk.run.

Полный список — в CHANGELOG.md.

0.4.0

Choose a tag to compare

@slavabars slavabars released this 13 Oct 21:22

0.4.0 — 2026-10-14

Added

  • Self-hosted «живой» конкурент (eval/competitors/serve.py): локальная causal-LM под POST /v1/systemone, отвечает по правдоподобию вариантов (sequence log-likelihood); реальные числа — в docs/competitor-benchmark.md.
  • CI-регрессия конвейера бенчмарка (tests/test_competitors_regression.py) и офлайн-тесты сервиса на крошечной локальной модели.

Changed

  • Vision-энкодер ClipVisionEncoder грузит бэкбон через AutoModel/AutoImageProcessor — поддерживает SigLIP (google/siglip-*), не только CLIP.
  • Бренд унифицирован на decision-cascade; SystemOne остаётся названием внешнего протокола совместимости.

Полный список изменений — в CHANGELOG.md.

v0.3.0

Choose a tag to compare

@slavabars slavabars released this 13 Oct 20:54

Кодовый релиз: публичный бенчмарк RouterBench и гигиена релиза.

Что нового

RouterBench (публичный decision-датасет)

  • Адаптер eval/datasets/routerbench.py — пин ревизии Hugging Face, вопросы
    escalate/route/benefit.
  • Интеграция в отчёт untrained-vs-trained (eval/report) и в сравнение с
    конкурентами (eval/competitors); свип --sweep «качество/доля эскалаций».
  • Честные числа в docs/routerbench.md: замороженный gpt2 даёт overall
    0.356 → 0.593, но escalate ≈ базовая частота класса — узкое место в
    замороженных признаках, а не в головах.

Гигиена релиза

  • Лицензия Apache-2.0 (LICENSE, NOTICE).
  • Упаковка: pyproject.toml, дистрибутив decision-cascade, единый источник
    версии src.__version__ = 0.3.0.
  • CI (.github/workflows/ci.yml): ruff + shellcheck + pytest + валидация упаковки.
  • CHANGELOG.md, SECURITY.md.
  • README: позиционирование уточнено до честной beta.

Установка

# из ассетов ниже
uv pip install decision_cascade-0.3.0-py3-none-any.whl
# или из исходников
uv pip install -e .

Прогон сервера и примеры — см. README.md и examples/README.md.

Веса голов по-прежнему публикуются отдельным релизом v0.2.0-weights.tar.gz.

Полный список изменений: CHANGELOG.md ([0.3.0]).

v0.2.0 weights

Choose a tag to compare

@slavabars slavabars released this 13 Oct 19:50

Trained decision-head packs (v0.2.0). Распакуйте архив и укажите DECISION_CHECKPOINT_DIR на нужный пак.

v0.1.0 weights

Choose a tag to compare

@slavabars slavabars released this 13 Oct 19:18

Trained decision-head packs (v0.1.0). Распакуйте архив и укажите DECISION_CHECKPOINT_DIR на нужный пак.