Перейти к содержанию

Скоринговый модуль (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/).