Модель бинарной классификации, которая по анкете клиента заранее оценивает: вернёт он кредит или нет.
- Установка и запуск
- Структура проекта
- Постановка бизнес-задачи
- Исходные данные
- Что сделала модель
- Результаты
- Выводы
- Дальнейшие шаги
-
Клонировать репозиторий и установить зависимости:
git clone https://github.com/DesertFull-sql/credit-risk.git cd credit-risk pip install -r requirements.txt -
Создать файл
.envпо образцу.env.exampleи указать строку подключения к базе данных:DATABASE_URL=postgresql+psycopg2://user:password@localhost/dbname -
Сравнить модели через кросс-валидацию:
python -m src.train
-
Оценить лучшую модель на отложенном тесте и сохранить её:
python -m src.evaluate
Результат сохраняется в файл models/credit_model.joblib.
credit_risk/
├── .env # секреты
├── .env.example # шаблон конфигурации
├── requirements.txt # зависимости
├── data/ # данные
├── models/ # сохранённые модели
├── notebooks/ # эксперименты
└── src/
├── config.py # настройки проекта: пути, признаки, константы
├── data.py # загрузка данных из PostgreSQL (SQLAlchemy)
├── preprocess.py # очистка: дубликаты, выбросы
├── features.py # трансформеры признаков (ColumnTransformer)
├── models.py # сборка пайплайнов трёх моделей
├── train.py # обучение и кросс-валидация
└── evaluate.py # итоговая оценка и сохранение модели
Банк выдаёт кредиты и теряет деньги на клиентах, которые их не возвращают.
Проблема: в момент заявки не видно, кто в итоге окажется надёжным, а кто — нет.
Задача: до выдачи кредита, используя только данные из анкеты (возраст, доход, стаж, параметры кредита), спрогнозировать, станет ли клиент проблемным. Тип задачи — бинарная классификация (два исхода: «в норме» / «проблемный»).
Что это даёт банку:
- меньше невозвратов и потерь;
- обоснованное решение: кого одобрить, а кого проверить внимательнее;
- скорость: модель отвечает мгновенно, вручную такой анализ за секунды не сделать.
Данные: таблица credit_risk в локальной базе PostgreSQL — 32 581 анкета, 11 признаков + целевая переменная.
| Признак | Что означает |
|---|---|
person_age |
возраст |
person_income |
годовой доход |
person_emp_length |
стаж работы в годах |
loan_amnt |
запрашиваемая сумма кредита |
loan_int_rate |
процентная ставка |
loan_percent_income |
доля ежемесячного платежа в доходе |
cb_person_cred_hist_length |
срок кредитной истории |
person_home_ownership |
жильё: своё, в ипотеке, аренда и т. п. |
loan_intent |
цель кредита |
loan_grade |
кредитный рейтинг клиента |
cb_person_default_on_file |
были ли просрочки раньше |
loan_status |
целевая переменная: в норме (0) / проблемный (1) |
Классы несбалансированы: проблемных кредитов значительно меньше, чем нормальных, — это учтено при обучении (class_weight="balanced").
- Загрузил данные из PostgreSQL через SQLAlchemy.
- Очистил данные (data cleaning): удалил полные дубликаты строк, отбросил выбросы (возраст старше 100 лет, стаж больше 50 лет), заполнил пропуски (импутация):
loan_int_rate— медианой,person_emp_length— константой-1. - Подготовил признаки (feature engineering): категориальные колонки (цель кредита, тип жилья, рейтинг) закодировал
OneHotEncoderсhandle_unknown="ignore"; для линейной модели добавил масштабированиеStandardScaler; всё собрал в единыйColumnTransformer, а его — в прогнозный конвейерPipelineвместе с моделью. - Обучил три модели: случайный лес (
RandomForest, 300 деревьев), дерево решений (DecisionTree, глубина 8) и логистическую регрессию (с масштабированием признаков). - Оценил честно через кросс-валидацию: каждую модель обучал на 5 фолдах данных и проверял на невиданных (стратифицированная схема, чтобы пропорции классов сохранялись). Метрики:
accuracy,precision,recall,F1,ROC-AUC. - Проверил лучшую модель на отложенном тесте (20% данных, которых модель никогда не видела, с сохранением пропорций классов) и сохранил её через
joblib.
| Модель | ROC-AUC | F1 |
|---|---|---|
| Random Forest (случайный лес) | 0.932 ± 0.005 | 0.818 ± 0.016 |
| Decision Tree (дерево решений) | 0.904 ± 0.005 | 0.776 ± 0.014 |
| Logistic Regression (логистическая регрессия) | 0.870 ± 0.006 | 0.642 ± 0.004 |
Лучший и самый стабильный результат у случайного леса, поэтому дальше работаю с ним.
Тест: 6 482 анкеты, которых модель не видела.
| Метрика | Значение |
|---|---|
| Accuracy (доля верных ответов) | 0.94 |
| ROC-AUC | 0.9356 |
Матрица ошибок (confusion_matrix):
предсказано
0 1
факт 0 [5042 22]
1 [ 395 1023]
- Precision класса 1 = 0.98 — из 100 анкет, которые модель назвала проблемными, ~98 действительно проблемные. Ложных тревог почти нет.
- Recall класса 1 = 0.72 — модель поймала 1 023 из 1 418 проблемных кредитов (~3 из 4), пропустила 395. Часть «плохих» проходит как «хорошие».
- Случайный лес обходит обе альтернативы и стабилен на всех фолдах — результат надёжный.
- Модель предупреждает редко, но почти всегда по делу:
precision = 0.98. - Цена этой осторожности —
recall = 0.72: примерно четверть проблемных кредитов пропускается. Если для банка важнее не упустить дефолты, порог решения можно сместить вручную — это выбор бизнеса, а не техники. - Для банка модель полезна как первый фильтр заявки: явно проблемные — на проверку, остальное — стандартный поток.
- подбор гиперпараметров через
GridSearchCV; - индикаторы пропусков (
add_missing_indicators); - отбор признаков и сравнение с бустингами (XGBoost, LightGBM);
- развёртывание модели как сервиса (FastAPI) для проверки заявок онлайн.