Норма, длина и нормализация вектора

Норма — это длина вектора; нормализация делает из вектора стрелку единичной длины, оставляя только направление.

Норма (евклидова, 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 признаки масштабируют, чтобы крупные значения не «затопили» мелкие.
Проверьте себя
1. Чему равна норма (длина) вектора [3, 4]?
A7
B5
C12
D25
2. Что получается, если вектор поделить на его собственную длину?
AНулевой вектор
BЕдиничный вектор того же направления (длина 1)
CВектор удвоенной длины
DСкаляр
3. Зачем масштабировать признаки перед обучением?
AЧтобы модель обучалась быстрее за счёт меньшего числа признаков
BЧтобы признаки с крупными значениями не доминировали над мелкими в расстояниях и градиентах
CЧтобы все признаки стали целыми числами
DЭто требование синтаксиса Python
Поддержать проект