Что такое Pgvector
Pgvector – расширение для PostgreSQL, которое позволяет работать с векторными данными – хранить, индексировать и выполнять запросы. Хранение векторов в реляционной базе данных дает несколько важных преимуществ по сравнению с использованием специализированного векторного хранилища:
- Можно использовать все возможности реляционных баз данных наряду с векторным поиском – объединять таблицы с векторами, применять дополнительные поля для фильтрации, извлекать связанную информацию и т. д.
- С помощью поиска по векторной схожести и алгоритмов ближайших соседей можно находить самые релевантные документы для пользовательских запросов.
Словом, pgvector может сделать жизнь ИИ-разработчика проще – особенно если он избавится от нескольких устойчивых заблуждений о возможностях и ограничениях плагина. Автор этой публикации разобрал распространенные среди начинающих специалистов мифы.
Миф 1: Для хранения векторов всегда нужно использовать индекс
Миф появился из-за того, что некоторые векторные базы и популярные библиотеки используют термин «индекс» для описания любого способа хранения векторов. Это привело к заблуждению о том, что индексы – единственный способ хранения векторов. С точки зрения PostgreSQL это не так. Дело в том, что в других векторных хранилищах «плоским» индексом называется структура без иерархии. В PostgreSQL по умолчанию таблицы тоже «плоские». То есть, если вы создадите таблицу с колонкой векторного типа и добавите туда векторы, у вас как раз получится то, что в других системах считается плоским векторным индексом.
Хотя при использовании pgvector индексы создавать необязательно, важно знать, что в некоторых ситуациях они действительно нужны. Возьмем, например, приложение, которое индексирует расшифровки звонков для поиска и извлечения информации. Допустим, в базе данных есть 10 миллионов векторных представлений, но на каждого клиента приходится не больше 10 тысяч эмбеддингов, а на каждого продавца – до 1000, и они обычно ищут звонки только за последние несколько недель – это меньше 100 записей.
Если нужно искать всего среди 100 или 1000 векторов, почти наверняка вам лучше обойтись без векторного индекса. Вместо этого можно использовать обычные B-Tree индексы (возможно, с разделением на части) для ограничения области поиска на нужный подмножество векторов. Это даст вам полный охват (векторные индексы работают с приближенным поиском, поэтому могут упускать часть данных) и сэкономит время, память и ресурсы процессора на поддержание индексов, которые могут быть бесполезны (и которые PostgreSQL, возможно, вообще не будет использовать).
Миф 2: Семантика векторных индексов похожа на семантику других индексов
SQL-запрос, использующий индекс, возвращает те же данные, что и запрос без индекса. Это основная логика SQL и реляционных баз данных, и многие начинающие разработчики считают, что векторные индексы работают по тому же принципу.
Но с векторными индексами дело обстоит иначе: они представляют собой структуру данных для эффективного приближенного поиска ближайших соседей. Для улучшения производительности они ограничивают диапазон поиска ближайших соседей определенными подмножествами графа. Эти подмножества выбираются так, чтобы в них с большой вероятностью содержались ближайшие соседи, но на 100% это не гарантируется.
Именно здесь возникает компромисс между производительностью и полнотой поиска: чем больше подмножеств графа вы проверяете, тем выше вероятность найти настоящих ближайших соседей, но тем больше времени это займет. Кроме того, разные типы векторных индексов предлагают различные параметры конфигурации – на сколько подмножеств вы делите коллекцию? Насколько тщательно вы просматриваете каждое подмножество? Эти решения влияют на баланс между производительностью и полнотой поиска, и чтобы выбрать тип индекса и параметры правильно, нужно обязательно ознакомиться с документацией pgvector.
Миф 3: Нельзя хранить более 2000 измерений в векторном индексе
Этот миф связан с тем, что блоки данных в PostgreSQL ограничены размером 8К. По умолчанию вектор – это набор чисел с плавающей точкой, и каждое такое число занимает 32 бита. Простые вычисления показывают, что с учетом накладных расходов при размере вектора около 2000 измерений вы приближаетесь к лимиту в 8К. Хотя данные можно хранить в нескольких блоках (благодаря функции TOAST), построить векторный индекс для слишком больших векторов не получится. Для решения этой проблемы обычно применяют два подхода:
- Использование моделей эмбеддингов, которые генерируют векторы с меньшим количеством измерений, либо моделей, обученных уменьшать размер векторов без потери производительности.
- Применение алгоритмов сокращения размерности, которые уменьшают количество измерений вектора, одновременно сохраняя точность. Среди таких алгоритмов – PCA, t-SNE и UMAP, и есть исследования, показывающие, что они работают достаточно эффективно.
Самый же простой подход – квантование. Это процесс использования более легковесных типов данных для каждого измерения. Pgvector поддерживает тип half_vec с квантованием: он преобразует числа с плавающей точкой в 16-битные значения, убирая наименее значимые разряды. Поскольку векторы используются для поиска ближайших соседей, эти незначительные разряды обычно несильно влияют на относительные расстояния между векторами.
Так как тип half_vec занимает половину размера обычного float, в PostgreSQL уже можно хранить векторы с примерно 4000 измерений. Сообщество работает над квантизацией до 8 бит с типом int_vec, что позволит хранить векторы с 8000 измерений.
Даже если вы используете небольшие векторы, которые без проблем помещаются в блок PostgreSQL и могут индексироваться, стоит взять квантование на вооружение: уменьшение объема данных в два раза значительно повышает производительность и снижает использование ресурсов – практически без потерь для точности поиска.
Миф 4: Использование векторного индекса с другими фильтрами приводит к пропуску данных
Это, на самом деле, правда – но станет мифом в ближайшее время, с выпуском pgvector версии 0.8.0. Сейчас проблема выглядит так:
Предположим, вы проиндексировали вики своей компании и хотите найти документы, которые наиболее похожи на запрос «процесс и политика повышения». Но у вашей компании есть несколько подразделений, каждое из которых имеет свою собственную политику продвижения по карьерной лестнице, а вас интересуют только документы из категории «инженерное подразделение». Ваш запрос может выглядеть так:
Как PostgreSQL выполнит такой запрос? Хотелось бы, чтобы поиск происходил только среди подмножества векторов, относящихся к категории «инженерное подразделение». Но если вы заранее не создали разделы или частичные индексы, то такого подмножества не будет. При поиске PostgreSQL сначала использует векторный индекс, находит 10 ближайших соседей, а затем применяет фильтр, отбрасывая документы, которые не относятся к инженерному подразделению. В результате может остаться от 10 до 0 строк. А вы хотели получить ровно 10 строк в результатах поиска, т.е. хотели найти k ближайших соседей после применения фильтра, но заранее нельзя сказать, сколько соседей должен вернуть индекс, чтобы этого достичь.
В версии 0.8.0 будут введены итеративные векторные индексы. Это позволит PostgreSQL сканировать индекс, находить ближайших соседей, применять фильтр, продолжать сканирование индекса и фильтрацию, пока не будет найдено нужное количество соседей.
Также в версии 0.8.0 будет улучшена оценка стоимости использования векторных индексов, что поможет PostgreSQL решать, когда использовать векторный индекс, а когда полагаться на индексы B-Tree или GiST. Эти улучшения упростят создание индексов и выполнение запросов, обеспечивая правильную работу PostgreSQL с ними.
Миф 5: Векторная схожесть применяется только в RAG-приложениях
В RAG эмбеддинги помогают находить и извлекать релевантный контекст, что позволяет большим языковым моделям (LLM) давать более точные ответы и снижать риск галлюцинаций. Однако есть и другие области, в которых способность векторных эмбеддингов находить семантически схожий контент может оказать неоценимую помощь, даже без использования LLM:
- Поддержка пользователей – чтобы найти статьи базы знаний, которые соответствуют запросу пользователя, и предложить их клиенту или агенту поддержки.
- Отслеживание проблем – чтобы обнаружить повторяющиеся отчеты об одной и той же проблеме.
- Рекомендации – чтобы порекомендовать товары или контент, похожие на те, что уже понравились пользователю. Например, «Если вам понравилась эта книга, вам, вероятно, понравится и эта...»
- Обнаружение аномалий – чтобы вместо поиска похожих объектов использовать расстояние между векторами для обнаружения случаев, когда новый объект не имеет ближайших соседей. Если он значительно отличается от всех существующих объектов, это может быть аномалия, которую стоит зафиксировать.
- Поиск похожих товаров – чтобы по фотографии продукта найти похожие товары.
Миф 6: pgvector не поддерживает BM25 (и другие разреженные векторы)
Существует два типа векторных эмбеддингов – плотные и разреженные:
- Плотные векторы обычно создаются обученными языковыми моделями и кодируют семантическое значение предложения или документа. Эти векторы неочевидны для интерпретации, так как нельзя связать каждое измерение с определенным словом или концепцией. Плотные векторы обычно имеют от 256 до 4096 измерений.
- Разреженные векторы, напротив, часто являются результатом традиционных алгоритмов текстового поиска, таких как TF-IDF, BM25, SPLADE, которые используют векторы для представления важности слов в тексте. В разреженных векторах каждое измерение соответствует слову, а значение указывает, насколько часто или важно это слово в тексте. Количество измерений в таких векторах зависит либо от количества уникальных слов в наборе данных (в случае TF-IDF и BM25), либо от количества слов, на которых обучена модель (например, 30 522 слова для SPLADE). Поскольку большинство текстов содержат лишь малую часть всех возможных слов, большинство измерений в разреженных векторах имеют значение 0.
В версии 0.7.0 pgvector добавил поддержку разреженных векторов с типом sparsevec. Этот тип хранит только ненулевые элементы вектора. Разреженные векторы можно вставлять, указывая только ненулевые значения и их индексы. Если вы используете клиентские библиотеки pgvector (доступные для многих языков и ORM), то они автоматически преобразуют векторы в правильный текстовый формат.
В заключение
Плагин pgvector постоянно развивается: версия 0.7.0 была выпущена в апреле 2024 года, а релиз 0.8.0 ожидается в конце октября 2024. С каждой очередной версией добавляются несколько новых функций и устраняются старые ограничения – pgvector становится все более мощным и гибким инструментом ИИ-разработчика.
Как вы планируете использовать новые знания о Pgvector в своих проектах? Расскажите о своих идеях!
Комментарии