בדף מוסבר איך להשתמש ב-Cloud Firestore כדי לבצע חיפושים בווקטורים של השכן הקרוב ביותר (KNN) באמצעות הטכניקות הבאות:
- אחסון ערכי ווקטורים
- יצירה וניהול של אינדקסים של וקטורים של KNN
- איך יוצרים שאילתה של K-nearest-neighbor (KNN) באמצעות אחת מהמדידות הנתמכות של מרחק וקטורים
אחסון של הטמעות וקטורים
אפשר ליצור ערכים וקטוריים כמו הטמעות טקסט מנתוני Cloud Firestore ולשמור אותם במסמכי Cloud Firestore.
פעולת כתיבה עם הטמעת וקטור
הדוגמה הבאה מראה איך לשמור הטמעת וקטור במסמך Cloud Firestore:
Python
Node.js
import { Firestore, FieldValue, } from "@google-cloud/firestore"; const db = new Firestore(); const coll = db.collection('coffee-beans'); await coll.add({ name: "Kahawa coffee beans", description: "Information about the Kahawa coffee beans.", embedding_field: FieldValue.vector([1.0 , 2.0, 3.0]) });
Go
Java
import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.FieldValue; import com.google.cloud.firestore.VectorQuery; CollectionReference coll = firestore.collection("coffee-beans"); Map<String, Object> docData = new HashMap<>(); docData.put("name", "Kahawa coffee beans"); docData.put("description", "Information about the Kahawa coffee beans."); docData.put("embedding_field", FieldValue.vector(new double[] {1.0, 2.0, 3.0})); ApiFuture<DocumentReference> future = coll.add(docData); DocumentReference documentReference = future.get();
הטמעות של וקטורים של מחשוב באמצעות פונקציה של Cloud Functions
כדי לחשב ולאחסן הטמעות וקטורים בכל פעם שמסמך מתעדכן או נוצר, אפשר להגדיר פונקציה של Cloud Functions:
Python
@functions_framework.cloud_event def store_embedding(cloud_event) -> None: """Triggers by a change to a Firestore document. """ firestore_payload = firestore.DocumentEventData() payload = firestore_payload._pb.ParseFromString(cloud_event.data) collection_id, doc_id = from_payload(payload) # Call a function to calculate the embedding embedding = calculate_embedding(payload) # Update the document doc = firestore_client.collection(collection_id).document(doc_id) doc.set({"embedding_field": embedding}, merge=True)
Node.js
/** * A vector embedding will be computed from the * value of the `content` field. The vector value * will be stored in the `embedding` field. The * field names `content` and `embedding` are arbitrary * field names chosen for this example. */ async function storeEmbedding(event: FirestoreEvent<any>): Promise<void> { // Get the previous value of the document's `content` field. const previousDocumentSnapshot = event.data.before as QueryDocumentSnapshot; const previousContent = previousDocumentSnapshot.get("content"); // Get the current value of the document's `content` field. const currentDocumentSnapshot = event.data.after as QueryDocumentSnapshot; const currentContent = currentDocumentSnapshot.get("content"); // Don't update the embedding if the content field did not change if (previousContent === currentContent) { return; } // Call a function to calculate the embedding for the value // of the `content` field. const embeddingVector = calculateEmbedding(currentContent); // Update the `embedding` field on the document. await currentDocumentSnapshot.ref.update({ embedding: embeddingVector, }); }
Go
// Not yet supported in the Go client library
Java
// Not yet supported in the Java client library
יצירה וניהול של אינדקסים וקטוריים
כדי לבצע חיפוש סמוך של המיקום הקרוב ביותר באמצעות הטמעות וקטורים, צריך ליצור אינדקס מתאים. בדוגמאות הבאות מוסבר איך ליצור ולנהל אינדקסים וקטוריים.
יצירת אינדקס וקטור
לפני שיוצרים אינדקס וקטורים, צריך לשדרג לגרסה האחרונה של Google Cloud CLI:
gcloud components update
כדי ליצור אינדקס וקטורים, משתמשים ב-gcloud firestore indexes composite create
:
gcloud
gcloud firestore indexes composite create \ --collection-group=collection-group \ --query-scope=COLLECTION \ --field-config field-path=vector-field,vector-config='vector-configuration' \ --database=database-id
כאשר:
- collection-group הוא המזהה של קבוצת הקולקציות.
- vector-field הוא שם השדה שמכיל את ההטמעה הווקטורית.
- database-id הוא המזהה של מסד הנתונים.
- vector-configuration כולל את הווקטור
dimension
ואת סוג האינדקס.dimension
הוא מספר שלם עד 2048. סוג האינדקס חייב להיותflat
. הפורמט של תצורת האינדקס נקבע באופן הבא:{"dimension":"DIMENSION", "flat": "{}"}
.
הדוגמה הבאה יוצרת אינדקס מורכב, כולל אינדקס וקטורים לשדה vector-field
ואינדקס עולה לשדה color
. אפשר להשתמש בסוג הזה של אינדקס כדי לסנן מראש את הנתונים לפני חיפוש של השכן הקרוב ביותר.
gcloud
gcloud firestore indexes composite create \ --collection-group=collection-group \ --query-scope=COLLECTION \ --field-config=order=ASCENDING,field-path="color" \ --field-config field-path=vector-field,vector-config='{"dimension":"1024", "flat": "{}"}' \ --database=database-id
הצגת רשימה של כל מדדי הווקטור
gcloud
gcloud firestore indexes composite list --database=database-id
מחליפים את database-id במזהה מסד הנתונים.
מחיקת אינדקס וקטור
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
כאשר:
- index-id הוא המזהה של האינדקס שרוצים למחוק.
כדי לאחזר את מזהה האינדקס, משתמשים ב-
indexes composite list
. - database-id הוא המזהה של מסד הנתונים.
תיאור של אינדקס וקטור
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
איפה:
- index-id הוא המזהה של האינדקס שרוצים לתאר. אפשר להשתמש גם ב-
indexes composite list
כדי לאחזר את מזהה האינדקס. - database-id הוא המזהה של מסד הנתונים.
שליחת שאילתת 'שכנות קרובה ביותר'
אפשר לבצע חיפוש לפי דמיון כדי למצוא את השכנים הקרובים ביותר של הטמעת וקטורים. כדי לבצע חיפושים לפי דמיון, נדרשים אינדקסים וקטוריים. אם אינדקס לא קיים, Cloud Firestore מציע ליצור אינדקס באמצעות gcloud CLI.
בדוגמה הבאה מוצג חיפוש של 10 השכנים הקרובים ביותר של וקטור השאילתה.
Python
Node.js
import { Firestore, FieldValue, VectorQuery, VectorQuerySnapshot, } from "@google-cloud/firestore"; // Requires a single-field vector index const vectorQuery: VectorQuery = coll.findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN' }); const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
מרחקים וקטוריים
שאילתות השכנות הקרובות ביותר תומכות באפשרויות הבאות למרחק וקטורי:
EUCLIDEAN
: מדידת המרחק האוקלידי בין הווקטורים. מידע נוסף זמין במאמר Euclidean.COSINE
: השוואת וקטורים על סמך הזווית ביניהם, שמאפשרת למדוד דמיון שלא מבוסס על עוצמת הוקטורים. מומלץ להשתמש ב-DOT_PRODUCT
עם וקטורים מנורמלים ליחידות במקום במרחק COSINE, כי הוא שווה מבחינה מתמטית ומניב ביצועים טובים יותר. למידע נוסף, ראו דמיון קוסינוס.DOT_PRODUCT
: דומה ל-COSINE
, אבל מושפע מהמידה של הוקטורים. מידע נוסף זמין במאמר מכפלת מטריצות.
צריך לבחור את מידת המרחק
בהתאם לכך שכל הטמעות הווקטורים שלכם מנורמלות או לא, תוכלו לקבוע באיזו מדדת מרחק להשתמש כדי למצוא את מדדת המרחק. למיקום וקטורי מנורמלי יש עוצמה (אורך) של 1.0 בדיוק.
בנוסף, אם אתם יודעים לגבי איזו מדידת מרחק אומן המודל שלכם, השתמשו במדד המרחק הזה כדי לחשב את המרחק בין הטמעות הווקטורים.
נתונים מנורמלים
אם יש לכם מערך נתונים שבו כל ההטמעות הווקטוריות מנורמלות, כל שלושת מדדי המרחק יספקו את אותן תוצאות חיפוש סמנטיות. למעשה, כל מדידת מרחק מחזירה ערך שונה, אבל הערכים האלה ממוינים באותו אופן. כשהטמעות (embeddings) מנורמלות, DOT_PRODUCT
היא בדרך כלל היעילה ביותר מבחינה חישובית, אבל ההבדל זניח ברוב המקרים. עם זאת, אם האפליקציה שלכם תלויה מאוד בביצועים, יכול להיות ש-DOT_PRODUCT
יעזור לכם לשפר את הביצועים.
נתונים לא מנורמלים
אם יש לכם מערך נתונים שבו הטמעות הווקטור לא מנורמלות, לא נכון מבחינה מתמטית להשתמש ב-DOT_PRODUCT
כמדד מרחק, כי מכפלת המכונים לא מודדת מרחק. בהתאם לאופן שבו היצירות הוטמעו ואילו סוג חיפוש מועדף, מדד המרחק COSINE
או EUCLIDEAN
מניב תוצאות חיפוש שטובות יותר באופן סובייקטיבי ממדדי המרחק האחרים.
יכול להיות שתצטרכו להתנסות ב-COSINE
או ב-EUCLIDEAN
כדי לקבוע איזו מהן מתאימה ביותר לתרחיש לדוגמה שלכם.
לא בטוח אם הנתונים מנורמלים או לא מנורמלים
אם אתם לא בטוחים אם הנתונים מנורמלים ואתם רוצים להשתמש ב-DOT_PRODUCT
, מומלץ להשתמש במקום זאת ב-COSINE
.
COSINE
היא כמו DOT_PRODUCT
עם נירמול מובנה.
המרחק שנמדד באמצעות COSINE
נע בין 0
ל-2
. תוצאה קרובה ל-0
מציינת שהווקטורים דומים מאוד.
סינון מסמכים מראש
כדי לסנן מסמכים מראש לפני שמוצאים את השכנים הקרובים ביותר, אפשר לשלב חיפוש של דמיון עם אופרטורים אחרים של שאילתות. יש תמיכה במסננים המשולבים and
ו-or
. מידע נוסף על מסנני שדות נתמכים זמין במאמר אופרטורים של שאילתות.
Python
Node.js
// Similarity search with pre-filter // Requires composite vector index const preFilteredVectorQuery: VectorQuery = coll .where("color", "==", "red") .findNearest({ vectorField: "embedding_field", queryVector: [3.0, 1.0, 2.0], limit: 5, distanceMeasure: "EUCLIDEAN", }); const vectorQueryResults = await preFilteredVectorQuery.get();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery preFilteredVectorQuery = coll .whereEqualTo("color", "red") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = preFilteredVectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
אחזור המרחק המחושב של הווקטור
כדי לאחזר את המרחק המחושב של הווקטור, מקצים שם למאפיין הפלט distance_result_field
בשאילתה FindNearest
, כפי שמתואר בדוגמה הבאה:
Python
Node.js
const vectorQuery: VectorQuery = coll.findNearest( { vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceResultField: 'vector_distance' }); const snapshot: VectorQuerySnapshot = await vectorQuery.get(); snapshot.forEach((doc) => { console.log(doc.id, ' Distance: ', doc.get('vector_distance')); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder().setDistanceResultField("vector_distance").build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
אם רוצים להשתמש במסכת שדות כדי להחזיר קבוצת משנה של שדות מסמך יחד עם distanceResultField
, עליך לכלול גם את הערך של distanceResultField
במסכת השדות, כפי שמוצג בדוגמה הבאה:
Python
Node.js
const vectorQuery: VectorQuery = coll .select('name', 'description', 'vector_distance') .findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceResultField: 'vector_distance' });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll .select("name", "description", "vector_distance") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceResultField("vector_distance") .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
יש לציין סף מרחק
תוכלו לציין סף דמיון שמחזיר רק מסמכים שנמצאים בתוך הסף. ההתנהגות של שדה הסף תלויה במידת המרחק שבחרתם:
- המרחקים
EUCLIDEAN
ו-COSINE
מגבילים את הסף למסמכים שבהם המרחק הוא קטן מהסף שצוין או שווה לו. מדדי המרחק האלה פוחתים ככל שהווקטורים הופכים דומים יותר. DOT_PRODUCT
distance מגביל את הסף למסמכים שבהם המרחק גדול מהסף שצוין או שווה לו. המרחקים של נקודות המוצרים גדלים ככל שהווקטורים הופכים דומים יותר.
בדוגמה הבאה מוסבר איך לציין ערך סף של מרחק כדי להציג עד 10 מסמכים קרובים ביותר שנמצאים במרחק של עד 4.5 יחידות באמצעות מדד המרחק EUCLIDEAN
:
Python
Node.js
const vectorQuery: VectorQuery = coll.findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceThreshold: 4.5 }); const snapshot: VectorQuerySnapshot = await vectorQuery.get(); snapshot.forEach((doc) => { console.log(doc.id); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceThreshold(4.5) .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId()); }
מגבלות
כשעובדים עם הטמעות וקטורים, חשוב לשים לב למגבלות הבאות:
- מאפיין ההטמעה המקסימלי הנתמך הוא 2048. כדי לאחסן אינדקסים גדולים יותר, צריך להשתמש בצמצום המאפיינים.
- המספר המקסימלי של מסמכים שצריך להחזיר משאילתה השכנה הקרובה ביותר הוא 1,000.
- חיפוש וקטורי לא תומך במאזינים לתמונות מצב בזמן אמת.
- רק ספריות הלקוח Python, Node.js, Go ו-Java תומכות בחיפוש וקטורי.
המאמרים הבאים
- שיטות מומלצות לעבודה עם Cloud Firestore.
- הסבר על קריאות וכתיבה בקנה מידה נרחב