Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions CODE_WALKTHROUGH.md
Original file line number Diff line number Diff line change
Expand Up @@ -1892,3 +1892,125 @@ S_mix = sum(w_i * S_i)
- stock shortfall;
- запрет невалидных долей;
- context-only статус газовых тегов.

## 23. Что добавил Stage 5

Stage 5 добавляет слой неопределенности и применимости вокруг исторического ML-прогноза.
Он не меняет внешний CLI и не включает реальные управляющие воздействия.

Общий поток:

```text
prepared dataset
-> supervised features
-> point model artifact
-> fit_stage5_uncertainty()
-> calibrated upper predictor
-> save_stage5_model()
-> predict_quality()
-> check_applicability()
-> predict() + predict_upper()
-> check_constraints()
```

### `source/ml/uncertainty.py`

`fit_upper_calibrator()` получает реальные validation targets и несколько вариантов raw
upper-прогнозов. Первая половина validation выбирает модель по pinball loss, вторая
половина считает non-negative shift:

```text
shift = max(0, q95(y - upper_raw))
```

`evaluate_upper_bounds()` проверяет held-out качество upper-границы: coverage и среднюю
ширину `upper - point`.

`check_applicability()` не прогнозирует серу. Он только отвечает, можно ли применять
модель к текущей строке признаков:

- missing/NaN -> `FEATURES_UNAVAILABLE`;
- выход за bounds -> `OUT_OF_DOMAIN`;
- все признаки внутри bounds -> available.

`fit_stage5_uncertainty()` проверяет, что point artifact и dataset используют один и тот же
feature order и одни временные split boundaries. Потом обучает quantile-регрессоры,
калибрует upper, считает test metrics и robustness cases.

`save_stage5_model()` сохраняет artifact так, чтобы metadata явно говорила:

```text
supports_forecast = true
supports_uncertainty = true
supports_actions = false
```

### `source/ml/artifacts.py`

`ModelBundle.predict_upper()` нужен для serving. Он проверяет:

- artifact действительно заявил `supports_uncertainty`;
- feature order совпадает с metadata;
- predictor вернул одно конечное значение на строку;
- upper не ниже point.

`ModelBundle.check_applicability()` берет `feature_bounds` из metadata и вызывает
`source.ml.uncertainty.check_applicability()`.

### `source/agents/quality.py`

В `history` mode quality-agent теперь ведет себя так:

1. Проверяет совместимость target signal/unit/horizon.
2. Если artifact поддерживает uncertainty, вызывает applicability gate.
3. Если gate недоступен или не пройден, возвращает unavailable issue.
4. Считает point forecast.
5. Если доступен uncertainty, считает upper forecast.
6. Возвращает `MetricEstimate` с `interval_kind=EMPIRICAL` и `interval_level=0.95`.

Главная идея: если upper нужен, но его нет, backend не должен делать вид, что качество
прошло constraint.

### `source/ml/policy.py`

Policy helpers работают отдельно от модели качества. Они отвечают на вопрос:

```text
достаточно ли кандидат лучше hold, чтобы разрешить рекомендацию
```

`assess_change_policy()` смотрит на первый отличающийся active criterion. Если отличие
только в `change_size`, это не улучшение. Если есть существенное улучшение, проверяется
cooldown. Cooldown действует только при feasible hold.

`tune_policy()` выбирает параметры на validation replay: сначала минимизирует пропущенные
обязательные изменения, потом лишние действия, потом общее число действий.

### `source/ml/__init__.py`

Фасад лениво экспортирует Stage 5 helper API:

- `fit_stage5_uncertainty`;
- `fit_upper_calibrator`;
- `check_applicability`;
- `evaluate_upper_bounds`;
- `assess_change_policy`;
- `tune_policy`;
- `PolicyParameters`.

Это удобно для внешнего кода и не заставляет импортировать тяжелые ML-зависимости, пока
конкретная функция не запрошена.

### `global_tests/test_stage5_uncertainty_policy.py`

Тесты Stage 5 проверяют:

- раздельный selection/calibration split;
- held-out coverage и width;
- missing/OOD не превращаются в pass;
- robustness cases сохраняют unavailable;
- materiality threshold и first differing criterion;
- cooldown только при feasible hold;
- tuning policy на validation replay;
- интеграцию quality-agent с upper bound;
- доступность Stage 5 helper API через lazy `source.ml` facade.
72 changes: 72 additions & 0 deletions PROJECT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1673,3 +1673,75 @@ Stage 4 связывает прогноз гидроочистки с модел

В коде Stage 4 живёт в `source/ml/blending.py`, а regression-тесты - в
`global_tests/test_stage4_blending.py`.

---

## 36. Что добавляет Stage 5

Stage 5 делает ML-прогноз честнее. До этого backend мог иметь только point forecast:

```text
ожидаемая сера = 9.2 mg/kg
```

Но одного числа мало. Нужно понимать:

- насколько верхняя граница риска выше point-прогноза;
- похож ли текущий режим на train-данные;
- можно ли использовать модель именно сейчас;
- стоит ли вообще менять рекомендацию, если улучшение слишком маленькое.

Поэтому Stage 5 добавляет три слоя.

### Uncertainty

`source/ml/uncertainty.py` строит empirical upper estimate уровня `0.95`.

Простыми словами:

```text
point = модельное ожидание
upper = осторожная верхняя оценка
```

Если constraint требует серу `<= 10`, backend должен смотреть на `upper`, а не только
на point. Если upper нет, это не pass. Это `UNCERTAINTY_UNAVAILABLE`.

### Applicability

`ModelBundle.check_applicability()` проверяет, не вышли ли входные признаки за
train-only bounds.

Результаты:

- `FEATURES_UNAVAILABLE` - нужного признака нет или он нечисловой;
- `OUT_OF_DOMAIN` - признак есть, но режим не похож на train-данные;
- available - можно использовать forecast.

Это важно: модель не должна уверенно отвечать там, где она не обучалась.

### Policy Guardrails

`source/ml/policy.py` решает не качество рецепта, а вопрос:

```text
достаточно ли отличие кандидата от hold, чтобы вообще давать новую рекомендацию
```

Правила:

- маленькое улучшение не создает рекомендацию;
- изменение только `change_size` не считается полезным;
- cooldown подавляет повторные рекомендации только когда hold безопасен;
- если hold нарушает hard constraint, cooldown не имеет права скрыть нарушение.

### Где это в коде

- `source/ml/uncertainty.py` - calibration, upper bound, applicability, robustness;
- `source/ml/policy.py` - materiality/cooldown policy;
- `source/ml/artifacts.py` - `supports_uncertainty`, `predict_upper`, `check_applicability`;
- `source/agents/quality.py` - осторожное применение uncertainty в history mode;
- `global_tests/test_stage5_uncertainty_policy.py` - regression tests.

Stage 5 всё ещё не включает реальное управление установкой. Газ, setpoint-ы и action
model остаются вне разрешенных действий backend.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Stage 2](STAGE2.md) — как оригинальные материалы превращаются в prepared dataset и `ProcessState`.
- [Stage 3](STAGE3.md) — hard constraints, причины отбраковки кандидатов, materiality и cooldown.
- [Stage 4](STAGE4.md) — hybrid chain, model blending и газовые теги как context-only сигналы.
- [Stage 5](STAGE5.md) — uncertainty, applicability, robustness и policy guardrails без action model.
- [Code walkthrough](CODE_WALKTHROUGH.md) — папки, файлы и хронология вызовов почти построчно.
- [ML system design](DESIGN.md#8-ml-неопределённость-и-модель-последствий) — обучение, метрики, анализ ошибок и жизненный цикл модели; общие контракты и данные описаны в том же документе.
- [Материалы задания](materials/README.md) — ТЗ, схемы и исходные данные.
Expand All @@ -31,6 +32,8 @@
в журнале;
- stage 4: модельная связка гидроочистка -> блендинг, массовый баланс рецептур и
gas context для `ht:F9`, `ht:F22`, `ht:Q21` без включения реального управления газом.
- stage 5: empirical upper estimate для прогноза серы, applicability/OOD gate, robustness
reporting и materiality/cooldown policy helpers без включения action model.

Полноценной ML-модели, промышленного управления реальными уставками и Streamlit UI пока
нет. Текущий backend уже умеет готовить данные и запускать безопасный demo-каркас с
Expand All @@ -41,6 +44,11 @@ Stage 4 показывает связанную цепочку и модельн
диапазонов и модели эффекта. Полная товарная спецификация также не заявлена:
`T95`, цетановое число и весь паспорт продукта пока `not_assessed`.

Stage 5 добавляет осторожность вокруг ML-прогноза: если artifact поддерживает uncertainty,
quality-agent сначала проверяет область применимости признаков, потом использует point и
upper sulfur. Missing/OOD/отсутствующий upper не превращаются в pass. Это не action model:
backend по-прежнему не рекомендует реальные setpoint-изменения и не управляет газом.

## Проверка

Нужны Python 3.11, Git LFS и `tar` с поддержкой RAR.
Expand All @@ -50,6 +58,7 @@ python -m venv .venv
python -m pip install -r requirements.txt
python -m source.main validate-stage0
python -m pytest
python -m pytest global_tests/test_stage5_uncertainty_policy.py
python -m ruff check .
python -m ruff format --check .
python -m mypy source
Expand Down
Loading
Loading