В этом посте мы рассмотрим наиболее важные параметры классификатора Sklearn KNeighbors и то, как они влияют на нашу модель с точки зрения переобучения или недостаточного подбора.

Этот классификатор реализует голосование k-ближайших соседей.

Мы будем использовать Titanic Data from kaggle. Ради этого поста мы будем выполнять как можно меньше функций, поскольку это не является целью этого поста.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Загрузить данные поезда

# get titanic & test csv files as a DataFrame
train = pd.read_csv(“input/train.csv”)
print train.shape
> (891, 12)

Проверить отсутствующие значения

#Checking for missing data
NAs = pd.concat([train.isnull().sum()], axis=1, keys=[‘Train’])
NAs[NAs.sum(axis=1) > 0]
> Age 177
Cabin 687
Embarked 2

Мы удалим столбцы «Кабина», «Имя» и «Билет», так как они требуют некоторой обработки для извлечения полезных функций.

# At this point we will drop the Cabin feature since it is missing a lot of the data
train.pop(‘Cabin’)
train.pop(‘Name’)
train.pop(‘Ticket’)
train.shape
> (891, 9)

Заполните отсутствующие значения возраста средним значением

# Filling missing Age values with mean
train[‘Age’] = train[‘Age’].fillna(train[‘Age’].mean())

Заполните отсутствующие значения "Embarked" наиболее частым значением.

# Filling missing Embarked values with most common value
train[‘Embarked’] = train[‘Embarked’].fillna(train[‘Embarked’].mode()[0])

«Pclass» - это категориальная функция, поэтому мы преобразуем его значения в строки.

train[‘Pclass’] = train[‘Pclass’].apply(str)

Давайте выполним простое горячее кодирование категориальных функций.

# Getting Dummies from all other categorical vars
for col in train.dtypes[train.dtypes == ‘object’].index:
   for_dummy = train.pop(col)
   train = pd.concat([train, pd.get_dummies(for_dummy, prefix=col)], axis=1)
labels = train.pop(‘Survived’)

Для тестирования мы решили разделить наши данные на 75% для обучения и 25% для тестирования.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(train, labels, test_size=0.25)

Давайте сначала подберем дерево решений с параметрами по умолчанию, чтобы получить базовое представление о производительности.

from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier()
model.fit(x_train, y_train)
> KNeighborsClassifier(algorithm=’auto’, leaf_size=30, metric=’minkowski’,
 metric_params=None, n_jobs=1, n_neighbors=5, p=2,
 weights=’uniform’)
y_pred = model.predict(x_test)

Мы будем использовать AUC (Площадь под кривой) в качестве метрики оценки. Наше целевое значение является двоичным, поэтому это проблема двоичной классификации. AUC - хороший способ оценки для этого типа проблем.

from sklearn.metrics import roc_curve, auc
false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred)
roc_auc = auc(false_positive_rate, true_positive_rate)
roc_auc
> 0.51680529621456472

n_neighbors

n_neighbors представляет количество соседей, которые будут использоваться для запросов коленей.

neighbors = list(xrange(1,30))
train_results = []
test_results = []
for n in neighbors:
   model = KNeighborsClassifier(n_neighbors=n)
   model.fit(x_train, y_train)
   train_pred = model.predict(x_train)
   false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred)
   roc_auc = auc(false_positive_rate, true_positive_rate)
   train_results.append(roc_auc)
   y_pred = model.predict(x_test)
   false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred)
   roc_auc = auc(false_positive_rate, true_positive_rate)
   test_results.append(roc_auc)
from matplotlib.legend_handler import HandlerLine2D
line1, = plt.plot(neighbors, train_results, ‘b’, label=”Train AUC”)
line2, = plt.plot(neighbors, test_results, ‘r’, label=”Test AUC”)
plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)})
plt.ylabel(‘AUC score’)
plt.xlabel(‘n_neighbors’)
plt.show()

Использование n_neighbors = 1 означает, что каждый образец использует себя в качестве эталона, что является случаем переобучения. По нашим данным, увеличение количества соседей улучшает результаты тестов.

p на расстоянии L_p

Это степенной параметр для метрики Минковского. Когда p = 1, это эквивалентно использованию manhattan_distance (l1) и euliddean_distance (l2) для p = 2. Для произвольного p используется расстояние Минковского (l_p).

distances = [1, 2, 3, 4, 5]
train_results = []
test_results = []
for p in distances:
   model = KNeighborsClassifier(p=p)
   model.fit(x_train, y_train)
   train_pred = model.predict(x_train)
   false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred)
   roc_auc = auc(false_positive_rate, true_positive_rate)
   train_results.append(roc_auc)
   y_pred = model.predict(x_test)
   false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred)
   roc_auc = auc(false_positive_rate, true_positive_rate)
   test_results.append(roc_auc)
from matplotlib.legend_handler import HandlerLine2D
line1, = plt.plot(distances, train_results, ‘b’, label=”Train AUC”)
line2, = plt.plot(distances, test_results, ‘r’, label=”Test AUC”)
plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)})
plt.ylabel(‘AUC score’)
plt.xlabel(‘p’)
plt.show()

В большинстве случаев выбор всегда между l1 и l2, но интересно увидеть результаты более высоких расстояний Минковского. Для наших данных использование l1 кажется лучше, чем l2 и другие расстояния l_p.

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