Навчальний простір / Python
Основи штучного інтелекту

UCI Bank Marketing: класифікація, baseline та метрики

Класифікація на відкритих даних потребує контролю витоку й дисбалансу.

Зміст матеріалу

Мета та потрібні знання

Навчити й оцінити класифікатор на відкритих банківських даних, порівняти його з простим baseline та перевірити доступність ознак у момент рішення. Потрібно знати train/test split, категоріальні ознаки, Pipeline, precision, recall та F1. Демонстрація використовує bank-full.csv із UCI Bank Marketing id222. Робота має дослідницький характер і не призначена для реального оцінювання конкретних клієнтів.

Теоретичний мінімум і постановка задачі

Ціль y позначає згоду на депозит: yes або no. Один рядок описує спостереження кампанії з характеристиками клієнта та контактів. Момент прогнозування в нашій постановці — перед поточним дзвінком. Duration вимірює тривалість цього дзвінка й у цей момент невідомий, тому його виключаємо. Висока оцінка з duration могла б відображати витік майбутньої інформації.

Демонстрація використовує лише явний перелік характеристик клієнта та попередньої кампанії. Такий перелік простіше перевірити за часом доступності, ніж автоматично взяти всі колонки. Дисбаланс означає різну кількість yes і no; accuracy сама по собі може приховати нездатність знаходити yes. Recall для yes описує частку знайдених згод, precision — частку справжніх згод серед прогнозованих.

Паспорт: UCI id222, DOI 10.24432/C5K306, файл bank-full.csv всередині bank.zip, автори S. Moro, P. Cortez, P. Rita, ліцензія CC BY 4.0. Це класичний файл із 45211 рядками та 17 колонками включно з y. Bank-additional-full.csv має іншу схему, тому його не можна підставляти без адаптації.

Підготовка середовища: Kaggle або локальний Python

У Kaggle створіть Python Notebook у розділі Code, увімкніть Internet у налаштуваннях сесії для завантаження з UCI та запускайте код комірками згори вниз. Якщо доступ до Internet у вашому обліковому записі недоступний, завантажте CSV з офіційного UCI-каталогу вручну й підключіть його як вхідні дані notebook. У панелі Input знайдіть фактичний шлях файлу; використайте його в read_csv. У notebook можуть бути попередньо встановлені потрібні пакети; перевірте імпорт перед додатковим встановленням.

Локально створіть віртуальне середовище: python3 -m venv .venv, активуйте source .venv/bin/activate на macOS/Linux. У Windows використайте py -m venv .venv і .venv\Scripts\Activate.ps1. Встановіть python -m pip install numpy pandas matplotlib scikit-learn. Збережіть версії через python -m pip freeze > requirements-lock.txt. У Kaggle збережіть виконану версію notebook разом із результатами.

Каталог Kaggle містить набори різного походження та з різними ліцензіями. Відкритість перевіряємо за сторінкою конкретного dataset: джерело, дозвіл використання, версія, автор, опис полів. Для власного Kaggle-набору обов’язково додайте посилання на першоджерело. API token для наведеного прямого завантаження UCI не потрібен.

Повний демонстраційний код

python
from pathlib import Path
from urllib.request import urlopen
from zipfile import ZipFile
from io import BytesIO
from hashlib import sha256
import pandas as pd

cache = Path("data")
cache.mkdir(exist_ok=True)
archive = cache / "uci-222.zip"
if not archive.exists():
    url = "https://archive.ics.uci.edu/static/public/222/bank+marketing.zip"
    with urlopen(url, timeout=30) as response:
        archive.write_bytes(response.read())
print("Archive SHA256:", sha256(archive.read_bytes()).hexdigest())
with ZipFile(archive) as outer:
    with ZipFile(BytesIO(outer.read("bank.zip"))) as inner:
        with inner.open("bank-full.csv") as source:
            df = pd.read_csv(source, sep=";")

from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.dummy import DummyClassifier
from sklearn.metrics import classification_report, confusion_matrix

assert df.shape == (45211, 17)
assert set(df["y"].unique()) == {"yes", "no"}
num = ["age", "balance", "previous"]
cat = ["job", "marital", "education", "default", "housing", "loan", "poutcome"]
X = df[num + cat].copy()  # duration і поточні підсумки контакту виключено.
y = df["y"].eq("yes").astype(int)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42)
preprocess = ColumnTransformer([
    ("num", Pipeline([("fill", SimpleImputer(strategy="median")),
                      ("scale", StandardScaler())]), num),
    ("cat", Pipeline([("fill", SimpleImputer(strategy="most_frequent")),
                      ("encode", OneHotEncoder(handle_unknown="ignore"))]), cat)
])
model = Pipeline([("prepare", preprocess),
                  ("classifier", LogisticRegression(max_iter=2000))])
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)
print("Train CV F1:", cross_val_score(model, X_train, y_train, cv=cv, scoring="f1"))
model.fit(X_train, y_train)
baseline = DummyClassifier(strategy="most_frequent").fit(X_train, y_train)
for name, estimator in [("baseline", baseline), ("logistic", model)]:
    predicted = estimator.predict(X_test)
    print(name)
    print(confusion_matrix(y_test, predicted, labels=[0, 1]))
    print(classification_report(y_test, predicted,
          labels=[0, 1], target_names=["no", "yes"], zero_division=0))

Пояснення коду та межі оцінювання

Зовнішній архів містить bank.zip; BytesIO дозволяє прочитати його в пам’яті. OneHotEncoder створює індикатори категорій та обробляє незнайому категорію без помилки. Unknown у вихідних даних — документоване текстове значення; воно не стає NaN автоматично. SimpleImputer обробляє лише фактичні пропуски. Pipeline навчає медіани, масштабування й категорії всередині кожного train fold.

Stratify зберігає приблизні частки класів. CV використовує лише training-частину; test оцінюється після фіксації моделі. Очікуються три CV-оцінки, дві матриці помилок та звіти. Числа метрик потрібно отримати запуском, а не переписувати припущення. Majority-baseline зазвичай не знаходить yes; zero_division=0 явно обробляє відсутність позитивних прогнозів.

Random split оцінює ретроспективне розрізнення спостережень за припущення схожого розподілу. Воно не підтверджує якість майбутньої кампанії. Дані впорядковані за часом і можуть містити залежні контакти; у файлі немає надійного id клієнта для групового розділення. Це обмеження треба вказати, а подальшу часову перевірку провести окремо.

Покрокове виконання

Прочитайте паспорт, перевірте value_counts цілі та поясніть baseline. Виконайте код. У матриці рядки є справжніми класами, стовпці — прогнозованими: labels[0,1] задає порядок. Порахуйте precision і recall для yes вручну за чотирма клітинками. Поясніть, яку ціну можуть мати хибні позитивні та хибні негативні рішення.

Перевірте, що duration відсутній у X. Дослідіть категорії poutcome й частку unknown; не приписуйте unknown конкретний відомий результат. Після тестового звіту запишіть висновок і не підбирайте параметри за test. Якщо продовжуєте підбір, потрібна нова незалежна фінальна оцінка або відокремлений validation-процес.

Самостійне завдання

Оберіть інший відкритий класифікаційний dataset Kaggle або UCI з документованою ціллю. Створіть паспорт, момент прогнозування та таблицю доступності ознак. Побудуйте baseline і pipeline підготовки, виберіть метрики за ціною помилки. Обґрунтуйте random, group або time split залежно від одиниці спостереження. Виконайте train-only CV та один фінальний test-звіт із аналізом помилок. Основну модель і висновки розробіть самостійно.

Для варіанта Bank Marketing замість нового набору дослідіть хронологічний holdout і порівняйте обмеження двох протоколів. Не порівнюйте числа як доведення універсальної переваги: склад test-груп різний. Додатково поясніть, як змінити threshold використовуючи validation, зберігаючи test закритим.

Типові помилки, налагодження та результат

ConvergenceWarning потребує перевірки масштабування, max_iter та стабільності рішення. Невідома категорія без handle_unknown може зірвати predict. Дуже висока оцінка потребує перевірки витоку, повторів і перетину груп. Виключайте лише обґрунтовані ознаки та документуйте причину.

Подайте виконаний notebook, паспорт, split-протокол, CV, матриці й пояснення помилок. Чому duration непридатний до дзвінка? Що навчалося лише на train? Як majority-baseline поводиться з yes? Які дані потрібні для перевірки майбутньої кампанії?

Схема процесу

UCI Bank Marketing: класифікація, baseline та метрикиОберіть елемент, щоб побачити пояснення

Фіксуємо ціль і момент рішення.

Самоперевірка

Перевірте розуміння
Чому duration виключено з ознак?
Як обґрунтувати відповідь своїми словами?
Момент прогнозування визначає доступність інформації. Тривалість поточного дзвінка є майбутньою характеристикою.
Перед завершенням роботи

Підсумок

Класифікація на відкритих даних потребує контролю витоку й дисбалансу. Pipeline навчає підготовку лише на training-даних. Метрики та обмеження split визначають зміст висновку.

Джерела

Як оформити та здати роботу →

Опрацювали матеріал?

Збережіть цей крок у своєму прогресі.

← Повернутися до дисципліни
© 2026 Анастасія Іскандарова-МалаЕлектронний навчальний посібник · ДДТУ

Пошук у посібнику

Спробуйте «C++», «RAII» або «бази даних».