Skip to content

Latest commit

 

History

History
310 lines (217 loc) · 13.9 KB

File metadata and controls

310 lines (217 loc) · 13.9 KB

План A/B-тестирования модели кредитного дефолта

1. Цель A/B-теста

Цель A/B-теста - сравнить текущую модель прогнозирования дефолта model_v1 с новой моделью model_v2 и определить, дает ли новая версия статистически и практически значимое улучшение качества риск-оценки.

В контексте банка модель используется как вспомогательный инструмент для оценки вероятности дефолта клиента по кредитной карте.

2. Гипотезы

Нулевая гипотеза H0

Новая модель model_v2 не улучшает качество прогнозирования дефолта по сравнению с моделью model_v1.

Альтернативная гипотеза H1

Новая модель model_v2 улучшает качество прогнозирования дефолта по сравнению с моделью model_v1 по основной метрике и не ухудшает критичные дополнительные метрики.

3. Группы эксперимента

Группа Доля трафика Модель Описание
Control 50% model_v1 Текущая модель, сохраненная как credit_default_model_v1.joblib
Treatment 50% model_v2 Демонстрационная новая версия: та же модель, но с порогом 0.50 вместо 0.47

Распределение клиентов выполняется случайно, но стабильно: один и тот же клиент должен попадать в одну и ту же группу в течение всего эксперимента. Для этого можно использовать hash от client_id.

Пример логики:

hash(client_id) % 100 < 50  -> control
hash(client_id) % 100 >= 50 -> treatment

Если client_id отсутствует, можно использовать случайное распределение на уровне запроса, но это хуже для реального production-сценария.

4. Длительность теста

Рекомендуемая длительность: 2-4 недели.

Причины:

  • необходимо накопить достаточное число наблюдений по дефолтному классу;
  • дефолт является отложенным событием;
  • короткий тест может дать нестабильные выводы из-за сезонности и случайных колебаний.

В учебном проекте допускается имитация A/B-теста на отложенной test-выборке.

5. Основная техническая метрика

Основная метрика:

F1-score для класса дефолта

Почему F1:

  • учитывает и Precision, и Recall;
  • подходит для несбалансированной бинарной классификации;
  • дефолтный класс важнее, чем класс отсутствия дефолта.

6. Дополнительные технические метрики

Метрика Зачем нужна
Precision для класса дефолта Показывает, насколько часто клиенты, отмеченные как рискованные, действительно дефолтные
Recall для класса дефолта Показывает, какую долю реальных дефолтов модель смогла обнаружить
ROC-AUC Оценивает ранжирующую способность модели
Average Precision Более информативна при дисбалансе классов
Доля клиентов, попавших в high-risk Важна для операционной нагрузки на риск-команду

7. Бизнес-метрики

7.1 Expected Loss

Ожидаемые финансовые потери можно оценить так:

Expected Loss = PD * LGD * EAD

Где:

  • PD - вероятность дефолта из модели;
  • LGD - loss given default, доля потерь при дефолте;
  • EAD - exposure at default, сумма задолженности или кредитный лимит.

Для сравнения моделей можно считать суммарный expected loss по клиентам, которым была бы одобрена кредитная карта.

7.2 Approval Rate при заданном уровне риска

Метрика показывает, какую долю клиентов можно одобрить при условии, что риск портфеля не превышает допустимый порог.

Пример:

Approval Rate = approved_clients / all_clients

Успешная модель должна позволять сохранить или увеличить approval rate без роста дефолтности портфеля.

7.3 Стоимость ошибок

Ошибки имеют разную бизнес-стоимость:

Ошибка Интерпретация Последствие
False Negative Модель не заметила рискованного клиента Возможные финансовые потери
False Positive Модель ошибочно посчитала надежного клиента рискованным Потеря клиента или прибыли

Для банка false negative обычно дороже, поэтому Recall может быть важнее Precision.

8. Статистический анализ

8.1 Для долей и ошибок

Для сравнения долей, например доли обнаруженных дефолтов или доли ошибок, можно использовать z-test для двух пропорций.

Пример:

H0: p_v1 = p_v2
H1: p_v1 != p_v2

8.2 Для средних бизнес-показателей

Для сравнения средних значений expected loss можно использовать:

  • t-test, если распределение достаточно стабильно;
  • bootstrap confidence interval, если распределение скошенное.

Для финансовых потерь предпочтительнее bootstrap, потому что распределение убытков часто асимметрично.

8.3 Доверительные интервалы

Для ключевых метрик строятся 95% доверительные интервалы:

  • F1-score: bootstrap по клиентам;
  • Precision/Recall: bootstrap или интервалы для долей;
  • expected loss: bootstrap.

9. Критерий успешности

