Использование pgvector

Обзор

Расширение pgvector реализует поиск по сходству векторов для PostgreSQL, что предполагает использование эмбеддинга (embedding). Эмбеддинг — это метод, который отображает сложные многомерные данные, включая текст и изображения, в векторном пространстве числовых векторов. Объекты с похожим смыслом располагаются рядом друг с другом в этом пространстве.

Такой поиск полезен в следующих сценариях:

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

  • Системы RAG (Retrieval Augmented Generation). Помогает языковым моделям ИИ искать релевантную информацию в базе знаний компании или в интернете для точных ответов.

  • Рекомендательные системы. Онлайн-кинотеатры, маркетплейсы и другие подобные системы используют поиск по сходству векторов, чтобы предлагать фильмы, музыку и товары на основе похожих интересов пользователей и свойств самих продуктов.

  • Мультимедийный поиск. Дает возможность загрузить изображение или ввести текстовый запрос и найти фотографии или видео, ему соответствующие.

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

Пакет, требуемый для установки pgvector, поставляется с ADP. Чтобы использовать pgvector, выполните команду CREATE EXTENSION в той базе данных, в которую необходимо поставить расширение:

CREATE EXTENSION vector;
ПРИМЕЧАНИЕ
Если расширение pgvector создано в базе данных template1, используемой в качестве шаблона БД по умолчанию, во всех вновь создаваемых базах данных будет установлено это расширение.

ADP использует версию 0.8.0 расширения pgvector. Чтобы это проверить, выполните следующий запрос:

SELECT extversion FROM pg_extension
    WHERE extname = 'vector';
 extversion
------------
0.8.0

Расширение pgvector позволяет хранить векторы вместе с другими данными. Оно поддерживает:

  • точный и приблизительный поиск ближайших соседей;

  • векторы одинарной точности, половинной точности, двоичные и разреженные векторы;

  • расчет метрик, перечисленных в таблице ниже.

    Название Синтаксис

    Расстояние L2 (евклидово)

    <->

    Скалярное произведение (внутреннее произведение). Имеет отрицательное значение

    <#>

    Косинусное расстояние

    <=>

    Расстояние L1 (расстояние между точками, имитирующее путь такси по сетке улиц, или Манхэттенское расстояние)

    <+>

    Расстояние Хэмминга (бинарные векторы)

    <~>

    Расстояние Жаккара (бинарные векторы)

    <%>

Расширение pgvector поддерживает следующие типы векторов:

  • vector

  • halfvec

  • bit

  • sparsevec

Для того чтобы протестировать функции pgvector, создайте таблицу со столбцом типа vector. Укажите количество измерений вектора в круглых скобках:

CREATE TABLE test (
    id serial PRIMARY KEY,
    embedding vector(3)
);

Добавьте в таблицу векторные данные:

INSERT INTO test (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'), ('[1,3,8]'), ('[4,4,4]'), ('[3,3,3]'), ('[1,1,3]');

Рассчитайте расстояние L2 между векторами в таблице и вектором [1,1,1] и отсортируйте по этому расстоянию результаты:

SELECT embedding FROM test ORDER BY embedding <-> '[1,1,1]';
 embedding
-----------
 [1,1,3]
 [1,2,3]
 [3,3,3]
 [4,4,4]
 [4,5,6]
 [1,3,8]

Из вывода видно, что ближайший вектор — [1,1,3], а наиболее удаленный — [1,3,8].

Рассчитайте расстояние L1 между векторами в таблице и вектором [1,1,1]:

SELECT embedding FROM test ORDER BY embedding <+> '[1,1,1]';
 embedding
-----------
 [1,1,3]
 [1,2,3]
 [3,3,3]
 [1,3,8]
 [4,4,4]
 [4,5,6]

Вывод отличается: теперь самый удаленный вектор — [4,5,6].

Индексы

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

Поддерживаемые типы индексов:

HNSW

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

Индекс HNSW поддерживает следующие типы векторов:

  • vector — до 2000 измерений (dimensions);

  • halfvec — до 4000 измерений;

  • bit — до 64000 измерений;

  • sparsevec — до 1000 ненулевых элементов.

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

Классы операторов для различных функций расстояния и типов векторов
Функция Vector Halfvec Bit Sparsevec

Расстояние L2

vector_l2_ops

halfvec_l2_ops

 — 

sparsevec_l2_ops

Скалярное произведение

vector_ip_ops

halfvec_ip_ops

 — 

sparsevec_ip_ops

Косинусное расстояние

vector_cosine_ops

halfvec_cosine_ops

 — 

sparsevec_cosine_ops

Расстояние L1

vector_l1_ops

halfvec_l1_ops

 — 

sparsevec_l1_ops

Расстояние Хэмминга

 — 

 — 

bit_hamming_ops

 — 

Расстояние Жаккара

 — 

 — 

bit_jaccard_ops

 — 

Следующий код создает индекс для функции расстояния L2 и типа vector (имя таблицы — table1, имя столбца — embedding):

CREATE INDEX ON table1 USING hnsw (embedding vector_l2_ops);

Код ниже добавляет индекс для функции расстояния Хэмминга и типа bit:

CREATE INDEX ON table2 USING hnsw (embedding bit_hamming_ops);

IVFFlat

Индекс IVFFlat разбивает векторы на "списки" (list) и ищет в подмножестве "списков" те элементы, которые наиболее близки к запрашиваемому вектору. Этот индекс быстрее создается и использует меньше памяти, чем HNSW, но имеет более низкую производительность запросов. Его следует создавать после заполнения таблицы данными.

Индекс IVFFlat поддерживает следующие типы:

  • vector — до 2000 измерений (dimensions);

  • halfvec — до 4000 измерений;

  • bit — до 64000 измерений.

Для поддерживаемых типов векторов используйте те же классы операторов, что и для индекса HNSW.

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

  • для количества строк до 1 миллиона;

  • для более миллиона строк.

Следующий код создает индекс для функции расстояния L2 и типа vector, а также устанавливает параметр lists равным 100:

CREATE INDEX ON table1 USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);

