AlloyDB Omni i model AI lokalnego w Kubernetes.

1. Wprowadzenie

Z tego ćwiczenia w Codelab dowiesz się, jak wdrożyć AlloyDB Omni w GKE i używać go z otwartym modelem umieszczania wdrożonym w tym samym klastrze Kubernetes. Wdrażanie modelu obok instancji bazy danych w tym samym klastrze GKE zmniejsza opóźnienia i zależności od usług innych firm. Może to być też wymagane ze względów bezpieczeństwa, gdy dane nie mogą opuścić organizacji, a usługi innych firm nie są dozwolone.

391e4244b25a7db0.png

Wymagania wstępne

  • podstawowa znajomość konsoli Google Cloud;
  • podstawowe umiejętności w zakresie interfejsu wiersza poleceń i Cloud Shell;

Czego się nauczysz

  • Jak wdrożyć AlloyDB Omni w klastrze Google Kubernetes
  • Jak połączyć się z AlloyDB Omni
  • Jak wczytywać dane do AlloyDB Omni
  • Wdrażanie w GKE modelu do umieszczania treści w otwartym formacie
  • Jak zarejestrować model wstawiania w AlloyDB Omni
  • Jak generować embeddingi na potrzeby wyszukiwania semantycznego
  • Jak używać wygenerowanych wektorów dystrybucyjnych do wyszukiwania semantycznego w AlloyDB Omni
  • Tworzenie i używanie indeksów wektorowych w AlloyDB

Czego potrzebujesz

  • Konto Google Cloud i projekt Google Cloud
  • przeglądarka internetowa, np. Chrome, obsługująca konsolę Google Cloud i Cloud Shell;

2. Konfiguracja i wymagania

Konfiguracja środowiska w samodzielnym tempie

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, którego nie używają interfejsy API Google. Zawsze możesz ją zaktualizować.
  • Identyfikator projektu jest niepowtarzalny w ramach wszystkich projektów Google Cloud i nie można go zmienić (po ustawieniu). Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie ma znaczenia, jaki to ciąg. W większości laboratoriów z kodem musisz podać identyfikator projektu (zwykle oznaczony jako PROJECT_ID). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować inny losowy. Możesz też spróbować użyć własnego adresu e-mail, aby sprawdzić, czy jest on dostępny. Nie można go zmienić po wykonaniu tego kroku. Pozostanie on do końca projektu.
  • Informacyjnie: istnieje jeszcze 3 wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć rozliczenia w konsoli Cloud, aby korzystać z zasobów i interfejsów API Cloud. Przejście przez ten samouczek nie będzie kosztowne, a być może nawet bezpłatne. Aby wyłączyć zasoby i uniknąć obciążenia opłatami po zakończeniu samouczka, możesz usunąć utworzone zasoby lub usunąć projekt. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.

Uruchomienie Cloud Shell

Google Cloud można obsługiwać zdalnie z laptopa, ale w tym ćwiczeniu będziesz używać Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.

W konsoli Google Cloud kliknij ikonę Cloud Shell na pasku narzędzi w prawym górnym rogu:

55efc1aaa7a4d3ad.png

Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po jego zakończeniu powinno wyświetlić się coś takiego:

7ffe5cbb04455448.png

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie poprawia wydajność sieci i uwierzytelnianie. Wszystkie zadania w tym CodeLab możesz wykonać w przeglądarce. Nie musisz niczego instalować.

3. Zanim zaczniesz

Włącz interfejs API

Dane wyjściowe:

W Cloud Shell sprawdź, czy identyfikator projektu jest skonfigurowany:

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

Jeśli nie jest zdefiniowany w konfiguracji Cloud Shell, skonfiguruj go za pomocą tych poleceń:

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

Włącz wszystkie niezbędne usługi:

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

Oczekiwany wynik

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. Wdrażanie AlloyDB Omni w GKE

Aby wdrożyć AlloyDB Omni w GKE, musimy przygotować klaster Kubernetes zgodnie z wymaganiami wymienionymi w wymaganiach operatora AlloyDB Omni.

Tworzenie klastra GKE

Musimy wdrożyć standardowy klaster GKE z konfiguracją puli wystarczającą do wdrożenia kontenera z instancją AlloyDB Omni. Do obsługi Omni potrzebujemy co najmniej 2 procesorów i 8 GB pamięci RAM z odrobiną miejsca na usługi operatora i monitorowania.

Skonfiguruj zmienne środowiskowe dla wdrożenia.

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

Następnie użyjemy gcloud do utworzenia klastra standardowego GKE.

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

Oczekiwane dane wyjściowe konsoli:

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

Przygotowanie klastra

Musimy zainstalować wymagane komponenty, takie jak usługa cert-manager. Możemy wykonać czynności opisane w dokumentacji dotyczącej instalacji cert-managera.

Używamy narzędzia wiersza poleceń Kubernetes, kubectl, które jest już zainstalowane w Cloud Shell. Zanim użyjemy tego narzędzia, musimy uzyskać dane logowania do naszego klastra.

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

Teraz możemy zainstalować cert-managera za pomocą kubectl:

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

Oczekiwane dane wyjściowe konsoli(ocenzurowane):

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

Instalowanie AlloyDB Omni

Operator AlloyDB Omni można zainstalować za pomocą narzędzia helm.

Aby zainstalować operatora AlloyDB Omni, uruchom to polecenie:

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

Oczekiwane dane wyjściowe konsoli(ocenzurowane):

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

Gdy operator AlloyDB Omni zostanie zainstalowany, możemy przejść do wdrażania klastra bazy danych.

Oto przykład pliku manifestu wdrożenia z włączonym parametrem googleMLExtension i wewnętrznym (prywatnym) systemem równoważenia obciążenia:

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

Wartość tajna hasła to reprezentacja Base64 słowa „VeryStrongPassword”. Bardziej niezawodnym sposobem jest przechowywanie wartości hasła w usłudze Google Secret Manager. Więcej informacji znajdziesz w dokumentacji.

Zapisz plik manifestu jako my-omni.yaml, aby zastosować go w następnym kroku. Jeśli korzystasz z Cloud Shell, możesz to zrobić w edytorze, klikając przycisk „Otwórz edytor” w prawym górnym rogu terminala.

47ab85dad9afdff7.png

Po zapisaniu pliku o nazwie my-omni.yaml wróć do terminala, naciskając przycisk „Otwórz terminal”.

b9b7747b39dbe8c7.png

Za pomocą narzędzia kubectl zastosuj plik manifestu my-omni.yaml do klastra:

kubectl apply -f my-omni.yaml

Oczekiwane dane wyjściowe konsoli:

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

Sprawdź stan klastra my-omni za pomocą narzędzia kubectl:

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

Podczas wdrażania klaster przechodzi przez różne fazy i ostatecznie powinien osiągnąć stan DBClusterReady.

Oczekiwane dane wyjściowe konsoli:

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

Połącz się z AlloyDB Omni

Łączenie za pomocą kontenera Kubernetes

Gdy klaster będzie gotowy, będziemy mogli używać binarnych plików klienta PostgreSQL w podzbiorze instancji AlloyDB Omni. Znajdujemy identyfikator pod, a następnie za pomocą kubectl łączymy się bezpośrednio z pod i uruchamiamy oprogramowanie klienta. Hasło to VeryStrongPassword ustawione za pomocą hasha w pliku 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

Przykładowe dane wyjściowe konsoli:

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. Wdrażanie modelu AI w GKE

Aby przetestować integrację AlloyDB Omni AI z modelami lokalnymi, musimy wdrożyć model na klaster.

Tworzenie puli węzłów dla modelu

Aby uruchomić model, musimy przygotować pulę węzłów do wykonywania wnioskowania. Z punktu widzenia wydajności najlepszym rozwiązaniem jest pula z akceleratorami graficznymi korzystającymi z konfiguracji węzła, np. g2-standard-8 z akceleratorem Nvidia L4.

Utwórz pulę węzłów z akceleratorem 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

Oczekiwany wynik

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

Przygotuj manifest wdrożenia

Aby wdrożyć model, musimy przygotować plik manifestu wdrożenia.

Używamy modelu BGE Base v1.5 z Hugging Face. Informacje o karcie modelu znajdziesz tutaj. Aby wdrożyć model, możemy użyć gotowych instrukcji z Hugging Face i pakietu wdrożeniowego z GitHuba.

Klonowanie pakietu

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

Zmodyfikuj plik manifestu, zastępując wartość cloud.google.com/gke-accelerator wartością nvidia-l4 i dodając limity do zasobów.

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

Oto poprawiony plik manifestu.

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

Wdróż model

Musimy przygotować konto usługi i przestrzeń nazw na potrzeby wdrożenia.

Utwórz przestrzeń nazw Kubernetes hf-gke-namespace.

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

Tworzenie konta usługi Kubernetes

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

Wdróż model

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

Sprawdzanie wdrożeń

kubectl get pods

Weryfikowanie usługi modelu

kubectl get service tei-service

Ma on pokazywać typ uruchomionej usługi ClusterIP.

Przykładowe dane wyjściowe:

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

Adres CLUSTER-IP usługi będzie używany jako adres punktu końcowego. Model może odpowiadać za pomocą identyfikatora URI http://34.118.233.48:8080/embed. Będzie on używany później, gdy zarejestrujesz model w AlloyDB Omni.

Możemy to przetestować, odsłaniając port za pomocą polecenia kubectl port-forward.

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

Przekierowanie portów będzie działać w jednej sesji Cloud Shell, a do przetestowania potrzebujemy innej sesji.

Otwórz kolejną kartę Cloud Shell, klikając znak „+” u góry.

4ca978f5142bb6ce.png

Uruchom polecenie curl w nowej sesji powłoki.

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

Powinien zwrócić tablicę wektorów podobną do tej w przykładowych danych wyjściowych (zaszyfrowanych):

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. Rejestrowanie modelu w AlloyDB Omni

Aby przetestować działanie AlloyDB Omni z wdrożonym modelem, musimy utworzyć bazę danych i zarejestrować model.

Tworzenie bazy danych

Utwórz maszynę wirtualną GCE jako punkt przeskoku, połącz się z AlloyDB Omni z klienckiej maszyny wirtualnej i utwórz bazę danych.

Potrzebujemy skrzynki przeskokowej, ponieważ zewnętrzny system równoważenia obciążenia GKE dla Omni zapewnia dostęp z VPC za pomocą prywatnego adresowania IP, ale nie umożliwia nawiązywania połączenia spoza VPC. Jest on ogólnie bezpieczniejszy i nie ujawnia instancji bazy danych w internecie. Aby uzyskać więcej informacji, zapoznaj się z diagramem.

391e4244b25a7db0.png

Aby utworzyć maszynę wirtualną w sesji Cloud Shell, wykonaj te czynności:

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

Aby znaleźć adres IP punktu końcowego AlloyDB Omni, użyj kubectl w Cloud Shell:

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

Zanotuj wartość PRIMARYENDPOINT. Oto przykład.

Wynik:

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

Adres IP 10.131.0.33 będzie używany w przykładach do łączenia się z instancją AlloyDB Omni.

Połącz się z maszyną wirtualną za pomocą gcloud:

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

Jeśli pojawi się prośba o wygenerowanie klucza SSH, postępuj zgodnie z instrukcjami. Więcej informacji o połączeniu przez SSH znajdziesz w dokumentacji.

W sesji ssh do maszyny wirtualnej zainstaluj klienta PostgreSQL:

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

Wyeksportuj adres IP systemu równoważenia obciążenia AlloyDB Omni w taki sposób, jak w tym przykładzie (zastąp adres IP adresem IP systemu równoważenia obciążenia):

export INSTANCE_IP=10.131.0.33

Połącz się z AlloyDB Omni. Hasło to VeryStrongPassword ustawione za pomocą hasha w pliku my-omni.yaml:

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

W utworzonej sesji psql wykonaj te czynności:

create database demo;

Zamknij sesję i połącz się z bazą danych demo (lub po prostu uruchom w tej samej sesji polecenie „\c demo”).

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

Tworzenie funkcji przekształcenia

W przypadku modeli wbudowanych innych firm musimy utworzyć funkcje przekształcania, które formatują dane wejściowe i wyjściowe zgodnie z formatem oczekiwanym przez model oraz nasze funkcje wewnętrzne.

Oto funkcja przekształcenia, która obsługuje dane wejściowe:

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

Wykonaj podany kod, gdy jesteś połączony z bazą danych demonstracyjnej, tak jak pokazano na przykładowym wyjściu:

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

A tutaj jest funkcja wyjściowa, która przekształca odpowiedź z modelu w tablicę liczb rzeczywistych:

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

Wykonaj je w ramach tej samej sesji:

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

Rejestrowanie modelu

Teraz możemy zarejestrować model w bazie danych.

Oto procedura rejestrowania modelu o nazwie bge-base-1.5. Zastąp adres IP 34.118.233.48 adresem IP usługi modelu (wyjście z 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');

Wykonaj podany kod, gdy jesteś połączony z bazą danych demonstracyjnej:

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

Możemy przetestować model rejestracji, używając tego zapytania testowego, które powinno zwrócić tablicę liczb rzeczywistych.

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

7. Testowanie modelu w AlloyDB Omni

Wczytaj dane

Aby przetestować działanie AlloyDB Omni z wdrożonym modelem, musimy załadować dane. Użyłem tych samych danych co w jednym z innych laboratoriów kodu dotyczących wyszukiwania wektorów w AlloyDB.

Dane można wczytać za pomocą Google Cloud SDK i oprogramowania klienta PostgreSQL. Możemy użyć tej samej maszyny wirtualnej klienta, która została użyta do utworzenia bazy danych demo. Jeśli używasz domyślnych ustawień obrazu maszyny wirtualnej, pakiet Google Cloud SDK powinien być już zainstalowany. Jeśli jednak użyjesz obrazu niestandardowego bez pakietu Google SDK, możesz go dodać zgodnie z tą dokumentacją.

Wyeksportuj adres IP systemu równoważenia obciążenia AlloyDB Omni w taki sposób, jak w tym przykładzie (zastąp adres IP adresem IP systemu równoważenia obciążenia):

export INSTANCE_IP=10.131.0.33

Połącz się z bazą danych i włącz rozszerzenie pgvector.

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

W sesji psql:

CREATE EXTENSION IF NOT EXISTS vector;

Zamknij sesję psql i w sesji wiersza poleceń wykonaj polecenia, aby wczytać dane do bazy danych demonstracyjnej.

Utwórz tabele:

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

Oczekiwane dane wyjściowe konsoli:

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

Oto lista utworzonych tabel:

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

Dane wyjściowe:

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

Wczytaj dane do tabeli 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"

Oczekiwane dane wyjściowe konsoli:

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

Oto kilka przykładowych wierszy z tabeli 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"

Dane wyjściowe:

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

Wczytaj dane do tabeli 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"

Oczekiwane dane wyjściowe konsoli:

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

Oto kilka przykładowych wierszy z tabeli cymbal_inventory.

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

Dane wyjściowe:

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

Wczytaj dane do tabeli 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"

Oczekiwane dane wyjściowe konsoli:

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

Oto kilka przykładowych wierszy z tabeli cymbal_stores.

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

Dane wyjściowe:

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

Tworzenie wektorów dystrybucyjnych

Nawiązywanie połączenia z demo bazą danych za pomocą psql i tworzenie wektorów dystrybucyjnych dla produktów opisanych w tabeli cymbal_products na podstawie nazw i opisów produktów.

Połącz się z bazą danych demonstracyjnej:

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

Do przechowywania danych zastępczych używamy tabeli cymbal_embedding z zastępczymi kolumnami, a jako tekst wejściowy funkcji – opisu produktu.

Włączanie pomiaru czasu zapytań, aby później porównać je z modelami zdalnymi:

\timing

Uruchom zapytanie, aby utworzyć zaszyfrowane dane:

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

Oczekiwane dane wyjściowe konsoli:

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

W tym przykładzie utworzenie zasobów danych dla 941 rekordów zajęło około 11 sekund.

Wykonywanie testowych zapytań

Połącz się z bazą danych demonstracyjnych za pomocą psql i włącz pomiar czasu, aby mierzyć czas wykonywania zapytań, tak jak w przypadku tworzenia wektorów zagęszczenia.

Znajdź 5 najlepszych produktów pasujących do zapytania „Jakie drzewa owocowe dobrze rosną w tej okolicy?”, używając odległości cosinusowej jako algorytmu wyszukiwania wektorowego.

W sesji psql wykonaj:

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;

Oczekiwane dane wyjściowe konsoli:

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

Zapytanie zostało wykonane w 28 ms i zwróciło listę drzew z tabeli cymbal_products pasujących do żądania i z asortymentem dostępnym w sklepie o numerze 1583.

Tworzenie indeksu ANN

Gdy mamy tylko mały zbiór danych, łatwo jest użyć dokładnego wyszukiwania, które skanuje wszystkie osadzone elementy. Jednak gdy dane się rozrastają, czas wczytywania i odpowiedzi również się wydłuża. Aby zwiększyć wydajność, możesz utworzyć indeksy na podstawie danych do wklejania. Oto przykład, jak to zrobić, korzystając z indeksu Google ScaNN do danych wektorowych.

Jeśli połączenie zostało utracone, ponownie nawiązać je z bazą danych demonstracyjnej:

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

Włącz rozszerzenie alloydb_scann:

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

Tworzenie indeksu:

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

Wypróbuj to samo zapytanie co poprzednio i porównaj wyniki:

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

Czas wykonywania zapytań został nieznacznie skrócony, a ten wzrost będzie bardziej zauważalny w przypadku większych zbiorów danych. Wyniki są dość podobne, a jedynie Cherry zostało zastąpione przez Fremont Cottonwood.

Wypróbuj inne zapytania i dowiedz się więcej o wybieraniu indeksu wektorowego w dokumentacji.

Pamiętaj, że AlloyDB Omni ma więcej funkcji i laboratoriów.

8. Czyszczenie środowiska

Możemy teraz usunąć klaster GKE z AlloyDB Omni i modelem AI

Usuwanie klastra GKE

W Cloud Shell wykonaj te czynności:

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}

Oczekiwane dane wyjściowe konsoli:

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

Usuwanie maszyny wirtualnej

W Cloud Shell wykonaj te czynności:

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

Oczekiwane dane wyjściowe konsoli:

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

Jeśli utworzyłeś/-aś nowy projekt na potrzeby tego samouczka, możesz zamiast tego usunąć cały projekt: https://console.cloud.google.com/cloud-resource-manager

9. Gratulacje

Gratulujemy ukończenia ćwiczenia.

Omówione zagadnienia

  • Jak wdrożyć AlloyDB Omni w klastrze Google Kubernetes
  • Jak połączyć się z AlloyDB Omni
  • Jak wczytywać dane do AlloyDB Omni
  • Wdrażanie w GKE modelu do umieszczania treści w otwartym formacie
  • Jak zarejestrować model wstawiania w AlloyDB Omni
  • Jak generować embeddingi na potrzeby wyszukiwania semantycznego
  • Jak używać wygenerowanych wektorów dystrybucyjnych do wyszukiwania semantycznego w AlloyDB Omni
  • Tworzenie i używanie indeksów wektorowych w AlloyDB

Więcej informacji o pracy z AI w AlloyDB Omni znajdziesz w dokumentacji.

10. Ankieta

Dane wyjściowe:

Jak będziesz korzystać z tego samouczka?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia