Скалярное произведение: проекция, угол и косинусная близость

Скалярное произведение превращает два вектора в одно число, которое говорит, насколько они «смотрят в одну сторону».

Скалярное произведение (dot product) векторов a и b — сумма произведений соответствующих координат: a·b = a1·b1 + a2·b2 + ... + an·bn. Результат — число (скаляр).

Формула и реализация

Берём по координате из каждого вектора, перемножаем, складываем всё. Одна строка кода — и при этом одна из самых важных операций во всём ML.

def dot(a, b):
    return sum(x * y for x, y in zip(a, b))

a = [1, 2, 2]
b = [2, 0, 1]
print("a·b =", dot(a, b))        # 1*2 + 2*0 + 2*1 = 4
print("a·a =", dot(a, a))        # = квадрат длины a

Вывод:

a·b = 4
a·a = 9

Геометрический смысл: проекция и угол

Есть вторая, геометрическая формула того же числа: a·b = |a|·|b|·cos(θ), где |a| и |b| — длины векторов, а θ — угол между ними. Отсюда читается смысл:

  • Векторы смотрят в одну сторону (θ ≈ 0°, cos ≈ 1) → произведение большое положительное.
  • Векторы перпендикулярны (θ = 90°, cos = 0) → произведение ноль. Это и есть определение «ортогональности».
  • Векторы смотрят в разные стороны (θ > 90°, cos < 0) → произведение отрицательное.

То есть скалярное произведение измеряет «согласованность направлений». А ещё это проекция: a·b с точностью до длины b показывает, насколько a «вкладывается» в направление b. В нейросети сигнал нейрона — это скалярное произведение входа на веса: «насколько вход похож на то, что нейрон ищет».

Вычисляем угол между векторами

Развернём геометрическую формулу: cos(θ) = (a·b) / (|a|·|b|). Зная косинус, найдём сам угол через math.acos.

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))

a = [1, 2, 2]
b = [2, 0, 1]

cos_theta = dot(a, b) / (norm(a) * norm(b))
angle_rad = math.acos(cos_theta)

print("cos(theta) =", round(cos_theta, 4))
print("Угол в градусах =", round(math.degrees(angle_rad), 2))

Вывод:

cos(theta) = 0.5963
Угол в градусах = 53.4

Косинусная близость — мера похожести в ML

Сам cos(θ) называют косинусной близостью (cosine similarity). Это любимая метрика похожести для текстов, рекомендаций и эмбеддингов: она смотрит только на направление, игнорируя длину. Два текста про одно и то же будут иметь близость около 1, даже если один длиннее другого. Значение лежит в диапазоне от −1 (противоположны) до 1 (сонаправлены).

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))

def cosine_similarity(a, b):
    return dot(a, b) / (norm(a) * norm(b))

x = [1, 1, 0]      # документ 1
y = [2, 2, 0]      # документ 2 — то же направление, но длиннее
z = [0, 0, 1]      # документ 3 — про другое

print("sim(x, y) =", round(cosine_similarity(x, y), 4))   # ~1: похожи
print("sim(x, z) =", round(cosine_similarity(x, z), 4))   # 0: ортогональны

Вывод:

sim(x, y) = 1.0
sim(x, z) = 0.0

Итог

  • Скалярное произведение = сумма произведений координат = одно число.
  • Геометрически a·b = |a|·|b|·cos(θ): измеряет согласованность направлений.
  • Ноль означает перпендикулярность; косинусная близость — стандартная мера похожести в ML.
Проверьте себя
1. Чему равно скалярное произведение [1, 2, 2]·[2, 0, 1]?
A4
B5
C0
D9
2. Что означает скалярное произведение, равное нулю?
AВекторы одинаковые
BВекторы перпендикулярны (ортогональны)
CОдин из векторов нулевой
DВекторы направлены противоположно
3. Почему косинусную близость используют для сравнения текстов?
AОна учитывает только длину векторов
BОна смотрит на направление, игнорируя длину, поэтому длинный и короткий текст про одно дают близость ≈ 1
CОна всегда равна 0 или 1
DЕё быстрее считать, чем скалярное произведение
Поддержать проект