Files
ML/task9/[Base]_Sklearn_knn_normalize_cv_[2025_2026].ipynb
T
2025-11-12 11:34:34 +03:00

998 KiB
Raw Blame History

Машинное обучение. ВМК МГУ

Практическое задание 3: Основы sklearn на примере KNN. Нормализация признаков и Кросс-валидация

Уровень: Базовый (Base)

О формате сдачи

🔷 При решении ноутбука используйте данный шаблон

✅ Можно добавлять новые ячейки любых типов
❌ Не нужно удалять текстовые ячейки c разметкой частей ноутбука и формулировками заданий

🔷 При оценивании задач учитывается код

✅ Задания, в которых необходим код, обычно помечаются фразами "Your code here"/"Ваш код" и аналогичными
❌ Ответы на вопросы без сопутствующего кода оцениваются в 0 баллов
❌ Наличе работоспособного кода в ноутбуке, если на сказано иного, обязательно

🔷 При оценивании задач учитываются выводы

✅ Задания, в которых необходимы выводы, обычно помечаются фразами Вывод"/"Ответ на вопрос"/"Ваш текст" и аналогичными
✅ Обычно выводы подразумевают под собой текстовый ответ (можно писать markdown, latex).
✅ Сопутствующие изображения, графики, таблички - приветствуются!
❌ При отсутствии выводов задание не засчитается на полный балл

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

Примерное время выполнения (execution time/время выполнения, если нажать run all) всех ячеек ноутбука при правильной реализации: 10 минут

Подготовка рабочей среды

Сначала установим нужные нам версии библиотек. Мы гарантируем, что в данных версиях задание будет корректно отрабатывать.

После установки нужных версий, возможно, нужно перезагрузить среду (runtime), но скорее всего вам это не понадобится

На скачивание файла и установку понадобится не более 5 минут.

Важно!

Устанавливать нужные версии нужно каждый раз, когда создается новый рантайм. Например, если вы 2 часа подряд делаете это задание, то подготовить библиотеки достаточно 1 раз. Но если вы, например, начали в понедельник, затем закрыли/выключили ноутбук, то при продолжении в среду, вам нужно будет запустить рантайм заново и следовательно заново установить библиотеки.

Важно! Если вы предпочитаете делать практические задания на своем личном ноутбуке, то проверьте, что вы установили рабочее окружение в соответствии с гайдом

In [ ]:
! curl https://raw.githubusercontent.com/MSU-ML-COURSE/ML-COURSE-25-26/refs/heads/master/requirements/requirements.txt -o ./requirements_2025_26_for_colab_small.txt
! pip install -r ./requirements_2025_26_for_colab_small.txt

Проверим версию библиотеки:

In [4]:
import catboost
assert(catboost.__version__ == '1.2.8')

Теперь можно приступать к выполнению задания! :)


Часть 0. Знакомство с библиотекой scikit-learn

Scikit-learn - это библиотека машинного обучения с открытым исходным кодом, которая предоставляет различные инструменты для предобработки данных и обучения моделей.

Подробную документацию с обширным количеством теоретических и практических примеров можно найти на сайте библиотеки

Импорт библиотеки

Можно импортировать библиотеку целиком:

In [5]:
import sklearn

Однако, так как библиотека очень обширная и содержит большое количество модулей, то импортируют либо модуль, либо классы/методы точечно и целенаправленно:

In [8]:
from sklearn.neighbors import KNeighborsClassifier # класс, с помощью которого мы в дальнейшем будем обучать kNN

Подсказка обычно по названию модуля интуитивно понятно, какие методы и функции в нем содержатся. С увеличением практики использования библиотеки вы сможете лучше и быстрее навигироваться в ней. Например, модуль neighbors содержит различные методы поиска ближайших соседей. Помните, что есть как версии для классификации, так и для задач регрессии? Вот версию для классификации мы выше как раз и импортировали. Однако если провалиться в описание модуля можно увидеть еще много разных классов и функций, которые так или иначе связаны с поиском соседей.

Общее устройство моделей

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

Обычно, чтобы решить задачу машинного обучения, нам нужно (как минимум):

  • Знать, какой моделью (алгоритмом) хотим воспользоваться
  • Обучить ее на некоторое обучающей выборке
  • Уметь узнавать предсказания модели на новой выборке

Вам не нужно запоминать для каждой конкретной модели, а как она вообще обучается и как это написать: библиотека все делает за вас.

Каждая модель машинного обучения имеет два метода: fit - метод ответственный за обучения модели и predict - метод ответственный за предсказание целевой переменной.

  • Как правило, метод fit принимает на вход два аргумента: X_{train} - обучающая выборка, y_{train} - значения целевых переменных на объектах обучающей выборки.
  • Метод predict принимает на вход набор данных X_{test} и выдает предсказанные значения целевых переменных y_{test} на этом наборе данных.
  • В моделях классификации бывает метод predict_proba, который принимает на вход набор данных и выдает вероятности принадлежности классам.
  • Если нами решается задача классификации, то вектор целевых переменных y содержит целые числа (или другое множество дискретных величин).
  • При решении задачи регрессии вектор y содержит вещественные числа.

Выборка (данные) X представляется в виде матрицы размера (n_samples, n_features), то есть каждому объекту соответствует отдельная строка, а каждому признаку - отдельный столбец.

Целевая переменная обычно представляется одномерным вектором размера (n_samples) - предсказаниями для каждого объекта, в случае предсказания вероятностей классов - матрицей размера (n_samples, n_classes) - значения вероктностей принадлежности объекта к тому или иному классу

Приведем пример стандартной работы с библиотекой scikit-learn

Подсказка Это совсем-совсем базовый способ обучения моделей, на практике используют много дополнительных приемов, постепенно которые мы будем с вами изучать

In [9]:
#Создаем обучающую выборку
X_train = [[0], [1], [2], [3]] # матрица размера 4 x 1, 4 - объекта, 1 признак
y_train = [0, 0, 1, 1] # обучающий вектор целевых переменных

#Создаем тестовую выборку
X_test = [[1.1], [2.8]] # матрица размера 2x1, 2 - объекта, 1 признак

#Создаем объект класса 3-NN классификатора
neigh = KNeighborsClassifier(n_neighbors=3)

#Обучаем классифкатора на созданной ранее выборке
neigh.fit(X_train, y_train)

#Предсказываем метку класса нового объекта с помощью метода predict
y_test = neigh.predict(X_test)
print(y_test) # y_test - вектор размера 2 (в тестовой выборке 2 объекта)
[0 1]

Общее устройство предобработки данных

На практике почти никогда вы не встретите идеальных данных :) Вам всегда понадобится на них посмотреть, проанализировать, где-то почистить, где-то преобразовать и пр.

Упрощая, все работу с данными можно проделать с помощью 2 библиотек:

  • pandas - с помощью этой библиотеки проанализировать данные, посмотреть на то, что в них хранится, принять решение о удалении мусора, замены каких-то значений и прочего. Данные техники вы совсем немного затронули в предыдущем домашнем задании, но подробнее мы к ним вернемся в следующем домашнем задании

  • skelarn - технические способы обработки данных, особенности которых связаны непосредственно с моделью или способом обучения

Пример Данная библиотека поможет решить вопрос: а как нам признаки с типом строка (например, название видео) "запихнуть" в kNN? Он же только чиселки умеет понимать?

Пример Или на следующий вопрос: с лекции немного припоминаю, что для более хорошей работы метрических методов хорошо бы данные привести в один масштаб. Как это можно быстро и безболезненно сделать?

Например, есть 2 признака:

  • вероятность покупки p - лежит на отрезке [0, 1]
  • стоимость покупки s - лежит на отрезке [0, 100000500000]

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

В sklearn способы обработки данных также предоставляются через различные классы, которые имеют одинаковый интерфейс:

Каждый модуль предобработки данных имеет два метода: fit и transform.

  • Как правило, метод fit принимает на вход обучающую выборку X_train и считает по ней необходимые для заданного типа преобразования статистики.
  • Метод transform преобразует входные данные, используя статистики, посчитанные при вызове метода fit и возвращает преобразованные данные.

Пояснение: Когда не хватает pandas или skelarn, можно обратиться к другим библиотекам, например, numpy :) Sklearn обычно одинаково хорошо работает с данными, представленным как и pd.DataFrame, так и numpy-array или обычными вложенными питоновскими списками


In [6]:
import numpy as np
X_train = np.array([[0, 0], [0.5, 100], [1, 1000005000]]) # numpy используем для удобства отображения
# выборка из 3 примеров, 1й признак - вероятность покупки p
# 2й признак - стоимость покупки s

print(X_train)
[[0.000000e+00 0.000000e+00]
 [5.000000e-01 1.000000e+02]
 [1.000000e+00 1.000005e+09]]
In [10]:
from sklearn.preprocessing import MinMaxScaler # штука, которая приводит все значения признаков к отрезку [0, 1]
# В части 1 подробнее разберем

scaler = MinMaxScaler() # создаем экземпляр класса

scaler.fit(X_train) # "обучаем" преобразователь, на самом деле под капотом считаются статистики
# X_train тут не меняется!
Out [10]:
MinMaxScaler()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
MinMaxScaler()
In [11]:
# Теперь метод для каждого признака посчитал его минимальные и максимальные значения
# Но это под капотом, сюда можно вообще не лезть
scaler.data_min_, scaler.data_max_
Out [11]:
(array([0.]), array([3.]))
In [12]:
X_new = scaler.transform(X_train)
X_new
# Значения в 1й признаке не поменялись, так как они уже удовлетворяли условию
# А во втором - стали на отрезке [0, 1]
Out [12]:
array([[0.        ],
       [0.33333333],
       [0.66666667],
       [1.        ]])

Часть 1. Нормализация признакового пространства

В задачах машинного обучения важную роль играет нормализация данных. Ее мы затронули как раз выше. Для числовых признаков, как правило, используют два типа нормализации:

  • Нормализация к распределению с нулевым матожиданием и единичной дисперсией, x := \frac{x - E(x)}{\sigma (x)}, где матожидание E(x) и стандартное отклонение \sigma (x) считаются по каждому признаку (столбцу) отдельно
  • Нормализация в отрезок [0, 1], x := \frac{x - \min(x)}{\max(x) - \min(x)}, где минимум и максимум берется по каждому признаку (столбцу) отдельно (как в примере выше)

