Точный и приближенный векторный поиск
Проблема нахождения N ближайших точек в многомерном (векторном) пространстве для заданной точки известна как поиск ближайших соседей или, сокращенно: векторный поиск. Существуют два основных подхода для решения векторного поиска:
- Точный векторный поиск вычисляет расстояние между заданной точкой и всеми точками в векторном пространстве. Это обеспечивает наилучшую возможную точность, т.е. возвращаемые точки гарантированно являются фактическими ближайшими соседями. Поскольку векторное пространство исследуется исчерпывающе, точный векторный поиск может быть слишком медленным для практического использования.
- Приближенный векторный поиск относится к группе техник (например, специальные структуры данных, такие как графы и случайные леса), которые вычисляют результаты гораздо быстрее, чем точный векторный поиск. Точность результата обычно "достаточно хороша" для практического использования. Многие приближенные техники предоставляют параметры для настройки компромисса между точностью результата и временем поиска.
Векторный поиск (точный или приближенный) можно записать на SQL следующим образом:
Точки в векторном пространстве хранятся в колонке vectors
типа массива, например, Array(Float64), Array(Float32) или Array(BFloat16). Ссылающийся вектор представляет собой константный массив и передается в виде общего табличного выражения. <DistanceFunction>
вычисляет расстояние между ссылочной точкой и всеми сохранёнными точками. Любая из доступных функций расстояния может быть использована для этого. <N>
указывает, сколько соседей должно быть возвращено.
Точный векторный поиск
Точный векторный поиск может быть выполнен с помощью приведенного выше запроса SELECT как есть. Время выполнения таких запросов обычно пропорционально числу хранимых векторов и их размерности, т.е. количеству элементов массива. Кроме того, поскольку ClickHouse выполняет переборное сканирование всех векторов, время выполнения также зависит от числа потоков, используемых в запросе (см. настройку max_threads).
Пример
возвращает
Приближенный векторный поиск
Индексы сходства векторов
ClickHouse предоставляет специальный индекс "сходства векторов" для выполнения приближенного векторного поиска.
Индексы сходства векторов доступны в ClickHouse версии 25.8 и выше. Если вы столкнетесь с проблемами, пожалуйста, откройте вопрос в репозитории ClickHouse.
Создание индекса сходства векторов
Индекс сходства векторов может быть создан на новой таблице следующим образом:
В качестве альтернативы, чтобы добавить индекс сходства векторов к существующей таблице:
Индексы сходства векторов — это специальные виды индексов пропуска (см. здесь и здесь). Соответственно, приведенное выше утверждение ALTER TABLE
приводит только к тому, что индекс создается для будущих новых данных, вставленных в таблицу. Чтобы создать индекс для существующих данных, вам нужно его материализовать:
Функция <distance_function>
должна быть
L2Distance
, евклидово расстояние, представляющее длину линии между двумя точками в евклидном пространстве, илиcosineDistance
, косинусное расстояние, представляющее угол между двумя ненулевыми векторами.
Для нормализованных данных L2Distance
обычно является наилучшим выбором, в противном случае рекомендуется использовать cosineDistance
для компенсации масштаба.
<dimensions>
указывает кардинальность массива (количество элементов) в underlying column. Если ClickHouse находит массив с другой кардинальностью при создании индекса, индексdiscarded и возвращается ошибка.
Необязательный параметр GRANULARITY <N>
указывает размер гранул индекса (см. здесь). Значение по умолчанию в 100 миллионов должно работать вполне удовлетворительно для большинства случаев использования, но его также можно настроить. Мы рекомендуем настраивать только для опытных пользователей, которые понимают последствия своих действий (см. ниже).
Индексы сходства векторов являются универсальными в том смысле, что они могут адаптироваться к различным методам приближенного поиска. Используемый метод указывается параметром <type>
. На данный момент единственный доступный метод — HNSW (научная статья), популярная и передовая техника для приближенного векторного поиска на основе иерархических графов близости. Если HNSW используется в качестве типа, пользователи могут дополнительно указать определенные параметры, специфичные для HNSW:
Эти параметры, специфичные для HNSW, доступны:
<quantization>
контролирует квантизацию векторов в графе близости. Возможные значения:f64
,f32
,f16
,bf16
,i8
илиb1
. Значение по умолчанию —bf16
. Обратите внимание, что этот параметр не влияет на представление векторов в underlying column.<hnsw_max_connections_per_layer>
контролирует количество соседей для каждого узла графа, также известный как гиперпараметр HNSWM
. Значение по умолчанию —32
. Значение0
означает использование значения по умолчанию.<hnsw_candidate_list_size_for_construction>
контролирует размер динамического списка кандидатов во время построения графа HNSW, также известный как гиперпараметр HNSWef_construction
. Значение по умолчанию —128
. Значение0
означает использование значения по умолчанию.
Значения по умолчанию всех параметров, специфичных для HNSW, вполне хорошо работают в большинстве случаев использования. Поэтому мы не рекомендуем настраивать параметры, специфичные для HNSW.
Дополнительные ограничения:
- Индексы сходства векторов могут быть построены только на колонках типа Array(Float32), Array(Float64) или Array(BFloat16). Массивы с nullable и low-cardinality float, такие как
Array(Nullable(Float32))
иArray(LowCardinality(Float32))
, не допускаются. - Индексы сходства векторов должны быть построены на одиночных колонках.
- Индексы сходства векторов могут быть построены на вычисленных выражениях (например,
INDEX index_name arraySort(vectors) TYPE vector_similarity([...])
), но такие индексы не могут быть использованы для приближенного поиска соседей позже. - Индексы сходства векторов требуют, чтобы все массивы в underlying column имели
<dimension>
-много элементов — это проверяется во время создания индекса. Чтобы как можно скорее выявить нарушения этого требования, пользователи могут добавить ограничение для векторной колонки, например,CONSTRAINT same_length CHECK length(vectors) = 256
. - Точно так же значения массива в underlying column не должны быть пустыми (
[]
) или иметь значение по умолчанию (также[]
).
Оценка потребления хранилища и памяти
Вектор, созданный для использования с типичной моделью ИИ (например, большая языковая модель, LLMs), состоит из сотен или тысяч значений с плавающей точкой. Таким образом, одно значение вектора может занимать несколько килобайт памяти. Пользователи, которые хотят оценить хранилище, необходимое для underlying vector column в таблице, а также основную память, необходимую для индекса сходства векторов, могут использовать два приведенных ниже формула:
Потребление хранилища векторной колонки в таблице (несжатое):
Пример для данных dbpedia:
Индекс сходства векторов должен быть полностью загружен с диска в основную память для выполнения поисков. Аналогично, индекс векторов также полностью строится в памяти, а затем сохраняется на диск.
Потребление памяти, необходимое для загрузки векторного индекса:
Пример для данных dbpedia:
Вышеуказанная формула не учитывает дополнительную память, необходимую индексам сходства векторов для выделения структур данных времени выполнения, таких как заранее выделенные буферы и кэши.
Использование индекса сходства векторов
Для использования индексов сходства векторов значение настройки compatibility должно быть ''
(значение по умолчанию) или '25.1'
или новее.
Индексы сходства векторов поддерживают запросы SELECT следующего вида:
Оптимизатор запросов ClickHouse пытается сопоставить вышеуказанный шаблон запроса и использовать доступные индексы сходства векторов. Запрос может использовать индекс сходства векторов, только если функция расстояния в запросе SELECT совпадает с функцией расстояния в определении индекса.
Опытные пользователи могут предоставить пользовательское значение для настройки hnsw_candidate_list_size_for_search (также известный как гиперпараметр HNSW "ef_search"), чтобы настроить размер списка кандидатов во время поиска (например, SELECT [...] SETTINGS hnsw_candidate_list_size_for_search = <value>
). Значение по умолчанию для настройки 256 хорошо работает в большинстве случаев использования. Более высокие значения настройки означают лучшую точность за счет более медленной производительности.
Если запрос может использовать индекс сходства векторов, ClickHouse проверяет, что LIMIT <N>
, указанный в запросах SELECT, находится в разумных пределах. Более конкретно, возвращается ошибка, если <N>
больше значения настройки max_limit_for_vector_search_queries со значением по умолчанию 100. Слишком большие значения LIMIT могут замедлить поиски и обычно указывают на ошибку в использовании.
Чтобы проверить, использует ли запрос SELECT индекс сходства векторов, вы можете префиксировать запрос с помощью EXPLAIN indexes = 1
.
В качестве примера, запрос
может вернуть
В этом примере 1 миллион векторов в данных dbpedia, каждый с размерностью 1536, хранятся в 575 гранулах, т.е. 1,7 тыс. строк на гранулу. Запрос запрашивает 10 соседей, и индекс сходства векторов находит эти 10 соседей в 10 отдельных гранулах. Эти 10 гранул будут прочитаны во время выполнения запроса.
Индексы сходства векторов используются, если вывод содержит Skip
и имя и тип векторного индекса (в примере, idx
и vector_similarity
). В этом случае индекс сходства векторов отбросил две из четырех гранул, т.е. 50% данных. Чем больше гранул может быть отброшено, тем более эффективным становится использование индекса.
Чтобы заставить индекс использоваться, вы можете выполнить запрос SELECT с настройкой force_data_skipping_indexes (укажите имя индекса в качестве значения настройки).
Постфильтрация и предварительная фильтрация
Пользователи могут дополнительно указать условие WHERE
с дополнительными фильтрующими условиями для запроса SELECT. ClickHouse будет оценивать эти фильтрующие условия, используя стратегию постфильтрации или предварительной фильтрации. Кратко говоря, обе стратегии определяют порядок, в котором фильтры оцениваются:
- Постфильтрация означает, что индекс сходства векторов оценивается сначала, а затем ClickHouse оценивает дополнительные фильтры, указанные в условии
WHERE
. - Предварительная фильтрация означает, что порядок оценки фильтров обратный.
Стратегии имеют различные компромиссы:
- Постфильтрация имеет общую проблему, что она может вернуть меньшее количество строк, чем запрашивается в условии
LIMIT <N>
. Эта ситуация возникает, когда одна или несколько строк результатов, возвращаемых индексом сходства векторов, не удовлетворяют дополнительные фильтры. - Предварительная фильтрация является общим нерешенным вопросом. Некоторые специализированные векторные базы данных предоставляют алгоритмы предварительной фильтрации, но большинство реляционных баз данных (включая ClickHouse) откатятся к точному поиску соседей, т.е. переборному сканированию без индекса.
Какая стратегия используется, зависит от условия фильтрации.
Дополнительные фильтры являются частью ключа партиции
Если дополнительное условие фильтрации является частью ключа партиции, то ClickHouse применит обрезку партиции. Например, таблица разбита на диапазоны по колонке year
, и выполняется следующий запрос:
ClickHouse обрежет все партиции, кроме 2025 года.
Дополнительные фильтры не могут быть оценены с помощью индексов
Если дополнительные условия фильтрации не могут быть оценены с помощью индексов (индекс первичного ключа, индекс пропуска), ClickHouse применит постфильтрацию.
Дополнительные фильтры могут быть оценены с помощью индекса первичного ключа
Если дополнительные условия фильтрации могут быть оценены с помощью первичного ключа (т.е. они образуют префикс первичного ключа), и
- условие фильтрации исключает хотя бы одну строку в пределах части, ClickHouse откатится к предварительной фильтрации для "выживших" диапазонов в пределах части,
- условие фильтрации не исключает никаких строк в пределах части, ClickHouse выполнит постфильтрацию для части.
В практических случаях скорее маловероятно, что сработает последний случай.
Дополнительные фильтры могут быть оценены с помощью индекса пропуска
Если дополнительные условия фильтрации могут быть оценены с помощью индексов пропуска (индекс minmax, индекс множеств и т.д.), ClickHouse выполняет постфильтрацию. В таких случаях сначала оценивается индекс сходства векторов, поскольку ожидается, что он удалит больше строк по сравнению с другими индексами пропуска.
Для более тонкого контроля над постфильтрацией и предварительной фильтрацией могут быть использованы две настройки:
Настройка vector_search_filter_strategy (по умолчанию: auto
, которая реализует вышеуказанные эвристики) может быть установлена на prefilter
. Это полезно для принудительной предварительной фильтрации в случаях, когда дополнительные условия фильтрации являются исключительно избирательными. Например, следующий запрос может извлечь выгоду от предварительной фильтрации:
Предполагая, что только очень небольшое количество книг стоит меньше 2 долларов, постфильтрация может вернуть ноль строк, потому что 10 лучших совпадений, возвращаемых индексом векторов, могут все стоить больше 2 долларов. Принудив предварительную фильтрацию (добавив SETTINGS vector_search_filter_strategy = 'prefilter'
к запросу), ClickHouse сначала находит все книги с ценой менее 2 долларов, а затем выполняет переборный векторный поиск для найденных книг.
В качестве альтернативного подхода к решению вышеуказанной проблемы настройка vector_search_index_fetch_multiplier (по умолчанию: 1.0
, максимум: 1000.0
) может быть настроена на значение > 1.0
(например, 2.0
). Количество ближайших соседей, извлекаемых из индекс векторов, умножается на значение настройки, а затем применяется дополнительный фильтр к этим строкам, чтобы вернуть LIMIT-количество строк. Например, мы можем снова выполнить запрос, но с коэффициентом 3.0
:
ClickHouse извлечет 3.0 x 10 = 30 ближайших соседей из индекс векторов в каждой части, а затем оценит дополнительные фильтры. Будет возвращено только десять ближайших соседей. Обратите внимание, что настройка vector_search_index_fetch_multiplier
может облегчить проблему, но в крайних случаях (когда условие WHERE очень селективно), все равно возможно получить менее N запрашиваемых строк.
Пересчет
Индексы пропуска в ClickHouse обычно фильтруют на уровне гранул, т.е. поиск в индексе пропуска (внутренне) возвращает список потенциально соответствующих гранул, что снижает количество считываемых данных в последующем сканировании. Это хорошо работает для индексов пропуска в общем, но в случае индексов сходства векторов это создает "несоответствие гранулярности". Более подробно, индекс сходства векторов определяет номера строк N наиболее похожих векторов для заданного ссылочного вектора, но затем ему нужно экстраполировать эти номера строк до номеров гранул. Затем ClickHouse загрузит эти гранулы с диска и повторит вычисление расстояний для всех векторов в этих гранулах. Этот этап называется пересчетом, и хотя теоретически он может улучшить точность — помните, что индекс сходства векторов возвращает только приблизительный результат, это явно не оптимально с точки зрения производительности.
Поэтому ClickHouse предоставляет оптимизацию, которая отключает пересчет и возвращает наиболее похожие векторы и их расстояния непосредственно из индекса. Оптимизация включена по умолчанию, см. настройку vector_search_with_rescoring. На высоком уровне это работает так, что ClickHouse делает наиболее похожие векторы и их расстояния доступными в виде виртуальной колонки _distances
. Чтобы это увидеть, выполните запрос векторного поиска с EXPLAIN header = 1
:
Запрос, выполненный без пересчета (vector_search_with_rescoring = 0
) и с включенными параллельными репликами, может откатиться к пересчету.
Настройка производительности
Настройка сжатия
Практически во всех случаях использования векторы в underlying column являются плотными и плохо сжимаются. В результате, сжатие замедляет вставки и чтения в/из векторной колонки. Поэтому мы рекомендуем отключить сжатие. Для этого укажите CODEC(NONE)
для векторной колонки следующим образом:
Настройка создания индекса
Жизненный цикл индексов сходства векторов связан с жизненным циклом частей. Другими словами, всякий раз, когда создается новая часть с определенным индексом сходства векторов, индекс также создается. Это обычно происходит, когда данные вставляются или во время слияний. К сожалению, HNSW известен долгим временем создания индекса, что может значительно замедлить вставки и слияния. Индексы сходства векторов идеальны только в том случае, если данные неизменяемы или редко меняются.
Чтобы ускорить создание индекса можно использовать следующие техники:
Во-первых, создание индекса можно параллелизовать. Максимальное количество потоков для создания индекса может быть настроено с помощью настройки сервера max_build_vector_similarity_index_thread_pool_size. Для оптимальной производительности значение настройки должно быть настроено в соответствии с количеством ядер CPU.
Во-вторых, чтобы ускорить операции INSERT, пользователи могут отключить создание индексов пропуска для вновь вставленных частей, используя настройку сессии materialize_skip_indexes_on_insert. SELECT-запросы на таких частях будут откатиться к точному поиску. Поскольку вставленные части, как правило, малые по сравнению с общим размером таблицы, ожидается, что влияние на производительность будет незначительным.
В-третьих, чтобы ускорить слияния, пользователи могут отключить создание индексов пропуска для сливаемых частей, используя настройку сессии materialize_skip_indexes_on_merge. Это в сочетании с выражением ALTER TABLE [...] MATERIALIZE INDEX [...] предоставляет явный контроль над жизненным циклом индексов сходства векторов. Например, создание индекса может быть отложено до тех пор, пока все данные не будут загружены или до периода низкой нагрузки системы, такого как выходные.
Настройка использования индекса
Запросы SELECT необходимо загружать индексы сходства векторов в основную память, чтобы использовать их. Чтобы избежать повторной загрузки одного и того же индекса сходства векторов в основную память, ClickHouse предоставляет специальный кэш в памяти для таких индексов. Чем больше этот кэш, тем меньше ненужных загрузок произойдет. Максимальный размер кэша можно настроить с помощью настройки сервера vector_similarity_index_cache_size. По умолчанию кэш может вырасти до 5 ГБ.
Текущий размер кэша индекса сходства векторов отображается в system.metrics:
Количество попаданий и промахов кэша для запроса с некоторым id запроса можно получить из system.query_log:
Для случаев использования в производстве мы рекомендуем, чтобы кэш был достаточно большим для того, чтобы все индексы векторов оставались в памяти в любые моменты времени.
Настройка квантизации
Квантизация — это техника для уменьшения объема памяти векторов и вычислительных затрат на создание и обход индексов векторов. Индексы векторов ClickHouse поддерживают следующие варианты квантизации:
Квантизация | Название | Хранение на размерности |
---|---|---|
f32 | Одинарная точность | 4 байта |
f16 | Полуторная точность | 2 байта |
bf16 (по умолчанию) | Полуторная точность (brain float) | 2 байта |
i8 | Четверная точность | 1 байт |
b1 | Двоичная | 1 бит |
Квантизация снижает точность векторных поисков по сравнению с поисками по оригинальным значениям с плавающей точкой полного разрешения (f32
). Однако на большинстве наборов данных квантизация с полуторной точностью brain float (bf16
) приводит к незначительной потере точности, поэтому индексы сходства векторов используют эту технику квантизации по умолчанию. Квантизация с четвертной точностью (i8
) и двоичная квантизация (b1
) приводит к заметной потере точности в векторных поисках. Мы рекомендуем обе квантизации только в тех случаях, когда размер индекса сходства векторов значительно больше размера доступной оперативной памяти DRAM. В таком случае также рекомендуется включить пересчет (vector_search_index_fetch_multiplier, vector_search_with_rescoring) для повышения точности. Двоичная квантизация рекомендована только для 1) нормализованных встраиваний (т.е. длина вектора = 1, модели OpenAI обычно нормализованы) и 2) если косинусное расстояние используется в качестве функции расстояния. Двоичная квантизация использует расстояние Хэмминга для построения и поиска графа близости. Этап пересчета использует оригинальные векторы с полным разрешением, хранящиеся в таблице, для определения ближайших соседей через косинусное расстояние.
Настройка передачи данных
Ссылающийся вектор в запросе на векторный поиск предоставляется пользователем и, как правило, извлекается, выполняя вызов к большой языковой модели (LLM). Типичный код на Python, который выполняет векторный поиск в ClickHouse, может выглядеть так
Встраиваемые векторы (search_v
в приведенном выше фрагменте) могут иметь очень большую размерность. Например, OpenAI предоставляет модели, которые генерируют встраивающие векторы с 1536 или даже 3072 размерностями. В приведенном выше коде драйвер Python для ClickHouse заменяет встраиваемый вектор на строку в человеко-читаемом формате и затем отправляет запрос SELECT полностью как строку. Предполагая, что встраиваемый вектор состоит из 1536 значений с плавающей точкой одинарной точности, отправленная строка достигает длины 20 кБ. Это создает высокую загрузку CPU для токенизации, разбора и выполнения тысяч преобразований строк в числа с плавающей точкой. Также требуется значительное пространство в файле журналов сервера ClickHouse, что приводит к увеличению объема system.query_log
.
Обратите внимание, что большинство моделей LLM возвращают встраиваемый вектор в виде списка или массива NumPy из нативных чисел с плавающей точкой. Поэтому мы рекомендуем Python-приложениям передавать параметр ссылочного вектора в бинарной форме, используя следующий стиль:
В этом примере ссылающийся вектор отправляется как есть в бинарной форме и переинтерпретируется как массив чисел с плавающей точкой на сервере. Это экономит время CPU на стороне сервера и избегает увеличения объема в журналах сервера и system.query_log
.
Администрирование и мониторинг
Размер на диске индексов сходства векторов можно получить из system.data_skipping_indices:
Пример вывода:
Различия с обычными индексами пропуска
Как и все обычные индексы пропуска, индексы сходства векторов создаются для гранул, и каждый индексируемый блок состоит из GRANULARITY = [N]
гранул ([N]
= 1 по умолчанию для нормальных индексов пропуска). Например, если гранулярность первичного индекса таблицы составляет 8192 (настройка index_granularity = 8192
) и GRANULARITY = 2
, то каждый индексируемый блок будет содержать 16384 строки. Однако структуры данных и алгоритмы для приближенного поиска соседей по своей сути ориентированы на строки. Они хранят компактное представление набора строк и также возвращают строки для запросов векторного поиска. Это вызывает некоторые довольно неинтуитивные различия в том, как индексы сходства векторов ведут себя по сравнению с обычными индексами пропуска.
Когда пользователь определяет индекс сходства векторов на колонке, ClickHouse внутренне создает "подиндекс" сходства векторов для каждого индексируемого блока. Подиндекс "локален" в том смысле, что он знает только о строках своего содержащего индексируемого блока. В примере выше и предполагая, что в колонке есть 65536 строк, мы получаем четыре индексируемых блока (охватывающих восемь гранул) и подиндекс сходства векторов для каждого индексируемого блока. Подиндекс теоретически может вернуть строки с N ближайшими точками в пределах своего индексируемого блока напрямую. Однако так как ClickHouse загружает данные с диска в память с гранулярностью гранул, подиндексы экстраполируют соответствующие строки до гранулярности гранул. Это отличается от обычных индексов пропуска, которые пропускают данные на уровне индексируемых блоков.
Параметр GRANULARITY
определяет, сколько подиндексов сходства векторов создается. Более крупные значения GRANULARITY
означают меньше, но крупнее подиндексы сходства векторов, до точки, когда у колонки (или части данных колонки) будет только один подиндекс. В этом случае подиндекс имеет "глобальный" вид всех строк колонки и может напрямую вернуть все гранулы колонки (части), содержащие соответствующие строки (таких гранул может быть не более LIMIT [N]
). На втором этапе ClickHouse загрузит эти гранулы и определит наилучшие строки, выполняя переборное вычисление расстояния для всех строк гранул. При малом значении GRANULARITY
каждый из подиндексов возвращает до LIMIT N
гранул. В результате необходимо загрузить больше гранул и выполнить постфильтрацию. Обратите внимание, что точность поиска в обоих случаях одинаково хороша, только производительность обработки отличается. Обычно рекомендуется использовать крупное значение GRANULARITY
для индексов сходства векторов и откатываться к меньшим значениям GRANULARITY
только в случае проблем, таких как чрезмерное потребление памяти структурами сходства векторов. Если для индексов сходства векторов не было указано значение GRANULARITY
, то значение по умолчанию составляет 100 миллионов.
Пример
возвращает
Дополнительные примеры наборов данных, использующих приближенный векторный поиск:
Квантизованный бит (QBit)
Один из распространенных подходов для ускорения точного поиска векторов — использование типа данных с меньшей точностью float.
Например, если векторы хранятся как Array(BFloat16)
вместо Array(Float32)
, размер данных уменьшается вдвое, и время выполнения запросов ожидается сокращение пропорционально.
Этот метод известен как квантизация. Хотя он ускоряет вычисления, это может снизить точность результата, несмотря на выполнение полного сканирования всех векторов.
При традиционной квантизации мы теряем точность как во время поиска, так и при хранении данных. В приведенном выше примере мы храним BFloat16
вместо Float32
, что означает, что мы никогда не сможем выполнить более точный поиск позже, даже если это будет необходимо. Одним из альтернативных подходов является хранение двух копий данных: квантизированных и с полной точностью. Хотя это работает, это требует избыточного хранения. Рассмотрим сценарий, в котором у нас есть Float64
как исходные данные, и мы хотим выполнять поиск с различной точностью (16-бит, 32-бит или полные 64-бита). Нам нужно будет хранить три отдельные копии данных.
ClickHouse предлагает тип данных Квантизованный бит (QBit
), который решает эти ограничения путем:
- Хранения оригинальных данных с полной точностью.
- Позволяя задавать точность квантизации во время выполнения запроса.
Это достигается путем хранения данных в битовой группировке (что означает, что все i-е биты всех векторов хранятся вместе), что позволяет считывать данные только на запрашиваемом уровне точности. Вы получаете преимущества по скорости за счет уменьшения I/O от квантизации, сохраняя все оригинальные данные доступными, когда это необходимо. Когда выбрана максимальная точность, поиск становится точным.
Тип данных QBit
и связанные с ним функции расстояния в настоящее время являются экспериментальными. Чтобы включить их, выполните SET allow_experimental_qbit_type = 1
.
Если у вас возникли проблемы, пожалуйста, откройте обращение в репозитории ClickHouse.
Чтобы объявить колонку типа QBit
, используйте следующий синтаксис:
Где:
element_type
— тип каждого элемента вектора. Поддерживаемые типы:BFloat16
,Float32
иFloat64
dimension
— количество элементов в каждом векторе
Создание таблицы QBit
и добавление данных
Поиск векторов с помощью QBit
Давайте найдем ближайших соседей к вектору, представляющему слово 'lemon', используя расстояние L2. Третий параметр в функции расстояния указывает точность в битах — более высокие значения обеспечивают большую точность, но требуют больше вычислений.
Вы можете найти все доступные функции расстояния для QBit
здесь.
Поиск с полной точностью (64 бита):
Поиск с сниженной точностью:
Обратите внимание, что с 12-битной квантизацией мы получаем хорошую аппроксимацию расстояний с более быстрой реализацией запроса. Относительный порядок остается в основном неизменным, 'apple' по-прежнему является ближайшим совпадением.
В текущем состоянии ускорение связано с уменьшением I/O, так как мы читаем меньше данных. Если оригинальные данные были широкими, как Float64
, выбор более низкой точности все равно приведет к вычислению расстояний по данным с той же шириной — просто с меньшей точностью.
Соображения по производительности
Преимущество производительности QBit
заключается в снижении операций ввода-вывода, так как требуется меньше данных для чтения из хранилища при использовании низкой точности. Параметр точности напрямую управляет компромиссом между точностью и скоростью:
- Более высокая точность (ближе к ширине оригинальных данных): более точные результаты, более медленные запросы
- Низкая точность: более быстрые запросы с приблизительными результатами, уменьшенное использование памяти
В настоящее время улучшение скорости происходит за счет уменьшения I/O, а не оптимизации вычислений. При использовании более низких значений точности вычисления расстояний все еще выполняются на данных оригинальной ширины.
Ссылки
Блоги: