تعد نماذج التضمين مفيدة للعديد من التطبيقات مثل الاسترجاع وإعادة الترتيب والتجميع والتصنيف. شهد مجتمع البحث تطورات كبيرة في السنوات الأخيرة في تضمين النماذج، مما أدى إلى تحسينات كبيرة في جميع التطبيقات المبنية على التمثيل الدلالي. يتم وضع نماذج مثل BGE وGTE وE5 في أعلى معيار MTEB وفي بعض الحالات تتفوق على خدمات التضمين الخاصة. توجد مجموعة متنوعة من أحجام النماذج الموجودة في مركز نماذج Hugging Face، بدءًا من الوزن الخفيف (معلمات 100-350 مليونًا) وحتى نماذج 7B (مثل Salesforce/SFR-Embedding-Mistral). تعد النماذج خفيفة الوزن المستندة إلى بنية التشفير مرشحة مثالية للتحسين والاستخدام على الواجهات الخلفية لوحدة المعالجة المركزية (CPU) التي تقوم بتشغيل التطبيقات القائمة على البحث الدلالي، مثل Retrieval Augmented Generation (RAG).

في هذه المدونة، سنعرض كيفية فتح تعزيز كبير للأداء على وحدات المعالجة المركزية المستندة إلى Xeon، ونوضح مدى سهولة دمج النماذج المحسنة في خطوط أنابيب RAG الحالية باستخدام fastRAG.

استرجاع المعلومات باستخدام نماذج التضمين

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

هل ينبغي دائمًا استخدام المتجهات الكثيفة لاسترجاع المعلومات؟ النهجان السائدان لهما مقايضات:

  • يتطابق الاسترجاع المتفرق مع n-grams أو العبارات أو البيانات الوصفية للبحث في مجموعات كبيرة بكفاءة وعلى نطاق واسع. ومع ذلك، قد يفقد المستندات ذات الصلة بسبب وجود فجوات معجمية بين الاستعلام والمستند.
  • يعمل الاسترجاع الدلالي على تشفير النص إلى نواقل كثيفة، مما يلتقط السياق والمعنى بشكل أفضل من حقيبة الكلمات. يمكنه استرداد المستندات ذات الصلة لغويًا على الرغم من عدم التطابق المعجمي. ومع ذلك، فهو مكثف من الناحية الحسابية، وله زمن وصول أعلى، ويتطلب نماذج تشفير متطورة مقارنة بالمطابقة المعجمية مثل BM25.

نماذج التضمين و RAG

تخدم نماذج التضمين أغراضًا متعددة وحاسمة في تطبيقات RAG:

  • العملية غير المتصلة بالإنترنت: تشفير المستندات في ناقلات كثيفة أثناء فهرسة/تحديث مخزن مستندات الاسترجاع (الفهرس).
  • ترميز الاستعلام: في وقت الاستعلام، يقومون بتشفير استعلام الإدخال في تمثيل متجه كثيف للاسترجاع.
  • إعادة الترتيب: بعد الاسترداد الأولي، يمكنهم إعادة ترتيب المستندات المستردة عن طريق تشفيرها في متجهات كثيفة ومقارنتها بمتجه الاستعلام. وهذا يسمح بإعادة ترتيب المستندات التي كانت تفتقر في البداية إلى التمثيلات الكثيفة.

يعد تحسين مكون نموذج التضمين في خطوط أنابيب RAG أمرًا مرغوبًا للغاية للحصول على تجربة ذات كفاءة أعلى، وعلى الأخص:

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

تحسين نماذج التضمين باستخدام Optimum Intel وIPEX

Optimum Intel هي مكتبة مفتوحة المصدر تعمل على تسريع خطوط الأنابيب الشاملة المبنية باستخدام مكتبات Hugging Face على أجهزة Intel. يتضمن Optimum Intel العديد من التقنيات لتسريع النماذج مثل تكميم البت المنخفض، وتقليص وزن النموذج، والتقطير، ووقت التشغيل المتسارع.

