Back to Manticoresearch

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

manual/russian/Searching/KNN.md

25.15.048.5 KB
Original Source

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

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

Для объединения векторного поиска KNN с полнотекстовым поиском для повышения релевантности см. Гибридный поиск.

Что такое эмбеддинг?

Эмбеддинг — это метод представления данных — таких как текст, изображения или звук — в виде векторов в многомерном пространстве. Эти векторы созданы таким образом, чтобы расстояние между ними отражало сходство представляемых ими данных. Этот процесс обычно использует алгоритмы, такие как word embeddings (например, Word2Vec, BERT) для текста или нейронные сети для изображений. Многомерная природа векторного пространства, с множеством компонентов на вектор, позволяет представлять сложные и тонкие взаимосвязи между элементами. Их сходство измеряется расстоянием между этими векторами, часто с использованием методов, таких как евклидово расстояние или косинусное сходство.

Manticore Search позволяет выполнять поиск по векторам методом k-ближайших соседей (KNN) с использованием библиотеки HNSW. Эта функциональность является частью Manticore Columnar Library.

<!-- example KNN -->

Настройка таблицы для поиска KNN

Для выполнения поиска KNN необходимо сначала настроить вашу таблицу. Векторы с плавающей точкой и поиск KNN поддерживаются только в таблицах реального времени (не в обычных таблицах). Таблица должна иметь хотя бы один атрибут типа float_vector, который служит вектором данных. Необходимо указать следующие свойства:

  • knn_type: Обязательная настройка; в настоящее время поддерживается только hnsw.

  • knn_dims: Обязательная настройка, определяющая размерность индексируемых векторов.

  • hnsw_similarity: Обязательная настройка, определяющая функцию расстояния, используемую индексом HNSW. Допустимые значения:

    • L2 - Квадрат L2
    • IP - Скалярное произведение
    • COSINE - Косинусное сходство

    Примечание: При использовании сходства COSINE векторы автоматически нормализуются при вставке. Это означает, что сохраненные значения векторов могут отличаться от исходных входных значений, так как они будут преобразованы в единичные векторы (векторы с математической длиной/величиной 1.0) для обеспечения эффективных вычислений косинусного сходства. Эта нормализация сохраняет направление вектора, стандартизируя его длину.

  • hnsw_m: Необязательная настройка, определяющая максимальное количество исходящих соединений в графе. По умолчанию 16.

  • hnsw_ef_construction: Необязательная настройка, определяющая компромисс между временем построения и точностью. По умолчанию 200.

ПРИМЕЧАНИЕ: Построение графа HNSW во время сохранения RT-чанков, OPTIMIZE TABLE / автоматическое слияние чанков и перестроение KNN через ALTER TABLE ... ADD/DROP/REBUILD по умолчанию выполняется параллельно на многоядерных хостах; количество рабочих процессов контролируется настройкой searchd knn_parallel_build (установите в 1 для принудительного последовательного выполнения). Это влияет только на производительность при построении. Поскольку параллельное построение HNSW может вставлять векторы в другом порядке, результирующий граф может не быть битово идентичным последовательному построению.

<!-- intro -->
SQL
<!-- request SQL -->
sql
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' );
<!-- response SQL -->
sql
Query OK, 0 rows affected (0.01 sec)
<!-- request JSON -->
json
POST /sql?mode=raw -d "create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' )"
<!-- response JSON -->
json
[
  {
    "total": 0,
    "error": "",
    "warning": ""
  }
]
<!-- intro -->
Режим plain (использование файла конфигурации) - Ручные векторы:
<!-- request Config -->
ini
table test_vec {
    type = rt
	...
    rt_attr_float_vector = image_vector
    knn = {"attrs":[{"name":"image_vector","type":"hnsw","dims":4,"hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200}]}
}

Примечание: Для автоматических эмбеддингов в режиме plain см. пример ниже, который показывает, как использовать параметры model_name и from в конфигурации knn.

<!-- end --> <!-- example knn_insert -->

Вставка векторных данных

Автоматические эмбеддинги (Рекомендуется)

Самый простой способ работы с векторными данными — использование автоматических эмбеддингов. С этой функцией вы создаете таблицу с параметрами MODEL_NAME и FROM, а затем просто вставляете свои текстовые данные — Manticore автоматически генерирует эмбеддинги для вас.

Создание таблицы с автоматическими эмбеддингами

При создании таблицы для автоматических эмбеддингов укажите:

  • MODEL_NAME: Модель эмбеддингов для использования
  • FROM: Какие поля использовать для генерации эмбеддингов (пустое значение означает все текстовые/строковые поля)
  • API_KEY: Требуется для удаленных моделей (OpenAI, Voyage, Jina). API-ключ проверяется при создании таблицы путем выполнения реального API запроса.
  • API_URL: Необязательно. Пользовательский URL конечной точки API. Если не указан, используется конечная точка провайдера по умолчанию (например, https://api.openai.com/v1/embeddings для OpenAI).
  • API_TIMEOUT: Необязательно. HTTP-таймаут в секундах для API запросов. По умолчанию 10 секунд. Установите в '0' для использования таймаута по умолчанию. Применяется как к запросам валидации при создании таблицы, так и к генерации эмбеддингов во время операций INSERT.

Поддерживаемые модели эмбеддингов:

Тип моделиПримерТребуется API ключПримечания
Sentence Transformerssentence-transformers/all-MiniLM-L6-v2НетЛокальные модели на основе BERT, автоматически загружаются
QwenQwen/Qwen3-Embedding-0.6BНетЛокальные модели семейства Qwen
LlamaTinyLlama/TinyLlama-1.1B-Chat-v1.0НетЛокальные модели семейства Llama
MistralLocutusque/TinyMistral-248M-v2НетЛокальные модели семейства Mistral
Gemmah2oai/embeddinggemma-300mНетЛокальные модели семейства Gemma
OpenAIopenai/text-embedding-ada-002ДаAPI_KEY='<OPENAI_API_KEY>'
VoyageМодели Voyage AIДаAPI_KEY='<VOYAGE_API_KEY>'
JinaМодели Jina AIДаAPI_KEY='<JINA_API_KEY>'

Требования к формату локальных моделей:

  • Должны быть сохранены в формате safetensors (только однофайловый)
  • Поддерживаемые семейства: Qwen, Llama, Mistral, Gemma
  • Протестированные модели: TinyLlama/TinyLlama-1.1B-Chat-v1.0, Locutusque/TinyMistral-248M-v2, Qwen/Qwen3-Embedding-0.6B, h2oai/embeddinggemma-300m
  • Другие модели safetensors также могут работать, но не гарантируется

Дополнительную информацию о настройке атрибута float_vector можно найти здесь.

<!-- intro -->
SQL:
<!-- request SQL -->

Использование sentence-transformers (ключ API не требуется)

sql
CREATE TABLE products (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM='title'
);

Использование локальных эмбеддингов Qwen (ключ API не требуется)

sql
CREATE TABLE products_qwen (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='Qwen/Qwen3-Embedding-0.6B' FROM='title' CACHE_PATH='/opt/homebrew/var/manticore/.cache/manticore'
);

Использование OpenAI (требуется параметр API_KEY)

sql
CREATE TABLE products_openai (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='openai/text-embedding-ada-002' FROM='title,description' API_KEY='...'
);

Использование OpenAI с пользовательским URL API и таймаутом (опционально)

sql
CREATE TABLE products_openai_custom (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='openai/text-embedding-ada-002' FROM='title,description'
    API_KEY='...' API_URL='https://custom-api.example.com/v1/embeddings' API_TIMEOUT='30'
);

Использование всех текстовых полей для эмбеддингов (FROM пуст)

sql
CREATE TABLE products_all (
    title TEXT,
    description TEXT,
    embedding_vector FLOAT_VECTOR KNN_TYPE='hnsw' HNSW_SIMILARITY='l2'
    MODEL_NAME='sentence-transformers/all-MiniLM-L6-v2' FROM=''
);
<!-- intro -->
Режим plain (с использованием файла конфигурации):
<!-- request Config -->
ini
table products {
    type = rt
    path = /path/to/products
    rt_field = title
    rt_field = description
    rt_attr_float_vector = embedding_vector
    knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"sentence-transformers/all-MiniLM-L6-v2","from":"title"}]}
}

Использование OpenAI с ключом API в режиме plain:

ini
table products_openai {
    type = rt
    path = /path/to/products_openai
    rt_field = title
    rt_field = description
    rt_attr_float_vector = embedding_vector
    knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"openai/text-embedding-ada-002","from":"title,description","api_key":"your-api-key-here"}]}
}

