البحث عن التشابه باستخدام Spanner وVertex AI

1. مقدمة

أتاحت التطورات الأخيرة في مجال التعلّم العميق إمكانية تمثيل النصوص والبيانات الأخرى بطريقة تعكس المعنى الدلالي. وقد أدّى ذلك إلى اتّباع نهج جديد في البحث يُعرف باسم "البحث المستند إلى المتّجهات"، ويستخدم تمثيلات متّجهة للنصوص (تُعرف باسم التضمينات) للعثور على المستندات الأكثر صلة بطلب البحث الذي أجراه المستخدم. يُفضّل استخدام البحث المتّجه على البحث التقليدي في تطبيقات مثل البحث عن الملابس، حيث يبحث المستخدمون غالبًا عن السلع من خلال وصفها أو نمطها أو سياقها بدلاً من أسماء المنتجات أو العلامات التجارية الدقيقة. يمكننا دمج قاعدة بيانات Cloud Spanner مع Vector Search لإجراء مطابقة التشابه بين المتجهات. من خلال استخدام Spanner وVector Search معًا، يمكن للعملاء إنشاء عملية تكامل فعّالة تجمع بين مدى توفّر Spanner وموثوقيته وقابليته للتوسّع، وميزات البحث المتقدّمة عن التشابه في Vertex AI Vector Search. يتم إجراء هذا البحث من خلال مقارنة تضمينات العناصر في فهرس "البحث المتّجه" وعرض النتائج الأكثر تشابهًا.

حالة الاستخدام

تخيّل أنّك عالم بيانات في متجر لبيع الأزياء بالتجزئة وتحاول مواكبة المؤشرات وعمليات البحث عن المنتجات والاقتراحات التي تتغيّر بسرعة. التحدي هو أنّ لديك موارد محدودة ومستودعات بيانات. توضّح مشاركة المدونة هذه كيفية تنفيذ حالة استخدام لاقتراحات الملابس باستخدام أسلوب البحث عن التشابه في بيانات الملابس.وتتضمّن الخطوات التالية:

  1. البيانات من Spanner
  2. المتّجهات التي تم إنشاؤها لبيانات الملابس باستخدام ML.PREDICT وتخزينها في Spanner
  3. دمج بيانات المتجهات في Spanner مع Vector Search باستخدام وظائف سير العمل ووظائف Dataflow
  4. تم إجراء "البحث المستند إلى المتجهات" للعثور على تطابق التشابه مع الإدخال الذي أدخله المستخدم

سننشئ تطبيق ويب تجريبيًا لإجراء بحث عن الملابس استنادًا إلى بيانات أدخلها المستخدم. يتيح التطبيق للمستخدمين البحث عن الملابس من خلال إدخال وصف نصي.

ربط Spanner بفهرس "البحث عن المتّجهات":

يتم تخزين بيانات البحث عن الملابس في Spanner. سنستدعي واجهة برمجة التطبيقات Embeddings من Vertex AI في بنية ML.PREDICT مباشرةً من بيانات Spanner. بعد ذلك، سنستفيد من مهام Dataflow وWorkflow التي تحمّل هذه البيانات (المستودع والتضمينات) بشكل مجمّع إلى "البحث المتّجه" في Vertex AI وتعيد تحميل الفهرس.

تنفيذ طلبات بحث المستخدمين على الفهرس:

عندما يُدخِل المستخدم وصفًا للملابس، ينشئ التطبيق عمليات التضمين في الوقت الفعلي باستخدام واجهة برمجة التطبيقات Text Embeddings. يتم بعد ذلك إرسال هذا الطلب كمدخل إلى Vector Search API للعثور على 10 أوصاف منتجات ذات صلة من الفهرس وعرض الصورة المقابلة.

نظرة عامة على الهندسة المعمارية

يظهر تصميم تطبيق "البحث المتّجه" في Spanner في الرسم البياني التالي المكوّن من جزأين:

Spanner إلى فهرس البحث المتّجه: a79932a25bee23a4.png

تطبيق العميل لتنفيذ طلبات بحث المستخدمين في الفهرس:

b2b4d5a5715bd4c4.pngما ستنشئه

