Recherche de similarités avec Spanner et Vertex AI

1. Introduction

Les progrès récents du deep learning ont rendu possible la représentation du texte et d'autres données de manière à capturer la signification sémantique. Cela a conduit à une nouvelle approche de la recherche, appelée recherche vectorielle, qui utilise des représentations vectorielles du texte (appelées représentations vectorielles continues) pour trouver les documents les plus pertinents par rapport à la requête de l'utilisateur. La recherche vectorielle est préférable à la recherche traditionnelle pour les applications telles que la recherche de vêtements, où les utilisateurs recherchent souvent des articles par leur description, leur style ou leur contexte plutôt que par nom exact de produit ou de marque. Nous pouvons intégrer la base de données Cloud Spanner à Vector Search pour effectuer une mise en correspondance de similarités vectorielles. En combinant Spanner et Vector Search, les clients peuvent créer une intégration puissante qui combine la disponibilité, la fiabilité et l'évolutivité de Spanner et les fonctionnalités avancées de recherche de similarités de Vertex AI Vector Search. Cette recherche est effectuée en comparant les représentations vectorielles continues des éléments dans l'index Vector Search et en renvoyant les correspondances les plus similaires.

Cas d'utilisation

Imaginez que vous êtes data scientist dans un détaillant en prêt-à-porter et que vous cherchez à suivre l'évolution rapide des tendances, des recherches de produits et des recommandations. La difficulté réside dans le fait que vous disposez de ressources limitées et de silos de données. Cet article de blog explique comment implémenter un cas d'utilisation de recommandation de vêtements en utilisant l'approche de recherche par similarité sur les données de vêtements.Voici les étapes à suivre:

  1. Données provenant de Spanner
  2. Vecteurs générés pour les données de vêtements à l'aide de ML.PREDICT et stockés dans Spanner
  3. Données vectorielles Spanner intégrées à Vector Search à l'aide de jobs Dataflow et de workflow
  4. Recherche vectorielle effectuée pour trouver une correspondance de similarité pour l'entrée saisie par l'utilisateur

Nous allons créer une application Web de démonstration pour rechercher des vêtements en fonction du texte saisi par l'utilisateur. L'application permet aux utilisateurs de rechercher des vêtements en saisissant une description textuelle.

Index Spanner vers Vector Search:

Les données de la recherche de vêtements sont stockées dans Spanner. Nous allons appeler l'API Vertex AI Embeddings dans la construction ML.PREDICT directement à partir des données Spanner. Ensuite, nous exploiterons les jobs Dataflow et Workflow qui permettent d'importer ces données de manière groupée (inventaire et représentations vectorielles continues) dans la recherche vectorielle de Vertex AI et d'actualiser l'index.

Exécution de requêtes utilisateur sur l'index:

Lorsqu'un utilisateur saisit une description pour un vêtement, l'application génère les représentations vectorielles continues en temps réel à l'aide de l'API Text Embeddings. Il est ensuite envoyé en entrée à l'API Vector Search afin de trouver 10 descriptions de produits pertinentes dans l'index et d'afficher l'image correspondante.

Présentation de l'architecture

L'architecture de l'application Spanner – Vector Search est illustrée dans le schéma en deux parties suivant:

Index Spanner vers Vector Search: a79932a25bee23a4.png

Application cliente permettant d'exécuter des requêtes utilisateur sur l'index:

b2b4d5a5715bd4c4.pngObjectifs de l'atelier

Index Spanner en vecteur:

  • Base de données Spanner pour stocker et gérer les données sources et les représentations vectorielles continues correspondantes
  • Job de workflow qui importe des données de manière groupée (ID et représentations vectorielles continues) dans la base de données Vertex AI Vector Search.
  • API Vector Search permettant de trouver des descriptions de produits pertinentes à partir de l'index.

Exécution de requêtes utilisateur sur l'index:

  • Application Web qui permet aux utilisateurs de saisir des descriptions textuelles de vêtements, effectue une recherche de similarités à l'aide du point de terminaison d'index déployé et renvoie les vêtements les plus proches à l'entrée.

Fonctionnement

