🔴 Сложный ⏱️ 60 минут

Скалярное произведение: от математики до нейронных сетей

Скалярное произведение: от математики до нейронных сетей 🎯

Представь: ты работаешь над поисковой системой. Пользователь вводит запрос “машинное обучение python”, и тебе нужно найти самые релевантные статьи из миллионов документов. Как определить, насколько два текста похожи? Или ты строишь рекомендательную систему — как понять, что два пользователя имеют схожие вкусы?

Ответ на все эти вопросы лежит в одной из самых фундаментальных операций линейной алгебры — скалярном произведении векторов. Это та самая математическая операция, которая стоит за: - 🔍 Semantic search в ChatGPT и других LLM - 🎬 Рекомендательными системами Netflix и Spotify
- 🤖 Attention механизмами в трансформерах (GPT, BERT) - 📊 Cosine similarity между embeddings - 🎯 Классификацией в нейронных сетях

В этом уроке мы разберём скалярное произведение с нуля до профессионального применения. Ты поймёшь не только “как считать”, но и “зачем это нужно” и “где реально используется”.

🎯 Ты узнаешь: - Что такое скалярное произведение на интуитивном уровне - Как его вычислять (4 разных способа) - Почему оно измеряет “похожесть” векторов - Как оно работает в embeddings моделях (Word2Vec, BERT, GPT) - Зачем оно нужно в attention механизмах - Как применять его в реальных ML-задачах

Читать примерно 60 минут | Уровень: с нуля до применения в продакшене


Блок 1: Что такое скалярное произведение простыми словами

Интуиция: “Насколько вектора смотрят в одну сторону”

Представь двух людей, которые идут по городу. У каждого своё направление движения — это вектор. Скалярное произведение отвечает на вопрос: “Насколько их маршруты совпадают?”

Если оба идут в одну сторону (параллельные векторы) → скалярное произведение большое положительное
Если идут перпендикулярно (один на север, другой на запад) → скалярное произведение = 0
Если идут в противоположные стороны (встречно) → скалярное произведение отрицательное

Эта же идея работает в ML: - Два embedding’а похожих слов (“король” и “королева”) имеют большое положительное скалярное произведение - Embedding’и несвязанных слов (“стол” и “космос”) дают значение близкое к нулю - Embedding’и противоположных понятий (“горячий” и “холодный”) дают отрицательное значение

Строгое математическое определение

Определение: Скалярное произведение (dot product) двух векторов — это сумма произведений соответствующих координат.

Для двух векторов в n-мерном пространстве:

$$ \vec{a} = (a_1, a_2, …, a_n), \quad \vec{b} = (b_1, b_2, …, b_n) $$

Скалярное произведение вычисляется как:

$$ \vec{a} \cdot \vec{b} = a_1 b_1 + a_2 b_2 + … + a_n bn = \sum{i=1}^{n} a_i b_i $$

Обозначения: - $\vec{a} \cdot \vec{b}$ — скалярное произведение (dot product) - $(\vec{a}, \vec{b})$ — альтернативное обозначение - $\langle \vec{a}, \vec{b} \rangle$ — обозначение в функциональном анализе - Результат — число (скаляр), не вектор!

Почему называется “скалярное”?

Потому что результат — это скаляр (одно число), а не вектор. Ты берёшь два вектора и получаешь число. Это как “сжатие” информации о двух направлениях в одно значение.

В Python это одна строка:

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

dot_product = np.dot(a, b)  # или a @ b
print(dot_product)  # 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32

Блок 2: Геометрический смысл — почему это “похожесть”

Формула через угол между векторами

Есть ещё одна формула скалярного произведения (через угол между векторами):

$$ \vec{a} \cdot \vec{b} = |\vec{a}| \cdot |\vec{b}| \cdot \cos(\theta) $$

Где: - $|\vec{a}|$ — длина (норма) вектора $\vec{a}$ - $|\vec{b}|$ — длина вектора $\vec{b}$ - $\theta$ — угол между векторами - $\cos(\theta)$ — косинус угла

Что это значит? - Если $\theta = 0°$ (векторы совпадают) → $\cos(0°) = 1$ → произведение максимально - Если $\theta = 90°$ (перпендикулярны) → $\cos(90°) = 0$ → произведение = 0 - Если $\theta = 180°$ (противоположны) → $\cos(180°) = -1$ → произведение минимально

Почему это важно для ML?

В машинном обучении мы постоянно работаем с векторными представлениями (embeddings). Угол между векторами показывает их семантическую близость.

Пример: Word Embeddings

Возьмём Word2Vec embeddings (каждое слово → вектор из 300 чисел):

# Упрощённый пример (реальные векторы имеют 300 измерений)
king = np.array([0.5, 0.3, 0.8, ..., 0.2])     # 300 чисел
queen = np.array([0.48, 0.29, 0.75, ..., 0.19]) # похожий вектор
table = np.array([0.1, -0.5, 0.2, ..., -0.3])   # совсем другой

# Считаем скалярное произведение
similarity_king_queen = np.dot(king, queen)  # большое число
similarity_king_table = np.dot(king, table)  # маленькое число

Слова “king” и “queen” имеют большое скалярное произведение, потому что их embedding’и “смотрят в одну сторону” (похожий контекст использования).

Normalization: cosine similarity

В ML чаще используют нормализованное скалярное произведение — cosine similarity:

$$ \text{cosine_similarity}(\vec{a}, \vec{b}) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| \cdot |\vec{b}|} = \cos(\theta) $$

Это даёт значения от -1 до 1: - 1 — векторы полностью совпадают по направлению - 0 — векторы ортогональны (нет связи) - -1 — векторы противоположны

from sklearn.metrics.pairwise import cosine_similarity

# Embeddings двух предложений
sentence1 = np.array([[0.2, 0.5, 0.8, 0.1]])
sentence2 = np.array([[0.21, 0.48, 0.79, 0.12]])

similarity = cosine_similarity(sentence1, sentence2)
print(f"Similarity: {similarity[0][0]:.4f}")  # близко к 1

Блок 3: Четыре способа вычисления

Способ 1: По определению (покомпонентно)

Формула: $$ \vec{a} \cdot \vec{b} = a_1 b_1 + a_2 b_2 + … + a_n b_n $$

Пример 1 (2D):

Найти скалярное произведение $\vec{a} = (3, 4)$ и $\vec{b} = (2, -1)$

Решение:

Шаг 1: Умножаем первые координаты

a₁ * b₁ = 3 * 2 = 6

Шаг 2: Умножаем вторые координаты

a₂ * b₂ = 4 * (-1) = -4

Шаг 3: Складываем результаты

6 + (-4) = 2

Ответ: $\vec{a} \cdot \vec{b} = 2$

Проверка в Python:

a = np.array([3, 4])
b = np.array([2, -1])
print(np.dot(a, b))  # 2

Пример 2 (3D — ML feature vector):

У нас есть два пользователя в рекомендательной системе. Каждый представлен вектором предпочтений: - Ось 1: насколько любит боевики (0-10) - Ось 2: насколько любит комедии (0-10)
- Ось 3: насколько любит драмы (0-10)

User A: vec_A = (8, 2, 5)  # любит боевики и драмы
User B: vec_B = (7, 3, 6)  # похожие вкусы

Найти скалярное произведение:

Решение:

Шаг 1: Умножаем координаты боевиков

8 * 7 = 56

Шаг 2: Умножаем координаты комедий

2 * 3 = 6

Шаг 3: Умножаем координаты драм

5 * 6 = 30

Шаг 4: Суммируем

56 + 6 + 30 = 92

Ответ: $\vec{a} \cdot \vec{b} = 92$

Интерпретация: Большое положительное число означает, что пользователи имеют похожие вкусы. Система может рекомендовать User B те фильмы, которые понравились User A.


Пример 3 (высокая размерность — embeddings):

Реальные embeddings имеют 768 (BERT), 1536 (OpenAI), или даже 4096 (Llama) измерений.

# Embeddings двух слов (упрощено до 5 измерений)
word1 = np.array([0.23, -0.45, 0.67, 0.12, -0.89])
word2 = np.array([0.25, -0.43, 0.65, 0.15, -0.87])

dot_product = np.dot(word1, word2)
print(f"Dot product: {dot_product:.4f}")

# Для реального BERT:
# from transformers import BertModel, BertTokenizer
# model = BertModel.from_pretrained('bert-base-uncased')
# tokens = tokenizer("Hello world", return_tensors="pt")
# embedding = model(**tokens).last_hidden_state[0]  # [seq_len, 768]

Способ 2: Через угол (геометрический)

Формула: $$ \vec{a} \cdot \vec{b} = |\vec{a}| \cdot |\vec{b}| \cdot \cos(\theta) $$

Пример 4:

Даны векторы $\vec{a} = (3, 0)$ и $\vec{b} = (0, 4)$ (перпендикулярны!)

Решение:

Шаг 1: Найдём длины векторов

|a| = √(3² + 0²) = √9 = 3
|b| = √(0² + 4²) = √16 = 4

Шаг 2: Определим угол Вектор $\vec{a}$ направлен вдоль оси X, вектор $\vec{b}$ — вдоль оси Y.
Угол между ними: $\theta = 90°$

Шаг 3: Применяем формулу

a · b = |a| * |b| * cos(90°)
a · b = 3 * 4 * 0 = 0

Ответ: $\vec{a} \cdot \vec{b} = 0$ (перпендикулярные векторы)

Проверка по определению:

a · b = 3*0 + 0*4 = 0 ✓

ML-интерпретация: Два ортогональных embedding’а означают, что концепции никак не связаны (например, “математика” и “кулинария”).


Способ 3: Матричная форма (для батчей в ML)

В ML мы часто работаем с батчами векторов (матрицы).

Формула: $$ A \cdot B^T $$

Где $A$ — матрица (n × d), $B^T$ — транспонированная матрица (d × m), результат — (n × m).

Пример 5 (batch processing в нейронке):

У нас есть 3 предложения, каждое — вектор из 4 чисел (упрощённые embeddings).