Пример

Ниже приведены: таблица до нормализации признаков, таблица после нормализации к нулевому матожиданию и единичной дисперсии, таблица после нормализации в отрезок [0, 1]

До нормализации

Признак 1 Признак 2
1 2
2 -1
3 0

Нормализация к нулевому матожиданию и единичной дисперсии

Признак 1 Признак 2
-1.225 1.336
0 -1.069
1.225 -0.267

Нормализация в отрезок [0, 1]

Признак 1 Признак 2
0 1
0.5 0
1 0.333

Задание 1.1 (юнит-тесты, 4 баллa)

В модуле scalers.py реализуйте два вида нормализации признакового пространства. StandardScaler - нормализация к распределению с нулевым матожиданием и единичной дисперсией, MinMaxScaler - нормализация в отрезок [0, 1].

Нормализаторы имеют два метода - метод fit и метод transform, что максимально повторяет концепцию sklearn. Метод fit получает на вход обучающую выборку и считает по ней все необходимые для заданного преобразования статистики, которые сохраняет во внутренние поля класса. Метод transform преобразует данные, используя статистистики, посчитанные при вызове метода fit.

Примеры запусков функций можно увидеть в открытых тестах.

Внимание! В текущую директорию необходимо загрузить файл scalers.py с реализованными классами. В Google Colab это можно сделать, нажав на значок "папки" слева, дальше на "загрузить" (значок листа со стрелкой вверх). Загружать нужно в папку "sample_data" (она у вас откроется по умолчанию)


Внимание! Перед тем, как выполнять задание дальше, убедитесь, что ваша реализация проходит тесты с системе проверке заданий, и вы загрузили сюда вашу реализацию

Теперь посмотрим работу "скейлеров" на примере данных:

In [13]:
from scalers import StandardScaler, MinMaxScaler
In [14]:
import numpy as np
import seaborn as sns
import pickle

from matplotlib import pyplot as plt


plt.rcParams["figure.figsize"] = (5,5)

Загрузим файл с данными data.pkl:

In [ ]:
import gdown

gdown.download(id='1cGV6SvpJuP_pa1mLCnI_SvTafEtW2sSO')

Загрузим данные в память. В переменной X будут храниться признаковые описания объектов, в переменной y~- метки классов

In [ ]:
with open('/content/data.pkl', 'rb') as file:
    X, y = pickle.load(file)
In [17]:
def plot_data_points(X, labels, xlim, ylim):
    g = sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=labels)
    g.set(xlim=xlim, ylim=ylim)
    plt.xlabel('x_1')
    plt.ylabel('x_2')
    plt.grid()

Визуализируем наши данные

In [18]:
plot_data_points(X, y, xlim=(-15, 15), ylim=(-15, 15))

Как видно, наши данные идеально разделяются прямой x=0

Функция plot\_knn\_bound принимает на вход объекты X, метки классов y, метод нормализации признаков scaler, число соседей n\_neighbors и границы рисунка xlim, ylim. Функция обучает KNN классификатор с числом соседей n\_neighbors и визуализирует разделяющую поверхность для классов, полученную с помощью обученного KNN классификатора.

  • Если вы не можете различить выбранные нами цвета, то измените список цветов в переменных cmap_light и cmap_bold. Актуальный список возможных цветов находится здесь
In [19]:
from matplotlib.colors import ListedColormap
from sklearn import neighbors, datasets

def plot_knn_bound(X, y, scaler=None, n_neighbors=10, xlim=(-15, 15), ylim=(-20, 20)):
    # step size in the mesh
    h = 0.05

    # Create color maps
    cmap_light = ListedColormap(['plum', 'blue', 'plum', 'green'][:np.unique(y).shape[0]])
    cmap_bold = ['plum', 'blue', 'plum', 'darkgreen'][:np.unique(y).shape[0]]

    x_min, x_max = xlim
    y_min, y_max = ylim
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    grid = np.c_[xx.ravel(), yy.ravel()]

    X_scaled = X # if scaler is None
    if scaler is not None:
        grid = scaler.transform(grid)
        X_scaled = scaler.transform(X)

    # we create an instance of Neighbours Classifier and fit the data.
    clf = neighbors.KNeighborsClassifier(n_neighbors, algorithm='brute')
    clf.fit(X_scaled, y)

    Z = clf.predict(grid)

    # Put the result into a color plot
    Z = Z.reshape(xx.shape)
    plt.contourf(xx, yy, Z, cmap=cmap_light)

    # Plot also the training points
    sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=y,
                    palette=cmap_bold, alpha=1.0, edgecolor="black")
    plt.xlabel('x_1')
    plt.ylabel('x_2')
    plt.title('Разделющие поверхности алгоритма {}-NN'.format(n_neighbors))
    plt.grid()
    plt.show()

Нарисуем разделющие поверхности $1$-NN и $10$-NN