Использование всех текстовых полей (пустой FROM):

ini
table products_all {
    type = rt
    path = /path/to/products_all
    rt_field = title
    rt_field = description
    rt_attr_float_vector = embedding_vector
    knn = {"attrs":[{"name":"embedding_vector","type":"hnsw","hnsw_similarity":"L2","hnsw_m":16,"hnsw_ef_construction":200,"model_name":"sentence-transformers/all-MiniLM-L6-v2","from":""}]}
}

Важные замечания для режима plain:

  • При использовании model_name вы не должны указывать dims — модель автоматически определяет размерность вектора. Параметры dims и model_name являются взаимоисключающими.
  • Когда вы не используете model_name (ручная вставка вектора), вы должны указать dims, чтобы обозначить размерность вектора.
  • Параметр from указывает, какие поля использовать для генерации эмбеддингов (список, разделенный запятыми, или пустая строка для всех текстовых/строковых полей). Этот параметр обязателен при использовании model_name.
  • Для API-моделей (OpenAI, Voyage, Jina) включите параметр api_key в конфигурацию knn
<!-- end -->
Вставка данных с автоматическими эмбеддингами
<!-- data for the following example: DROP TABLE IF EXISTS products; CREATE TABLE products(title text, embedding_vector float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title'); DROP TABLE IF EXISTS products_openai; CREATE TABLE products_openai(title text, description text, embedding_vector float_vector knn_type='hnsw' hnsw_similarity='l2' model_name='sentence-transformers/all-MiniLM-L6-v2' from='title,description'); --> <!-- example inserting_embeddings -->

При использовании автоматических эмбеддингов вы можете:

  • Пропустить поле вектора и позволить Manticore сгенерировать эмбеддинги из полей, перечисленных в FROM
  • Предоставить свой собственный вектор явно для строки
  • Указать (), чтобы пропустить генерацию и сохранить вектор, состоящий из нулей

Если позже вы запустите ALTER TABLE ... REBUILD EMBEDDINGS, строки, которые в данный момент содержат нулевые векторы из (), также будут перегенерированы.

<!-- intro -->
SQL:
<!-- request SQL -->

Вставить только текстовые данные — эмбеддинги генерируются автоматически

sql
INSERT INTO products (title) VALUES
('machine learning artificial intelligence'),
('banana fruit sweet yellow');

Вставить пользовательский вектор

sql
INSERT INTO products (title, embedding_vector) VALUES
('machine learning artificial intelligence', (0.653448,0.192478,0.017971,0.339821));

Вставить несколько полей — оба используются для эмбеддинга, если FROM='title,description'

sql
INSERT INTO products_openai (title, description) VALUES
('smartphone', 'latest mobile device with advanced features'),
('laptop', 'portable computer for work and gaming');

Вставить пустой вектор (без автоматической генерации; сохраняет нулевой вектор)

sql
INSERT INTO products (title, embedding_vector) VALUES
('no embedding item', ());
<!-- intro -->
JSON:
<!-- request JSON -->

Вставить только текстовые данные — эмбеддинги генерируются автоматически

JSON
POST /sql?mode=raw -d "INSERT INTO products (title) VALUES ('machine learning artificial intelligence'),('banana fruit sweet yellow')"

Вставить несколько полей — оба используются для эмбеддинга, если FROM='title,description'

JSON
POST /sql?mode=raw -d "INSERT INTO products_openai (title, description) VALUES ('smartphone', 'latest mobile device with advanced features'), ('laptop', 'portable computer for work and gaming')"

Вставить пустой вектор (документ исключен из векторного поиска)

JSON
POST /sql?mode=raw -d "INSERT INTO products (title, embedding_vector) VALUES ('no embedding item', ())"
<!-- end -->
Поиск с автоматическими эмбеддингами
<!-- example embeddings_search -->

Поиск работает таким же образом — предоставьте текст запроса, и Manticore сгенерирует эмбеддинги и найдет похожие документы:

<!-- intro -->
SQL:
<!-- request SQL -->
sql
SELECT id, knn_dist() FROM products WHERE knn(embedding_vector, 'machine learning');
<!-- response SQL -->
sql
+------+------------+
| id   | knn_dist() |
+------+------------+
|    1 | 0.12345678 |
|    2 | 0.87654321 |
+------+------------+
2 rows in set (0.00 sec)
<!-- intro -->
JSON:
<!-- request JSON -->

Использование текстового запроса с автоматическими эмбеддингами

json
POST /search
{
    "table": "products",
    "knn": {
        "field": "embedding_vector",
        "query": "machine learning"
    }
}

Использование векторного запроса напрямую

json
POST /search
{
    "table": "products",
    "knn": {
        "field": "embedding_vector",
        "query": [0.1, 0.2, 0.3, 0.4]
    }
}
<!-- response JSON -->
json
{
    "took": 0,
    "timed_out": false,
    "hits": {
        "total": 2,
        "total_relation": "eq",
        "hits": [
            {
                "_id": 1,
                "_score": 1,
                "_knn_dist": 0.12345678,
                "_source": {
                    "title": "machine learning artificial intelligence"
                }
            },
            {
                "_id": 2,
                "_score": 1,
                "_knn_dist": 0.87654321,
                "_source": {
                    "title": "banana fruit sweet yellow"
                }
            }
        ]
    }
}
<!-- end -->

Ручная вставка вектора

<!-- example manual_vector -->

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

Важно: При использовании hnsw_similarity='cosine' векторы автоматически нормализуются при вставке в единичные векторы (векторы с математической длиной/величиной 1.0). Эта нормализация сохраняет направление вектора, стандартизируя его длину, что необходимо для эффективных вычислений косинусного сходства. Это означает, что сохраненные значения будут отличаться от ваших исходных входных значений.

<!-- intro -->
SQL:
<!-- request SQL -->
sql
insert into test values ( 1, 'yellow bag', (0.653448,0.192478,0.017971,0.339821) ), ( 2, 'white bag', (-0.148894,0.748278,0.091892,-0.095406) );
<!-- response SQL -->
sql
Query OK, 2 rows affected (0.00 sec)
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /insert
{
	"table":"test_vec",
	"id":1,
	"doc": 	{ "title" : "yellow bag", "image_vector" : [0.653448,0.192478,0.017971,0.339821] }
}

POST /insert
{
	"table":"test_vec",
	"id":2,
	"doc": 	{ "title" : "white bag", "image_vector" : [-0.148894,0.748278,0.091892,-0.095406] }
}
<!-- response JSON -->
json
{
	"table":"test",
	"_id":1,
	"created":true,
	"result":"created",
	"status":201
}

{
	"table":"test",
	"_id":2,
	"created":true,
	"result":"created",
	"status":201
}
<!-- end --> <!-- example knn_search -->

KNN векторный поиск

Теперь вы можете выполнить KNN-поиск, используя предложение knn в формате SQL или JSON. Оба интерфейса поддерживают одинаковые основные параметры, обеспечивая согласованный опыт независимо от выбранного формата:

  • SQL: select ... from <table name> where knn ( <field>, <query vector> [,<options>] )
  • JSON:
    POST /search
    {
        "table": "<table name>",
        "knn":
        {
            "field": "<field>",
            "query": "<text or vector>",
            "ef": <ef>,
      	  "rescore": <rescore>,
      	  "oversampling": <oversampling>
        }
    }
    

Параметры:

  • field: Это имя атрибута вектора с плавающей точкой, содержащего векторные данные.
  • k: Устаревшая опция. Используйте параметр запроса limit вместо этого. Ранее использовалась для указания количества документов, которые должен вернуть один индекс HNSW. Однако фактическое количество документов, включенных в окончательные результаты, может отличаться. Например, если система работает с таблицами реального времени, разделенными на дисковые чанки, каждый чанк может вернуть k документов, что приведет к общему количеству, превышающему указанное k (так как совокупное количество будет num_chunks * k). С другой стороны, окончательное количество документов может быть меньше k, если после запроса k документов некоторые из них будут отфильтрованы на основе определенных атрибутов. Важно отметить, что параметр k не применяется к оперативным чанкам (ramchunks). В контексте оперативных чанков процесс извлечения работает иначе, и поэтому влияние параметра k на количество возвращаемых документов неприменимо.
  • query: (Рекомендуемый параметр) Поисковый запрос, который может быть:
    • Текстовой строкой: Автоматически преобразуется в эмбеддинги, если для поля настроено автоматическое создание эмбеддингов. Вернет ошибку, если у поля нет автоматических эмбеддингов.
    • Массивом векторов: Работает так же, как query_vector.
  • query_vector: (Устаревший параметр) Поисковый вектор в виде массива чисел. Все еще поддерживается для обратной совместимости. Примечание: Используйте либо query, либо query_vector, но не оба в одном запросе.
  • ef: необязательный размер динамического списка, используемого во время поиска. Более высокое значение ef приводит к более точному, но более медленному поиску. По умолчанию равно 10.
  • rescore: Включает повторное ранжирование KNN (включено по умолчанию). Установите в 0 в SQL или false в JSON, чтобы отключить повторное ранжирование. После завершения поиска KNN с использованием квантованных векторов (с возможным передискретизацией) расстояния пересчитываются с исходными (полноразрядными) векторами, и результаты пересортировываются для повышения точности ранжирования.
  • oversampling: Устанавливает коэффициент (значение с плавающей точкой), на который умножается k при выполнении поиска KNN, что приводит к извлечению большего количества кандидатов, чем требуется, с использованием квантованных векторов. По умолчанию применяется oversampling=3.0. Эти кандидаты могут быть позже переоценены, если повторное ранжирование включено. Передискретизация также работает с неквантованными векторами. Поскольку она увеличивает k, что влияет на работу индекса HNSW, это может вызвать небольшое изменение точности результатов.
  • early_termination: Включает или отключает адаптивное досрочное завершение во время обхода графа HNSW. Включено по умолчанию. Установите в 0 в SQL или false в JSON, чтобы отключить. Подробности см. в разделе Досрочное завершение.

Документы всегда сортируются по расстоянию до поискового вектора. Любые дополнительные критерии сортировки, которые вы укажете, будут применены после этого основного условия сортировки. Для получения расстояния существует встроенная функция knn_dist().

<!-- intro -->
SQL:
<!-- request SQL -->
sql
select id, knn_dist() from test where knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=2000, oversampling=3.0, rescore=1 } );
<!-- response SQL -->
sql
+------+------------+
| id   | knn_dist() |
+------+------------+
|    1 | 0.28146550 |
|    2 | 0.81527930 |
+------+------------+
2 rows in set (0.00 sec)
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /search
{
	"table": "test",
	"knn":
	{
		"field": "image_vector",
		"query": [0.286569,-0.031816,0.066684,0.032926],
		"ef": 2000,
		"rescore": true,
		"oversampling": 3.0
	}
}
<!-- response JSON -->
json
{
	"took":0,
	"timed_out":false,
	"hits":
	{
		"total":2,
		"total_relation":"eq",
		"hits":
		[
			{
				"_id": 1,
				"_score":1,
				"_knn_dist":0.28146550,
				"_source":
				{
					"title":"yellow bag",
					"image_vector":[0.653448,0.192478,0.017971,0.339821]
				}
			},
			{
				"_id": 2,
				"_score":1,
				"_knn_dist":0.81527930,
				"_source":
				{
					"title":"white bag",
					"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
				}
			}
		]
	}
}
<!-- end --> <!-- example knn_quantization -->

Квантование векторов

Индексы HNSW необходимо полностью загружать в память для выполнения поиска KNN, что может привести к значительному потреблению памяти. Для уменьшения использования памяти может применяться скалярное квантование — техника, которая сжимает многомерные векторы, представляя каждый компонент (измерение) ограниченным количеством дискретных значений. Manticore поддерживает 8-битное и 1-битное квантование, что означает, что каждый компонент вектора сжимается с 32-битного числа с плавающей точкой до 8 бит или даже 1 бита, уменьшая использование памяти в 4 или 32 раза соответственно. Эти сжатые представления также позволяют выполнять более быстрые вычисления расстояний, так как больше компонентов вектора может быть обработано за одну SIMD инструкцию. Хотя скалярное квантование вносит некоторую ошибку аппроксимации, это часто является оправданным компромиссом между точностью поиска и эффективностью использования ресурсов. Для еще большей точности квантование можно комбинировать с повторным ранжированием и передискретизацией: извлекается больше кандидатов, чем запрошено, и расстояния для этих кандидатов пересчитываются с использованием исходных 32-битных векторов с плавающей точкой.

Поддерживаемые типы квантования включают:

  • 8bit: Каждый компонент вектора квантуется до 8 бит.
  • 1bit: Каждый компонент вектора квантуется до 1 бита. Используется асимметричное квантование, при котором векторы запросов квантуются до 4 бит, а хранимые векторы — до 1 бита. Этот подход обеспечивает большую точность, чем более простые методы, хотя и с некоторым компромиссом по производительности.
  • 1bitsimple: Каждый компонент вектора квантуется до 1 бита. Этот метод быстрее, чем 1bit, но обычно менее точен.
<!-- intro -->
SQL:
<!-- request SQL -->
sql
create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit');
<!-- response SQL -->
sql
Query OK, 0 rows affected (0.01 sec)
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /sql?mode=raw -d "create table test ( title text, image_vector float_vector knn_type='hnsw' knn_dims='4' hnsw_similarity='l2' quantization='1bit')"
<!-- response JSON -->
json
[
  {
    "total": 0,
    "error": "",
    "warning": ""
  }
]
<!-- end --> <!-- Example knn_similar_docs -->

Поиск похожих документов по id

ПРИМЕЧАНИЕ: Поиск похожих документов по id требует наличия Manticore Buddy. Если это не работает, убедитесь, что Buddy установлен.

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

  • SQL: select ... from <table name> where knn ( <field>, <k>, <document id> )
  • JSON:
    POST /search
    {
        "table": "<table name>",
        "knn":
        {
            "field": "<field>",
            "doc_id": <document id>,
            "k": <k>
        }
    }
    

Параметры:

  • field: Это имя атрибута вектора с плавающей точкой, содержащего векторные данные.
  • k: Это количество документов для возврата и ключевой параметр для индексов Hierarchical Navigable Small World (HNSW). Он определяет количество документов, которое должен вернуть один индекс HNSW. Однако фактическое количество документов, включенных в окончательные результаты, может варьироваться. Например, если система работает с таблицами реального времени, разделенными на дисковые чанки, каждый чанк может вернуть k документов, что приводит к общему количеству, превышающему указанное k (так как совокупное количество будет num_chunks * k). С другой стороны, окончательное количество документов может быть меньше k, если после запроса k документов некоторые из них отфильтровываются на основе определенных атрибутов. Важно отметить, что параметр k не применяется к ramchunks. В контексте ramchunks процесс извлечения работает по-другому, и поэтому влияние параметра k на количество возвращаемых документов не применимо.
  • document id: Идентификатор документа для поиска сходства KNN.
<!-- intro -->
SQL:
<!-- request SQL -->
sql
select id, knn_dist() from test where knn ( image_vector, 5, 1 );
<!-- response SQL -->
sql
+------+------------+
| id   | knn_dist() |
+------+------------+
|    2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec)
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /search
{
  "table": "test",
  "knn":
  {
    "field": "image_vector",
    "doc_id": 1,
    "k": 5
  }
}
<!-- response JSON -->
json
{
	"took":0,
	"timed_out":false,
	"hits":
	{
		"total":1,
		"total_relation":"eq",
		"hits":
		[
			{
				"_id": 2,
				"_score":1643,
				"_knn_dist":0.81527930,
				"_source":
				{
					"title":"white bag",
					"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
				}
			}
		]
	}
}
<!-- end --> <!-- Example knn_filtering -->

Фильтрация результатов векторного поиска KNN

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

<!-- intro -->
SQL:
<!-- request SQL -->
sql
select id, knn_dist() from test where knn ( image_vector, 5, (0.286569,-0.031816,0.066684,0.032926) ) and match('white') and id < 10;
<!-- response SQL -->
sql
+------+------------+
| id   | knn_dist() |
+------+------------+
|    2 | 0.81527930 |
+------+------------+
1 row in set (0.00 sec)
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /search
{
	"table": "test",
	"knn":
	{
		"field": "image_vector",
		"query": [0.286569,-0.031816,0.066684,0.032926],
		"k": 5
	},
	"query":
	{
		"bool":
		{
			"must":
			[
				{ "match": {"_all":"white"} },
				{ "range": { "id": { "lt": 10 } } }
			]
		}
	}
}
<!-- response JSON -->
json
{
	"took":0,
	"timed_out":false,
	"hits":
	{
		"total":1,
		"total_relation":"eq",
		"hits":
		[
			{
				"_id": 2,
				"_score":1643,
				"_knn_dist":0.81527930,
				"_source":
				{
					"title":"white bag",
					"image_vector":[-0.148894,0.748278,0.091892,-0.095406]
				}
			}
		]
	}
}
<!-- end --> <!-- example knn_filtering_strategies -->

