Косинусная близость — косинус угла между двумя векторами:
cos(a, b) = (a · b) / (|a| × |b|)
В числителе скалярное произведение, в знаменателе — произведение длин. Значение лежит от −1 до 1: единица означает сонаправленность, ноль — перпендикулярность, минус единица — противоположность. Для эмбеддингов текста на практике почти всё попадает в интервал от 0 до 1.
Ключевая особенность: длина векторов не учитывается, важно только направление. Поэтому длинный документ и короткая заметка об одном и том же оказываются близки, хотя их векторы разной величины.
Практическая деталь: если векторы нормированы к единичной длине, знаменатель равен единице, и косинус превращается в скалярное произведение. Поэтому библиотеки нормируют векторы один раз при записи.
#Что означают конкретные числа
Разберём три пары запроса и фрагмента из архива:
| Пара | Отношение | Ожидаемая близость |
|---|---|---|
| «когда встреча с Ивановым» — «встреча с Ивановым перенесена на среду» | ответ на вопрос | высокая |
| «когда встреча с Ивановым» — «надо бы назначить встречу с Ивановым» | та же тема, ответа нет | тоже высокая |
| «когда встреча с Ивановым» — «купить корм коту» | ничего общего | низкая |
Первые две строки — суть проблемы. Обе фразы про встречу с Ивановым, обе получат высокую оценку, и векторный поиск их не различит. Отделить «ответ» от «та же тема» косинус не умеет в принципе.
Отсюда стандартное решение: векторный поиск отбирает кандидатов широко, а дальше их пересортировывает отдельная модель, которая сравнивает запрос и фрагмент напрямую — разбор в тексте про rerank-модели.
#Где путают
Заблуждение первое: есть универсальный порог релевантности. Нет. У каждой модели эмбеддингов своё распределение оценок: у одной несвязанные тексты дают 0,1, у другой — 0,7, потому что все её векторы лежат в узком конусе. Порог калибруется на своих данных: возьмите два десятка заведомо релевантных и заведомо нерелевантных пар и посмотрите, где проходит граница именно у вас.
Заблуждение второе: близость показывает правильность. Она показывает похожесть на запрос. Фрагмент с устаревшей информацией близок к запросу ровно так же, как свежий — различать их должна не метрика, а данные о времени и происхождении.
Заблуждение третье: отрицание учитывается. «Договор подписан» и «договор не подписан» отличаются одним словом и оказываются очень близки. Это известное слабое место эмбеддингов и одна из причин, почему чистый векторный поиск нельзя использовать для фактических вопросов без проверки текста — сравнение подходов в разборе семантического поиска против ключевых слов.
Заблуждение четвёртое: косинус и евклидово расстояние взаимозаменяемы. Для нормированных векторов они дают одинаковый порядок результатов, для ненормированных — нет. Ошибка проявляется не сбоем, а тихо съехавшей выдачей.
#Частые вопросы
#Почему близость двух совершенно разных текстов не равна нулю?
Потому что векторы одной модели занимают не всё пространство, а его часть. Смещение общее для всех текстов, и оно поднимает базовый уровень. Ориентироваться нужно на разницу между парами, а не на абсолютное значение.
#Можно ли сравнивать векторы, полученные разными моделями?
Нельзя. У каждой модели своя система координат; числа из разных моделей несопоставимы, даже если совпала размерность. При смене эмбеддера индекс строится заново, целиком.
#Как ускорить сравнение на большом архиве?
Нормировать векторы при записи и заменить косинус скалярным произведением, а при действительно больших объёмах — перейти на приближённый индекс; когда это оправдано, разобрано в справке про KNN и приближённый поиск соседей.
#Что делать, если поиск находит «примерно про то»?
Это штатное поведение метрики, а не поломка. Лечится вторым этапом: переранжированием кандидатов или комбинацией с текстовым поиском. Что вообще представляют собой сравниваемые векторы — в тексте про эмбеддинги простыми словами.
Остальные термины — в глоссарии терминов ИИ.