‫Spanner إلى "فهرس المتجهات":

  • قاعدة بيانات Spanner لتخزين بيانات المصدر وعمليات التضمين المقابلة وإدارتها
  • مهمة سير عمل تحمّل البيانات (المعرّف والتضمينات) بشكل مجمّع إلى قاعدة بيانات Vertex AI Vector Search.
  • واجهة برمجة تطبيقات "البحث المتّجه" تُستخدَم للعثور على أوصاف المنتجات ذات الصلة من الفهرس.

تنفيذ طلبات بحث المستخدمين على الفهرس:

  • تطبيق ويب يتيح للمستخدمين إدخال أوصاف نصية للملابس، ويُجري بحثًا عن التشابه باستخدام نقطة نهاية الفهرس التي تم تفعيلها، ويعرض أقرب الملابس إلى الوصف الذي تم إدخاله.

آلية العمل

عندما يُدخل المستخدم وصفًا نصيًا للملابس، يرسل تطبيق الويب الوصف إلى Vector Search API. بعد ذلك، تستخدم Vector Search API تضمينات أوصاف الملابس للعثور على أوصاف المنتجات الأكثر صلةً من الفهرس. بعد ذلك، يتم عرض أوصاف المنتجات والصور المقابلة لها للمستخدم. في ما يلي سير العمل العام:

  1. إنشاء تضمينات للبيانات المخزّنة في Spanner
  2. تصدير عمليات التضمين وتحميلها إلى فهرس "بحث Google" المتّجه
  3. استعلم عن فهرس "البحث المتّجه" للعثور على عناصر مشابهة من خلال إجراء بحث عن أقرب جيران.

2. المتطلبات

  • متصفّح، مثل Chrome أو Firefox
  • مشروع على السحابة الإلكترونية من Google Cloud تم تفعيل الفوترة فيه

قبل البدء

  1. في Google Cloud Console، ضمن صفحة اختيار المشروع، اختَر أو أنشِئ مشروعًا على Google Cloud.
  2. تأكَّد من تفعيل الفوترة لمشروعك على السحابة الإلكترونية. كيفية التحقّق من تفعيل الفوترة في مشروع
  3. تأكَّد من تفعيل جميع واجهات برمجة التطبيقات اللازمة (Cloud Spanner وVertex AI وGoogle Cloud Storage).
  4. ستستخدم Cloud Shell، وهي بيئة سطر أوامر تعمل في Google Cloud ومحمّلة مسبقًا بأداة gcloud. راجِع المستندات لمعرفة أوامر gcloud وطريقة استخدامها. إذا لم يتم ضبط مشروعك، استخدِم الأمر التالي لضبطه:
gcloud config set project <YOUR_PROJECT_ID>
  1. انتقِل إلى صفحة Cloud Spanner باستخدام مشروع Google Cloud النشط لبدء الاستخدام

3- الخادم الخلفي: إنشاء مصدر بيانات Spanner وعمليات التضمين

في حالة الاستخدام هذه، تحتوي قاعدة بيانات Spanner على مستودع الملابس مع الصور والأوصاف ذات الصلة. تأكَّد من إنشاء تضمينات للوصف النصي وتخزينها في قاعدة بيانات Spanner بتنسيق ARRAY<float64>.

  1. إنشاء بيانات Spanner

أنشئ مثيلاً باسم "spanner-vertex" وقاعدة بيانات باسم "spanner-vertex-embeddings". أنشئ جدولاً باستخدام DDL:

CREATE TABLE
  apparels ( id NUMERIC,
    category STRING(100),
    sub_category STRING(50),
    uri STRING(200),
    content STRING(2000),
    embedding ARRAY<FLOAT64>
    )
PRIMARY KEY
  (id);
  1. إدراج البيانات في الجدول باستخدام INSERT SQL

تتوفّر نصوص برمجية لإدراج بيانات نموذجية هنا.

  1. إنشاء نموذج Text Embeddings

هذا الإذن مطلوب لنتمكّن من إنشاء تضمينات للمحتوى الذي تم إدخاله. في ما يلي تعريف البيانات نفسه:

CREATE MODEL text_embeddings INPUT(content STRING(MAX))
OUTPUT(
  embeddings
    STRUCT<
      statistics STRUCT<truncated BOOL, token_count FLOAT64>,
      values ARRAY<FLOAT64>>
)
REMOTE OPTIONS (
  endpoint = '//aiplatform.googleapis.com/projects/abis-345004/locations/us-central1/publishers/google/models/textembedding-gecko');
  1. إنشاء تضمينات نصية لبيانات المصدر

أنشئ جدولاً لتخزين التضمينات وأدرِج التضمينات التي تم إنشاؤها. في تطبيق قاعدة بيانات من الواقع العملي، سيكون تحميل البيانات إلى Spanner حتى الخطوة 2 معاملة. للحفاظ على أفضل ممارسات التصميم، أفضّل إبقاء جداول المعاملات موحّدة، وبالتالي إنشاء جدول منفصل لعمليات التضمين.

CREATE TABLE apparels_embeddings (id string(100), embedding ARRAY<FLOAT64>)
PRIMARY KEY (id);

INSERT INTO apparels_embeddings(id, embeddings) 
SELECT CAST(id as string), embeddings.values
FROM ML.PREDICT(
  MODEL text_embeddings,
  (SELECT id, content from apparels)
) ;

بعد أن أصبح المحتوى المجمّع وعمليات التضمين جاهزة، لننشئ الآن فهرسًا ونقطة نهاية لـ "البحث المتّجهي" من أجل تخزين عمليات التضمين التي ستساعد في تنفيذ "البحث المتّجهي".

4. مهمة سير العمل: تصدير بيانات Spanner إلى Vector Search

  1. إنشاء حزمة في Cloud Storage

هذا الإجراء مطلوب لتخزين التضمينات من Spanner في حزمة GCS بتنسيق json تتوقّعه خدمة "البحث المتّجه" كإدخال. أنشئ حزمة في المنطقة نفسها التي تتوفّر فيها بياناتك في Spanner. أنشئ مجلدًا في الداخل إذا لزم الأمر، ولكن أنشئ بشكل أساسي ملفًا فارغًا باسم empty.json.

  1. إعداد Cloud Workflow

لإعداد عملية تصدير مجمّع من Spanner إلى فهرس Vertex AI Vector Search، اتّبِع الخطوات التالية:

إنشاء فهرس فارغ:

تأكَّد من أنّ "فهرس البحث المتّجه" يقع في المنطقة نفسها التي تقع فيها حزمة Cloud Storage والبيانات. اتّبِع الخطوات الـ 11 الواردة ضمن علامة التبويب "وحدة التحكّم" في قسم إنشاء فهرس للتعديل المجمّع في صفحة "إدارة الفهارس". في المجلد الذي تم تمريره إلى contentsDeltaUri، أنشئ ملفًا فارغًا باسم empty.json لأنّه لن يكون بإمكانك إنشاء فهرس بدون هذا الملف. يؤدي ذلك إلى إنشاء فهرس فارغ.

إذا كان لديك فهرس حاليًا، يمكنك تخطّي هذه الخطوة. سيؤدي سير العمل إلى استبدال الفهرس.

ملاحظة: لا يمكنك نشر فهرس فارغ إلى نقطة نهاية. لذلك، سنؤجّل خطوة نشرها إلى نقطة نهاية إلى خطوة لاحقة، بعد تصدير بيانات المتجهات إلى Cloud Storage.

استنساخ مستودع git هذا: هناك طرق متعددة لاستنساخ مستودع git، إحدى الطرق هي تنفيذ الأمر التالي باستخدام GitHub CLI. نفِّذ الأمرَين التاليَين من "وحدة Cloud Shell الطرفية":

gh repo clone cloudspannerecosystem/spanner-ai

cd spanner-ai/vertex-vector-search/workflows

يحتوي هذا المجلد على ملفَين

  • batch-export.yaml: هذا هو تعريف سير العمل.
  • sample-batch-input.json: هذا مثال على مَعلمات إدخال سير العمل.

إعداد ملف input.json من الملف النموذجي: انسخ ملف json النموذجي أولاً.

cp sample-batch-input.json input.json

بعد ذلك، عدِّل input.json بإضافة تفاصيل مشروعك. في هذه الحالة، يجب أن يكون ملف json على النحو التالي:

{
  "project_id": "<<YOUR_PROJECT>>",
  "location": "<<us-central1>>",
  "dataflow": {
    "temp_location": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_temp"
  },
  "gcs": {
    "output_folder": "gs://<<YOUR_BUCKET>>/<<FOLDER_IF_ANY>>/workflow_output"
  },
  "spanner": {
    "instance_id": "spanner-vertex",
    "database_id": "spanner-vertex-embeddings",
    "table_name": "apparels_embeddings",
    "columns_to_export": "embedding,id"
  },
  "vertex": {
    "vector_search_index_id": "<<YOUR_INDEX_ID>>"
  }
}

إعداد الأذونات

بالنسبة إلى بيئات الإنتاج، ننصحك بشدة بإنشاء حساب خدمة جديد ومنحه دورًا واحدًا أو أكثر من أدوار إدارة الهوية وإمكانية الوصول (IAM) التي تتضمّن الحدّ الأدنى من الأذونات المطلوبة لإدارة الخدمة. يجب توفُّر الأدوار التالية لإعداد سير العمل من أجل تصدير البيانات من Spanner (تضمينات) إلى فهرس "البحث المتّجه":

حساب خدمة سير العمل على السحابة الإلكترونية:

يستخدم هذا الإعداد تلقائيًا حساب الخدمة التلقائي في Compute Engine.

في حال استخدام حساب خدمة تم إعداده يدويًا، يجب تضمين الأدوار التالية:

لتشغيل مهمة Dataflow: مشرف Dataflow، عامل Dataflow

لانتحال هوية حساب خدمة عامل Dataflow، يجب أن يكون لديك إذن مستخدم حساب الخدمة.

لكتابة السجلّات: كاتب السجلّات

لتفعيل إعادة إنشاء Vertex AI Vector Search: مستخدم Vertex AI

حساب خدمة عامل Dataflow:

في حال استخدام حساب خدمة تم إعداده يدويًا، يجب تضمين الأدوار التالية:

لإدارة Dataflow: مشرف Dataflow، عامل Dataflow لقراءة البيانات من Spanner: قارئ قاعدة بيانات Cloud Spanner إذن الكتابة على سجلّ حاويات GCS المحدّد: مالك حزمة تخزين GCS

  1. نشر Cloud Workflow

انشر ملف YAML الخاص بسير العمل في مشروعك على Google Cloud. يمكنك ضبط المنطقة أو الموقع الجغرافي الذي سيتم فيه تنفيذ سير العمل عند تشغيله.

gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1" [--service account=<service_account>]

or 

gcloud workflows deploy vector-export-workflow --source=batch-export.yaml --location="us-central1"

من المفترض أن يكون سير العمل مرئيًا الآن في صفحة "سير العمل" في Google Cloud Console.

ملاحظة: يمكنك أيضًا إنشاء سير العمل ونشره من وحدة تحكّم Google Cloud. اتّبِع التعليمات الظاهرة على الشاشة في Cloud Console. بالنسبة إلى تعريف سير العمل، انسخ محتوى ملف batch-export.yaml وألصِقه.

بعد اكتمال ذلك، نفِّذ سير العمل لتبدأ عملية تصدير البيانات.

  1. تنفيذ سير عمل Cloud

نفِّذ الأمر التالي لتشغيل سير العمل:

gcloud workflows execute vector-export-workflow --data="$(cat input.json)"

يجب أن يظهر التنفيذ في علامة التبويب "عمليات التنفيذ" في "مسارات العمل". من المفترض أن يؤدي ذلك إلى تحميل بياناتك في قاعدة بيانات "البحث المتّجه" وفهرستها.

ملاحظة: يمكنك أيضًا التنفيذ من وحدة التحكّم باستخدام الزر "تنفيذ". اتّبِع التعليمات التي تظهر على الشاشة، ثم انسخ محتوى ملف input.json المخصّص والصقه في الحقل المخصّص للإدخال.

5- نشر فهرس "البحث عن المتّجهات"

نشر الفهرس في نقطة نهاية

يمكنك اتّباع الخطوات التالية لنشر الفهرس:

  1. في صفحة فهارس البحث المتّجه، من المفترض أن يظهر زر DEPLOY (نشر) بجانب الفهرس الذي أنشأته في الخطوة 2 من القسم السابق. يمكنك بدلاً من ذلك الانتقال إلى صفحة معلومات الفهرس والنقر على الزر "نشر إلى نقطة نهاية".
  2. قدِّم المعلومات اللازمة ونفِّذ الفهرس في نقطة نهاية.

