1. Главная
  2. Docs
  3. Глоссарий
  4. Векторный поиск

Векторный поиск

15

Векторный поиск — поиск не по совпадению слов, а по близости числовых представлений. Запрос кодируется в вектор, и из индекса извлекаются фрагменты с ближайшими векторами.

Точный поиск и приближённый

Честно сравнить запрос со всеми фрагментами — операция линейная по размеру индекса. На десятках тысяч фрагментов это допустимо и даёт точный результат: перебор ста тысяч векторов занимает единицы–десятки миллисекунд. На миллионах — уже сотни миллисекунд на запрос, что для интерактивной системы неприемлемо.

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

Практический вывод: если объём небольшой, приближённый поиск не нужен — он добавляет и сложность, и погрешность без выигрыша.

Как устроен HNSW

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

M — число связей. Сколько рёбер заводится на вектор в графе. Типичные значения — 8–48, чаще всего 16. Больше связей — выше полнота и память (граф растёт примерно пропорционально M), меньше — быстрее поиск, но выше риск застрять в локальном оптимуме и пропустить соседей.

ef_construction — качество построения. Сколько кандидатов рассматривается при добавлении вектора в граф. Типично 100–500. Выше — лучше граф и полнота, но дольше строится индекс; на постоянных обновляемых базах это становится заметной статьёй расходов.

ef_search — главный регулятор в эксплуатации. Ширина «луча» при спуске по графу: сколько кандидатов просматривается на нижнем слое. Должен быть не меньше числа извлекаемых фрагментов. Типичные значения 50–500: на базах в миллион векторов полнота recall@10 порядка 0,95–0,99 обычно достигается при ef_search 100–400. Растёт — полнота растёт, задержка растёт почти линейно; настраивается без перестройки индекса.

Как устроен IVF

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

nlist — число групп. Эвристика — порядок корня из числа векторов, умноженный на небольшой множитель. Групп слишком много — каждая мала, и релевантные векторы распадаются по разным группам; слишком мало — просмотр одной группы приближается к полному перебору.

nprobe — сколько групп просматривать. Второй регулятор компромисса полнота/скорость: доля просмотренных групп примерно соответствует вероятности не пропустить релевантный вектор. Типично просматривают от единиц до десятков процентов групп; nprobe также меняется без перестройки.

Слабое место IVF — вектор на границе двух групп: релевантные соседи оказываются в непросмотренной группе. Лечится увеличением nprobe и кратным дублированием граничных векторов, ценой памяти.

Компромисс полнота/скорость

Полнота измерима. Метрика recall@k: доля запросов контрольного набора, для которых в выдачу попал фрагмент, размеченный как релевантный. Настройка без этой метрики — перебор наугад: и ef_search, и nprobe крутят вслепую, пока что-то не «покажется лучше».

Порядок настройки. Сначала фиксируется контрольный набор из 50–200 запросов с эталонными фрагментами, затем снимается кривая «полнота — задержка» при разных ef_search или nprobe. Рабочая точка выбирается по требованиям к задержке: дальше определённого порога полнота растёт так медленно, что прирост не стоит лишних миллисекунд и памяти.

Типичные ориентиры. HNSW на миллионе векторов — доли миллисекунды до единиц миллисекунд на запрос при полноте 0,95+; IVF на том же объёме — сравнимая задержка при чуть меньшей полноте и заметно меньшей памяти.

Связь с остальной системой. Потеря полноты на этапе поиска не компенсируется ничем downstream: реранкинг сортирует только то, что попало в выдачу. Поэтому полноту поиска поднимают прежде, чем улучшают реранкинг или меняют модель генерации; как измерять весь конвейер — в статье про метрики RAG.

Сколько фрагментов извлекать

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

Рабочая схема — извлекать с запасом, 20–50 кандидатов, и сокращать до пяти–десяти реранкингом. Так поиск отвечает за полноту, а реранкер за точность.

Фильтрация по метаданным

Векторная близость не единственный критерий. Обычно нужно ограничить выдачу: только действующая редакция, только документы этого подразделения, только то, что доступно этому пользователю.

Разграничение доступа — не опция. Если система отвечает всем по всем документам, она рано или поздно выдаст сотруднику то, что ему не положено, и обнаружится это в худший момент.

Влияние фильтра на полноту. Жёсткий фильтр, применяемый к приближенному поиску, сужает область поиска: если группа или подграф отсеяны целиком, часть релевантных теряется. Зрелые базы применяют фильтр во время обхода, а не после; как это устроено в конкретных продуктах — тема статьи про векторную базу данных.

Где векторный поиск ошибается

Похоже по теме, но не по сути. Самая частая ошибка: возвращаются фрагменты про ту же область, не отвечающие на вопрос. Лечится реранкингом.

Точные идентификаторы. Артикулы, номера, коды ошибок теряются. Лечится гибридным поиском.

Отрицания и условия. «Без НДС» и «с НДС» дают близкие векторы.

Редкие термины. Если слово не встречалось модели при обучении, вектор будет неинформативным. Для отраслевой лексики это реальная проблема.

Дубли. Почти одинаковые фрагменты занимают всю выдачу, вытесняя другие релевантные. Решается отбором на разнообразие.

Частые вопросы

Сколько фрагментов извлекать на запрос?

Зависит от того, есть ли реранкинг. Без него берут 5–10 и рискуют полнотой. С реранкингом правильнее извлекать с запасом — 20–50 кандидатов — и сокращать до пяти–десяти после пересортировки. Точное число подбирается замером, а не по умолчанию из библиотеки.

Нужен ли приближённый поиск?

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

Почему находится похожее, но не то?

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

Как подобрать ef_search или nprobe?

Только по кривой «полнота — задержка» на контрольном наборе запросов. Начинают со значения в несколько раз больше числа извлекаемых фрагментов и снижают, пока полнота recall@k держится выше требуемого порога. Значение по умолчанию из библиотеки — стартовая точка, а не ответ: для разных корпусов и размерностей оптимум отличается в разы.

Почему после включения фильтров полнота упала?

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

Чем HNSW лучше IVF, если полнота у обоих настраиваемая?

Разные расходы. HNSW тратит больше памяти на граф и дольше строится, но стабильнее по полноте на фильтрованных запросах и чаще обновляется на месте. IVF экономнее по памяти и быстрее строится, но чувствительнее к разбиению на группы и граничным векторам. Выбор определяется объёмом, режимом обновлений и жёсткостью фильтрации, а не абстрактным «лучше».

Что почитать по теме