Косинусное сходство в Python: методы расчета и практическое применение

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Для кого эта статья:

  • профессионалы в области анализа данных и разработчики
  • студенты и обучающиеся, стремящиеся освоить навыки программирования и алгоритмов
  • специалисты в области машинного обучения и обработки текстовых данных

Когда речь заходит о сравнении текстовых данных, векторных представлений или поиске похожих документов, косинусное сходство становится тем самым инструментом, который превращает хаос информации в структурированную аналитику. В 2025 году, с экспоненциальным ростом текстовых данных и векторных представлений, умение эффективно применять косинусное сходство в Python — это уже не просто навык, а необходимость для аналитика, разработчика и исследователя. Давайте погрузимся в мир углов между векторами, где 1.0 означает полное совпадение, а 0 — полное различие. 🚀

Освоить математические основы и практическое применение косинусного сходства можно на Курсе «Аналитик данных» с нуля от Skypro. Программа включает не только теорию, но и практические задания по обработке текстовых данных, рекомендательным системам и кластеризации — всем областям, где косинусное сходство играет ключевую роль. Студенты курса разрабатывают собственные проекты с использованием Python, NumPy и scikit-learn, что помогает закрепить знания на практике.

Теоретические основы косинусного сходства

Косинусное сходство — метрика, измеряющая угол между двумя ненулевыми векторами в n-мерном пространстве. Чем меньше угол, тем больше сходство. Математически это выражается через скалярное произведение и норму векторов:

Python
Скопировать код
cos(θ) = (A·B)/(||A||·||B||)

Где:

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

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

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

Для текстовых данных и большинства задач машинного обучения используется положительное пространство, поэтому диапазон сужается до [0, 1]. 📊

Ключевое преимущество косинусного сходства — инвариантность к масштабу. То есть, для векторов одного направления, но разной длины (например, короткий и длинный документы с одинаковой тематикой), сходство будет высоким.

СвойствоКосинусное сходствоЕвклидово расстояние
Чувствительность к масштабуНетДа
Диапазон значений[-1, 1] или [0, 1][0, ∞)
Применимость к разреженным даннымВысокаяСредняя
Вычислительная сложностьO(n)O(n)
Интуитивная интерпретацияУгол между векторамиРасстояние между точками
Кинга Идем в IT: пошаговый план для смены профессии

Базовые алгоритмы расчёта косинусного сходства в Python

Рассмотрим несколько способов расчёта косинусного сходства в Python, от простейших до более сложных и оптимизированных.

  1. Базовая реализация с использованием NumPy:
Python
Скопировать код
import numpy as np

def cosine_similarity(a, b):
# Вычисляем скалярное произведение
dot_product = np.dot(a, b)

# Вычисляем нормы векторов
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)

# Вычисляем косинусное сходство
similarity = dot_product / (norm_a * norm_b)

return similarity

# Пример использования
vector_a = np.array([1, 2, 3])
vector_b = np.array([4, 5, 6])

similarity = cosine_similarity(vector_a, vector_b)
print(f"Косинусное сходство: {similarity:.6f}") # Примерно 0.974631
  1. Расчёт для текстовых данных с использованием TF-IDF:
Python
Скопировать код
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity as sklearn_cosine

# Создаём примеры текстов
documents = [
"Python is a great programming language for data analysis",
"Data analysis and machine learning are important skills",
"Python libraries make machine learning accessible"
]

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

# Вычисляем косинусное сходство между всеми текстами
similarity_matrix = sklearn_cosine(tfidf_matrix)
print("Матрица сходства:")
print(similarity_matrix)
  1. Вычисление для двумерных массивов (матриц признаков):
Python
Скопировать код
import numpy as np
from scipy import spatial

# Создаём две матрицы признаков
features_a = np.array([[1, 2, 3], [4, 5, 6]])
features_b = np.array([[7, 8, 9], [10, 11, 12]])

# Вычисляем косинусное сходство для каждой пары векторов
similarities = []
for i in range(len(features_a)):
for j in range(len(features_b)):
similarity = 1 – spatial.distance.cosine(features_a[i], features_b[j])
similarities.append((i, j, similarity))

# Выводим результаты
for i, j, sim in similarities:
print(f"Сходство между vector_a[{i}] и vector_b[{j}]: {sim:.6f}")

Александр Петров, старший Data Scientist

Когда я только начинал работать с рекомендательными системами, я столкнулся с задачей оптимизации поиска похожих товаров в каталоге из 500 тысяч позиций. Сначала я использовал наивный подход с двумя вложенными циклами для расчёта косинусного сходства между всеми парами товаров, что привело к вычислительному аду — расчёты шли больше суток.

Переписав код с использованием векторизированных операций NumPy, я сократил время до 30 минут:

Python
Скопировать код
import numpy as np

# Матрица признаков товаров (500,000 x 100)
product_features = np.random.random((500000, 100))

# Нормализуем векторы для упрощения расчетов
norms = np.linalg.norm(product_features, axis=1, keepdims=True)
normalized_features = product_features / norms

# Вычисляем косинусное сходство для первых 100 товаров (для примера)
subset = normalized_features[:100]
similarity_matrix = np.dot(subset, normalized_features.T)

# Получаем топ-5 самых похожих товаров для каждого товара из подмножества
top_similar = np.argsort(-similarity_matrix, axis=1)[:, 1:6]

Но настоящий прорыв произошел после применения приближенных методов поиска ближайших соседей с библиотекой Annoy, что сократило время до нескольких секунд с минимальной потерей точности. Это был ценный урок: правильный выбор алгоритма и инструмента может дать прирост производительности в сотни раз.

Эффективные библиотеки для вычисления в Python

В Python существует несколько высокопроизводительных библиотек для расчёта косинусного сходства, каждая со своими преимуществами. 🔍

  1. scikit-learn: Предоставляет функцию cosine_similarity, оптимизированную для разреженных и плотных матриц
Python
Скопировать код
from sklearn.metrics.pairwise import cosine_similarity

# Для плотных матриц
features1 = [[1, 2, 3], [4, 5, 6]]
features2 = [[7, 8, 9], [10, 11, 12]]
similarity = cosine_similarity(features1, features2)

# Для разреженных матриц (TF-IDF и т.д.)
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(["текст 1", "текст 2"])
similarity = cosine_similarity(tfidf_matrix)
  1. SciPy: Предлагает функцию spatial.distance.cosine, которая вычисляет косинусное расстояние (1 – сходство)
Python
Скопировать код
from scipy import spatial

vector1 = [1, 2, 3]
vector2 = [4, 5, 6]

# Обратите внимание: возвращает расстояние
distance = spatial.distance.cosine(vector1, vector2)
# Преобразуем в сходство
similarity = 1 – distance
  1. Gensim: Специализированная библиотека для обработки текстов с поддержкой косинусного сходства
Python
Скопировать код
from gensim import corpora, models, similarities

# Подготовка данных
documents = [
"Python is great for data analysis",
"Machine learning is fun with Python",
"NLP is an important field of AI"
]
texts = [[word for word in document.lower().split()] for document in documents]

# Создание словаря и корпуса
dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]

# Создание TF-IDF модели
tfidf = models.TfidfModel(corpus)
index = similarities.SparseMatrixSimilarity(tfidf[corpus], num_features=len(dictionary))

# Запрос
query = "Python for machine learning"
query_bow = dictionary.doc2bow(query.lower().split())
query_tfidf = tfidf[query_bow]

# Вычисление сходства
similarities = index[query_tfidf]
print(list(enumerate(similarities)))
  1. Annoy и FAISS: Библиотеки для приближенного поиска ближайших соседей, оптимизированные для работы с большими наборами данных
Python
Скопировать код
# Пример использования Annoy
from annoy import AnnoyIndex
import numpy as np

# Создаём тестовые данные
dim = 100
num_vectors = 10000
vectors = np.random.normal(size=(num_vectors, dim))

# Создаём индекс
index = AnnoyIndex(dim, 'angular') # Для косинусного сходства используем 'angular'
for i in range(num_vectors):
index.add_item(i, vectors[i])

# Строим индекс
index.build(10) # Больше деревьев = выше точность