بدلاً من ذلك، يمكنك الاطّلاع على دفتر الملاحظات هذا لتوزيعه على نقطة نهاية (انتقِل إلى جزء التوزيع في دفتر الملاحظات). بعد نشر الفهرس، سجِّل رقم تعريف الفهرس وعنوان URL لنقطة النهاية.

6. الواجهة الأمامية: بيانات المستخدم إلى Vector Search

لننشئ تطبيقًا بسيطًا بلغة Python مع تجربة مستخدم مستندة إلى Gradio لاختبار التنفيذ بسرعة. يمكنك الرجوع إلى التنفيذ هنا لتنفيذ هذا التطبيق التجريبي في دفتر ملاحظات Colab الخاص بك.

  1. سنستخدم حزمة تطوير البرامج (SDK) الخاصة بمنصة الذكاء الاصطناعي (AI Platform) في لغة Python لاستدعاء Embeddings API، وكذلك لاستدعاء نقطة نهاية فهرس "البحث المتّجه".
# [START aiplatform_sdk_embedding]
!pip install google-cloud-aiplatform==1.35.0 --upgrade --quiet --user


import vertexai
vertexai.init(project=PROJECT_ID, location="us-central1")


from vertexai.language_models import TextEmbeddingModel


import sys
if "google.colab" in sys.modules:
    # Define project information
    PROJECT_ID = " "  # Your project id
    LOCATION = " "  # Your location 


    # Authenticate user to Google Cloud
    from google.colab import auth
    auth.authenticate_user()
  1. سنستخدم Gradio لعرض تطبيق الذكاء الاصطناعي الذي ننشئه بسرعة وسهولة من خلال واجهة مستخدم. أعِد تشغيل وقت التشغيل قبل تنفيذ هذه الخطوة.
!pip install gradio
import gradio as gr
  1. من تطبيق الويب عند إدخال المستخدم، استدعِ Embeddings API، وسنستخدم نموذج تضمين النص: textembedding-gecko@latest

يستدعي الأسلوب أدناه "نموذج تضمين النص" ويعرض تضمينات المتجهات للنص الذي أدخله المستخدم:

def text_embedding(content) -> list:
    """Text embedding with a Large Language Model."""
    model = TextEmbeddingModel.from_pretrained("textembedding-gecko@latest")
    embeddings = model.get_embeddings(content)
    for embedding in embeddings:
        vector = embedding.values
        #print(f"Length of Embedding Vector: {len(vector)}")
    return vector

اختبارها

text_embedding("red shorts for girls")

من المفترض أن يظهر لك ناتج مشابه لما يلي (يُرجى العِلم أنّه تم اقتصاص الصورة من حيث الارتفاع، لذا لن تتمكّن من رؤية استجابة المتّجه بأكملها):

5d8355ec04dac1f9.png

  1. تحديد رقم تعريف الفهرس الذي تم نشره ورقم تعريف نقطة النهاية
from google.cloud import aiplatform
DEPLOYED_INDEX_ID = "spanner_vector1_1702366982123"
#Vector Search Endpoint
index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
  1. حدِّد طريقة "البحث المتّجه" لاستدعاء نقطة نهاية الفهرس وعرض النتيجة التي تتضمّن 10 مطابقات الأقرب إلى استجابة التضمين المقابلة لبيانات أدخلها المستخدم.

في تعريف الطريقة أدناه الخاص بـ "البحث المتّجه"، لاحظ أنّه يتم استدعاء طريقة find_neighbors لتحديد أقرب 10 متجهات.

def vector_search(content) -> list:
  result = text_embedding(content)
  #call_vector_search_api(content)
  index_endpoint = aiplatform.MatchingEngineIndexEndpoint('projects/273845608377/locations/us-central1/indexEndpoints/2021628049526620160')
  # run query
  response = index_endpoint.find_neighbors(
      deployed_index_id = DEPLOYED_INDEX_ID,
      queries = [result],
      num_neighbors = 10
  )
  out = []
  # show the results
  for idx, neighbor in enumerate(response[0]):
      print(f"{neighbor.distance:.2f} {spanner_read_data(neighbor.id)}")
      out.append(f"{spanner_read_data(neighbor.id)}")
  return out

