الضبط الدقيق (Fine-tuning) مقابل RAG: متى نستخدم أياً منهما
الضبط الدقيق (Fine-tuning) مقابل RAG: متى نستخدم أياً منهما
غالباً ما يكون الجدل حول الضبط الدقيق (Fine-tuning) مقابل التوليد المعزز بالاسترجاع (RAG) ملوثاً بتحيزات موفري الخدمات والمفاهيم الخاطئة حول المقايضات. إذا كنت تبني تطبيقات نماذج لغة كبيرة (LLM) في بيئة الإنتاج، فلا يمكنك تحمل اتخاذ هذا القرار المعماري بشكل خاطئ. اختيار المسار الخاطئ يعني إهدار دورات معالجة GPU، وبيانات قديمة، وأنظمة معرضة للهلوسة (hallucinations).
يزيل هذا المنشور التشويش النظري. سنقوم بفحص مساحة المشكلة الدقيقة، والنظر في معمارية كل من التوليد المعزز بالاسترجاع (RAG) وضبط النموذج الدقيق، ونمنحك إطاراً واضحاً لتحديد متى تستخدم كل منهما.
المشكلة
تكون نماذج اللغة الكبيرة (LLMs) مجمدة في الزمن. فالنموذج الذي تم تدريبه في عام 2024 لا يعرف شيئاً عن الكود البرمجي الخاص بفريقك والذي تمت كتابته بالأمس، ولا يفهم تحديثات مخطط API في الوقت الفعلي التي تم إطلاقها قبل ساعة. عندما تطلب من نموذج أساسي التفكير في مستندات داخلية أو بيانات مؤسسية خاصة، فإنه إما سيهلوس بثقة أو يرفض الإجابة.
لديك بيانات خاصة بمجالك. وتحتاج إلى نموذج LLM لاستخدامها. هذه هي المشكلة الأساسية.
لماذا يعد هذا صعباً
إن سد الفجوة بين أوزان LLM الثابتة والبيانات الخاصة الديناميكية يطرح تحديات هندسية صعبة. لا يمكنك ببساطة وضع ملف PDF بحجم 50 ميجابايت في نافذة سياق الأوامر (prompt context window) - فحتى مع النماذج التي تدعم نوافذ سياق تتجاوز مليون رمز (token)، تعاني السياقات الطويلة من ظاهرة "الضياع في المنتصف" (lost in the middle)، وزمن انتقال هائل، وتكاليف رموز باهظة.
وإذا قررت تحديث أوزان النموذج، فإنك تدخل في عالم فوضوي من التدريب الموزع، وتنظيم مجموعات البيانات، والنسيان الكارثي (catastrophic forgetting). تنقطع أنابيب التدريب، وتتطلب تنسيقات البيانات معايير صارمة، كما أن تقييم ما إذا كان النموذج الذي تم ضبطه دقيقاً قد تحسن بالفعل يتطلب أطر تقييم يدوية ومكلفة.
البنية المعمارية: RAG مقابل الضبط الدقيق (Fine-Tuning)
التوليد المعزز بالاسترجاع (RAG)
يترك RAG أوزان نموذج اللغة الكبيرة دون تغيير. وبدلاً من ذلك، فإنه يغير الأوامر ديناميكياً في وقت الاستدلال. حيث تقوم بتحويل بياناتك الخاصة إلى متجهات عالية الأبعاد، وتخزينها في قاعدة بيانات متجهة، وإجراء عمليات بحث عن التشابه الدلالي عند ورود استعلام. ثم يتم حقن المستندات المسترجعة في سياق الأمر.
RAG هي مشكلة استرجاع بيانات تتنكر في زي مشكلة ذكاء اصطناعي. وتبدو البنية المعمارية عادةً كالتالي:
- أنبوب إدخال البيانات (استخراج النص من ملفات PDF، Confluence، إلخ)
- استراتيجية التقسيم (تقسيم النص إلى كتل من 512-1024 رمز)
- نموذج التضمين (مثل
text-embedding-3-small) - مخزن المتجهات (مثل Pinecone, Qdrant, pgvector)
- نموذج التوليد (مثل GPT-4, Claude 3 Opus)
الضبط الدقيق (Fine-Tuning)
يغير الضبط الدقيق أوزان الشبكة العصبية بشكل دائم. حيث تأخذ نموذجاً مدرباً مسبقاً (مثل Llama-3-8B) وتقوم بتدريبه بشكل أكبر على مجموعة بيانات منسقة من أزواج الأوامر والإكمال. يؤدي هذا إلى دمج المعرفة أو السلوك مباشرة في معلمات النموذج.
بنية الضبط الدقيق (تحديداً LoRA/QLoRA):
- إعداد مجموعة البيانات (تنسيق JSONL من التعليمات والاستجابات)
- اختيار النموذج الأساسي
- محولات الضبط الدقيق الفعال للمعلمات (PEFT adapters)
- عنقود التدريب الموزع
- حلقات التقييم
- خادم الاستدلال مع المحولات المدمجة
الضبط الدقيق مقابل RAG: تفصيل عملي
استخدم RAG عندما تحتاج إلى أن يعرف النموذج حقائق. استخدم الضبط الدقيق عندما تحتاج إلى أن يتعلم النموذج تنسيقاً، أو نبرة صوت، أو سلوكاً.
لا تقم بضبط النموذج دقيقاً لحفظ سياسة الموارد البشرية لشركتك. سوف ينسى التفاصيل، ويهلوس بالأرقام، ويتطلب إعادة تدريب كاملة عند تغيير السياسة. استخدم RAG لهذا الغرض.
لا تستخدم RAG لتعليم النموذج كيفية إخراج مخططات JSON معقدة ومحددة للغاية أو كتابة كود بلغة DSL داخلية مخصصة بعمق. ستفيض نافذة السياق بأمثلة التوليد القليلة (few-shot examples)، وسيؤدي زمن انتقال إلى إبطاء تطبيقك. استخدم الضبط الدقيق لهذا الغرض.
التطبيق
بناء أنبوب عمل RAG
بالنسبة لـ RAG، سنستخدم Python 3.11، و LlamaIndex 0.10.x، و Qdrant.
# requirements.txt
# llama-index==0.10.15
# llama-index-vector-stores-qdrant==0.1.2
# qdrant-client==1.7.3
import qdrant_client
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.vector_stores.qdrant import QdrantVectorStore
# 1. Initialize Vector Store
client = qdrant_client.QdrantClient(location=":memory:")
vector_store = QdrantVectorStore(client=client, collection_name="internal_docs")
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 2. Load Documents
documents = SimpleDirectoryReader("./data").load_data()
# 3. Build Index
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context,
)
# 4. Query
query_engine = index.as_query_engine()
response = query_engine.query("What is the new API rate limit?")
print(response)
بناء أنبوب عمل الضبط الدقيق
بالنسبة للضبط الدقيق، نستخدم Unsloth 2024.4 لتدريب أسرع بمرتين واستخدام أقل لذاكرة VRAM.
# requirements.txt
# unsloth[cu121-ampere] @ git+https://github.com/unslothai/unsloth.git
# trl==0.8.1
from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import load_dataset
# 1. Load Base Model and LoRA Adapters
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/llama-3-8b-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
)
model = FastLanguageModel.get_peft_model(
model,
r = 16,
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"],
lora_alpha = 16,
lora_dropout = 0,
bias = "none",
use_gradient_checkpointing = "unsloth",
)
# 2. Prepare Dataset
dataset = load_dataset("json", data_files="internal_dsl_dataset.jsonl", split="train")
def format_prompts(examples):
instructions = examples["instruction"]
outputs = examples["output"]
texts = []
for instruction, output in zip(instructions, outputs):
text = f"Instruction: {instruction}\nOutput: {output}<|end_of_text|>"
texts.append(text)
return { "text" : texts }
dataset = dataset.map(format_prompts, batched = True)
# 3. Train
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = 2048,
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
max_steps = 60,
learning_rate = 2e-4,
fp16 = not torch.cuda.is_bf16_supported(),
bf16 = torch.cuda.is_bf16_supported(),
logging_steps = 1,
output_dir = "outputs",
),
)
trainer.train()
فخاخ يجب الحذر منها
فخاخ RAG
- التقسيم الأعمى (Blind Chunking): يؤدي قطع النص بشكل عشوائي كل 512 رمزاً إلى تقسيم الجمل في المنتصف وتدمير المعنى الدلالي. يجب استخدام التقسيم الدلالي (semantic chunking) أو تقسيم الأحرف المتكرر.
- تجاهل البيانات الوصفية (Metadata): البحث عن تشابه المتجهات غبي بطبيعته. إذا بحثت عن "أرباح الربع الثالث"، فقد يسترجع التضمين مستنداً من عام 2021 لأنه يبدو مشابهاً رياضياً. قم دائماً بالتصفية حسب البيانات الوصفية (التاريخ، الكاتب، الفئة) قبل إجراء البحث المتجه.
- الضياع في المنتصف (Lost in the Middle): يؤدي إدخال 20 مستنداً مسترجعاً في نافذة السياق غالباً إلى تجاهل نموذج اللغة للمستندات الموجودة في المنتصف. أعد ترتيب المستندات المسترجعة (باستخدام أدوات مثل Cohere Rerank) لوضع العناصر الأكثر صلة في البداية والنهاية تماماً من نافذة السياق.
فخاخ الضبط الدقيق
- المدخلات السيئة تؤدي لمخرجات سيئة (Garbage In, Garbage Out): يضخم الضبط الدقيق جودة مجموعة البيانات الخاصة بك. إذا كانت بيانات التدريب تحتوي على أخطاء في التنسيق أو هلوسة أو تناقضات، فإن نموذجك المضبط سيعيد إنتاج هذه الأخطاء بدقة.
- الفرط في التدريب (Overfitting): التدريب لعدد كبير جداً من الدورات (epochs) على مجموعة بيانات صغيرة سيدمر قدرات التفكير العامة للنموذج. سوف يتعلم تلاوة بيانات التدريب حرفياً ولكنه سيفشل بشكل كبير عند التعامل مع مدخلات جديدة.
- النسيان الكارثي (Catastrophic Forgetting): عندما يتعلم النموذج مهمتك المحددة، فإنه سينسى حتماً معلومات أخرى. فالنموذج الذي تم ضبطه بدقة لكتابة استعلامات SQL قد يصبح فجأة سيئاً للغاية في كتابة كود Python أو تلخيص النصوص.
الخلاصة
توقف عن معاملة الضبط الدقيق مقابل RAG كخيار ثنائي (إما هذا أو ذاك). إنهما يحلان مشكلتين مختلفتين.
يوفر RAG سياقاً خارجياً. بينما يوفر الضبط الدقيق سلوكاً داخلياً للنموذج.
إذا كان تطبيقك يتطلب التفكير في قواعد معرفية خاصة وديناميكية، فقم ببناء أنبوب عمل RAG قوي. وإذا كان تطبيقك يتطلب من النموذج إخراج هياكل معقدة باستمرار، أو التحدث بنبرة علامة تجارية محددة للغاية، أو العمل بكفاءة على نماذج أصغر وأرخص مفتوحة المصدر، فيجب عليك استخدام الضبط الدقيق.
تستخدم البنيات المعمارية الأكثر تقدماً في المؤسسات كلا النهجين. حيث يقومون بضبط نموذج صغير ورخيص (مثل Llama 3 8B) لفهم مخطط مخرجات JSON ونوايا المستخدم تماماً، ثم يربطون هذا النموذج المضبط بأنبوب RAG ضخم لاسترداد الحقائق الفعلية المطلوبة لملء هذا المخطط.
اتخذ خيارك المعماري بناءً على سرعة تغير بياناتك ومتطلبات المخرجات، وليس بناءً على أحدث الصيحات على وسائل التواصل الاجتماعي. ابنِ بقوة، وقيم بلا هوادة، وافهم الآليات الأساسية لأدواتك.
خدمة سفن لابس
تطوير وكلاء الذكاء الاصطناعي ومسارات RAG