# Поиск ближайших соседей
query_vector = np.random.normal(size=dim)
nearest_neighbors = index.get_nns_by_vector(query_vector, 5)
print(f"Ближайшие соседи: {nearest_neighbors}")
БиблиотекаТип данныхПроизводительностьОсобенности
scikit-learnПлотные и разреженные матрицыВысокая на средних объёмахПростота использования, интеграция с ML-конвейером
SciPyВекторы и матрицы NumPyВысокая для отдельных векторовМножество дополнительных метрик расстояния
GensimТекстовые данные, разреженные матрицыОптимизирована для текстовСпециализация на NLP, встроенные модели векторизации
AnnoyПлотные векторыОчень высокая на больших объёмахПриближённый поиск, сохранение индекса на диск
FAISSПлотные векторыСверхвысокая, GPU-ускорениеСложный API, широкий выбор индексов, масштабируемость

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

При работе с большими наборами данных стандартные методы расчёта косинусного сходства могут быть неэффективными. Рассмотрим несколько стратегий оптимизации. ⚡️

1. Векторизация вместо циклов

Замена явных циклов на векторизованные операции NumPy значительно ускоряет вычисления:

Python
Скопировать код
import numpy as np
import time

# Генерируем большие векторы
n = 10000
dim = 1000
vectors = np.random.random((n, dim))

# Неоптимизированный подход
def cosine_similarity_loop(vectors):
n = vectors.shape[0]
similarity_matrix = np.zeros((n, n))

start_time = time.time()
for i in range(n):
for j in range(n):
dot = np.dot(vectors[i], vectors[j])
norm_i = np.linalg.norm(vectors[i])
norm_j = np.linalg.norm(vectors[j])
similarity_matrix[i, j] = dot / (norm_i * norm_j)
elapsed = time.time() – start_time

return similarity_matrix, elapsed

# Оптимизированный подход
def cosine_similarity_vectorized(vectors):
start_time = time.time()

# Предварительно вычисляем нормы всех векторов
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
normalized = vectors / norms

# Скалярное произведение всех пар нормализованных векторов
similarity_matrix = np.dot(normalized, normalized.T)

elapsed = time.time() – start_time
return similarity_matrix, elapsed

# Вычисляем на небольшом подмножестве для демонстрации
subset = vectors[:100] # берём первые 100 векторов

_, time_loop = cosine_similarity_loop(subset)
_, time_vectorized = cosine_similarity_vectorized(subset)

print(f"Время выполнения (циклы): {time_loop:.2f} сек")
print(f"Время выполнения (векторизация): {time_vectorized:.2f} сек")
print(f"Ускорение: {time_loop/time_vectorized:.1f}x")

2. Использование разреженных матриц для текстовых данных

Текстовые данные часто представляются разреженными матрицами, где большинство элементов равны нулю. Использование специализированных структур данных экономит память и ускоряет вычисления:

Python
Скопировать код
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import time
import scipy.sparse as sp

# Генерируем синтетические текстовые данные
vocabulary_size = 10000
num_docs = 5000
words_per_doc = 50

# Создаём разреженную матрицу TF-IDF
rows = []
cols = []
data = []

for i in range(num_docs):
word_indices = np.random.choice(vocabulary_size, words_per_doc, replace=False)
word_weights = np.random.random(words_per_doc)

for j, idx in enumerate(word_indices):
rows.append(i)
cols.append(idx)
data.append(word_weights[j])

# Создаём разреженную матрицу в формате CSR
sparse_matrix = sp.csr_matrix((data, (rows, cols)), shape=(num_docs, vocabulary_size))

# Измеряем время вычисления косинусного сходства на разреженной матрице
start_time = time.time()
similarity_sparse = cosine_similarity(sparse_matrix[:100]) # для демонстрации берём подмножество
sparse_time = time.time() – start_time

# Преобразуем в плотную матрицу и измеряем время
dense_matrix = sparse_matrix.toarray()
start_time = time.time()
similarity_dense = cosine_similarity(dense_matrix[:100])
dense_time = time.time() – start_time

print(f"Время (разреженная матрица): {sparse_time:.2f} сек")
print(f"Время (плотная матрица): {dense_time:.2f} сек")
print(f"Ускорение: {dense_time/sparse_time:.1f}x")
print(f"Разреженность матрицы: {1 – len(data)/(num_docs*vocabulary_size):.3%}")

3. Приближенные методы для миллионов векторов

Когда количество векторов исчисляется миллионами, точное вычисление всех пар сходств становится невозможным. В таких случаях используются приближенные методы поиска ближайших соседей, такие как FAISS от компании Meta AI Research:

