توليدُ النّصوصِ العربيّةِ مع نموذجِ GPT

GPT

إعداد: م. سري السباعي

التّدقيق العلمي: د.م. حسن قزّاز، م. محمّد سرميني

تمهيد

هذا مقال نشرح فيه برمجة محوِّلٍ لتوليد نصوص عربيّةٍ، ولا بُدَّ قبل الولوج إلى لذلك من ذكر مقدّمات تعين القارئ على فهم المقالة مع تنبيه وخاتمة، فانتظمت المقالة في أربع مقدّمات وتنبيه ثمّ برمجة المحوّل ثمّ خاتمة

المقدّمة الأولى في تعريف عامٍّ عن نموذج المحوّلات

وقد سبقني محمد سرميني بالتعريف به مفصلاّ في مقال على المدونة بعنوان [1] “المُحوّل Transformer” ولا ضير من إعادة بعض ذلك مختصرًا؛ فنقول: ظهر هذا النّموذج للنّاس عام ٢٠١٧ في ورقة “الانتباه هو كلّ ما تحتاج” والتي بيّن مؤلفوها تصميمَ النّموذج و هيكلته ومعادلاته ولم يُفصّلوا في الأخيرة، فجاء John Thickstun جون ثكستن ففرّع وفصّل ذلك في ورقة مختصرة، وتعريفها مُختصرا: “هو نموذج عميق فاهم للسياقات النصية يتكون من مُشّفر ومفكك كلاهما أو أحدهما” فمن لدن ذلك الزّمن لوقتنا وهذا النّموذج صار بُغية كلّ باحث وغاية كلّ طالب، إذ عمَّت استعمالاته فروعَ معالجةِ اللُّغة الطبيعيّة NLP وقد كان أساسه ترجمة النصوص التتابعيّة، وظهرت نماذج قويةٌ وضخمة جدّا مثل GPT و BERT و T5 و LaMa وغيرها من نماذج غطَّت عموم مجال المعالجة، بل قد جاوز النّموذج مجاله واسْتُعملَ في مجالات أُخرَى كمعالجة الصّور Image Processing والسلاسل الزمنيّة Time Series وبيانات البروتينات والتعلّم المعزّز Reinforcement Learning، وما ذاك إلا دليل على قوّته وقد وصفه أندريه كرباثي Andrej Kerbathy  “بأنه أحسنُ فكرةٍ في المجال”.

المقدّمة الثانية في استعمالات المحوّلات في معالجة اللغات الطبيعيّة

إنّ المحوّل مكوّنٌ من قسمين رئيسيّين مشفّر  Encoder و فاكِّ التشفير (مُفكّك) Decoder ، ومن ذلك نتجت ثلاثة نماذج مختلفة لكلّ منها استعمال: 

  1. نموذج يستعملها كلاهما وهذا يتعامل مع النصوص التتابعيّة، والتي يكون مُدخلها نصًّا يخالف في طوله مُخرجه مثل الترجمة والتّلخيص ومن أمثلتها نموذج .T5 
  2. ونموذج يستعمل المُشفِّرَ وحده ومثاله الأشهر BERT، وقد كان سبب عمله أنّ يتعلم تمثيل النصوص وتضمينها مع فهم معانيها. 
  3. ونموذج يستعمل الفاكَّ وحده كذلك وأشهرهم هو GPT، وهو الذي سنبينه إن شاء الله وقد أسميناه مُتِمًّما اختصارًا لاسمه “محوّلٌ توليديٌّ مُدَرَّب” وهدفه الأساسيّ توليد النصوص من خلال توقّع التّالي من السّابق. 

وليعلم القارئ أنّ هذا التقسيم ليس خاصًّا بكلٍّ منها بل كلّ قسم يمكن استعماله في مهمات أخرى ولكن بيّنّا أصول ذلك.

المقدّمة الثالثة فيما يحتاجه القارئ ليفهم المقالة