In [20]:
plot_knn_bound(X, y, n_neighbors=1)
plot_knn_bound(X, y, n_neighbors=10)

Задание 1.2 (кросс-проверка, 2 балла)

Чем отличаются поверхности, полученные при числе соседей 1 и 10? Объясните, чем вызваны данные отличия

Ваши выводы тут:

При 1 соседе поверхность имеет выраженные границы между классами. Наблюдается высокая вариативность предсказаний даже при небольших изменениях входных данных.

При 10 соседях поверхность имеет более гладкие границы, но смещение может увеличивается


Задание 1.3 (кросс-проверка, 1 балл)

Данную проблему может решить нормализация признакового пространства.

Начертите разделяющие поверхности для KNN, обученного на нормализованных признаках с помощью реализованных вами нормализаторов. Используйте функцию plot\_knn\_bound. Менять функцию plot\_knn\_bound нельзя.

In [21]:
from scalers import StandardScaler, MinMaxScaler

scaler_std = StandardScaler()
scaler_std.fit(X)
plot_knn_bound(X, y, scaler=scaler_std, n_neighbors=1, xlim=(-4, 4))
plot_knn_bound(X, y, scaler=scaler_std, n_neighbors=10, xlim=(-4, 4))

scaler_mm = MinMaxScaler()
scaler_mm.fit(X)
plot_knn_bound(X, y, scaler=scaler_mm, n_neighbors=1, xlim=(-4, 4))
plot_knn_bound(X, y, scaler=scaler_mm, n_neighbors=10, xlim=(-4, 4))

Часть 2. Кросс-валидация на задаче регрессии

Часто модели в машинном обучении имеют вид не просто какой-то функции, а какой-то функции с некоторыми, так называемыми гиперпараметрами - это такие переменные величины, которые могут существенно повлиять на качество и вид модели, но напрямую обучить их нельзя (то есть в методе fit они вообще не обучаются). Одним из примеров таких гиперпараметров является:

n_neighbors - кол-во соседей в методах kNN. Если вернуться в начало ноутбука, то можно увидеть, что имено это значение мы явно задавали, когда создавали модель с помощью класса KNeighborsClassifier.

В этот момент возникает закономерный вопрос: а как нам найти то значение гиперпараметров, при которых наша модель будет показывать более высокие результаты решения задачи?


Продолжаем проваливаться в "кроличью нору" дальше. Для того, чтобы ответить на предыдущий вопрос, нужно сначала понять следующее: а как оценивать-то собственно качество моделей? Пусть даже мы зафиксировали какой-то гиперпараметр, как нам оценивать, насколько наша модель хороша?

Чтобы уметь оценивать качество моделей ("насколько вообще хорошо мы решили задачу") хочется иметь некий "стандарт качества" - данные, которые вообще не участвовали в обучении ("модель их не видела") и верные ответы на этих данных (которые модель также не видела)

При решении прикладных задач, как правило, доступна лишь обучающая выборка, а "стандарт качества" зачастую отсутствует. Решить данную проблему нам поможет техника, называемая кросс-валиация. Данная техника позволяет создавать "стандарты качества" (тестовые наборы, выборки) используя только обучающую выборку, но не позволяя модели "подглядывать" в создаваемые тестовые наборы.

Алгоритм применения кросс-валидации

Пусть зафиксирован некоторый набор значений гиперпараметров модели. В KNN под гиперпараметрами могут пониматься число соседей n_neighbors, метрика близости \rho, стратегия выбора весов w_i.

  • При кросс-валидации обучающая выборка делится на n равных частей (folds, фолды).
  • Затем обучаются n моделей с заданными гиперпараметрами (у всех моделей гиперпараметры одинаковые и фиксированы) - $i$-ая модель обучается на всей обучающей выборке, кроме объектов, которые попали в $i$-ый фолд (out-of-fold).
  • $i$-й фолд объявляется "стандартом качества" (тестовым). Затем измеряется качество $i$-ой модели на $i$-ом фолде. Так как он не участвовал в обучении этой модели, то полученная оценка качества модели будет являться корректной.
  • Финальным значением метрики качества для модели с заданными гиперпараметрами является среднее полученных нами значений на n фолдах.

Идея: Подумайте, как в описанную выше схему корректно добавить обучение скейлеров. Корректно ли будет масштабировать сначала все данные и потом делать кросс-валидацию или все-таки нужно как-то действовать хитрее?

Собственно, теперь мы можем выбрать наилучшие гиперпараметры по следующему алгоритму:

  • Фиксируем, какие значения гиперпараметров хотим перебрать
  • Для каждого набора значений проводим кросс-валидацию
  • Для каждого набора значений сохраняем качество на кросс-валидации
  • Выбираем тот набор значений гиперпараметров, где качество оказыается наилучшим!

Принцип работы кросс-валидации схематично изображен на рисунке.

cross-val-2.png

Небольшое видео объяснение о том, как работает кросс-валидация, можно посмотреть по ссылке


Теперь применем все полученные знания на практике на примере тренировочного датасета!

