Поиск сходства с помощью Spanner и Vertex AI

1. Введение

Последние достижения в области глубокого обучения позволили представлять текст и другие данные таким образом, чтобы они передавали семантическое значение. Это привело к появлению нового подхода к поиску, называемого векторным поиском, который использует векторные представления текста (известные как эмбеддинги) для поиска документов, наиболее релевантных запросу пользователя. Векторный поиск предпочтительнее традиционного поиска в таких приложениях, как поиск одежды, где пользователи часто ищут товары по их описанию, стилю или контексту, а не по точным названиям товаров или брендов. Мы можем интегрировать базу данных Cloud Spanner с векторным поиском для выполнения векторного сопоставления по сходству. Используя Spanner и векторный поиск вместе, клиенты могут создать мощную интеграцию, которая сочетает в себе доступность, надежность и масштабируемость Spanner и расширенные возможности поиска по сходству векторного поиска Vertex AI. Этот поиск выполняется путем сравнения эмбеддингов элементов в индексе векторного поиска и возврата наиболее похожих совпадений.

Вариант использования

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

  1. Данные получены от компании Spanner.
  2. Векторы, сгенерированные для данных об одежде с помощью ML.PREDICT и сохраненные в Spanner.
  3. Интеграция векторных данных Spanner с векторным поиском с использованием потоков данных и заданий рабочих процессов.
  4. Для поиска сходства между введенными пользователем данными был выполнен векторный поиск.

Мы разработаем демонстрационное веб-приложение для поиска одежды на основе текстового ввода пользователя. Приложение позволит пользователям искать одежду, вводя текстовое описание.

Кроссворд для векторного поиска по индексу:

Данные для поиска одежды хранятся в Spanner. Мы будем вызывать API Vertex AI Embeddings непосредственно из данных Spanner в конструкции ML.PREDICT. Затем мы воспользуемся заданиями Dataflow и Workflow, которые будут массово загружать эти данные (инвентарь и эмбеддинги) в векторный поиск Vertex AI и обновлять индекс.

Выполнение пользовательских запросов к индексу:

Когда пользователь вводит описание одежды, приложение в режиме реального времени генерирует векторные представления (embeddings) с помощью API текстовых векторных представлений. Затем эти данные отправляются в качестве входных данных в API векторного поиска, где находится 10 релевантных описаний товаров в индексе, после чего отображается соответствующее изображение.

Обзор архитектуры

Архитектура приложения Spanner-Vector Search представлена ​​на следующей двухчастной диаграмме:

Кроссворд для векторного поиска по индексу: a79932a25bee23a4.png

Клиентское приложение для выполнения пользовательских запросов к индексу:

b2b4d5a5715bd4c4.png Что вы построите

Гаечный ключ к векторному индексу:

  • База данных Spanner для хранения и управления исходными данными и соответствующими векторными представлениями.
  • Задача рабочего процесса, которая выполняет массовую загрузку данных (идентификаторов и векторных представлений) в базу данных векторного поиска Vertex AI.
  • API векторного поиска, используемый для поиска релевантных описаний товаров в индексе.

Выполнение пользовательских запросов к индексу:

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

Как это работает

Когда пользователь вводит текстовое описание одежды, веб-приложение отправляет это описание в API векторного поиска. Затем API векторного поиска использует векторные представления описаний одежды для поиска наиболее релевантных описаний товаров в индексе. После этого пользователю отображаются описания товаров и соответствующие изображения. Общий рабочий процесс выглядит следующим образом:

  1. Сгенерировать векторные представления для данных, хранящихся в Spanner.
  2. Экспорт и загрузка векторных представлений в индекс векторного поиска.
  3. Для поиска похожих элементов в векторном поисковом индексе выполните поиск по ближайшему соседу.

2. Требования

  • Браузер, например Chrome или Firefox.
  • Проект Google Cloud с включенной функцией выставления счетов.

Прежде чем начать

  1. В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google Cloud.
  2. Убедитесь, что для вашего облачного проекта включена функция выставления счетов. Узнайте, как проверить, включена ли функция выставления счетов для проекта.
  3. Убедитесь, что все необходимые API (Cloud Spanner, Vertex AI, Google Cloud Storage) включены .
  4. Вы будете использовать Cloud Shell — среду командной строки, работающую в Google Cloud и поставляемую с предустановленным gcloud. Для получения информации о командах и использовании gcloud обратитесь к документации . Если ваш проект не задан, используйте следующую команду для его настройки:
gcloud config set project <YOUR_PROJECT_ID>
  1. Для начала перейдите на страницу Cloud Spanner с вашим активным проектом Google Cloud.

3. Бэкенд: Создайте источник данных Spanner и встраивания.

В данном случае база данных Spanner хранит информацию об ассортименте одежды с соответствующими изображениями и описаниями. Убедитесь, что вы сгенерировали векторные представления для текстового описания и сохранили их в базе данных Spanner в виде массива ARRAY<float64>.

  1. Создайте данные для гаечного ключа.

Создайте экземпляр с именем "spanner-vertex" и базу данных с именем "spanner-vertex-embeddings". Создайте таблицу, используя DDL:

CREATE TABLE
  apparels ( id NUMERIC,
    category STRING(100),
    sub_category STRING(50),
    uri STRING(200),
    content STRING(2000),
    embedding ARRAY<FLOAT64>
    )
PRIMARY KEY
  (id);
  1. Вставьте данные в таблицу, используя SQL-запрос INSERT.

Здесь доступны скрипты для вставки примеров данных.

  1. Создание модели векторных представлений текста

Это необходимо для генерации эмбеддингов для содержимого входных данных. Ниже приведен DDL-код для этой задачи:

CREATE MODEL text_embeddings INPUT(content STRING(MAX))
OUTPUT(
  embeddings
    STRUCT<
      statistics STRUCT<truncated BOOL, token_count FLOAT64>,
      values ARRAY<FLOAT64>>
)
REMOTE OPTIONS (
  endpoint = '//aiplatform.googleapis.com/projects/abis-345004/locations/us-central1/publishers/google/models/textembedding-gecko');
  1. Сгенерировать текстовые векторные представления для исходных данных.

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

CREATE TABLE apparels_embeddings (id string(100), embedding ARRAY<FLOAT64>)
PRIMARY KEY (id);

INSERT INTO apparels_embeddings(id, embeddings) 
SELECT CAST(id as string), embeddings.values
FROM ML.PREDICT(
  MODEL text_embeddings,
  (SELECT id, content from apparels)
) ;

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

4. Задание рабочего процесса: Экспорт данных Spanner в Vector Search.

  1. Создайте корзину облачного хранилища.

Это необходимо для хранения эмбеддингов из Spanner в хранилище GCS в формате JSON, который Vector Search ожидает в качестве входных данных. Создайте хранилище в том же регионе, что и ваши данные в Spanner. При необходимости создайте внутри него папку, но в основном создайте в ней пустой файл с именем empty.json.

  1. Настройка облачного рабочего процесса

Чтобы настроить пакетный экспорт из Spanner в индекс векторного поиска Vertex AI:

Создайте пустой индекс :

Убедитесь, что векторный поисковый индекс находится в том же регионе, что и ваш сегмент облачного хранилища и данные. Следуйте инструкциям из 11 шагов на вкладке «Консоль» в разделе «Создание индекса для пакетного обновления» на странице управления индексами. В папке, передаваемой в contentsDeltaUri, создайте пустой файл с именем empty.json, поскольку без этого файла вы не сможете создать индекс. Это создаст пустой индекс.

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

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

Клонирование этого репозитория Git : Существует несколько способов клонирования репозитория Git, один из них — выполнить следующую команду с помощью CLI GitHub . Выполните следующие 2 команды в терминале Cloud Shell:

gh repo clone cloudspannerecosystem/spanner-ai

cd spanner-ai/vertex-vector-search/workflows

Эта папка содержит два файла.

  • batch-export.yaml : Это определение рабочего процесса.
  • sample-batch-input.json : Это пример входных параметров рабочего процесса.

Настройка файла input.json из примера: Сначала скопируйте пример файла json.

cp sample-batch-input.json input.json

Затем отредактируйте input.json , указав данные вашего проекта. В этом случае ваш JSON должен выглядеть следующим образом:

{
  "project_id": "<<YOUR_PROJECT>>",
  "location": "<<us-central1>>",
  "dataflow": {
    "temp_location": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_temp"
  },
  "gcs": {
    "output_folder": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_output"
  },
  "spanner": {
    "instance_id": "spanner-vertex",
    "database_id": "spanner-vertex-embeddings",
    "table_name": "apparels_embeddings",
    "columns_to_export": "embedding,id"
  },
  "vertex": {
    "vector_search_index_id": "<<YOUR_INDEX_ID>>"
  }
}

Настройка прав доступа

Для производственных сред мы настоятельно рекомендуем создать новую учетную запись службы и предоставить ей одну или несколько ролей IAM, содержащих минимально необходимые разрешения для управления службой. Для настройки рабочего процесса экспорта данных из Spanner (встраивания) в индекс Vector Search необходимы следующие роли:

Учетная запись службы облачных рабочих процессов :

По умолчанию используется учетная запись службы Compute Engine по умолчанию.

Если вы используете учетную запись службы, настроенную вручную, необходимо включить следующие роли:

Чтобы запустить задание потока данных: Администрирование потока данных, Рабочий процесс потока данных.

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

Для записи логов: Logs Writer.

Для запуска перестроения векторного поиска Vertex AI: Пользователь Vertex AI.

Учетная запись службы Dataflow Worker :

Если вы используете учетную запись службы, настроенную вручную, необходимо включить следующие роли:

Для управления потоком данных: Dataflow Admin , Dataflow Worker. Для чтения данных из Spanner: Cloud Spanner Database Reader. Права на запись в выбранный реестр контейнеров GCS: GCS Storage Bucket Owner.

  1. Разверните облачный рабочий процесс.

Разверните YAML-файл рабочего процесса в свой проект Google Cloud. Вы можете настроить регион или местоположение, где будет выполняться рабочий процесс.

gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1" [--service account=<service_account>]

or 

gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1"

Теперь рабочий процесс должен отображаться на странице «Рабочие процессы» в консоли Google Cloud.

Примечание : Вы также можете создать и развернуть рабочий процесс из консоли Google Cloud. Следуйте подсказкам в консоли Cloud. Для определения рабочего процесса скопируйте и вставьте содержимое файла batch-export.yaml.

После завершения этого процесса запустите рабочий процесс, чтобы начать экспорт данных.

  1. Запустите облачный рабочий процесс.

Для выполнения рабочего процесса выполните следующую команду:

gcloud workflows execute vector-export-workflow --data="$(cat input.json)"

Результат выполнения должен отобразиться на вкладке «Выполнения» в разделе «Рабочие процессы». Это позволит загрузить ваши данные в базу данных Vector Search и проиндексировать их.

Примечание : Вы также можете выполнить команду из консоли, используя кнопку «Выполнить». Следуйте подсказкам, а в качестве входных данных скопируйте и вставьте содержимое вашего пользовательского файла input.json.

5. Развернуть векторный поисковый индекс.

Разверните индекс на конечной точке

Для развертывания индекса выполните следующие действия:

  1. На странице индексов векторного поиска вы должны увидеть кнопку «РАЗВЕРНУТЬ» рядом с индексом, который вы только что создали на шаге 2 предыдущего раздела. В качестве альтернативы вы можете перейти на страницу информации об индексе и нажать кнопку «РАЗВЕРНУТЬ НА КОНЕЧНУЮ ТОЧКУ».
  2. Предоставьте необходимую информацию и разверните индекс на конечной точке.

В качестве альтернативы вы можете использовать этот блокнот для развертывания на конечной точке (перейдите к разделу развертывания в блокноте). После развертывания запишите идентификатор развернутого индекса и URL-адрес конечной точки.

6. Фронтенд: Преобразование пользовательских данных в векторный поиск.

Давайте создадим простое приложение на Python с пользовательским интерфейсом на основе Gradio, чтобы быстро протестировать нашу реализацию: вы можете обратиться к реализации здесь, чтобы создать это демонстрационное приложение в своем блокноте Colab .

  1. Мы будем использовать Python SDK от aiplatform для вызова API Embeddings, а также для вызова конечной точки индекса Vector Search.
# [START aiplatform_sdk_embedding]
!pip install google-cloud-aiplatform==1.35.0 --upgrade --quiet --user


import vertexai
vertexai.init(project=PROJECT_ID, location="us-central1")


from vertexai.language_models import TextEmbeddingModel


import sys
if "google.colab" in sys.modules:
    # Define project information
    PROJECT_ID = " "  # Your project id
    LOCATION = " "  # Your location 


    # Authenticate user to Google Cloud
    from google.colab import auth
    auth.authenticate_user()
  1. Мы будем использовать Gradio для быстрой и простой демонстрации разрабатываемого нами приложения с искусственным интеллектом и пользовательским интерфейсом. Перед выполнением этого шага перезапустите среду выполнения.
!pip install gradio
import gradio as gr
  1. В веб-приложении при вводе пользователем данных вызывается API Embeddings, мы будем использовать модель встраивания текста: textembedding-gecko@latest

Приведённый ниже метод вызывает модель векторного представления текста и возвращает векторные представления текста, введённого пользователем:

