0Pricing
AI Engineering Academy · درس

البحث الدلالي باستخدام NumPy

ابنوا نظام بحث دلالي باستخدام Python فقط وNumPy لحساب التشابه الجيبي بين embedding للاستعلام ومجموعة من embeddings المستندات.

البحث الدلالي باستخدام NumPy درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

البحث الدلالي بدون قاعدة بيانات

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

يعمل هذا الأسلوب جيدًا مع ما يصل إلى عشرات الآلاف من المستندات، وهو مثالي لإنشاء نموذج أولي قبل الاستثمار في قاعدة بيانات متجهية.

إنشاء مجموعة المستندات

ابدأ بجمع مستنداتك وإنشاء embedding واحد لكل مستند باستخدام OpenAI API. خزّن embeddings في مصفوفة NumPy ثنائية الأبعاد، بحيث يمثل كل صف متجه مستند واحد. واحتفظ بقائمة موازية من نصوص المستندات، حتى تتمكن من استرجاع المحتوى الأصلي بعد العثور على أفضل التطابقات.

import numpy as np
from openai import OpenAI

client = OpenAI()

documents = [
    'Python is a high-level programming language.',
    'NumPy provides fast numerical computing for Python.',
    'Embeddings represent text as dense vectors.',
    'Cosine similarity measures angle between vectors.',
    'RAG combines retrieval with language generation.'
]

response = client.embeddings.create(
    model='text-embedding-3-small',
    input=documents
)

corpus_embeddings = np.array([item.embedding for item in response.data])
print(f'Corpus shape: {corpus_embeddings.shape}')  # (5, 1536)

إنشاء Embedding لاستعلام المستخدم

عندما يرسل المستخدم استعلام بحث، أنشئ له embedding باستخدام النموذج نفسه الذي استُخدم لإنشاء embeddings للمستندات. سيؤدي استخدام نماذج مختلفة — مثل استخدام text-embedding-3-small للمستندات وtext-embedding-3-large للاستعلامات — إلى إنتاج متجهات في فضاءات مختلفة، وبالتالي إلى درجات تشابه بلا معنى.

from openai import OpenAI
import numpy as np

client = OpenAI()

query = 'How do I compute similarity between text?'

response = client.embeddings.create(
    model='text-embedding-3-small',   # must match corpus model
    input=query
)

query_embedding = np.array(response.data[0].embedding)
print(f'Query vector shape: {query_embedding.shape}')  # (1536,)

حساب التشابه الجيبي باستخدام NumPy

للعثور على التشابه بين الاستعلام وكل مستند في عملية واحدة، احسب الضرب النقطي لمتجه الاستعلام مع مصفوفة متجهات المستندات. وبما أن كلا نوعي embeddings من OpenAI مطبّعان وفق معيار L2، فإن هذا يساوي التشابه الجيبي لجميع المستندات دفعة واحدة، بتعقيد O(n * d)، حيث n هو عدد المستندات وd هو البُعد.

import numpy as np

# corpus_embeddings: (n_docs, 1536)
# query_embedding: (1536,)

def semantic_search(query_vec, corpus_vecs):
    # Matrix-vector dot product: shape (n_docs,)
    similarities = corpus_vecs @ query_vec
    return similarities

# Example call (assuming pre-computed embeddings)
# sims = semantic_search(query_embedding, corpus_embeddings)
# print(sims)  # array of similarity scores, one per document

ترتيب أفضل نتائج K واسترجاعها

استخدم np.argsort لترتيب المستندات حسب درجة التشابه ترتيبًا تنازليًا، ثم استخرج مؤشرات أفضل k نتائج. يمنحك ذلك مؤشرات المستندات الأكثر صلة، ويمكنك استخدامها للبحث عن النص الأصلي في قائمتك الموازية.

import numpy as np