ستلاحظ أيضًا الإشارة إلى الطريقة spanner_read_data. لنطّلِع على ذلك في الخطوة التالية.

  1. حدِّد تنفيذ طريقة قراءة البيانات في Spanner التي تستدعي طريقة execute_sql لاستخراج الصور المتوافقة مع أرقام تعريف متجهات أقرب عنصر مجاور التي تم عرضها في الخطوة الأخيرة.
!pip install google-cloud-spanner==3.36.0


from google.cloud import spanner


instance_id = "spanner-vertex"
database_id = "spanner-vertex-embeddings"
projectId = PROJECT_ID
client = spanner.Client()
client.project = projectId
instance = client.instance(instance_id)
database = instance.database(database_id)
def spanner_read_data(id):
    query = "SELECT uri FROM apparels where id = " + id
    outputs = []
    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(query)


        for row in results:
            #print(row)
            #output = "ID: {}, CONTENT: {}, URI: {}".format(*row)
            output = "{}".format(*row)
            outputs.append(output)


    return "\n".join(outputs)

من المفترَض أن يعرض عناوين URL للصور التي تتطابق مع المتجهات المحدّدة.

  1. أخيرًا، لنضع الأجزاء معًا في واجهة مستخدم ونبدأ عملية "البحث المتّجه".
from PIL import Image


def call_search(query):
  response = vector_search(query)
  return response


input_text = gr.Textbox(label="Enter your query. Examples: Girls Tops White Casual, Green t-shirt girls, jeans shorts, denim skirt etc.")
output_texts = [gr.Image(label="") for i in range(10)]
demo = gr.Interface(fn=call_search, inputs=input_text, outputs=output_texts, live=True)
resp = demo.launch(share = True)

يجب أن تظهر النتيجة كما هو موضّح أدناه:

8093b39fbab1a9cc.png

الصورة: الرابط

يمكنك مشاهدة الفيديو الخاص بالنتيجة هنا.

7. تَنظيم

لتجنُّب تحمّل رسوم في حسابك على Google Cloud مقابل الموارد المستخدَمة في هذه المشاركة، اتّبِع الخطوات التالية:

  1. في Google Cloud Console، انتقِل إلى صفحة إدارة الموارد.
  2. في قائمة المشاريع، اختَر المشروع الذي تريد حذفه، ثم انقر على "حذف".
  3. في مربّع الحوار، اكتب رقم تعريف المشروع، ثم انقر على "إيقاف" لحذف المشروع.
  4. إذا كنت لا تريد حذف المشروع، احذف مثيل Spanner من خلال الانتقال إلى المثيل الذي أنشأته للتو لهذا المشروع والنقر على الزر DELETE INSTANCE (حذف المثيل) في أعلى يسار صفحة نظرة عامة على المثيل.
  5. يمكنك أيضًا الانتقال إلى فهرس "البحث المتّجه"، وإلغاء نشر نقطة النهاية والفهرس، وحذف الفهرس.

8. الخاتمة

تهانينا! لقد أكملت عملية تنفيذ Spanner - Vertex Vector Search بنجاح من خلال

  1. إنشاء مصدر بيانات Spanner وتضمينات للتطبيقات التي يتم الحصول عليها من قاعدة بيانات Spanner
  2. إنشاء فهرس قاعدة بيانات "البحث المتّجه"
  3. دمج بيانات المتجهات من Spanner في Vector Search باستخدام مهام Dataflow وWorkflow
  4. نشر الفهرس في نقطة نهاية
  5. أخيرًا، استدعاء Vector Search بشأن بيانات أدخلها المستخدم في عملية تنفيذ مستندة إلى Python لحزمة تطوير البرامج (SDK) الخاصة بـ Vertex AI

يمكنك توسيع نطاق التنفيذ ليشمل حالة الاستخدام الخاصة بك أو تحسين حالة الاستخدام الحالية باستخدام ميزات جديدة. يمكنك الاطّلاع على مزيد من المعلومات حول إمكانات تعلُّم الآلة في Spanner هنا.