حتى الآن تعاملنا مع الوكيل ككلٍّ واحد. في هذا الفيديو ننظر إلى النموذج اللغوي الموجود داخله. سنبدأ بتقسيم العمل بين الـ harness الموجود على حاسوبكم والنموذج الموجود على خادم بعيد. ثم سنركّز على النموذج. سنرى كيف يتحوّل النص إلى tokens، وكيف يصبح كل token متّجهًا من الأرقام، وكيف يتنبّأ النموذج بـ token واحد في كل مرة، وكيف تبدو الطبقات المتراصّة داخله. كل واحد من هذه الأمور يفسّر شيئًا ستلاحظونه لاحقًا في سلوك الوكيل. في هذا المساق نستخدم هذه النماذج ولا نبنيها، لذلك سنبقى على مستوى الحدس طوال الوقت.
لنبدأ بتذكّر بنية الوكيل، لأنها تبيّن لنا بالضبط أين يقع النموذج. كل شيء يبدأ منكم. تعطون الـ harness مهمة موصوفة بلغة عادية. الـ harness هو البرنامج الذي يعمل على حاسوبكم أنتم، البرنامج الذي ثبّتموه وتكتبون فيه.
يجمع الـ harness مهمتكم ومعلومات ذات صلة بالمشروع، ثم يرسل طلبًا إلى النموذج عبر الشبكة. في هذا الإعداد، يعمل النموذج على خادم بعيد. من المهم أن نكون دقيقين هنا. النموذج ليس على حاسوبكم، ولا يستطيع أن يرى حاسوبكم. كل ما يعرفه، كان على الـ harness أن يرسله إليه.
ينظر النموذج في الطلب، ثم يردّ. وكثيرًا ما لا يكون هذا الردّ إجابة تقرؤونها أنتم. بل يكون طلبًا من النموذج نفسه لاستدعاء أداة. الأداة عملية ملموسة يمكن تنفيذها في مشروعكم، مثل قراءة ملف، أو تعديل الكود، أو تشغيل أمر. يستطيع النموذج أن يطلب أداة، لكنه لا يستطيع تنفيذها.
يتولّى الـ harness استدعاء الأداة وفق الصلاحيات المعمول بها. في هذا المثال، تعمل الأداة على حاسوبكم، ويجمع الـ harness نتيجتها. قد تكون هذه النتيجة محتوى ملف، أو مُخرَجات اختبار، أو تأكيدًا بأن التعديل قد تمّ.
يرسل الـ harness هذه النتيجة إلى النموذج في الطلب التالي، ويقرّر النموذج ما الخطوة التالية. يمكن للنموذج أن يطلب استدعاء أداة آخر، وأن يتلقّى نتيجته، ثم يختار خطوة أخرى. هذه هي حلقة الوكيل. قد تساعد الخطوة في المهمة، لكنها قد تفشل أيضًا أو تحتاج إلى تصحيح.
عندما يعيد النموذج ردًّا نهائيًا بدل استدعاء أداة آخر، يعيد الـ harness النتيجة إليكم. عندها فقط يظهر نصّ إجابة الوكيل في نافذة المحادثة أمامكم. النموذج يقترح الإجراءات. والـ harness يحدّد الأدوات المتاحة، ويدير الصلاحيات، وينفّذ المسموح به. في بقية هذا الفيديو، سنرى كيف يولّد النموذج هذه الردود واستدعاءات الأدوات.
النموذج لا يرى نصّكم أبدًا كما ترونه أنتم. أول ما يحدث هو أن نصّكم الخام يُقسَّم إلى قطع تُسمّى tokens. هذه هي العبارة tokenization is simple، قبل أن نقسّمها.
وهذه نتيجة التقسيم. لاحظوا أن tokenization انقسمت إلى قطعتين: token و ization، بينما بقيت كلمة simple كاملة. إذن الـ tokens ليست هي الكلمات نفسها. إنها قطع من النص، والكلمات الشائعة تكون عادةً قطعة واحدة، بينما تنقسم الكلمات الأقل شيوعًا إلى عدة قطع. بل يمكنكم أن تروا أن بعض الـ tokens في مثالنا تتضمّن حرف المسافة الذي يفصل بين الكلمات في النص.
للـ tokenizer مفردات، وهي مجموعة الـ tokens التي يمثّل بها النص. وللحصول على أرقام ملموسة طوال الفيديو، سنستخدم Llama 3، لأنه نُشر بشكل مفتوح، فأحجامه وتصميمه موثّقة علنًا. تضمّ مفرداته نحو 128 ألف token. النموذج يعمل على الـ tokens. يمكن أن يمثّل الـ token كلمة كاملة، أو جزءًا من كلمة، أو علامة ترقيم، أو أجزاءً أخرى من النص.
تساعد الـ tokens على تفسير لماذا قد يصعب على النموذج اللغوي عدّ الأحرف، حتى عندما يبدو السؤال بسيطًا بالنسبة إلينا. كم مرة يظهر الحرف r في كلمة strawberry؟ إجابة خاطئة شائعة لدى النماذج اللغوية هي: مرتان. والإجابة الصحيحة: ثلاث مرات. النماذج الأحدث تُجيب عادةً إجابة صحيحة عن هذه الكلمة. فقد اشتهر هذا السؤال حتى صار موجودًا في بيانات تدريبها، لذا قد تكون الإجابة الصحيحة محفوظة لا محسوبة.
عندما تقرؤون الكلمة، ترون عشرة أحرف منفصلة أمامكم. يمكنكم أن تشيروا إلى حروف r واحدًا تلو الآخر وتعدّوها، فتحصلون على ثلاثة.
أما النموذج فلا يتلقّى الكلمة بهذه الطريقة. في هذا المخطط، تشكّل strawberry مع المسافة قبلها token واحدًا. التقسيم الدقيق يعتمد على الـ tokenizer. يتلقّى النموذج مُعرِّف token واحدًا للكلمة كلها، بدل مُعرِّف منفصل لكل حرف. وعليه أن يستنتج التهجئة قبل أن يتمكّن من عدّ الأحرف.
في بعض النماذج اللغوية، قد تُقسَّم كلمة strawberry بطريقة مختلفة. لكن حتى تقسيم الكلمة إلى بضعة tokens لا يعطي النموذج token واحدًا لكل حرف. فكل قطعة لا تزال مجموعة من عدة أحرف، والنموذج يعمل على هذه القطع لا على الأحرف التي بداخلها. يمكن للنموذج أن يتعلّم أثناء التدريب كيف تُهجّى الـ tokens. لكن هذا يختلف عن تلقّي قائمة أحرف يمكنه عدّها مباشرة.
كيف نجعل مهمة العدّ أكثر موثوقية؟ إحدى الطرق هي وضع الأحرف في النص نفسه. النموذج الذي يكتب الكلمة أولًا حرفًا حرفًا يصبح لديه شيء يستطيع عدّه فعلًا. كتابة الأحرف قد تساعد، لكنها لا تضمن صحة العدّ. فقد يتهجّى النموذج الكلمة بشكل صحيح ثم يخطئ في عدّها. وللحصول على عدد دقيق، يستطيع الوكيل تشغيل سطر من الكود. يمكننا فحص السلسلة النصية وعملية العدّ، ثم التحقّق من النتيجة. هذا دور مفيد للأدوات: يمكن للنموذج أن يطلب من برنامج تنفيذ عملية دقيقة. معرفة كيفية تمثيل النص تساعدنا على اختيار طريقة أفضل لأداء المهمة.
كيف تُستخدم الـ tokens في الحساب؟ لكل token مُعرِّف رقمي، لكن الشبكة تحتاج إلى أكثر من هذا المُعرِّف لتعمل به. فهي تستخدم تمثيلًا تعلّمته للـ token. يخزّن النموذج هذه التمثيلات في جدول بحث، فيه صفّ واحد لكل token في المفردات.
لتحويل token إلى أرقام، نبحث ببساطة عن صفّه في هذا الجدول. هنا نجد الصفّ الخاص بـ ization ونستخرجه.
هذا الصفّ قائمة من الأرقام، أي متّجه، ويُسمّى هذا المتّجه الـ embedding الخاص بالـ token. لكل token عدد كبير جدًا من الأرقام. في أكبر نماذج Llama 3، يحتوي كل متّجه embedding على نحو 16 ألف رقم.
القيم في الـ embedding تأتي من التدريب. قد يبدأ الجدول بقيم عشوائية، لكن التدريب يعدّلها مع بقية معاملات الشبكة. قد يقرّب التدريب بين الـ tokens المتشابهة معنًى أو استخدامًا في فضاء المتّجهات هذا. عندها تمثّل المسافات والاتجاهات علاقات تعلّمها النموذج بين الـ tokens. لا يحدث هذا دائمًا، لكنه يلمّح إلى طريقة ترميز النماذج اللغوية للمعلومات بداخلها.
إذن ماذا يفعل النموذج فعلًا بكل هذه المتّجهات؟ لتوليد النص، يتنبّأ النموذج بالـ token الذي قد يأتي بعد ذلك. يبدأ مثالنا بالكلمات the cat sat on the.
نمرّر هذا النص عبر النموذج. لنتعامل الآن مع النموذج نفسه كصندوق أسود. سنفتحه بعد قليل.
ما يخرج ليس إجابة واحدة. إنه احتمال لكل token في المفردات قد يكون هو التالي. في هذا الرسم، أعلى احتمال هو لكلمة mat بنسبة 40%، تليها chair، ثم hat، ثم rug. نعرض بضعة مرشّحين فقط من توزيع يشمل المفردات كلها. لتوليد النص، نختار token من هذا التوزيع، ونضيفه، ثم نطلب التنبّؤ التالي. وبتكرار هذه العملية يتكوّن الردّ.
لنفتح هذا الصندوق الأسود، على مستوى الحدس، لأننا في هذا المساق نستخدم هذه النماذج، ولا نبنيها بدقة رياضية. نبدأ بالـ tokens المُدخَلة مرتّبة من اليسار إلى اليمين. The cat sat on the hat.
يتحوّل كل token إلى متّجه الـ embedding، أي الأرقام نفسها التي بحثنا عنها في الجدول. إذن صار لكل موقع token الآن متّجه أرقام خاص به يقع فوق كلمته.
فوق كل token نبني عمودًا طويلًا من الطبقات، يتكرّر فيه البناء نفسه. هناك نوعان من الطبقات، وهما يتناوبان. طبقات الـ attention معروضة بلون، وطبقات الـ feed-forward مرسومة بلون آخر على شكل رموز شبكة صغيرة. النماذج الحقيقية تضمّ طبقات كثيرة كهذه. هنا نعرض اثنتين فقط من كل نوع، حتى تبقى الصورة واضحة.
الجزء الذي يدمج المعلومات عبر المواقع يُسمّى attention head. طبقات الـ feed-forward تعالج كل موقع على حدة. أما طبقات الـ attention فمختلفة. فهي تتيح للـ token أن ينظر إلى الوراء نحو الـ tokens السابقة ويأخذ منها معلومات. وبهذا يأخذ النموذج السياق المحيط في الحسبان. لن نشرح الـ attention heads هنا بتفصيل أكثر. المهم أن كل موقع يستطيع أن يستخدم المعلومات من الـ tokens حتى ذلك الموقع.
في أعلى العمود الأخير تمامًا، ينتج النموذج بالضبط ما رأيناه سابقًا. أي توزيعًا على كل الـ tokens الممكنة، وهو تنبّؤه بما سيأتي بعد ذلك.
هكذا يعمل التوليد. نختار token ونضيفه. ويُضاف إلى المخطط عمود جديد لهذا الموقع، يستخدم طبقات النموذج نفسها. ثم يتنبّأ النموذج بالـ token التالي، وتتكرّر العملية.
يتحوّل النص إلى tokens، وكل token إلى متّجه مُتعلَّم. ومن هذه المتّجهات يتنبّأ النموذج بالـ token الذي قد يأتي تاليًا.
الـ attention يدمج معلومات الموقع الحالي وما قبله، والـ feed-forward يعالج كل موقع. ومعًا تتنبّأ الطبقات بالـ token التالي.
هذا هو الأساس. كل ما يفعله الوكيل، كل ردّ وكل استدعاء أداة، يمرّ عبر هذا المتنبّئ الواحد. في الفيديو التالي نواصل تتبّع هذا التنبّؤ نفسه، عبر حلقة التوليد، إلى المحادثة، وصولًا إلى حدود نافذة السياق.