Byte Pair Encoding (BPE) هو خوارزمية لتقسيم النص إلى وحدات أصغر تُسمى «رموزًا» (tokens)، تعمل بدمج أكثر زوج من الرموز المتجاورة تكرارًا في النص تدريجيًا حتى الوصول إلى حجم مفردات محدد مسبقًا. يُستخدم على نطاق واسع في نماذج اللغة لأنه يوازن بين تقسيم الكلمات الكاملة (مفردات ضخمة) وتقسيم الحروف (تسلسلات طويلة مكلفة)، فيمنح النموذج مفردات محدودة قادرة على تمثيل أي نص تقريبًا. النتيجة العملية: نص شائع يُغطّى برموز قليلة، ونص نادر أو بلغة مختلفة يُقسَّم إلى رموز أكثر، وهذا ينعكس مباشرة على التكلفة.
ما هو Byte Pair Encoding ولماذا نحتاجه؟
تخيّل أنك تريد تحويل أي نص إلى أرقام يفهمها النموذج. أبسط طريقة هي مفردات من الكلمات الكاملة: كل كلمة رقم واحد. المشكلة أن عدد الكلمات في أي لغة كبير جدًا، والكلمات الجديدة والأخطاء الإملائية وأسماء الأعلام لن تجد لها رقمًا، فتحتاج رمزًا احتياطيًا «غير معروف» يفقد المعلومة. الطرف الآخر هو التقسيم إلى حروف أو بايتات: مفردات صغيرة جدًا لكن التسلسلات تصبح طويلة، والنموذج يستهلك خطوات حساب أكثر لكل جملة.
يأتي BPE كحل وسط. يبدأ من وحدات صغيرة (غالبًا بايتات، أي 256 رمزًا أساسيًا)، ثم يبحث عن الزوج المتكرر ويستبدله برمز جديد واحد. بهذه الطريقة تُبنى المفردات من الأجزاء الأكثر شيوعًا فعليًا في بيانات التدريب: مقاطع مثل «ال»، أو لواحق مثل «ing»، أو أجزاء كلمات شائعة. أي نص جديد يمكن تمثيله دائمًا لأنه في أسوأ الحالات يعود إلى البايتات الأساسية، فلا وجود لمشكلة «رمز غير معروف».
نحتاج BPE لثلاثة أسباب مترابطة. أولًا، ضبط حجم المفردات: نختار عدد الرموز مسبقًا (مثلًا 32 ألفًا أو 100 ألف) ونوقف الدمج عنده. ثانيًا، كفاءة التمثيل: النصوص الشائعة تُغطّى برموز أقل، فيقلّ طول التسلسل الذي يعالجه النموذج. ثالثًا، المرونة عبر اللغات والرموز والمسافات والأرقام، إذ يستطيع التمثيل تغطية كل شيء دون قائمة كلمات مغلقة.
كيف يعمل BPE خطوة بخطوة: مثال رقمي كامل
لنبسّط: لنفترض أن «المفردات الأولية» حروف فقط، وأن بيانات التدريب هي الكلمة المتكررة «اباب» ثلاث مرات، مكتوبة كسلسلة واحدة مع فاصل لتمييز حدود الكلمات:
اباب اباب اباب
الخطوة صفر: كل حرف رمز مستقل، والمفردات الأولية هي {ا, ب}. نحسب تكرارات الأزواج المتجاورة داخل كل كلمة:
(ا,ب) يظهر مرتين في كل كلمة × 3 كلمات = 6
(ب,ا) يظهر مرة واحدة في كل كلمة × 3 كلمات = 3
الخطوة الأولى: الزوج الأكثر تكرارًا هو (ا,ب) بـ 6 مرات، فنضيف رمزًا جديدًا نسمّيه «AB» وندمجه في كل موضع. تصبح الكلمة «AB AB» في كل مرة (لاحظ أن الدمج لا يعبر حدود الكلمات). المفردات الآن {ا, ب, AB}. نعيد الحساب على التسلسل الجديد:
(AB,AB) يظهر مرة واحدة في كل كلمة × 3 = 3
الخطوة الثانية: ندمج (AB,AB) في رمز جديد «ABAB» بـ 3 مرات. تصبح كل كلمة رمزًا واحدًا، ولا يتبقى أي زوج متجاور قابل للدمج. توقفنا. المفردات النهائية {ا, ب, AB, ABAB} وحجمها 4.
الآن قِس الأثر. النص الأصلي «اباب اباب اباب» طوله 11 حرفًا (مع المسافتين). بعد التقسيم النهائي يصبح:
ABAB ABAB ABAB = 3 رموز (بإضافة رمز المسافة أو بدونه حسب المعالج)
أي أن نفس النص انتقل من 11 وحدة إلى 3 رموز. هذه هي الفكرة كاملة: الدمج المتكرر يحوّل التسلسلات الشائعة إلى وحدات أكبر. في الواقع الحقيقي تبدأ المفردات غالبًا بـ 256 بايتًا وتُجرى آلاف عمليات الدمج، وكل عملية تختار الزوج الأعلى تكرارًا في كامل corpus التدريب، لا في مثال صغير كما هنا.
النقطة الدقيقة التي يغفلها كثيرون: BPE «خوارزمية تدريب» تُنتج قائمة دمج مرتّبة، ثم «خوارزمية تقسيم» تطبّق هذه القائمة على أي نص جديد بترتيبها. عند التقسيم لا نعيد حساب التكرارات؛ نطبّق الدمج الأول، ثم الثاني، وهكذا. لذلك قد يُقسَّم النص الجديد بشكل مختلف عن بيانات التدريب، وهذا طبيعي ومقصود.
تأثير حجم المفردات على عدد الرموز وتكلفة الاستدعاء
حجم المفردات (Vocabulary Size) هو عدد الرموز الفريدة التي يعرفها النموذج. كلما زاد، أصبح كل رمز قادرًا على تمثيل مقطع أطول من النص، فيقل عدد الرموز اللازمة لتمثيل الجملة نفسها. لكن العلاقة ليست خطية، ولا تخلو من مقايضات.
مثال رقمي مبني على افتراضات صريحة: لنفترض جملة عربية طولها 40 حرفًا (بما فيها المسافات)، ولنفترض — على سبيل التبسيط — أن متوسط طول الرمز (عدد الحروف التي يمثلها الرمز الواحد) يتغير مع حجم المفردات كالتالي:
حجم المفردات | متوسط طول الرمز (حروف) | عدد الرموز ≈ 40 ÷ الطول
32K | 1.8 | 22.2 ≈ 23
64K | 2.4 | 16.7 ≈ 17
128K | 3.0 | 13.3 ≈ 14
الآن لنفترض سعر استدعاء افتراضي قدره 0.50 وحدة نقدية لكل مليون رمز إدخال (رقم افتراضي للتوضيح فقط، وليس سعرًا حقيقيًا لأي مزوّد):
التكلفة = عدد الرموز × (0.50 ÷ 1,000,000)
23 رمزًا → 0.0000115 وحدة
17 رمزًا → 0.0000085 وحدة
14 رمزًا → 0.0000070 وحدة
الفارق بين 32K و128K يبدو ضئيلًا في طلب واحد، لكنه يتضاعف مع ملايين الطلبات. ومع ذلك، المفردات الأكبر تعني جدول تضمين (Embedding Table) أكبر: مصفوفة تحوّل كل رمز إلى متجه رقمي، فتزيد ذاكرة النموذج وقد تبطئ التدريب.
ملاحظة مهمة: الحساب أعلاه تقديري؛ متوسط طول الرمز يعتمد على اللغة والمحتوى وتوزيع المفردات، وليس على حجمها وحده.
أخطاء شائعة وتجربة عملية باستخدام حاسبة الرموز
أخطاء شائعة:
- افتراض أن "الكلمة = رمز واحد"؛ في الواقع الكلمات الطويلة والنادرة تُقسَّم إلى عدة رموز.
- مقارنة عدد الرموز بين لغتين دون مراعاة أن النصوص غير متكافئة دلاليًا.
- الظن أن مفردات أكبر تعني دائمًا تكلفة أقل: قد يزيد طول الرمز قليلًا بينما ترتفع تكلفة الذاكرة والبنية التحتية.
- نسيان احتساب الرموز الخاصة (مثل رموز بداية ونهاية النص) ضمن الإجمالي.
- الاعتماد على تقدير "حرف ≈ رمز" بدل القياس الفعلي.
قائمة تحقق قبل إرسال أي طلب إلى الـ API:
[ ] قِس عدد الرموز فعليًا بحاسبة الرموز الخاصة بالنموذج المستخدم
[ ] اختبر نصًا عربيًا ونصًا مختلطًا (عربي + إنجليزي + رموز)
[ ] تحقق من حد السياق (Context Window) قبل الإرسال
[ ] احسب التكلفة = (رموز الإدخال + رموز الإخراج المتوقعة) × سعر الرمز
[ ] قارن بين نموذجين مختلفي المفردات على نفس النص
فكرة عملية قصيرة (بseudocode):
tokens = tokenizer.encode(text)
cost = (len(tokens.in) + expected_out) * price_per_token
if len(tokens.in) > context_limit: split_or_summarize(text)
التجربة العملية: خذ فقرة عربية واحدة (نحو 200 كلمة)، مرّرها على حاسبة الرموز لنموذجين أو ثلاثة بمفردات مختلفة، وسجّل عدد الرموز لكل نموذج ثم احسب التكلفة التقديرية بالمعادلة أعلاه. ستلاحظ أن الفروق بين النماذج أكبر في النصوص العربية والمصطلحات التقنية منها في النصوص الإنجليزية الشائعة، لأن تمثيل العربية غالبًا يستهلك رموزًا أكثر.
الأسئلة الشائعة
ما الفرق بين BPE والتقسيم بالكلمات الكاملة؟
التقسيم بالكلمات الكاملة (Word-level tokenization) يعامل كل كلمة كوحدة واحدة لا تتجزأ، فيحتاج إلى مفردات ضخمة ويخفق مع الكلمات الجديدة أو الأخطاء الإملائية فيرميها كرمز «غير معروف». أما BPE فيبدأ من وحدات أصغر (بايتات أو محارف) ويدمج الأزواج الأكثر تكرارًا تدريجيًا، فيستطيع تمثيل أي كلمة — حتى لم يراها أثناء التدريب — بتجميعها من أجزاء يعرفها، لكنه قد يقسّم الكلمة الواحدة إلى عدة رموز.
لماذا تختلف عدد الرموز بين اللغات في نفس النص؟
لأن BPE يُبنى على تكرار الأنماط في بيانات التدريب، واللغات ذات التمثيل الوافر تُنتج مفردات تغطي كلماتها الشائعة بكفاءة عالية، بينما تُقسَّم لغات أخرى إلى رموز أكثر لأن أنماطها أقل تكرارًا في تلك البيانات. كما أن بعض اللغات تُكتب بمقاطع أو محارف تحتاج بايتات أكثر في ترميز UTF-8، فينتج عنها عدد رموز أكبر للنص نفسه.
هل زيادة حجم المفردات تقلل التكلفة دائمًا؟
لا. زيادة المفردات تقلّل عدد الرموز عادةً، وهذا يخفّض تكلفة الإدخال/الإخراج عند التسعير بالرمز، لكنها ترفع حجم مصفوفة التضمين وطبقة الإخراج فيزيد استهلاك الذاكرة والحساب، وقد تضعف جودة تمثيل الرموز النادرة لأن كل رمز يُدرَّب على أمثلة أقل. التوازن الأمثل يعتمد على اللغة والمهمة، والتحقق العملي بعدّ الرموز والتكلفة هو الفيصل.
كيف أختبر عدد الرموز قبل إرسال الطلب إلى الـ API؟
استخدم حاسبة رموز (tokenizer calculator) متوافقة مع النموذج المستهدف، أو استدعِ مكتبة الترميز الخاصة به محليًا على نصك التجريبي. قِس عدد الرموز للنص نفسه بصيغ مختلفة (مختصر مقابل مفصّل، لغة عربية مقابل إنجليزية) وقدّر التكلفة بضرب العدد في سعر الرمز المعلن، مع تذكّر أن الإخراج قد يتجاوز طول الإدخال.
ما علاقة BPE بمرحلة التضمين Embeddings؟
BPE يحدّد «وحدات» النص التي ستُرمَّز، ثم تُحوَّل كل وحدة إلى مُعرِّف رقمي (ID) يُبحث عنه في جدول التضمين للحصول على متجه كثيف يمثلها. أي أن BPE يقع قبل التضمين ويحدد شكل مدخلاته؛ فاختيار حجم مفردات وتقسيم مختلفين يغيّر عدد المتجهات التي تدخل النموذج وبالتالي التمثيل الداخلي والنتيجة.