إنشاء نص مكون من 5 أسطر بنمط GPT بلغة Python باستخدام TensorFlow / Keras

بدأت المحولات ، على الرغم من إطلاقها في عام 2017 ، في اكتساب قوة جذب كبيرة في العامين الماضيين فقط. مع انتشار التكنولوجيا من خلال منصات مثل HuggingFace و NLP و نماذج اللغات الكبيرة (LLMs) أصبحت أكثر سهولة من أي وقت مضى.

حتى الآن - حتى مع كل الضجيج من حولهم ومعهم كثير الأدلة النظرية ، لا توجد العديد من عمليات التنفيذ المخصصة على الإنترنت ، والموارد ليست متاحة بسهولة كما هو الحال مع بعض أنواع الشبكات الأخرى ، التي كانت موجودة لفترة أطول. بينما يمكنك تبسيط دورة العمل الخاصة بك باستخدام محول مسبق الصنع من HuggingFace (موضوع دليل آخر) - يمكنك الوصول إلى شعور كيف يعمل من خلال بناء واحد بنفسك ، قبل تجريده من خلال المكتبة. سنركز هنا على البناء بدلاً من النظرية والتحسين.

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

أنواع LLMs و GPT-Fyodor

بينما يمكن أن يصبح التصنيف أكثر تعقيدًا - يمكنك ذلك بصورة عامة تصنيف نماذج اللغة القائمة على Transformer إلى ثلاث فئات:

  • النماذج القائمة على التشفير - ألبرت ، بيرت ، ديستيلبيرت ، روبرتا
  • فك القائم - GPT و GPT-2 و GPT-3 و TransformerXL
  • نماذج Seq2Seq - BART ، mBART ، T5

القائم على التشفير تستخدم النماذج فقط ترميز Transformer في بنيتها (عادةً ، مكدسة) وهي رائعة لفهم الجمل (التصنيف ، التعرف على الكيانات المسماة ، الإجابة على الأسئلة).

القائم على فك لا تستخدم النماذج سوى وحدة فك ترميز Transformer في بنيتها (عادةً ما تكون مكدسة) وهي رائعة للتنبؤ في المستقبل ، مما يجعلها مناسبة لتوليد النص.

Seq2Seq تجمع النماذج بين كل من أجهزة التشفير وأجهزة فك التشفير وهي رائعة في إنشاء النص والتلخيص والأهم من ذلك - الترجمة.

عائلة نماذج GPT ، التي اكتسبت الكثير من الجذب في العامين الماضيين ، هي نماذج محولات قائمة على وحدة فك التشفير ، وهي رائعة في إنتاج نصوص شبيهة بالبشر ، ومدرَّبة على مجموعة كبيرة من البيانات ، وتعطى دفعة باعتبارها جديدة بدء البذور للجيل. على سبيل المثال:

generate_text('the truth ultimately is')

وهو ما يغذي هذا الموجه في نموذج يشبه GPT ، وينتج:

'the truth ultimately is really a joy in history, this state of life through which is almost invisible, superfluous  teleological...'

هذا ، في الواقع ، مفسد صغير من نهاية الدليل! المفسد الصغير الآخر هو العمارة التي أنتجت هذا النص:

inputs = layers.Input(shape=(maxlen,))
embedding_layer = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
transformer_block = keras_nlp.layers.TransformerDecoder(embed_dim, num_heads)(embedding_layer)
outputs = layers.Dense(vocab_size, activation='softmax')(transformer_block)
    
model = keras.Model(inputs=inputs, outputs=outputs)

5 خطوط هي كل ما يتطلبه الأمر لبناء نموذج محول لوحدة فك التشفير فقط - محاكاة GPT صغيرة. نظرًا لأننا سنقوم بتدريب النموذج على روايات فيودور دوستويفسكي (والتي يمكنك استبدالها بأي شيء آخر ، من ويكيبيديا إلى تعليقات رديت) - سوف نسمي النموذج مبدئيًا GPT- فيودور.

KerasNLP

تكمن الحيلة في GPT-Fyodor المكون من 5 أسطر KerasNLP، الذي تم تطويره بواسطة فريق Keras الرسمي ، كامتداد أفقي لـ Keras ، والذي يهدف بطريقة Keras الحقيقية إلى جلب NLP قوة الصناعة إلى أطراف أصابعك ، مع طبقات جديدة (التشفير ، وأجهزة فك التشفير ، وحفلات الزفاف الرمزية ، وحفلات الزفاف ، والمقاييس ، الرموز المميزة ، وما إلى ذلك).

KerasNLP ليست حديقة حيوانات نموذجية. إنه جزء من Keras (كحزمة منفصلة) ، والذي يقلل من حاجز الدخول لتطوير نموذج البرمجة اللغوية العصبية ، تمامًا كما يقلل من حاجز الدخول إلى تطوير التعلم العميق العام مع الحزمة الرئيسية.

ملحوظة: حتى كتابة هذا التقرير ، لا يزال إنتاج KerasNLP قيد الإنتاج ، وفي مراحله الأولى. قد تكون الاختلافات الدقيقة موجودة في الإصدارات المستقبلية. الكتابة تستخدم الإصدار 0.3.0.

لتتمكن من استخدام KerasNLP ، سيتعين عليك تثبيته عبر pip:

$ pip install keras_nlp

ويمكنك التحقق من الإصدار باستخدام:

keras_nlp.__version__

تنفيذ نموذج GPT مع Keras

لنبدأ باستيراد المكتبات التي سنستخدمها - TensorFlow و Keras و KerasNLP و NumPy:

import tensorflow as tf
from tensorflow import keras
import keras_nlp
import numpy as np

تحميل البيانات

دعونا نحمل في عدد قليل من روايات دوستويفسكي - قد تكون إحداها أقصر من أن يلائمها النموذج ، دون قدر معقول من التجهيز من المراحل الأولى فصاعدًا. سنستخدم بأمان الملفات النصية الأولية من مشروع غوتنبرغوبسبب بساطة التعامل مع هذه البيانات:

crime_and_punishment_url = 'https://www.gutenberg.org/files/2554/2554-0.txt'
brothers_of_karamazov_url = 'https://www.gutenberg.org/files/28054/28054-0.txt'
the_idiot_url = 'https://www.gutenberg.org/files/2638/2638-0.txt'
the_possessed_url = 'https://www.gutenberg.org/files/8117/8117-0.txt'

paths = [crime_and_punishment_url, brothers_of_karamazov_url, the_idiot_url, the_possessed_url]
names = ['Crime and Punishment', 'Brothers of Karamazov', 'The Idiot', 'The Possessed']
texts = ''
for index, path in enumerate(paths):
    filepath = keras.utils.get_file(f'{names[index]}.txt', origin=path)
    text = ''
    with open(filepath, encoding='utf-8') as f:
        text = f.read()
        
        
        
        texts += text[10000:]

لقد قمنا ببساطة بتنزيل جميع الملفات وتصفحها وتسلسلها واحدة فوق الأخرى. يتضمن هذا بعض التنوع في اللغة المستخدمة ، مع الاحتفاظ بها بشكل واضح فيودور! لكل ملف ، تخطينا أول 10 آلاف حرفًا ، وهو ما يقرب من متوسط ​​طول المقدمة ومقدمة جوتنبرج ، لذلك يُترك لنا نص الكتاب بشكل كبير لكل تكرار. دعنا نلقي نظرة على حوالي 500 حرف عشوائي في ملف texts السلسلة الآن:


texts[25000:25500]
'nd that was whynI addressed you at once. For in unfolding to you the story of my life, Indo not wish to make myself a laughing-stock before these idle listeners,nwho indeed know all about it already, but I am looking for a mannof feeling and education. Know then that my wife was educated in anhigh-class school for the daughters of noblemen, and on leaving shendanced the shawl dance before the governor and other personages fornwhich she was presented with a gold medal and a certificate of merit.n'

دعنا نفصل السلسلة إلى جمل قبل القيام بأي معالجة أخرى:

text_list = texts.split('.')
len(text_list) 

