K-Nearest Neighbor, шаг за шагом с scikit-learn

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

Вступление

K-Nearest Neighbor (KNN) - это управляемая модель машинного обучения ᵀᴹ для задач двоичной классификации. KNN предсказывает класс (классы) значения на основе ближайшей близости к определенному K количеству соседей.

Контролируемое обучение?

Прогнозирование выходных данных с использованием «новых данных» с использованием моделей классификации или регрессии, которые были обучены с «известными» входными данными.

KNN в основном состоит из двух параметров: нахождения подходящего количества соседей (K) и того, как расстояние между точками измеряется. Чаще всего KNN измеряется евклидовым расстоянием, расстоянием по прямой между двумя точками:

Скажем, p относится к известному атрибуту в нашем наборе данных, а q - это неизвестный атрибут, который мы пытаемся классифицировать. Если бы мы изобразили p и q на диаграмме рассеяния, KNN определил бы расстояние между двумя точками как квадратный корень из суммы квадратов разности (поскольку у нас не может быть отрицательного расстояния) всех атрибутов ( n) для каждого поля атрибута (i). Я знаю, это звучит глупо, но все, что мы делаем, это вычисляем прямолинейное расстояние от неклассифицированного атрибута до ближайшего числа соседей, известных атрибутов, как определено K:

Неизвестный атрибут адаптирует класс большинства соседей в пределах периметра K (так, что в приведенном выше примере неизвестный атрибут классифицируется как B, где K = 3). Модели KNN по умолчанию имеют одинаковый вес для полей каждого атрибута; вы можете параметризовать модель, назначив веса для конкретных атрибутов, включая расстояние (если, скажем, близость между точками в значительной степени определяет, каким может быть ее класс).

Но что, если голоса разделились? Для функции KNN scikit-learn по умолчанию атрибут будет назначен первому классу в последовательности. Таким образом, если у нас есть «Класс 0» и «Класс 1», и у нас есть равное количество голосов (скажем, K = 4, и голосование делится поровну), тогда атрибуту будет присвоен «Класс 0».

Уловка с KNN заключается в определении соответствующего количества соседей - K - так, чтобы точность модели не соответствовала ни недостаточной («сложная» модель с недостаточным количеством соседей), ни переоснащению («простая» модель со слишком большим количеством соседей) кривой обобщения.

Кривая обобщения?

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

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

Шаг 1. Начало работы

Scikit-learn, также известный как sklearn, представляет собой библиотеку машинного обучения с открытым исходным кодом, включающую различные модели регрессии и классификации. Я рекомендую проверить их обширную документацию и руководства для дальнейшего понимания (инструкции по установке sklearn здесь).

Откройте предпочитаемую вами среду программирования Python и импортируйте необходимые пакеты:

# Generally useful packages to have on deck:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# If you want to practice with sklearn's prepackaged datasets:
from sklearn.datasets import load_wine
# The sklearn packages we'll need:
from sklearn import metrics
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

Для практики в sklearn.datasets есть все, что вам нужно для начала работы. Однако для этого конкретного примера я взял некоторые данные из Kaggle. Давайте посмотрим на набор данных аренды по разным городам Бразилии:

df=pd.DataFrame(pd.read_csv('data_practice/houses_to_rent_v2.csv'))
display(df.head())
df.columns

Шаг 2. Убедитесь, что ваши данные подходят для модели!

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

a) Поля для «животное» и «мебель» похожи на логические, что означает, что они просто указывают, принимает ли дом домашних животных или он меблирован (Верно / Неверно). Таким образом, мы можем преобразовать «Нет» в «0», а «Да» - в «1» (или, наоборот, ваш призыв).

б) Ну а города? Здесь мы думаем о том, чего мы хотим, чтобы наша модель KNN делала. Есть разные способы взглянуть на нашу модель классификации: например, мы могли бы просто попытаться предсказать, находится ли арендуемая квартира в Сан-Паулу (пометив город как «1», а остальные - как «0»). Сан-Паулу - это крупнейший мегаполис Бразилии и Южной Америки, поэтому мы можем сделать некоторые разумные предположения о стоимости жизни, что отражается в более высоких ценах на аренду и меньших квадратных футах. Но для целей этого руководства давайте просто поэкспериментируем с моделью, чтобы посмотреть, насколько хорошо она справляется с прогнозированием классификационных меток для каждого города в наборе данных.

# converts each unique string values to a unique integer
df.city = pd.factorize(df['city'])[0] 
# Convert your "boolean" string values into 1s and 0s.
df.animal = pd.Series(np.where(df.animal == ‘acept’, 1, 0))
df.furniture = pd.Series(np.where(df.furniture == ‘furnished’, 1, 0))

Также убедитесь, что все ваши поля числовые. Возможно, потребуется внести небольшие корректировки, особенно с наборами данных, в создании которых вы не участвовали.

Шаг 3. Масштабируйте набор данных

Масштабирование, также известное как стандартизация, - это процесс, с помощью которого данные нормализуются и заменяются Z-оценкой:

Для объяснения того, почему данные нуждаются в стандартизации, прочтите: Почему требуется масштабирование в KNN и K-Means?

from sklearn.preprocessing import StandardScaler
# StandardScaler() transform the data such that its distribution 
# will have a mean value 0 and standard deviation of 1.
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

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

Шаг 4. Разделите набор данных на обучающие и тестовые наборы.

Во-первых, нам нужно отделить поля атрибутов от меток. Это означает, что мы хотим отличать переменные-предикторы (X) от меток, которые мы хотим классифицировать (y), для которых в данном случае используется поле атрибута «город».

# Excluding ‘city’ and 'total(R$)', the latter because I think it's # redundant to have a field that's just the sum of 4 other fields
X = df.iloc[:, 1:-1].values
 
# Including ‘city’ only
y = df.iloc[:, 0].values

Теперь мы разделим набор данных на обучающий и тестовый набор: первый - это часть набора данных, которая вводится в модель для прогнозирования класса, а вторая будет использоваться для проверки точности модели. Отсюда то, что делает KNN «контролируемой» классификационной моделью.

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0,test_size=0.20)
print(X_train.shape, X_test.shape)
(8553, 11) (2139, 11)

Некоторые подробности о том, как работает функция train_test_split ():

random_state указывает метод случайной выборки. По умолчанию, если не указано иное, функция будет использовать экземпляр из numpy.random и создавать образец, который будет другим, если вы запустите его несколько раз. Однако в этом случае я присвоил случайному начальному значению значение «0». Данные по-прежнему случайны, но метод, с помощью которого производится выборка, можно воспроизвести. Т.е. Если бы вы повторили в точности то, что я делал до сих пор, с тем же набором данных, вы бы получили точно такую ​​же «случайную выборку», если бы вы присвоили случайному начальному значению значение «0».

test_size - это просто желаемая доля вашего тестового набора по отношению ко всему набору данных. Таким образом, при 0,20 я указываю, что мой тестовый набор равен 20% от всего набора данных (из 10 692 строк). По умолчанию размер теста составляет 25%. Какой размер считается подходящим для наборов тестирования, зависит от статистической строгости вашей модели; обратитесь к этой теме StackExchange для более глубокого погружения.

Шаг 5: запустите и интерпретируйте модель

Используя функцию KNeighborsClassifier (), мы определяем параметр K модели (просто попробуйте начать с случайного значения). Мы обучаем модель KNN на обучающей выборке и применяем ее к тестовой выборке с помощью метода .predict ().

from sklearn.neighbors import KNeighborsClassifier
# Set K = 6 (an arbitrary value) to observe the output
knn = KNeighborsClassifier(n_neighbors=6)
# Train the algorithm
model=knn.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(metrics.accuracy_score(y_test, y_pred))
0.6610565684899485

Точность модели при K = 6 составляет примерно 66%. Это хорошо? Что ж, это зависит от вашего набора данных, исследовательского запроса, того, что говорится в соответствующей справочной литературе в вашей области знаний и т. Д.

Но на самом деле мы можем глубже взглянуть на показатель точности, используя функцию sklearn classification_report ().

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))

Точность и отзывчивость - это компромиссы друг друга.

Точность: сколько было правильно отнесено к этому классу (источник);

Класс «0» (обозначающий Сан-Паулу) имеет наивысшую точность, где все экземпляры классифицированных положительных результатов были правильно идентифицированы в 67% случаев.

Напомним: процент правильно классифицированных общих релевантных результатов (источник);

Для всех экземпляров, которые были классифицированы как положительные (т. Е. Экземпляров, которые правильно принадлежали к соответствующему классу «город»), класс «0» имеет лучший результат 98%, что означает, что модель была эффективной для определения соответствующих элементов для этого класса.

f1-score: среднее гармоническое значение между точностью и отзывом »( источник );

Показатель f1 полезен для сравнения с общим показателем точности.

Поддержка: количество вхождений данного класса в набор данных (источник);

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

Что-то еще, что мы могли бы знать, это оптимальное значение K, чтобы обеспечить наилучшую возможную точность прогноза. Мы можем попробовать запустить модель несколько раз, перебирая диапазон для K:

# Choose how many neighbors to test
k_range = range(1,300)
# Create a list to store scores
scores=[]
error = []
# Run the KNN
for k in k_range:
    knn = KNeighborsClassifier(n_neighbors=k)
    knn.fit(X_train, y_train)
    y_pred = knn.predict(X_test)
   # Append the accuracy score
    scores.append(metrics.accuracy_score(y_test, y_pred))
   # Append the error rate 
    error.append(np.mean(y_pred != y_test))
# Print the scores
print(scores)

И наоборот, мы можем попытаться определить K на основе наименьшего коэффициента ошибок:

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

Иногда самое важное, что можно сделать с помощью модели, - это ограничения и уместность самого набора данных.