Далее будем решать задачу предсказания цены дома в Калифорнии с помощью $KNN$-регрессии. Цена дома - вещественное число, поэтому наша задача - задача регрессии. В качестве метрики качества будем использовать $R^2-$score.

R^2(y\_true, y\_predict) = 1 - \frac{\sum_{i=1}^{n} (y\_true_i - y\_predict_i)^2}{\sum_{i=1}^{n} (y\_true_i - E(y\_true_i))^2}

Примечание вы же помните, что предсказания и верные ответы можно оценивать по-разному и это называется метриками качества? Если данная теория подзабылась, то освежите, пожалуйста, знания лекционным материалом

In [22]:
from sklearn.datasets import fetch_california_housing # Да, из sklearn даже можно импортировать данные
from sklearn.model_selection import train_test_split # Вспомогательная функцию которая разобьет нам датасет на 2 части
In [23]:
X, y = fetch_california_housing(return_X_y=True)

Мы разобьем выборку на 2 части: обучающую (75%) и тестовую (25%) с помощью функции train\_test\_split. По умолчанию данная функцию перемешивает данные, что часто оказывается полезно в задачах подобного рода.

Зачем мы разбиваем? Мы пытаемся "эмулировать" жизеннную ситуацию. ML-разработчику приносят данные, говорят "обучи как можно лучше", потом мы протестируем твою модель на новых данных. Вот та тестовая выборка, которую мы выделили (25%) - это те данные, в которые ни в коем случае во время обучения подглядывать нельзя, нас потом на них будут проверять!

In [24]:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

Pipeline и перебор гиперпараметров

Кросс-валидация в skelarn, как нетрудно догадаться, уже реализована, остается научиться ее запускать :)

Но перед этим изучим еще парочку джедайских техник работы с sklearn, которые облегчат жизнь при написании моделей

In [25]:
from sklearn.neighbors import KNeighborsRegressor # kNN - регрессия
from sklearn.pipeline import Pipeline # разберем ниже
from sklearn.model_selection import GridSearchCV # класс, выполняющий кросс-валидацию
from sklearn.preprocessing import MinMaxScaler, StandardScaler # будем здесь использовать уже реализованные скейлеры

Что нам собственно нужно при решении задачи с учетом кросс-валидации?

  • Понять, предобрабатываем ли мы как-то данные или нет
  • Зафиксировать модель
  • Зафиксировать метрику качества
  • Понять, какие гиперпараметры мы хотим перебрать
  • Собственно выбрать лучший набор с помощью кросс-валидации
  • И оценить, а что на тестовой выборке-то получилось!

Шаг 1 и шаг 2 - объединяются логически - часто бывает так, что нам сначала нужно применить логику предобработки данных, а затем уже применить и обучить модель.

Логическая цепочка в таком случае будет выглдять так:

X -> предобрабатываем X -> X уходит в fit некоторой модели

sklearn Предоставляет удобное решение для составление таких цепочек-шагов обработки результатов, полученных на предыдущем шаге. Инструмент назвается Pipeline (пайплайны):

In [26]:
pipeline = Pipeline([
    ('normalizer', MinMaxScaler()),
    ('classifier', KNeighborsRegressor())
])

В данном примере вы говорим, что хотим, чтобы сначала данные отскелились, а потом, что получилось, подалось бы модели. Причем шаги этой цепочки обозначены строковыми и понятными обозначениям, которые нам скоро понадобятся

Pipeline наследует абсолютно то же интерфейс, что и остальные модули обучения в sklearn - имеет методы fit и predict! То есть получили некоторый "сборный конструктор" из различных частей.

In [27]:
pipeline.fit(X_train, y_train)
pipeline.predict(X_test)
Out [27]:
array([0.5482  , 0.7506  , 4.628604, ..., 1.328   , 2.4206  , 4.089404],
      shape=(5160,))

Далее встает вопрос: а как этот процесс обучения можно варьировать? В самом KNeighborsRegressor много гиперпараметров (число соседей, метрика расстояния и пр.), которые задаются при создании класса, а мы в Pipeline создали дефолтный экземпляр класса.. И вообще, можно же еще и перебирать, хотим ли мы использовать скейлер или нет, вдруг будет лучше без него? (а такое тоже бывает!)

И здесь sklearn нас снова выручает и дает возможность варьировать создание моделей, которые мы используем в Pipeline:

In [28]:
parameters = {
    'classifier__n_neighbors': [1, 5, 10],
    'classifier__metric': ['euclidean', 'cosine'],
    'classifier__weights': ['uniform', 'distance'],
    'normalizer': ['passthrough', MinMaxScaler(), StandardScaler()]
}

Мы можем задать такой словарик типа str -> list, который будет описывать все наши изменения, которые должны будут примениться в Pipeline (применяться они будут через класс кросс-валидации)

Если в качестве ключа написать ровно то название, которым мы обозначили шаг в Pipeline, то потом появляется возможность программно сопоставить эти два шага и заменить значения в Pipeline:

'normalizer': ['passthrough', MinMaxScaler(), StandardScaler()]

В данном случае у нас в Pipeline значения в названии шага normalizer будут меняться в соответствии с предложенными вариантам в списке

Пояснение: passthrough - ключевое слово, которые говорит sklearn "пропусти этот шаг"

А здесь уже логика чуть посложнее: у класса KNeighborsRegressor, который лежит в шаге classifier, при создании можно указать различный набор гиперпараметров, например, тот же n_negihbors. Обращаясь через двойное нижнее подчеркивание появляется возможность на лету изменять значения этих гиперпараметров в классе:

'classifier__n_neighbors': [1, 5, 10]

Здесь мы говорим "посмотри, что за модель лежит на шаге classifier, и замени у нее гиперпараметр n_neighbors в соответствии с сеткой перебора"

Задание 2.1 (кросс, 2 балла)

Запустите кросс-валидацию на 3 фолдах с помощью класса GridSearchCV и метода fit этого модуля. В качестве метрики используйте $R^2$-score (строкое представление в sklearn "r2"). Параметры для перебора описаны ниже

In [29]:
# задаем нужный пайплайн
pipeline = Pipeline([
    ('normalizer', 'passthrough'),
    ('classifier', KNeighborsRegressor())
])

# и сетку перебора параметров
parameters = {
    'classifier__n_neighbors': [1, 5, 10],
    'classifier__metric': ['euclidean', 'cosine'],
    'classifier__weights': ['uniform', 'distance'],
    'normalizer': ['passthrough', MinMaxScaler(), StandardScaler()]
}

grid_search = GridSearchCV(estimator=pipeline, param_grid=parameters, cv=3, scoring='r2', n_jobs=-1, verbose=10)
grid_search.fit(X_train, y_train)
Out [29]:
Fitting 3 folds for each of 36 candidates, totalling 108 fits
[CV 1/3; 2/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 2/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 1/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 1/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough
[CV 3/3; 1/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=-0.275 total time=   0.0s
[CV 1/3; 3/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 2/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.509 total time=   0.0s
[CV 2/3; 1/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=-0.250 total time=   0.0s
[CV 3/3; 2/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.545 total time=   0.0s
[CV 2/3; 2/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 3/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 4/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 3/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 4/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=-0.285 total time=   0.0s
[CV 2/3; 4/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 3/3; 4/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 1/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 4/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=-0.250 total time=   0.0s
[CV 1/3; 5/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 2/3; 2/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.544 total time=   0.0s
[CV 2/3; 5/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 4/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=-0.275 total time=   0.0s
[CV 3/3; 5/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 1/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=-0.285 total time=   0.0s
[CV 1/3; 6/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 3/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.516 total time=   0.1s
[CV 2/3; 6/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 5/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.509 total time=   0.0s
[CV 3/3; 5/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.545 total time=   0.0s
[CV 3/3; 6/36] START classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 7/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 5/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.544 total time=   0.0s
[CV 2/3; 7/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 1/3; 7/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.100 total time=   0.0s
[CV 3/3; 7/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 7/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.096 total time=   0.0s
[CV 3/3; 3/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.490 total time=   0.1s
[CV 1/3; 8/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 7/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.098 total time=   0.0s
[CV 2/3; 8/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 8/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 9/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 3/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.536 total time=   0.1s
[CV 2/3; 6/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.536 total time=   0.1s
[CV 2/3; 9/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 3/3; 8/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.691 total time=   0.0s
[CV 2/3; 8/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.692 total time=   0.0s
[CV 1/3; 8/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.691 total time=   0.0s
[CV 3/3; 9/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 10/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 10/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 3/3; 6/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.490 total time=   0.1s
[CV 1/3; 6/36] END classifier__metric=euclidean, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.516 total time=   0.1s
[CV 3/3; 10/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 11/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 10/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.120 total time=   0.0s
[CV 2/3; 11/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 11/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 2/3; 10/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.120 total time=   0.0s
[CV 1/3; 12/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 3/3; 10/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.119 total time=   0.0s
[CV 2/3; 12/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 11/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.695 total time=   0.0s
[CV 3/3; 12/36] START classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 3/3; 11/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.697 total time=   0.1s
[CV 1/3; 13/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 11/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.698 total time=   0.1s
[CV 2/3; 13/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 1/3; 13/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.116 total time=   0.0s
[CV 3/3; 13/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 13/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.114 total time=   0.0s
[CV 1/3; 14/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 13/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.118 total time=   0.0s
[CV 2/3; 14/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 9/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.668 total time=   0.2s
[CV 3/3; 14/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 2/3; 9/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.701 total time=   0.1s
[CV 1/3; 15/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 3/3; 9/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.669 total time=   0.2s
[CV 2/3; 15/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 12/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.705 total time=   0.1s
[CV 1/3; 14/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.694 total time=   0.1s
[CV 3/3; 15/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 16/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 14/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.698 total time=   0.1s
[CV 2/3; 16/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 12/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.673 total time=   0.1s
[CV 3/3; 16/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 16/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.141 total time=   0.0s
[CV 1/3; 17/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 14/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.695 total time=   0.1s
[CV 3/3; 17/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 2/3; 16/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.143 total time=   0.0s
[CV 3/3; 16/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.145 total time=   0.0s
[CV 2/3; 18/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 3/3; 12/36] END classifier__metric=euclidean, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.672 total time=   0.1s
[CV 1/3; 19/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough[CV 3/3; 19/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough

[CV 1/3; 17/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.702 total time=   0.1s
[CV 2/3; 17/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 17/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.703 total time=   0.1s
[CV 1/3; 18/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 17/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.706 total time=   0.1s
[CV 1/3; 15/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.677 total time=   0.2s
[CV 2/3; 15/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.708 total time=   0.2s
[CV 2/3; 20/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 15/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.673 total time=   0.2s
[CV 1/3; 21/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 3/3; 21/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 22/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 18/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.713 total time=   0.2s
[CV 3/3; 18/36] START classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 18/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.683 total time=   0.2s
[CV 1/3; 23/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 18/36] END classifier__metric=euclidean, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.679 total time=   0.2s
[CV 3/3; 23/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 23/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.450 total time=   1.7s
[CV 2/3; 23/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 21/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.458 total time=   1.9s
[CV 1/3; 22/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 20/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.458 total time=   1.9s
[CV 3/3; 20/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 21/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.472 total time=   1.9s
[CV 2/3; 21/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 22/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=0.102 total time=   1.9s
[CV 3/3; 22/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 19/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=0.078 total time=   2.2s
[CV 2/3; 19/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough
[CV 3/3; 19/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=0.135 total time=   2.2s
[CV 1/3; 20/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 23/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.470 total time=   1.8s
[CV 1/3; 24/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 23/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.458 total time=   0.8s
[CV 2/3; 24/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 22/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=0.078 total time=   0.8s
[CV 1/3; 25/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 3/3; 20/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.470 total time=   0.8s
[CV 3/3; 25/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 3/3; 22/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=passthrough;, score=0.135 total time=   0.8s
[CV 2/3; 21/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=StandardScaler();, score=0.509 total time=   0.8s
[CV 2/3; 26/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 27/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 19/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=passthrough;, score=0.102 total time=   0.8s
[CV 3/3; 27/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 20/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.450 total time=   0.9s
[CV 2/3; 28/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 24/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.472 total time=   0.9s
[CV 1/3; 29/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 2/3; 24/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.509 total time=   0.5s
[CV 3/3; 24/36] START classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler()
[CV 3/3; 24/36] END classifier__metric=cosine, classifier__n_neighbors=1, classifier__weights=distance, normalizer=StandardScaler();, score=0.458 total time=   0.6s
[CV 3/3; 29/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 27/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.657 total time=   1.1s
[CV 2/3; 27/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 25/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.382 total time=   1.2s
[CV 2/3; 25/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 26/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.639 total time=   1.2s
[CV 3/3; 26/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 25/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.385 total time=   1.2s
[CV 1/3; 26/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 3/3; 27/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.648 total time=   1.2s
[CV 1/3; 28/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 28/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.390 total time=   1.3s
[CV 3/3; 28/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough
[CV 1/3; 29/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.641 total time=   1.2s
[CV 2/3; 29/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 29/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.649 total time=   1.1s
[CV 1/3; 30/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 27/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=StandardScaler();, score=0.687 total time=   1.3s
[CV 2/3; 30/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 3/3; 26/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.647 total time=   1.2s
[CV 1/3; 31/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 2/3; 25/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=passthrough;, score=0.367 total time=   1.4s
[CV 3/3; 31/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 1/3; 26/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.643 total time=   1.4s
[CV 2/3; 32/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 28/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.393 total time=   1.4s
[CV 1/3; 33/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 2/3; 29/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.642 total time=   1.3s
[CV 2/3; 33/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 3/3; 28/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=passthrough;, score=0.404 total time=   1.4s
[CV 3/3; 33/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler()
[CV 1/3; 30/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.660 total time=   1.4s
[CV 1/3; 34/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 30/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.688 total time=   1.7s
[CV 3/3; 30/36] START classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler()
[CV 1/3; 31/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.378 total time=   1.7s
[CV 2/3; 31/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough
[CV 3/3; 31/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.388 total time=   1.7s
[CV 1/3; 32/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 2/3; 32/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.658 total time=   1.7s
[CV 3/3; 32/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler()
[CV 1/3; 33/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.665 total time=   2.0s
[CV 2/3; 34/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 3/3; 33/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.664 total time=   1.9s
[CV 3/3; 34/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough
[CV 2/3; 33/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=StandardScaler();, score=0.700 total time=   2.1s
[CV 1/3; 35/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 34/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.405 total time=   1.4s
[CV 2/3; 35/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 3/3; 30/36] END classifier__metric=cosine, classifier__n_neighbors=5, classifier__weights=distance, normalizer=StandardScaler();, score=0.650 total time=   1.7s
[CV 3/3; 35/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler()
[CV 1/3; 32/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.660 total time=   1.6s
[CV 3/3; 32/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=MinMaxScaler();, score=0.661 total time=   1.6s
[CV 1/3; 36/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 36/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 31/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=uniform, normalizer=passthrough;, score=0.366 total time=   1.8s
[CV 3/3; 36/36] START classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler()
[CV 2/3; 34/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.403 total time=   1.6s
[CV 3/3; 34/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=passthrough;, score=0.419 total time=   1.7s
[CV 1/3; 35/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.664 total time=   1.7s
[CV 2/3; 35/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.665 total time=   1.7s
[CV 3/3; 35/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=MinMaxScaler();, score=0.667 total time=   0.9s
[CV 1/3; 36/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.673 total time=   0.9s
[CV 3/3; 36/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.669 total time=   0.9s
[CV 2/3; 36/36] END classifier__metric=cosine, classifier__n_neighbors=10, classifier__weights=distance, normalizer=StandardScaler();, score=0.706 total time=   1.0s
GridSearchCV(cv=3,
             estimator=Pipeline(steps=[('normalizer', 'passthrough'),
                                       ('classifier', KNeighborsRegressor())]),
             n_jobs=-1,
             param_grid={'classifier__metric': ['euclidean', 'cosine'],
                         'classifier__n_neighbors': [1, 5, 10],
                         'classifier__weights': ['uniform', 'distance'],
                         'normalizer': ['passthrough', MinMaxScaler(),
                                        StandardScaler()]},
             scoring='r2', verbose=10)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GridSearchCV(cv=3,
             estimator=Pipeline(steps=[('normalizer', 'passthrough'),
                                       ('classifier', KNeighborsRegressor())]),
             n_jobs=-1,
             param_grid={'classifier__metric': ['euclidean', 'cosine'],
                         'classifier__n_neighbors': [1, 5, 10],
                         'classifier__weights': ['uniform', 'distance'],
                         'normalizer': ['passthrough', MinMaxScaler(),
                                        StandardScaler()]},
             scoring='r2', verbose=10)
Pipeline(steps=[('normalizer', MinMaxScaler()),
                ('classifier',
                 KNeighborsRegressor(metric='euclidean', n_neighbors=10,
                                     weights='distance'))])
MinMaxScaler()
KNeighborsRegressor(metric='euclidean', n_neighbors=10, weights='distance')

Посмотрим на результаты кросс-валидации. Посмотреть результаты можно удобно через аттрибут cv_results_:

In [30]:
for elem in zip (grid_search.cv_results_['params'], grid_search.cv_results_['mean_test_score']):
  print(elem)
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(-0.2698362539419594))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.5329368736058075))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.5139255367340351))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(-0.2698362539419594))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.5329368736058075))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.5139255367340351))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(0.0977684191856583))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.6915621333110309))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.6793573692425973))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(0.1199731657678866))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.6964752425342952))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.6834229913464561))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(0.1157913069850766))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.6955574490923349))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.6860471330995802))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(0.14310059566240177))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.7035316821435433))
({'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.691615057187545))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(0.10527040776150857))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.4590323476108448))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.47958515234726945))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(0.1052704077615086))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.4590323476108448))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 1, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.47958515234726945))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(0.37805502479959713))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.6429483717302537))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.6639133943561187))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(0.3955985736693544))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.6439729194747269))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 5, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.666103286084983))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': 'passthrough'}, np.float64(0.3775295488771868))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': MinMaxScaler()}, np.float64(0.6595874224717159))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'uniform', 'normalizer': StandardScaler()}, np.float64(0.6765890035252449))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': 'passthrough'}, np.float64(0.4089433531052468))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}, np.float64(0.6650747099452051))
({'classifier__metric': 'cosine', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': StandardScaler()}, np.float64(0.6829471618912982))

Задание 2.2 (кросс, 1 балл)

Какой наибольший r2\_score удалось достичь на кросс-валидации? Какие закономерности вы видите?

  • Обучите модель с наилучшими параметрами на всей обучающей выборке
  • измерьте r2\_score на тестовой выборке.
In [31]:
from sklearn.metrics import r2_score # изучите самостоятельно, как с помощью этой функции измерять качество

print("Максимальный r2_score ", grid_search.best_score_)
print("Лучшие параметры ", grid_search.best_params_)

best_model = grid_search.best_estimator_
best_model.fit(X_train, y_train)
y_pred = best_model.predict(X_test)

test_r2 = r2_score(y_test, y_pred)

print("r2_score на тестовой выборке ", test_r2)
Максимальный r2_score  0.7035316821435433
Лучшие параметры  {'classifier__metric': 'euclidean', 'classifier__n_neighbors': 10, 'classifier__weights': 'distance', 'normalizer': MinMaxScaler()}
r2_score на тестовой выборке  0.7117082182374788

Ваши выводы тут:

Лучшие параметры модели:

n_neighbors=10

weights=distance

metric=euclidean

Нормализация с использованием MinMaxScaler

Модель показала хорошее качество: R^2 около 0.7 говорит, что 0.7 дисперсии целевой переменной объясняется моделью.

Таким образом:

Увеличение числа соседей до 10 дало лучший баланс.

Использование весов 'distance' улучшает качество по сравнению с равными весами.

Евклидова метрика оказалась эффективнее других

Нормализация данных (MinMaxScaler) важна для метрических методов.

Поздравляем с первой обученной моделью машинного обучения!