# 3 предложения × 4 измерения
sentences = np.array([
    [0.2, 0.5, 0.8, 0.1],  # sentence 1
    [0.3, 0.4, 0.7, 0.2],  # sentence 2
    [0.1, 0.6, 0.9, 0.0]   # sentence 3
])

# Хотим посчитать similarity каждого предложения с каждым
# Результат: матрица 3×3
similarity_matrix = sentences @ sentences.T

print("Similarity matrix:")
print(similarity_matrix)
# [[1.14 1.07 1.19]
#  [1.07 1.08 1.12]
#  [1.19 1.12 1.38]]

Интерпретация: - Диагональ (1.14, 1.08, 1.38) — каждое предложение с самим собой - Вне диагонали — похожесть между разными предложениями

Это основа self-attention в трансформерах!


Способ 4: Рекурсивное вычисление (для алгоритмов)

Иногда нужно считать скалярное произведение инкрементально (по одному элементу):

def dot_product_iterative(a, b):
    """Скалярное произведение через цикл"""
    if len(a) != len(b):
        raise ValueError("Vectors must have same length")
    
    result = 0
    for i in range(len(a)):
        result += a[i] * b[i]
    return result

# Тест
a = [1, 2, 3, 4]
b = [2, 0, -1, 3]
print(dot_product_iterative(a, b))  # 1*2 + 2*0 + 3*(-1) + 4*3 = 11

Это полезно в online learning или streaming алгоритмах, где данные приходят по одному.


Блок 4: Свойства скалярного произведения

1. Коммутативность

$$ \vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a} $$

Порядок не важен!

Доказательство:

a · b = a₁b₁ + a₂b₂ + ... + aₙbₙ
b · a = b₁a₁ + b₂a₂ + ... + bₙaₙ
(умножение чисел коммутативно, поэтому равны)

Пример:

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(np.dot(a, b))  # 32
print(np.dot(b, a))  # 32 (то же самое)

2. Дистрибутивность

$$ \vec{a} \cdot (\vec{b} + \vec{c}) = \vec{a} \cdot \vec{b} + \vec{a} \cdot \vec{c} $$

ML-применение: Если у тебя есть embedding документа как сумма embeddings слов, ты можешь разложить скалярное произведение:

# Embedding документа = среднее embeddings слов
doc = (word1 + word2 + word3) / 3

# Similarity с query
query_doc_similarity = query @ doc
# = query @ (word1 + word2 + word3) / 3
# = (query @ word1 + query @ word2 + query @ word3) / 3

3. Ассоциативность относительно скаляра

$$ (\lambda \vec{a}) \cdot \vec{b} = \lambda (\vec{a} \cdot \vec{b}) = \vec{a} \cdot (\lambda \vec{b}) $$

ML-применение: Scaling embeddings не меняет направление, только магнитуду.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(np.dot(2*a, b))      # 64
print(2 * np.dot(a, b))    # 64
print(np.dot(a, 2*b))      # 64

4. Норма через скалярное произведение

$$ |\vec{a}| = \sqrt{\vec{a} \cdot \vec{a}} $$

Почему это важно? L2 normalization в ML:

# Нормализация embedding (чтобы длина = 1)
def normalize(vec):
    norm = np.sqrt(np.dot(vec, vec))
    return vec / norm

embedding = np.array([3, 4])  # длина = 5
normalized = normalize(embedding)
print(normalized)  # [0.6, 0.8]
print(np.linalg.norm(normalized))  # 1.0

Нормализованные embeddings используют в cosine similarity, face recognition, retrieval системах.


Блок 5: Скалярное произведение в ML/AI — реальные применения

Применение 1: Semantic Search (поиск по смыслу)

Задача: Найти документы, близкие по смыслу к запросу.

Решение: 1. Преобразуем запрос и документы в embeddings (вектора) 2. Считаем cosine similarity между запросом и каждым документом 3. Сортируем по убыванию similarity

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

# Документы
documents = [
    "Machine learning is a subset of AI",
    "Deep learning uses neural networks",
    "Python is a programming language",
    "Neural networks mimic human brain"
]

# Запрос
query = "What is deep learning?"

# Получаем embeddings
doc_embeddings = model.encode(documents)  # shape: (4, 384)
query_embedding = model.encode(query)     # shape: (384,)

# Считаем cosine similarity через скалярное произведение
# (embeddings уже нормализованы в SentenceTransformer)
similarities = doc_embeddings @ query_embedding

# Сортируем документы по релевантности
ranked_docs = sorted(
    zip(documents, similarities),
    key=lambda x: x[1],
    reverse=True
)

for doc, score in ranked_docs:
    print(f"{score:.4f}: {doc}")

Вывод:

0.7234: Deep learning uses neural networks
0.6543: Neural networks mimic human brain
0.4521: Machine learning is a subset of AI
0.2134: Python is a programming language

Применение 2: Attention механизм в трансформерах

Как работает Self-Attention:

  1. Для каждого токена создаём 3 вектора: Query (Q), Key (K), Value (V)
  2. Считаем attention scores через скалярное произведение Q и K
  3. Применяем softmax
  4. Умножаем на V

Формула Attention:

$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V $$

Где $QK^T$ — это матрица скалярных произведений между всеми Query и Key!

Упрощённый пример:

import torch
import torch.nn.functional as F

# Предложение: "I love AI"
# Embeddings: 3 слова × 4 измерения
embeddings = torch.tensor([
    [1.0, 0.5, 0.2, 0.8],  # "I"
    [0.3, 0.9, 0.1, 0.7],  # "love"
    [0.6, 0.4, 0.8, 0.3]   # "AI"
])

# Создаём Q, K, V (упрощённо, без весов)
Q = embeddings  # queries
K = embeddings  # keys
V = embeddings  # values

d_k = Q.shape[-1]  # 4

# Шаг 1: Скалярное произведение Q и K
# Результат: матрица 3×3 (каждое слово со всеми)
attention_scores = Q @ K.T / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))

print("Attention scores (raw):")
print(attention_scores)

# Шаг 2: Softmax (нормализуем)
attention_weights = F.softmax(attention_scores, dim=-1)

print("\nAttention weights (after softmax):")
print(attention_weights)

# Шаг 3: Умножаем на V
output = attention_weights @ V

print("\nOutput (weighted sum of values):")
print(output)

Интерпретация: - Строка 1 attention_weights: насколько слово “I” обращает внимание на каждое слово - Строка 2: насколько “love” обращает внимание - Строка 3: насколько “AI” обращает внимание

Это ядро GPT, BERT, T5 и всех современных LLM!

Применение 3: Рекомендательные системы (Collaborative Filtering)

Задача: Предсказать рейтинг пользователя для фильма.

User-Item Matrix:

         Фильм1  Фильм2  Фильм3
User1      5       3       ?
User2      4       ?       2
User3      ?       5       4

Подход: Представить пользователей и фильмы как векторы в latent space (скрытом пространстве).

# Latent vectors (упрощённо, 2 измерения)
user1 = np.array([0.8, 0.2])  # любит экшн
user2 = np.array([0.3, 0.9])  # любит драмы

movie1 = np.array([0.9, 0.1])  # экшн
movie2 = np.array([0.2, 0.8])  # драма

# Предсказанный рейтинг = скалярное произведение
rating_user1_movie1 = np.dot(user1, movie1)
print(f"User1 → Movie1: {rating_user1_movie1:.2f}")  # 0.74 (высокий)

rating_user1_movie2 = np.dot(user1, movie2)
print(f"User1 → Movie2: {rating_user1_movie2:.2f}")  # 0.32 (низкий)

rating_user2_movie2 = np.dot(user2, movie2)
print(f"User2 → Movie2: {rating_user2_movie2:.2f}")  # 0.78 (высокий)

Это основа Matrix Factorization (SVD, ALS) в Netflix, Spotify, Amazon.

Применение 4: Нейронные сети (линейный слой)

Forward pass в нейронке — это множество скалярных произведений!

# Линейный слой: y = Wx + b
# W — матрица весов (output_size × input_size)
# x — входной вектор (input_size,)
# y — выходной вектор (output_size,)

class LinearLayer:
    def __init__(self, input_size, output_size):
        # Инициализация весов (маленькие случайные числа)
        self.W = np.random.randn(output_size, input_size) * 0.01
        self.b = np.zeros(output_size)
    
    def forward(self, x):
        # y = Wx + b
        # Каждая компонента y[i] = W[i] · x + b[i]
        # (скалярное произведение i-й строки W с вектором x)
        return self.W @ x + self.b

# Пример
layer = LinearLayer(input_size=5, output_size=3)
x = np.array([1.0, 0.5, -0.3, 0.8, 0.2])

output = layer.forward(x)
print("Output:", output)
# Каждое число в output — это скалярное произведение 
# одной строки весов с входом x

Применение 5: Gradient Descent (обучение моделей)

При обновлении весов в gradient descent мы используем скалярное произведение градиента с направлением обновления.

# Простой пример: линейная регрессия
# y = wx + b, минимизируем (y - y_pred)²

def gradient_descent_step(w, b, X, y, learning_rate=0.01):
    n = len(X)
    
    # Предсказания
    y_pred = w * X + b
    
    # Ошибка
    error = y_pred - y
    
    # Градиенты (через скалярное произведение!)
    dw = (2/n) * np.dot(error, X)  # ∂L/∂w
    db = (2/n) * np.sum(error)     # ∂L/∂b
    
    # Обновление
    w -= learning_rate * dw
    b -= learning_rate * db
    
    return w, b

# Данные
X = np.array([1, 2, 3, 4, 5])
y = np.array([2, 4, 6, 8, 10])  # y = 2x

# Инициализация
w, b = 0.0, 0.0

# Обучение
for epoch in range(100):
    w, b = gradient_descent_step(w, b, X, y)
    if epoch % 20 == 0:
        y_pred = w * X + b
        loss = np.mean((y - y_pred)**2)
        print(f"Epoch {epoch}: w={w:.3f}, b={b:.3f}, loss={loss:.3f}")

Блок 6: Оптимизация вычислений

