Понад косинусову схожість: порівняння п’яти технік пошуку для RAG
Косинус, BM25, гібридний RRF, крос-енкодер для переранжування та MMR — пояснення з кодом, компроміси та підхід, що спочатку враховує запит, для вибору техніки пошуку для вашого RAG.
Джерела: **Beyond Cosine Similarity: The 5 RAG Retrieval Techniques That Actually Move the Needle**
Два речення, „Кіт сидів на килимку“ та „Кішка відпочивала на килимі“, зовсім не мають спільних слів, проте модель з ембеддингами оцінює їх приблизно у 0,85. Саме так косинусна схожість здобуває свою репутацію, і саме тут зазвичай закінчуються більшість навчальних матеріалів з RAG: показують косинус, вставляють уривок та переходять далі.
collection.query(query_embeddings=embedding, n_results=5)
Цей підхід працює. Він також є лише одним із п’яти методів пошуку, кожен з яких розроблений для різного типу проблем. Покладання лише на косинус означає, що цілі категорії запитів провалюються без жодних помилок, а користувачі отримують неправильні відповіді. Ось що насправді вимірює косинус, де він дає помилки, а також чотири інші методи та спосіб вибору потрібного підходу.
Що вимірює косинус
Модель ембеддингу перетворює текст на вектор — список сотень чи тисяч чисел. Тексти, що мають схоже значення, стають векторами, спрямованими в одному напрямку. Косинусна схожість порівнює кут між двома векторами, а не відстань між ними.
Це ключовий момент. Однорядкове речення та цілий абзац можуть означати одне й те саме. Якщо їхні вектори спрямовані в один бік, косинус показує високий рейтинг. Довжина, яка відображається як модуль, є шумом, а напрямок — сенсом.
Типові оцінки виглядають так:
| Пара | Оцінка |
|---|---|
| "Кіт сидів на килимку" та "Кішка відпочивала на килимі" | 0.85 |
| "Як працює увага?" та "Поясніть механізм трансформера" | 0.79 |
| "Мова програмування Python" та "Python — вид змій" | 0.38 |
| "Я обожнюю ранкову каву" та "Теорія квантової заплутаності" | 0.03 |
Перший рядок демонструє, чому косинус є ефективним показником. Немає спільних слів, але оцінка становить 0.85. Модель зрозуміла, що "кіт" та "кішка" знаходяться у сусідніх частинах семантичного простору, і косинус це враховує без жодного збігу ключових слів.
Чому не евклідова відстань
Очевидною альтернативою є евклідова відстань – це буквально відстань між двома векторами. Проблема полягає у тому, що якщо повторити документ десять разів, його вектор стане у десять разів більшим, тож він буде виглядати дуже відмінним від оригіналу, хоча за змістом нічого не змінилося. Косинус це ігнорує – важлива лише напрямок.
Попередження щодо оцінок
Оцінки за косинусом не є універсальними. Значення 0,7 від моделі all-MiniLM-L6-v2 – це інший сигнал, ніж значення 0,7 від моделі text-embedding-3-large, адже кожна модель має власний простір ембеддингів із власною щільністю. Налаштовуйте пороги оцінок на власному наборі даних для оцінки та не копіюйте числа з посібника, де використовувалась інша модель.
Аналогія з фестивалем для тих, хто не є інженерами
Перш ніж переходити до коду, уявіть собі п’ять методів у форматі ментальної моделі. Ви знаходитеся на музичному фестивалі та шукаєте друга серед натовпу, маючи у своєму розпорядженні три стратегії.
- За напрямком (косинус). Ви обидва погоджуєтесь дивитися на сцену. Однаковий напрямок означає одну й ту саму особу, незалежно від того, наскільки ви далеко один від одного. Важливий кут, у який ви показуєте, а розташування між вами — ні.
- За іменною табличкою (пошук за ключовими словами, BM25). Ви кричите: «Хтось на ім’я Алекс?» Цей метод працює ідеально, якщо іменна табличка збігається точно, але не спрацьовує у випадку з такими іменами, як Алексіс, Ал чи Сенді, які є однією й тією самою особою під різними назвами.
- За обличчям та іменною табличкою (гібридний метод). Ви перевіряєте обидва критерії. Той, хто відповідає обом, потрапляє на перше місце, а той, хто відповідає лише одному, все одно привертає увагу. Цей метод забезпечує найширше покриття.
Урок, який більшість навчальних посібників ігнорує: жоден окремий метод не може знайти всіх. Обраний вами метод визначає, які відповіді та яких людей ви назавжди пропустите.
П’ять методів, по одному
1. Схожість косинуса, стандартний варіант
Що вона робить. Порівнює семантичний напрямок векторів запиту та документа.
Коли її використовувати. Коли запити формульовані мовою природи та не потрібні точні терміни: загальне використання RAG, відповіді на запитання з документів, будь-що, що передбачає значну кількість парафразування.
У чому є обмеження. Буквальні рядки. Код продукту, номер рахунку чи ім’я особи, які майже не зустрічаються у навчальних даних, можуть призвести до повної невдачі методу косинуса. Вектори „GPT-4o“ та „GPT-4“ можуть знаходитися достатньо близько один до одного, що створює шум замість сигналу.
У коді:
collection.query(
query_embeddings=query_embedding,
n_results=5
)
Компроміс. Значення має перевагу над точним формулюванням. Цей метод є швидким, досить точним та не вимагає додаткової налаштування.
2. Пошук за ключовими словами BM25 – інструмент точності
Як це працює. Він оцінює документи за тим, наскільки часто у них зустрічаються слова запиту, з додатковим зниженням балів у залежності від поширеності цих слів у всьому корпусі. Для цього не використовуються ембеддинги – лише підрахунок.
Коли використовувати. Коли запит ґрунтується на точному ідентифікаторі, який має збігатися символ за символом, наприклад номер рахунку-фактури, назва моделі з датою випуску чи посилання CVE.
Що він не бере до уваги. Будь-яке значення тексту. Слово „Feline“ не знайде документи, у яких написано „cat“. Синоніми та парафрази для нього невидимі.
Важливий результат. У одному невеликому проекті з нуля додавання алгоритму BM25 разом із косинусом підвищило рівень відтворення результатів з 0,25 до 0,75. Причиною було те, що запити справжніх користувачів містили точні назви продуктів, які косинус сприймав як близькі варіанти замість того, щоб ідентифікувати їх як точні збіги. Варто розглядати це як одну точку даних, а не універсальний показник.
Компроміс. Точність ідентичних збігів за рахунок відсутності семантичної універсалізації.
3. Гібридний пошук – стандарт у практиці
Як це працює. Використовуються косинус та BM25 одночасно, а два списки ранжування об’єднуються за допомогою методу Reciprocal Rank Fusion (RRF). Жоден з цих методів сам по собі не покриває всі типи запитів.
Використовуйте його тоді. Запити поєднують природну мову з конкретними термінами, що характерно майже для кожної системи обробки даних. Користувач, який запитує "яка політика повернення грошей для замовлення №8821?", використовує як частину семантичний термін (політика повернення грошей), так і точне збіг (№8821).
Крок поєднання. Кожна техніка повертає власний рангований список. RRF присвоює кожному документу оцінку на основі його позиції в кожному списку — 1 поділене на (k плюс ранг), де k — це константа, зазвичай 60, після чого сумуються оцінки від усіх технік. Документи, які посідають високі позиції в обох списках, піднімаються на вершину.
# Pseudocode for RRF merge
def rrf_score(rank, k=60):
return 1 / (k + rank)
def merge_results(cosine_results, bm25_results):
scores = {}
for rank, doc in enumerate(cosine_results):
scores[doc.id] = scores.get(doc.id, 0) + rrf_score(rank)
for rank, doc in enumerate(bm25_results):
scores[doc.id] = scores.get(doc.id, 0) + rrf_score(rank)
return sorted(scores.items(), key=lambda x: x[1], reverse=True)
У AWS OpenSearch підтримує це вбудовано: ви створюєте гібридний запит із параметром ваги, і система внутрішньо здійснює їх об’єднання.
Компроміс. Найкраща загальна точність у пошуку, але вартість цього приблизно вдвічі вища, що зазвичай є виправданим.
4. Переранжування за допомогою крос-енкодера
Як це працює. Це двоетапний підхід. На першому етапі через косинусний або гібридний пошук знаходяться 20 найкращих кандидатів. На другому етапі кожен кандидат разом із запитом подається до крос-енкодера, який оцінює їхню релевантність, розглядаючи обидва одночасно.
Коли використовувати. Коли найкращі результати є релевантними, але у неправильному порядку, і точність має більше значення, ніж швидкість. Цей метод використовується на другому етапі, а не під час першого проходження пошуку.
Чому два етапи? Крос-енкодер значно краще оцінює релевантність, ніж косинус, але його не можна використовувати для обробки всієї бази даних. Двадцять викликів моделі замість одного є прийнятними. Десять тисяч викликів під час запиту — ні.
from sentence_transformers import CrossEncoder
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')# Stage 1: retrieve candidates
candidates = collection.query(query_embeddings=embedding, n_results=20)# Stage 2: rerank
pairs = [(query, chunk) for chunk in candidates['documents'][0]]
scores = reranker.predict(pairs)
top_5 = sorted(zip(scores, candidates['documents'][0]), reverse=True)[:5]
Серйозне попередження. Не використовуйте механізм переранжування як перший етап обробки. Витрати на затримку дуже високі, адже замість одного пошуку ембеддингу потрібно виконати двадцять повних обчислень моделі.
Компроміс. Найвища точність, але й найвищі витрати, тому використовуйте його лише на другому етапі.
5. Максимальна маржинальна релевантність (MMR) для різноманітності
Як це працює. Досягається компроміс між близькістю до запиту та схожістю з уже обраними фрагментами. Кожен новий обраний фрагмент має бути релевантним та відрізнятися від того, що вже обрано.
Коли використовувати. Коли всі топові фрагменти говорять про одне й те саме, наприклад при загальному питанні, на яке кожен результат повторює один і той самий факт.
Налаштування лямбда.
lambda = 1.0означає чисту релевантність, як у стандартному косинусному підході
lambda = 0.0 означає сутою різноманітність, повністю ігноруючи релевантністьlambda = 0.5 — це збалансована вихідна точка для налаштуваньdef mmr_select(query_embedding, candidates, lambda_param=0.5, k=5):
selected = []
remaining = list(candidates)
while len(selected) < k and remaining:
mmr_scores = []
for doc in remaining:
relevance = cosine_similarity(query_embedding, doc.embedding)
if selected:
redundancy = max(cosine_similarity(doc.embedding, s.embedding)
for s in selected)
else:
redundancy = 0
mmr_scores.append(lambda_param * relevance - (1 - lambda_param) * redundancy)
best = remaining[mmr_scores.index(max(mmr_scores))]
selected.append(best)
remaining.remove(best)
return selected
Де це має сенс. Анотації, дослідницькі запитання та будь-які випадки, коли читач хоче охоплення різних ідей, а не глибокого аналізу однієї.
Компроміс. Цей підхід усуває зайвість, але параметри лямбда потребують налаштування.
Вибір: нехай вирішують ваші запити
Перед тим, як обрати метод, вивчіть 20 реальних запитів від ваших справжніх користувачів чи з вашої галузі. Гіпотетичні запити не підходять.
- 80% або більше — це звичайна природна мова. Косинусний коефіцієнт є стандартом. Почніть з нього, адже він швидкий, точний та не вимагає додаткової інфраструктури.
- 30% або більше — це запити з точними термінами, кодами, ідентифікаторами чи власними назвами. Додайте алгоритм BM25. Як тільки ви помітите таку закономірність, гібридний пошук стане стандартом у продакшені, тож впровадьте метод об’єднання RRF. Покращення рівня відтворення даних зазвичай є швидким та значним.
Що не враховують навчальні матеріали
Техніки можуть взаємно перешкоджати, а не доповнювати одна одну. У одному експерименті використання механізму розширення запиту, який генерує кілька варіантів запиту та здійснює пошук для кожного з них, разом із гібридним методом пошуку здавалося безумовно ефективним рішенням. Більше варіантів мало призвести до кращих результатів. Однак рейтинги знизилися з 0,807 до 0,702. Більше сигналів пошуку так само легко посилюють шум, як і корисні сигнали, а додаткові варіанти запиту приводили до включення сусідніх, але неправильних даних. Гібридний метод пошуку вже працював добре, а розширення запиту лише створювало перешкоди. Додають техніки по одній, вимірюють їх окремо перед поєднанням та пам’ятають, що порядок має значення, адже те, що є корисним окремо, може завдати шкоди у поєднанні.
Скалярний добуток — це не косинус. Деякі бази даних векторів за замовчуванням використовують скалярний добуток. Вони збігаються лише тоді, коли вектори є одинично нормованими. Якщо ваша модель ембеддингів не нормалізує свій вихід, оцінки скалярного добутку не можна порівнювати між різними запитами, тому значення 0,9 для одного запиту та 0,9 для іншого означають різні речі. Перевірте, що насправді обчислює ваша база даних. Наприклад, ChromaDB зазвичай описується як система, що використовує інший стандартний показник, ніж Qdrant, тому переконайтеся в цьому в актуальній документації обох систем.
Запити визначають техніку, а не документи. Інженери часто витрачають час на класифікацію свого корпусу як технічного чи діалогового, довгого чи короткого. Це має значення для розділення на частини. Для вибору техніки пошуку аналіз запитів має набагато більше значення. Технічний корпус, який шукають за допомогою запитів у природній мові, є проблемою косинуса, а той самий корпус, який шукають за номером деталі, — гібридною проблемою.
Повний процес
Ось багатошарова структура, яку у кінцевому підсумку мають багато продакшн-систем RAG:
User Query
↓
[Optional: HyDE — if query is vague]
↓
Stage 1 Retrieval: Hybrid (Cosine + BM25 → RRF merge)
→ Top 20 candidates
↓
[Optional: Cross-encoder reranking — if precision matters]
→ Top 5 candidates
↓
[Optional: MMR — if results are redundant]
→ Final k candidates
↓
LLM Context Assembly + Generation
Не кожній системі потрібен кожен рівень обробки даних. Почніть з використання косинусного коефіцієнта, додайте гібридний підхід у разі помилок точного збігу, додайте механізм переранжування при неправильному порядку результатів та додайте MMR у разі повторення результатів. Вимірюйте ефективність на кожному етапі, адже те, що допомагає одній системі, може нашкодити іншій.
Найважливіше число
Звіти з експериментів із пошуком часто показують різницю у рівні відтворення результатів між пошуком, що використовує лише косинусний коефіцієнт, та гібридним пошуком на змішаних наборах запитів; у одному невеликому проекті цей розрив зріс з 0,25 до 0,75. Якщо це також частково стосується ваших даних, це означає, що приблизно в три рази більше правильних відповідей потрапляє до мовної моделі перед тим, як вона напише хоча б одне слово, без будь-якої оптимізації запитів, оновлень моделі чи змін інфраструктури. Усе зводиться до правильного вибору методу пошуку.