البحث باستخدام تضمينات المتّجهات

تعرض لك الصفحة كيفية استخدام Cloud Firestore لإجراء الخوارزمية الأقرب. عمليات البحث المتجهة المجاورة (KNN) باستخدام الأساليب التالية:

  • تخزين قيم الخط المتجه
  • إنشاء وإدارة فهارس متجه KNN
  • قم بعمل استعلام خوارزمية الجار الأقرب (KNN) باستخدام أحد المتجهات المدعومة مقاييس المسافة

تخزين تضمينات المتجه

يمكنك إنشاء قيم متجه، مثل تضمينات النص من بيانات Cloud Firestore وتخزينها في Cloud Firestore مستندات

كتابة العملية باستخدام تضمين متّجه

يوضح المثال التالي كيفية تخزين تضمين متجه في مستند واحد (Cloud Firestore):

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

firestore_client = firestore.Client()
collection = firestore_client.collection("coffee-beans")
doc = {
    "name": "Kahawa coffee beans",
    "description": "Information about the Kahawa coffee beans.",
    "embedding_field": Vector([1.0, 2.0, 3.0]),
}

collection.add(doc)
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])
});

حساب عمليات تضمين المتجهات باستخدام دالة السحابة

لحساب تضمينات المتجه وتخزينها كلما تم تحديث مستند أو يمكنك إعداد دالة 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,
  });
}

إنشاء فهارس المتجهات وإدارتها

وقبل أن تتمكن من إجراء بحث الجار الأقرب باستخدام تضمينات المتجه، يجب عليك إنشاء فهرس مقابل. توضح الأمثلة التالية كيفية إنشاء وإدارة الفهارس المتجهة.

إنشاء فهرس متجه

قبل إنشاء فهرس متجه، يجب الترقية إلى أحدث إصدار من 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
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Requires a single-field vector index
vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
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();

المسافات المتجهة

تتيح طلبات البحث ذات الجار الأقرب الخيارات التالية لمسافة المتجه:

  • EUCLIDEAN: يقيس مسافة 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
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Similarity search with pre-filter
# Requires a composite vector index
vector_query = collection.where("color", "==", "red").find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
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();

استرجاع المسافة المتجهة المحسوبة

يمكنك استرداد المسافة المتجهة المحسوبة من خلال تعيين اسم خاصية الناتج distance_result_field في طلب البحث FindNearest، باعتباره كما هو موضح في المثال التالي:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}, Distance: {doc.get('vector_distance')}")
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'));
});

إذا أردت استخدام قناع حقل لعرض مجموعة فرعية من حقول المستند بالإضافة إلى distanceResultField، يجب أيضًا تضمين القيمة distanceResultField في قناع الحقل، كما هو موضّح في المثال التالي:

Python
vector_query = collection.select(["color", "vector_distance"]).find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)
Node.js
const vectorQuery: VectorQuery = coll
    .select('color', 'vector_distance')
    .findNearest({
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

تحديد حد للمسافة

يمكنك تحديد حد للتشابه يعرض المستندات فقط ضمن الحد الأقصى المسموح به. يعتمد سلوك حقل الحدّ على مقياس المسافة. تختار:

  • تقتصر المسافة بين EUCLIDEAN وCOSINE على المستندات المسموح بها المسافة أقل من أو تساوي الحد الأدنى المحدد. هذه المسافة والقياسات تنخفض كلما أصبحت المتجهات أكثر تشابهًا.
  • تستند المسافة إلى DOT_PRODUCT إلى الحدّ الأدنى للمستندات التي تتضمّن المسافة. أكبر من أو مساوٍ للحد الأدنى المحدّد. مسافات الضرب النقطية عندما تصبح المتجهات أكثر تشابهًا.

يوضح المثال التالي كيفية تحديد حد للمسافة لعرض أقرب 10 مستندات تبعد 4.5 وحدة على الأكثر باستخدام مقياس المسافة EUCLIDEAN:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_threshold=4.5,
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}")
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);
});

القيود

أثناء عملك مع تضمينات المتجهات، لاحظ القيود التالية:

  • الحد الأقصى لبُعد تضمين المسموح به هو 2048. لتخزين فهارس أكبر، استخدم تقليل الأبعاد.
  • الحد الأقصى لعدد المستندات التي يتم عرضها من استعلام الجار الأقرب هو 1000.
  • لا يوفّر بحث المتجه أدوات معالجة اللقطات في الوقت الفعلي.
  • تدعم مكتبات العملاء Python وNode.js فقط البحث المتجه.

الخطوات التالية