def get_top_k(query_vec, corpus_vecs, documents, k=3):
    similarities = corpus_vecs @ query_vec
    # argsort gives ascending order; [::-1] reverses to descending
    ranked_indices = np.argsort(similarities)[::-1]
    top_k_indices = ranked_indices[:k]
    return [
        {'text': documents[i], 'score': float(similarities[i])}
        for i in top_k_indices
    ]

# results = get_top_k(query_embedding, corpus_embeddings, documents, k=3)
# for r in results:
#     print(f'{r["score"]:.4f}: {r["text"]}')

مثال كامل على البحث الدلالي

بجمع الخطوات كلها: أنشئ embedding لمجموعة المستندات، وأنشئ embedding للاستعلام، واحسب أوجه التشابه، ثم أرجع النتائج المرتبة. يُعد هذا النمط الكامل جوهر كل خطوة استرجاع في RAG، حتى عندما تستبدل قاعدة بيانات متجهية NumPy في الخلفية.

import numpy as np
from openai import OpenAI

client = OpenAI()

docs = [
    'Embeddings map text to numerical vectors.',
    'Python lists store ordered collections.',
    'Cosine similarity compares vector directions.',
    'RAG retrieves documents to ground LLM answers.',
    'Dictionaries store key-value pairs in Python.'
]

corpus_resp = client.embeddings.create(model='text-embedding-3-small', input=docs)
corpus = np.array([d.embedding for d in corpus_resp.data])

query = 'finding similar text using angles'
q_resp = client.embeddings.create(model='text-embedding-3-small', input=query)
q_vec = np.array(q_resp.data[0].embedding)

scores = corpus @ q_vec
for i in np.argsort(scores)[::-1][:3]:
    print(f'{scores[i]:.3f}: {docs[i]}')

تحديد عتبة الدرجات

ليست كل نتائج أفضل k ذات صلة فعلية؛ ففي بعض الأحيان يظل أفضل تطابق غير مناسب دلاليًا. أضف عتبة للدرجة لاستبعاد النتائج منخفضة التشابه. وتتراوح العتبة المعتادة للتشابه الجيبي بين 0.70 و0.80، لكن ينبغي معايرتها وفق مجالك المحدد باستخدام استعلامات حقيقية.

import numpy as np

def search_with_threshold(query_vec, corpus_vecs, documents, k=5, threshold=0.75):
    similarities = corpus_vecs @ query_vec
    ranked = np.argsort(similarities)[::-1][:k]
    results = []
    for i in ranked:
        if similarities[i] >= threshold:
            results.append({'text': documents[i], 'score': float(similarities[i])})
    return results

# Only returns documents above the minimum similarity threshold

خصائص أداء البحث باستخدام NumPy

يبلغ التعقيد الزمني للبحث عن التشابه باستخدام NumPy مقدار O(n * d) لكل استعلام، حيث n هو عدد المستندات وd هو بُعد embedding. بالنسبة إلى embeddings ذات 1536 بُعدًا:

  • 10,000 مستند: نحو 5ms لكل استعلام على وحدة معالجة مركزية حديثة
  • 100,000 مستند: نحو 50ms لكل استعلام
  • 1,000,000 مستند: نحو 500ms — بطيء جدًا، لذا انتقل إلى قاعدة بيانات متجهية

تُعد NumPy ممتازة لإنشاء النماذج الأولية، لكنها لا توفر بحثًا تقريبيًا أو تصفية أو حفظًا مستمرًا مدمجًا.

حفظ Embeddings على القرص

إعادة حساب embeddings عند كل تشغيل تهدر استدعاءات واجهة API والمال. احفظ embeddings الخاصة بمجموعة المستندات ونصوص المستندات على القرص، بحيث لا تعيد إنشاء embeddings إلا عند تغير المجموعة.

تخزّن np.save مصفوفة embeddings بكفاءة، ويمكنك حفظ قائمة المستندات بصيغة JSON. وعند بدء التشغيل، حمّل الملفين بدلًا من استدعاء واجهة API.

import numpy as np
import json

# Save
np.save('/tmp/corpus_embeddings.npy', corpus_embeddings)
with open('/tmp/corpus_docs.json', 'w') as f:
    json.dump(documents, f)

# Load
corpus_embeddings = np.load('/tmp/corpus_embeddings.npy')
with open('/tmp/corpus_docs.json') as f:
    documents = json.load(f)

print(f'Loaded {len(documents)} docs, shape {corpus_embeddings.shape}')

معالجة المستندات الجديدة تدريجيًا

عند وصول مستندات جديدة، لا تحتاج إلى إعادة إنشاء embeddings لمجموعة المستندات بأكملها. أنشئ embeddings للمستندات الجديدة فقط، واستخدم np.vstack لإلحاق متجهاتها بالمصفوفة الحالية. وتذكر إلحاق النصوص الجديدة بقائمة المستندات بالترتيب نفسه.

import numpy as np
from openai import OpenAI

client = OpenAI()

# Assume these exist from a previous session:
# corpus_embeddings: (n, 1536)
# documents: list of strings

new_docs = ['New document about vector search.']
resp = client.embeddings.create(model='text-embedding-3-small', input=new_docs)
new_vecs = np.array([item.embedding for item in resp.data])

corpus_embeddings = np.vstack([corpus_embeddings, new_vecs])
documents.extend(new_docs)
print(f'Corpus now has {len(documents)} documents')

قيود البحث داخل الذاكرة

يفرض البحث الدلالي باستخدام NumPy قيودًا مهمة مقارنة بقاعدة بيانات متجهية مخصصة لهذا الغرض:

  • لا يوجد حفظ مستمر — كل شيء موجود في ذاكرة RAM ويُفقد عند إعادة التشغيل
  • لا توجد تصفية للبيانات الوصفية — لا يمكنك تصفية النتائج حسب التاريخ أو الفئة أو المؤلف
  • المسح الخطي فقط — لا توجد فهرسة تقريبية لأقرب الجيران
  • لا يوجد وصول متزامن — هذا الأسلوب غير مناسب لعمليات النشر الإنتاجية متعددة المستخدمين

تدفع هذه القيود إلى استخدام قاعدة بيانات متجهية مخصصة لأنظمة RAG الإنتاجية.

تحقق سريع

اختبر مدى فهمك لمفاهيم هندسة الذكاء الاصطناعي الواردة في هذا الدرس.

مراجعة الدرس

تعلمت في هذا الدرس أن: حساب الضرب النقطي للمصفوفة مع embeddings المطبعّة وفق معيار L2 يحسب التشابه الجيبي لمجموعة المستندات بأكملها في عملية واحدة، وأن استخدام np.argsort مع العكس يسترجع أكثر المستندات تشابهًا ضمن أفضل k نتائج، وأن البحث باستخدام NumPy مثالي للنماذج الأولية، لكنه يفتقر إلى الحفظ المستمر وتصفية البيانات الوصفية. بعد ذلك سنستخدم التجميع وUMAP لاكتشاف بنية الموضوعات في مجموعة من embeddings.

الأسئلة الشائعة

هل درس «البحث الدلالي باستخدام NumPy» مجاني؟

نعم — نص درس «البحث الدلالي باستخدام NumPy» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.

ماذا ستتعلم في «البحث الدلالي باستخدام NumPy»؟

ابنوا نظام بحث دلالي باستخدام Python فقط وNumPy لحساب التشابه الجيبي بين embedding للاستعلام ومجموعة من embeddings المستندات. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟

لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «البحث الدلالي باستخدام NumPy»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟

نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ما هي Vector Embeddings؟
  2. إنشاء Embeddings باستخدام OpenAI
  3. البحث الدلالي باستخدام NumPy
  4. تجميع Embeddings وتصويرها
← العودة إلى AI Engineering Academy