توضّح لك الصفحة كيفية استخدام Cloud Firestore لإجراء عمليات بحث متجهة عن أقرب K جيران (KNN) باستخدام التقنيات التالية:
- قيم المتّجه للمتجر
- إنشاء فهارس متجهات KNN وإدارتها
- إجراء طلب بحث عن الجار الأقرب (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 Function
لحساب وتخزين تضمينات المتجهات كلما تم تعديل مستند أو إنشاؤه، يمكنك إعداد دالة Cloud:
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. يمكن أيضًا إدارة فهارس المتجهات باستخدام Firebase CLI وTerraform.
إنشاء فهرس متجهات
قبل إنشاء فهرس متّجه، عليك الترقية إلى أحدث إصدار من 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
where:
- 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
where:
- index-id هو رقم تعريف الفهرس المطلوب حذفه.
استخدِم
indexes composite list
لاسترداد رقم تعريف الفهرس. - database-id هو رقم تعريف قاعدة البيانات.
وصف فهرس المتّجهات
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
where:
- 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
: تقيس المسافة الإقليدية بين المتجهات. لمزيد من المعلومات، يُرجى الاطّلاع على المسافة الإقليدية.COSINE
: تقارن هذه الطريقة المتّجهات استنادًا إلى الزاوية بينها، ما يتيح لك قياس التشابه الذي لا يستند إلى مقدار المتّجهات. ننصحك باستخدامDOT_PRODUCT
مع الخطوط المتجهة الموحّدة بدلاً من مسافة COSINE، لأنّها مكافئة رياضيًا وتوفّر أداءً أفضل. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة تشابه الجيب التمامي.-
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
. لمزيد من المعلومات عن فلاتر الحقول المتوافقة، اطّلِع على عوامل تشغيل طلب البحث.
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
يقتصر الحد الأدنى للمسافة على المستندات التي تكون فيها المسافة أكبر من أو تساوي الحد الأدنى المحدّد. تزداد مسافات حاصل الضرب النقطي كلما أصبحت المتجهات أكثر تشابهًا.
يوضّح المثال التالي كيفية تحديد حدّ مسافة لعرض ما يصل إلى 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. لتخزين فهارس أكبر، استخدِم تقنية خفض الأبعاد.
- الحد الأقصى لعدد المستندات التي يمكن عرضها من طلب بحث عن أقرب تطابق هو 1000 مستند.
- لا تتيح ميزة "البحث المتّجه" برامج معالجة اللقطات في الوقت الفعلي.
- تتيح مكتبات برامج Python وNode.js وGo وJava للعملاء البحث المستند إلى المتجهات فقط.
الخطوات التالية
- اطّلِع على أفضل الممارسات المتعلّقة بـ Cloud Firestore.
- فهم عمليات القراءة والكتابة على نطاق واسع