AlloyDB Omni e modello AI locale su Kubernetes.

1. Introduzione

In questo codelab imparerai a eseguire il deployment di AlloyDB Omni su GKE e a utilizzarlo con un modello di embedding aperto di cui è stato eseguito il deployment nello stesso cluster Kubernetes. Il deployment di un modello accanto all'istanza del database nello stesso cluster GKE riduce la latenza e le dipendenze dai servizi di terze parti. Inoltre, potrebbe essere richiesto dai requisiti di sicurezza quando i dati non devono uscire dall'organizzazione e l'utilizzo di servizi di terze parti non è consentito.

391e4244b25a7db0.png

Prerequisiti

  • Conoscenza di base della console Google Cloud
  • Competenze di base nell'interfaccia a riga di comando e in Cloud Shell

Cosa imparerai a fare

  • Come eseguire il deployment di AlloyDB Omni sul cluster Google Kubernetes
  • Come connettersi ad AlloyDB Omni
  • Come caricare i dati in AlloyDB Omni
  • Come eseguire il deployment di un modello di embedding aperto in GKE
  • Come registrare il modello di embedding in AlloyDB Omni
  • Come generare incorporamenti per la ricerca semantica
  • Come utilizzare gli embedding generati per la ricerca semantica in AlloyDB Omni
  • Come creare e utilizzare gli indici vettoriali in AlloyDB

Che cosa ti serve

  • Un account Google Cloud e un progetto Google Cloud
  • Un browser web come Chrome che supporta la console Google Cloud e Cloud Shell

2. Configurazione e requisiti

Configurazione dell'ambiente a tuo ritmo

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Il nome del progetto è il nome visualizzato per i partecipanti al progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarlo.
  • L'ID progetto è univoco per tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo essere stato impostato). La console Cloud genera automaticamente una stringa univoca; di solito non ti interessa quale sia. Nella maggior parte dei codelab, dovrai fare riferimento al tuo ID progetto (in genere identificato come PROJECT_ID). Se l'ID generato non ti piace, puoi generarne un altro casuale. In alternativa, puoi provare il tuo e vedere se è disponibile. Non può essere modificato dopo questo passaggio e rimane invariato per tutta la durata del progetto.
  • Per tua informazione, esiste un terzo valore, un Numero progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare le API/risorse Cloud. La partecipazione a questo codelab non ha costi, o quasi. Per arrestare le risorse ed evitare di incorrere in fatturazione al termine di questo tutorial, puoi eliminare le risorse che hai creato o il progetto. I nuovi utenti di Google Cloud sono idonei al programma Prova senza costi di 300$.

Avvia Cloud Shell

Sebbene Google Cloud possa essere utilizzato da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Nella console Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

55efc1aaa7a4d3ad.png

Dovrebbe richiedere solo pochi istanti per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere qualcosa di simile a questo:

7ffe5cbb04455448.png

Questa macchina virtuale contiene tutti gli strumenti di sviluppo di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione di rete. Tutto il lavoro in questo codelab può essere svolto in un browser. Non devi installare nulla.

3. Prima di iniziare

Attiva l'API

Output:

In Cloud Shell, assicurati che l'ID progetto sia configurato:

PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID

Se non è definito nella configurazione di Cloud Shell, configuralo utilizzando i seguenti comandi

export PROJECT_ID=<your project>
gcloud config set project $PROJECT_ID

Attiva tutti i servizi necessari:

gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com

Risultato previsto

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. Esegui il deployment di AlloyDB Omni su GKE

Per eseguire il deployment di AlloyDB Omni su GKE, dobbiamo preparare un cluster Kubernetes seguendo i requisiti elencati nei requisiti dell'operatore AlloyDB Omni.

Crea un cluster GKE

Dobbiamo eseguire il deployment di un cluster GKE standard con una configurazione del pool sufficiente per eseguire il deployment di un pod con l'istanza AlloyDB Omni. Per Omni sono necessarie almeno 2 CPU e 8 GB di RAM, con un po' di spazio per i servizi di operatore e monitoraggio.

Configura le variabili di ambiente per il deployment.

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=e2-standard-4

Poi utilizziamo gcloud per creare il 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

Output della console previsto:

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

Prepara il cluster

Dobbiamo installare i componenti richiesti, come il servizio cert-manager. Possiamo seguire i passaggi descritti nella documentazione per l'installazione di cert-manager.

Utilizziamo lo strumento a riga di comando Kubernetes, kubectl, che è già installato in Cloud Shell. Prima di utilizzare l'utilità, dobbiamo ottenere le credenziali per il nostro cluster.

gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}

Ora possiamo utilizzare kubectl per installare cert-manager:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml

Output della console previsto(oscurato):

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