إنّ نموذج المحوّل عمّت شهرته وطبقت الآفاق فظنَّ بعض الناس أنّ فهمه ليس إلا للأساتذة والمختارين، وما ذلك إلا لقلّة البحث والقراءة، وإلا فقد تكاثرت المقالات الشّارحة له عند الأجانب تكاثرًا عجيبًا ما بين مبرمج ناظم وراسم ملوّن ورياضيٍّ مفسّر، كلهم يبغي تسهيله وإفهامه الغيرَ. وأما هنا في مقالتنا فليعلم القارئ أنّ ما عليه معرفته أمران؛ شيء من أساسيّات الجبر الخطيّ من ضرب المصفوفات متعددة الطبقات Tensors والمصفوفات -وإن أحببت تذكيرًا بها فانظر الورقة المرفقة في المصادر[2]- وينبغي كذلك معرفةُ الدوالِّ الأساسيّة في مكتبة بايتورش Pytorch إذ هي المستعملة في هذه المقالة، وأمّا غير ذلك فسنشرحه بإذن الله تعالى أثناء ذكره.

المقدّمة الرّابعة في المقسّمات Tokenizers

وهي عمليّة تقسيم الكلام المكتوب حتى يسهل تضمينه وتمثيله ليفهمه الحاسب، وله أقسام أساسيّة أربعة: أولها التقسيم الكلامي أي أنّ كلّ كلمة قسمٌ منفرد وهذا مكلفٌ، وثانيها التقسيم الحرفيّ أي أن نقسّم الكلام بأحرفه وثالثها التقسيم المُقطِّع حيث يتمّ تفصيص (تجزيء) الكلمات إلى وحدات أصغر (يُطلق عليها وحدة لفظية tokens) وله أنواع كثيرة، ورابعها التقسيم اللغويّ (الصرفيّ). وفي هذا المقال للتيسير سنستخدم التقسيم الحرفيّ. 

تنبيه: 

يظنُّ البعض أنّ هذا التقدّم الحادث في أيامنا نذير شؤم وفي أسوأ صورهِ، وهذا خطأ فادح إذ أنّ هذه النماذج لا تعدو أن تكون معادلة رياضيّةً تنتجُ توزيعًا إحصائيًّا، ولكنها أداة نافعة لمن أحسن استعمالها فمن ذلك التعلم أيَّ تعلّم أحببت ومن تلك البرمجة تستطيع إن  فكّرتَ في أمرٍ أن تحوِّله لشفرةٍ بسرعة وهذا مفيدٌ جدًا. 

وكما قال أستاذنا هواري الغوتي في مقالةٍ له عن هذه المسألة [3] 

“لا تخافوا من الذّكاء الاصطناعيّ فهو لا يزال طفلًا عمرُهُ ٥ أيام”

 والآن لنبدأ مقالتنا بعد هذه التطويلة اللازمة وليعذرني القراّء الكرام، فأنا أحبّ أن يكونوا معي على نفس التَّلَّةِ نرى نفسَ الوادي.

شرح النموذج وتدريبه

النصّ المُستخدم

سنستخدم لتدريب النّموذج مجموعة مقالات من صحيفة الخليج عام ٢٠٠٤  وهي تحتوي على حوالي ٣ ملايين كلمة على أربعة مواضيع، وهي منشورة بصيغة html فقمت باستخلاصها وجمعها في ملفٍّ نصيّ واحد مع معالجتها بإزالة مالا حاجة له من تشكيل وغيره [4].  

أسلوب الشّرح

سأقوم بشرح كلّ جزء من المحوّل منفصلًا شرحًا وبرمجةً، ثمّ أجمعها في نصّ واحد ليسهل استخدامها ونسخها 

أوّلًا استيراد الملفّ المستعمل مع طريقة التٌقطيع

شكل-1 استيراد الملف النصي

بالنسبة لإدخال المدخل فلا بدّ من تقسيمه (تجزيئه) لأنّ إدخال النصّ كله دفعة واحدة سيكون مكلفًا بشكل كبير، فلا بدّ من أخذِ أقسام منه كلّ مرة وهو ما يسمى بالمجموعة الجزئيّة (وأحبّ أن أسميه التلقيم من اللقم) ليفهم المتحوّل النصّ قطعةً قطعة، وكلّ قطعة تُقَّطع بأساليب اخترنا منها على الحروف تسهيلًا للغرض وتوضيحًا لأيسر الطرق

شكل-2 عينة من النص المختار

ثمّ ننظف النصّ من الحروف غير العربية  كما في السطر الثالث مستخدمين مكتبة التعابير المُنمَّطة regex expressions

شكل-3 معالجة النص وتهيئته

وهنا تنبيه على موضوع المُقسّم فقد سبق ذكر أننا سنستعمل ذات الحروف، ويمكننا بناؤه بالشكل التالي:

شكل-4 بناء المشفر والفاكّ مع مثال

وشرح النص السابق سطرا سطرا:

  1. قاموس يحتوي على الحروف (المجموعة من النص الأصلي) مفاتيحًا وأرقامها (دليلها index) قيمها
  2. قاموس مفاتيحه دلائل الأول (عكس ذلك أي الرقم هو المفتاح والحرف هو القيمة 
  3. المُشفر encoder هي دُلَيْلة (تصغير دالة أو تُسمى الدالة لِمْدَوِيَّة) تستقبل النص وتستعمل القاموس الأول لتُشَفِّرَهُ أي تُرجعه كقائمة (زمرة) من الأرقام وهذه ستكون مُدخل النموذج الأساسي
  4. الفاكّ أو المُفَكِّك decoder وعمله عَكسُ عمل المُشفر بإرجاع القائمة نصا مقروءا باستعمال القاموس الثاني 
  5. ما بعدها من السطور مثال على تشفير جملة “مرحبا بك في هذه اللعبة” وفكّه 

ثمّ نحوّل كلّ ذلك إلى مصفوفة متعددة الأبعاد tensors (مرصوصة) بمكتبة بايتورش، حيث سوف تُستعمل في تدريب النموذج المبنيّ بها إذ تشترط ذلك.

شكل-5 تحويل المُدخلات لمصفوفة في بايتورش

والآن نُقسم البيانات قسمين أوّلهما تسعة أعشار (٩٠%) -وقد اختيرت هذه النسبة مع أن المشهور ثمانية أعشار (٨٠%) لأن النصوص قليلة وما نريد منها إلا التأكيد والضبط والبُعدَ عن التشبّع (الملاءمة الزائدة)-  لندرّب النموذج عليها، وأما العشر الأخير (١٠%) فهو للتأكيد إذ لا نريد من النموذج حفظَ الترتيبات فقط؛ بل نريده أن يتحرّر من ذلك قليلًا لينتج شيئًا جديدًا ولكن تحت هذا التوزيع الإحصائيّ للنصوص المعطاة

شكل-6 تقسيم البيانات لتدريب وتأكيد

شرح طريقة توزيع المدخلات والمخرجات (المتوقعات)

ملاحظة عن استغلال المُدخلات: عند إعطاءِ جملةٍ ذات طول محدّد المُقَسَّمات مع مخرجها وهو الجزء التالي، فإنّ نفس الجملة تتضمن أمثلة يمكن استعمالها لتدريب النموذج ولنرى المثال التالي :

كلمة مرحبا إذا قسمناها إلى حروفها مع آخره كمخرج تصير م ر ح ب ← ا،

ولكن إن تأمل النّاظر لأدرك أننا يمكن أن نستخرج المزيد من الأمثلة من نفس المدخل، فما بالنا لا نعتبر أن أول مدخلٍ هو الميم ولنتوقع راءها ومن ثمّ كلاهما ولنتوقع حاءهما وهكذا نستفيد زيادةً من أمثلة التدريب كثيرةً.

شكل-7 تقسيم المُدخلات وعدد الأمثلة في المرة الواحدة
شكل-8 تقسيم المُدخلات وعدد الأمثلة في المرة الواحدة

الآن سوف أشرح النموذج المستعمل قطعة قطعةً حسب المُخطط التدفقي التالي: 

ثمّ أذكر برمجةَ كلّ ذلك منفصلًا وبعد ذلك إن شاء الله أورد النصّ مجموعًا في مكانٍ واحدٍ حتى يسهل استعماله في التدريب.

شكل-9 المخطط التدفقي

فيما يلي شرح لأهم المراحل في المخطط التدفقي السابق والموضّح في الشكل-9. 

  • المدخلات: أما المدخل فهو مصفوفة ذات طول محدّد يحتوي على عدد من المُقسّمات (في حالتنا حروف) تسير على طول النّموذج، ونبدأ بعمل جدولٍ من المُضمنات العشوائيّة (وهي معنوية) التي سوف تتغير تدريجيًّا مع تدريب النموذج، فأعمدة الجدول طولها اختياريّ وهو أبعاد كلّ متجهٍ ممثل لكلّ مُقسم وأما صفوفه فعددها عدد المُقسمات لدينا (في حالتنا قد طبعناها فوق) 

أما تضمين الترتيب فهي إضافة لم تكن في نماذج الشبكات التكراريّة،  لأنّ ترتيب المدخلات فيها هو من هيكلتها فلا داعيَ له، ولكن هنا فإنّ المدخل لا يُعرف أوّله من آخره إلا إذا جمعناه مع متجهٍ يبين أماكن كُلِّ قطعةٍ، وهناك طريقتان فالأولى أن يتعلم الأماكن هذه والأخرى أن نضيف ثابتًا (وهي نفسها لها عدّة طرق ممّا ذكره أصحاب الورقة الأصليّة على حساب الدّوال الدّورانيّة مثل الجيب وتمامه، وهنا طرق أيسر وأوضح مثل متجهات رقميّة تصاعديّة) وهنا فإننا سوف نستعمل الأولى لسهولتها وقلّة تعقيدها. 

  • طبقة الانتباه وفيها مربط الفرس إذ هي أهمّ طبقة وهي التي غيرت وفتحت لنا بعد فضل الله أبواب هذه النماذج الضخمة النافعة، بكلّ اختصار هذه الطبقة يكون فيها التواصل بين أجزاء النصّ المُدخلة كالتالي: 
    • كلّ جزء نصيّ وكمثال ضميرٌ في جملة “رأيتُ أحمد فأحببته” فإنّ الضمير في فعل الحب راجع على أحمد ولا بدّ للنموذج أن يتعلم هذه العلاقات التي في الجملة. انظر جملةَ “سُرقت عينُ خالدٍ قرنًا ثمّ رجعت له” فنحن نفهم منها أنّ المسروق مال وهي أحد معانِ كلمة العين وأيضا نفهم أن مدّة السرقة قرنٌ ولم نفهم أنه قرن الحيوان، كلّ ذلك من السياق. من الممكن قراءة المزيد من خلال المرجع [5].
    • مبدأ الانتباه صُمّم وظهر عام ٢٠١٦ في بحث بادونا ومن معه [6]، ثمّ استُعمل في تصميم النموذج نموذج المحوّل عام ٢٠١٧ على يد باحثي جوجل [7] و سبق ذلك. أساس المبدأ أن تتواصل كلّ الأجزاء مع بعضها عن طريق جُداءٍ ثلاثيّ فيما بينها كالتالي: كلّ جزءٍ يبحث عن معنى في الأجزاء الأخرى ومصفوفة هذا تُسمى الاستفهام (السؤال) (السّؤال/المفتاح/القيمة Q/K/V)، ثمّ ما يَرد به كلّ جزءٍ يكون في مصفوفة تُسمى المفتاح؛ والتي تجيب كلّ جزء سؤالَه وفي النهاية لدينا مصفوفة القيمة والتي تعطي خلاصة هاتين العمليتين 

طبعا هذه الأحرف هي نتيجة ضرب مصفوفة المُدخلات في ثلاث مصفوفات هكذا 

X, input 

WK,WV, WQ

ضرب المدخل بكلّ واحدةِ يُنتج مصفوفات الحروف الثلاثة السابقة ثمّ نقوم بالتنبه، ولعل سائلًا يقول وما نفعُ هذه الطريقة؟ فنقول إنّ هذه الطريقة تساعد كلّ جزء أن يعرف ما يحتاج من الأجزاء الأخرى، وما يقدّم لها من معلومات وهكذا يستطيع النموذج أن يركز على العلاقات المهمة بين المُدخلات (الأجزاء الكلاميّة).

  • طبقة النظيم والتسوية: وهذه من أسهل الطبقات فهمًا، إذ تقوم بتسوية الأرقام أي جعلها ذات توزيعٍ طبيعيّ (غاوسي) ذات متوسّطٍ يساوي واحدًا وتباينٍ بصفر، وهذا يساعد في مواجهة مشكلة اضمحلال المشتقّ الرّاجع في عمليّة الانتشار الخلفيّ (أو التحديث الخلفيّ) وفي وضعنا هنا نقوم بعمل ذلك على الصفوف للمصفوفة الناتجة من الطبقة السابقة 
  • طبقة الشبكة العصبيّة هي طبقة ذات طبقات عصبونيّة متعدّدة حسب اختيار المُصمِّم وفي حالنا فلنا القدرة على اختيارها. 
  • أما الطبقة الخطيّة فهي شبكة عصبونيّة تُسقطُ المُدخل على أبعاد أقلّ عددًا 
  • ثم بعد ذلك نُحصي سوفت ماكس “softmax” المُخرج لنحصل على إحصائيّة الحرف التالي فنقوم باستمثال (تحسين) optimization  وسيطات (متغيرا) parameters النموذج عن طريق معادلة الانتروبيا المتقاطعة cross entropy لنقوم بتحسين الأوزان بناءً على المُخرج الصحيح 

وهنا تعليقات إضافية على ما بقي من النموذج: 

  1. أما الأسهم القافزة فهي لحلّ مشكلة تلاشي المشتقّ في الانتشار الخلفي،ّ حيث نستعمل نفس القيمة مع المُخرج السابق لها ويلاحظ هذا من النص البرمجي. 
  2. في استعمالنا المُختصر فإننا لم نبنِ كلّ الطبقات ويمكِن مراجعة الشيفرة المُعطاة. 
  3. المُدخل يُمكن تضمين (أي تحويلها الى متجهات كثيفة dense vectors تحمل معاني تُتَعلّم) أماكن حروفه بطريقتين: أولاهما مُضمّنات مُتعلّمة أثناء تدريب النموذج وهو المستعمل هنا، وأخرى مُتجهات ثابتة يتمّ حسابها في البداية ولها طرق متعدّدة.

وهذه شفرة النصّ كاملةً ليسهل استخدامها

import torch
import torch.nn as nn
from torch.nn import functional as F

# hyperparameters
batch_size = 16 # how many independent sequences will we process in parallel?
block_size = 32 # what is the maximum context length for predictions?
max_iters = 5000
eval_interval = 100
learning_rate = 1e-3
device = 'cuda' if torch.cuda.is_available() else 'cpu'
eval_iters = 200
n_embd = 64
n_head = 4
n_layer = 4
dropout = 0.0

torch.manual_seed(1337)

# wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt
with open('input.txt', 'r', encoding='utf-8') as f:
    text = f.read()

# here are all the unique characters that occur in this text
chars = sorted(list(set(text)))
vocab_size = len(chars)
# create a mapping from characters to integers
stoi = { ch:i for i,ch in enumerate(chars) }
itos = { i:ch for i,ch in enumerate(chars) }
encode = lambda s: [stoi[c] for c in s] # encoder: take a string, output a list of integers
decode = lambda l: ''.join([itos[i] for i in l]) # decoder: take a list of integers, output a string

# Train and test splits
data = torch.tensor(encode(text), dtype=torch.long)
n = int(0.9*len(data)) # first 90% will be train, rest val
train_data = data[:n]
val_data = data[n:]

# data loading
def get_batch(split):
    # generate a small batch of data of inputs x and targets y
    data = train_data if split == 'train' else val_data
    ix = torch.randint(len(data) - block_size, (batch_size,))
    x = torch.stack([data[i:i+block_size] for i in ix])
    y = torch.stack([data[i+1:i+block_size+1] for i in ix])
    x, y = x.to(device), y.to(device)
    return x, y

@torch.no_grad()
def estimate_loss():
    out = {}
    model.eval()
    for split in ['train', 'val']:
        losses = torch.zeros(eval_iters)
        for k in range(eval_iters):
            X, Y = get_batch(split)
            logits, loss = model(X, Y)
            losses[k] = loss.item()
        out[split] = losses.mean()
    model.train()
    return out

class Head(nn.Module):
    """ one head of self-attention """

    def __init__(self, head_size):
        super().__init__()
        self.key = nn.Linear(n_embd, head_size, bias=False)
        self.query = nn.Linear(n_embd, head_size, bias=False)
        self.value = nn.Linear(n_embd, head_size, bias=False)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))

        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        B,T,C = x.shape
        k = self.key(x)   # (B,T,C)
        q = self.query(x) # (B,T,C)
        # compute attention scores ("affinities")
        wei = q @ k.transpose(-2,-1) * C**-0.5 # (B, T, C) @ (B, C, T) -> (B, T, T)
        wei = wei.masked_fill(self.tril[:T, :T] == 0, float('-inf')) # (B, T, T)
        wei = F.softmax(wei, dim=-1) # (B, T, T)
        wei = self.dropout(wei)
        # perform the weighted aggregation of the values
        v = self.value(x) # (B,T,C)
        out = wei @ v # (B, T, T) @ (B, T, C) -> (B, T, C)
        return out


class MultiHeadAttention(nn.Module):
    """ multiple heads of self-attention in parallel """

    def __init__(self, num_heads, head_size):
        super().__init__()
        self.heads = nn.ModuleList([Head(head_size) for _ in range(num_heads)])
        self.proj = nn.Linear(n_embd, n_embd)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        out = torch.cat([h(x) for h in self.heads], dim=-1)
        out = self.dropout(self.proj(out))
        return out

class FeedFoward(nn.Module):
    """ a simple linear layer followed by a non-linearity """

    def __init__(self, n_embd):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(n_embd, 4 * n_embd),
            nn.ReLU(),
            nn.Linear(4 * n_embd, n_embd),
            nn.Dropout(dropout),
        )

    def forward(self, x):
        return self.net(x)

class Block(nn.Module):
    """ Transformer block: communication followed by computation """

    def __init__(self, n_embd, n_head):
        # n_embd: embedding dimension, n_head: the number of heads we'd like
        super().__init__()
        head_size = n_embd // n_head
        self.sa = MultiHeadAttention(n_head, head_size)
        self.ffwd = FeedFoward(n_embd)
        self.ln1 = nn.LayerNorm(n_embd)
        self.ln2 = nn.LayerNorm(n_embd)

    def forward(self, x):
        x = x + self.sa(self.ln1(x))
        x = x + self.ffwd(self.ln2(x))
        return x

# super simple bigram model
class BigramLanguageModel(nn.Module):

    def __init__(self):
        super().__init__()
        # each token directly reads off the logits for the next token from a lookup table
        self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
        self.position_embedding_table = nn.Embedding(block_size, n_embd)
        self.blocks = nn.Sequential(*[Block(n_embd, n_head=n_head) for _ in range(n_layer)])
        self.ln_f = nn.LayerNorm(n_embd) # final layer norm
        self.lm_head = nn.Linear(n_embd, vocab_size)

    def forward(self, idx, targets=None):
        B, T = idx.shape

        # idx and targets are both (B,T) tensor of integers
        tok_emb = self.token_embedding_table(idx) # (B,T,C)
        pos_emb = self.position_embedding_table(torch.arange(T, device=device)) # (T,C)
        x = tok_emb + pos_emb # (B,T,C)
        x = self.blocks(x) # (B,T,C)
        x = self.ln_f(x) # (B,T,C)
        logits = self.lm_head(x) # (B,T,vocab_size)

        if targets is None:
            loss = None
        else:
            B, T, C = logits.shape
            logits = logits.view(B*T, C)
            targets = targets.view(B*T)
            loss = F.cross_entropy(logits, targets)

        return logits, loss

    def generate(self, idx, max_new_tokens):
        # idx is (B, T) array of indices in the current context
        for _ in range(max_new_tokens):
            # crop idx to the last block_size tokens
            idx_cond = idx[:, -block_size:]
            # get the predictions
            logits, loss = self(idx_cond)
            # focus only on the last time step
            logits = logits[:, -1, :] # becomes (B, C)
            # apply softmax to get probabilities
            probs = F.softmax(logits, dim=-1) # (B, C)
            # sample from the distribution
            idx_next = torch.multinomial(probs, num_samples=1) # (B, 1)
            # append sampled index to the running sequence
            idx = torch.cat((idx, idx_next), dim=1) # (B, T+1)
        return idx

model = BigramLanguageModel()
m = model.to(device)
# print the number of parameters in the model
print(sum(p.numel() for p in m.parameters())/1e6, 'M parameters')

# create a PyTorch optimizer
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)

for iter in range(max_iters):

    # every once in a while evaluate the loss on train and val sets
    if iter % eval_interval == 0 or iter == max_iters - 1:
        losses = estimate_loss()
        print(f"step {iter}: train loss {losses['train']:.4f}, val loss {losses['val']:.4f}")

    # sample a batch of data
    xb, yb = get_batch('train')

    # evaluate the loss
    logits, loss = model(xb, yb)
    optimizer.zero_grad(set_to_none=True)
    loss.backward()
    optimizer.step()

# generate from the model
context = torch.zeros((1, 1), dtype=torch.long, device=device)
print(decode(m.generate(context, max_new_tokens=2000)[0].tolist()))

الخاتمة

تكلّمنا في هذا المقال عن المُحوّل وعن مكوّناته الرّئيسيّة وأنواعه واستخداماته بشكل مختصر، وتم تقديم نموذج يستعمل فاك التشفير Decoder بمفرده،  بهدف بناء  محوّلٌ توليديٌّ مُدَرَّب للنصوص العربية وقد أسميناه مُتِمًّما اختصارًا لاسمه.  وبهذا نختم مقالتنا فمن أحب تجربة الشيفرة بنفسه فما عليه الا تغيير اسم الملف النصي لاسم ملفه والتلاعب بالمتغيرات الأولية من عدد الرؤوس وعدد الطبقات وغيرها كيفما يُحب وتسمحُ له ما عنده من القوة الحاسوبية

المراجع

  1. المحوّل Transformer
  2. The Matrix Cookbook
  3. مقالة في جريدة جامعة الأمير سلطان عدد ١٥٠
  4. مقالات من صحيفة الخليج عام ٢٠٠٤ (تحتاج طلب وصول)
  5. آلية الاهتمام Attention
  6. https://arxiv.org/abs/1409.0473 
  7. https://arxiv.org/abs/1706.03762

الكاتب

0 Shares:
اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

You May Also Like