Представьте, что управляете онлайн-магазином, предлагающим тысячи товаров.

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

Например, когда пользователь вводит «лето», вы можете показывать предметы вроде шортов, платьев, панам и пляжных зонтов.

Как бы вы реализовали такую систему?



Если вы используете реляционную базу данных вроде Postgres или MySQL, то можете выполнять запрос, который путём сопоставления будет находить товары, содержащие введённые пользователем слова.

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

Чтобы более наглядно представить проблему, предположим, что у вас есть следующая таблица товаров:

ID Название
1 Летнее платье
2 Панама
3 Пляжный зонт
... ...
999 Зимний плащ
Теперь выполним запрос для поиска позиций, в названии которых содержится «лето» (в оригинале summer — прим. пер.):

SELECT * FROM products WHERE name LIKE '%summer%';

=> Found 1 result
=> Product(id=1 name="Summer dress")

Всё верно, был найден 1 результат — «Летнее платье». Но как же «Панама» и «Пляжный зонт»? Возможно, именно эти вещи ищет пользователь, а в результатах поиска они отсутствуют.

И здесь нет ошибок в написании, поэтому нечёткий поиск (fuzzy search) не поможет. База данных не находит эти товары, потому что не понимает, что они в более общем смысле относятся к «лету».

Вот здесь и приходят на выручку векторные базы данных.

Содержание



Начнём с основ — что такое вектор?


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



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

Например, если вы будете рекомендовать посетителям товары, то можете использовать для их представления вектор с сотнями измерений, каждое из которых будет выражать отдельный аспект того или иного товара (цвет, размер, цену, сезонность и так далее).

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

Что такое векторная база данных?


Векторная база данных — это простая база данных, оптимизированная под хранение и поиск векторов, даже таких, которые состоят из сотен и тысяч измерений.

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

Например, в нашем сценарии векторы «лето» и «панама» окажутся рядом, а вектор «зимний плащ» будет отдалён.



Примеры использования


Векторные базы данных можно использовать в очень разных ситуациях. Вот несколько примеров:

  • Контекст для чат-ботов: получение программой подходящих FAQ, документов или прежних бесед для предоставления более эффективных ответов. Подробнее о генерации ответов, дополненных результатами поиска (retrieval-augmented generation, RAG), читайте здесь.
  • Контекстный поиск: поиск, при котором сопоставление запроса производится не по словам, а по смыслу.
  • Персонализация: создание рекомендаций на основе того, что понравилось пользователям с аналогичными интересами.
  • Память для LLM: в качестве внешней памяти, помогающей LLM (большим языковым моделям) вспоминать прошлые взаимодействия пользователя или его данные.
  • Обратный поиск изображений: поиск похожих изображений на основе контента или стиля.
  • Рекомендация музыки: предложение песен на основе предпочтений пользователя или аналогичных произведений.

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

Принцип работы векторных баз данных


В общем виде схема работы векторных баз данных выглядит так:

▍ 1. Создание эмбеддингов




На первом этапе вы преобразуете данные (текст, изображения и так далее) в векторы, используя подходящую модель вроде OpenAI или Cohere, либо библиотеку вроде SentenceTransformers.

▍ 2. Индексация векторов




База данных будет сохранять векторы удобным для их последующего запроса способом. Зачастую это делается с помощью техники, называемой «приближенный поиск ближайших соседей» (approximate nearest neighbors, ANN), чтобы БД не приходилось сравнивать между собой все векторы в датасете.

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

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




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

Это очень упрощённое представление, но оно позволяет понять внутренний принцип работы векторных баз данных.

Простейший векторный поиск на чистом Python


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

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

import math

# Пример вектора запроса
QUERY_VECTOR = [0.2, 0.3, 0.8]

# Пример базы данных
VECTOR_DATABASE = [
    [0.1, 0.2, 0.9],
    [0.8, 0.7, 0.3],
    [0.4, 0.1, 0.5]
]

def cosine_similarity(vec1, vec2):
    dot_product = sum(a * b for a, b in zip(vec1, vec2))
    magnitude1 = math.sqrt(sum(a * a for a in vec1))
    magnitude2 = math.sqrt(sum(b * b for b in vec2))
    return dot_product / (magnitude1 * magnitude2)

# Поиск самого близкого к запросу вектора
similarities = [cosine_similarity(QUERY_VECTOR, vec) for vec in VECTOR_DATABASE]
most_similar_index = max(range(len(similarities)), key=lambda i: similarities[i])

