kNN на пальцах

Реализуем kNN с нуля на чистом Python — и поймём его насквозь.

kNN настолько прост, что его легко написать самому без библиотек. Сделаем это пошагово — код запускается, можно экспериментировать.

Шаг 1. Данные с метками

У нас есть точки на плоскости, каждая принадлежит классу A или B. Это обучающая выборка.

Шаг 2. Расстояние

«Ближайший» — значит с наименьшим расстоянием. Берём обычное евклидово расстояние: √((x1−x2)² + (y1−y2)²) — теорема Пифагора, длина отрезка между точками.

Шаг 3. Соседи и голосование

Считаем расстояния от новой точки до всех, сортируем, берём k ближайших и смотрим, какой класс среди них чаще.

# Обучающие точки: (x, y, класс)
points = [
    (1.0, 1.0, "A"),
    (1.5, 2.0, "A"),
    (1.2, 0.8, "A"),
    (5.0, 5.0, "B"),
    (6.0, 5.5, "B"),
]

# Новая точка, класс которой надо предсказать
new = (5.5, 5.0)

def dist(p, q):
    return ((p[0]-q[0])**2 + (p[1]-q[1])**2) ** 0.5

# Расстояние от новой точки до каждой обучающей
scored = []
for x, y, label in points:
    d = dist((x, y), new)
    scored.append((d, label))

scored.sort()             # ближайшие — первыми
k = 3
nearest = scored[:k]

print(f"{k} ближайших соседа:")
for d, label in nearest:
    print(f"  расстояние={round(d, 2)}, класс={label}")

# Голосование: какой класс встречается чаще
votes = {}
for d, label in nearest:
    votes[label] = votes.get(label, 0) + 1

winner = max(votes, key=votes.get)
print("Голоса:", votes)
print("Предсказанный класс:", winner)

Вывод:

3 ближайших соседа:
  расстояние=0.5, класс=B
  расстояние=0.71, класс=B
  расстояние=5.0, класс=A
Голоса: {'B': 2, 'A': 1}
Предсказанный класс: B

Новая точка (5.5, 5.0) попала в скопление класса B — туда её и отнесли двумя голосами против одного.

Почему важен выбор k

Число k — это сколько соседей учитывать.

  • Маленькое k (например, 1) — модель чувствительна к шуму: один случайный сосед может всё решить.
  • Большое k — модель усредняет, граница сглаживается, но детали теряются.
  • Чётное k в бинарной задаче может дать ничью голосов, поэтому часто берут нечётное.

Хорошее k подбирают экспериментом — проверяя качество на отдельных данных.

Зачем нормализовать признаки

kNN считает расстояния, поэтому признаки с большим размахом давят на результат. Если один признак — возраст (0–100), а другой — зарплата (0–200000), то зарплата перевесит всё расстояние, и возраст почти не учтётся. Решение — привести признаки к одному масштабу (нормализация). Этой подготовке данных посвящён отдельный урок в разделе про данные.

Сильные и слабые стороны kNN

Обратите внимание на любопытную деталь: у kNN нет «обучения» в привычном смысле. Он не подбирает веса и не строит формулу заранее — просто хранит все обучающие точки и считает расстояния в момент предсказания. Такие методы называют «ленивыми»: вся работа откладывается до запроса.

Отсюда плюсы и минусы. Плюс — простота и отсутствие долгого обучения. Минус — на больших данных каждое предсказание медленное, ведь нужно сравниться со всеми точками, и приходится держать в памяти весь датасет. Поэтому kNN хорош для небольших и средних задач с понятной геометрией, но редко применяется там, где объектов миллионы.

Итог

  • kNN: посчитать расстояния до всех точек, взять k ближайших, проголосовать за класс.
  • Расстояние — обычно евклидово (теорема Пифагора).
  • Малое k чувствительно к шуму, большое — сглаживает; нечётное k избегает ничьих.
  • Признаки разного масштаба надо нормализовать, иначе расстояние перекосит.
Проверьте себя
1. Какие шаги выполняет kNN, чтобы классифицировать новую точку?
AОбучает прямую границу методом градиентного спуска
BСчитает расстояния до всех точек, берёт k ближайших и голосует за преобладающий класс
CСтроит дерево вопросов да/нет
DУсредняет все метки в датасете
2. Чем грозит слишком маленькое k (например, k = 1)?
AМодель станет слишком медленной
BМодель станет чувствительной к шуму: один случайный сосед решает исход
CРасстояния перестанут считаться
Dk не влияет на результат
3. Почему перед применением kNN признаки часто нормализуют?
AЧтобы уменьшить размер датасета
BПотому что kNN считает расстояния, и признак с большим размахом иначе перевесит остальные
CНормализация ускоряет сортировку
DЭто требование любого алгоритма
Поддержать проект