الدرس 9 من 23

استراتيجيات التقطيع المتقدمة

مقارنة طرق التقطيع

4 دقيقة للقراءة

التقطيع يحدد كيفية تقسيم المستندات إلى وحدات قابلة للاسترجاع. الاستراتيجية الصحيحة تؤثر بشكل كبير على جودة الاسترجاع.

لماذا التقطيع مهم

التقطيع السيء يؤدي إلى:

  • سياق منقسم: معلومات ذات صلة في قطع مختلفة
  • ضوضاء: محتوى غير ذي صلة مختلط مع ذي صلة
  • معنى ضائع: مفاهيم رئيسية مفككة

نظرة عامة على طرق التقطيع

الطريقة كيف تعمل الأفضل لـ
حجم ثابت التقسيم بعدد الأحرف/الرموز مستندات بسيطة
تكراري التقسيم بالفواصل بشكل هرمي نص مهيكل
جملة التقسيم على حدود الجمل محتوى سردي
دلالي التقسيم بتغيير الموضوع/المعنى مستندات معقدة

التقطيع بحجم ثابت

أبسط نهج—التقسيم بعدد الأحرف:

from langchain.text_splitter import CharacterTextSplitter

splitter = CharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separator=""  # تقسيم على مستوى الأحرف
)

chunks = splitter.split_text(document)

الإيجابيات: بسيط، أحجام قطع متوقعة السلبيات: يقسم في منتصف الجملة، يتجاهل الهيكل

التقسيم التكراري بالأحرف

الأكثر استخداماً—يحترم هيكل المستند:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separators=[
        "\n\n",    # الفقرات أولاً
        "\n",      # ثم الأسطر
        ". ",      # ثم الجمل
        ", ",      # ثم العبارات
        " ",       # ثم الكلمات
        ""         # أخيراً الأحرف
    ]
)

chunks = splitter.split_documents(documents)

كيف يعمل:

  1. حاول التقسيم على \n\n (فقرات)
  2. إذا كانت القطع لا تزال كبيرة، جرب \n
  3. استمر نزولاً في قائمة الفواصل
  4. يضمن نقاط فصل طبيعية

التقطيع القائم على الجملة

يحافظ على جمل كاملة:

from langchain.text_splitter import SentenceTransformersTokenTextSplitter

# باستخدام sentence-transformers tokenizer
splitter = SentenceTransformersTokenTextSplitter(
    chunk_overlap=50,
    tokens_per_chunk=256
)

# أو مع NLTK
import nltk
nltk.download('punkt')

from langchain.text_splitter import NLTKTextSplitter

splitter = NLTKTextSplitter(
    chunk_size=512,
    chunk_overlap=50
)

chunks = splitter.split_text(document)

الأفضل لـ: محتوى طويل، مقالات، توثيق

التقطيع الدلالي

يقسم بناءً على تغييرات المعنى باستخدام التضمينات:

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()

# التقسيم عندما ينخفض تشابه التضمين
splitter = SemanticChunker(
    embeddings=embeddings,
    breakpoint_threshold_type="percentile",
    breakpoint_threshold_amount=95  # التقسيم عند المئين 95 لعدم التشابه
)

chunks = splitter.split_text(document)

كيف يعمل:

  1. يضمّن الجمل المتتالية
  2. يحسب التشابه بين الجمل المتجاورة
  3. يقسم حيث ينخفض التشابه بشكل كبير
# تقطيع دلالي مخصص
def semantic_chunk(text: str, threshold: float = 0.7):
    sentences = split_into_sentences(text)
    embeddings = embed_model.encode(sentences)

    chunks = []
    current_chunk = [sentences[0]]

    for i in range(1, len(sentences)):
        similarity = cosine_similarity(embeddings[i-1], embeddings[i])

        if similarity < threshold:
            # اكتُشف تغيير الموضوع - ابدأ قطعة جديدة
            chunks.append(" ".join(current_chunk))
            current_chunk = [sentences[i]]
        else:
            current_chunk.append(sentences[i])

    chunks.append(" ".join(current_chunk))
    return chunks

مقارنة الطرق

الطريقة جودة الاسترجاع سرعة المعالجة الاتساق
حجم ثابت منخفضة سريعة عالي
تكراري متوسطة-عالية سريعة عالي
جملة متوسطة متوسطة عالي
دلالي عالية بطيئة متغير

المعايير

الدراسات تظهر أن استراتيجية التقطيع تؤثر بشكل كبير على الاسترجاع:

الاستراتيجية Recall@10 Precision@10
ثابت 256 0.65 0.42
تكراري 512 0.78 0.58
جملة 0.75 0.55
دلالي 0.82 0.64

النتائج تختلف حسب مجموعة البيانات. اختبر دائماً على بياناتك المحددة.

اختيار طريقة

البداية
مستندات بسيطة، موحدة؟
  ├─ نعم → حجم ثابت (سريع، متوقع)
نص مهيكل (عناوين، فقرات)؟
  ├─ نعم → تكراري (يحترم الهيكل)
محتوى سردي طويل؟
  ├─ نعم → قائم على الجملة
مواضيع معقدة، أقسام متغيرة الطول؟
  ├─ نعم → دلالي (أفضل جودة، أبطأ)
الافتراضي → تكراري (أفضل توازن)

نصائح التنفيذ

class AdaptiveChunker:
    """اختيار طريقة التقطيع بناءً على نوع المستند."""

    def __init__(self):
        self.recursive = RecursiveCharacterTextSplitter(
            chunk_size=512, chunk_overlap=50
        )
        self.semantic = SemanticChunker(
            embeddings=OpenAIEmbeddings(),
            breakpoint_threshold_type="percentile"
        )

    def chunk(self, document: str, doc_type: str) -> list[str]:
        if doc_type in ["faq", "qa"]:
            # أزواج الأسئلة والأجوبة يجب أن تبقى معاً
            return self._chunk_qa(document)
        elif doc_type in ["technical", "research"]:
            # المستندات التقنية تستفيد من التقطيع الدلالي
            return self.semantic.split_text(document)
        else:
            # الافتراضي للتكراري لمعظم المحتوى
            return self.recursive.split_text(document)

    def _chunk_qa(self, document: str) -> list[str]:
        # حافظ على أزواج الأسئلة والأجوبة معاً
        qa_pattern = r'Q:.*?A:.*?(?=Q:|$)'
        return re.findall(qa_pattern, document, re.DOTALL)

رؤية رئيسية: أفضل طريقة تقطيع تعتمد على نوع محتواك. ابدأ بالتكراري للاستخدام العام، ثم جرب التقطيع الدلالي للمستندات المعقدة حيث الجودة أهم.

التالي، لنستكشف التقطيع الهرمي والسياقي لسيناريوهات الاسترجاع المتقدمة. :::

اختبار

الوحدة 3: استراتيجيات التقطيع المتقدمة

خذ الاختبار