Лабораторная работа №7: Введение в ML. Классификация с KNN
Цель: Построить свой первый полноценный пайплайн машинного обучения: от разделения данных до оценки качества модели. Мы решим задачу бинарной классификации, используя алгоритм k-ближайших соседей (KNN), и научимся выбирать правильные метрики, когда Accuracy врет.
Инструменты:
sklearn.model_selection: для разделения данных.sklearn.preprocessing: для масштабирования (StandardScaler).sklearn.neighbors: сама модель KNN.sklearn.metrics: инструменты оценки.
Данные: Мы будем работать с классическим датасетом Breast Cancer Wisconsin (Рак груди).
- Признаки (X): Характеристики клеточного ядра (радиус, текстура, периметр и т.д.).
- Целевая переменная (y): Диагноз (0 = Malignant/Злокачественная, 1 = Benign/Доброкачественная).
- Примечание: В sklearn часто 0 - это Malignant, но всегда лучше проверять
target_names.
- Примечание: В sklearn часто 0 - это Malignant, но всегда лучше проверять
Часть 1: Подготовка данных и Train/Test Split
Модели нельзя давать “подглядывать” в ответы теста.
Часть 2: Масштабирование (Scaling) — Критически важно для KNN
KNN измеряет расстояния. Если один признак изменяется в пределах [0, 0.1], а другой [0, 1000], второй будет полностью доминировать. Нужно привести их к одному масштабу.
Золотое правило масштабирования
Правило: StandardScaler обучается (fit) ТОЛЬКО на Train, и применяется (transform) к Train и Test. Иначе — утечка данных.
Задание 2.1: StandardScaler
- Создайте объект
StandardScaler. - Обучите его на
X_trainи трансформируйтеX_train. - Трансформируйте
X_test(без обучения!).
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# TODO: Обучите scaler на train и преобразуйте train
# X_train_scaled = scaler.fit_transform(...)
# TODO: Только преобразуйте test (используйте transform)
# X_test_scaled = scaler.transform(...)
# Проверка: среднее должно быть ~0, стд ~1
# print("Train Mean (первые 5 признаков):", X_train_scaled[:, :5].mean(axis=0))
Часть 3: Обучение и Предсказание
Задание 3.1: KNN Classifier
- Импортируйте
KNeighborsClassifier. - Создайте модель с
n_neighbors=5. - Обучите (
fit) на масштабированных данных. - Сделайте предсказание (
predict) для теста.
from sklearn.neighbors import KNeighborsClassifier
# TODO: Создайте и обучите модель
# knn = KNeighborsClassifier(n_neighbors=5)
# knn.fit(..., ...)
# TODO: Получите предсказания для теста
# y_pred = knn.predict(...)
Часть 4: Оценка качества (Evaluation)
Мы диагностируем рак.
Типы ошибок классификации
- Ошибка I рода (False Positive): Сказали “Рак”, а человек здоров. Стресс, биопсия, но человек жив.
- Ошибка II рода (False Negative): Сказали “Здоров”, а у человека рак. Человек может умереть.
В этой задаче Recall (способность найти всех больных) важнее Precision.
Примечание: В sklearn датасете 0 = Malignant (Злокачественная). Чтобы метрики считались корректно для “Болезни” (класса 0), можно либо инвертировать метки, либо смотреть внимательно на отчет. В classification_report мы увидим метрики для каждого класса.
Задание 4.1: Матрица ошибок и Метрики
- Постройте Confusion Matrix (тепловую карту).
- Выведите
classification_report. - Посчитайте
accuracy_score.
from sklearn.metrics import confusion_matrix, classification_report, accuracy_score
# TODO: Постройте матрицу ошибок
# cm = confusion_matrix(y_test, y_pred)
# Визуализация
# plt.figure(figsize=(6, 5))
# sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=data.target_names, yticklabels=data.target_names)
# plt.ylabel('Истинный класс')
# plt.xlabel('Предсказанный класс')
# plt.title('Матрица ошибок')
# plt.show()
# TODO: Выведите текстовый отчет
# print(classification_report(y_test, y_pred, target_names=data.target_names))
# Вопрос: Каков Recall для класса 'malignant'? Много ли опасных случаев мы пропустили?
Часть 5 (Бонус): Поиск лучшего K
Как выбрать n_neighbors? Давайте переберем значения от 1 до 20 и посмотрим, как меняется ошибка.
Задание 5.1: Цикл обучения
- Запустите цикл
kот 1 до 20. - В каждой итерации обучайте KNN, делайте предсказание и сохраняйте
accuracy(или F1) в список. - Постройте график зависимости метрики от K.
scores = []
k_range = range(1, 21)
# TODO: Напишите цикл
# for k in k_range:
# knn_temp = KNeighborsClassifier(n_neighbors=k)
# knn_temp.fit(X_train_scaled, y_train)
# scores.append(knn_temp.score(X_test_scaled, y_test))
# plt.figure(figsize=(10, 6))
# plt.plot(k_range, scores, marker='o')
# plt.xlabel('Value of K')
# plt.ylabel('Testing Accuracy')
# plt.grid()
# plt.show()
# Какое K выглядит оптимальным (где график выходит на плато или имеет пик)?