Создание векторного поиска высокой производительности

Высокопроизводительный векторный поиск

Векторные хранилища играют важную роль в развитии машинного обучения, служа как неотъемлемые хранилища числовых кодировок данных. Векторы – это математические объекты, используемые для представления категориальных данных в многомерном пространстве. В контексте машинного обучения векторные хранилища предоставляют средство для хранения, извлечения, фильтрации и манипулирования этими векторами. ‍

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

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

‍Qwak Vector Store

Мы рады объявить о выпуске нашего управляемого сервиса векторных хранилищ Qwak. Векторное хранилище Qwak предоставляет масштабируемое решение для преобразования и загрузки векторных данных, а также низколатентный запросный движок, который может обеспечивать расширенную фильтрацию по метаданным и свойствам.

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

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

Создание модели, преобразующей слова в векторы

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

Для этой модели мы будем использовать модель sentence transformer all-MiniLM-L12-v2 из Hugging Face. Сначала мы импортируем класс QwakModel из qwak-sdk и определяем нашу базовую модель. ‍

Нам потребуется определить две функции: функцию build() и функцию predict(). Поскольку модель предварительно обучена, мы просто передаем нашу функцию build и устанавливаем self.model в экземпляр SentenceTransformer. ‍

В функции predict мы определяем, как наша модель будет обрабатывать входные данные.

import qwak
from pandas import DataFrame
from qwak.model.base import QwakModel
from qwak.model.schema import ModelSchema, ExplicitFeature
from sentence_transformers import SentenceTransformer

from helpers import get_device


class SentenceEmbeddingsModel(QwakModel):
    def __init__(self):
        self.model_id = "sentence-transformers/all-MiniLM-L12-v2"
        self.model = None
        self.device = None

    def build(self):
        qwak.log_metric({"val_accuracy": 1})

    def schema(self):
        return ModelSchema(
            inputs=[
                ExplicitFeature(name="input", type=str),
            ]
        )

    def initialize_model(self):
        self.device = get_device()
        print(f"Inference using device: {self.device}")
        self.model = SentenceTransformer(
            model_name_or_path=self.model_id,
            device=self.device,
        )

    @qwak.api()
    def predict(self, df):
        text_embeds = self.model.encode(
            df["input"].values.tolist(),
            convert_to_tensor=True,
            normalize_embeddings=True,
            device=self.device,
            batch_size=128,
        ).tolist()

        return DataFrame({"embeddings": text_embeds})

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

Мы возвращаем DataFrame со значением поля вывода и списком векторов.

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

Пример этой модели можно найти в нашем репозитории примеров Qwak.

  1. Клонируйте репозиторий локально
  2. Убедитесь, что у вас установлен и настроен Qwak CLI.
  3. Перейдите в директорию sentence_transformers_poetry.
  4. Запустите команду make build, чтобы запустить задание обучения для этой модели. Вы можете перейти на вкладку Модели -> Сборки в пользовательском интерфейсе Qwak и отслеживать прогресс сборки.
  5. Теперь, когда модель успешно обучена и сохранена в репозитории моделей Qwak, вы можете запустить команду make deploy, чтобы взять эту версию сборки и развернуть ее в качестве конечной точки реального времени. Вы также можете отслеживать шаги развертывания, перейдя на вкладку Модели -> Развертывания в пользовательском интерфейсе Qwak.
  6. После завершения развертывания нажмите на вкладку Test Model в верхнем правом углу платформы, и Qwak сгенерирует примеры вызовов вывода, которые вы можете использовать для вызова вашей конечной точки в реальном времени и тестирования ваших предсказаний в реальном времени!

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

Создать коллекцию

Коллекции – это организационная функция Qwak, которая позволяет структурировать и управлять различными группами векторов в вашем векторном хранилище. Коллекции позволяют указать конфигурацию метрики (косинус, L2), а также количество измерений вектора, обеспечивая тонкую настройку группировки и индексации данных.

Вы можете создавать коллекции в пользовательском интерфейсе или определять их как код с использованием qwak-sdk. Для этого учебника вы можете увидеть пример созданной нами коллекции ниже. Мы выбираем косинусную метрику для группировки и 384 измерения для использования в векторной плоскости. Нам также необходимо выбрать векторизатор.

Векторизатор – это развернутая модель Qwak, которая принимает данные в качестве входных данных и возвращает вектор в своей функции predict – так же, как модель, которую мы создали на предыдущем шаге!

Здесь мы выбираем модель sentence-transformer, которая работает на платформе Qwak, и Qwak Vector Store автоматически использует функцию вложения этой модели при подготовке входных данных для вставки или поиска векторного хранилища, позволяя нам отправлять свободный текст в API коллекций Qwak.

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

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

Сначала мы должны подготовить и форматировать наши данные так, чтобы их можно было правильно вставить в векторное хранилище.

Давайте сначала установим зависимости нашего проекта.

pip install pandas pyarrow numpy qwak-sdk

У нас есть исходный файл parquet, который содержит ряд статей из Википедии и их содержимое. У нас также есть поля для URL-адреса, заголовка статьи и длины текста, которые мы будем использовать в качестве свойств для наших векторов. Сначала мы считываем файл parquet в фрейм данных с помощью pandas и фильтруем статьи, не содержащие текст.

import pandas as pd
import numpy as np
import os

df = pd.read_parquet("short_articles.parquet")
df = df[df["text"].str.len() > 0].sample(frac=0.25)
df = df.reset_index()

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

## собираем наши идентификаторы для каждой статьи
ids = df["article_id"].tolist()
## собираем свойства, которые мы прикрепим к каждому вектору
properties = df.apply(
   lambda r:{
       "url": r.url,
       "title": r.title,
       "title_len": r.title_len,
       "text": r.text,
       "text_len": r.text_len}
   , axis=1
).tolist()

Нам нужно получить коллекцию, которую мы создали в пользовательском интерфейсе. С помощью клиента Qwak VectorStoreClient мы находим коллекцию, которую мы создали на предыдущем шаге, или можем создать новую коллекцию, используя метод create_collection().

from qwak.exceptions import QwakException
from qwak.vector_store import VectorStoreClient

## Создаем клиент векторов и получаем коллекцию
client = VectorStoreClient()

# Получаем коллекцию или создаем новую
collection_name = "wikipedia-vectorizer-demo"
try:
   collection = client.get_collection_by_name(collection_name)
except QwakException:
   collection = client.create_collection(
       name=collection_name,
       description="Индексация статей Википедии",
       dimension=384,
       metric="cosine",
       vectorizer="sentence_transformer"  # Название развернутой модели в реальном времени на Qwak
   )
 

С нашими идентификаторами и правильно отформатированными метаданными мы выбираем поле текста статьи из Dataframe, так как это столбец, который мы хотим вставить в наш векторный набор. Мы используем метод upsert() из клиента коллекции, который мы получили на предыдущем шаге. Используя параметр “natural_inputs” для нашего текста статьи, команда upsert вызовет модель в реальном времени, использует функцию predict для возвращения нашего текста статьи как векторов и сохраняет векторы в базе данных.

collection.upsert(
   ## Список идентификаторов статей
   ids=ids,
   # Естественные входы
   natural_inputs=df['text'].tolist(),
   ## Список словарей свойств статьи
   properties=properties
)

Запрос векторного хранилища и результаты

Теперь, когда наше векторное хранилище заполнено, давайте его использовать!

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

Мы берем наш запрос “утки” и передаем его в метод search клиента. На сервере клиент вектора вызовет нашу модель “sentence-transformer” для векторизации нашего запроса, чтобы его можно было правильно использовать в векторном хранилище. Мы можем указать количество результатов и свойства вектора, которые мы хотим получить. Мы также можем вернуть расстояние между нашим запросом и возвращенными результатами, если мы хотим оценить производительность запроса или качество нашей индексации векторов.

from qwak.vector_store import VectorStoreClient

## Поиск векторного хранилища с помощью вектора, предоставленного моделью
search_results = collection.search(
   natural_input="Утки",
   top_results=2,
   output_properties=["title", "title_len", "url"],
   include_distance=True,
   include_vector=False
)

Мы указываем клиенту вернуть заголовок, длину заголовка и URL трех ближайших статей, и вот результаты!

[print(x.properties, x.distance) for x in search_results]

# Объекты результатов поиска
{'title': 'Анахайм Дакс', 'title_len': 13.0, 'url': 'https://simple.wikipedia.org/wiki/Anaheim%20Ducks'} 0.38589573
{'title': 'Утка', 'title_len': 4.0, 'url': 'https://simple.wikipedia.org/wiki/Duck'} 0.4547128

Наш запрос вернул статьи, связанные с командой Анахайм Дакс, которая является хоккейной командой в НХЛ, и звук “Кря” уток. Не самый лучший результат поиска, но для всего нескольких тысяч векторов статей мы сойдемся на этом! Вы должны использовать функцию метрики коллекций, чтобы экспериментировать с различными вычислениями расстояния и измерять, как они влияют на производительность.

Поддержка предварительной фильтрации векторных запросов

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

Предварительная фильтрация представляет собой подход, при котором подходящие кандидаты определяются до начала векторного поиска. Впоследствии, векторный поиск исключительно рассматривает кандидатов, присутствующих в списке “allow”.

Основные преимущества предварительной фильтрации запросов:

  1. Предсказание количества результатов: Применение фильтра к уже сокращенному списку кандидатов позволяет просто оценить количество элементов в результатах поиска.
  2. Мгновенное обнаружение совпадений: Если фильтр является сильно ограничивающим, то есть совпадает только с небольшим процентом точек данных по сравнению с размером набора данных, вы мгновенно узнаете, если нет совпадений в исходном векторном поиске.
from qwak.vector_store import VectorStoreClient
from qwak.vector_store.filters import Or, GreaterThan, Equal

## Поиск векторного хранилища с использованием вектора, предоставленного моделью
search_results = collection.search(
   natural_input="Утки",
   top_results=2,
   output_properties=["title", "title_len", "url"],
   filter=Or(
    GreaterThan(property="text_len", value=110.0),
    Equal(property="title_len", value=16.0)
  )
)

Узнайте больше в нашей документации о предварительной фильтрации векторных запросов.

Резюме

Qwak Vector Store теперь доступно, и вы можете начать прямо сегодня, перейдя на вкладку “Коллекции” в пользовательском интерфейсе Qwak и создав новую коллекцию. Чтобы узнать больше о функциональности Qwak Vector Store, вы можете посетить нашу документацию или обратиться к члену команды Qwak.

Статья изначально опубликована здесь. Перепечатано с разрешения.