Стратегии фильтрации: предварительная фильтрация vs. последующая фильтрация

При объединении векторного поиска KNN с фильтрами атрибутов Manticore поддерживает две стратегии, которые отличаются тем, когда фильтр применяется относительно обхода графа HNSW.

  • Предварительная фильтрация (по умолчанию; prefilter=1 (SQL) или "prefilter": true (JSON, по умолчанию)) передает фильтр в сам обход HNSW. Каждый кандидат проверяется фильтром перед добавлением в кучу результатов - только соответствующие документы вносят вклад в окончательные k результатов. Это уменьшает бесполезные вычисления расстояний и гарантирует возврат ровно k соответствующих документов (при условии, что существует k соответствующих документов).

  • Последующая фильтрация (prefilter=0 (SQL) или "prefilter": false (JSON)) сначала выполняет поиск KNN по всему набору данных, а затем применяет фильтр к результатам. Это безопасно и предсказуемо: граф HNSW обходится без помех, и фильтр влияет только на то, какие результаты возвращаются клиенту. Недостаток в том, что граф может тратить усилия на кандидатов, которые в конечном итоге будут отброшены. При строгом фильтре, который соответствует только небольшой части документов, возвращаемых k результатов может быть значительно меньше запрошенного, потому что большинство кандидатов KNN не проходят фильтр.

Внутренне Manticore использует алгоритм на основе ACORN-1 для предварительной фильтрации. Наивная предварительная фильтрация просто пропускала бы несоответствующие узлы, что рискует потерей "мостовых" узлов, соединяющих иначе разделенные части графа HNSW, вызывая коллапс полноты при увеличении избирательности фильтра. ACORN-1 избегает этого: когда узел не проходит фильтр, его соседи все равно добавляются в очередь исследования. Это позволяет обходу обходить отфильтрованные узлы и поддерживать связность графа. Исследование ACORN-1 активируется автоматически, когда менее 60% от общего количества документов проходят фильтр.

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

<!-- intro -->
SQL:
<!-- request SQL -->
sql
-- prefilter (default): filter applied during HNSW traversal (ACORN-1 used automatically)
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926) )
AND price < 100;

-- postfilter: KNN runs over full dataset, filter applied to results
SELECT id, knn_dist() FROM test
WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { prefilter=0 } )
AND price < 100;
<!-- intro -->
JSON:
<!-- request JSON -->
json
// prefilter (default): filter applied during HNSW traversal
POST /search
{
    "table": "test",
    "knn": {
        "field": "image_vector",
        "query": [0.286569,-0.031816,0.066684,0.032926]
    },
    "query": {
        "range": { "price": { "lt": 100 } }
    }
}

// postfilter: filter applied after KNN search
POST /search
{
    "table": "test",
    "knn": {
        "field": "image_vector",
        "query": [0.286569,-0.031816,0.066684,0.032926],
        "prefilter": false
    },
    "query": {
        "range": { "price": { "lt": 100 } }
    }
}
<!-- end -->

Досрочное завершение

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

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

Обратите внимание, что передискретизация умножает эффективное k, используемое во время обхода HNSW, поэтому досрочное завершение также выигрывает от передискретизации: более высокое эффективное k означает больше кандидатов, которых можно потенциально пропустить.

<!-- example knn_early_termination -->

Для явного управления досрочным завершением используйте опцию early_termination:

<!-- intro -->
SQL:
<!-- request SQL -->
sql
-- disable early termination
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=0 } );

-- enable early termination explicitly (default)
SELECT id, knn_dist() FROM test WHERE knn ( image_vector, (0.286569,-0.031816,0.066684,0.032926), { ef=200, early_termination=1 } );
<!-- intro -->
JSON:
<!-- request JSON -->
json
POST /search
{
    "table": "test",
    "knn":
    {
        "field": "image_vector",
        "query": [0.286569,-0.031816,0.066684,0.032926],
        "ef": 200,
        "early_termination": false
    }
}
<!-- end -->

Когда отключать досрочное завершение:

  • Когда точность набора результатов критически важна и вы не можете позволить себе никакого приближения сверх того, что уже предоставляет HNSW.
  • При использовании низких значений k (около 30 или меньше), где досрочное завершение дает мало преимуществ в производительности, но может снизить точность.
<!-- proofread -->