Норма, длина и нормализация вектора
Норма — это длина вектора; нормализация делает из вектора стрелку единичной длины, оставляя только направление.
Норма (евклидова, L2) вектора — его длина:
|a| = sqrt(a1² + a2² + ... + an²). Это та же теорема Пифагора, но в любом числе измерений.
Длина = корень из суммы квадратов
На плоскости длина стрелки (3, 4) по Пифагору равна sqrt(3² + 4²) = 5. Формула обобщается на любую размерность: возводим каждую координату в квадрат, складываем, берём корень. Заметьте: это в точности sqrt(a·a) — норма выражается через скалярное произведение вектора с самим собой.
import math
def dot(a, b): return sum(x * y for x, y in zip(a, b))
def norm(a): return math.sqrt(dot(a, a))
print("|[3, 4]| =", norm([3, 4]))
print("|[1, 2, 2]| =", norm([1, 2, 2]))
print("|[0, 0, 0]| =", norm([0, 0, 0]))
Вывод:
|[3, 4]| = 5.0 |[1, 2, 2]| = 3.0 |[0, 0, 0]| = 0.0
Евклидово расстояние между точками
Расстояние между двумя точками — это длина вектора их разности: dist(a, b) = |a − b|. Так работает алгоритм k ближайших соседей (kNN): чтобы найти похожие объекты, он считает евклидово расстояние до всех остальных и берёт ближайшие.
import math
def distance(a, b):
return math.sqrt(sum((x - y) ** 2 for x, y in zip(a, b)))
p1 = [0, 0]
p2 = [3, 4]
p3 = [1, 1]
print("dist(p1, p2) =", distance(p1, p2))
print("dist(p1, p3) =", round(distance(p1, p3), 4))
print("Ближе к p1:", "p3" if distance(p1, p3) < distance(p1, p2) else "p2")
Вывод:
dist(p1, p2) = 5.0 dist(p1, p3) = 1.4142 Ближе к p1: p3
Нормализация: оставить только направление
Нормализовать вектор — поделить его на собственную длину. Получается единичный вектор (длина ровно 1), указывающий в ту же сторону. Так мы выбрасываем информацию о масштабе и сохраняем чистое направление. Именно нормализованные векторы дают «честную» косинусную близость: sim = a_unit · b_unit.
import math
def norm(a): return math.sqrt(sum(x * x for x in a))
def normalize(a):
n = norm(a)
return [x / n for x in a]
v = [3, 4]
u = normalize(v)
print("Единичный вектор:", [round(x, 4) for x in u])
print("Его длина:", round(norm(u), 4)) # должна быть 1
Вывод:
Единичный вектор: [0.6, 0.8] Его длина: 1.0
Зачем нормализуют признаки в ML
Это разные «нормализации», но связанные одной идеей — привести к сопоставимому масштабу. Если один признак — это зарплата (десятки тысяч), а другой — возраст (десятки), то расстояния и градиенты будут полностью определяться зарплатой, а возраст «потеряется». Поэтому признаки масштабируют: делят на разброс или приводят к диапазону. Без этого kNN, k-means и градиентный спуск работают плохо. Запомните принцип: числа разного масштаба нужно привести к общему, иначе крупные затопят мелкие.
# Мин-макс масштабирование признака в диапазон [0, 1]
salary = [30000, 50000, 80000, 120000]
lo, hi = min(salary), max(salary)
scaled = [(x - lo) / (hi - lo) for x in salary]
print("Исходное:", salary)
print("В [0,1] :", [round(x, 3) for x in scaled])
Вывод:
Исходное: [30000, 50000, 80000, 120000] В [0,1] : [0.0, 0.222, 0.556, 1.0]
Итог
- Норма (длина) — корень из суммы квадратов координат, обобщение Пифагора.
- Евклидово расстояние — длина вектора разности; на нём держится kNN.
- Нормализация делит вектор на его длину → единичный вектор (только направление).
- В ML признаки масштабируют, чтобы крупные значения не «затопили» мелкие.