Скоринговый модуль (T15)¶
scoring.py переводит признаки (Features) в PD, баллы, грейд и вклады факторов (SPEC §8). Запрошенной суммы среди входов нет (принцип 3).
Интерфейс¶
У модуля один интерфейс — ScoringModel.predict(features) → ScoreResult: PD, баллы, грейд, вклад каждого признака (значение, интервал, баллы) и версия модели. Decision.model_version заполняется из ScoreResult.model_version, поэтому каждое решение хранит, какой моделью оно принято.
ScorecardModel — реализация для файла скоринговой карты в формате контракта Scorecard (docs/v2/CONTRACTS.md). Интервалы читаются функцией find_bin из apps.ml, той же, что при обучении и валидации, поэтому баллы в конвейере совпадают с баллами в apps/ml/. Сумма базовых баллов и баллов интервалов округляется до десятых, PD считается из неё (Scorecard.pd_from_points), грейд — по границам карты.
Чтобы подставить модель банка, достаточно положить карту в том же формате и указать путь в .env. Для модели другого вида пишется новый класс с методами predict, version и feature_set_version; остальной конвейер не меняется.
Версия набора признаков¶
Карта обучена на определённой версии признаков (config/features.json, поле version). В контракте Scorecard для неё нет поля, поэтому она берётся из паспорта рядом с картой: scorecard_vN.training.json, поле feature_set_version (формат T10, apps/ml/README.md). Если версия признаков заявки другая, основная модель отказывается считать (FeatureSetMismatch): баллы по чужим признакам были бы бессмысленны.
Основная модель и претендент¶
| Роль | Файл по умолчанию | Переменная |
|---|---|---|
| Основная модель — принимает решение | apps/ml/scorecard_v1.json — обученная карта (T10) |
SCORECARD_CHAMPION |
| Претендент — теневой режим | apps/api/scorecards/scorecard_v0.json — экспертная карта |
SCORECARD_CHALLENGER (none выключает) |
Пути — от корня репозитория. Scorer.score(features) возвращает результат основной модели и претендента (ChallengerResult). Оба результата пишутся в лог; результат претендента сохраняется в Decision.challenger, но на решение не влияет. Если претендент не загрузился, упал или ждёт другую версию признаков, в логе появляется предупреждение, а challenger остаётся пустым — основная модель считает как обычно.
Строка сравнения моделей пишется с уровнем INFO, предупреждения — с WARNING (логгер apps.api.scoring). Своей настройки логирования у API пока нет, и после миграций корневой логгер стоит на WARN (alembic.ini), поэтому строки INFO в API пока не видны; основной след теневого режима — Decision.challenger.
Экспертная карта scorecard_v0¶
12 признаков из SPEC §8: возраст бизнеса, худшая просрочка компании и собственника, запросы КИ за 3 месяца, долг к выручке, волатильность поступлений, месяцы без поступлений, остаток в днях расходов, регулярность налогов, группа риска ОКВЭД, смена собственника или директора, отрицательный капитал. Баллы интервала показывают, насколько он надёжнее (+) или рискованнее (−) среднего, как в обученной карте. WoE у экспертных интервалов нет (woe: null). Базовые баллы (564) подобраны так, чтобы средняя PD на синтетическом мире была близка к доле дефолтов.
Для сравнения на мире (зерно 42, 5000 компаний): Gini на отложенной выборке у scorecard_v0 — 0,32, у обученной scorecard_v1 — 0,46. Средняя PD — 7,2% при доле дефолтов 7,4%. Это аргумент в пользу обучения, и именно такое сравнение даёт теневой режим.
Упрощения¶
# SIMPLIFICATION:претендент — наша экспертная карта, а не другая версия модели банка. В банке рядом работали бы несколько версий моделей, и результаты претендента собирались бы для решения о замене основной модели.- Веса
scorecard_v0заданы вручную по смыслу признаков; их подтверждает экономический блок. - Результаты теневого режима пишутся в лог и в
Decision. Отдельного хранилища для сравнения моделей и мониторинга нет. - Штраф к оценке за недоступный необязательный источник (ФНС, ФССП, арбитраж; флаги T14) пока не применяется: его размер не задан в политике (
config/).