Python
Скопировать код
import numpy as np
import faiss
import time

# Генерируем большой набор векторов
d = 128 # размерность
nb = 1000000 # количество векторов в базе
nq = 1000 # количество запросов
k = 5 # количество ближайших соседей для поиска

xb = np.random.random((nb, d)).astype('float32') # векторы базы
xq = np.random.random((nq, d)).astype('float32') # векторы запросов

# Нормализуем векторы для косинусного сходства
faiss.normalize_L2(xb)
faiss.normalize_L2(xq)

# Точный метод (для сравнения)
start_time = time.time()
index_flat = faiss.IndexFlatIP(d) # IP = Inner Product = косинусное сходство для нормализованных векторов
index_flat.add(xb)
D_flat, I_flat = index_flat.search(xq, k)
flat_time = time.time() – start_time

# Приближенный метод
start_time = time.time()
nlist = 100 # количество кластеров
quantizer = faiss.IndexFlatIP(d)
index_ivf = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_INNER_PRODUCT)
index_ivf.train(xb)
index_ivf.add(xb)
index_ivf.nprobe = 10 # количество кластеров для поиска (компромисс точность/скорость)
D_ivf, I_ivf = index_ivf.search(xq, k)
ivf_time = time.time() – start_time

# Оценка качества приближения
recall_at_k = sum([len(set(I_flat[i]) & set(I_ivf[i])) for i in range(nq)]) / (nq * k)

print(f"Время (точный метод): {flat_time:.2f} сек")
print(f"Время (приближенный метод): {ivf_time:.2f} сек")
print(f"Ускорение: {flat_time/ivf_time:.1f}x")
print(f"Recall@{k}: {recall_at_k:.2%}")

4. Стратегии для сверхбольших данных

  • Обработка по частям: разделение данных на блоки и обработка каждого блока отдельно
  • Распределенные вычисления: использование фреймворков вроде Dask или PySpark для параллельных расчетов на кластере
  • GPU-ускорение: использование CUDA для расчётов на GPU (поддерживается в FAISS и CuPy)
  • Предварительная фильтрация: исключение заведомо непохожих векторов перед детальным расчётом
  • Локально-чувствительное хеширование (LSH): техника для быстрого приближенного поиска

Не уверены, какую карьерную траекторию выбрать в области анализа данных? Тест на профориентацию от Skypro поможет определить, подходит ли вам специализация по косинусному сходству и другим алгоритмам анализа данных. Тест учитывает ваши технические навыки, интересы к математическим алгоритмам и предрасположенность к работе с большими данными. По результатам вы получите персональные рекомендации по развитию в Data Science или смежных областях.

Практические кейсы применения косинусного сходства

Косинусное сходство широко применяется в различных областях анализа данных и машинного обучения. Рассмотрим конкретные примеры с кодом и результатами. 🌟

1. Рекомендательные системы

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

Python
Скопировать код
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# Пример датасета фильмов
movies = pd.DataFrame({
'id': [1, 2, 3, 4, 5],
'title': ['The Matrix', 'Inception', 'Interstellar', 'The Dark Knight', 'Pulp Fiction'],
'genre': ['Sci-Fi Action', 'Sci-Fi Thriller', 'Sci-Fi Adventure', 'Action Thriller', 'Crime Drama'],
'description': [
'A computer hacker learns about the true nature of reality.',
'A thief enters dreams and steals ideas from subconscious.',
'Explorers travel through a wormhole in space.',
'Batman fights against the criminal mastermind Joker.',
'The lives of criminals intertwine in a series of events.'
]
})

# Создаём TF-IDF векторы из жанров и описаний
tfidf = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf.fit_transform(movies['genre'] + ' ' + movies['description'])

# Вычисляем косинусное сходство между всеми фильмами
cosine_sim = cosine_similarity(tfidf_matrix, tfidf_matrix)

# Функция для получения рекомендаций
def get_recommendations(title, cosine_sim=cosine_sim, movies=movies):
# Получаем индекс фильма
idx = movies.index[movies['title'] == title].tolist()[0]

# Получаем оценки сходства для выбранного фильма
sim_scores = list(enumerate(cosine_sim[idx]))

# Сортируем фильмы по оценке сходства
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

# Исключаем сам фильм
sim_scores = sim_scores[1:6]

# Получаем индексы фильмов
movie_indices = [i[0] for i in sim_scores]

# Возвращаем рекомендованные фильмы с оценками сходства
return [(movies['title'].iloc[i], sim_scores[idx][1]) for idx, i in enumerate(movie_indices)]

# Получаем рекомендации для "Inception"
recommendations = get_recommendations('Inception')
for title, score in recommendations:
print(f"{title}: {score:.4f}")

2. Кластеризация текстов и документов

Косинусное сходство часто используется в алгоритмах кластеризации текстовых данных:

Python
Скопировать код
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score

# Набор новостных заголовков
headlines = [
"Scientists discover new species in Amazon rainforest",
"New breakthrough in cancer research announced",
"Stock market hits record high amid economic growth",
"Tech company releases innovative smartphone design",
"Global warming threatens polar bear populations",
"Researchers develop more efficient solar panels",
"Dow Jones index drops after Fed announcement",
"New smartphone features advanced camera system",
"Climate change linked to extreme weather events",
"Medical researchers announce vaccine breakthrough",
"Investment firms report quarterly earnings growth",
"Technology giant unveils latest gadget lineup"
]

# Создаём TF-IDF векторы
vectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(headlines)

# Определяем оптимальное количество кластеров
silhouette_scores = []
K = range(2, 6)
for k in K:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(X)
score = silhouette_score(X, kmeans.labels_)
silhouette_scores.append(score)

optimal_k = K[np.argmax(silhouette_scores)]
print(f"Оптимальное количество кластеров: {optimal_k}")

# Проводим кластеризацию с оптимальным k
kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X)

# Визуализируем кластеры с помощью PCA
pca = PCA(n_components=2)
X_dense = X.toarray()
coords = pca.fit_transform(X_dense)

plt.figure(figsize=(10, 6))
scatter = plt.scatter(coords[:, 0], coords[:, 1], c=clusters, cmap='viridis')
plt.legend(handles=scatter.legend_elements()[0], labels=[f'Cluster {i}' for i in range(optimal_k)])

# Анализируем содержимое кластеров
for cluster_id in range(optimal_k):
print(f"\nCluster {cluster_id}:")
cluster_headlines = [headlines[i] for i in range(len(headlines)) if clusters[i] == cluster_id]
for headline in cluster_headlines:
print(f" – {headline}")

Мария Соколова, NLP-инженер

В 2024 году я работала над системой обнаружения плагиата для образовательной платформы. Перед нами стояла задача проверять тысячи студенческих работ на уникальность, сравнивая их между собой и с базой из 10 миллионов документов.

Первоначально система была построена на точном подсчете косинусного сходства между документами, но она работала катастрофически медленно — проверка одной работы занимала до 20 минут. Я реализовала следующий алгоритм, сократив время до нескольких секунд:

  1. Предварительная обработка всех документов: токенизация, удаление стоп-слов, лемматизация
  2. Представление документов в виде n-грамм (последовательностей из n слов)
  3. Хеширование n-грамм с использованием MinHash для создания компактных сигнатур документов
  4. Применение Locality Sensitive Hashing (LSH) для быстрого поиска потенциальных совпадений
  5. Точное вычисление косинусного сходства только для предварительно отобранных пар документов

Ключевой код системы выглядел примерно так:

Python
Скопировать код
from datasketch import MinHash, MinHashLSH
from nltk.util import ngrams
import nltk
nltk.download('punkt')

def create_document_signature(text, num_perm=128):
"""Создает MinHash сигнатуру для документа"""
# Токенизация и н-граммы
tokens = nltk.word_tokenize(text.lower())
n_grams = list(ngrams(tokens, 3)) # триграммы

# Создание MinHash
minhash = MinHash(num_perm=num_perm)
for gram in n_grams:
minhash.update(str(gram).encode('utf-8'))

return minhash

# Создание LSH индекса для быстрого поиска
lsh = MinHashLSH(threshold=0.7, num_perm=128) # 0.7 – порог сходства

# Индексация базы документов (в реальном проекте выполнялась заранее)
document_db = {
"doc1": "Text of the first document...",
"doc2": "Text of the second document...",
# ... миллионы других документов
}

for doc_id, doc_text in document_db.items():
signature = create_document_signature(doc_text)
lsh.insert(doc_id, signature)

# Проверка нового документа
new_doc = "Text to check for plagiarism..."
new_signature = create_document_signature(new_doc)

# Быстрый поиск потенциальных совпадений
candidates = lsh.query(new_signature)

# Точная проверка только для кандидатов
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

if candidates:
vectorizer = TfidfVectorizer()
documents = [new_doc] + [document_db[doc_id] for doc_id in candidates]
tfidf_matrix = vectorizer.fit_transform(documents)

# Вычисляем сходство нового документа со всеми кандидатами
similarities = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:])

for i, doc_id in enumerate(candidates):
print(f"Сходство с {doc_id}: {similarities[0][i]:.3f}")

Эта оптимизация не только ускорила систему, но и значительно снизила потребление ресурсов сервера. Самой большой неожиданностью было то, что при предварительно установленном пороге LSH в 0.7, мы не упустили ни одного случая плагиата с косинусным сходством выше 0.8, что было нашим целевым порогом.

3. Поиск похожих изображений

Косинусное сходство применяется для поиска похожих изображений на основе извлеченных с помощью предобученных нейронных сетей признаков:

Python
Скопировать код
import numpy as np
import tensorflow as tf
from tensorflow.keras.applications import ResNet50, preprocess_input
from tensorflow.keras.preprocessing import image
from sklearn.metrics.pairwise import cosine_similarity
import os
import matplotlib.pyplot as plt
from PIL import Image

# Загружаем предобученную модель ResNet50
base_model = ResNet50(weights='imagenet', include_top=False, pooling='avg')

# Функция для извлечения признаков из изображения
def extract_features(img_path):
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
features = base_model.predict(x, verbose=0)
return features.flatten()

# Директория с изображениями
image_dir = "path_to_images"
image_paths = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith(('jpg', 'jpeg', 'png'))]

# Извлекаем признаки из всех изображений
features = {}
for path in image_paths:
features[path] = extract_features(path)

# Конвертируем в массив
feature_matrix = np.array(list(features.values()))

# Вычисляем косинусное сходство между всеми изображениями
similarity_matrix = cosine_similarity(feature_matrix)

# Функция для поиска похожих изображений
def find_similar_images(image_path, top_n=5):
if image_path not in features:
query_features = extract_features(image_path)
similarities = cosine_similarity([query_features], feature_matrix)[0]
else:
idx = list(features.keys()).index(image_path)
similarities = similarity_matrix[idx]

# Сортируем по убыванию сходства
most_similar_indices = similarities.argsort()[::-1][1:top_n+1] # исключаем само изображение
similar_images = [list(features.keys())[i] for i in most_similar_indices]
similarity_scores = [similarities[i] for i in most_similar_indices]

return similar_images, similarity_scores

# Визуализируем результаты для произвольного изображения
query_image = image_paths[0]
similar_images, scores = find_similar_images(query_image)

plt.figure(figsize=(15, 10))
plt.subplot(3, 3, 1)
plt.imshow(Image.open(query_image))
plt.title("Query Image")
plt.axis('off')

for i, (img_path, score) in enumerate(zip(similar_images, scores)):
plt.subplot(3, 3, i+2)
plt.imshow(Image.open(img_path))
plt.title(f"Similarity: {score:.3f}")
plt.axis('off')

plt.tight_layout()
plt.show()

4. Другие практические применения

  • Обнаружение дубликатов: поиск дублирующихся документов, товаров в каталоге или записей в базе данных
  • Анализ тональности: классификация текстов на основе сходства с набором эталонных текстов известной тональности
  • Поисковые системы: ранжирование результатов поиска по релевантности запросу
  • Биоинформатика: сравнение геномных последовательностей и белковых структур
  • Обнаружение аномалий: поиск нетипичных данных, сильно отличающихся от основного массива
  • Компьютерное зрение: идентификация объектов и распознавание лиц на основе векторных представлений

Косинусное сходство — это тот самый универсальный инструмент, который позволяет структурировать хаос данных в практически любой области. От рекомендательных систем до распознавания мошенничества, от классификации текстов до поиска похожих изображений — понимание математических основ и умение эффективно реализовать вычисления делает разработчика и аналитика по-настоящему ценным специалистом. Главное помнить, что косинусное сходство — это не панацея, а один из инструментов в вашем аналитическом арсенале, который должен применяться с пониманием его сильных сторон и ограничений.