Модель model_v2 считается успешной, если выполняются условия:

  1. F1-score для класса дефолта статистически значимо выше, чем у model_v1;
  2. Recall не ниже, чем у model_v1, либо снижение Recall экономически обосновано;
  3. Precision не падает более чем на допустимый порог, например 2-3 п.п.;
  4. expected loss снижается;
  5. нет существенного роста операционной нагрузки, например доли клиентов, отправленных на ручную проверку.

10. Практическая демонстрация в API

В текущей реализации API поддерживает две версии модели: model_v1 и model_v2. Версия модели передается через поле model_version в JSON body.

models/
├── credit_default_model_v1.joblib
└── credit_default_model_v2.joblib

Поле в JSON body:

{
  "model_version": "model_v1",
  "data": {
    "...": "..."
  }
}

Для второй модели:

{
  "model_version": "model_v2",
  "data": {
    "...": "..."
  }
}

Также можно реализовать автоматическое распределение трафика:

POST /predict-ab

где API сам выбирает модель по hash от client_id.

11. Связь с архитектурой

Текущая архитектура уже поддерживает основные элементы A/B-теста:

  • API может логировать версию модели;
  • PostgreSQL может хранить результаты запросов;
  • RabbitMQ может использоваться для асинхронного логирования событий;
  • batch-worker может обрабатывать отложенные расчеты метрик;
  • A/B-события сохраняются в отдельную таблицу ab_test_events;

Практическая реализация A/B-логирования подробно описана в разделе 13. Ключевая таблица - ab_test_events, где сохраняются версия модели, группа эксперимента, prediction, probability, threshold, request/response JSON и будущий фактический таргет actual_default.

После появления фактического таргета actual_default можно сравнивать качество моделей.

12. Ограничения

  • Дефолт является отложенным событием, поэтому онлайн A/B-тест требует времени.
  • Нельзя полностью автоматизировать кредитное решение без контроля рисков.
  • Модель должна использоваться с human-in-the-loop для спорных кейсов.
  • Необходимо контролировать fairness и возможные proxy-переменные, чтобы избежать дискриминации.

13. Практическая реализация A/B-логирования

В проекте реализовано сохранение A/B-событий в PostgreSQL. Таблица создается при старте БД из файла:

sql/init_db.sql

Структура таблицы:

CREATE TABLE IF NOT EXISTS ab_test_events (
    id BIGSERIAL PRIMARY KEY,
    client_id TEXT,
    group_name TEXT,
    model_version TEXT NOT NULL,
    prediction INTEGER,
    probability DOUBLE PRECISION,
    threshold DOUBLE PRECISION,
    decision TEXT,
    actual_default INTEGER,
    request_json JSONB,
    response_json JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

При вызове /predict сервис сохраняет A/B-событие сразу после инференса. При вызове /predict-batch-async событие сохраняет batch-worker после обработки сообщения из RabbitMQ.

Правила групп:

  • model_v1 -> control;
  • model_v2 -> treatment.

В событие сохраняются:

  • версия модели;
  • группа A/B-теста;
  • prediction;
  • probability;
  • threshold;
  • business decision (approve или review);
  • request JSON (record + request_context для аудита);
  • response JSON;
  • client_id, если он передан во входном объекте;
  • actual_default, который может быть заполнен позже после появления фактического исхода.

Пример входного запроса:

{
  "model_version": "model_v2",
  "data": {
    "client_id": "client_001",
    "LIMIT_BAL": 20000,
    "SEX": 2
  }
}

Пример SQL-запроса для первичной проверки A/B-событий:

SELECT
    group_name,
    model_version,
    COUNT(*) AS n_events,
    AVG(prediction) AS predicted_default_rate,
    AVG(probability) AS avg_probability
FROM ab_test_events
GROUP BY group_name, model_version
ORDER BY group_name, model_version;

После заполнения actual_default можно считать confusion matrix по группам:

SELECT
    group_name,
    model_version,
    COUNT(*) AS n_events,
    SUM(CASE WHEN prediction = 1 AND actual_default = 1 THEN 1 ELSE 0 END) AS tp,
    SUM(CASE WHEN prediction = 1 AND actual_default = 0 THEN 1 ELSE 0 END) AS fp,
    SUM(CASE WHEN prediction = 0 AND actual_default = 1 THEN 1 ELSE 0 END) AS fn,
    SUM(CASE WHEN prediction = 0 AND actual_default = 0 THEN 1 ELSE 0 END) AS tn
FROM ab_test_events
WHERE actual_default IS NOT NULL
GROUP BY group_name, model_version;

На основе tp, fp, fn, tn можно рассчитать Precision, Recall и F1-score для каждой версии модели.

В текущей реализации model_v2 отличается от model_v1 порогом классификации. Это минимальная, но рабочая демонстрация сравнения двух версий через API и таблицу ab_test_events. В реальном развитии проекта model_v2 можно заменить на модель, обученную с другими гиперпараметрами или алгоритмом.