Какое свойство косинусного сходства (cosine similarity) делает его предпочтительным методом поиска ближайших соседей?

Ответ

Инвариантность к длине вектора (масштабу) — это ключевое свойство, которое делает cosine similarity особенно полезным для поиска ближайших соседей в задачах обработки текстов и рекомендательных систем.

Почему это важно:

  • При сравнении текстовых документов, представленных в виде векторов (TF-IDF, word2vec, BERT-эмбеддинги), длина вектора часто соответствует количеству слов или общей частоте терминов. Два документа на одну тему могут сильно различаться по длине (например, краткая статья и подробный обзор), но иметь схожее семантическое направление.
  • Cosine similarity измеряет косинус угла между векторами, игнорируя их евклидову норму. Это позволяет находить семантически близкие объекты, даже если они имеют разную «интенсивность».

Пример на Python:

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

# Два документа с одинаковой тематикой, но разной длиной
doc_short = np.array([1, 2, 1])        # TF-IDF для короткого текста
doc_long  = np.array([10, 20, 10])     # TF-IDF для длинного текста (тот же баланс терминов)
doc_other = np.array([1, 0, 3])        # Документ на другую тему

print("Cosine similarity (short vs long):", cosine_similarity([doc_short], [doc_long])[0][0])   # 1.0
print("Euclidean distance (short vs long):", np.linalg.norm(doc_short - doc_long))              # Большое значение
print("Cosine similarity (short vs other):", cosine_similarity([doc_short], [doc_other])[0][0]) # ~0.5345

Другие преимущества для nearest neighbor search:

  • Эффективность для разреженных данных: Хорошо работает с разреженными матрицами (например, TF-IDF), где большинство элементов — нули.
  • Устойчивость к доминированию частых признаков: В рекомендательных системах пользователь с большим количеством взаимодействий не будет автоматически считаться похожим на всех.

Когда использовать: Поиск похожих документов, коллаборативная фильтрация, кластеризация текстовых эмбеддингов.

Ответ 18+ 🔞

Давай разберём эту штуку, как будто я тебе на кухне объясняю, с пивом в руке. Сиди, слушай.

Вот представь: есть у тебя два текста. Один — короткий пост в телеге, три строчки. Другой — целая диссертация на ту же тему. По логике, они про одно и то же, да? Но если тупо сравнивать цифры, как будто ты взвешиваешь картошку, то диссертация будет весить овердохуища. И обычная метрика расстояния скажет: «Нет, брат, они разные, как Луна и жопа». А это же пиздец как несправедливо!

Так вот, инвариантность к длине вектора (масштабу) — это как раз волшебный пендель этой проблеме в сраку. Косинусная мера смотрит не на «вес», а на направление. Как будто сравнивает не две кучи картошки, а два вектора — куда они смотрят. Если оба смотрят в одну сторону (тематика одна), то косинус угла между ними будет близок к единице, даже если один вектор — спичка, а другой — телеграфный столб.

Почему это охренительно важно:

  • Всякие там TF-IDF, эмбеддинги из BERT — это по сути и есть вектора. Длина — это частота слов или общая «болтливость» документа. А косинусной мере похуй на болтливость. Её интересует суть, семантический курс.
  • Она измеряет косинус угла. Угол ноль градусов? Косинус 1.0. Идеальное совпадение по смыслу. И не важно, что один документ — это «люблю пиццу», а второй — трёхтомник «История пиццы от неолита до наших дней».

Смотри, как это в коде выглядит, ёпта:

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

# Два документа на одну тему. Один — чирикнул, второй — разорался.
doc_short = np.array([1, 2, 1])        # Короткий текст, TF-IDF мелкий
doc_long  = np.array([10, 20, 10])     # Длинный текст, но пропорции те же! Тот же баланс слов.
doc_other = np.array([1, 0, 3])        # А это уже про другое совсем

print("Косинусная близость (короткий vs длинный):", cosine_similarity([doc_short], [doc_long])[0][0])   # 1.0
print("Евклидово расстояние (короткий vs длинный):", np.linalg.norm(doc_short - doc_long))              # Цифра огромная, пиздец!
print("Косинусная близость (короткий vs другой):", cosine_similarity([doc_short], [doc_other])[0][0]) # ~0.5345

Видишь? Евклидова метрика — терпения ноль ебать — орет, что документы разные (большая цифра = большое расстояние). А косинусная — хитрая жопа — говорит: «Ребят, вы в одном направлении, у вас косинус 1.0, полная идентичность по смыслу». А с третьим, другим документом, она честно показывает ~0.53, мол, есть сходство, но так себе.

Чем ещё она хороша для поиска соседей:

  • Любит разреженные данные. Когда в твоей матрице из нулей торчат единички (как в TF-IDF), она не парится и работает быстро.
  • Не даёт засилью частых признаков всех обмануть. В рекомендашках: если один юзер лайкнул хуй в пальто всего, а другой — только пять фильмов, но осмысленно, они не станут «похожими» только потому, что первый — маньяк-активист.

Короче, когда это юзать: Когда ищешь похожие документы, строишь рекомендации или кластеризуешь текстовые эмбеддинги. Когда тебе важна суть, а не размер. А если нужно учитывать именно «вес» или интенсивность — тогда это не твой инструмент, иди на евклидову метрику. Всё, вопрос закрыт.