def text_embedding(content) -> list:
    """Text embedding with a Large Language Model."""
    model = TextEmbeddingModel.from_pretrained("textembedding-gecko@latest")
    embeddings = model.get_embeddings(content)
    for embedding in embeddings:
        vector = embedding.values
        #print(f"Length of Embedding Vector: {len(vector)}")
    return vector

Проверьте это

text_embedding("red shorts for girls")

В результате вы должны увидеть изображение, похожее на приведенное ниже (обратите внимание, что изображение обрезано по высоте, поэтому вы не можете увидеть весь вектор ответа):

5d8355ec04dac1f9.png

  1. Укажите идентификатор развернутого индекса и идентификатор конечной точки.
from google.cloud import aiplatform
DEPLOYED_INDEX_ID = "spanner_vector1_1702366982123"
#Vector Search Endpoint
index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
  1. Определите метод векторного поиска для вызова конечной точки индекса и отображения результата с 10 ближайшими совпадениями для ответа встраивания, соответствующего тексту, введенному пользователем.

В приведенном ниже определении метода Vector Search обратите внимание, что метод find_neighbors вызывается для определения 10 ближайших векторов.

def vector_search(content) -> list:
  result = text_embedding(content)
  #call_vector_search_api(content)
  index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
  # run query
  response = index_endpoint.find_neighbors(
      deployed_index_id = DEPLOYED_INDEX_ID,
      queries = [result],
      num_neighbors = 10
  )
  out = []
  # show the results
  for idx, neighbor in enumerate(response[0]):
      print(f"{neighbor.distance:.2f} {spanner_read_data(neighbor.id)}")
      out.append(f"{spanner_read_data(neighbor.id)}")
  return out

Вы также заметите вызов метода spanner_read_data. Давайте рассмотрим его на следующем шаге.

  1. Определите реализацию метода чтения данных Spanner, который вызывает метод execute_sql для извлечения изображений, соответствующих идентификаторам векторов ближайших соседей, возвращенных на предыдущем шаге.
!pip install google-cloud-spanner==3.36.0


from google.cloud import spanner


instance_id = "spanner-vertex"
database_id = "spanner-vertex-embeddings"
projectId = PROJECT_ID
client = spanner.Client()
client.project = projectId
instance = client.instance(instance_id)
database = instance.database(database_id)
def spanner_read_data(id):
    query = "SELECT uri FROM apparels where id = " + id
    outputs = []
    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(query)


        for row in results:
            #print(row)
            #output = "ID: {}, CONTENT: {}, URI: {}".format(*row)
            output = "{}".format(*row)
            outputs.append(output)


    return "\n".join(outputs)

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

  1. Наконец, давайте соберем все элементы воедино в пользовательском интерфейсе и запустим процесс векторного поиска.
from PIL import Image


def call_search(query):
  response = vector_search(query)
  return response


input_text = gr.Textbox(label="Enter your query. Examples: Girls Tops White Casual, Green t-shirt girls, jeans shorts, denim skirt etc.")
output_texts = [gr.Image(label="") for i in range(10)]
demo = gr.Interface(fn=call_search, inputs=input_text, outputs=output_texts, live=True)
resp = demo.launch(share = True)

Результат должен выглядеть так, как показано ниже:

8093b39fbab1a9cc.png

Изображение: Ссылка

Посмотреть видео с результатами можно здесь .

7. Уборка

Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, использованные в этой статье, выполните следующие действия:

  1. В консоли Google Cloud перейдите на страницу «Управление ресурсами» .
  2. В списке проектов выберите проект, который хотите удалить, и нажмите кнопку «Удалить».
  3. В диалоговом окне введите идентификатор проекта, а затем нажмите «Завершить», чтобы удалить проект.
  4. Если вы не хотите удалять проект, удалите экземпляр Spanner, перейдя к только что созданному для этого проекта экземпляру и нажав кнопку «УДАЛИТЬ ЭКЗЕМПЛЯР» в правом верхнем углу страницы обзора экземпляра.
  5. Вы также можете перейти к индексу Vector Search, удалить конечную точку и индекс, а также удалить индекс.

8. Заключение

Поздравляем! Вы успешно завершили реализацию алгоритма Spanner - Vertex Vector Search.

  1. Создание источника данных Spanner и встраивания данных для приложений, использующих базу данных Spanner.
  2. Создание индекса базы данных Vector Search.
  3. Интеграция векторных данных из Spanner в Vector Search с использованием заданий Dataflow и Workflow.
  4. Развертывание индекса на конечной точке.
  5. Наконец, в реализации SDK Vertex AI на основе Python реализован алгоритм векторного поиска по пользовательскому вводу.

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