# Вывод самого близкого вектора
print(f"Most similar vector: {VECTOR_DATABASE[most_similar_index]}")
print(f"Similarity score: {similarities[most_similar_index]}")

Этот скрипт демонстрирует принцип работы механизма поиска векторов. На практике по мере роста набора данных вам потребуется более эффективный способ их поиска и сравнения. В этом случае подходит выделенная векторная база данных.

Поиск по сходству и метрики расстояния


Далее речь пойдёт о том, как векторные базы данных измеряют сходство. Вот два популярных способа:

  • Евклидова метрика: подразумевает измерение прямого расстояния между двумя векторами. Обычно используется в задачах кластеризации и классификации, когда важна величина вектора (например, для вычисления GPS-координат или значений RGB).
  • Коэффициент Отиаи: используется для измерения косинуса угла между двух векторов. Полученная величина представляет, насколько схожими векторы являются по направлению (но не величине). Эта техника зачастую используется в системах оценки схожести текста или рекомендательных механизмах.

Но существует и много других метрик расстояния. Итоговый выбор будет зависеть от ваших данных и решаемой задачи.

Уменьшение размерности


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

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

Справиться с подобными сложностями можно при помощи техник уменьшения размерности, таких как метод главных компонент (principal component analysis, PCA) или t-SNE. Их применение позволит сократить число измерений, сохранив наиболее важную информацию.

К примеру, вы можете сократить 1000-мерный вектор до 100-мерного так:

from sklearn.decomposition import PCA

# Пример эмбеддингов с 1000 измерений.
vectors = [
    [0.1, 0.2, ...],
    [0.8, 0.7, ...],
    [0.4, 0.1, ...]
]

# Сокращение векторов до 100 измерений.
pca = PCA(n_components=100)
reduced_vectors = pca.fit_transform(vectors)

print(reduced_vectors)

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

