1. Introduction
Dans cet atelier de programmation, vous allez apprendre à déployer AlloyDB Omni sur GKE et à l'utiliser avec un modèle d'encapsulation ouvert déployé dans le même cluster Kubernetes. Le déploiement d'un modèle à côté de l'instance de base de données dans le même cluster GKE réduit la latence et les dépendances vis-à-vis des services tiers. De plus, des exigences de sécurité peuvent l'exiger lorsque les données ne doivent pas quitter l'organisation et que l'utilisation de services tiers n'est pas autorisée.
Prérequis
- Connaissances de base de la console Google Cloud
- Compétences de base concernant l'interface de ligne de commande et Cloud Shell
Points abordés
- Déployer AlloyDB Omni sur un cluster Google Kubernetes
- Se connecter à AlloyDB Omni
- Charger des données dans AlloyDB Omni
- Déployer un modèle d'encapsulation ouvert sur GKE
- Enregistrer un modèle d'encapsulation dans AlloyDB Omni
- Générer des représentations vectorielles continues pour la recherche sémantique
- Utiliser des représentations vectorielles continues générées pour la recherche sémantique dans AlloyDB Omni
- Créer et utiliser des index vectoriels dans AlloyDB
Prérequis
- Un compte Google Cloud et un projet Google Cloud
- Un navigateur Web tel que Chrome compatible avec la console Google Cloud et Cloud Shell
2. Préparation
Configuration de l'environnement au rythme de chacun
- Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. Si vous n'avez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.
- Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères non utilisée par les API Google. Vous pourrez toujours le modifier.
- L'ID du projet est unique parmi tous les projets Google Cloud et non modifiable une fois défini. La console Cloud génère automatiquement une chaîne unique (en général, vous n'y accordez d'importance particulière). Dans la plupart des ateliers de programmation, vous devrez indiquer l'ID de votre projet (généralement identifié par
PROJECT_ID
). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre de manière aléatoire. Vous pouvez également en spécifier un et voir s'il est disponible. Après cette étape, l'ID n'est plus modifiable et restera donc le même pour toute la durée du projet. - Pour information, il existe une troisième valeur (le numéro de projet) que certaines API utilisent. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
- Vous devez ensuite activer la facturation dans la console Cloud pour utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour désactiver les ressources et éviter ainsi que des frais ne vous soient facturés après ce tutoriel, vous pouvez supprimer le projet ou les ressources que vous avez créées. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.
Démarrer Cloud Shell
Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, nous allons nous servir de Google Cloud Shell pour cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.
Dans la console Google Cloud, cliquez sur l'icône Cloud Shell dans la barre d'outils supérieure :
Le provisionnement et la connexion à l'environnement prennent quelques instants seulement. Une fois l'opération terminée, le résultat devrait ressembler à ceci :
Cette machine virtuelle contient tous les outils de développement nécessaires. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Vous pouvez effectuer toutes les tâches de cet atelier de programmation dans un navigateur. Vous n'avez rien à installer.
3. Avant de commencer
Activer l'API
Résultat :
Dans Cloud Shell, assurez-vous que l'ID de votre projet est configuré :
PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID
Si ce n'est pas le cas dans la configuration de Cloud Shell, configurez-le à l'aide des commandes suivantes.
export PROJECT_ID=<your project>
gcloud config set project $PROJECT_ID
Activez tous les services nécessaires :
gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
Résultat attendu
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=test-project-001-402417 student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417 Updated property [core/project]. student@cloudshell:~ (test-project-001-402417)$ gcloud services enable compute.googleapis.com gcloud services enable container.googleapis.com Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.
4. Déployer AlloyDB Omni sur GKE
Pour déployer AlloyDB Omni sur GKE, nous devons préparer un cluster Kubernetes en suivant les exigences listées dans les Exigences concernant l'opérateur AlloyDB Omni.
Créer un cluster GKE
Nous devons déployer un cluster GKE standard avec une configuration de pool suffisante pour déployer un pod avec une instance AlloyDB Omni. Omni nécessite au moins deux processeurs et 8 Go de RAM, avec un peu d'espace pour les services d'opérateur et de surveillance.
Configurez les variables d'environnement pour votre déploiement.
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=e2-standard-4
Nous utilisons ensuite gcloud pour créer le cluster GKE standard.
gcloud container clusters create ${CLUSTER_NAME} \
--project=${PROJECT_ID} \
--region=${LOCATION} \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--release-channel=rapid \
--machine-type=${MACHINE_TYPE} \
--num-nodes=1
Résultat attendu sur la console :
student@cloudshell:~ (gleb-test-short-001-415614)$ export PROJECT_ID=$(gcloud config get project) export LOCATION=us-central1 export CLUSTER_NAME=alloydb-ai-gke export MACHINE_TYPE=n2-highmem-2 Your active configuration is: [gleb-test-short-001-415614] student@cloudshell:~ (gleb-test-short-001-415614)$ gcloud container clusters create ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --region=${LOCATION} \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --release-channel=rapid \ --machine-type=${MACHINE_TYPE} \ --num-nodes=1 Note: The Kubelet readonly port (10255) is now deprecated. Please update your workloads to use the recommended alternatives. See https://cloud.google.com/kubernetes-engine/docs/how-to/disable-kubelet-readonly-port for ways to check usage and for migration instructions. Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster alloydb-ai-gke in us-central1.. NAME: omni01 ZONE: us-central1-a MACHINE_TYPE: e2-standard-4 PREEMPTIBLE: INTERNAL_IP: 10.128.0.3 EXTERNAL_IP: 35.232.157.123 STATUS: RUNNING student@cloudshell:~ (gleb-test-short-001-415614)$
Préparer le cluster
Nous devons installer les composants requis, tels que le service cert-manager. Nous pouvons suivre les étapes de la documentation pour installer cert-manager.
Nous utilisons l'outil de ligne de commande Kubernetes, kubectl, qui est déjà installé dans Cloud Shell. Avant d'utiliser l'utilitaire, nous devons obtenir les identifiants de notre cluster.
gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}
Nous pouvons maintenant utiliser kubectl pour installer cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
Résultat attendu sur la console (masqué) :
student@cloudshell:~$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml namespace/cert-manager created customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created ... validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
Installer AlloyDB Omni
Installer l'opérateur AlloyDB Omni peut être installé à l'aide de l'utilitaire Helm.
Exécutez la commande suivante pour installer l'opérateur AlloyDB Omni:
export GCS_BUCKET=alloydb-omni-operator
export HELM_PATH=$(gcloud storage cat gs://$GCS_BUCKET/latest)
export OPERATOR_VERSION="${HELM_PATH%%/*}"
gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive
helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \
--create-namespace \
--namespace alloydb-omni-system \
--atomic \
--timeout 5m
Résultat attendu sur la console (masqué) :
student@cloudshell:~$ gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive Copying gs://alloydb-omni-operator/1.2.0/alloydbomni-operator-1.2.0.tgz to file://./alloydbomni-operator-1.2.0.tgz Completed files 1/1 | 126.5kiB/126.5kiB student@cloudshell:~$ helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \ > --create-namespace \ > --namespace alloydb-omni-system \ > --atomic \ > --timeout 5m NAME: alloydbomni-operator LAST DEPLOYED: Mon Jan 20 13:13:20 2025 NAMESPACE: alloydb-omni-system STATUS: deployed REVISION: 1 TEST SUITE: None student@cloudshell:~$
Une fois l'opérateur AlloyDB Omni installé, nous pourrons suivre le déploiement de notre cluster de base de données.
Voici un exemple de fichier manifeste de déploiement avec le paramètre googleMLExtension activé et un équilibreur de charge interne (privé) :
apiVersion: v1
kind: Secret
metadata:
name: db-pw-my-omni
type: Opaque
data:
my-omni: "VmVyeVN0cm9uZ1Bhc3N3b3Jk"
---
apiVersion: alloydbomni.dbadmin.goog/v1
kind: DBCluster
metadata:
name: my-omni
spec:
databaseVersion: "15.7.0"
primarySpec:
adminUser:
passwordRef:
name: db-pw-my-omni
features:
googleMLExtension:
enabled: true
resources:
cpu: 1
memory: 8Gi
disks:
- name: DataDisk
size: 20Gi
storageClass: standard
dbLoadBalancerOptions:
annotations:
networking.gke.io/load-balancer-type: "internal"
allowExternalIncomingTraffic: true
La valeur secrète du mot de passe est une représentation Base64 du mot de passe "VeryStrongPassword". Le moyen le plus fiable consiste à utiliser Secret Manager de Google pour stocker la valeur du mot de passe. Pour en savoir plus, consultez la documentation.
Enregistrez le fichier manifeste sous le nom my-omni.yaml pour qu'il soit appliqué à l'étape suivante. Si vous êtes dans Cloud Shell, vous pouvez le faire à l'aide de l'éditeur en appuyant sur le bouton "Ouvrir l'éditeur" en haut à droite du terminal.
Après avoir enregistré le fichier sous le nom my-omni.yaml, revenez au terminal en appuyant sur le bouton "Ouvrir le terminal".
Appliquez le fichier manifeste my-omni.yaml au cluster à l'aide de l'utilitaire kubectl:
kubectl apply -f my-omni.yaml
Résultat attendu sur la console :
secret/db-pw-my-omni created dbcluster.alloydbomni.dbadmin.goog/my-omni created
Vérifiez l'état de votre cluster my-omni à l'aide de l'utilitaire kubectl:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
Pendant le déploiement, le cluster passe par différentes phases et doit finalement se terminer par l'état DBClusterReady.
Résultat attendu sur la console :
$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default NAME PRIMARYENDPOINT PRIMARYPHASE DBCLUSTERPHASE HAREADYSTATUS HAREADYREASON my-omni 10.131.0.33 Ready DBClusterReady
Se connecter à AlloyDB Omni
Se connecter à l'aide d'un pod Kubernetes
Lorsque le cluster est prêt, nous pouvons utiliser les binaires du client PostgreSQL sur le pod de l'instance AlloyDB Omni. Nous trouvons l'ID du pod, puis utilisons kubectl pour nous connecter directement au pod et exécuter le logiciel client. Le mot de passe est "VeryStrongPassword", comme défini via le hachage dans my-omni.yaml:
DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres
Exemple de sortie de console:
DB_CLUSTER_NAME=my-omni DB_CLUSTER_NAMESPACE=default DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'` kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres Password for user postgres: psql (15.7) SSL connection (protocol: TLSv1.3, cipher: TLS_AES_128_GCM_SHA256, compression: off) Type "help" for help. postgres=#
5. Déployer un modèle d'IA sur GKE
Pour tester l'intégration de l'IA AlloyDB Omni avec des modèles locaux, nous devons déployer un modèle sur le cluster.
Créer un pool de nœuds pour le modèle
Pour exécuter le modèle, nous devons préparer un pool de nœuds pour exécuter l'inférence. Du point de vue des performances, la meilleure approche consiste à utiliser un pool avec des accélérateurs graphiques utilisant une configuration de nœud telle que g2-standard-8 avec un accélérateur Nvidia L4.
Créez le pool de nœuds avec l'accélérateur L4:
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create gpupool \
--accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
--project=${PROJECT_ID} \
--location=${LOCATION} \
--node-locations=${LOCATION}-a \
--cluster=${CLUSTER_NAME} \
--machine-type=g2-standard-8 \
--num-nodes=1
Résultat attendu
student@cloudshell$ export PROJECT_ID=$(gcloud config get project) Your active configuration is: [pant] export LOCATION=us-central1 export CLUSTER_NAME=alloydb-ai-gke student@cloudshell$ gcloud container node-pools create gpupool \ > --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \ > --project=${PROJECT_ID} \ > --location=${LOCATION} \ > --node-locations=${LOCATION}-a \ > --cluster=${CLUSTER_NAME} \ > --machine-type=g2-standard-8 \ > --num-nodes=1 Note: Machines with GPUs have certain limitations which may affect your workflow. Learn more at https://cloud.google.com/kubernetes-engine/docs/how-to/gpus Note: Starting in GKE 1.30.1-gke.115600, if you don't specify a driver version, GKE installs the default GPU driver for your node's GKE version. Creating node pool gpupool...done. Created [https://container.googleapis.com/v1/projects/student-test-001/zones/us-central1/clusters/alloydb-ai-gke/nodePools/gpupool]. NAME MACHINE_TYPE DISK_SIZE_GB NODE_VERSION gpupool g2-standard-8 100 1.31.4-gke.1183000
Préparer le fichier manifeste de déploiement
Pour déployer le modèle, nous devons préparer un fichier manifeste de déploiement.
Nous utilisons le modèle d'embedding BGE Base v1.5 de Hugging Face. Pour consulter la fiche du modèle, cliquez ici. Pour déployer le modèle, nous pouvons utiliser les instructions déjà préparées de Hugging Face et le package de déploiement de GitHub.
Cloner le package
git clone https://github.com/huggingface/Google-Cloud-Containers
Modifiez le fichier manifeste en remplaçant la valeur cloud.google.com/gke-accelerator par nvidia-l4 et en ajoutant des limites aux ressources.
vi Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config/deployment.yaml
Voici un fichier manifeste corrigé.
apiVersion: apps/v1
kind: Deployment
metadata:
name: tei-deployment
spec:
replicas: 1
selector:
matchLabels:
app: tei-server
template:
metadata:
labels:
app: tei-server
hf.co/model: Snowflake--snowflake-arctic-embed-m
hf.co/task: text-embeddings
spec:
containers:
- name: tei-container
image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-embeddings-inference-cu122.1-4.ubuntu2204:latest
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
env:
- name: MODEL_ID
value: Snowflake/snowflake-arctic-embed-m
- name: NUM_SHARD
value: "1"
- name: PORT
value: "8080"
volumeMounts:
- mountPath: /dev/shm
name: dshm
- mountPath: /data
name: data
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 1Gi
- name: data
emptyDir: {}
nodeSelector:
cloud.google.com/gke-accelerator: nvidia-l4
Déployer le modèle
Nous devons préparer un compte de service et un espace de noms pour le déploiement.
Créez un espace de noms Kubernetes hf-gke-namespace.
export NAMESPACE=hf-gke-namespace
kubectl create namespace $NAMESPACE
Créer un compte de service Kubernetes
export SERVICE_ACCOUNT=hf-gke-service-account
kubectl create serviceaccount $SERVICE_ACCOUNT --namespace $NAMESPACE
Déployer le modèle
kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config
Vérifier les déploiements
kubectl get pods
Vérifier le service de modèle
kubectl get service tei-service
Il est censé afficher le type de service en cours d'exécution ClusterIP.
Exemple de résultat :
student@cloudshell$ kubectl get service tei-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tei-service ClusterIP 34.118.233.48 <none> 8080/TCP 10m
Nous allons utiliser l'adresse CLUSTER-IP du service comme adresse de point de terminaison. L'encapsulation du modèle peut répondre par l'URI http://34.118.233.48:8080/embed. Il sera utilisé plus tard lorsque vous enregistrerez le modèle dans AlloyDB Omni.
Nous pouvons le tester en l'exposant à l'aide de la commande kubectl port-forward.
kubectl port-forward service/tei-service 8080:8080
Le transfert de port s'exécute dans une session Cloud Shell. Nous avons besoin d'une autre session pour le tester.
Ouvrez un autre onglet Cloud Shell à l'aide du signe "+" situé en haut.
Exécutez une commande curl dans la nouvelle session shell.
curl http://localhost:8080/embed \
-X POST \
-d '{"inputs":"Test"}' \
-H 'Content-Type: application/json'
Elle doit renvoyer un tableau de vecteurs semblable à l'exemple de résultat suivant (masqué):
curl http://localhost:8080/embed \ > -X POST \ > -d '{"inputs":"Test"}' \ > -H 'Content-Type: application/json' [[-0.018975832,0.0071419072,0.06347208,0.022992613,0.014205903 ... -0.03677433,0.01636146,0.06731572]]
6. Enregistrer le modèle dans AlloyDB Omni
Pour tester le fonctionnement d'AlloyDB Omni avec le modèle déployé, nous devons créer une base de données et enregistrer le modèle.
Créer une base de données
Créez une VM GCE en tant que jump box, connectez-vous à AlloyDB Omni depuis votre VM cliente et créez une base de données.
Nous avons besoin de la passerelle de transfert, car l'équilibreur de charge externe GKE pour Omni vous permet d'accéder au VPC à l'aide d'une adresse IP privée, mais ne vous permet pas de vous connecter en dehors du VPC. Il est plus sécurisé en général et n'expose pas votre instance de base de données à Internet. Veuillez vérifier le schéma pour plus de clarté.
Pour créer une VM dans la session Cloud Shell, exécutez:
export ZONE=us-central1-a
gcloud compute instances create instance-1 \
--zone=$ZONE
Recherchez l'adresse IP du point de terminaison AlloyDB Omni à l'aide de kubectl dans Cloud Shell:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
Notez PRIMARYENDPOINT. Voici un exemple :
sortie :
student@cloudshell:~$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default NAME PRIMARYENDPOINT PRIMARYPHASE DBCLUSTERPHASE HAREADYSTATUS HAREADYREASON my-omni 10.131.0.33 Ready DBClusterReady student@cloudshell:~$
L'adresse IP 10.131.0.33 est celle que nous utiliserons dans nos exemples pour nous connecter à l'instance AlloyDB Omni.
Connectez-vous à la VM à l'aide de gcloud:
gcloud compute ssh instance-1 --zone=$ZONE
Si vous êtes invité à générer une clé SSH, suivez les instructions. Pour en savoir plus sur la connexion SSH, consultez la documentation.
Dans la session SSH de la VM, installez le client PostgreSQL:
sudo apt-get update
sudo apt-get install --yes postgresql-client
Exportez l'adresse IP de l'équilibreur de charge AlloyDB Omni comme dans l'exemple suivant (remplacez l'adresse IP par celle de votre équilibreur de charge):
export INSTANCE_IP=10.131.0.33
Connectez-vous à AlloyDB Omni. Le mot de passe est "VeryStrongPassword", comme défini via le hachage dans my-omni.yaml:
psql "host=$INSTANCE_IP user=postgres sslmode=require"
Dans la session psql établie, exécutez:
create database demo;
Quittez la session et connectez-vous à la démonstration de la base de données (ou exécutez simplement "\c demo" dans la même session).
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
Créer des fonctions de transformation
Pour les modèles d'encapsulation tiers, nous devons créer des fonctions de transformation qui formatent l'entrée et la sortie au format attendu par le modèle et nos fonctions internes.
Voici la fonction de transformation qui gère l'entrée:
-- Input Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
transformed_input JSON;
model_qualified_name TEXT;
BEGIN
SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
RETURN transformed_input;
END;
$$;
Exécutez le code fourni lorsque vous êtes connecté à la base de données de démonstration, comme indiqué dans l'exemple de sortie:
demo=# -- Input Transform Function corresponding to the custom model endpoint CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT) RETURNS JSON LANGUAGE plpgsql AS $$ DECLARE transformed_input JSON; model_qualified_name TEXT; BEGIN SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input; RETURN transformed_input; END; $$; CREATE FUNCTION demo=#
Voici la fonction de sortie qui transforme la réponse du modèle en tableau de nombres réels:
-- Output Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
transformed_output REAL[];
BEGIN
SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
RETURN transformed_output;
END;
$$;
Exécutez-le dans la même session:
demo=# -- Output Transform Function corresponding to the custom model endpoint CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON) RETURNS REAL[] LANGUAGE plpgsql AS $$ DECLARE transformed_output REAL[]; BEGIN SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output; RETURN transformed_output; END; $$; CREATE FUNCTION demo=#
Enregistrer le modèle
Nous pouvons maintenant enregistrer le modèle dans la base de données.
Voici l'appel de procédure pour enregistrer le modèle avec le nom bge-base-1.5. Remplacez l'adresse IP 34.118.233.48 par l'adresse IP de votre service de modèle (sortie de kubectl get service tei-service):
CALL
google_ml.create_model(
model_id => 'bge-base-1.5',
model_request_url => 'http://34.118.233.48:8080/embed',
model_provider => 'custom',
model_type => 'text_embedding',
model_in_transform_fn => 'tei_text_input_transform',
model_out_transform_fn => 'tei_text_output_transform');
Exécutez le code fourni lorsque vous êtes connecté à la base de données de démonstration:
demo=# CALL google_ml.create_model( model_id => 'bge-base-1.5', model_request_url => 'http://34.118.233.48:8080/embed', model_provider => 'custom', model_type => 'text_embedding', model_in_transform_fn => 'tei_text_input_transform', model_out_transform_fn => 'tei_text_output_transform'); CALL demo=#
Nous pouvons tester le modèle de registre à l'aide de la requête de test suivante, qui devrait renvoyer un tableau de nombres réels.
select google_ml.embedding('bge-base-1.5','What is AlloyDB Omni?');
7. Tester le modèle dans AlloyDB Omni
Charger des données
Pour tester le fonctionnement d'AlloyDB Omni avec le modèle déployé, nous devons charger des données. J'ai utilisé les mêmes données que dans l'un des autres ateliers de programmation pour la recherche vectorielle dans AlloyDB.
Pour charger les données, vous pouvez utiliser le SDK Google Cloud et le logiciel client PostgreSQL. Nous pouvons utiliser la même VM cliente que celle utilisée pour créer la base de données de démonstration. Le SDK Google Cloud devrait déjà y être installé si vous avez utilisé les valeurs par défaut pour l'image de VM. Toutefois, si vous avez utilisé une image personnalisée sans SDK Google, vous pouvez l'ajouter en suivant la documentation.
Exportez l'adresse IP de l'équilibreur de charge AlloyDB Omni comme dans l'exemple suivant (remplacez l'adresse IP par celle de votre équilibreur de charge):
export INSTANCE_IP=10.131.0.33
Connectez-vous à la base de données et activez l'extension pgvector.
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
Dans la session psql:
CREATE EXTENSION IF NOT EXISTS vector;
Quittez la session psql, puis dans la session de ligne de commande, exécutez des commandes pour charger les données dans la base de données de démonstration.
Créez les tables:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"
Résultat attendu sur la console :
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo" Password for user postgres: SET SET SET SET SET set_config ------------ (1 row) SET SET SET SET SET SET CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE SEQUENCE ALTER TABLE ALTER SEQUENCE ALTER TABLE ALTER TABLE ALTER TABLE student@cloudshell:~$
Voici la liste des tables créées:
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
Sortie :
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+" Password for user postgres: List of relations Schema | Name | Type | Owner | Persistence | Access method | Size | Description --------+------------------+-------+----------+-------------+---------------+------------+------------- public | cymbal_embedding | table | postgres | permanent | heap | 8192 bytes | public | cymbal_inventory | table | postgres | permanent | heap | 8192 bytes | public | cymbal_products | table | postgres | permanent | heap | 8192 bytes | public | cymbal_stores | table | postgres | permanent | heap | 8192 bytes | (4 rows) student@cloudshell:~$
Chargez des données dans la table cymbal_products:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"
Résultat attendu sur la console :
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header" COPY 941 student@cloudshell:~$
Voici quelques exemples de lignes du tableau cymbal_products.
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"
Sortie :
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3" Password for user postgres: uniq_id | left | left | sale_price ----------------------------------+--------------------------------+----------------------------------------------------+------------ a73d5f754f225ecb9fdc64232a57bc37 | Laundry Tub Strainer Cup | Laundry tub strainer cup Chrome For 1-.50, drain | 11.74 41b8993891aa7d39352f092ace8f3a86 | LED Starry Star Night Light La | LED Starry Star Night Light Laser Projector 3D Oc | 46.97 ed4a5c1b02990a1bebec908d416fe801 | Surya Horizon HRZ-1060 Area Ru | The 100% polypropylene construction of the Surya | 77.4 (3 rows) student@cloudshell:~$
Chargez des données dans la table cymbal_inventory:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"
Résultat attendu sur la console :
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header" Password for user postgres: COPY 263861 student@cloudshell:~$
Voici quelques exemples de lignes du tableau cymbal_inventory.
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
Sortie :
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3" Password for user postgres: store_id | uniq_id | inventory ----------+----------------------------------+----------- 1583 | adc4964a6138d1148b1d98c557546695 | 5 1490 | adc4964a6138d1148b1d98c557546695 | 4 1492 | adc4964a6138d1148b1d98c557546695 | 3 (3 rows) student@cloudshell:~$
Chargez des données dans la table cymbal_stores:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"
Résultat attendu sur la console :
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header" Password for user postgres: COPY 4654 student@cloudshell:~$
Voici quelques exemples de lignes du tableau cymbal_stores.
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
Sortie :
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3" Password for user postgres: store_id | name | zip_code ----------+-------------------+---------- 1990 | Mayaguez Store | 680 2267 | Ware Supercenter | 1082 4359 | Ponce Supercenter | 780 (3 rows) student@cloudshell:~$
Créer des représentations vectorielles continues
Connectez-vous à la base de données de démonstration à l'aide de psql et créez des représentations vectorielles continues pour les produits décrits dans la table cymbal_products en fonction de leurs noms et descriptions.
Connectez-vous à la base de données de démonstration:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
Nous utilisons une table cymbal_embedding avec un embedding de colonne pour stocker nos embeddings et nous utilisons la description du produit comme entrée textuelle de la fonction.
Activez le chronométrage de vos requêtes pour les comparer ultérieurement avec des modèles distants :
\timing
Exécutez la requête pour créer les représentations vectorielles continues:
INSERT INTO cymbal_embedding(uniq_id,embedding) SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;
Résultat attendu sur la console :
demo=# INSERT INTO cymbal_embedding(uniq_id,embedding) SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products; INSERT 0 941 Time: 11069.762 ms (00:11.070) demo=#
Dans cet exemple, la création d'embeddings pour 941 enregistrements a pris environ 11 secondes.
Exécuter des requêtes de test
Connectez-vous à la base de données de démonstration à l'aide de psql et activez le chronométrage pour mesurer le temps d'exécution de nos requêtes, comme nous l'avons fait pour créer des représentations vectorielles continues.
Trouvons les cinq premiers produits correspondant à une requête telle que "Quels arbres fruitiers poussent bien ici ?" en utilisant la distance cosinus comme algorithme pour la recherche vectorielle.
Dans la session psql, exécutez:
SELECT
cp.product_name,
left(cp.product_description,80) as description,
cp.sale_price,
cs.zip_code,
(ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance
FROM
cymbal_products cp
JOIN cymbal_embedding ce on
ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
cs.store_id=ci.store_id
AND ci.inventory>0
AND cs.store_id = 1583
ORDER BY
distance ASC
LIMIT 5;
Résultat attendu sur la console :
demo=# SELECT cp.product_name, left(cp.product_description,80) as description, cp.sale_price, cs.zip_code, (ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance FROM cymbal_products cp JOIN cymbal_embedding ce on ce.uniq_id=cp.uniq_id JOIN cymbal_inventory ci on ci.uniq_id=cp.uniq_id JOIN cymbal_stores cs on cs.store_id=ci.store_id AND ci.inventory>0 AND cs.store_id = 1583 ORDER BY distance ASC LIMIT 5; product_name | description | sale_price | zip_code | distance -----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------- California Sycamore | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is | 300.00 | 93230 | 0.22753925487632942 Toyon | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e | 10.00 | 93230 | 0.23497374266229387 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e | 25.00 | 93230 | 0.24215884459965364 California Redwood | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a | 1000.00 | 93230 | 0.24564130578287147 Cherry Tree | This is a beautiful cherry tree that will produce delicious cherries. It is an d | 75.00 | 93230 | 0.24846117929767153 (5 rows) Time: 28.724 ms demo=#
La requête a été exécutée en 28 ms et a renvoyé une liste d'arbres de la table "cymbal_products" correspondant à la requête et dont l'inventaire est disponible dans le magasin numéro 1583.
Créer un index ANN
Lorsque nous disposons d'un petit ensemble de données, il est facile d'utiliser la recherche exacte qui analyse tous les représentations vectorielles continues, mais lorsque les données augmentent, la charge et le temps de réponse augmentent également. Pour améliorer les performances, vous pouvez créer des index sur vos données d'embedding. Voici un exemple d'utilisation de l'index Google ScaNN pour les données vectorielles.
Si vous avez perdu la connexion à la base de données de démonstration, reconnectez-vous:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
Activez l'extension alloydb_scann:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
Créez l'index:
CREATE INDEX cymbal_embedding_scann ON cymbal_embedding USING scann (embedding cosine);
Essayez la même requête qu'avant et comparez les résultats:
demo=# SELECT cp.product_name, left(cp.product_description,80) as description, cp.sale_price, cs.zip_code, (ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance FROM cymbal_products cp JOIN cymbal_embedding ce on ce.uniq_id=cp.uniq_id JOIN cymbal_inventory ci on ci.uniq_id=cp.uniq_id JOIN cymbal_stores cs on cs.store_id=ci.store_id AND ci.inventory>0 AND cs.store_id = 1583 ORDER BY distance ASC LIMIT 5; product_name | description | sale_price | zip_code | distance -----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------- California Sycamore | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is | 300.00 | 93230 | 0.22753925487632942 Toyon | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e | 10.00 | 93230 | 0.23497374266229387 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e | 25.00 | 93230 | 0.24215884459965364 California Redwood | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a | 1000.00 | 93230 | 0.24564130578287147 Fremont Cottonwood | This is a beautiful cottonwood tree that can grow to be over 100 feet tall. It i | 200.00 | 93230 | 0.2533482837690365 (5 rows) Time: 14.665 ms demo=#
Le temps d'exécution des requêtes a légèrement diminué, et ce gain serait plus visible avec des ensembles de données plus volumineux. Les résultats sont assez similaires, et seul Cherry a été remplacé par Fremont Cottonwood.
Essayez d'autres requêtes et découvrez comment choisir un indice vectoriel dans la documentation.
N'oubliez pas qu'AlloyDB Omni propose plus de fonctionnalités et de labs.
8. Nettoyer l'environnement
Nous pouvons maintenant supprimer notre cluster GKE avec AlloyDB Omni et un modèle d'IA.
Supprimer le cluster GKE
Dans Cloud Shell, exécutez :
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container clusters delete ${CLUSTER_NAME} \
--project=${PROJECT_ID} \
--region=${LOCATION}
Résultat attendu sur la console :
student@cloudshell:~$ gcloud container clusters delete ${CLUSTER_NAME} \ > --project=${PROJECT_ID} \ > --region=${LOCATION} The following clusters will be deleted. - [alloydb-ai-gke] in [us-central1] Do you want to continue (Y/n)? Y Deleting cluster alloydb-ai-gke...done. Deleted
Supprimer la VM
Dans Cloud Shell, exécutez :
export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
--project=${PROJECT_ID} \
--zone=${ZONE}
Résultat attendu sur la console :
student@cloudshell:~$ export PROJECT_ID=$(gcloud config get project) export ZONE=us-central1-a gcloud compute instances delete instance-1 \ --project=${PROJECT_ID} \ --zone=${ZONE} Your active configuration is: [cloudshell-5399] The following instances will be deleted. Any attached disks configured to be auto-deleted will be deleted unless they are attached to any other instances or the `--keep-disks` flag is given and specifies them for keeping. Deleting a disk is irreversible and any data on the disk will be lost. - [instance-1] in [us-central1-a] Do you want to continue (Y/n)? Y Deleted
Si vous avez créé un projet pour cet atelier de programmation, vous pouvez le supprimer entièrement: https://console.cloud.google.com/cloud-resource-manager
9. Félicitations
Bravo ! Vous avez terminé cet atelier de programmation.
Points abordés
- Déployer AlloyDB Omni sur un cluster Google Kubernetes
- Se connecter à AlloyDB Omni
- Charger des données dans AlloyDB Omni
- Déployer un modèle d'encapsulation ouvert sur GKE
- Enregistrer un modèle d'encapsulation dans AlloyDB Omni
- Générer des représentations vectorielles continues pour la recherche sémantique
- Utiliser des représentations vectorielles continues générées pour la recherche sémantique dans AlloyDB Omni
- Créer et utiliser des index vectoriels dans AlloyDB
Pour en savoir plus sur l'utilisation de l'IA dans AlloyDB Omni, consultez la documentation.
10. Enquête
Résultat :