इस पेज पर, Cloud Firestore का इस्तेमाल करके, सबसे नज़दीकी (केएनएन) वेक्टर खोज करने का तरीका बताया गया है. इसके लिए, इन तकनीकों का इस्तेमाल किया जाता है:
- स्टोर वेक्टर वैल्यू
- केएनएन वेक्टर इंडेक्स बनाना और मैनेज करना
- वेक्टर के लिए काम करने वाले किसी भी दूरी के मेज़र का इस्तेमाल करके, सबसे नज़दीकी के-नेबर (केएनएन) क्वेरी बनाएं
वेक्टर एम्बेडमेंट सेव करना
अपने Cloud Firestore डेटा से, टेक्स्ट एम्बेड जैसी वेक्टर वैल्यू बनाई जा सकती हैं और उन्हें Cloud Firestore दस्तावेज़ों में सेव किया जा सकता है.
वेक्टर एम्बेडिंग के साथ ऑपरेशन लिखना
यहां दिए गए उदाहरण में, किसी Cloud Firestore दस्तावेज़ में वेक्टर एम्बेडमेंट को सेव करने का तरीका बताया गया है:
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]) });
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_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)
/** * 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, }); }
// Not yet supported in the Go client library
// Not yet supported in the Java client library
वेक्टर इंडेक्स बनाना और मैनेज करना
वेक्टर एम्बेडमेंट की मदद से, सबसे मिलते-जुलते एलिमेंट की खोज करने से पहले, आपको उससे जुड़ा इंडेक्स बनाना होगा. यहां दिए गए उदाहरणों में, Google Cloud CLI की मदद से वेक्टर इंडेक्स बनाने और मैनेज करने का तरीका बताया गया है. वेक्टर इंडेक्स को Firebase CLI और Terraform की मदद से भी मैनेज किया जा सकता है.
वेक्टर इंडेक्स बनाना
वेक्टर इंडेक्स बनाने से पहले, Google Cloud CLI को नए वर्शन में अपग्रेड करें:
gcloud components update
वेक्टर इंडेक्स बनाने के लिए, gcloud firestore indexes composite create
का इस्तेमाल करें:
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
where:
- collection-group, कलेक्शन ग्रुप का आईडी है.
- vector-field उस फ़ील्ड का नाम है जिसमें वेक्टर एम्बेडिंग होती है.
- database-id, डेटाबेस का आईडी है.
- vector-configuration में वेक्टर
dimension
और इंडेक्स टाइप शामिल होता है.dimension
, 2048 तक का पूर्णांक हो सकता है. इंडेक्स टाइपflat
होना चाहिए. इंडेक्स कॉन्फ़िगरेशन को इस तरह से फ़ॉर्मैट करें:{"dimension":"DIMENSION", "flat": "{}"}
.
नीचे दिए गए उदाहरण में, फ़ील्ड vector-field
के लिए वेक्टर इंडेक्स और फ़ील्ड color
के लिए, बढ़ते क्रम में इंडेक्स बनाने का तरीका बताया गया है. इस तरह के इंडेक्स का इस्तेमाल, नियरेस्ट नेबर सर्च से पहले डेटा को पहले से फ़िल्टर करने के लिए किया जा सकता है.
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 firestore indexes composite list --database=database-id
database-id की जगह डेटाबेस का आईडी डालें.
वेक्टर इंडेक्स मिटाना
gcloud firestore indexes composite deleteindex-id --database=database-id
where:
- index-id, मिटाए जाने वाले इंडेक्स का आईडी है.
इंडेक्स आईडी वापस पाने के लिए,
indexes composite list
का इस्तेमाल करें. - database-id, डेटाबेस का आईडी है.
वेक्टर इंडेक्स के बारे में बताना
gcloud firestore indexes composite describeindex-id --database=database-id
where:
- index-id, उस इंडेक्स का आईडी है जिसकी जानकारी देनी है. इंडेक्स आईडी वापस पाने के लिए, or
indexes composite list
का इस्तेमाल करें. - database-id, डेटाबेस का आईडी है.
नियरेस्ट नेबर क्वेरी बनाना
किसी वैक्टर एम्बेडमेंट के सबसे नज़दीकी वैक्टर ढूंढने के लिए, मिलती-जुलती वैल्यू खोजने की सुविधा का इस्तेमाल किया जा सकता है. मिलती-जुलती खोजों के लिए, वेक्टर इंडेक्स की ज़रूरत होती है. अगर कोई इंडेक्स मौजूद नहीं है, तो Cloud Firestore, gcloud CLI का इस्तेमाल करके इंडेक्स बनाने का सुझाव देता है.
नीचे दिए गए उदाहरण में, क्वेरी वेक्टर के 10 सबसे नज़दीकी नेबर ढूंढे गए हैं.
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();
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
: वेक्टर के बीच की इयूक्लिडियन दूरी को मेज़र करता है. ज़्यादा जानने के लिए, यूक्लिडियन लेख पढ़ें.COSINE
: वेक्टर के बीच के कोण के आधार पर उनकी तुलना करता है. इससे, वेक्टर के मैग्नीट्यूड के आधार पर नहीं, बल्कि उनकी समानता को मेज़र किया जा सकता है. हमारा सुझाव है कि कोसाइन दूरी के बजाय, यूनिट नॉर्मलाइज़्ड वैक्टर के साथDOT_PRODUCT
का इस्तेमाल करें. यह गणितीय तौर पर बेहतर परफ़ॉर्मेंस के बराबर है. ज़्यादा जानने के लिए, कोसाइन मिलती-जुलती देखें.DOT_PRODUCT
: यहCOSINE
जैसा ही है, लेकिन इस पर वैक्टर के ऐंगल का असर पड़ता है. ज़्यादा जानने के लिए, डॉट प्रॉडक्ट देखें.
दूरी का मेज़रमेंट चुनना
आपके सभी वेक्टर एम्बेड सामान्य किए गए हैं या नहीं, इस आधार पर यह तय किया जा सकता है कि दूरी का पता लगाने के लिए किस मेज़र का इस्तेमाल किया जाए. नॉर्मलाइज़ किए गए वैक्टर एम्बेड की मैग्नीट्यूड (लंबाई) बिलकुल 1.0 होती है.
इसके अलावा, अगर आपको पता है कि आपके मॉडल को किस दूरी के मेज़र के साथ ट्रेन किया गया था, तो अपने वेक्टर एम्बेडमेंट के बीच की दूरी का हिसाब लगाने के लिए, उस दूरी के मेज़र का इस्तेमाल करें.
नॉर्मलाइज़ किया गया डेटा
अगर आपके पास ऐसा डेटासेट है जिसमें सभी वेक्टर एम्बेड को सामान्य किया गया है, तो दूरी का आकलन करने के तीनों तरीकों से, सेमैनटिक सर्च के एक जैसे नतीजे मिलते हैं. असल में, दूरी का हर पैरामीटर अलग-अलग वैल्यू दिखाता है. हालांकि, उन वैल्यू को एक ही तरह से क्रम में लगाया जाता है. जब एम्बेडमेंट को सामान्य किया जाता है, तो आम तौर पर DOT_PRODUCT
सबसे ज़्यादा बेहतर तरीके से काम करता है. हालांकि, ज़्यादातर मामलों में अंतर मामूली होता है. हालांकि, अगर आपका ऐप्लिकेशन परफ़ॉर्मेंस के लिहाज़ से काफ़ी संवेदनशील है, तो DOT_PRODUCT
की मदद से परफ़ॉर्मेंस को बेहतर बनाया जा सकता है.
नॉर्मलाइज़ नहीं किया गया डेटा
अगर आपके पास ऐसा डेटासेट है जिसमें वेक्टर एम्बेड को सामान्य नहीं किया गया है, तो DOT_PRODUCT
को दूरी के पैमाने के तौर पर इस्तेमाल करना गणित के हिसाब से सही नहीं है. इसकी वजह यह है कि डॉट प्रॉडक्ट से दूरी का पता नहीं चलता. एम्बेड को जनरेट करने के तरीके और खोज के टाइप के आधार पर, COSINE
या EUCLIDEAN
में से किसी एक का इस्तेमाल करके, खोज के नतीजे मिलते हैं. ये नतीजे, दूरी का आकलन करने के अन्य तरीकों से बेहतर होते हैं.
यह पता लगाने के लिए कि आपके इस्तेमाल के उदाहरण के लिए कौनसा विकल्प सबसे अच्छा है, COSINE
या EUCLIDEAN
में से किसी एक के साथ प्रयोग करना ज़रूरी हो सकता है.
यह पता नहीं है कि डेटा सामान्य है या नहीं
अगर आपको नहीं पता कि आपका डेटा नॉर्मलाइज़ किया गया है या नहीं और आपको DOT_PRODUCT
का इस्तेमाल करना है, तो हमारा सुझाव है कि आप इसके बजाय COSINE
का इस्तेमाल करें.
COSINE
, DOT_PRODUCT
की तरह ही है, जिसमें नॉर्मलाइज़ेशन की सुविधा पहले से मौजूद है.
COSINE
का इस्तेमाल करके, 0
से 2
तक की दूरी को मेज़र किया जा सकता है. 0
के करीब का नतीजा, यह दिखाता है कि वेक्टर काफ़ी मिलते-जुलते हैं.
दस्तावेज़ों को पहले से फ़िल्टर करना
मिलते-जुलते दस्तावेज़ों को खोजने से पहले, उन्हें फ़िल्टर करने के लिए, मिलते-जुलते शब्दों की खोज को अन्य क्वेरी ऑपरेटर के साथ जोड़ा जा सकता है. and
और
or
कंपोजिट फ़िल्टर का इस्तेमाल किया जा सकता है. काम करने वाले फ़ील्ड फ़िल्टर के बारे में ज़्यादा जानने के लिए, क्वेरी ऑपरेटर लेख पढ़ें.
// 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();
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();
कैलकुलेट की गई वेक्टर दूरी को वापस पाना
कैलकुलेट की गई वेक्टर दूरी को वापस पाने के लिए, FindNearest
क्वेरी पर distance_result_field
आउटपुट प्रॉपर्टी का नाम असाइन करें. उदाहरण के लिए:
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')); });
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
की वैल्यू भी शामिल करनी होगी. इसका उदाहरण यहां दिया गया है:
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' });
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, थ्रेशोल्ड को उन दस्तावेज़ों तक सीमित करता है जिनमें दूरी, तय थ्रेशोल्ड से ज़्यादा या उसके बराबर हो. सदिशों के एक जैसे होने पर, डॉट प्रॉडक्ट की दूरियां बढ़ जाती हैं.
यहां दिए गए उदाहरण में, EUCLIDEAN
दूरी मेट्रिक का इस्तेमाल करके, सबसे नज़दीक के 10 दस्तावेज़ों को दिखाने के लिए, दूरी का थ्रेशोल्ड तय करने का तरीका बताया गया है. ये दस्तावेज़, ज़्यादा से ज़्यादा 4.5 यूनिट की दूरी पर होने चाहिए:
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); });
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 के लिए सबसे सही तरीकों के बारे में पढ़ें.
- बड़े पैमाने पर डेटा पढ़ने और लिखने के बारे में जानें.