Евклидово расстояние в кластеризации: формула, k-means и нормализация

Евклидово расстояние в кластеризации: формула, k-means и нормализация Полезное

Евклидово расстояние — это длина прямого отрезка между двумя точками в пространстве, вычисленная по теореме Пифагора. В кластеризации оно служит мерой различия объектов: чем меньше расстояние, тем объекты ближе и вероятнее попадут в один кластер. Важно сразу развести три вещи, которые часто путают: само расстояние (мера различия), сходство (обратная величина, растет при сближении) и квадрат расстояния (та же метрика без извлечения корня, ее использует k-means). Ниже — формула для 2D и n-мерного пространства, роль в k-means, сравнение с манхэттенским и косинусным расстоянием и разбор того, почему без нормализации признаков метрика врет.

Формула: 2D и n-мерное пространство

На плоскости расстояние между точками a = (x1, y1) и b = (x2, y2) равно корню из суммы квадратов разностей координат: d = sqrt((x1 — x2)^2 + (y1 — y2)^2). Это прямое следствие теоремы Пифагора — разности координат образуют катеты, расстояние — гипотенузу.

В n-мерном пространстве формула обобщается на любое число признаков: d = sqrt(sum((p_i — q_i)^2)) по всем координатам i от 1 до n. Каждый признак объекта (возраст, доход, число покупок) — это одна координата. Геометрическая интуиция та же, просто измерений больше двух, и нарисовать их нельзя.

Считать вручную не нужно — в Python есть numpy.linalg.norm и scipy.spatial.distance.euclidean, оба дают тот же результат:

import numpy as np
from scipy.spatial import distance

a = np.array([1.0, 2.0])
b = np.array([4.0, 6.0])

