توضّح لك الصفحة كيفية استخدام Cloud Firestore لإجراء عمليات بحث متّجهة باستخدام تقنية "أقرب جيران" (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]) });
انتقال
جافا
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
لاحتساب عمليات تضمين المتجهات وتخزينها عند تعديل مستند أو إنشائه، يمكنك إعداد وظيفة 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, }); }
انتقال
// Not yet supported in the Go client library
جافا
// Not yet supported in the Java client library
إنشاء وإدارة مؤشرات المتجهات
قبل أن تتمكّن من إجراء بحث عن أقرب العناصر باستخدام عمليات إدراج المتجهات، يجب إنشاء فهرس ملائم. توضِّح الأمثلة التالية كيفية إنشاء وإدارة الفهارس المتّجهة باستخدام Google Cloud CLI. يمكن أيضًا إدارة فهارس Vector باستخدام واجهة 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
حيث:
- collection-group هو رقم تعريف مجموعة المجموعات.
- vector-field هو اسم الحقل الذي يحتوي على إدراج المتجه.
- database-id هو رقم تعريف قاعدة البيانات.
- يتضمّن vector-configuration المتجه
dimension
ونوع الفهرس. dimension
هو عدد صحيح يصل إلى 2048. يجب أن يكون نوع الفهرسflat
. اضبط تنسيق إعدادات الفهرس على النحو التالي:{"dimension":"DIMENSION", "flat": "{}"}
.
ينشئ المثال التالي فهرسًا مركبًا، بما في ذلك فهرس متّجه للحقل vector-field
وفهرسًا تصاعديًا للحقل color
. يمكنك استخدام هذا النوع من الفهارس لفلترة data
مسبقًا قبل إجراء بحث عن أقرب العناصر المتشابهة.
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();
انتقال
جافا
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();
انتقال
جافا
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')); });
انتقال
جافا
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' });
انتقال
جافا
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); });
انتقال
جافا
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.
- فهم عمليات القراءة والكتابة على نطاق واسع