Скалярное произведение: от математики до нейронных сетей 🎯
Представь: ты работаешь над поисковой системой. Пользователь вводит запрос “машинное обучение 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:
- Для каждого токена создаём 3 вектора: Query (Q), Key (K), Value (V)
- Считаем attention scores через скалярное произведение Q и K
- Применяем softmax
- Умножаем на 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
💪 Начать тренировку