البحث باستخدام قوائم التضمين
تشرح هذه الصفحة كيفية إعداد نظام ColBERT لاسترجاع النصوص ونظام ColPali لاسترجاع النصوص باستخدام مصفوفة البنى في ميلفوس، والتي تمكنك من تخزين مستند مع أجزائه المضمنة في قوائم التضمين.
نظرة عامة
لبناء نظام استرجاع نصي، قد تحتاج إلى تقسيم المستندات إلى أجزاء وتخزين كل جزء مع تضميناته ككيان في قاعدة بيانات متجهة لضمان الدقة والدقة، خاصةً بالنسبة للمستندات الطويلة حيث يمكن أن يؤدي تضمين النص الكامل إلى إضعاف الخصوصية الدلالية أو تجاوز حدود مدخلات النموذج.
ومع ذلك، فإن تخزين البيانات في أجزاء يؤدي إلى نتائج بحث على شكل قطع، مما يعني أن الاسترجاع يحدد في البداية الأجزاء ذات الصلة بدلاً من المستندات المتماسكة. لمعالجة ذلك، يجب إجراء معالجة إضافية بعد البحث.
إن ColBERT (arXiv: 2004.12832) هو نظام استرجاع نص-نص يوفر بحثًا فعالاً وكفؤًا عن المقاطع من خلال تفاعلات متأخرة في السياق عبر BERT. وهو يتيح ترميزًا رمزيًا مستقلاً للاستعلامات والمستندات وحساب تشابهها.
الترميز الرمزي الحكيم
أثناء استيعاب البيانات في ColBERT، يتم تقسيم كل مستند إلى رموز، والتي يتم بعد ذلك تحويلها إلى رموز متجهة وتخزينها كقائمة تضمين، كما في ∈ d , , , d . عند وصول استعلام، يتم أيضًا ترميزه وترميزه وتخزينه كقائمة تضمينات، كما في , ∈ q e e ] .
في الصيغ أعلاه
د: مستند
q: الاستعلام
E: قائمة التضمين التي تمثل المستند.
E: قائمة التضمين التي تمثل الاستعلام.
,,: عدد التضمينات المتجهة في قائمة التضمين التي تمثل المستند في نطاق R .
∈,,,]: عدد التضمينات المتجهة في قائمة التضمين التي تمثل الاستعلام في نطاق R .
التفاعل المتأخر
بمجرد اكتمال التضمين المتجه، تتم مقارنة قائمة تضمين الاستعلام بقائمة تضمين كل مستند، رمزًا برمز، لتحديد درجة التشابه النهائية.
التفاعل المتأخر
كما هو موضح في الرسم البياني أعلاه، يحتوي الاستعلام على رمزين مميزين، وهما machine و learning ، ويحتوي المستند الموجود في النافذة على أربعة رموز مميزة: neural network و python و و tutorial. بمجرد تحويل هذه الرموز إلى متجهات، تتم مقارنة التضمينات المتجهة لكل رمز رمزي للاستعلام مع تلك الموجودة في المستند للحصول على قائمة درجات التشابه. ثم يتم جمع أعلى الدرجات من كل قائمة درجات للحصول على الدرجة النهائية. تُعرف عملية تحديد الدرجة النهائية للمستند بالدرجة القصوى للتشابه(MAX_SIM). للحصول على تفاصيل حول التشابه الأقصى، راجع التشابه الأقصى.
عند تنفيذ نظام استرجاع النصوص الشبيه بـ ColBERT في ميلفوس، لا يقتصر الأمر على تقسيم المستندات إلى رموز.
بدلاً من ذلك، يمكنك تقسيم المستندات إلى مقاطع بأي حجم مناسب، وتضمين كل مقطع لإنشاء قائمة تضمين، وتخزين المستند مع المقاطع المضمنة فيه في كيان.
امتداد كولبالي
استنادًا إلى ColBERT، يقترح ColPali (arXiv: 2407.01449) نهجًا جديدًا لاسترجاع المستندات الغنية بصريًا يستفيد من نماذج اللغة المرئية (VLMs). أثناء استيعاب البيانات، يتم تحويل كل صفحة مستند إلى صورة عالية الدقة، ثم يتم تقسيمها إلى رقع، بدلاً من ترميزها. على سبيل المثال، يمكن أن تنتج صورة صفحة مستند بحجم 448 × 448 بكسل 1,024 رقعة، كل منها بقياس 14 × 14 بكسل.
تحافظ هذه الطريقة على المعلومات غير النصية، مثل تخطيط المستند والصور وهياكل الجداول، والتي تُفقد عند استخدام أنظمة الاسترجاع النصية فقط.
ملحق كوبالي
يُطلق على النموذج اللغوي المرئي المستخدم في ColPali اسم PaliGemma (arXiv: 2407.07726)، والذي يتألف من مشفر صور(SigLIP-400M)، ونموذج لغوي لفك التشفير فقط(Gemma2-2B)، وطبقة خطية تقوم بإسقاط مخرجات مشفر الصور في الفضاء المتجه لنموذج اللغة، كما هو موضح في الرسم البياني أعلاه.
أثناء استيعاب البيانات، يتم تقسيم صفحة المستند، الممثلة كصورة خام، إلى عدة رقع مرئية، يتم تضمين كل منها لتوليد قائمة من التضمينات المتجهة. ثم يتم إسقاطها في الفضاء المتجه لنموذج اللغة للحصول على قائمة التضمين النهائية، كما هو الحال في ∈ d , , , d . عندما يصل استعلام، يتم ترميزه، ويتم تضمين كل رمز رمزي لتوليد قائمة من التضمينات المتجهة، كما في , ∈ q e e ] . بعد ذلك، تم تطبيق MAX_SIM لمقارنة قائمتي التضمين والحصول على النتيجة النهائية بين الاستعلام وصفحة المستند.
نظام استرجاع النصوص ColBERT
في هذا القسم، سنقوم في هذا القسم بإعداد نظام ColBERT لاسترجاع النصوص باستخدام مصفوفة الهياكل الخاصة بـ Milvus. قبل ذلك، قم بإعداد مثيل Milvus v2.6.xمجموعة سحابة Milvus v2.6.x، واحصل على رمز وصول Cohere.
الخطوة 1: تثبيت التبعيات
قم بتشغيل الأمر التالي لتثبيت التبعيات.
pip install --upgrade huggingface-hub transformers datasets pymilvus cohere
الخطوة 2: قم بتحميل مجموعة بيانات Cohere
في هذا المثال، سنستخدم في هذا المثال مجموعة بيانات كوهير الخاصة بويكيبيديا واسترجاع أول 10,000 سجل. يمكنك العثور على معلومات حول مجموعة البيانات هذه على هذه الصفحة.
from datasets import load_dataset
lang = "simple"
docs = load_dataset(
"Cohere/wikipedia-2023-11-embed-multilingual-v3",
lang,
split="train[:10000]"
)
سيؤدي تشغيل البرامج النصية أعلاه إلى تنزيل مجموعة البيانات إذا لم تكن متاحة محليًا. كل سجل في مجموعة البيانات عبارة عن فقرة من صفحة ويكيبيديا. يوضح الجدول التالي بنية مجموعة البيانات هذه.
اسم العمود |
الوصف |
|---|---|
|
معرّف السجل |
|
عنوان URL للسجل الحالي. |
|
عنوان المستند المصدر. |
|
فقرة من المستند المصدر. |
|
تضمين النص من المستند المصدر. |
الخطوة 3: تجميع الفقرات حسب العنوان
للبحث عن المستندات بدلاً من الفقرات، علينا تجميع الفقرات حسب العنوان.
df = docs.to_pandas()
groups = df.groupby('title')
data = []
for title, group in groups:
data.append({
"title": title,
"paragraphs": [{
"text": row['text'],
'emb': row['emb']
} for _, row in group.iterrows()]
})
في هذا الرمز، نقوم بتخزين الفقرات المجمّعة كمستندات وتضمينها في قائمة data. يحتوي كل مستند على مفتاح paragraphs ، وهو عبارة عن قائمة بالفقرات؛ ويحتوي كل كائن فقرة على المفتاحين text و emb.
الخطوة 4: إنشاء مجموعة لمجموعة بيانات Cohere
بمجرد أن تصبح البيانات جاهزة، سننشئ مجموعة. في المجموعة، هناك حقل اسمه paragraphs ، وهو عبارة عن مصفوفة من الهياكل.
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# Create collection schema
schema = client.create_schema()
schema.add_field('id', DataType.INT64, is_primary=True, auto_id=True)
schema.add_field('title', DataType.VARCHAR, max_length=512)
# Create struct schema
struct_schema = client.create_struct_field_schema()
struct_schema.add_field('text', DataType.VARCHAR, max_length=65535)
struct_schema.add_field('emb', DataType.FLOAT_VECTOR, dim=512)
schema.add_field('paragraphs', DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=struct_schema, max_capacity=200)
# Create index parameters
index_params = client.prepare_index_params()
index_params.add_index(
field_name="paragraphs[emb]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE"
)
# Create a collection
client.create_collection(
collection_name='wiki_documents',
schema=schema,
index_params=index_params
)
الخطوة 5: إدراج مجموعة بيانات Cohere في المجموعة
يمكننا الآن إدراج البيانات المعدة في المجموعة التي أنشأناها أعلاه.
client.insert(
collection_name='wiki_documents',
data=data
)
الخطوة 6: البحث داخل مجموعة بيانات Cohere
وفقًا لتصميم ColBERT، يجب ترميز نص الاستعلام ثم تضمينه في قائمة تضمين. في هذه الخطوة، سنستخدم نفس النموذج الذي استخدمه كوهير لإنشاء تضمينات للفقرات في مجموعة بيانات ويكيبيديا.
import cohere
co = cohere.ClientV2("COHERE_API_KEY")
query_inputs = [
{
'content': [
{'type': 'text', 'text': 'Adobe'},
]
},
{
'content': [
{'type': 'text', 'text': 'software'}
]
}
]
embeddings = co.embed(
inputs=query_inputs,
model='embed-multilingual-v3.0',
input_type="classification",
embedding_types=["float"],
)
في الكود، يتم تنظيم نصوص الاستعلام إلى رموز في query_inputs ويتم تضمينها في قائمة من المتجهات العائمة. بعد ذلك يمكنك استخدام قائمة تضمين ميلفوس EmbeddingList لإجراء بحث تشابه على النحو التالي.
from pymilvus.client.embedding_list import EmbeddingList
query_emb_list = EmbeddingList()
if (embeddings.embeddings.float):
query_emb_list.add_batch(embeddings.embeddings.float)
results = client.search(
collection_name="wiki_documents",
data=[query_emb_list],
anns_field="paragraphs[emb]",
search_params={
"metric_type": "MAX_SIM_COSINE"
},
limit=10,
output_fields=["title"]
)
for hit in results[0]:
print(f"Document {hit['entity']['title']}: {hit['distance']:.4f}")
تكون مخرجات الكود أعلاه مشابهة لما يلي:
# Document Software: 2.3035
# Document Application: 2.1875
# Document Adobe Illustrator: 2.1167
# Document Open source: 2.0542
# Document Computer: 1.9811
# Document Microsoft: 1.9784
# Document Web browser: 1.9655
# Document Program: 1.9627
# Document Website: 1.9594
# Document Computer science: 1.9460
تتراوح درجة التشابه في جيب التمام من -1 إلى 1 ، وتوضح درجات التشابه في الناتج أعلاه بوضوح مجموع درجات التشابه على مستوى الرموز المتعددة.
نظام كولبالي لاسترجاع النصوص
في هذا القسم، سنقوم في هذا القسم بإعداد نظام استرجاع النصوص المستند إلى ColPali باستخدام مصفوفة الهياكل الخاصة بـ Milvus. قبل ذلك، قم بإعداد مثيل لميلفوس الإصدار 2.6.xمجموعة سحابة زيلز السحابية المتوافقة مع ميلفوس الإصدار 2.6.x.
الخطوة 1: تثبيت التبعيات
pip install --upgrade huggingface-hub transformers datasets pymilvus 'colpali-engine>=0.3.0,<0.4.0'
الخطوة 2: تحميل مجموعة بيانات Vidore
في هذا القسم، سنستخدم مجموعة بيانات Vidore المسماة vidore_v2_finance_en. مجموعة البيانات هذه عبارة عن مجموعة من التقارير السنوية من القطاع المصرفي، وهي مخصصة لمهام فهم المستندات الطويلة. وهي واحدة من 10 مجموعات تضم معيار ViDoRe v3. يمكنك العثور على تفاصيل حول مجموعة البيانات هذه في هذه الصفحة.
from datasets import load_dataset
ds = load_dataset("vidore/vidore_v3_finance_en", "corpus")
df = ds['test'].to_pandas()
سيؤدي تشغيل البرامج النصية أعلاه إلى تنزيل مجموعة البيانات إذا لم تكن متوفرة محليًا. كل سجل في مجموعة البيانات هو صفحة من تقرير مالي. يوضح الجدول التالي بنية مجموعة البيانات هذه.
اسم العمود |
الوصف |
|---|---|
|
سجل في مجموعة البيانات |
|
صورة الصفحة بالبايت. |
|
معرّف المستند الوصفي. |
|
رقم الصفحة للصفحة الحالية في المستند. |
الخطوة 3: توليد تضمينات لصور الصفحات
كما هو موضح في قسم "نظرة عامة "، فإن نموذج ColPali هو نموذج VLM الذي يقوم بإسقاط الصور في الفضاء المتجه لنموذج نصي. في هذه الخطوة، سنستخدم أحدث نموذج ColPali vidore/colpali-v1.3. يمكنك العثور على تفاصيل حول هذا النموذج في هذه الصفحة.
import torch
from typing import cast
from colpali_engine.models import ColPali, ColPaliProcessor
model_name = "vidore/colpali-v1.3"
model = ColPali.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="cuda:0", # or "mps" if on Apple Silicon
).eval()
processor = ColPaliProcessor.from_pretrained(model_name)
بمجرد أن يصبح النموذج جاهزًا، يمكنك محاولة إنشاء تصحيحات لصورة معينة على النحو التالي.
from PIL import Image
from io import BytesIO
# Use the iterrow() generator to get the first row
row = next(df.iterrows())[1]
# Include the image in the above row in a list
images = [ Image.open(row['image']['bytes'] ]
patches = processor.process_images(images).to(model.device)
patches_embeddings = model(**patches_in_pixels)[0]
# Check the shape of the embeddings generated for the patches
print(patches_embeddings.shape)
# [1031, 128]
في الشيفرة أعلاه، يقوم نموذج ColPali بتغيير حجم الصورة إلى 448 × 448 بكسل، ثم يقسمها إلى رقع، كل منها بقياس 14 × 14 بكسل. أخيرًا، يتم تضمين هذه الرقع في 1,031 تضمينًا، كل منها يحتوي على 128 بُعدًا.
يمكنك إنشاء تضمينات لجميع الصور باستخدام حلقة على النحو التالي:
data = []
for index, row in df.iterrows():
row = next(df.iterrows())[1]
corpus_id = row['corpus_id']
images = [Image.open(BytesIO(row['image']['bytes']))]
batch_images = processor.process_images(images).to(model.device)
patches = model(**batch_images)[0]
doc_id = row['doc_id']
markdown = row['markdown']
page_number_in_doc = row['page_number_in_doc']
data.append({
"corpus_id": corpus_id,
"patches": [ {"emb": emb} for emb in patches ],
"doc_id": markdown,
"page_number_in_doc": row['page_number_in_doc']
})
تستغرق هذه الخطوة وقتًا طويلاً نسبيًا نظرًا لكمية البيانات الكبيرة التي يجب تضمينها.
الخطوة 4: إنشاء مجموعة لمجموعة بيانات التقارير المالية
بمجرد أن تصبح البيانات جاهزة، سننشئ مجموعة. في المجموعة، حقل اسمه patches هو مصفوفة من الهياكل.
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri=YOUR_CLUSTER_ENDPOINT,
token=YOUR_API_KEY
)
schema = client.create_schema()
schema.add_field(
field_name="corpus_id",
datatype=DataType.INT64,
is_primary=True
)
patch_schema = client.create_struct_field_schema()
patch_schema.add_field(
field_name="emb",
datatype=DataType.FLOAT_VECTOR,
dim=128
)
schema.add_field(
field_name="patches",
datatype=DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=patch_schema,
max_capacity=1031
)
schema.add_field(
field_name="doc_id",
datatype=DataType.VARCHAR,
max_length=512
)
schema.add_field(
field_name="page_number_in_doc",
datatype=DataType.INT64
)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="patches[emb]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE"
)
client.create_collection(
collection_name="financial_reports",
schema=schema,
index_params=index_params
)
الخطوة 5: إدراج التقارير المالية في المجموعة
الآن يمكننا إدراج التقارير المالية المعدة في المجموعة.
client.insert(
collection_name="financial_reports",
data=data
)
من المخرجات، يمكنك أن تجد أنه تم إدراج جميع الصفحات من مجموعة بيانات فيدور.
الخطوة 6: البحث داخل التقارير المالية
بمجرد أن تصبح البيانات جاهزة، يمكننا إجراء عمليات بحث مقابل البيانات الموجودة في المجموعة على النحو التالي:
from pymilvus.client.embedding_list import EmbeddingList
queries = [
"quarterly revenue growth chart"
]
batch_queries = processor.process_queries(queries).to(model.device)
with torch.no_grad():
query_embeddings = model(**batch_queries)
query_emb_list = EmbeddingList()
query_emb_list.add_batch(query_embeddings[0].cpu())
results = client.search(
collection_name="financial_reports",
data=[query_emb_list],
anns_field="patches[emb]",
search_params={
"metric_type": "MAX_SIM_COSINE"
},
limit=10,
output_fields=["doc_id", "page_number_in_doc"]
)