Lorsqu'un utilisateur saisit une description textuelle pour un vêtement, l'application Web l'envoie à l'API Vector Search. L'API Vector Search utilise ensuite les représentations vectorielles continues des descriptions de vêtements pour trouver les descriptions de produits les plus pertinentes à partir de l'index. Les descriptions des produits et les images correspondantes sont ensuite présentées à l'utilisateur. Voici le workflow général :

  1. Générer des représentations vectorielles continues pour les données stockées dans Spanner
  2. Exportez et importez des représentations vectorielles continues dans un index Vector Search.
  3. Interrogez l'index Vector Search pour trouver des éléments similaires en effectuant une recherche du voisin le plus proche.

2. Conditions requises

  • Un navigateur tel que Chrome ou Firefox
  • Un projet Google Cloud avec facturation activée

Avant de commencer

  1. Dans la console Google Cloud, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.
  2. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier si la facturation est activée sur un projet.
  3. Assurez-vous que toutes les API nécessaires (Cloud Spanner, Vertex AI, Google Cloud Storage) sont activées
  4. Vous allez utiliser Cloud Shell, un environnement de ligne de commande qui s'exécute dans Google Cloud et qui est préchargé avec gcloud. Consultez la documentation pour en savoir plus sur les commandes gcloud et leur utilisation. Si votre projet n'est pas défini, utilisez la commande suivante pour le définir :
gcloud config set project <YOUR_PROJECT_ID>
  1. Pour commencer, accédez à la page Cloud Spanner contenant votre projet Google Cloud actif.

3. Backend: créer votre source de données Spanner et vos représentations vectorielles continues

Dans ce cas d'utilisation, la base de données Spanner héberge l'inventaire des vêtements avec les images et la description correspondantes. Veillez à générer des représentations vectorielles continues pour la description textuelle et à les stocker dans votre base de données Spanner sous le nom ARRAY<float64>.

  1. Créer les données Spanner

Créez une instance nommée "spanner-vertex". et une base de données nommée "spanner-vertex-embeddings". Créez une table à l'aide du LDD:

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. Insérer des données dans la table à l'aide de l'instruction INSERT SQL

Des scripts d'insertion pour des exemples de données sont disponibles ici.

  1. Créer un modèle de représentations vectorielles continues de texte

Cette étape est obligatoire pour générer des représentations vectorielles continues pour le contenu de l'entrée. Voici le LDD pour ceci:

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. Générer des représentations vectorielles continues de texte pour les données sources

Créez une table pour stocker les représentations vectorielles continues et insérez celles qui ont été générées. Dans une application de base de données réelle, la charge des données vers Spanner jusqu'à l'étape 2 serait transactionnelle. Pour conserver les bonnes pratiques de conception, je préfère garder les tables transactionnelles normalisées, et donc créer une table distincte pour les représentations vectorielles continues.

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)
) ;

Maintenant que le contenu groupé et les représentations vectorielles continues sont prêts, créons un index Vector Search et un point de terminaison pour stocker les représentations vectorielles continues qui permettront d'exécuter Vector Search.

4. Job de workflow: exportation des données Spanner vers Vector Search

  1. Créer un bucket Cloud Storage

Cela est nécessaire pour stocker les représentations vectorielles continues de Spanner dans un bucket GCS au format JSON attendu par Vector Search en entrée. Créez un bucket dans la même région que vos données dans Spanner. Créez un dossier à l'intérieur si nécessaire, mais créez-y principalement un fichier vide appelé empty.json.

  1. Configurer Cloud Workflow

Pour configurer une exportation par lot de Spanner vers un index Vertex AI Vector Search:

Créez un index vide:

Assurez-vous que l'index Vector Search se trouve dans la même région que votre bucket Cloud Storage et les données. Suivez les 11 étapes de l'instruction sous l'onglet "Console" de la section Créer un index pour la mise à jour groupée de la page "Gérer les index". Dans le dossier transmis à contentsDeltaUri, créez un fichier vide nommé empty.json, car vous ne pourrez pas créer d'index sans ce fichier. Cela crée un index vide.

Si vous disposez déjà d'un index, vous pouvez ignorer cette étape. Le workflow écrasera votre index.

Remarque: Vous ne pouvez pas déployer un index vide sur un point de terminaison. Nous reportons donc à une étape ultérieure l'étape de déploiement sur un point de terminaison, après avoir exporté les données vectorielles vers Cloud Storage.

Cloner ce dépôt Git: il existe plusieurs façons de cloner un dépôt Git. L'une d'elles consiste à exécuter la commande suivante à l'aide de la CLI GitHub. Exécutez les deux commandes ci-dessous à partir du terminal Cloud Shell:

gh repo clone cloudspannerecosystem/spanner-ai

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

Ce dossier contient deux fichiers

  • batch-export.yaml: il s'agit de la définition du workflow.
  • sample-batch-input.json: exemple des paramètres d'entrée du workflow.

Configurez le fichier input.json à partir de l'exemple de fichier:commencez par copier l'exemple de fichier JSON.

cp sample-batch-input.json input.json

Modifiez ensuite input.json en indiquant les détails de votre projet. Dans ce cas, votre fichier JSON doit se présenter comme suit:

{
  "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>>"
  }
}

Autorisations de configuration

Pour les environnements de production, nous vous recommandons vivement de créer un compte de service et de lui attribuer un ou plusieurs rôles IAM contenant les autorisations minimales requises pour la gestion du service. Les rôles suivants sont nécessaires pour configurer le workflow permettant d'exporter les données de Spanner (représentations vectorielles continues) vers l'index Vector Search:

Compte de service Cloud Workflow:

Par défaut, il utilise le compte de service Compute Engine par défaut.

Si vous utilisez un compte de service configuré manuellement, vous devez inclure les rôles suivants:

Pour déclencher un job Dataflow: Administrateur Dataflow, Nœud de calcul Dataflow.

Pour emprunter l'identité d'un compte de service de nœud de calcul Dataflow: Utilisateur du compte de service.

Pour écrire des journaux: Rédacteur de journaux.

Pour déclencher la recompilation Vertex AI Vector Search: Utilisateur Vertex AI.

Compte de service de nœud de calcul Dataflow:

Si vous utilisez un compte de service configuré manuellement, vous devez inclure les rôles suivants:

Pour gérer Dataflow: Administrateur Dataflow, Nœud de calcul Dataflow. Pour lire des données issues de Spanner: Lecteur de bases de données Cloud Spanner Accès en écriture sur le registre Container Registry GCS sélectionné: Propriétaire du bucket de stockage GCS

  1. Déployer le workflow Cloud Workflow

Déployez le fichier YAML du workflow dans votre projet Google Cloud. Vous pouvez configurer la région ou l'emplacement où le workflow sera exécuté.

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"

Le workflow devrait maintenant être visible sur la page Workflows de la console Google Cloud.

Remarque: Vous pouvez également créer et déployer le workflow à partir de la console Google Cloud. Suivez les instructions dans la console Cloud. Pour définir le workflow, copiez et collez le contenu du fichier batch-export.yaml.

Une fois cette opération terminée, exécutez le workflow pour que l'exportation des données commence.

  1. Exécuter le workflow Cloud

Exécutez la commande suivante pour lancer le workflow:

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

L'exécution doit s'afficher dans l'onglet "Exécutions" de Workflows. Vos données doivent être chargées dans la base de données Vector Search et être indexées.

Remarque: Vous pouvez également l'exécuter à partir de la console à l'aide du bouton "Exécuter". Suivez les instructions et pour l'entrée, copiez et collez le contenu de votre fichier input.json personnalisé.

5. Déployer l'index Vector Search

Déployer l'index sur un point de terminaison

Pour déployer l'index, procédez comme suit:

  1. Sur la page Index Vector Search, vous devriez voir un bouton DÉPLOYER à côté de l'index que vous venez de créer à l'étape 2 de la section précédente. Vous pouvez également accéder à la page d'informations sur l'index et cliquer sur le bouton DÉPLOYER SUR UN POINT DE TERMINAISON.
  2. Fournissez les informations nécessaires et déployez l'index sur un point de terminaison.

Vous pouvez également consulter ce notebook pour le déployer sur un point de terminaison (passez à la partie du notebook). Une fois le déploiement terminé, notez l'ID de l'index déployé et l'URL du point de terminaison.

6. Interface: données utilisateur vers Vector Search

Créons une application Python simple avec une expérience utilisateur alimentée par gradio afin de tester rapidement notre implémentation. Cliquez ici pour consulter l'implémentation de cette application de démonstration dans votre propre notebook colab.

  1. Nous allons utiliser le SDK Python AIplatform pour appeler l'API Embeddings et pour appeler le point de terminaison d'index 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. Nous utiliserons gradio pour faire une démonstration de l'application d'IA que nous créons rapidement et facilement avec une interface utilisateur. Redémarrez l'environnement d'exécution avant d'implémenter cette étape.
!pip install gradio
import gradio as gr
  1. À partir de l'application Web, à partir de l'entrée utilisateur, appelez l'API Embeddings. Nous utiliserons le modèle de représentation vectorielle continue de texte: textembedding-gecko@latest.

La méthode ci-dessous appelle le modèle de représentation vectorielle continue de texte et renvoie les représentations vectorielles continues du texte saisi par l'utilisateur:

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

Tester

text_embedding("red shorts for girls")

Vous devriez obtenir un résultat semblable à ce qui suit (notez que l'image est recadrée en hauteur, vous ne pouvez donc pas voir l'intégralité de la réponse vectorielle):

5d8355ec04dac1f9.png

  1. Déclarer l'ID de l'index déployé et l'ID du point de terminaison
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. Définissez la méthode Vector Search pour appeler le point de terminaison de l'index et afficher le résultat avec les 10 correspondances les plus proches pour la réponse de représentation vectorielle continue correspondant au texte saisi par l'utilisateur.

Dans la définition de la méthode ci-dessous pour Vector Search, notez que la méthode find_Neighbors est appelée pour identifier les 10 vecteurs les plus proches.

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

Vous remarquerez également l'appel à la méthode spanner_read_data. Découvrons-le à l'étape suivante.

  1. Définissez l'implémentation de la méthode de lecture des données Spanner qui appelle la méthode "execute_sql" pour extraire les images correspondant aux ID des vecteurs des voisins les plus proches renvoyés à la dernière étape.
!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)

Il doit renvoyer les URL des images correspondant aux vecteurs choisis.

  1. Pour finir, nous allons réunir tous les éléments dans une interface utilisateur et déclencher le processus Vector Search.
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)

Vous devriez obtenir le résultat suivant:

8093b39fbab1a9cc.png

Image: Link

Pour voir la vidéo, cliquez ici.

7. Effectuer un nettoyage

Pour éviter que les ressources utilisées dans cet article soient facturées sur votre compte Google Cloud, procédez comme suit:

  1. Dans la console Google Cloud, accédez à la page Gérer les ressources.
  2. Dans la liste des projets, sélectionnez celui que vous souhaitez supprimer, puis cliquez sur "Supprimer".
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur "Arrêter" pour supprimer le projet.
  4. Si vous ne souhaitez pas supprimer le projet, supprimez l'instance Spanner. Pour cela, accédez à l'instance que vous venez de créer pour ce projet, puis cliquez sur le bouton SUPPRIMER L'INSTANCE en haut à droite de la page de présentation de l'instance.
  5. Vous pouvez également accéder à l'index Vector Search, annuler le déploiement du point de terminaison et de l'index, et supprimer l'index.

8. Conclusion

Félicitations ! Vous avez terminé l'implémentation de Spanner – Vertex Vector Search en

  1. Créer une source de données Spanner et des représentations vectorielles continues pour les applications qui proviennent de la base de données Spanner
  2. Création de l'index de base de données Vector Search...
  3. Intégration de données vectorielles de Spanner à Vector Search à l'aide de jobs Dataflow et Workflow
  4. Déployer un index sur un point de terminaison
  5. Enfin, appeler Vector Search en cas d'entrée utilisateur dans une implémentation du SDK Vertex AI basée sur Python

N'hésitez pas à étendre l'implémentation à votre propre cas d'utilisation ou à improviser le cas d'utilisation actuel avec de nouvelles fonctionnalités. Pour en savoir plus sur les fonctionnalités de machine learning de Spanner, cliquez ici.