Пусть задана обучающая выборка и поступил новый объект, для которого нужно предсказать класс (в случае задачи классификации) или вещественную метку (задача регрессии). Для этого можно посмотреть какие объекты лежат рядом с ним и сделать выводы, опираясь на них:
Этот метод так и называется — метод k ближайших соседей:
k выбирается с помощью кросс-валидации. Кроме того можно варьировать:
В библиотеке sklearn метод ближайших соседей реализован в классе KNeighborsClassifier для классификации и KNeighborsRegression
Посмотрим как будет меняться граница между классами в зависимости от k:
import numpy as np
import pylab as plt
%matplotlib inline
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from matplotlib.colors import ListedColormap
Загрузим данные:
data = [datasets.make_moons(noise=0.3, random_state=0),
datasets.make_circles(noise=0.2, factor=0.5, random_state=1),
datasets.make_classification(n_features=2, n_redundant=0, n_informative=2,
random_state=1, n_clusters_per_class=1)
]
def get_grid(X):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
np.arange(y_min, y_max, 0.1))
return xx, yy
plt.figure(figsize=(14, 10))
for j, ds in enumerate(data, 1):
X, y = ds
xx, yy = get_grid(X)
for i, k in enumerate([1, 3, 15]):
clf = KNeighborsClassifier(n_neighbors=k)
clf.fit(X, y)
Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
ax = plt.subplot(3, 3, i * 3 + j)
ax.contourf(xx, yy, Z, cmap=plt.cm.RdBu, alpha=0.6)
ax.scatter(X[:, 0], X[:, 1], c=y, )
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_xticks(())
ax.set_yticks(())
ax.set_title("k = %i" % k)
plt.show()
При увеличении числа соседей появляются зоны "неуверенности": примерно одинаковое число соседей попадает в окрестность объекта.
Теперь посмотрим поменяется ли разделяющая поверхность в зависимости от метрики:
plt.figure(figsize=(14, 10))
for j, ds in enumerate(data, 1):
X, y = ds
xx, yy = get_grid(X)
for i, p in enumerate([1, 2]):
clf = KNeighborsClassifier(n_neighbors=15, p=p)
clf.fit(X, y)
Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
ax = plt.subplot(3, 3, i * 3 + j)
ax.contourf(xx, yy, Z, cmap=plt.cm.RdBu, alpha=0.6)
ax.scatter(X[:, 0], X[:, 1], c=y, )
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_xticks(())
ax.set_yticks(())
ax.set_title("p = %i" % p)
plt.show()
И как повляет весовая схема:
plt.figure(figsize=(14, 10))
for j, ds in enumerate(data, 1):
X, y = ds
xx, yy = get_grid(X)
for i, weights in enumerate(['uniform', 'distance']):
clf = KNeighborsClassifier(n_neighbors=15, weights=weights)
clf.fit(X, y)
Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
ax = plt.subplot(3, 3, i * 3 + j)
ax.contourf(xx, yy, Z, cmap=plt.cm.RdBu, alpha=0.6)
ax.scatter(X[:, 0], X[:, 1], c=y, )
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_xticks(())
ax.set_yticks(())
ax.set_title("weights = %s" % weights)
plt.show()