Aivaro
  • Оглавление
  • Вопросы
  • Практика
  • Вики
  • Материалы сообщества
  • Тесты
  • Поиск
✈Telegram @ai_varo
RUEN中文
…
Оглавление/Вопросы/#949

Как работает Text Similarity через эмбеддинги (cosine similarity) против BM25 (keyword overlap)?

Краткий тезис

Text Similarity — ключевая метрика для поиска, ранжирования и сравнения текстов. Два принципиально разных подхода: Cosine similarity на основе эмбеддингов (семантическая близость) и BM25 на основе TF-IDF (лексическое совпадение терминов). Cosine similarity улавливает смысл, но требует дорогих моделей; BM25 быстр и точен для точных ключевых слов, но не понимает синонимов. Гибридные схемы объединяют сильные стороны обоих методов.

2. BM25: overlap терминов

BM25 (Best Matching 25) — вероятностная модель ранжирования, эволюция TF-IDF. Оценивает релевантность документа запросу на основе частоты терминов и длины документа.

Формула (упрощённо): [ [text](/wiki/text){BM25}(q, d) = \sum_{t \in q} [text](/wiki/text){IDF}(t) \cdot \frac{f(t, d) \cdot (k_1 + 1)}{f(t, d) + k_1 \cdot (1 - b + b \cdot \frac{|d|}{[text](/wiki/text){avgdl}})} ] Где:

  • ( f(t, d) ) — частота термина в документе,
  • ( [text](/wiki/text){IDF}(t) ) — обратная документная частота,
  • ( k_1, b ) — параметры насыщения и нормализации длины.

Особенности:

  • Лексический: учитывает только точные совпадения слов.
  • Быстрый: работает на инвертированном индексе (Elasticsearch, Lucene).
  • Насыщение: высокая частота термина не даёт бесконечного роста веса.

Пример (через Whoosh):

from whoosh.index import create_in
from whoosh.fields import Schema, TEXT
from whoosh.qparser import QueryParser
from whoosh.scoring import BM25F

schema = Schema(title=TEXT(stored=True), content=TEXT)
ix = create_in("indexdir", schema)
writer = ix.writer()
writer.add_document(title="Doc1", content="BM25 для поиска текстов")
writer.commit()

with ix.searcher(weighting=BM25F()) as searcher:
    query = QueryParser("content", ix.schema).parse("поиск")
    results = searcher.search(query)
    print(results[0].score)  # 0.42

Недостатки:

  • Не понимает синонимы: «автомобиль» и «машина» — разные термины.
  • Чувствителен к стеммингу/лемматизации: без нормализации «бег» и «бегать» не совпадут.

3. Семантическая vs лексическая близость

АспектCosine similarity (эмбеддинги)BM25 (keyword overlap)
ПринципВекторное представление смыслаЧастотный анализ терминов
Понимание синонимовДа (например, «купить» ≈ «приобрести»)Нет (только точные совпадения)
Работа с опечаткамиЧастично (если модель обучена на шум)Нет (нужна fuzzy matching)
СкоростьМедленно (инференс + поиск по векторам)Быстро (инвертированный индекс)
МасштабированиеТребует FAISS или PineconeГотовые решения (Elasticsearch)
Длина текстаУстойчив (нормализация)Требует настройки параметра b
Пример сценарияПоиск по смыслу: «как приготовить пасту» → рецептыПоиск по точному названию: «BM25 алгоритм»

Когда что выбирать:

  • Cosine similarity: вопросно-ответные системы, рекомендации, поиск по описаниям (например, «найди похожие статьи»).
  • BM25: поиск по каталогам, юридические документы, где важны точные термины (например, «статья 228 УК РФ»).

4. Гибрид: BM25 + эмбеддинги

Гибридный подход объединяет лексическую точность BM25 и семантическую гибкость эмбеддингов. Популярные схемы:

a) Ранжирование с перевесом (Reciprocal Rank Fusion, RRF):

def rrf(scores_bm25, scores_cosine, k=60):
    combined = {}
    for doc_id in set(scores_bm25) | set(scores_cosine):
        rank_bm25 = scores_bm25.get(doc_id, 0)
        rank_cosine = scores_cosine.get(doc_id, 0)
        combined[doc_id] = 1/(k + rank_bm25) + 1/(k + rank_cosine)
    return sorted(combined.items(), key=lambda x: -x[1])

b) Двухэтапный поиск (Hybrid search):

  1. Stage 1 (BM25): быстрый отбор кандидатов (top-1000).
  2. Stage 2 (Cosine similarity): переранжирование эмбеддингами (top-10).

c) Взвешенная сумма: [ [text](/wiki/text){score} = [alpha](/wiki/alpha) \cdot [text](/wiki/text){BM25}(q, d) + (1 - [alpha](/wiki/alpha)) \cdot [text](/wiki/text){cosine_sim}(q, d) ] Где ([alpha](/wiki/alpha)) — гиперпараметр (обычно 0.3–0.7).

Пример реализации (Elasticsearch с ELSER):

{
  "query": {
    "bool": {
      "should": [
        { "match": { "content": "поиск текстов" } },
        { "knn": { "field": "embedding", "query_vector": [0.1, 0.2, ...] } }
      ]
    }
  }
}

Преимущества:

  • Робастность: если BM25 не находит точных совпадений, эмбеддинги «спасают» семантикой.
  • Производительность: BM25 отсекает шум, уменьшая нагрузку на векторный поиск.

Недостатки:

  • Сложность настройки: выбор ([alpha](/wiki/alpha)), порогов, моделей.
  • Дополнительное хранилище: и инвертированный индекс, и векторная БД.

5. Пет-проект для закрепления

Задача: Реализовать гибридный поиск по коллекции новостей (например, датасет Lenta.ru).

Инструменты:

  • Python: sentence-transformers, rank_bm25, scikit-learn.
  • Данные: 1000 новостей (тексты + заголовки).
  • Векторная БД: faiss (in-memory) или chromadb.

Шаги:

  1. Предобработка: очистка, лемматизация (pymorphy2), удаление стоп-слов.
  2. Индексация BM25: BM25Okapi из rank_bm25.
  3. Генерация эмбеддингов: model.encode(docs).
  4. Поиск:
    • BM25: bm25.get_scores(query) → top-100.
    • Cosine: cosine_similarity(query_emb, doc_embs) → top-100.
  5. Гибрид: RRF или взвешенная сумма → top-10.
  6. Оценка: NDCG@10 на размеченных запросах (например, 10 запросов с релевантными документами).

Ожидаемый результат:

  • Таблица метрик: BM25 (NDCG@10 = 0.65), Cosine (0.72), Hybrid (0.81).
  • Вывод: гибрид даёт прирост 10–15% за счёт комбинации методов.

Связь с другими вопросами

ВопросТема
912Основы эмбеддингов, отличие от BM25

Навигация

  • Предыдущий: 948
  • Следующий: 950
  • Индекс: 00. Индекс разборов