Как работает cosine similarity?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает 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, 2, 3] и [2, 4, 6] имеют cosine similarity = 1 (идентичны по направлению)
- Но евклидовое расстояние между ними не нулевое
-
Интуитивность: измеряет угол между векторами
- Маленький угол = похожие векторы
- Большой угол = непохожие векторы
-
Вычислительная эффективность: O(n) где n — размерность
-
Работает в высоких размерностях: в отличие от евклидова расстояния
# Сравнение: 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 и многих других приложений машинного обучения.