Главная / Статьи / Восстановление по ассоциации: когнитивная модель, основанная на памяти, для векторного поиска

Восстановление по ассоциации: когнитивная модель, основанная на памяти, для векторного поиска

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

2273 слов

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

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

Хранение по смыслу, а не по имени

У человеческой памяти нет алфавитного индекса. Не существует такой «ментальной папки» под названием «Еда», в которой находилась бы подпапка «Итальянская кухня» с файлом под названием «Пицца». Именно такая строгая иерархия используется в традиционных базах данных: каждая запись находится по определенному адресу и находится с помощью точного ключа.

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

База данных векторов использует именно этот принцип. Элементы хранятся в соответствии с их значением, а извлекаются по степени их сходства с запросом, а не по точному совпадению ключа.

Что кодирует вектор

Чтобы понять базу данных, сначала необходимо понять, что в ней хранится. Вектор здесь — это просто упорядоченный список чисел, представляющий собой значение.

У машины нет живого понимания того, что такое пицца. Модель вкладки может научиться, обрабатывая огромные объемы текста, тому, какое значение имеет слово. Слово «пицца» находится рядом с такими словами, как «сыр», «итальянский», «тесто», «печь» и «ломтик». Это окружение отличается от окружения слова «суши», но в значительной степени совпадает с окружением слов «плоский хлеб» или «кальцоне».

Модель сводит эти паттерны к списку чисел фиксированной длины, обычно от нескольких сотен до нескольких тысяч значений; типичными размерами являются 384 и 1 536. Ни одно отдельное число не имеет читаемой метки, но вместе они точно определяют положение элемента в высокомерномензиональном пространстве. Важна следующая свойство: входные данные с похожим значением имеют векторы, расположенные близко друг к другу. «Пицца» находится рядом с «плоским хлебом», но далеко от «отчета о квартальной прибыли».

Другими словами, значение преобразуется в расстояние. Всё остальное в этой статье следует из этого единственного шага.

Похожесть как спектр, а не совпадение

Традиционный запрос является бинарным: строка либо удовлетворяет условию, либо нет. Если применить фильтр по слову «собака», будут получены строки, содержащие именно слово «собака», а для слов «щенок», «золотистый ретривер» или «верный четырехногий компаньон» результатов не будет.

Семантическое сходство заменяет этот ответ «да/нет» на оценку, показывающую степень близости двух значений. На примерной шкале «щенок» может получить оценку 0,94 по сравнению с «собакой», «волк» — примерно 0,71, а «счёт-фактура» — около 0,08. В качестве метрики обычно используется косинусное сходство или связанная с ним мера расстояния, такая как скалярное произведение или евклидово расстояние; правильный выбор зависит от того, как обучалась модель вживления.

Это соответствует эффекту меловой пыли: это не точное совпадение, а какой-то триггер, активирующий близлежащие воспоминания, которые в свою очередь активируют соседние. В векторной базе данных механизмы являются числовыми. Запрос преобразуется в вектор, и база данных возвращает сохраненные элементы, векторы которых находятся ближе всего к нему. «Близко» означает схожесть по смыслу.

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

Замечание относительно чисел

Оценки сходства являются относительными, а не абсолютными. Значение 0,8 от одной модели генерации векторов несопоставимо с значением 0,8 от другой модели, и даже внутри одной модели типичный диапазон зависит от области применения. Рассматривайте эти оценки как способ сортировки кандидатов, и если вы устанавливаете пороговое значение, настраивайте его на основе собственных данных, а не выбирайте округленное число.

Связанная поправка: обычный SQL, основанный на ключевых словах, не может выражать семантическое сходство, но это не означает, что базы данных SQL исключены из использования. Расширения, такие как pgvector, о которых пойдет речь ниже, добавляют векторные столбцы и операторы сходства в Postgres, так что эта функциональность может находиться внутри реляционной базы данных.

Поиск ближайших соседей в масштабе

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

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

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

Поэтому системы обработки данных опираются на алгоритмы приближенного поиска по ближайшему соседу. Вместо того чтобы проверять каждую звезду, они используют индекс, который сужает диапазон поиска до перспективных областей, принимая небольшую потерю точности в обмен на значительное ускорение. Самым распространенным алгоритмом сегодня является HNSW — сокращение от Hierarchical Navigable Small World. Чтобы использовать векторную базу данных, не обязательно знать ее внутренние механизмы, но стоит понимать, что именно благодаря ему поиск среди ста миллионов векторных представлений может занять всего несколько миллисекунд.

Понятие «приблизительный» заслуживает внимания. Индекс типа ANN иногда может пропустить настоящего ближайшего соседа, а скорость нахождения реально лучших результатов, называемая воспроизводимостью, зависит от параметров индекса, при которых для повышения точности идет компромисс между объемом памяти и задержкой. Если качество поиска становится непонятно неравномерным в масштабном использовании, стоит проверить настройки индекса; наш гид по настройке индексов HNSW для производственных систем RAG подробно рассматривает эти параметры.

Внутри хранилища эмбеддингов

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

Представьте библиотеку, которая игнорирует десятичную систему Дьюи и расставляет книги в зависимости от их «настроения». Книги о горе находятся рядом с книгами о потере, которые, в свою очередь, стоят рядом с книгами о одиночестве, затем — об изоляции, а после этого — мемуары о одиночных экспедициях. Никто не назначал эти категории вручную; такое расположение возникло потому, что читатели, привлечённые одной темой, обычно хотят ознакомиться и с другими.

Pinecone, Weaviate, Chroma и Qdrant — примеры таких библиотек. Вы загружаете в них эмбеддинги документов, описания продуктов, изображения, преобразованные в векторы, или паттерны поведения пользователей, и они поддерживают индекс, благодаря чему поиск остаётся быстрым даже при увеличении объёма коллекции.

Каждая сохраняемая запись обычно состоит из трёх частей:

  • Идентификатор, уникально определяющий элемент.
  • Вектор, представляющий его смысл.
  • Необязательные метаданные, такие как название, дата, категория или URL источника, которые могут использоваться для фильтрации результатов.

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

Полный цикл извлечения информации

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

  1. Встраивание контента. Каждый документ, статья, описание продукта или запись, которые нужно сделать поисковыми, проходит через модель встраивания, после чего полученный вектор хранится рядом с исходным контентом. Длинные документы обычно сначала разделяются на части, поскольку один вектор для всего руководства слишком сильно смешивает множество идей.
  2. Встраивание запроса с помощью той же модели. Это не является факультативным шагом. Разные модели встраивания генерируют векторы в несвязанных пространствах, поэтому сравнение запроса от одной модели с документами от другой приводит к бессмысленным значениям расстояний. Смена модели означает необходимость повторного встраивания всего набора данных.
  3. Поиск. Вектор запроса отправляется в базу данных, где алгоритм поиска ближайших соседей находит самые близкие хранимые векторы, после чего возвращаются лучшие результаты, обычно с показателями сходства.
  • Используйте результаты. В пайплайне RAG полученные фрагменты текста передаются языковой модели в качестве контекста, поэтому ответ основан на реальных документах, а не на догадках. В системах рекомендаций результаты представляют собой сами рекомендации. В функциях поиска они — это результаты, отображаемые пользователю.
  • С точки зрения пользователя релевантный ответ появляется в течение нескольких секунд. При этом смысл преобразуется в числа, эти числа сравниваются с миллионами сохраненных элементов, возвращаются наиболее близкие результаты, а ответ формируется на основе подлинного контента.

    Почему векторный поиск сейчас повсюду

    Недавно векторные базы данных были нишевым инструментом, к которому обращались лишь при создании семантических поисковых систем или специализированных рекомендательных сервисов. Сейчас они стали стандартной частью многих ИИ-приложений. Для технологии RAG необходимо место для хранения и поиска эмбеддингов документов. Агенты выполняют поиск в базах знаний на основе смысла. Масштабные рекомендательные системы работают с использованием векторного сходства. Мультимодальный поиск, такой как нахождение изображений по текстовому описанию или товаров по загруженной фотографии, также осуществляется с помощью векторов.

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

    Где ломается аналогия с памятью

    Сравнение с мозгом полезно для интуитивного понимания, но на практике важны некоторые отличия:

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

    Практические занятия

    Вы можете экспериментировать, не создавая никакой инфраструктуры:

    • ChromaDB работает локально внутри Python без необходимости создания учётной записи, использования API-ключей или проведения процедур развертывания; его установка и настройка занимают всего несколько строк кода. Он подходит для обучения и небольших проектов.
    • Qdrant предлагает бесплатный облачный тариф и удобный Python-клиент, что делает его отличным выбором, если вам нужна система, близкая к производственной, без необходимости самостоятельного управления серверами. Перед использованием проверьте текущие лимиты тарифа.
    • pgvector — это расширение для Postgres. Если у вас уже работает Postgres, оно позволяет добавлять функции векторного поиска без создания отдельной базы данных, тем самым сохраняя простоту архитектуры.

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

    Одна и та же модель по обе стороны оценки

    Косинусная близость — это число за фразой «эти два воспоминания похожи». Оно имеет смысл только если запрос и фрагмент закодированы одной моделью. Нулевой вектор или смесь двух моделей тихо ставит шум в соседи.

    def cosine(a: list[float], b: list[float]) -> float:
        dot = sum(x * y for x, y in zip(a, b))
        na = sum(x * x for x in a) ** 0.5
        nb = sum(y * y for y in b) ** 0.5
        if na == 0 or nb == 0:
            return 0.0
        return dot / (na * nb)
    
    # query and passage must come from the same embedding model
    score = cosine(embed(query), embed(passage))

    Боевой запрос добавляет фильтр по метаданным до поиска ближайших соседей, чтобы один арендатор не получил соседей другого. Фильтр — не реранкер. Он решает, кто вообще попадает в набор кандидатов.

    hits = collection.query(
        query_embeddings=[embed(query)],
        n_results=8,
        where={"tenant_id": tenant_id},
    )

    Основные выводы

    • Эмбеддинг преобразует смысл в координаты, поэтому похожие элементы оказываются рядом друг с другом.
    • Оценки сходства используются для ранжирования кандидатов; пороги необходимо настраивать на основе собственных данных.
    • Индексы типа ANN, такие как HNSW, идут на небольшую потерю полноты результатов ради значительного ускорения при обработке больших объемов данных.
    • Фильтры метаданных преобразуют первичные показатели сходства в ответы, соответствующие правилам времени, источника и доступа.
    • Запросы и документы должны использовать одну и ту же модель эмбеддингов, причем качество поиска зависит как от самой базы данных, так и от способа разбиения данных на части и качества самих данных.