استراتيجيات التقطيع المتقدمة
مقارنة طرق التقطيع
التقطيع يحدد كيفية تقسيم المستندات إلى وحدات قابلة للاسترجاع. الاستراتيجية الصحيحة تؤثر بشكل كبير على جودة الاسترجاع.
لماذا التقطيع مهم
التقطيع السيء يؤدي إلى:
- سياق منقسم: معلومات ذات صلة في قطع مختلفة
- ضوضاء: محتوى غير ذي صلة مختلط مع ذي صلة
- معنى ضائع: مفاهيم رئيسية مفككة
نظرة عامة على طرق التقطيع
| الطريقة | كيف تعمل | الأفضل لـ |
|---|---|---|
| حجم ثابت | التقسيم بعدد الأحرف/الرموز | مستندات بسيطة |
| تكراري | التقسيم بالفواصل بشكل هرمي | نص مهيكل |
| جملة | التقسيم على حدود الجمل | محتوى سردي |
| دلالي | التقسيم بتغيير الموضوع/المعنى | مستندات معقدة |
التقطيع بحجم ثابت
أبسط نهج—التقسيم بعدد الأحرف:
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)
كيف يعمل:
- حاول التقسيم على
\n\n(فقرات) - إذا كانت القطع لا تزال كبيرة، جرب
\n - استمر نزولاً في قائمة الفواصل
- يضمن نقاط فصل طبيعية
التقطيع القائم على الجملة
يحافظ على جمل كاملة:
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)
كيف يعمل:
- يضمّن الجمل المتتالية
- يحسب التشابه بين الجمل المتجاورة
- يقسم حيث ينخفض التشابه بشكل كبير
# تقطيع دلالي مخصص
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)
رؤية رئيسية: أفضل طريقة تقطيع تعتمد على نوع محتواك. ابدأ بالتكراري للاستخدام العام، ثم جرب التقطيع الدلالي للمستندات المعقدة حيث الجودة أهم.
التالي، لنستكشف التقطيع الهرمي والسياقي لسيناريوهات الاسترجاع المتقدمة. :::