SIMD и векторизация

Современные CPU и GPU умеют считать скалярное произведение параллельно через SIMD (Single Instruction, Multiple Data).

import time

# Обычный цикл (медленно)
def dot_product_slow(a, b):
    result = 0
    for i in range(len(a)):
        result += a[i] * b[i]
    return result

# NumPy векторизация (быстро)
def dot_product_fast(a, b):
    return np.dot(a, b)

# Бенчмарк
n = 1_000_000
a = np.random.randn(n)
b = np.random.randn(n)

# Медленный способ
start = time.time()
result_slow = dot_product_slow(a, b)
time_slow = time.time() - start

# Быстрый способ
start = time.time()
result_fast = dot_product_fast(a, b)
time_fast = time.time() - start

print(f"Slow: {time_slow:.4f}s")
print(f"Fast: {time_fast:.4f}s")
print(f"Speedup: {time_slow/time_fast:.1f}x")

Результат: NumPy в 50-100 раз быстрее!

GPU ускорение (PyTorch/TensorFlow)

Для батчей в ML используем GPU:

import torch

# CPU tensors
a_cpu = torch.randn(1000, 512)
b_cpu = torch.randn(512, 1000)

# GPU tensors
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
a_gpu = a_cpu.to(device)
b_gpu = b_cpu.to(device)

# Вычисление на GPU (параллельно!)
result = a_gpu @ b_gpu  # shape: (1000, 1000)

# В BERT/GPT такие операции выполняются тысячи раз
# GPU даёт ускорение в 10-100 раз

Практика: 30 заданий

Базовые (задания 1-10)

Задание 1: Вычислить скалярное произведение векторов $\vec{a} = (2, 3)$ и $\vec{b} = (4, 5)$


Задание 2: Найти скалярное произведение $\vec{a} = (1, -2, 3)$ и $\vec{b} = (4, 0, -1)$


Задание 3: Даны векторы $\vec{a} = (1, 1)$ и $\vec{b} = (-1, 1)$. Найти $\vec{a} \cdot \vec{b}$ и угол между ними.


Задание 4: Вычислить $\vec{a} \cdot \vec{a}$, где $\vec{a} = (3, 4)$


Задание 5: Найти скалярное произведение нулевого вектора с любым вектором.


Задание 6: Даны $\vec{a} = (2, 1)$ и $\vec{b} = (1, -2)$. Проверить, ортогональны ли векторы.


Задание 7: Найти $\vec{a} \cdot (2\vec{b})$, где $\vec{a} = (1, 2)$, $\vec{b} = (3, 4)$


Задание 8: Даны $\vec{a} = (1, 0, 0)$, $\vec{b} = (0, 1, 0)$, $\vec{c} = (0, 0, 1)$ (единичные векторы осей). Найти все парные скалярные произведения.


Задание 9: Вычислить норму вектора $\vec{a} = (5, 12)$ через скалярное произведение.


Задание 10: Найти скалярное произведение $\vec{a} = (1, 1, 1, 1)$ и $\vec{b} = (2, 2, 2, 2)$


Средние (задания 11-20)

Задание 11: Даны embeddings двух слов (упрощённо, 4D): - “machine” = (0.8, 0.5, 0.2, 0.1) - “learning” = (0.7, 0.6, 0.3, 0.2)

Найти их cosine similarity.


Задание 12: Проверить дистрибутивность: $\vec{a} \cdot (\vec{b} + \vec{c})$

Даны: $\vec{a} = (1, 2)$, $\vec{b} = (3, 4)$, $\vec{c} = (5, 6)$


Задание 13: Найти проекцию вектора $\vec{a} = (3, 4)$ на вектор $\vec{b} = (1, 0)$ (ось X).


Задание 14: Даны 3 точки: A(1, 2), B(4, 6), C(7, 10). Лежат ли они на одной прямой? (Используй скалярное произведение)


Задание 15: Нормализовать вектор $\vec{a} = (3, 4)$ (сделать его единичным).


Задание 16: Рассчитать batch similarity между 2 запросами и 3 документами.

Queries (2 × 3):

Q = [[1, 0, 1],
     [0, 1, 1]]

Documents (3 × 3):

D = [[1, 0, 0],
     [0, 1, 0],
     [1, 1, 1]]

Задание 17: Вычислить угол между векторами $\vec{a} = (1, 0)$ и $\vec{b} = (1, 1)$ в градусах.


Задание 18: Даны 3 слова с embeddings (2D): - “cat” = (0.8, 0.2) - “dog” = (0.75, 0.25) - “car” = (0.1, 0.9)

Какое слово наиболее похоже на “cat”?


Задание 19: Реализовать k-Nearest Neighbors (kNN) поиск через скалярное произведение.

Дано: 5 точек в 2D, найти 2 ближайшие к query.


Задание 20: Применить linear kernel в SVM. Kernel = скалярное произведение.

Даны 2 вектора признаков: $\vec{x} = (2, 3)$, $\vec{z} = (1, 4)$. Вычислить $K(\vec{x}, \vec{z})$.


Продвинутые (задания 21-30)