يستفيد وقت التشغيل والتحسينات المضمنة في Optimum Intel من Intel® Advanced Vector Extensions 512 (Intel® AVX-512)، وتعليمات الشبكة العصبية الموجهة (VNNI) وIntel® Advanced Matrix Extensions (Intel® AMX) على وحدات المعالجة المركزية Intel لتسريع النماذج. على وجه التحديد، فقد تم دمجها في BFloat16 (bf16) و int8 مسرعات GEMM في كل مركز لتسريع التدريب على التعلم العميق واستنتاج أعباء العمل. يتم تقديم الاستدلال المتسارع AMX في PyTorch 2.0 وIntel Extension for PyTorch (IPEX) بالإضافة إلى تحسينات أخرى لمختلف المشغلين المشتركين.

يمكن تحسين النماذج المدربة مسبقًا بسهولة باستخدام Optimum Intel؛ يمكن العثور على العديد من الأمثلة البسيطة هنا.

مثال: تحسين نماذج التضمين BGE

في هذه المدونة، نركز على نماذج التضمين التي تم إصدارها مؤخرًا من قبل الباحثين في أكاديمية بكين للذكاء الاصطناعي، حيث تُظهر نماذجهم نتائج تنافسية على لوحة المتصدرين MTEB المعتمدة على نطاق واسع.

التفاصيل الفنية لـ BGE

نماذج التشفير الثنائي هي أجهزة تشفير قائمة على المحولات تم تدريبها لتقليل مقياس التشابه، مثل تشابه جيب التمام، بين نصين متشابهين لغويًا كمتجهات. على سبيل المثال، تستخدم نماذج التضمين الشائعة نموذج BERT كنموذج أساسي تم تدريبه مسبقًا وتقوم بضبطه لتضمين المستندات. يتم إنشاء المتجه الذي يمثل النص المشفر من مخرجات النموذج؛ على سبيل المثال، يمكن أن يكون [CLS] ناقل الرمز المميز أو متوسط ​​جميع ناقلات الرمز المميز.

على عكس بنيات التضمين الأكثر تعقيدًا، تقوم أجهزة التشفير الثنائية بتشفير المستندات الفردية فقط، وبالتالي فهي تفتقر إلى التفاعل السياقي بين الكيانات المشفرة مثل مستند الاستعلام ومستند المستند. ومع ذلك، فإن نماذج التضمين ثنائية التشفير الحديثة تقدم أداءً تنافسيًا وسريعة للغاية نظرًا لبنيتها البسيطة.

نحن نركز على 3 نماذج BGE: صغيرة وقاعدية وكبيرة تتكون من معلمات 45M و110M و355M ترميز إلى ناقلات التضمين بحجم 384/768/1024، على التوالي.

نلاحظ أن عملية التحسين التي نعرضها أدناه هي عملية عامة ويمكن تطبيقها على نماذج التضمين الأخرى (بما في ذلك أجهزة التشفير الثنائية وأجهزة التشفير المتقاطعة وما شابه).

خطوة بخطوة: التحسين عن طريق التكميم

نقدم دليلاً خطوة بخطوة لتحسين أداء نماذج التضمين، مع التركيز على تقليل زمن الوصول (بحجم دفعة يبلغ 1) وزيادة الإنتاجية (يتم قياسها بالمستندات المشفرة في الثانية). تستخدم هذه الوصفة optimum-intel وIntel Neural Compressor لتحديد حجم النموذج، ويستخدم IPEX لتحسين وقت التشغيل على الأجهزة المستندة إلى Intel.

الخطوة 1: تثبيت الحزم

لتثبيت optimum-intel و intel-extension-for-transformers قم بتشغيل الأمر التالي:

pip install -U optimum[neural-compressor] intel-extension-for-transformers

الخطوة 2: التكميم الثابت بعد التدريب

يتطلب التكميم الثابت بعد التدريب مجموعة معايرة لتحديد النطاق الديناميكي للأوزان وعمليات التنشيط. تتم المعايرة عن طريق تشغيل مجموعة تمثيلية من عينات البيانات من خلال النموذج، وجمع الإحصائيات، ثم تحديد حجم النموذج بناءً على المعلومات المجمعة لتقليل فقدان الدقة.