# вручную по формуле: sqrt(3^2 + 4^2) = 5
d = ((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) ** 0.5
print(d)                          # 5.0
print(np.linalg.norm(a - b))      # 5.0
print(distance.euclidean(a, b))   # 5.0

# n-мерное пространство: 4 координаты
p = np.array([1.0, 2.0, 3.0, 4.0])
q = np.array([2.0, 0.0, 5.0, 4.0])
print(np.linalg.norm(p - q))      # 3.0  = sqrt(1 + 4 + 4 + 0)

Роль в k-means

k-means — самый распространенный алгоритм разбиения на кластеры — завязан именно на евклидову метрику, а точнее на ее квадрат. Алгоритм ищет такие центры кластеров (центроиды), при которых минимальна сумма квадратов евклидовых расстояний от каждой точки до центра ее кластера. Эту сумму в scikit-learn называют инерцией (inertia).

Работает он итеративно: назначает каждую точку ближайшему по евклидову расстоянию центроиду, затем пересчитывает центроид как среднее точек кластера, и повторяет до стабилизации. Именно среднее (а не медиана) минимизирует сумму квадратов евклидовых расстояний — поэтому связка «квадрат евклида + среднее» внутренне согласована.

Это же и граница применимости: k-means корректно работает с евклидовой метрикой и не рассчитан на произвольную. Для манхэттенского расстояния используют k-medoids (PAM), где центр кластера — реальный объект, а не среднее. Пример на нормализованных данных (о нормализации — ниже):

import numpy as np
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

rng = np.random.default_rng(42)
c1 = rng.normal([20, 30000], [2, 3000], size=(20, 2))  # моложе, доход ниже
c2 = rng.normal([40, 90000], [2, 3000], size=(20, 2))  # старше, доход выше
data = np.vstack([c1, c2])

data_z = StandardScaler().fit_transform(data)   # нормализуем ДО кластеризации
km = KMeans(n_clusters=2, n_init=10, random_state=42).fit(data_z)

print(np.bincount(km.labels_))   # [20 20] - две группы по 20 точек
print(round(km.inertia_, 4))     # 1.2633 - сумма квадратов расстояний до центров

Евклидово, манхэттенское, косинусное: когда какое

Евклидово — не единственная метрика. Выбор зависит от природы данных, а не от привычки. Манхэттенское (city block) — сумма модулей разностей координат; оно не возводит разности в квадрат, поэтому слабее реагирует на выбросы по отдельной координате и часто устойчивее в пространствах высокой размерности. Косинусное — это 1 минус косинус угла между векторами; оно измеряет различие в направлении и игнорирует длину векторов, поэтому его берут для текстов (мешок слов, TF-IDF, эмбеддинги), где важна тема, а не общий объем.

Метрика Что измеряет Чувствительность к масштабу длины Когда брать
Евклидово Прямое геометрическое расстояние Высокая, реагирует на выбросы Плотные числовые признаки соизмеримого масштаба; k-means
Манхэттенское Сумма модулей разностей Умеренная, устойчивее к выбросам Много признаков, разреженные или зашумленные данные; k-medoids
Косинусное Угол (направление) между векторами Игнорирует длину вектора Тексты, эмбеддинги, высокая размерность, важна тема

Разницу видно на простом примере. Векторы u = (2, 0) и w = (4, 0) смотрят в одну сторону, но разной длины — евклидово расстояние это заметит, косинусное обнулится:

from scipy.spatial import distance
import numpy as np

u = np.array([2.0, 0.0])
v = np.array([0.0, 2.0])
w = np.array([4.0, 0.0])

# u и v: разные направления, одинаковая длина
print(distance.euclidean(u, v))  # 2.8284
print(distance.cityblock(u, v))  # 4.0
print(distance.cosine(u, v))     # 1.0   (угол 90 градусов)

# u и w: одно направление, разная длина
print(distance.euclidean(u, w))  # 2.0
print(distance.cosine(u, w))     # 0.0   (угол 0 градусов)

Отдельно стоит квадрат евклидова расстояния — та же метрика без корня. Он не меняет порядок близости (ближайшая точка остается ближайшей), но экономит вычисление корня и сильнее разводит далекие пары; k-means оперирует именно им.

Влияние масштаба признаков и нормализация

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

Возьмем трех человек: разница по доходу дает расстояние в тысячи, а 20 лет разницы в возрасте почти не видны:

import numpy as np
from scipy.spatial import distance

# признаки: возраст (лет) и доход (руб/мес)
X = np.array([
    [25, 40000],
    [26, 90000],
    [45, 41000],
], dtype=float)

print(distance.euclidean(X[0], X[1]))  # 50000.00001  (разница только в доходе)
print(distance.euclidean(X[0], X[2]))  # 1000.2       (разница 20 лет почти не видна)

По сырым данным объект 0 «ближе» к объекту 2, хотя между ними 20 лет разницы — потому что доход задавил возраст. Лечится это нормализацией: приводим каждый признак к сопоставимому масштабу до вычисления расстояния. Частый способ — z-нормализация (стандартизация): из каждого значения вычитаем среднее по столбцу и делим на стандартное отклонение. После этого признаки становятся соизмеримыми, и вклад дает уже возраст:

import numpy as np
from scipy.spatial import distance

X = np.array([
    [25, 40000],
    [26, 90000],
    [45, 41000],
], dtype=float)

# z-нормализация по столбцам: (x - mean) / std
Xz = (X - X.mean(axis=0)) / X.std(axis=0)

print(distance.euclidean(Xz[0], Xz[1]))  # 2.1452
print(distance.euclidean(Xz[0], Xz[2]))  # 2.1740

После нормализации расстояния стали одного порядка, и объект 0 оказался чуть дальше от объекта 2 (большой разрыв в возрасте) — картина перевернулась и стала осмысленной. Альтернатива стандартизации — min-max масштабирование в диапазон [0, 1]; выбор зависит от наличия выбросов (при сильных выбросах z-нормализация надежнее). Важная оговорка: нормализацию считают на обучающей выборке и теми же параметрами применяют к новым данным, иначе метрика поедет.

Выводы

  • Евклидово расстояние — длина прямого отрезка между точками; в кластеризации это мера различия объектов, формула обобщается с 2D на любое число признаков через сумму квадратов разностей координат.
  • k-means минимизирует сумму квадратов евклидовых расстояний до центроидов и завязан именно на эту метрику; для манхэттенской берут k-medoids.
  • Манхэттенское устойчивее к выбросам, косинусное измеряет направление и игнорирует длину вектора — метрику выбирают под природу данных, а не по умолчанию.
  • Без нормализации признак с большим диапазоном подавляет остальные; z-нормализация или min-max приводят признаки к сопоставимому масштабу до расчета расстояний.
  • Считать метрики удобно через numpy и scipy, а параметры нормализации фиксировать на обучении и применять к новым данным теми же.

Где применяется / связь с практикой

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

Освойте тему на практике

Системно эти темы (k-means, метрики близости, подготовка признаков, оценка качества кластеров) собраны на курсе Machine Learning. Basic. Посмотреть формат и уровень до старта можно на открытых уроках — там разбирают реальные задачи с кодом.

Смежные темы: Введение в Data Science: первые шаги в анализе данных, Статистическая значимость в исследованиях и анализе.

FAQ

Чем евклидово расстояние отличается от его квадрата и что использует k-means?
Квадрат — это та же сумма квадратов разностей, но без извлечения корня. Порядок близости он не меняет, зато экономит вычисление корня, поэтому k-means минимизирует именно сумму квадратов евклидовых расстояний до центроидов.

Всегда ли нужно нормализовать признаки перед евклидовым расстоянием?
Почти всегда, если признаки в разных единицах и диапазонах, иначе крупномасштабный признак подавит остальные. Исключение — когда все признаки уже в одной шкале (например, пиксели изображения 0-255) или масштаб намеренно несет смысл.

Работает ли евклидово расстояние в пространствах высокой размерности?
С ростом числа признаков расстояния между всеми точками сближаются и хуже различают объекты — это «проклятие размерности». В таких случаях помогает снижение размерности (например, PCA) либо переход к манхэттенской или косинусной метрике.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)