البحث الهجين في Pinecone وpgvector
اضبطوا البحث الهجين في Pinecone باستخدام وضع الفهرس المتناثر-الكثيف، وفي pgvector باستخدام استعلامات متوازية مع دمج RRF، وقارنوا جودة الاسترجاع على مجموعة بياناتكم.
البحث الهجين في Pinecone وpgvector درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
البحث الهجين الأصلي في قواعد بيانات المتجهات
يمكنك تنفيذ البحث الهجين بنفسك باستخدام فهرسين منفصلين ودمج RRF، إلا أن قواعد بيانات المتجهات المخصصة للإنتاج توفر بشكل متزايد بحثًا هجينًا مدمجًا يتعامل مع الاسترجاع المتناثر والكثيف ضمن استعلام واحد. يدعم كل من Pinecone وpgvector الأوضاع الهجينة، لكن بخيارات معمارية ومفاضلات مختلفة. يتيح لك فهم الخيارين اختيار الأداة المناسبة لبنيتك التحتية.
وضع الفهرس المتناثر-الكثيف في Pinecone
يدعم Pinecone فهرسًا متناثرًا-كثيفًا يخزّن فيه كل سجل متجه تضمينًا كثيفًا (كمصفوفة من الأعداد العشرية) ومتجهًا متناثرًا (كقاموس يربط معرّف الرمز بوزنه). ويمكن للاستعلامات تحديد متجه استعلام كثيف ومتجه استعلام متناثر، ثم يدمج Pinecone النتائج باستخدام تركيبة خطية موزونة. ويختلف هذا عن RRF؛ إذ يستخدم Pinecone دمج الدرجات الخام بأوزان قابلة للضبط تُسمى alpha.
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key='YOUR_API_KEY')
# Create a sparse-dense index
pc.create_index(
name='hybrid-index',
dimension=1536, # dense vector dimension
metric='dotproduct', # must use dotproduct for hybrid
spec=ServerlessSpec(cloud='aws', region='us-east-1'),
)
index = pc.Index('hybrid-index')إنشاء متجهات متناثرة باستخدام SPLADE
لاستخدام الوضع المتناثر-الكثيف في Pinecone، تحتاج إلى إنشاء متجهات متناثرة لكل مستند. النهج الأكثر فاعلية هو SPLADE (Sparse Lexical and Expansion)، وهو نموذج عصبي ينتج تمثيلات متناثرة متعلَّمة مع التوسيع؛ إذ يضيف مصطلحات مرتبطة دلاليًا إلى المتجه المتناثر، بما يتجاوز ما يظهر حرفيًا في النص. وبدلًا من ذلك، يمكنك استخدام أوزان المصطلحات البسيطة في BM25 بوصفها متجهات متناثرة.
from transformers import AutoTokenizer, AutoModelForMaskedLM
import torch
# Load SPLADE model
tokenizer = AutoTokenizer.from_pretrained('naver/splade-cocondenser-ensembledistil')
model = AutoModelForMaskedLM.from_pretrained('naver/splade-cocondenser-ensembledistil')
def generate_sparse_vector(text: str) -> dict:
tokens = tokenizer(text, return_tensors='pt', truncation=True, max_length=512)
with torch.no_grad():
output = model(**tokens)
logits = output.logits
# Max-pool over sequence length and apply log1p activation
sparse = torch.max(torch.log1p(torch.relu(logits)), dim=1).values.squeeze()
# Return non-zero indices and values as a sparse dict
nz_indices = sparse.nonzero().squeeze().tolist()
nz_values = sparse[nz_indices].tolist()
return {'indices': nz_indices, 'values': nz_values}إدراج المتجهات الهجينة في Pinecone
يخزّن كل سجل في Pinecone ضمن فهرس هجين id ومصفوفة values كثيفة وقاموس sparse_values يضم indices وvalues، إضافةً إلى metadata اختيارية. أدرج السجلات دفعاتٍ من 100 سجل لزيادة معدل النقل إلى أقصى حد، مع البقاء ضمن حدود حجم الطلب في Pinecone.
def upsert_hybrid_docs(index, documents: list[dict], batch_size: int = 100):
records = []
for doc in documents:
dense_vec = embed(doc['text']) # OpenAI embedding
sparse_vec = generate_sparse_vector(doc['text']) # SPLADE
records.append({
'id': doc['id'],
'values': dense_vec,
'sparse_values': sparse_vec,
'metadata': {'text': doc['text'], 'source': doc['source']},
})
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
index.upsert(vectors=batch)
print(f'Upserted batch {i//batch_size + 1}')الاستعلام عن الفهرس الهجين في Pinecone
يمرر الاستعلام الهجين كلًا من vector الكثيف وsparse_vector المتناثر إلى واجهة استعلام Pinecone. وتتحكم معلمة alpha (من 0 إلى 1) في التوازن: alpha=1.0 يعني كثيفًا خالصًا، وalpha=0.0 يعني متناثرًا خالصًا، وalpha=0.5 يمنح كليهما الوزن نفسه. اضبط alpha باستخدام مجموعة تحقق؛ وتتراوح القيم المثلى المعتادة بين 0.3 و0.7.
def hybrid_query_pinecone(index, query: str, alpha: float = 0.5, top_k: int = 5):
dense_vec = embed(query)
sparse_vec = generate_sparse_vector(query)
# Scale vectors by alpha for weighted fusion
scaled_dense = [v * alpha for v in dense_vec]
scaled_sparse = {
'indices': sparse_vec['indices'],
'values': [v * (1 - alpha) for v in sparse_vec['values']],
}
results = index.query(
vector=scaled_dense,
sparse_vector=scaled_sparse,
top_k=top_k,
include_metadata=True,
)
return results.matchespgvector: البحث عن المتجهات في PostgreSQL
تضيف إضافة pgvector نوع العمود vector وعوامل المسافة إلى PostgreSQL. ولتنفيذ البحث الهجين، تشغّل استعلامين بالتوازي: استعلام أقرب جار تقريبي على عمود المتجه، واستعلام بحث في النص الكامل باستخدام tsvector وtsquery المدمجين في PostgreSQL، ثم تدمج النتائج باستخدام RRF في تعليمات تطبيقك البرمجية.
-- Setup: enable extensions
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Create hybrid-capable table
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(1536),
content_tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED
);
-- Indexes for both search modes
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON documents USING GIN (content_tsv);تشغيل الاستعلامات الكثيفة والمتناثرة في pgvector
باستخدام pgvector، شغّل الاستعلام الكثيف باستخدام عامل مسافة جيب التمام <=> للعثور على أقرب جيران تضمين الاستعلام، وشغّل الاستعلام المتناثر باستخدام ts_rank_cd مقابل عمود tsvector لتقييم تطابقات الكلمات المفتاحية. استرجع مجموعتي النتائج كلًّا على حدة، ثم ادمجهما باستخدام RRF في Python.
import psycopg2
import json
def pgvector_hybrid_search(conn, query: str, top_k: int = 5):
dense_vec = embed(query)
tsquery = ' & '.join(query.split()) # simple AND query
# Dense query
dense_sql = '''
SELECT id, content, 1 - (embedding <=> %s::vector) AS score
FROM documents
ORDER BY embedding <=> %s::vector
LIMIT 20
'''
# Sparse query
sparse_sql = '''
SELECT id, content, ts_rank_cd(content_tsv, to_tsquery('english', %s)) AS score
FROM documents
WHERE content_tsv @@ to_tsquery('english', %s)
ORDER BY score DESC
LIMIT 20
'''
with conn.cursor() as cur:
cur.execute(dense_sql, [json.dumps(dense_vec), json.dumps(dense_vec)])
dense_rows = cur.fetchall()
cur.execute(sparse_sql, [tsquery, tsquery])
sparse_rows = cur.fetchall()
return fuse_with_rrf(dense_rows, sparse_rows, top_k)تطبيق RRF على نتائج pgvector
بعد جمع الصفوف المرتبة من استعلامَي PostgreSQL، طبّق RRF باستخراج معرّفات المستندات بترتيبها من كل مجموعة نتائج، ثم شغّل خوارزمية الدمج. تمنحك القائمة المدمجة النهائية مستندات top-K الأكثر صلة من حيث المعنى الدلالي وتداخل الكلمات المفتاحية معًا.
def fuse_with_rrf(dense_rows, sparse_rows, top_k: int = 5, k: int = 60):
from collections import defaultdict
doc_texts = {}
scores = defaultdict(float)
for rank, row in enumerate(dense_rows, start=1):
doc_id, text, _ = row
scores[doc_id] += 1.0 / (k + rank)
doc_texts[doc_id] = text
for rank, row in enumerate(sparse_rows, start=1):
doc_id, text, _ = row
scores[doc_id] += 1.0 / (k + rank)
doc_texts[doc_id] = text
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
return [{'id': doc_id, 'text': doc_texts[doc_id], 'rrf_score': s}
for doc_id, s in ranked[:top_k]]قياس جودة الاسترجاع
بعد تنفيذ البحث الهجين، قيّمه بدقة مقارنةً بخط الأساس للاسترجاع الكثيف فقط. استخدم مجموعة اختبار مرجعية تضم 100 استعلام على الأقل مع مستندات ذات صلة معروفة. قِس NDCG@5 وMRR ومعدل الإصابة@3. تتراوح التحسينات المعتادة الناتجة عن البحث الهجين بين 5% و20% في NDCG@5، وتظهر أكبر المكاسب في الاستعلامات التي تتضمن مصطلحات تقنية دقيقة تفشل النماذج الكثيفة في العثور عليها.
def evaluate_retrieval(search_fn, test_queries, golden_relevant, k=5):
ndcg_scores = []
hit_count = 0
for query, relevant_ids in zip(test_queries, golden_relevant):
results = search_fn(query, top_k=k)
retrieved_ids = [r['id'] for r in results]
# Hit rate
if any(rid in relevant_ids for rid in retrieved_ids):
hit_count += 1
# Simplified NDCG (binary relevance)
dcg = sum(
1.0 / (i + 1)
for i, rid in enumerate(retrieved_ids)
if rid in relevant_ids
)
idcg = sum(1.0 / (i + 1) for i in range(min(len(relevant_ids), k)))
ndcg_scores.append(dcg / idcg if idcg > 0 else 0)
return {
'hit_rate': hit_count / len(test_queries),
'ndcg': sum(ndcg_scores) / len(ndcg_scores),
}ضبط Alpha في الوضع الهجين لـ Pinecone
تتطلب معلمة alpha في الاستعلام الهجين لـ Pinecone ضبطًا منهجيًا. ويُعد التقسيم حسب نوع الاستعلام نهجًا مفيدًا: صنّف استعلامات الاختبار إلى استعلامات دلالية غالبًا (إعادة صياغة أو مفاهيم) أو معجمية غالبًا (مصطلحات أو رموز دقيقة)، وقِس قيمة alpha المثلى لكل مجموعة على حدة. تطبّق بعض أنظمة الإنتاج اختيار alpha ديناميكيًا، فتصنّف الاستعلام وقت التشغيل وتختار alpha المناسبة تلقائيًا.
def find_optimal_alpha(index, test_queries, golden_relevant, alphas=None):
if alphas is None:
alphas = [0.0, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0]
best_alpha, best_score = 0.5, 0
for alpha in alphas:
hits = 0
for query, relevant in zip(test_queries, golden_relevant):
results = hybrid_query_pinecone(index, query, alpha=alpha, top_k=5)
if any(r['id'] in relevant for r in results):
hits += 1
hit_rate = hits / len(test_queries)
print(f'alpha={alpha}: hit_rate={hit_rate:.3f}')
if hit_rate > best_score:
best_score, best_alpha = hit_rate, alpha
return best_alphaالاختيار بين البحث الهجين في Pinecone وpgvector
استخدم البحث الهجين في Pinecone عندما تحتاج إلى بنية تحتية مُدارة وتحجيم تلقائي، وترغب في إسناد تعقيد إنشاء المتجهات المتناثرة إلى واجهة API واحدة. واستخدم البحث الهجين في pgvector عندما تكون لديك بنية PostgreSQL قائمة، أو تحتاج إلى اتساق المعاملات بين المستندات وبياناتها الوصفية، أو تريد مرونة SQL الكاملة للتصفية، أو تحتاج إلى تجنب الارتهان لمورّد واحد. يحقق النهجان جودة ممتازة في الاسترجاع الهجين عند ضبطهما ضبطًا صحيحًا.
اختبار سريع
اختبر مدى فهمك لتنفيذ البحث الهجين من هذا الدرس.
مراجعة الدرس
تعلّمت في هذا الدرس أن: الوضع المتناثر-الكثيف في Pinecone يخزّن المتجهين الكثيف والمتناثر لكل سجل ويدمجهما باستخدام معلمة alpha قابلة للضبط، وأن البحث الهجين في pgvector يجمع بين البحث النصي الكامل عبر SQL واستعلامات المتجهات، مع دمجهما باستخدام RRF في تعليمات التطبيق البرمجية، وأن ضبط alpha باستخدام مجموعة تحقق ضروري للعثور على التوازن الأمثل لتوزيع استعلاماتك. بعد ذلك سنستكشف الاسترجاع على مرحلتين مع إعادة الترتيب.
الأسئلة الشائعة
هل درس «البحث الهجين في Pinecone وpgvector» مجاني؟
نعم — نص درس «البحث الهجين في Pinecone وpgvector» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
ماذا ستتعلم في «البحث الهجين في Pinecone وpgvector»؟
اضبطوا البحث الهجين في Pinecone باستخدام وضع الفهرس المتناثر-الكثيف، وفي pgvector باستخدام استعلامات متوازية مع دمج RRF، وقارنوا جودة الاسترجاع على مجموعة بياناتكم. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟
لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «البحث الهجين في Pinecone وpgvector»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟
نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الاسترجاع الكثيف مقابل المتناثر: المفاضلات
- تنفيذ البحث بالكلمات المفتاحية باستخدام BM25
- دمج الدرجات باستخدام دمج الرتب التبادلي
- البحث الهجين في Pinecone وpgvector