الصورة التقطتها ريل نابستر على بيكساباي
في أحد مشاريعي الأخيرة، كان علي إنشاء نظام بحث دلالي يمكنه التوسع بأداء عالٍ وتقديم استجابات في الوقت الفعلي لعمليات البحث في التقارير. استخدمنا PostgreSQL مع pgvector على AWS RDS، مقترنًا بـ AWS Lambda، لتحقيق ذلك. كان التحدي يتمثل في السماح للمستخدمين بالبحث باستخدام استعلامات اللغة الطبيعية بدلاً من الاعتماد على الكلمات الرئيسية الصارمة، كل ذلك مع ضمان أن تكون الاستجابات أقل من ثانية أو ثانيتين أو حتى أقل ويمكنها فقط الاستفادة من موارد وحدة المعالجة المركزية.
في هذا المنشور، سأشرح الخطوات التي اتخذتها لبناء نظام البحث هذا، بدءًا من الاسترجاع وحتى إعادة الترتيب، والتحسينات التي تم إجراؤها باستخدام OpenVINO والدفعات الذكية للترميز.
تتكون أنظمة البحث الحديثة عادةً من خطوتين رئيسيتين: الاسترجاع وإعادة الترتيب.
1) الاسترجاع: تتضمن الخطوة الأولى استرجاع مجموعة فرعية من المستندات ذات الصلة بناءً على استعلام المستخدم. يمكن القيام بذلك باستخدام نماذج التضمين المدربة مسبقًا، مثل عمليات التضمين الصغيرة والكبيرة في OpenAI، أو نماذج التضمين الخاصة بـ Cohere، أو عمليات التضمين mxbai الخاصة بـ Mixbread. يركز الاسترجاع على تضييق نطاق مجموعة المستندات عن طريق قياس مدى تشابهها مع الاستعلام.
إليك مثال مبسط باستخدام مكتبة Huggingface لتحويل الجمل للاسترجاع والتي تعد إحدى مكتباتي المفضلة لهذا:
from sentence_transformers import SentenceTransformer import numpy as np # Load a pre-trained sentence transformer model model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") # Sample query and documents (vectorize the query and the documents) query = "How do I fix a broken landing gear?" documents = ["Report 1 on landing gear failure", "Report 2 on engine problems"] # Get embeddings for query and documents query_embedding = model.encode(query) document_embeddings = model.encode(documents) # Calculate cosine similarity between query and documents similarities = np.dot(document_embeddings, query_embedding) # Retrieve top-k most relevant documents top_k = np.argsort(similarities)[-5:] print("Top 5 documents:", [documents[i] for i in top_k])
2) إعادة الترتيب: بمجرد استرداد المستندات الأكثر صلة، نقوم بتحسين تصنيف هذه المستندات بشكل أكبر باستخدام نموذج التشفير المتقاطع. تعمل هذه الخطوة على إعادة تقييم كل مستند فيما يتعلق بالاستعلام بشكل أكثر دقة، مع التركيز على الفهم السياقي الأعمق.
تعد إعادة الترتيب مفيدة لأنها تضيف طبقة إضافية من التحسين من خلال تسجيل مدى ملاءمة كل مستند بشكل أكثر دقة.
إليك مثال رمزي لإعادة الترتيب باستخدام برنامج التشفير المتقاطع/ms-marco-TinyBERT-L-2-v2، وهو برنامج تشفير متقاطع خفيف الوزن:
from sentence_transformers import CrossEncoder # Load the cross-encoder model cross_encoder = CrossEncoder("cross-encoder/ms-marco-TinyBERT-L-2-v2") # Use the cross-encoder to rerank top-k retrieved documents query_document_pairs = [(query, doc) for doc in documents] scores = cross_encoder.predict(query_document_pairs) # Rank documents based on the new scores top_k_reranked = np.argsort(scores)[-5:] print("Top 5 reranked documents:", [documents[i] for i in top_k_reranked])
أثناء التطوير، وجدت أن مراحل الترميز والتنبؤ كانت تستغرق وقتًا طويلاً عند التعامل مع 1000 تقرير بالإعدادات الافتراضية لمحولات الجملة. وقد أدى ذلك إلى خلق اختناق في الأداء، خاصة وأننا كنا نهدف إلى الحصول على استجابات في الوقت الفعلي.
أدناه قمت بتوصيف الكود الخاص بي باستخدام SnakeViz لتصور العروض:
كما ترون، فإن خطوات الترميز والتنبؤ بطيئة بشكل غير متناسب، مما يؤدي إلى تأخيرات كبيرة في عرض نتائج البحث. بشكل عام، استغرق الأمر حوالي 4-5 ثوانٍ في المتوسط. ويرجع ذلك إلى حقيقة وجود عمليات حظر بين خطوات الترميز والتنبؤ. إذا أضفنا أيضًا عمليات أخرى مثل استدعاء قاعدة البيانات والتصفية وما إلى ذلك، فسننتهي بسهولة بإجمالي 8-9 ثوانٍ.
السؤال الذي واجهته كان: هل يمكننا جعل الأمر أسرع؟ الإجابة هي نعم، من خلال الاستفادة من OpenVINO، وهي واجهة خلفية محسنة لاستدلال وحدة المعالجة المركزية. يساعد OpenVINO في تسريع استنتاج نماذج التعلم العميق على أجهزة Intel، والتي نستخدمها في AWS Lambda.
مثال التعليمات البرمجية لتحسين OpenVINO
إليك كيفية دمج OpenVINO في نظام البحث لتسريع الاستدلال:
import argparse import numpy as np import pandas as pd from typing import Any from openvino.runtime import Core from transformers import AutoTokenizer def load_openvino_model(model_path: str) -> Core: core = Core() model = core.read_model(model_path ".xml") compiled_model = core.compile_model(model, "CPU") return compiled_model def rerank( compiled_model: Core, query: str, results: list[str], tokenizer: AutoTokenizer, batch_size: int, ) -> np.ndarray[np.float32, Any]: max_length = 512 all_logits = [] # Split results into batches for i in range(0, len(results), batch_size): batch_results = results[i : i batch_size] inputs = tokenizer( [(query, item) for item in batch_results], padding=True, truncation="longest_first", max_length=max_length, return_tensors="np", ) # Extract input tensors (convert to NumPy arrays) input_ids = inputs["input_ids"].astype(np.int32) attention_mask = inputs["attention_mask"].astype(np.int32) token_type_ids = inputs.get("token_type_ids", np.zeros_like(input_ids)).astype( np.int32 ) infer_request = compiled_model.create_infer_request() output = infer_request.infer( { "input_ids": input_ids, "attention_mask": attention_mask, "token_type_ids": token_type_ids, } ) logits = output["logits"] all_logits.append(logits) all_logits = np.concatenate(all_logits, axis=0) return all_logits def fetch_search_data(search_text: str) -> pd.DataFrame: # Usually you would fetch the data from a database df = pd.read_csv("cnbc_headlines.csv") df = df[~df["Headlines"].isnull()] texts = df["Headlines"].tolist() # Load the model and rerank openvino_model = load_openvino_model("cross-encoder-openvino-model/model") tokenizer = AutoTokenizer.from_pretrained("cross-encoder/ms-marco-TinyBERT-L-2-v2") rerank_scores = rerank(openvino_model, search_text, texts, tokenizer, batch_size=16) # Add the rerank scores to the DataFrame and sort by the new scores df["rerank_score"] = rerank_scores df = df.sort_values(by="rerank_score", ascending=False) return df if __name__ == "__main__": parser = argparse.ArgumentParser( description="Fetch search results with reranking using OpenVINO" ) parser.add_argument( "--search_text", type=str, required=True, help="The search text to use for reranking", ) args = parser.parse_args() df = fetch_search_data(args.search_text) print(df)
مع هذا الأسلوب يمكننا الحصول على تسريع بمقدار 2-3x مما يقلل الوقت الأصلي من 4-5 ثوانٍ إلى 1-2 ثانية. كود العمل الكامل موجود على جيثب.
كان هناك عامل حاسم آخر في تحسين الأداء وهو تحسين عملية الترميز وضبط حجم الدفعة و طول الرمز المميز . من خلال زيادة حجم الدفعة (batch_size=16) وتقليل طول الرمز المميز (max_length=512)، يمكننا موازنة عملية الترميز وتقليل الحمل الزائد للعمليات المتكررة. في تجاربنا، وجدنا أن حجم الدُفعة الذي يتراوح بين 16 و64 يعمل بشكل جيد، مع أي شيء أكبر يؤدي إلى انخفاض الأداء. وبالمثل، فقد استقرنا على الحد الأقصى للطول وهو 128، وهو أمر قابل للتطبيق إذا كان متوسط طول تقاريرك قصيرًا نسبيًا. بفضل هذه التغييرات، حققنا سرعة إجمالية قدرها 8x، مما أدى إلى تقليل وقت إعادة الترتيب إلى أقل من ثانية واحدة، حتى على وحدة المعالجة المركزية.
من الناحية العملية، كان هذا يعني تجربة أحجام دفعات مختلفة وأطوال الرموز المميزة للعثور على التوازن الصحيح بين السرعة والدقة لبياناتك. ومن خلال القيام بذلك، شهدنا تحسينات كبيرة في أوقات الاستجابة، مما جعل نظام البحث قابلاً للتوسع حتى مع 1000 تقرير.
من خلال استخدام OpenVINO وتحسين الترميز والتجميع، تمكنا من إنشاء نظام بحث دلالي عالي الأداء يلبي متطلبات الوقت الفعلي على إعداد وحدة المعالجة المركزية فقط. في الواقع، لقد شهدنا تسريعًا إجماليًا بمقدار 8x. يؤدي الجمع بين الاسترجاع باستخدام محولات الجملة وإعادة الترتيب باستخدام نموذج التشفير المتقاطع إلى إنشاء تجربة بحث قوية وسهلة الاستخدام.
إذا كنت تقوم ببناء أنظمة مماثلة مع قيود على وقت الاستجابة والموارد الحسابية، فإنني أوصي بشدة باستكشاف OpenVINO والدفعات الذكية لإطلاق العنان لأداء أفضل.
نأمل أن تكون قد استمتعت بهذا المقال. إذا وجدت هذه المقالة مفيدة، أعطني إعجابًا حتى يتمكن الآخرون من العثور عليها أيضًا، وشاركها مع أصدقائك. تابعني على Linkedin لتبقى على اطلاع بأحدث أعمالي. شكرا على القراءة!
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3