Installa AlloyDB Omni

L'operatore AlloyDB Omni può essere installato utilizzando l'utilità Helm.

Esegui il seguente comando per installare l'operatore 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

Output della console previsto(oscurato):

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:~$

Una volta installato l'operatore AlloyDB Omni, possiamo procedere con il deployment del cluster di database.

Ecco un esempio di manifest di deployment con il parametro googleMLExtension abilitato e il bilanciatore del carico interno (privato):

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

Il valore del secret per la password è una rappresentazione Base64 della parola della password "VeryStrongPassword". Il modo più affidabile è utilizzare Secret Manager di Google per memorizzare il valore della password. Per saperne di più, consulta la documentazione.

Salva il manifest come my-omni.yaml per applicarlo nel passaggio successivo. Se sei in Cloud Shell, puoi farlo utilizzando l'editor premendo il pulsante "Apri editor" in alto a destra nel terminale.

47ab85dad9afdff7.png

Dopo aver salvato il file con il nome my-omni.yaml, torna al terminale premendo il pulsante "Apri terminale".

b9b7747b39dbe8c7.png

Applica il manifest my-omni.yaml al cluster utilizzando l'utilità kubectl:

kubectl apply -f my-omni.yaml

Output della console previsto:

secret/db-pw-my-omni created
dbcluster.alloydbomni.dbadmin.goog/my-omni created

Controlla lo stato del cluster my-omni utilizzando l'utilità kubectl:

kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default

Durante il deployment, il cluster attraversa diverse fasi e alla fine dovrebbe terminare con lo stato DBClusterReady.

Output della console previsto:

$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
NAME      PRIMARYENDPOINT   PRIMARYPHASE   DBCLUSTERPHASE   HAREADYSTATUS   HAREADYREASON
my-omni   10.131.0.33        Ready          DBClusterReady

Connettersi ad AlloyDB Omni

Connettiti utilizzando il pod Kubernetes

Quando il cluster è pronto, possiamo utilizzare i binari del client PostgreSQL nel pod dell'istanza AlloyDB Omni. Individuiamo l'ID pod e poi utilizziamo kubectl per connetterci direttamente al pod ed eseguire il software client. La password è VeryStrongPassword impostata tramite l'hash in 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

Esempio di output della 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. Esegui il deployment del modello di IA su GKE

Per testare l'integrazione dell'IA di AlloyDB Omni con i modelli locali, dobbiamo eseguire il deployment di un modello nel cluster.

Crea un pool di nodi per il modello

Per eseguire il modello, dobbiamo preparare un pool di nodi per l'esecuzione dell'inferenza. L'approccio migliore dal punto di vista delle prestazioni è un pool con acceleratori grafici che utilizza una configurazione del nodo come g2-standard-8 con acceleratore Nvidia L4.

Crea il pool di nodi con l'acceleratore 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

Risultato previsto

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

Prepara il manifest di deployment

Per eseguire il deployment del modello, dobbiamo preparare un manifest di deployment.

Utilizziamo il modello di embedding BGE Base v1.5 di Hugging Face. Puoi leggere la scheda del modello qui. Per eseguire il deployment del modello, possiamo utilizzare le istruzioni già preparate di Hugging Face e il pacchetto di deployment di GitHub.

Clona il pacchetto

git clone https://github.com/huggingface/Google-Cloud-Containers

Modifica il file manifest sostituendo il valore cloud.google.com/gke-accelerator con il nostro nvidia-l4 e aggiungendo limiti alle risorse.

vi Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config/deployment.yaml

Ecco un manifest corretto.

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

Esegui il deployment del modello

Dobbiamo preparare un account di servizio e uno spazio dei nomi per il deployment.

Crea uno spazio dei nomi Kubernetes hf-gke-namespace.

export NAMESPACE=hf-gke-namespace
kubectl create namespace $NAMESPACE

Crea un account di servizio Kubernetes

export SERVICE_ACCOUNT=hf-gke-service-account
kubectl create serviceaccount $SERVICE_ACCOUNT --namespace $NAMESPACE

Esegui il deployment del modello

kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config

Verifica i deployment

kubectl get pods

Verificare il servizio del modello

kubectl get service tei-service

Dovrebbe mostrare il tipo di servizio in esecuzione ClusterIP

Esempio di output:

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

CLUSTER-IP per il servizio è ciò che utilizzeremo come indirizzo endpoint. L'embedding del modello può rispondere tramite l'URI http://34.118.233.48:8080/embed. Verrà utilizzato in un secondo momento quando registrerai il modello in AlloyDB Omni.

Possiamo testarlo esponendolo utilizzando il comando kubectl port-forward.

kubectl port-forward service/tei-service 8080:8080

Il port forwarding verrà eseguito in una sessione Cloud Shell e abbiamo bisogno di un'altra sessione per testarlo.

Apri un'altra scheda di Cloud Shell utilizzando il segno "+" in alto.

4ca978f5142bb6ce.png

Esegui un comando curl nella nuova sessione di shell.

curl http://localhost:8080/embed \
    -X POST \
    -d '{"inputs":"Test"}' \
    -H 'Content-Type: application/json'

Dovrebbe restituire un array di vettori come nel seguente output di esempio (oscurato):

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. Registra il modello in AlloyDB Omni

Per testare il funzionamento di AlloyDB Omni con il modello di cui è stato eseguito il deployment, dobbiamo creare un database e registrare il modello.

Crea database

Crea una VM GCE come jump box, connettiti ad AlloyDB Omni dalla VM client e crea un database.

Abbiamo bisogno della jump box perché il bilanciatore del carico esterno GKE per Omni ti consente di accedere dal VPC utilizzando l'indirizzamento IP privato, ma non ti consente di connetterti dall'esterno del VPC. È più sicuro in generale e non espone l'istanza del database a internet. Controlla il diagramma per maggiori dettagli.

391e4244b25a7db0.png

Per creare una VM nella sessione Cloud Shell, esegui:

export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE 

Trova l'IP dell'endpoint AlloyDB Omni utilizzando kubectl in Cloud Shell:

kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default

Prendi nota di PRIMARYENDPOINT. Ecco un esempio.

output:

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:~$

10.131.0.33 è l'IP che utilizzeremo nei nostri esempi per connetterci all'istanza AlloyDB Omni.

Connettiti alla VM utilizzando gcloud:

gcloud compute ssh instance-1 --zone=$ZONE 

Se ti viene chiesto di generare una chiave SSH, segui le istruzioni. Scopri di più sulla connessione SSH nella documentazione.

Nella sessione SSH alla VM, installa il client PostgreSQL:

sudo apt-get update
sudo apt-get install --yes postgresql-client

Esporta l'IP del bilanciatore del carico AlloyDB Omni come nell'esempio seguente (sostitui IP con l'IP del bilanciatore del carico):

export INSTANCE_IP=10.131.0.33

Connettiti ad AlloyDB Omni. La password è VeryStrongPassword impostata tramite l'hash in my-omni.yaml:

psql "host=$INSTANCE_IP user=postgres sslmode=require"

Nella sessione psql stabilita, esegui:

create database demo;

Esci dalla sessione e connettiti al database demo (in alternativa, puoi semplicemente eseguire "\c demo" nella stessa sessione)

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Creare funzioni di trasformazione

Per i modelli di embedding di terze parti, dobbiamo creare funzioni di trasformazione che formattano l'input e l'output nel formato previsto dal modello e dalle nostre funzioni interne.

Ecco la funzione di trasformazione che gestisce l'input:

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

Esegui il codice fornito mentre sei connesso al database di esempio, come mostrato nell'output di esempio:

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=#

Ed ecco la funzione di output che trasforma la risposta del modello nell'array di numeri reali:

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

Eseguilo nella stessa sessione:

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=#

Registra il modello

Ora possiamo registrare il modello nel database.

Ecco la chiamata alla procedura per registrare il modello con il nome bge-base-1.5, replace the IP 34.118.233.48 by your model service IP address (the output from 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');

Esegui il codice fornito mentre sei connesso al database di prova:

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=#

Possiamo testare il modello di registrazione utilizzando la seguente query di test che dovrebbe restituire un array di numeri reali.

select google_ml.embedding('bge-base-1.5','What is AlloyDB Omni?');

7. Testare il modello in AlloyDB Omni

Carica dati

Per testare il funzionamento di AlloyDB Omni con il modello di cui è stato eseguito il deployment, dobbiamo caricare alcuni dati. Ho utilizzato gli stessi dati di uno degli altri codelab per la ricerca di vettori in AlloyDB.

Un modo per caricare i dati è utilizzare il Google Cloud SDK e il software client PostgreSQL. Possiamo utilizzare la stessa VM client utilizzata per creare il database demo. Google Cloud SDK dovrebbe essere già installato se hai utilizzato i valori predefiniti per l'immagine VM. Tuttavia, se hai utilizzato un'immagine personalizzata senza l'SDK di Google, puoi aggiungerla seguendo la documentazione.

Esporta l'IP del bilanciatore del carico AlloyDB Omni come nell'esempio seguente (sostitui IP con l'IP del bilanciatore del carico):

export INSTANCE_IP=10.131.0.33

Connettiti al database e attiva l'estensione pgvector.

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Nella sessione psql:

CREATE EXTENSION IF NOT EXISTS vector;

Esci dalla sessione psql ed esegui i comandi nella sessione a riga di comando per caricare i dati nel database di prova.

Crea le tabelle:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"

Output della console previsto:

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:~$ 

Ecco l'elenco delle tabelle create:

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"

Output:

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:~$ 

Carica i dati nella tabella 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"

Output della console previsto:

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:~$ 

Ecco un esempio di alcune righe della tabella 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"

Output:

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:~$ 

Carica i dati nella tabella 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"

Output della console previsto:

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:~$ 

Ecco un esempio di alcune righe della tabella cymbal_inventory.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"

Output:

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:~$ 

Carica i dati nella tabella 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"

Output della console previsto:

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:~$

Ecco un esempio di alcune righe della tabella cymbal_stores.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"

Output:

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:~$ 

Creare incorporamenti

Connettiti al database di prova utilizzando psql e crea incorporamenti per i prodotti descritti nella tabella cymbal_products in base ai nomi e alle descrizioni dei prodotti.

Connettiti al database di prova:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Utilizziamo una tabella di incorporamento dei piatti con incorporamento delle colonne per memorizzare gli incorporamenti e la descrizione del prodotto come input di testo della funzione.

Attiva la temporizzazione delle query per confrontarle in un secondo momento con i modelli remoti:

\timing

Esegui la query per creare gli embedding:

INSERT INTO cymbal_embedding(uniq_id,embedding)  SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;

Output della console previsto:

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=#

In questo esempio, la creazione di embedding per 941 record ha richiesto circa 11 secondi.

Esegui query di test

Connettiti al database di prova utilizzando psql e attiva la misurazione del tempo per misurare il tempo di esecuzione delle nostre query, come abbiamo fatto per la creazione degli embedding.

Cerchiamo i 5 migliori prodotti corrispondenti a una richiesta come "Quali alberi da frutto crescono bene qui?" utilizzando la distanza coseno come algoritmo per la ricerca di vettori.

Nella sessione psql, esegui:

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;

Output della console previsto:

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 query è stata eseguita in 28 ms e ha restituito un elenco di alberi dalla tabella cymbal_products corrispondente alla richiesta e con l'inventario disponibile nel negozio con il numero 1583.

Creare l'indice ANN

Quando abbiamo solo un piccolo set di dati, è facile utilizzare la ricerca esatta per eseguire la scansione di tutti gli embedding, ma quando i dati aumentano, aumentano anche il carico e il tempo di risposta. Per migliorare le prestazioni, puoi creare indici sui dati di incorporamento. Ecco un esempio di come farlo utilizzando l'indice Google ScaNN per i dati vettoriali.

Riconnettiti al database di prova se hai perso la connessione:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Attiva l'estensione alloydb_scann:

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

Crea l'indice:

CREATE INDEX cymbal_embedding_scann ON cymbal_embedding USING scann (embedding cosine);

Prova la stessa query di prima e confronta i risultati:

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=#

Il tempo di esecuzione della query è leggermente diminuito e questo vantaggio sarebbe più evidente con set di dati più grandi. I risultati sono abbastanza simili e solo Cherry è stata sostituita da Fremont Cottonwood.

Prova altre query e scopri di più sulla scelta dell'indice di vettori nella documentazione.

E non dimenticare che AlloyDB Omni offre più funzionalità e lab.

8. Ripulire l'ambiente

Ora possiamo eliminare il cluster GKE con AlloyDB Omni e un modello di IA

Elimina il cluster GKE

In Cloud Shell, esegui:

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}

Output della console previsto:

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

Elimina VM

In Cloud Shell, esegui:

export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
  --project=${PROJECT_ID} \
  --zone=${ZONE}

Output della console previsto:

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

Se hai creato un nuovo progetto per questo codelab, puoi eliminare il progetto completo all'indirizzo https://console.cloud.google.com/cloud-resource-manager

9. Complimenti

Complimenti per aver completato il codelab.

Argomenti trattati

  • Come eseguire il deployment di AlloyDB Omni sul cluster Google Kubernetes
  • Come connettersi ad AlloyDB Omni
  • Come caricare i dati in AlloyDB Omni
  • Come eseguire il deployment di un modello di embedding aperto in GKE
  • Come registrare il modello di embedding in AlloyDB Omni
  • Come generare incorporamenti per la ricerca semantica
  • Come utilizzare gli embedding generati per la ricerca semantica in AlloyDB Omni
  • Come creare e utilizzare gli indici vettoriali in AlloyDB

Per saperne di più sull'utilizzo dell'IA in AlloyDB Omni, consulta la documentazione.

10. Sondaggio

Output:

Come utilizzerai questo tutorial?

Leggi solo Leggi e completa gli esercizi