Список векторных баз данных


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

  • Qdrant: опенсорсная векторная БД с акцентом на производительности.
  • pgvector: если вы уже используете Postgres, то она станет простым апгрейдом. Также рекомендую pgvectorscale, которая оптимизирует обработку больших датасетов и ускоряет выполнение запросов.
  • sqlite-vec: дополняет SQLite возможностями векторного поиска.
  • Pinecone: полностью управляемая и простая в освоении.
  • Convex: база данных реального времени с поддержкой эмбеддингов.
  • Faiss: библиотека для поиска по сходству, разработанная Facebook.
  • MeiliSearch: опенсорсный механизм поиска с поддержкой векторов.
  • Chroma: простая в использовании опенсорсная векторная БД.

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (17)


  1. CrazyElf
    06.12.2024 13:24

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


    1. Jack47
      06.12.2024 13:24

      Насколько я понимаю SVD для этого сценария действительно больше и лучше подходит чем PCA. Особенно если пространство формируется из статистики пользователей-покупок-поведения.


      1. Jack47
        06.12.2024 13:24

        А вот кстати интересно, насколько быстрее TruncatedSVD на GPU (NVIDIA) чем на обычном CPU? Должно быть намного быстрее. PCA наверное тоже быстрее но не так как SVD как раз изза плотности (неразряженности) матрицы ковариаций. Память GPU становится узким местом.


  1. Iwanowsky
    06.12.2024 13:24

    А может проще искать по БД с продуктами и тегами, их описывающими? Как раз Панама и была бы найдена по тегу "Лето". Теги можно присваивать и с помощью ИИ по наименованию товара, текстовому описанию и картинке (распознаванию объектов на картинке).


    1. vagon333
      06.12.2024 13:24

      А может проще искать по БД с продуктами и тегами, их описывающими?


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

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


      1. Jack47
        06.12.2024 13:24

        Да, семантический поиск это сила. Хотел бы дополнить - в статье указывается про тематический поиск - скажем по ключевому слову лето найти товары типа платья и т.п.

        Для этого, из того что я понял хорошо подходит не столько семантический поиск а именно SVD (Singular Value Desomposition). Допустим есть таблица в которой столбцы - это покупатели а строки - это товары. SVD может представить эту матрицу таким образом (найдя наиболее влиятельные сингулярные значения ) что она будет в некотором смысле определять скрытые факторы(абстракция, но можно представить как группы покупателей и группы товаров или , ключевые слова поиска).
        Это даст возможность группировать товары не столько по сематнике сколько по поведению покупателей (скажем, если покупатели купившие один товар часто покупают другой и т.п.)


    1. thevudi
      06.12.2024 13:24

      Тогда их нужно будет регулярно обновлять и индексировать, а также использовать нечёткий поиск.

      Имхо, не лучший вариант.


  1. SergeyKIT
    06.12.2024 13:24

    Действительно автор постарался простыми словами донести азы векторных БД. Плюс за работу!

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


  1. Andy_U
    06.12.2024 13:24

    Я один не увидел, как для конкретных в данном случае товаров строятся их вектора? И как строится само "векторное" пространство?


    1. Jack47
      06.12.2024 13:24

      В случае именно товаров, насколько мне известно, бывают различные подходы. При этом формирование векторного пространства можно условно разделить на два принципиально разных подхода - когда измерения имеют четкий физический смысл (например, цена или размер) и когда они полностью абстрактные. Например, часть измерений этого пространства может быть взята напрямую из многомерных характеристик товаров (цена, категория, размер, цвет и т.п.). Другая часть может использовать текстовые описания товаров - это аналог BERT и в этом случае измерения фиксированы, но начальные значения векторов в этих измерениях формируются случайно. Еще одна часть, например, может строиться на основе изображений товаров (CNN подход). Ну и, пожалуй самый важный с точки зрения продавца товара - это поведенческий подход для формирования пространства, когда векторы товаров изначально распределяются случайным образом, но в процессе работы системы они перегруппировываются на основе поведения покупателей (рядом оказываются товары, которые часто покупаются вместе). Все эти подходы обычно используются в комбинации, с разными весами в зависимости от конкретной задачи.

      Можно так же заметить, что в абстрактных пространства , скажем, для поведенческого подхода можно использовать методы типа SVD (Singular Value Decomposition) а для контекстуальных вещей типа BERT используются уже нейронные сети.
      Конечно же , можно навернуть сверху на это все PCA для снижения размерности пространств


      1. Andy_U
        06.12.2024 13:24

        векторы товаров изначально распределяются случайным образом

        Но для этого хотя бы надо как-то размерность пространства задать?


        1. Jack47
          06.12.2024 13:24

          Конечно, но это я думаю детали. То, что я читал по этому поводу ситуация такая - в LLM там бывает до нескольких тысяч, в BERT там до трех сотен. А в случае с SVD там вообще сотней обходятся. Но я это все прочитал в статьях, поскольку мой практический опыт использования абстрактных пространств ограничен OCR приложениями. Там это делалось для минимизации размерности (методом PCA) бинаризованного и преобразованного с помощью преобразования Адамара изображения символа в классификаторах. На этом мой практический опыт закончен, все остальное я прочитал из статей. Однако, индексацию векторных пространств я осваивал еще на алгоритмах QuadTree (сортировка на плоскости для ГИС ) и OctTree (квантизация цветовых RGB палитр ) - там пространства не абстрактные.
          Кстати, насколько я понимаю упомянутый в статье ANN - это развитие методов из OctTree / QuadTree для пространств большей размерности.
          Статья на ностальгию пробила - занимался я этим всем аж 25 лет назад :D


  1. ENick
    06.12.2024 13:24

    • "Faiss: библиотека для поиска по сходству, разработанная Facebook."

      Не только библиотека для поиска, результаты можно сохранить на диске: vector_store.save_local("faiss_index")


  1. Andrey_Solomatin
    06.12.2024 13:24

    И здесь нет ошибок в написании, поэтому нечёткий поиск (fuzzy search) не поможет. База данных не находит эти товары, потому что не понимает, что они в более общем смысле относятся к «лету».

    Векторная база на этих данных тоже не сможет. Сравнения поиска на строках и поиска на обогащенный данных выглядит как манипуляция и подрывает доверие к остальной информации.


    1. Jack47
      06.12.2024 13:24

      Если делать это как в статье показано с простыми векторами [0.2, 0.3, 0.8], такой поиск действительно не сработает. Насколько я понимаю, в статье совсем не упомянуто что то вроде

      model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')

      То есть загрузка эмбеддингов - вообще про нее ничего не говорится, интересно почему.


  1. pi-null-mezon
    06.12.2024 13:24

    Что-то хабр в последнее время стал часто публиковать вот такие поверхностные материалы. Жаль


  1. sdramare
    06.12.2024 13:24

    Наверное конкретно для задачи поиска по словам имеет смысл упомянуть ElasticSearch/OpenSearch, как довольно широко используемые решения.