Чтобы определить, сколько списков будет просматриваться в поисках ближайших соседей, укажите параметр probes. Большее значение обеспечивает лучшую полноту поиска, меньшее значение увеличивает скорость. Используйте команду SET, чтобы указать параметр probes:

SET ivfflat.probes = 10;

Примеры

Данные примеры используют эмбеддинги. Чтобы получить их, воспользуйтесь любой моделью для их создания (некоторые из моделей доступны онлайн). В приведенных примерах каждый эмбеддинг имеет 3072 измерения. Для их хранения применяется столбец типа vector(3072).

Расстояние L2

Создайте таблицу со столбцом типа vector:

CREATE TABLE description (
  id serial PRIMARY KEY,
  title text,
  embedding vector(768)
);

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

INSERT INTO description (title, embedding) VALUES
  ('A solution for managing PostgreSQL clusters', '[-0.009550615, -0.012897374, ... 0.011087871]'),
  ('A binary backup management system', '[-0.010036648, 0.011803862, ... 0.036223497]'),
  ('A separate product designed for simple, convenient, and fast software deployment and exploitation', '[0.020033414, -0.001558028, ... -0.00035061908]'),
  ('A tool for backup creation', '[0.011411955, -0.0039920947, ... 0.013054479]'),
  ('Easy cluster installation', '[0.0138596445, 0.0040847682, ... -0.0062671695]'),
  ('A tool to install and manage PostgreSQL clusters', '[-0.004355611, -0.0049024294, ... 0.004386888]');

Полную версию SQL-операторов можно найти в файле description.sql.

Сравните значения title в парах и отсортируйте их по расстоянию L2:

SELECT
    d1.title AS title_d1,
    d2.title AS title_d2,
    d1.embedding <-> d2.embedding AS distance
FROM description d1
JOIN description d2 ON d1.id < d2.id
ORDER BY distance;
                   title_d1                   |                     title_d2                    |      distance
---------------------------------------------+--------------------------------------------------+---------------------
 A solution for managing PostgreSQL clusters | A tool to install and manage PostgreSQL clusters | 0.2973072644418864
 A binary backup management system           | A tool for backup creation                       | 0.4217937355892653
 Easy cluster installation                   | A tool to install and manage PostgreSQL clusters | 0.4695271407723517
 A solution for managing PostgreSQL clusters | Easy cluster installation                        | 0.4905144093027282
 A separate product designed for simple ...  | A tool for backup creation                       | 0.5002226333872557
 A tool for backup creation                  | A tool to install and manage PostgreSQL clusters | 0.5172138587711625
 A separate product designed for simple ...  | Easy cluster installation                        | 0.5173604255461222
 A solution for managing PostgreSQL clusters | A binary backup management system                | 0.5213006656823225
 A binary backup management system           | A separate product designed for simple...        | 0.5278033761319432
 A solution for managing PostgreSQL clusters | A tool for backup creation                       | 0.5297814997533667
 A separate product designed for simple ...  | A tool to install and manage PostgreSQL clusters | 0.5426847414928297
 A binary backup management system           | A tool to install and manage PostgreSQL clusters | 0.5449763171711131
 A solution for managing PostgreSQL clusters | A separate product designed for simple...        | 0.5463234163351685
 A binary backup management system           | Easy cluster installation                        | 0.5631321163341745
 A tool for backup creation                  | Easy cluster installation                        | 0.5684921266921485

Ближайшие по смыслу значения A solution for managing PostgreSQL clusters и A tool to install and manage PostgreSQL clusters.

Скалярное произведение

Создайте новую таблицу и заполните ее данными (значения поля embedding приведены с сокращениями):

CREATE TABLE items (
    id serial PRIMARY KEY,
    item text,
    embedding vector(768)
);

INSERT INTO items (item, embedding) VALUES
  ('cookies', '[-0.002850077, -0.0040400615, ... 0.0043995334]'),
  ('apples', '[0.020033414, -0.001558028, ... 0.01091539]'),
  ('chocolate', '[-0.012762195, 0.023601593, ... -0.010077247]'),
  ('croissant', '[0.011127311, -0.008577864, ... 0.01457113]'),
  ('carrot', '[-0.0061358884, 0.027397538, ... 0.008722801]'),
  ('cheesecake', '[-0.01261848, -0.027980627, ... -0.005194875]'),
  ('flour', '[-0.013135226,  0.011132385, ... 0.00960297]'),
  ('bakery', '[0.00017604351, -0.012099541, ...  0.03317081]');

Полную версию SQL-операторов можно найти в файле items.sql.

Вычислите скалярное произведение между вектором, соответствующим значению bakery, и остальными векторами значений. Умножьте его на -1, поскольку <#> возвращает отрицательное скалярное произведение.

SELECT item,
       (embedding <#> (SELECT embedding FROM items WHERE item = 'bakery')) * -1 AS inner_product
FROM items
ORDER BY inner_product DESC;
    item    |    inner_product
------------+---------------------
 bakery     | 0.33495470881462097
 croissant  | 0.22958263754844666
 flour      | 0.22580863535404205
 cookies    | 0.21599330008029938
 cheesecake |  0.2063595950603485
 chocolate  | 0.20056280493736267
 carrot     | 0.19186848402023315
 apples     | 0.19073499739170074

В данном примере самым релевантным значением bakery является croissant.

Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней