1. מבוא
בקודלאב הזה תלמדו איך לפרוס את AlloyDB Omni ב-GKE ולהשתמש בו עם מודל הטמעה פתוח שנפרס באותו אשכול Kubernetes. פריסה של מודל לצד מכונה של מסד נתונים באותו אשכול GKE מפחיתה את זמן האחזור ואת יחסי התלות בשירותי צד שלישי. בנוסף, יכול להיות שהדרישה הזו תהיה חלק מדרישות האבטחה כשהנתונים לא אמורים לצאת מהארגון ואי אפשר להשתמש בשירותים של צד שלישי.
דרישות מוקדמות
- הבנה בסיסית של Google Cloud, מסוף
- מיומנויות בסיסיות בממשק שורת הפקודה וב-Cloud Shell
מה תלמדו
- איך פורסים את AlloyDB Omni באשכול Google Kubernetes
- איך מתחברים ל-AlloyDB Omni
- איך טוענים נתונים ל-AlloyDB Omni
- איך פורסים מודל הטמעה פתוח ב-GKE
- איך רושמים מודל הטמעה ב-AlloyDB Omni
- איך יוצרים הטמעות (embeddings) לחיפוש סמנטי
- איך משתמשים בהטמעות (embeddings) שנוצרו לחיפוש סמנטי ב-AlloyDB Omni
- איך יוצרים אינדקסים של וקטורים ומשתמשים בהם ב-AlloyDB
מה צריך להכין
- חשבון Google Cloud ופרויקט ב-Google Cloud
- דפדפן אינטרנט כמו Chrome שתומך במסוף Google Cloud וב-Cloud Shell
2. הגדרה ודרישות
הגדרת סביבה בקצב אישי
- נכנסים למסוף Google Cloud ויוצרים פרויקט חדש או משתמשים מחדש בפרויקט קיים. אם עדיין אין לכם חשבון Gmail או חשבון Google Workspace, עליכם ליצור חשבון.
- שם הפרויקט הוא השם המוצג של המשתתפים בפרויקט. זוהי מחרוזת תווים שלא משמשת את Google APIs. תמיד אפשר לעדכן אותו.
- מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud ואי אפשר לשנות אותו אחרי שמגדירים אותו. מסוף Cloud יוצר מחרוזת ייחודית באופן אוטומטי. בדרך כלל לא משנה מה המחרוזת הזו. ברוב ה-codelabs תצטרכו להפנות למזהה הפרויקט (בדרך כלל מזהים אותו בתור
PROJECT_ID
). אם המזהה שנוצר לא מוצא חן בעיניכם, תוכלו ליצור מזהה אקראי אחר. לחלופין, אפשר לנסות כתובת משלכם ולבדוק אם היא זמינה. לא ניתן לשנות את השם אחרי השלב הזה, והוא יישאר למשך כל פרק הזמן של הפרויקט. - לידיעתכם, יש ערך שלישי, מספר פרויקט, שמשתמשים בו בחלק מממשקי ה-API. מידע נוסף על כל שלושת הערכים האלה זמין במסמכי העזרה.
- בשלב הבא, כדי להשתמש במשאבים או ב-API של Cloud, תצטרכו להפעיל את החיוב במסוף Cloud. השלמת הקודלאב הזה לא תעלה הרבה, אם בכלל. כדי להשבית את המשאבים ולמנוע חיובים אחרי סיום המדריך, אפשר למחוק את המשאבים שיצרתם או למחוק את הפרויקט. משתמשים חדשים ב-Google Cloud זכאים להשתתף בתוכנית תקופת ניסיון בחינם בסך 300$.
הפעלת Cloud Shell
אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל בסדנת הקוד הזו נשתמש ב-Google Cloud Shell, סביבת שורת פקודה שפועלת ב-Cloud.
במסוף Google Cloud, לוחצים על סמל Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:
תהליך ההקצאה והחיבור לסביבת העבודה אמור להימשך רק כמה רגעים. בסיום, אמור להופיע משהו כזה:
המכונה הווירטואלית הזו כוללת את כל הכלים הדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, משפרת מאוד את ביצועי הרשת ואת האימות. אתם יכולים לבצע את כל העבודה בקודלאב הזה בדפדפן. אין צורך להתקין שום דבר.
3. לפני שמתחילים
הפעלת ה-API
פלט:
ב-Cloud Shell, מוודאים שמזהה הפרויקט מוגדר:
PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID
אם הוא לא מוגדר בהגדרות של Cloud Shell, מגדירים אותו באמצעות הפקודות הבאות:
export PROJECT_ID=<your project>
gcloud config set project $PROJECT_ID
מפעילים את כל השירותים הנדרשים:
gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
הפלט הצפוי
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. פריסת AlloyDB Omni ב-GKE
כדי לפרוס את AlloyDB Omni ב-GKE, צריך להכין אשכול Kubernetes בהתאם לדרישות שמפורטות בדרישות של אופרטור AlloyDB Omni.
יצירת אשכול GKE
אנחנו צריכים לפרוס אשכול GKE רגיל עם הגדרת מאגר מספקת לפריסה של אשכול עם מכונה של AlloyDB Omni. אנחנו צריכים לפחות 2 מעבדים ו-8GB של זיכרון RAM ל-Omni, עם מקום נוסף לשירותי הפעלה ומעקב.
מגדירים את משתני הסביבה לפריסה.
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=e2-standard-4
לאחר מכן, משתמשים ב-gcloud כדי ליצור את האשכול הרגיל של 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
הפלט הצפוי במסוף:
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)$
הכנת האשכול
אנחנו צריכים להתקין רכיבים נדרשים כמו שירות cert-manager. אפשר לפעול לפי השלבים שמפורטים במסמכי התיעוד של התקנת cert-manager.
אנחנו משתמשים בכלי שורת הפקודה של Kubernetes, kubectl, שכבר מותקן ב-Cloud Shell. לפני שמשתמשים בכלי, צריך לקבל פרטי כניסה לאשכול.
gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}
עכשיו אפשר להשתמש ב-kubectl כדי להתקין את cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
הפלט הצפוי במסוף(הושמט מידע):
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
התקנה של AlloyDB Omni
התקנה של AlloyDB Omni operator אפשר להתקין באמצעות הכלי helm.
מריצים את הפקודה הבאה כדי להתקין את אופרטור 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
הפלט הצפוי במסוף(הושמט מידע):
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:~$
אחרי שתתקינו את אופרטור AlloyDB Omni, נוכל להמשיך לפריסה של אשכול מסדי הנתונים שלנו.
דוגמה למניפסט פריסה עם הפרמטר googleMLExtension מופעל ומאזן עומסים פנימי (פרטי):
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
הערך הסודי של הסיסמה הוא ייצוג Base64 של מילת הסיסמה 'VeryStrongPassword'. הדרך האמינה יותר היא להשתמש במנהל הסודות של Google כדי לאחסן את ערך הסיסמה. מידע נוסף זמין במסמכים.
שומרים את המניפסט בתור my-omni.yaml כדי להחיל אותו בשלב הבא. אם אתם נמצאים ב-Cloud Shell, תוכלו לעשות זאת באמצעות העורך בלחיצה על הלחצן 'Open Editor' (פתיחת העורך) בפינה השמאלית העליונה של הטרמינל.
אחרי שמירת הקובץ בשם my-omni.yaml, חוזרים למסוף בלחיצה על הלחצן 'פתיחת מסוף'.
מחילים את המניפסט my-omni.yaml על האשכול באמצעות הכלי kubectl:
kubectl apply -f my-omni.yaml
הפלט הצפוי במסוף:
secret/db-pw-my-omni created dbcluster.alloydbomni.dbadmin.goog/my-omni created
בודקים את הסטטוס של האשכולות my-omni באמצעות הכלי kubectl:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
במהלך הפריסה, האשכולות עוברים שלבים שונים ובסופו של דבר אמורים להגיע למצב DBClusterReady.
הפלט הצפוי במסוף:
$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default NAME PRIMARYENDPOINT PRIMARYPHASE DBCLUSTERPHASE HAREADYSTATUS HAREADYREASON my-omni 10.131.0.33 Ready DBClusterReady
התחברות ל-AlloyDB Omni
חיבור באמצעות Kubernetes Pod
כשהאשכול יהיה מוכן, נוכל להשתמש בקבצים הבינאריים של לקוח PostgreSQL ב-pod של המכונה של AlloyDB Omni. אנחנו מחפשים את מזהה ה-pod, ואז משתמשים ב-kubectl כדי להתחבר ישירות ל-pod ולהריץ את תוכנת הלקוח. הסיסמה היא VeryStrongPassword כפי שהוגדרה באמצעות הגיבוב בקובץ 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
פלט לדוגמה במסוף:
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. פריסת מודל AI ב-GKE
כדי לבדוק את השילוב של AlloyDB Omni AI עם מודלים מקומיים, צריך לפרוס מודל באשכול.
יצירת מאגר צמתים לדגם
כדי להריץ את המודל, צריך להכין מאגר צמתים להרצת היקש. מבחינת ביצועים, הגישה הטובה ביותר היא להשתמש במאגר עם מאיצי גרפיקה באמצעות הגדרת צומת כמו g2-standard-8 עם מאץ' Nvidia ברמה L4.
יוצרים את מאגר הצמתים עם מואץ 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
הפלט הצפוי
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
הכנת מניפסט הפריסה
כדי לפרוס את המודל, צריך להכין מניפסט פריסה.
אנחנו משתמשים במודל הטמעת הנתונים (embedding) BGE Base v1.5 של Hugging Face. אפשר לקרוא את כרטיס הדגם כאן. כדי לפרוס את המודל, אפשר להשתמש בהוראות שכבר הוכנו מ-Hugging Face ובחבילת הפריסה מ-GitHub.
שכפול החבילה
git clone https://github.com/huggingface/Google-Cloud-Containers
עורכים את המניפסט, מחליפים את הערך cloud.google.com/gke-accelerator ב-nvidia-l4 ומוסיפים מגבלות למשאבים.
vi Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config/deployment.yaml
הנה מניפסט מתוקן.
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
פריסת המודל
אנחנו צריכים להכין חשבון שירות ומרחב שמות לפריסה.
יוצרים מרחב שמות ב-Kubernetes בשם hf-gke-namespace.
export NAMESPACE=hf-gke-namespace
kubectl create namespace $NAMESPACE
יצירת חשבון שירות ב-Kubernetes
export SERVICE_ACCOUNT=hf-gke-service-account
kubectl create serviceaccount $SERVICE_ACCOUNT --namespace $NAMESPACE
פריסת המודל
kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config
אימות הפריסות
kubectl get pods
אימות שירות המודל
kubectl get service tei-service
אמור להופיע סוג השירות שפועל ClusterIP
פלט לדוגמה:
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 של השירות היא זו שבה נשתמש ככתובת נקודת הקצה. הטמעת המודל יכולה להגיב באמצעות URI http://34.118.233.48:8080/embed. הוא ישמש מאוחר יותר כשתרשמו את המודל ב-AlloyDB Omni.
אפשר לבדוק אותו על ידי חשיפת היציאה באמצעות הפקודה kubectl port-forward.
kubectl port-forward service/tei-service 8080:8080
העברת היציאות תפעל בסשן אחד של Cloud Shell, ונצטרך סשן נוסף כדי לבדוק אותה.
פותחים כרטיסייה נוספת ב-Cloud Shell באמצעות הסימן '+' בחלק העליון.
מריצים את הפקודה curl בסשן המעטפת החדש.
curl http://localhost:8080/embed \
-X POST \
-d '{"inputs":"Test"}' \
-H 'Content-Type: application/json'
הפונקציה אמורה להחזיר מערך וקטורים כמו בדוגמה הבאה (הנתונים קודדו):
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. רישום המודל ב-AlloyDB Omni
כדי לבדוק איך AlloyDB Omni פועל עם המודל שנפרס, צריך ליצור מסד נתונים ולרשום את המודל.
יצירת מסד נתונים
יוצרים מכונה וירטואלית ב-GCE כקופסת מעבר, מתחברים ל-AlloyDB Omni מהמכונה הווירטואלית של הלקוח ויוצרים מסד נתונים.
אנחנו צריכים את תיבת ה-jump box כי מאזן העומסים החיצוני של GKE ל-Omni מעניק גישה מ-VPC באמצעות כתובות IP פרטיות, אבל לא מאפשר להתחבר מחוץ ל-VPC. היא מאובטחת יותר באופן כללי, והיא לא חושפת את מכונה של מסד הנתונים לאינטרנט. לקבלת הבהרה, אפשר לעיין בתרשים.
כדי ליצור מכונה וירטואלית בסשן של Cloud Shell, מריצים את הפקודה:
export ZONE=us-central1-a
gcloud compute instances create instance-1 \
--zone=$ZONE
מחפשים את כתובת ה-IP של נקודת הקצה של AlloyDB Omni באמצעות kubectl ב-Cloud Shell:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
כותבים את הערך של PRIMARYENDPOINT. כאן מוצגת דוגמה.
התוצר הסופי:
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:~$
כתובת ה-IP 10.131.0.33 היא כתובת ה-IP שבה נשתמש בדוגמאות שלנו כדי להתחבר למכונה של AlloyDB Omni.
מתחברים ל-VM באמצעות gcloud:
gcloud compute ssh instance-1 --zone=$ZONE
אם מופיעה בקשה ליצירת מפתח SSH, פועלים לפי ההוראות. מידע נוסף על חיבור SSH זמין במסמכי התיעוד.
בפעילות ה-SSH למכונה הווירטואלית, מתקינים את לקוח PostgreSQL:
sudo apt-get update
sudo apt-get install --yes postgresql-client
מייצאים את כתובת ה-IP של מאזן העומסים של AlloyDB Omni, כמו בדוגמה הבאה (מחליפים את כתובת ה-IP בכתובת ה-IP של מאזן העומסים):
export INSTANCE_IP=10.131.0.33
מתחברים ל-AlloyDB Omni. הסיסמה היא VeryStrongPassword כפי שהוגדר באמצעות הגיבוב בקובץ my-omni.yaml:
psql "host=$INSTANCE_IP user=postgres sslmode=require"
בסשן psql שנוצר, מריצים את הפקודה:
create database demo;
יוצאים מהסשן ומתחברים לדמו של מסד הנתונים (או פשוט מריצים את "\c demo" באותו סשן)
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
יצירת פונקציות טרנספורמציה
במודלים של הטמעה (embedding) של צד שלישי, אנחנו צריכים ליצור פונקציות טרנספורמציה שמעבירות את הקלט והפלט לפורמט שצפוי למודל ולפונקציות הפנימיות שלנו.
זוהי פונקציית הטרנספורמציה שמטפלת בקלט:
-- 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;
$$;
מריצים את הקוד שסופק בזמן החיבור למסד הנתונים לדוגמה, כפי שמוצג בפלט לדוגמה:
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=#
זוהי פונקציית הפלט שממירה את התגובה מהמודל למערך של המספרים הממשיים:
-- 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;
$$;
מריצים אותו באותו סשן:
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=#
רישום המודל
עכשיו אפשר לרשום את המודל במסד הנתונים.
זוהי קריאת הפרוצדורה לרישום המודל בשם bge-base-1.5, rחליפת כתובת ה-IP 34.118.233.48 בכתובת ה-IP של שירות המודל (הפלט מ-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');
מריצים את הקוד שסופק בזמן החיבור למסד הנתונים לדוגמה:
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=#
אפשר לבדוק את מודל הרישום באמצעות שאילתת הבדיקה הבאה, שאמורה להחזיר מערך של מספרים ממשיים.
select google_ml.embedding('bge-base-1.5','What is AlloyDB Omni?');
7. בדיקת המודל ב-AlloyDB Omni
טעינה של נתונים
כדי לבדוק איך AlloyDB Omni פועל עם המודל שנפרס, אנחנו צריכים לטעון נתונים. השתמשתי באותו נתונים כמו באחד מהקודלאבים האחרים לחיפוש וקטורים ב-AlloyDB.
אחת מהדרכים לטעון את הנתונים היא להשתמש ב-Google Cloud SDK ובתוכנת הלקוח של PostgreSQL. אנחנו יכולים להשתמש באותה מכונה וירטואלית של לקוח ששימשה ליצירת מסד הנתונים demo. אם השתמשתם בברירת המחדל לתמונת ה-VM, Google Cloud SDK כבר אמור להיות מותקן שם. אבל אם השתמשתם בתמונה בהתאמה אישית ללא Google SDK, תוכלו להוסיף אותה לפי המסמכים.
מייצאים את כתובת ה-IP של מאזן העומסים של AlloyDB Omni, כמו בדוגמה הבאה (מחליפים את כתובת ה-IP בכתובת ה-IP של מאזן העומסים):
export INSTANCE_IP=10.131.0.33
מתחברים למסד הנתונים ומפעילים את התוסף pgvector.
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
בסשן psql:
CREATE EXTENSION IF NOT EXISTS vector;
יוצאים מהסשן של psql ומריצים פקודות בסשן של שורת הפקודה כדי לטעון את הנתונים למסד הנתונים לדוגמה.
יוצרים את הטבלאות:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"
הפלט הצפוי במסוף:
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:~$
זו רשימת הטבלאות שנוצרו:
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
פלט:
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:~$
טוענים נתונים לטבלה 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"
הפלט הצפוי במסוף:
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:~$
לפניכם דוגמה לכמה שורות מהטבלה 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"
פלט:
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:~$
טוענים נתונים לטבלה 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"
הפלט הצפוי במסוף:
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:~$
לפניכם דוגמה לכמה שורות מהטבלה cymbal_inventory.
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
פלט:
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:~$
טוענים נתונים לטבלה 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"
הפלט הצפוי במסוף:
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:~$
לפניכם דוגמה לכמה שורות מהטבלה cymbal_stores.
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
פלט:
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:~$
יצירת הטמעות (embeddings)
מתחברים למסד הנתונים לדוגמה באמצעות psql ויוצרים הטמעות (embeddings) של המוצרים המתוארים בטבלה cymbal_products על סמך השמות והתיאורים של המוצרים.
מתחברים למסד הנתונים לדוגמה:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
אנחנו משתמשים בטבלה cymbal_embedding עם הטמעת עמודה כדי לאחסן את הטמעות הנתונים שלנו, ומשתמשים בתיאור המוצר כקלט הטקסט של הפונקציה.
מפעילים את תזמון השאילתות כדי להשוות אותן מאוחר יותר למודלים מרוחקים.
\timing
מריצים את השאילתה כדי ליצור את הטמעות הנתונים (embeddings):
INSERT INTO cymbal_embedding(uniq_id,embedding) SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;
הפלט הצפוי במסוף:
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=#
בדוגמה הזו, היצירה של הטמעות (embeddings) ל-941 רשומות נמשכה כ-11 שניות.
הרצת שאילתות בדיקה
מתחברים למסד הנתונים לדוגמה באמצעות psql ומפעילים את הטיימינג כדי למדוד את זמן הביצוע של השאילתות שלנו, כמו שעשינו בבניית הטמעות (embeddings).
נמצא את 5 המוצרים המובילים שתואמים לבקשה כמו "אילו עצי פרי גדלים כאן טוב?" באמצעות מרחק קוסינוס כאלגוריתם לחיפוש וקטור.
בסשן psql, מריצים את הפקודה:
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;
הפלט הצפוי במסוף:
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=#
השאילתה בוצעה תוך 28 אלפיות השנייה והחזירה רשימה של עצים מהטבלה cymbal_products שתואמים לבקשה ועם מלאי זמין בחנות מספר 1583.
יצירת אינדקס ANN
כשיש לנו רק מערך נתונים קטן, קל להשתמש בחיפוש מדויק שסורק את כל הטמעות הקוד (embeddings), אבל כשהנתונים גדלים, גם העומס וזמן התגובה גדלים. כדי לשפר את הביצועים, אפשר ליצור אינדקסים לנתוני ההטמעה. הנה דוגמה לאופן שבו עושים זאת באמצעות אינדקס Google ScaNN לנתוני וקטורים.
אם החיבור למסד הנתונים לדוגמה אבד, צריך להתחבר מחדש:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
מפעילים את התוסף alloydb_scann:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
יצירת האינדקס:
CREATE INDEX cymbal_embedding_scann ON cymbal_embedding USING scann (embedding cosine);
מנסים את אותה שאילתה כמו קודם ומשווים את התוצאות:
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=#
זמן ביצוע השאילתה הופחת מעט, והיתרון הזה יהיה משמעותי יותר במערכי נתונים גדולים יותר. התוצאות דומות למדי, ורק Cherry הוחלפה על ידי Fremont Cottonwood.
אפשר לנסות שאילתות אחרות ולקרוא מידע נוסף על בחירת אינדקס וקטור במסמכי העזרה.
זכרו: ב-AlloyDB Omni יש עוד תכונות וקורסים ב-Labs.
8. פינוי הסביבה
עכשיו אפשר למחוק את אשכול ה-GKE עם AlloyDB Omni ועם מודל AI
מחיקת אשכול GKE
ב-Cloud Shell, מריצים את הפקודה:
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}
הפלט הצפוי במסוף:
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
מחיקת מכונה וירטואלית
ב-Cloud Shell, מריצים את הפקודה:
export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
--project=${PROJECT_ID} \
--zone=${ZONE}
הפלט הצפוי במסוף:
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
אם יצרתם פרויקט חדש לקודלאב הזה, תוכלו למחוק את הפרויקט המלא במקום זאת: https://console.cloud.google.com/cloud-resource-manager
9. מזל טוב
כל הכבוד על השלמת ה-Codelab.
מה עסקנו בו
- איך פורסים את AlloyDB Omni באשכול Google Kubernetes
- איך מתחברים ל-AlloyDB Omni
- איך טוענים נתונים ל-AlloyDB Omni
- איך פורסים מודל הטמעה פתוח ב-GKE
- איך רושמים מודל הטמעה ב-AlloyDB Omni
- איך יוצרים הטמעות (embeddings) לחיפוש סמנטי
- איך משתמשים בהטמעות (embeddings) שנוצרו לחיפוש סמנטי ב-AlloyDB Omni
- איך יוצרים אינדקסים של וקטורים ומשתמשים בהם ב-AlloyDB
מידע נוסף על עבודה עם AI ב-AlloyDB Omni זמין במסמכי העזרה.
10. סקר
פלט: