في هذه المقالة، سنستعرض كيفية إنشاء نموذج لغوي بسيط للغاية باستخدام Ruby. في حين تتطلب نماذج اللغة الكبيرة الحقيقية كميات هائلة من البيانات والموارد الحسابية، يمكننا إنشاء نموذج بسيط يوضح العديد من المفاهيم الأساسية وراء نمذجة اللغة. في مثالنا، سنبني نموذجًا أساسيًا لسلسلة ماركوف "يتعلم" من النص المدخل ثم يولد نصًا جديدًا بناءً على الأنماط التي لاحظها.
ملاحظة: هذا البرنامج التعليمي مخصص لأغراض تعليمية ويوضح نهجًا مبسطًا لنمذجة اللغة. إنه ليس بديلاً عن برامج التعلم العميق الحديثة مثل GPT-4، بل هو مقدمة للأفكار الأساسية.
نموذج اللغة هو نظام يعين احتمالات لتسلسلات من الكلمات. وفي جوهره، تم تصميمه لالتقاط البنية الإحصائية للغة من خلال تعلم احتمالية حدوث تسلسل معين في سياق معين. وهذا يعني أن النموذج يحلل أجزاء كبيرة من النص لفهم كيفية تتبع الكلمات بعضها البعض عادةً، مما يسمح له بالتنبؤ بالكلمة أو العبارة التي قد تأتي بعد ذلك في تسلسل. وتشكل هذه القدرات أهمية مركزية ليس فقط لمهام مثل إنشاء النص والإكمال التلقائي ولكن أيضًا لمجموعة متنوعة من تطبيقات معالجة اللغة الطبيعية (NLP)، بما في ذلك الترجمة والتلخيص وتحليل المشاعر.
تستخدم نماذج اللغة الحديثة واسعة النطاق (LLMs) مثل GPT-4 تقنيات التعلم العميق ومجموعات البيانات الضخمة لالتقاط الأنماط المعقدة في اللغة. تعمل هذه النماذج عن طريق معالجة النص المدخل من خلال طبقات عديدة من الخلايا العصبية الاصطناعية، مما يمكنها من فهم وتوليد نص يشبه النص البشري بطلاقة ملحوظة. ومع ذلك، تكمن وراء هذه الأنظمة المتطورة نفس الفكرة الأساسية: فهم وتوقع تسلسل الكلمات بناءً على الاحتمالات المكتسبة.
تعد سلسلة ماركوف واحدة من أبسط الطرق لنمذجة اللغة. سلسلة ماركوف هي نموذج إحصائي يعمل على افتراض أن احتمالية ظهور كلمة ما تعتمد فقط على مجموعة محدودة من الكلمات السابقة، وليس على تاريخ النص بالكامل. يُعرف هذا المفهوم باسم خاصية ماركوف. من الناحية العملية، يفترض النموذج أنه يمكن التنبؤ بالكلمة التالية في التسلسل فقط من خلال النظر إلى الكلمة (الكلمات) الأحدث - وهو تبسيط يجعل المشكلة أكثر قابلية للحل حسابيًا مع الاستمرار في التقاط الأنماط المفيدة في البيانات.
في نموذج اللغة المبني على سلسلة ماركوف:
في تنفيذنا، سنستخدم "ترتيبًا" قابلًا للتكوين لتحديد عدد الكلمات السابقة التي يجب أخذها في الاعتبار عند إجراء التنبؤات. يوفر الترتيب الأعلى سياقًا أكبر، مما قد يؤدي إلى نص أكثر تماسكًا وارتباطًا بالسياق، حيث يحتوي النموذج على مزيد من المعلومات حول ما حدث قبل ذلك. وعلى العكس من ذلك، يقدم الترتيب الأدنى المزيد من العشوائية ويمكن أن يؤدي إلى تسلسلات أكثر إبداعًا، وإن كانت أقل قابلية للتنبؤ، من الكلمات. هذا التوازن بين التماسك والإبداع هو اعتبار أساسي في نمذجة اللغة.
ومن خلال فهم هذه المبادئ الأساسية، يمكننا أن ندرك بساطة نماذج سلسلة ماركوف والأفكار الأساسية التي تدعم نماذج اللغة العصبية الأكثر تعقيدًا. ولا تساعد هذه الرؤية الموسعة في فهم الآليات الإحصائية وراء التنبؤ باللغة فحسب، بل إنها تضع الأساس أيضًا للتجريب باستخدام تقنيات أكثر تقدمًا في معالجة اللغة الطبيعية.
قبل البدء، تأكد من تثبيت Ruby على نظامك. يمكنك التحقق من إصدار Ruby لديك عن طريق تشغيل:
ruby -v
إذا لم يتم تثبيت Ruby، فيمكنك تنزيله من ruby-lang.org .
بالنسبة لمشروعنا، قد ترغب في إنشاء دليل وملف مخصصين:
mkdir tiny_llm cd tiny_llm touch llm.rb
أنت الآن جاهز لكتابة كود Ruby الخاص بك.
بالنسبة لنموذج اللغة، ستحتاج إلى مجموعة نصية. يمكنك استخدام أي ملف نصي للتدريب. بالنسبة لمثالنا البسيط، يمكنك استخدام عينة صغيرة من النص، على سبيل المثال:
sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT
قبل التدريب، من المفيد معالجة النص مسبقًا:
بالنسبة لأغراضنا، تعمل طريقة String#split
الخاصة بـ Ruby بشكل جيد بما يكفي للتجزئة.
سنقوم بإنشاء فئة Ruby تسمى MarkovChain
لتغليف سلوك النموذج. ستتضمن الفئة:
train
التي تقوم ببناء السلسلة من النص المُدخل.generate
تقوم بإنتاج نص جديد عن طريق أخذ العينات من السلسلة.فيما يلي الكود الكامل للنموذج:
class MarkovChain def initialize(order = 2) @order = order # The chain is a hash that maps a sequence of words (key) to an array of possible next words. @chain = Hash.new { |hash, key| hash[key] = [] } end # Train the model using the provided text. def train(text) # Optionally normalize the text (eg, downcase) processed_text = text.downcase.strip words = processed_text.split # Iterate over the words using sliding window technique. words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end # Generate new text using the Markov chain. def generate(max_words = 50, seed = nil) # Choose a random seed from the available keys if none is provided or if the seed is invalid. if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words # Form the key from the last 'order' words. key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? # Randomly choose the next word from the possibilities. next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end
**التهيئة:** يحدد المنشئ initialize
الترتيب (الافتراضي هو 2) وينشئ تجزئة فارغة لسلسلتنا. يتم إعطاء التجزئة كتلة افتراضية بحيث يبدأ كل مفتاح جديد كمصفوفة فارغة.
**تدريب النموذج:**تأخذ طريقة train
سلسلة نصية، وتضبطها، وتقسمها إلى كلمات. باستخدام each_cons
، يتم إنشاء مجموعات متتالية من الكلمات بطول order + 1
تعمل الكلمات order
الأول كمفتاح، ويتم إلحاق الكلمة الأخيرة بمجموعة من الاستمراريات المحتملة لهذا المفتاح.
**إنشاء نص:** تبدأ طريقة generate
بمفتاح أساسي. إذا لم يتم توفير أي مفتاح، يتم اختيار مفتاح عشوائي. ثم يتم إنشاء تسلسل بشكل متكرر من خلال البحث عن الكلمات الأخيرة order
واختيار الكلمة التالية حتى يتم الوصول إلى الحد الأقصى لعدد الكلمات.
الآن بعد أن أصبح لدينا فئة MarkovChain
، فلنبدأ بتدريبها على بعض بيانات النص.
# Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create a new MarkovChain instance with order 2 model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!"
عند تشغيل الكود أعلاه (على سبيل المثال، عن طريق حفظه في llm.rb
وتنفيذ ruby llm.rb
)، سيتم تدريب النموذج باستخدام نص العينة المقدم.
بمجرد تدريب النموذج، يمكنك إنشاء نص جديد. دعنا نضيف بعض التعليمات البرمجية لإنشاء نص نموذجي وطباعته:
# Generate new text using the trained model. generated_text = model.generate(50) puts "Generated Text:" puts generated_text
يمكنك أيضًا محاولة توفير بذرة لتوليد النص. على سبيل المثال، إذا كنت تعرف أحد المفاتيح في النموذج (مثل "once upon"
)، فيمكنك القيام بما يلي:
seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed
من خلال تجربة بذور ومعلمات مختلفة (مثل الترتيب والحد الأقصى لعدد الكلمات)، يمكنك رؤية كيفية اختلاف الناتج.
هذا هو البرنامج النصي Ruby الكامل الذي يجمع كل الخطوات المذكورة أعلاه:
#!/usr/bin/env ruby # llm.rb # Define the MarkovChain class class MarkovChain def initialize(order = 2) @order = order @chain = Hash.new { |hash, key| hash[key] = [] } end def train(text) processed_text = text.downcase.strip words = processed_text.split words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end def generate(max_words = 50, seed = nil) if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create and train the model model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!" # Generate text without a seed generated_text = model.generate(50) puts "\nGenerated Text:" puts generated_text # Generate text with a specific seed seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed
llm.rb
.llm.rb
ruby llm.rb
يجب أن ترى إخراجًا يشير إلى أن النموذج تم تدريبه ثم مثالين للنص الناتج.
يلخص الجدول التالي بعض مقاييس المقارنة لإصدارات مختلفة من تطبيقات Tiny LLM الخاصة بنا. يتم شرح كل مقياس أدناه:
فيما يلي جدول التخفيض مع بيانات المعايير المرجعية:
نموذج | طلب | وقت التدريب (مللي ثانية) | زمن التوليد (مللي ثانية) | استخدام الذاكرة (ميغا بايت) | تقييم التماسك |
---|---|---|---|---|---|
برنامج LLM الصغير الإصدار 1 | 2 | 50 | 10 | 10 | 3/5 |
برنامج Tiny LLM v2 | 3 | 70 | 15 | 12 | 3.5/5 |
برنامج Tiny LLM v3 | 4 | 100 | 20 | 15 | 4/5 |
توفر هذه المعايير نظرة عامة سريعة على التنازلات بين تكوينات النماذج المختلفة. ومع زيادة الترتيب، يميل النموذج إلى استغراق وقت أطول قليلاً للتدريب وإنشاء النص، كما يستخدم المزيد من الذاكرة. ومع ذلك، غالبًا ما تكون هذه الزيادات في استهلاك الموارد مصحوبة بتحسينات في تماسك النص الناتج.
في هذا البرنامج التعليمي، أوضحنا كيفية إنشاء نموذج لغوي بسيط للغاية باستخدام Ruby. من خلال الاستفادة من تقنية سلسلة ماركوف، قمنا ببناء نظام:
على الرغم من أن هذا النموذج التجريبي بعيد كل البعد عن مستوى الإنتاج في برامج الماجستير في القانون، إلا أنه بمثابة حجر الأساس لفهم كيفية عمل نماذج اللغة على المستوى الأساسي. يمكنك التوسع في هذه الفكرة من خلال دمج تقنيات أكثر تقدمًا، أو التعامل مع علامات الترقيم بشكل أفضل، أو حتى دمج Ruby مع مكتبات التعلم الآلي للحصول على نماذج أكثر تطورًا.
برمجة سعيدة!