يعرض المقتطف التالي مقتطفًا برمجيًا للتكميم:

def quantize(model_name: str, output_path: str, calibration_set: "datasets.Dataset"):
    model = AutoModel.from_pretrained(model_name)
    tokenizer = AutoTokenizer.from_pretrained(model_name)

    def preprocess_function(examples):
        return tokenizer(examples["text"], padding="max_length", max_length=512, truncation=True)

    vectorized_ds = calibration_set.map(preprocess_function, num_proc=10)
    vectorized_ds = vectorized_ds.remove_columns(["text"])

    quantizer = INCQuantizer.from_pretrained(model)
    quantization_config = PostTrainingQuantConfig(approach="static", backend="ipex", domain="nlp")
    quantizer.quantize(
        quantization_config=quantization_config,
        calibration_dataset=vectorized_ds,
        save_directory=output_path,
        batch_size=1,
    )
    tokenizer.save_pretrained(output_path)

في عملية المعايرة لدينا نستخدم مجموعة فرعية من مجموعة بيانات qasper.

الخطوة 3: تحميل وتشغيل الاستدلال

يمكن تحميل النموذج الكمي ببساطة عن طريق تشغيل:

from optimum.intel import IPEXModel

model = IPEXModel.from_pretrained("Intel/bge-small-en-v1.5-rag-int8-static")

يمكن إجراء تشفير الجمل إلى متجهات بشكل مشابه لما اعتدنا عليه في مكتبة المحولات:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Intel/bge-small-en-v1.5-rag-int8-static")
inputs = tokenizer(sentences, return_tensors="pt")

with torch.no_grad():
    outputs = model(**inputs)
    
    embeddings = outputs[0][:, 0]

نحن نقدم تفاصيل إضافية ومهمة حول كيفية تكوين إعداد الواجهة الخلفية لوحدة المعالجة المركزية (CPU) في قسم التقييم أدناه (الإعداد الصحيح للجهاز).

تقييم النموذج مع MTEB

يؤدي تحديد أوزان النماذج إلى دقة أقل إلى فقدان الدقة، حيث نفقد الدقة عند التحرك من fp32 الأوزان ل int8. ولذلك، فإننا نهدف إلى التحقق من دقة النماذج المحسنة من خلال مقارنتها بالنماذج الأصلية بمهمتين لـ MTEB:

  • استرجاع – حيث يتم ترميز المجموعة وإنشاء قوائم مرتبة من خلال البحث في الفهرس المحدد للاستعلام.
  • إعادة الترتيب – إعادة ترتيب نتائج الاسترجاع لتحسين صلتها بالموضوع عند الاستعلام.

يوضح الجدول أدناه متوسط ​​الدقة (في مجموعات بيانات متعددة) لكل نوع مهمة (MAP لإعادة الترتيب، وNDCG@10 للاسترجاع)، حيث int8 هو نموذجنا الكمي و fp32 هو النموذج الأصلي (النتائج مأخوذة من لوحة المتصدرين الرسمية لـ MTEB). تُظهر النماذج الكمية معدل خطأ أقل من 1% مقارنة بالنموذج الأصلي في مهمة إعادة الترتيب وأقل من 1.55% في مهمة الاسترجاع.

إعادة الترتيب استرجاع
BGE-صغير
قاعدة BGE
BGE-كبير
كثافة العمليات8 fp32 فرق
0.5826 0.5836 -0.17%
0.5886 0.5886 0%
0.5985 0.6003 -0.3%
كثافة العمليات8 fp32 فرق
0.5138 0.5168 -0.58%
0.5242 0.5325 -1.55%
0.5346 0.5429 -1.53%

السرعة والكمون

نقوم بمقارنة أداء نماذجنا مع طريقتين شائعتين أخريين لاستخدام النماذج:

  1. استخدام مكتبة PyTorch وHuggingface’s Transformers مع bf16.
  2. استخدام امتداد Intel لوقت تشغيل PyTorch (IPEX) مع bf16 وتتبع النموذج باستخدام torchscript.

ملاحظات الإعداد التجريبية:

  • الأجهزة (وحدة المعالجة المركزية): Intel Xeon 8480+ من الجيل الرابع مع مقبسين، 56 نواة لكل مقبس.
  • تم تقييم نموذج Pytorch بـ 56 نواة على مقبس واحد لوحدة المعالجة المركزية.
  • تم تقييم إعدادات IPEX/Optimum باستخدام ipexrun، ومقبس وحدة المعالجة المركزية (CPU) واحد، ونوى تتراوح من 22 إلى 56.
  • تم تثبيت TCMalloc وتعريفه كمتغير بيئة في كافة عمليات التشغيل.

كيف أجرينا التقييم؟

لقد أنشأنا برنامجًا نصيًا يُنتج أمثلة عشوائية باستخدام مفردات النموذج. لقد قمنا بتحميل النموذج الأصلي والنموذج المحسّن وقارنا مقدار الوقت المستغرق لتشفير تلك الأمثلة في السيناريوهين اللذين ذكرناهما أعلاه: زمن الوصول عند التشفير بحجم الدُفعة 1، والإنتاجية باستخدام ترميز الأمثلة المجمعة.

  1. خط الأساس PyTorch والوجه المعانق:
import torch
from transformers import AutoModel

model = AutoModel.from_pretrained("BAAI/bge-small-en-v1.5")

@torch.inference_mode()
def encode_text():
    outputs = model(inputs)

with torch.cpu.amp.autocast(dtype=torch.bfloat16):
    encode_text()
  1. IPEX الشعلة و bf16:
import torch
from transformers import AutoModel
import intel_extension_for_pytorch as ipex


model = AutoModel.from_pretrained("BAAI/bge-small-en-v1.5")
model = ipex.optimize(model, dtype=torch.bfloat16)

vocab_size = model.config.vocab_size
batch_size = 1
seq_length = 512
d = torch.randint(vocab_size, size=[batch_size, seq_length])
model = torch.jit.trace(model, (d,), check_trace=False, strict=False)
model = torch.jit.freeze(model)

@torch.inference_mode()
def encode_text():
    outputs = model(inputs)

with torch.cpu.amp.autocast(dtype=torch.bfloat16):
    encode_text()
  1. إنتل الأمثل مع IPEX و int8 نموذج:
import torch
from optimum.intel import IPEXModel

model = IPEXModel.from_pretrained("Intel/bge-small-en-v1.5-rag-int8-static")

@torch.inference_mode()
def encode_text():
    outputs = model(inputs)

encode_text()

أداء الكمون

في هذا التقييم، نهدف إلى قياس مدى سرعة استجابة النماذج. هذا مثال لحالة استخدام لترميز الاستعلامات في خطوط أنابيب RAG. في هذا التقييم، قمنا بتعيين حجم الدفعة على 1 ونقوم بقياس زمن الوصول لأطوال المستندات المختلفة.

يمكننا أن نرى أن النموذج الكمي يتمتع بأفضل زمن وصول بشكل عام، أقل من 10 مللي ثانية للنماذج الصغيرة والقاعدة و<20 مللي ثانية للنموذج الكبير. بالمقارنة مع النموذج الأصلي، يُظهر النموذج الكمي سرعة تصل إلى 4.5x في زمن الوصول.


الشكل 1. الكمون لنماذج BGE.

أداء الإنتاجية

في تقييم الإنتاجية لدينا، نهدف إلى البحث عن أعلى أداء للتشفير من حيث عدد المستندات في الثانية. قمنا بتعيين أطوال النص لتكون 256 رمزًا مميزًا، حيث يعد ذلك تقديرًا جيدًا لمستند متوسط ​​في مسار RAG، ويتم تقييمه بأحجام دفعات مختلفة (4، 8، 16، 32، 64، 128، 256).

تظهر النتائج أن النماذج الكمية تصل إلى قيم إنتاجية أعلى مقارنة بالنماذج الأخرى، وتصل إلى ذروة الإنتاجية عند حجم الدفعة 128. بشكل عام، بالنسبة لجميع أحجام النماذج، يُظهر النموذج الكمي تحسنًا يصل إلى 4x مقارنة بخط الأساس bf16 نموذج في أحجام دفعة مختلفة.

الإنتاجية صغيرة
الشكل 2. الإنتاجية لBGE الصغيرة.

قاعدة الإنتاجية
الشكل 3. الإنتاجية لقاعدة BGE.

الإنتاجية كبيرة
الشكل 4. الإنتاجية لBGE كبيرة.

نماذج التضمين المحسنة باستخدام fastRAG

على سبيل المثال، سنوضح كيفية دمج نماذج الاسترجاع/إعادة الترتيب المحسنة في fastRAG (والتي يمكن أيضًا دمجها بسهولة في أطر عمل RAG الأخرى مثل Langchain وLlamaIndex).

fastRAG هو إطار عمل بحثي، تم تطويره بواسطة Intel Labs، من أجل استرجاع فعال ومحسن لخطوط الأنابيب التوليدية المعززة، والتي تتضمن أحدث ما توصلت إليه ماجستير إدارة الأعمال واسترجاع المعلومات. يتوافق fastRAG تمامًا مع Haystack ويتضمن وحدات RAG جديدة وفعالة للنشر الفعال على أجهزة Intel. للبدء باستخدام fastRAG، ندعو القراء للاطلاع على تعليمات التثبيت هنا والبدء باستخدام fastRAG باستخدام دليلنا.

لقد قمنا بدمج نماذج التضمين ثنائية التشفير المحسنة في وحدتين:

  1. QuantizedBiEncoderRetriever – لفهرسة واسترجاع المستندات من فهرس كثيف
  2. QuantizedBiEncoderRanker – لإعادة ترتيب قائمة المستندات باستخدام نموذج التضمين كجزء من مسار الاسترجاع المتقن.

فهرسة سريعة باستخدام المسترد الأمثل

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

أولاً، قم بإنشاء مخزن المستندات:

from haystack.document_store import InMemoryDocumentStore

document_store = InMemoryDocumentStore(use_gpu=False, use_bm25=False, embedding_dim=384, return_embedding=True)

ثم أضف بعض المستندات إليه:

from haystack.schema import Document


examples = [
   "There is a blue house on Oxford Street.",
   "Paris is the capital of France.",
   "The first commit in fastRAG was in 2022"  
]

documents = []
for i, d in enumerate(examples):
    documents.append(Document(content=d, id=i))
document_store.write_documents(documents)

قم بتحميل المسترد باستخدام نموذج تضمين مشفر ثنائي محسّن، وقم بتشفير جميع المستندات الموجودة في مخزن المستندات:

from fastrag.retrievers import QuantizedBiEncoderRetriever

model_id = "Intel/bge-small-en-v1.5-rag-int8-static"
retriever = QuantizedBiEncoderRetriever(document_store=document_store, embedding_model=model_id)
document_store.update_embeddings(retriever=retriever)

إعادة الترتيب باستخدام الترتيب الأمثل

فيما يلي مثال لتحميل نموذج محسّن إلى عقدة ترتيب تقوم بتشفير وإعادة ترتيب جميع المستندات التي تستردها من فهرس معين عند استعلام:

from haystack import Pipeline
from fastrag.rankers import QuantizedBiEncoderRanker

ranker = QuantizedBiEncoderRanker("Intel/bge-large-en-v1.5-rag-int8-static")

p = Pipeline()
p.add_node(component=retriever, name="retriever", inputs=["Query"])
p.add_node(component=ranker, name="ranker", inputs=["retriever"])
results = p.run(query="What is the capital of France?")


print(results)

منتهي! يمكن استخدام المسار الذي تم إنشاؤه لاسترداد المستندات من مخزن المستندات وترتيب المستندات المستردة باستخدام نماذج التضمين (أخرى) لإعادة ترتيب المستندات. ويرد مثال أكثر اكتمالا في هذا دفتر الملاحظات.

لمزيد من الأساليب والنماذج والأمثلة المتعلقة بـ RAG، ندعو القراء لاستكشاف دفاتر الملاحظات fastRAG/examples.

شاركها.
اترك تعليقاً