← Назад к вопросам

Как работает cosine similarity?

1.7 Middle🔥 171 комментариев
#Машинное обучение#Метрики и оценка моделей

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает cosine similarity?

Cosine similarity (косинусное сходство) — это мера сходства между двумя ненулевыми векторами, основанная на косинусе угла между ними. Это одна из самых популярных метрик в машинном обучении и обработке естественного языка.

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

Косинусное сходство вычисляется как:

cos(θ) = (A · B) / (||A|| × ||B||)

Где:

  • A · B — скалярное произведение (dot product) векторов A и B
  • ||A|| — норма (длина) вектора A (L2 норма, евклидова норма)
  • ||B|| — норма вектора B
  • θ — угол между векторами

Результат находится в диапазоне [-1, 1]:

  • 1 — идентичные векторы (угол 0°)
  • 0 — ортогональные векторы (угол 90°)
  • -1 — противоположные векторы (угол 180°)

Вычисление вручную

import numpy as np

# Пример: два вектора
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])

# Способ 1: вручную
dot_product = np.dot(A, B)  # 1*4 + 2*5 + 3*6 = 32
norm_A = np.linalg.norm(A)  # sqrt(1^2 + 2^2 + 3^2) = sqrt(14)
norm_B = np.linalg.norm(B)  # sqrt(4^2 + 5^2 + 6^2) = sqrt(77)

cosine_sim = dot_product / (norm_A * norm_B)
print(f"Cosine Similarity: {cosine_sim:.4f}")  # 0.9746

# Способ 2: используя sklearn
from sklearn.metrics.pairwise import cosine_similarity

# Заметим: cosine_similarity требует 2D arrays
cos_sim = cosine_similarity([A], [B])[0, 0]
print(f"Cosine Similarity (sklearn): {cos_sim:.4f}")  # 0.9746

# Способ 3: используя PyTorch
import torch

A_torch = torch.tensor([1., 2., 3.])
B_torch = torch.tensor([4., 5., 6.])

cos_sim_torch = torch.nn.functional.cosine_similarity(A_torch, B_torch, dim=0)
print(f"Cosine Similarity (PyTorch): {cos_sim_torch:.4f}")  # 0.9746

Геометрическая интерпретация

import numpy as np
import matplotlib.pyplot as plt

# Визуализация в 2D
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Случай 1: похожие векторы (угол близко к 0)
A1 = np.array([1, 1])
B1 = np.array([2, 2])
cos_sim1 = np.dot(A1, B1) / (np.linalg.norm(A1) * np.linalg.norm(B1))

ax = axes[0]
ax.quiver(0, 0, A1[0], A1[1], angles='xy', scale_units='xy', scale=1, color='blue', label='A')
ax.quiver(0, 0, B1[0], B1[1], angles='xy', scale_units='xy', scale=1, color='red', label='B')
ax.set_xlim(-0.5, 3)
ax.set_ylim(-0.5, 3)
ax.set_aspect('equal')
ax.grid(True)
ax.legend()
ax.set_title(f'Cosine Similarity: {cos_sim1:.2f}\n(θ близко к 0°)')

# Случай 2: ортогональные векторы (угол 90°)
A2 = np.array([1, 0])
B2 = np.array([0, 1])
cos_sim2 = np.dot(A2, B2) / (np.linalg.norm(A2) * np.linalg.norm(B2))

ax = axes[1]
ax.quiver(0, 0, A2[0], A2[1], angles='xy', scale_units='xy', scale=1, color='blue', label='A')
ax.quiver(0, 0, B2[0], B2[1], angles='xy', scale_units='xy', scale=1, color='red', label='B')
ax.set_xlim(-0.5, 1.5)
ax.set_ylim(-0.5, 1.5)
ax.set_aspect('equal')
ax.grid(True)
ax.legend()
ax.set_title(f'Cosine Similarity: {cos_sim2:.2f}\n(θ = 90°)')

# Случай 3: противоположные векторы (угол 180°)
A3 = np.array([1, 1])
B3 = np.array([-2, -2])
cos_sim3 = np.dot(A3, B3) / (np.linalg.norm(A3) * np.linalg.norm(B3))

ax = axes[2]
ax.quiver(0, 0, A3[0], A3[1], angles='xy', scale_units='xy', scale=1, color='blue', label='A')
ax.quiver(0, 0, B3[0], B3[1], angles='xy', scale_units='xy', scale=1, color='red', label='B')
ax.set_xlim(-2.5, 2)
ax.set_ylim(-2.5, 2)
ax.set_aspect('equal')
ax.grid(True)
ax.legend()
ax.set_title(f'Cosine Similarity: {cos_sim3:.2f}\n(θ = 180°)')

plt.tight_layout()
plt.show()

Практическое применение: поиск похожих документов

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

# Датасет документов
documents = [
    "Machine learning is a subset of artificial intelligence",
    "Deep learning uses neural networks",
    "Machine learning and deep learning are related",
    "Artificial intelligence is transforming technology",
    "Python is a programming language"
]

# Преобразование текста в TF-IDF матрицу
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(documents)

# Вычисление cosine similarity между всеми парами
similarity_matrix = cosine_similarity(X)

print("Cosine Similarity Matrix:")
print(similarity_matrix)

# Найти самые похожие документы для первого
query_idx = 0
similarities = similarity_matrix[query_idx]

# Исключим сам документ и найдём top похожие
top_indices = np.argsort(similarities)[-4:-1][::-1]  # top 3, исключая сам себя

print(f"\nДокумент {query_idx}: {documents[query_idx]}")
print("\nСамые похожие документы:")
for idx in top_indices:
    print(f"Документ {idx} (similarity={similarities[idx]:.4f}): {documents[idx]}")

Почему cosine similarity популярна?

  1. Инвариантность к масштабу: не зависит от длины вектора

    • Вектор [1, 2, 3] и [2, 4, 6] имеют cosine similarity = 1 (идентичны по направлению)
    • Но евклидовое расстояние между ними не нулевое
  2. Интуитивность: измеряет угол между векторами

    • Маленький угол = похожие векторы
    • Большой угол = непохожие векторы
  3. Вычислительная эффективность: O(n) где n — размерность

  4. Работает в высоких размерностях: в отличие от евклидова расстояния

# Сравнение: cosine vs euclidean
import numpy as np
from scipy.spatial.distance import cosine, euclidean

A = np.array([1, 2, 3])
B = np.array([2, 4, 6])  # идентичен A по направлению, но в 2 раза больше

print(f"Cosine similarity: {1 - cosine(A, B):.4f}")  # 1.0000 (идентичны по направлению)
print(f"Euclidean distance: {euclidean(A, B):.4f}")  # 3.7417 (далеко друг от друга)
print(f"Manhattan distance: {np.sum(np.abs(A - B))}")  # 3

Практический пример: рекомендательная система

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# User-item матрица (рейтинги)
# users x items
ratings = np.array([
    [5, 3, 0, 1],  # User 1
    [4, 0, 0, 1],  # User 2
    [1, 1, 0, 5],  # User 3
    [1, 0, 0, 4],  # User 4
])

# Найти похожих пользователей
user_similarity = cosine_similarity(ratings)
print("User-to-user similarity:")
print(user_similarity)

# Рекомендация: найти похожих пользователей и их предпочтения
query_user = 0
similar_users = np.argsort(user_similarity[query_user])[-3:][::-1]

print(f"\nСамые похожие на User {query_user}:")
for user_id in similar_users:
    if user_id != query_user:
        print(f"User {user_id} (similarity={user_similarity[query_user, user_id]:.4f})")

# Рекомендации на основе похожих пользователей
query_user = 0
unrated_items = np.where(ratings[query_user] == 0)[0]

for item in unrated_items:
    # Усредняем рейтинги похожих пользователей для этого item
    similar_ratings = []
    for user_id in similar_users:
        if user_id != query_user and ratings[user_id, item] > 0:
            weight = user_similarity[query_user, user_id]
            similar_ratings.append(weight * ratings[user_id, item])
    
    if similar_ratings:
        predicted_rating = np.mean(similar_ratings)
        print(f"Predicted rating для item {item}: {predicted_rating:.2f}")

Варианты cosine similarity

# 1. Angular distance (основана на cosine similarity)
# Расстояние = 1 - cosine_similarity
angular_distance = 1 - cosine_similarity(A, B)

# 2. Angular similarity (в градусах)
cos_sim = np.dot(A, B) / (np.linalg.norm(A) * np.linalg.norm(B))
angle_degrees = np.arccos(np.clip(cos_sim, -1, 1)) * 180 / np.pi
print(f"Angle: {angle_degrees:.2f}°")

# 3. Softmax для конвертирования в вероятности
from scipy.special import softmax

similarities = np.array([0.9, 0.7, 0.5, 0.3])
probabilities = softmax(similarities * 10)  # temperature scaling
print(f"Probabilities: {probabilities}")

Cosine similarity в embedding пространстве

from sentence_transformers import SentenceTransformer
import numpy as np

# Загрузка модели для embeddings
model = SentenceTransformer('all-MiniLM-L6-v2')

# Тексты
sentences = [
    "The cat sat on the mat",
    "A feline rested on the rug",
    "The weather is sunny",
    "It's a beautiful day"
]

# Получение embeddings
embeddings = model.encode(sentences)

# Вычисление cosine similarity
from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(embeddings)

print("Similarity matrix for sentences:")
for i in range(len(sentences)):
    for j in range(i+1, len(sentences)):
        print(f"'{sentences[i]}' vs '{sentences[j]}': {similarity_matrix[i, j]:.4f}")

Оптимизация вычисления для больших датасетов

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

# Для больших матриц используй sparse matrices
from scipy.sparse import csr_matrix

# Sparse матрица (для экономии памяти)
X_sparse = csr_matrix(X)

# cosine_similarity работает с sparse matrices
similarity = cosine_similarity(X_sparse)

# Для очень больших датасетов используй approximate методы
from sklearn.random_projection import SparseRandomProjection

# Уменьшение размерности
rp = SparseRandomProjection(n_components=50)
X_reduced = rp.fit_transform(X)

# Вычисление similarity на уменьшенных данных
similarity_approx = cosine_similarity(X_reduced)

Вывод: cosine similarity — это простая, интуитивная и мощная метрика для измерения сходства между векторами. Она остаётся стандартом для текстовых данных, embeddings и многих других приложений машинного обучения.

Как работает cosine similarity? | PrepBro