لدينا 69 ألف جملة. عندما تقوم باستبدال ملف n أحرف بمسافات بيضاء وعد الكلمات:

len(texts.replace('n', ' ').split(' ')) 

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

ومع ذلك ، دعنا نقسم هذه إلى ملف السلامه اولا, تجربه بالعربي و التحقق من صحة تعيين. أولاً ، دعنا نزيل السلاسل الفارغة ونقوم بترتيب الجمل:


text_list = list(filter(None, text_list))

import random
random.shuffle(text_list)

بعد ذلك ، سنفصل 70/15/15:

length = len(text_list)
text_train = text_list[:int(0.7*length)]
text_test = text_list[int(0.7*length):int(0.85*length)]
text_valid = text_list[int(0.85*length):]

هذه طريقة بسيطة وفعالة لإجراء تقسيم اختبار التدريب والتحقق من الصحة. دعونا نلقي نظرة خاطفة على text_train:

[' It was a dull morning, but the snow had ceased',
 'nn"Pierre, you who know so much of what goes on here, can you really havenknown nothing of this business and have heard nothing about it?"nn"What? What a set! So it's not enough to be a child in your old age,nyou must be a spiteful child too! Varvara Petrovna, did you hear what hensaid?"nnThere was a general outcry; but then suddenly an incident took placenwhich no one could have anticipated', ...

حان الوقت للتوحيد القياسي والتوجيه!

تحويل النص

لا تفهم الشبكات الكلمات - فهم يفهمون الأرقام. سنرغب في ترميز الكلمات:

...
sequence = ['I', 'am', 'Wall-E']
sequence = tokenize(sequence)
print(sequence) # [4, 26, 472]
...

أيضًا ، نظرًا لاختلاف الجمل في الطول - تتم إضافة المساحة المتروكة عادةً إلى اليسار أو اليمين لضمان نفس الشكل عبر الجمل التي يتم تغذيتها. لنفترض أن أطول جملة لدينا تتكون من 5 كلمات (الرموز المميزة). في هذه الحالة ، ستكون جملة Wall-E مبطنًا بصفرين لذلك نضمن نفس شكل الإدخال:

sequence = pad_sequence(sequence)
print(sequence) # [4, 26, 472, 0, 0]

تقليديا ، تم ذلك باستخدام TensorFlow Tokenizer وكيراس pad_sequences() طرق - ومع ذلك ، طبقة أكثر فائدة ، TextVectorization، التي يمكن استخدامها ، والتي رمزية و يبطن مدخلاتك ، مما يسمح لك باستخراج المفردات وحجمها ، دون معرفة المفردات مقدمًا!

تحقق من دليلنا العملي العملي لتعلم Git ، مع أفضل الممارسات ، والمعايير المقبولة في الصناعة ، وورقة الغش المضمنة. توقف عن أوامر Googling Git وفي الواقع تعلم ذلك!

دعونا نتكيف ونناسب أ TextVectorization طبقة:

from tensorflow.keras.layers import TextVectorization

def custom_standardization(input_string):
    sentence = tf.strings.lower(input_string)
    sentence = tf.strings.regex_replace(sentence, "n", " ")
    return sentence

maxlen = 50



vectorize_layer = TextVectorization(
    standardize = custom_standardization,
    output_mode="int",
    output_sequence_length=maxlen + 1,
)

vectorize_layer.adapt(text_list)
vocab = vectorize_layer.get_vocabulary()

custom_standardization() الطريقة يمكن أن تحصل على وقت أطول بكثير من هذا. لقد قللنا ببساطة جميع المدخلات واستبدلناها n مع " ". هذا هو المكان الذي يمكنك فيه حقًا وضع معظم المعالجات المسبقة للنص - وتزويده بطبقة Vector من خلال الخيار الاختياري standardize جدال. بمجرد adapt() طبقة النص (مصفوفة NumPy أو قائمة النصوص) - يمكنك الحصول على المفردات وحجمها من هناك:

vocab_size = len(vocab)
vocab_size 

أخيرًا ، لإزالة الرموز المميزة للكلمات ، سننشئ امتدادًا index_lookup قاموس:

index_lookup = dict(zip(range(len(vocab)), vocab))    
index_lookup[5] 

يقوم بتعيين جميع الرموز المميزة ([1, 2, 3, 4, ...]) إلى كلمات في المفردات (['a', 'the', 'i', ...]). من خلال تمرير مفتاح (فهرس الرمز المميز) ، يمكننا بسهولة استعادة الكلمة. يمكنك الآن تشغيل ملف vectorize_layer() على أي مدخلات ولاحظ الجمل الموجهة:

vectorize_layer(['hello world!'])

مما يؤدي إلى:

<tf.Tensor: shape=(1, 51), dtype=int64, numpy=
array([[   1, 7509,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0]], dtype=int64)>

مرحبًا به فهرس 1 بينما العالم لديه فهرس 7509! الباقي هو الحشو ل maxlen حسبنا.

لدينا الوسائل لتحويل النص - الآن ، دعنا ننشئ مجموعات بيانات من text_train, text_test و text_valid، باستخدام طبقة التحويل الخاصة بنا كوسيط تحويل بين الكلمات والمتجهات التي يمكن إدخالها في GPT-Fyodor.

إنشاء مجموعة البيانات

سنقوم بإنشاء ملف tf.data.Dataset لكل مجموعة لدينا ، باستخدام from_tensor_slices() وتقديم قائمة ، حسناً ، شرائح الموتر (الجمل):

batch_size = 64

train_dataset = tf.data.Dataset.from_tensor_slices(text_train)
train_dataset = train_dataset.shuffle(buffer_size=256)
train_dataset = train_dataset.batch(batch_size)

test_dataset = tf.data.Dataset.from_tensor_slices(text_test)
test_dataset = test_dataset.shuffle(buffer_size=256)
test_dataset = test_dataset.batch(batch_size)

valid_dataset = tf.data.Dataset.from_tensor_slices(text_valid)
valid_dataset = valid_dataset.shuffle(buffer_size=256)
valid_dataset = valid_dataset.batch(batch_size)

بمجرد الإنشاء والتعديل (مرة أخرى ، من أجل إجراء جيد) - يمكننا تطبيق وظيفة المعالجة المسبقة (تحويل الاتجاه وتقسيم التسلسل):

def preprocess_text(text):
    text = tf.expand_dims(text, -1)
    tokenized_sentences = vectorize_layer(text)
    x = tokenized_sentences[:, :-1]
    y = tokenized_sentences[:, 1:]
    return x, y


train_dataset = train_dataset.map(preprocess_text)
train_dataset = train_dataset.prefetch(tf.data.AUTOTUNE)

test_dataset = test_dataset.map(preprocess_text)
test_dataset = test_dataset.prefetch(tf.data.AUTOTUNE)

valid_dataset = valid_dataset.map(preprocess_text)
valid_dataset = valid_dataset.prefetch(tf.data.AUTOTUNE)

preprocess_text() تتوسع الدالة ببساطة حسب البعد الأخير ، وتوجه النص باستخدام vectorize_layer وينشئ المدخلات والأهداف ، يقابلها رمز واحد. سيستخدم النموذج [0..n] لاستنتاج n+1، مما ينتج عنه تنبؤ لكل كلمة ، مع مراعاة جميع الكلمات قبل ذلك. دعنا نلقي نظرة على إدخال واحد في أي من مجموعات البيانات:

for entry in train_dataset.take(1):
    print(entry)

بالتحقيق في المدخلات والأهداف التي تم إرجاعها ، على دفعات من 64 (بطول 30 لكل منها) ، يمكننا أن نرى بوضوح كيف يتم تعويضها بواحد:

(<tf.Tensor: shape=(64, 50), dtype=int64, numpy=
array([[17018,   851,     2, ...,     0,     0,     0],
       [  330,    74,     4, ...,     0,     0,     0],
       [   68,   752, 30273, ...,     0,     0,     0],
       ...,
       [    7,    73,  2004, ...,     0,     0,     0],
       [   44,    42,    67, ...,     0,     0,     0],
       [  195,   252,   102, ...,     0,     0,     0]], dtype=int64)>, <tf.Tensor: shape=(64, 50), dtype=int64, numpy=
array([[  851,     2,  8289, ...,     0,     0,     0],
       [   74,     4,    34, ...,     0,     0,     0],
       [  752, 30273,  7514, ...,     0,     0,     0],
       ...,
       [   73,  2004,    31, ...,     0,     0,     0],
       [   42,    67,    76, ...,     0,     0,     0],
       [  252,   102,  8596, ...,     0,     0,     0]], dtype=int64)>)

أخيرًا - حان الوقت لبناء النموذج!

تعريف النموذج

سنستخدم طبقات KerasNLP هنا. بعد أن Input، سنقوم بترميز الإدخال من خلال ملف TokenAndPositionEmbedding طبقة تمر في vocab_size, maxlen و embed_dim. نفس الشيء embed_dim أن هذه الطبقة مخرجات ومدخلات في TransformerDecoder سوف يكون محتجزة في وحدة فك الترميز. اعتبارًا من الكتابة ، يحافظ Decoder تلقائيًا على أبعاد الإدخال ، ولا يسمح لك بإسقاطه في إخراج مختلف ، ولكنه يتيح لك تحديد الأبعاد الكامنة من خلال intermediate_dim جدال.

سنضرب أبعاد التضمين في اثنين للتمثيل الكامن ، ولكن يمكنك الاحتفاظ بها كما هي أو استخدام رقم منفصل عن التعتيم التضمين:

embed_dim = 128
num_heads = 4

def create_model():
    inputs = keras.layers.Input(shape=(maxlen,), dtype=tf.int32)
    embedding_layer = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
    decoder = keras_nlp.layers.TransformerDecoder(intermediate_dim=embed_dim, 
                                                            num_heads=num_heads, 
                                                            dropout=0.5)(embedding_layer)
    
    outputs = keras.layers.Dense(vocab_size, activation='softmax')(decoder)
    
    model = keras.Model(inputs=inputs, outputs=outputs)
    
    model.compile(
        optimizer="adam", 
        loss='sparse_categorical_crossentropy',
        metrics=[keras_nlp.metrics.Perplexity(), 'accuracy']
    )
    return model

model = create_model()
model.summary()

في الجزء العلوي من وحدة فك التشفير ، لدينا ملف Dense طبقة لاختيار الكلمة التالية في التسلسل ، بامتداد softmax التنشيط (الذي ينتج التوزيع الاحتمالي لكل رمز مميز تالٍ). دعنا نلقي نظرة على ملخص النموذج:

Model: "model_5"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_6 (InputLayer)        [(None, 30)]              0         
                                                                 
 token_and_position_embeddin  (None, 30, 128)          6365824   
 g_5 (TokenAndPositionEmbedd                                     
 ing)                                                            
                                                                 
 transformer_decoder_5 (Tran  (None, 30, 128)          132480    
 sformerDecoder)                                                 
                                                                 
 dense_5 (Dense)             (None, 30, 49703)         6411687   
                                                                 
=================================================================
Total params: 13,234,315
Trainable params: 13,234,315
Non-trainable params: 0
_________________________________________________________________

تكدس GPT-2 العديد من وحدات فك التشفير - تحتوي GPT-2 Small على 12 وحدة فك ترميز مكدسة (117M بارامز) ، بينما تحتوي GPT-2 Extra Large على 48 وحدة فك ترميز مكدسة (1.5B بارامترات). يجب أن يعمل نموذج وحدة فك التشفير المفرد الخاص بنا مع معلمات متواضعة 13M بشكل جيد بما يكفي للأغراض التعليمية. باستخدام LLMs - أثبت التوسع أنه استراتيجية جيدة للغاية ، وتسمح المحولات بالتوسع الجيد ، مما يجعل من الممكن تدريب النماذج الكبيرة للغاية.

يحتوي GPT-3 على ملف "ضئيل" معلمات 175B. قام فريق Google Brain بتدريب نموذج معلمة 1.6T لإجراء بحث متباين مع الحفاظ على الحساب على نفس المستوى مثل النماذج الأصغر بكثير.

في واقع الأمر ، إذا قمنا بزيادة عدد أجهزة فك التشفير من 1 إلى 3:

def create_model():
    inputs = keras.layers.Input(shape=(maxlen,), dtype=tf.int32)
    x = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
    for i in range(4):
        x = keras_nlp.layers.TransformerDecoder(intermediate_dim=embed_dim*2, num_heads=num_heads,                                                             dropout=0.5)(x)
    do = keras.layers.Dropout(0.4)(x)
    outputs = keras.layers.Dense(vocab_size, activation='softmax')(do)
    
    model = keras.Model(inputs=inputs, outputs=outputs)

سيتم زيادة عدد المعلمات لدينا بمقدار 400 كيلو:

Total params: 13,631,755
Trainable params: 13,631,755
Non-trainable params: 0

تأتي معظم المعلمات في شبكتنا من TokenAndPositionEmbedding و Dense طبقات!

جرب أعماق مختلفة لوحدة فك التشفير - من 1 إلى كل الطرق التي يمكن أن يتعامل بها جهازك مع النتائج. على أي حال - نحن جاهزون تقريبًا لتدريب النموذج! لنقم بإنشاء رد اتصال مخصص سينتج عينة من النص في كل فترة ، حتى نتمكن من رؤية كيف يتعلم النموذج تكوين الجمل من خلال التدريب.

رد الاتصال المخصص

class TextSampler(keras.callbacks.Callback):
    def __init__(self, start_prompt, max_tokens):
        self.start_prompt = start_prompt
        self.max_tokens = max_tokens
        
    
    
    def sample_token(self, logits):
        logits, indices = tf.math.top_k(logits, k=5, sorted=True)
        indices = np.asarray(indices).astype("int32")
        preds = keras.activations.softmax(tf.expand_dims(logits, 0))[0]
        preds = np.asarray(preds).astype("float32")
        return np.random.choice(indices, p=preds)

    def on_epoch_end(self, epoch, logs=None):
        decoded_sample = self.start_prompt
        
        for i in range(self.max_tokens-1):
            tokenized_prompt = vectorize_layer([decoded_sample])[:, :-1]
            predictions = self.model.predict([tokenized_prompt], verbose=0)
            
            
            
            
            sample_index = len(decoded_sample.strip().split())-1
            
            sampled_token = self.sample_token(predictions[0][sample_index])
            sampled_token = index_lookup[sampled_token]
            decoded_sample += " " + sampled_token
            
        print(f"nSample text:n{decoded_sample}...n")


random_sentence = ' '.join(random.choice(text_valid).replace('n', ' ').split(' ')[:4])
sampler = TextSampler(random_sentence, 30)
reducelr = keras.callbacks.ReduceLROnPlateau(patience=10, monitor='val_loss')

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

أخيرًا ، حان وقت التدريب! دعونا نشرب train_dataset و validation_dataset مع وجود عمليات الاسترجاعات:

model = create_model()
history = model.fit(train_dataset, 
                    validation_data=valid_dataset,
                    epochs=10, 
                    callbacks=[sampler, reducelr])

اختار القائم بأخذ العينات جملة مؤسفة تبدأ بالاقتباس الختامي والاقتباس الأولي ، لكنها لا تزال تنتج نتائج مثيرة للاهتمام أثناء التدريب:

# Epoch training
Epoch 1/10
658/658 [==============================] - ETA: 0s - loss: 2.7480 - perplexity: 15.6119 - accuracy: 0.6711
# on_epoch_end() sample generation
Sample text:
”  “What do you had not been i had been the same man was not be the same eyes to been a whole man and he did a whole man to the own...
# Validation
658/658 [==============================] - 158s 236ms/step - loss: 2.7480 - perplexity: 15.6119 - accuracy: 0.6711 - val_loss: 2.2130 - val_perplexity: 9.1434 - val_accuracy: 0.6864 - lr: 0.0010
...
Sample text:
”  “What do you know it is it all this very much as i should not have a great impression  in the room to be  able of it in my heart...

658/658 [==============================] - 149s 227ms/step - loss: 1.7753 - perplexity: 5.9019 - accuracy: 0.7183 - val_loss: 2.0039 - val_perplexity: 7.4178 - val_accuracy: 0.7057 - lr: 0.0010

يبدأ بـ:

"ما لم أكن قد كنت عليه" ...

الأمر الذي لا معنى له حقًا. بحلول نهاية العهود العشر القصيرة ، ينتج شيئًا على غرار:

"ماذا تقصد أن هذا هو أكثر الرجال عاديين بالطبع" ...

في حين أن الجملة الثانية لا تزال غير منطقية - إنها أكثر منطقية من الأولى. التدريب الأطول على المزيد من البيانات (مع خطوات معالجة أكثر تعقيدًا) من شأنه أن يؤدي إلى نتائج أفضل. لقد قمنا بتدريبه فقط على 10 فترات مع ارتفاع نسبة الانقطاع عن الدراسة لمكافحة الحجم الصغير لمجموعة البيانات. إذا تم تركه يتدرب لفترة أطول ، فإنه سينتج نصًا شبيهًا جدًا بفيودور ، لأنه كان سيحفظ أجزاء كبيرة منه.

ملحوظة: نظرًا لأن الإخراج مطول إلى حد ما ، يمكنك تعديل ملف verbose الحجة أثناء ملاءمة النموذج لتقليل مقدار النص على الشاشة.

نموذج الاستدلال

لإجراء الاستدلال ، سنرغب في تكرار واجهة ملف TextSampler - طريقة تقبل بذرة و response_length (max_tokens). سنستخدم نفس الطرق المستخدمة في جهاز أخذ العينات:

def sample_token(logits):
        logits, indices = tf.math.top_k(logits, k=5, sorted=True)
        indices = np.asarray(indices).astype("int32")
        preds = keras.activations.softmax(tf.expand_dims(logits, 0))[0]
        preds = np.asarray(preds).astype("float32")
        return np.random.choice(indices, p=preds)

def generate_text(prompt, response_length=20):
    decoded_sample = prompt
    for i in range(response_length-1):
        tokenized_prompt = vectorize_layer([decoded_sample])[:, :-1]
        predictions = model.predict([tokenized_prompt], verbose=0)
        sample_index = len(decoded_sample.strip().split())-1

        sampled_token = sample_token(predictions[0][sample_index])
        sampled_token = index_lookup[sampled_token]
        decoded_sample += " " + sampled_token
    return decoded_sample

الآن ، يمكنك تشغيل الطريقة على عينات جديدة:

generate_text('the truth ultimately is')


generate_text('the truth ultimately is')

تحسين النتائج؟

لذا ، كيف يمكنك تحسين النتائج؟ هناك بعض الأشياء القابلة للتنفيذ التي يمكنك القيام بها:

  • تنظيف البيانات (قم بتنظيف بيانات الإدخال بدقة أكبر ، لقد قطعنا للتو رقمًا تقريبيًا من البداية وأزلنا أحرف السطر الجديد)
  • احصل على المزيد من البيانات (عملنا فقط مع بضع ميغا بايت من البيانات النصية)
  • مقياس النموذج إلى جانب البيانات (تكديس وحدات فك التشفير ليس بالأمر الصعب!)

وفي الختام

في حين أن خط أنابيب المعالجة المسبقة هو أضيق الحدود ويمكن تحسينه - أنتج خط الأنابيب الموضح في هذا الدليل نموذجًا لائقًا على غرار GPT ، مع وجود 5 أسطر فقط من التعليمات البرمجية المطلوبة لبناء محول مخصص لوحدة فك التشفير فقط ، باستخدام Keras!

المحولات شائعة وقابلة للتطبيق على نطاق واسع لنمذجة التسلسل العام (ويمكن التعبير عن العديد من الأشياء على شكل تسلسلات). حتى الآن ، كان العائق الرئيسي أمام الدخول هو التنفيذ المرهق ، ولكن مع KerasNLP - يمكن لممارسي التعلم العميق الاستفادة من التطبيقات لبناء النماذج بسرعة وسهولة.

الطابع الزمني:

اكثر من ستاكابوز