Задание 21: Реализовать Attention Score вручную для одного токена.

Дано: - Query: Q = [1, 0, 1] - Keys: K = [[1, 0, 0], [0, 1, 0], [1, 0, 1]] - Values: V = [[2, 0], [0, 3], [1, 1]]

Вычислить attention output для Q.


Задание 22: Градиент скалярного произведения. Найти $\frac{\partial}{\partial \vec{a}} (\vec{a} \cdot \vec{b})$.


Задание 23: Докажи неравенство Коши-Шварца через скалярное произведение: $$ |\vec{a} \cdot \vec{b}| \leq |\vec{a}| \cdot |\vec{b}| $$


Задание 24: Реализовать Centered Cosine Similarity (Pearson correlation через dot product).

Даны 2 вектора: $\vec{x} = (1, 2, 3, 4, 5)$, $\vec{y} = (2, 4, 5, 4, 5)$


Задание 25: Multi-Head Attention. Даны 2 головы (heads) attention. Вычислить итоговый output.

Head 1 output: [1, 2]
Head 2 output: [3, 1]
Combine: concatenate + linear projection W = [[1, 0], [0, 1], [1, 1], [1, -1]]


Задание 26: QR-декомпозиция через Gram-Schmidt (использует скалярное произведение).

Даны 2 вектора: $\vec{a} = (1, 1, 0)$, $\vec{b} = (1, 0, 1)$. Ортогонализировать их.


Задание 27: Вычислить Mahalanobis distance (использует скалярное произведение).

Дано: точка $\vec{x} = (2, 2)$, среднее $\vec{\mu} = (0, 0)$, covariance matrix: $$ \Sigma = \begin{bmatrix} 2 & 1 \ 1 & 2 \end{bmatrix} $$


Задание 28: Эффективное вычисление расстояний в batch (используя скалярное произведение).

Даны точки X (n × d) и центроиды C (k × d). Найти расстояние от каждой точки до каждого центроида.


Задание 29: Soft Attention с temperature scaling.

Даны query и 3 keys. Вычислить attention weights с температурой T = 0.5 (делает распределение более “острым”).

Q = [1, 2], K = [[1, 0], [0, 1], [1, 2]]


Задание 30: Реализовать Contrastive Loss (InfoNCE) через скалярное произведение.

Дано: - Anchor embedding: a = [1, 0, 1] - Positive embedding: p = [0.9, 0.1, 0.9] - 2 Negative embeddings: n1 = [0, 1, 0], n2 = [-1, 0, -1] - Temperature: τ = 0.5


Частые ошибки

Ошибка 1: Путать скалярное произведение с векторным
Неправильно: “Скалярное произведение двух векторов — это вектор”
Правильно: Результат скалярного произведения — число (скаляр). Векторное произведение (cross product) даёт вектор, это другая операция.
Почему важно: В ML мы всегда работаем со скалярным произведением. Векторное произведение используется редко (в 3D графике).


Ошибка 2: Забывать нормализовать при вычислении cosine similarity
Неправильно:

sim = np.dot(a, b)  # это НЕ cosine similarity!

Правильно:

sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

Почему важно: Без нормализации длинные векторы будут иметь большие скалярные произведения, даже если направления разные. Cosine similarity измеряет угол, а не магнитуду.


Ошибка 3: Не учитывать размерности в матричном умножении
Неправильно:

A = np.random.randn(3, 4)
B = np.random.randn(3, 5)
result = A @ B  # ERROR! Несовместимые размерности

Правильно:

result = A @ B.T  # (3, 4) @ (5, 3)^T = (3, 4) @ (3, 5) = ERROR
# Нужно:
result = A @ B.T  # если B — (5, 4), тогда B.T — (4, 5)

Почему важно: В ML постоянно работаем с батчами. Ошибка размерности — самая частая!


Ошибка 4: Думать, что большое скалярное произведение = большая похожесть
Неправильно: Сравнивать сырые скалярные произведения embeddings разной длины.
Правильно: Использовать cosine similarity (нормализованное скалярное произведение).

Пример:

a = np.array([10, 10, 10])  # длинный вектор
b = np.array([1, 1, 1])     # короткий, но то же направление

print(np.dot(a, b))  # 30 (большое)

c = np.array([5, 5, 5])
print(np.dot(a, c))  # 150 (ещё больше, но направление то же)

# Cosine similarity одинаковая!
from sklearn.metrics.pairwise import cosine_similarity
print(cosine_similarity([a], [b]))  # 1.0
print(cosine_similarity([a], [c]))  # 1.0

Ошибка 5: Игнорировать overflow в exponential (softmax)
Неправильно:

scores = np.array([100, 200, 300])
weights = np.exp(scores) / np.sum(np.exp(scores))  # OVERFLOW!

Правильно: Вычитать максимум (numerical stability trick)

scores = np.array([100, 200, 300])
scores_shifted = scores - np.max(scores)  # [-200, -100, 0]
weights = np.exp(scores_shifted) / np.sum(np.exp(scores_shifted))

Почему важно: В attention механизмах scores могут быть большими → overflow → NaN.


Ошибка 6: Не масштабировать attention scores по $\sqrt{d_k}$
Неправильно:

attention = softmax(Q @ K.T) @ V

Правильно:

d_k = Q.shape[-1]
attention = softmax(Q @ K.T / np.sqrt(d_k)) @ V

Почему важно: Без масштабирования при больших размерностях скалярное произведение становится огромным → softmax превращается в “жёсткий выбор” → плохой градиент.


Ошибка 7: Вычислять расстояния через цикл вместо векторизации
Неправильно (медленно):

distances = []
for x in X:
    for c in C:
        dist = np.linalg.norm(x - c)
        distances.append(dist)

Правильно (быстро):

# Используя трюк со скалярным произведением
distances = np.sqrt(
    np.sum(X**2, axis=1, keepdims=True) + 
    np.sum(C**2, axis=1) - 
    2 * X @ C.T
)

Почему важно: В ML работаем с тысячами точек — векторизация даёт ускорение в 100x.


Ошибка 8: Путать $\vec{a} \cdot \vec{b}$ и $\vec{a}^T \vec{b}$
Неправильно: Думать, что это разные операции.
Правильно: Это одно и то же! $\vec{a} \cdot \vec{b} = \vec{a}^T \vec{b}$ (если векторы-столбцы).

a = np.array([[1], [2], [3]])  # столбец
b = np.array([[4], [5], [6]])

# Оба способа дают одно и то же:
print(a.T @ b)        # [[32]]
print(np.dot(a.T, b)) # [[32]]
print(np.dot(a.flatten(), b.flatten()))  # 32

Главное запомнить

📝 Ключевые понятия

Скалярное произведение — сумма произведений соответствующих координат: $\vec{a} \cdot \vec{b} = \sum a_i b_i$

Геометрический смысл — мера “похожести” направлений: $\vec{a} \cdot \vec{b} = |\vec{a}| |\vec{b}| \cos(\theta)$

Косинусное сходство — нормализованное скалярное произведение: $\cos(\theta) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}$, даёт значения от -1 до 1

Ортогональность — если $\vec{a} \cdot \vec{b} = 0$, векторы перпендикулярны (угол 90°)

Норма через скалярное произведение — $|\vec{a}| = \sqrt{\vec{a} \cdot \vec{a}}$ (квадрат длины вектора)

Свойства — коммутативность ($\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a}$), дистрибутивность ($\vec{a} \cdot (\vec{b}+\vec{c}) = \vec{a} \cdot \vec{b} + \vec{a} \cdot \vec{c}$)

В embeddings — скалярное произведение измеряет семантическую близость слов/документов

В attention — $QK^T$ (матрица скалярных произведений) определяет, на что обращать внимание

В нейросетях — каждый нейрон вычисляет скалярное произведение весов с входом: $y = \vec{w} \cdot \vec{x} + b$

Векторизация — используй NumPy/PyTorch для быстрого вычисления (в 50-100x быстрее циклов)

Numerical stability — при softmax вычитай max из scores, при больших размерностях масштабируй на $\sqrt{d_k}$

Gradient — градиент скалярного произведения по $\vec{a}$ равен $\vec{b}$: $\nabla_{\vec{a}}(\vec{a} \cdot \vec{b}) = \vec{b}$


Связь с другими темами курса

Что нужно было знать до этого урока: - Векторы — понятие вектора, координаты, размерность - Базовая алгебра — операции с числами, суммирование - Тригонометрия — косинус угла (для геометрического смысла)

Что изучить дальше: - Матричное умножение — обобщение скалярного произведения на матрицы - Линейные преобразования — как матрицы трансформируют векторы - Собственные векторы и значения — используют скалярное произведение для нахождения - Градиентный спуск — оптимизация через градиенты (скалярные произведения) - Метрические пространства — расстояния и нормы - Трансформеры — архитектура, где attention = скалярное произведение

Где это нужно в жизни:

Для ML-инженеров: - 💻 Semantic search — поиск похожих документов через embeddings + cosine similarity - 🤖 Transformer models — весь механизм attention построен на скалярном произведении - 📊 Рекомендательные системы — collaborative filtering, matrix factorization - 🎯 Classification — линейные классификаторы (logistic regression, SVM) - 📈 Gradient descent — обновление весов, backpropagation - 🔍 k-NN, clustering — вычисление расстояний между точками - 🧠 Neural networks — каждый forward pass = множество скалярных произведений

Для Data Scientists: - 📉 Feature engineering — проекции, PCA (снижение размерности) - 📊 Correlation analysis — Pearson correlation = centered cosine similarity - 🔬 Anomaly detection — Mahalanobis distance использует скалярное произведение - 📝 Text mining — TF-IDF vectors + cosine similarity для похожести текстов

Для исследователей: - 🔬 Quantum computing — inner product в Hilbert space - 🧬 Bioinformatics — sequence alignment через similarity scores - 🎨 Computer vision — feature matching, template correlation


Интересные факты

💡 История — Концепция скалярного произведения введена Германом Грассманом в 1844 году в работе “Линейное расширение” (Die Lineale Ausdehnungslehre). Джозайа Гиббс популяризировал современную векторную нотацию в 1880-х.

💡 В квантовой механике — скалярное произведение используется для вычисления вероятности перехода между состояниями. Формула: $P = |\langle \psi_1 | \psi_2 \rangle|^2$

💡 Google PageRank — алгоритм ранжирования страниц использует скалярное произведение для вычисления важности. Каждая веб-страница — вектор в гигантском пространстве!

💡 BERT embeddings — в BERT один токен представлен вектором из 768 чисел. Каждый attention head вычисляет 512×512 = 262,144 скалярных произведений для одного предложения!

💡 Hardware acceleration — современные GPU имеют специальные инструкции для быстрого вычисления скалярного произведения (tensor cores). NVIDIA A100 может выполнять 312 триллионов операций скалярного произведения в секунду!

💡 Kernel trick — в SVM, polynomial kernel $K(\vec{x}, \vec{z}) = (\vec{x} \cdot \vec{z} + 1)^d$ позволяет работать в высокомерном пространстве без явного преобразования векторов (genius!)

💡 Word2Vec magic — знаменитое свойство “king - man + woman = queen” работает благодаря тому, что embedding пространство структурировано так, что скалярные произведения отражают семантические отношения.

💡 CLIP от OpenAI — модель, которая связывает изображения и текст, обучается контрастивным способом: максимизируя скалярное произведение между правильными парами (image, text) и минимизируя для неправильных.


Лайфхаки и полезные трюки

1. Быстрое вычисление нормы в NumPy
Вместо np.sqrt(np.dot(a, a)) используй:

np.linalg.norm(a)  # быстрее и читабельнее

2. Векторизованный cosine similarity для батчей

def batch_cosine_similarity(A, B):
    """A: (n, d), B: (m, d) → similarity matrix (n, m)"""
    A_norm = A / np.linalg.norm(A, axis=1, keepdims=True)
    B_norm = B / np.linalg.norm(B, axis=1, keepdims=True)
    return A_norm @ B_norm.T

# Вместо:
# for i in range(n):
#     for j in range(m):
#         sim[i,j] = cosine_similarity(A[i], B[j])  # медленно!

3. Трюк для numerical stability в softmax

def stable_softmax(x):
    """Вычитаем max для избежания overflow"""
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

4. Быстрая проверка ортогональности

def is_orthogonal(a, b, tol=1e-10):
    """Проверка с учётом floating point ошибок"""
    return abs(np.dot(a, b)) < tol

5. Efficient k-NN через FAISS (Facebook AI)

import faiss

# Для миллионов векторов
d = 128  # размерность
index = faiss.IndexFlatIP(d)  # Inner Product (скалярное произведение)
index.add(embeddings)  # добавляем базу

# Поиск 10 ближайших
k = 10
D, I = index.search(query_embeddings, k)  # в 100x быстрее naive

6. Mnemonic для запоминания формулы через уголСкалярное произведение = произведение длин × КОСинус
Легко запомнить: КОСинус потому что измеряем КОСвень (угол) между векторами 😄

7. Debugging attention: визуализация attention weights

import seaborn as sns
import matplotlib.pyplot as plt

def plot_attention(attention_weights, tokens):
    """Визуализация attention matrix"""
    plt.figure(figsize=(10, 10))
    sns.heatmap(attention_weights, 
                xticklabels=tokens, 
                yticklabels=tokens,
                cmap='viridis',
                annot=True)
    plt.title("Attention Weights")
    plt.show()

8. Оптимизация для GPU (PyTorch)

# Медленно:
for i in range(n):
    result[i] = torch.dot(a[i], b[i])

# Быстро (векторизовано):
result = (a * b).sum(dim=1)  # element-wise умножение + sum

9. Проверка размерностей до выполнения

def safe_dot(a, b):
    """Dot product с проверкой размерностей"""
    assert a.shape[-1] == b.shape[-1], \
        f"Incompatible shapes: {a.shape} and {b.shape}"
    return np.dot(a, b)

10. Lazy evaluation для больших батчей

# Если не хватает памяти для полной матрицы similarity
def similarity_generator(queries, docs, batch_size=100):
    """Вычисляет по батчам, экономя память"""
    for i in range(0, len(queries), batch_size):
        batch = queries[i:i+batch_size]
        yield batch @ docs.T

💡 Совет: Скалярное произведение — это фундамент линейной алгебры в ML. Потрать время, чтобы действительно понять его на интуитивном уровне. Каждый раз, когда видишь матричное умножение, attention, similarity — за всем этим стоит скалярное произведение!

Следующий шаг: Изучи матричное умножение — это просто множество скалярных произведений, организованных в матрицу! После этого трансформеры станут гораздо понятнее 🚀

Практика: Реализуй mini-GPT с attention механизмом с нуля, используя только NumPy и скалярное произведение. Это лучший способ по-настоящему понять, как работают LLM!

Понял тему? Закрепи в боте! 🚀

Попрактикуйся на задачах и получи персональные рекомендации от AI

💪 Начать тренировку
💬 Есть вопрос? Спроси бота!