تولید متن 5 خطی به سبک GPT در پایتون با TensorFlow/Keras

ترانسفورمرز، حتی با وجود اینکه در سال 2017 منتشر شد، تنها در دو سال گذشته شروع به جذب قابل توجهی کرده است. با گسترش فناوری از طریق پلتفرم هایی مانند HuggingFace، NLP و مدل‌های زبان بزرگ (LLM) در دسترس تر از همیشه شده اند.

با این حال - حتی با همه تبلیغات اطراف آنها و با بسیاری راهنماهای تئوری گرا، پیاده سازی های سفارشی زیادی به صورت آنلاین وجود ندارد، و منابع به آسانی مانند برخی از انواع شبکه های دیگر که مدت طولانی تری وجود داشته اند، در دسترس نیستند. در حالی که می توانید چرخه کاری خود را با استفاده از ترانسفورماتور از پیش ساخته شده از HuggingFace (موضوع راهنمای دیگری) ساده کنید - می توانید به احساس چگونه کار می کند با ساختن یکی از خودتان، قبل از انتزاع آن از طریق یک کتابخانه. ما در اینجا به جای تئوری و بهینه سازی، بر ساختن تمرکز خواهیم کرد.

در این راهنما، ما در حال ساختن یک مدل زبان خودرگرسیون به تولید متن. ما بر جنبه‌های عملی و حداقلی/مختصر بارگذاری داده‌ها، تقسیم آن، بردار کردن آن، ساختن یک مدل، نوشتن یک تماس سفارشی و آموزش/استنتاج تمرکز خواهیم کرد. هر یک از این وظایف را می توان به راهنمای دقیق تری تقسیم کرد، بنابراین ما پیاده سازی را به عنوان یک دستورالعمل عمومی نگه می داریم و بسته به مجموعه داده شما، فضایی را برای سفارشی سازی و بهینه سازی باقی می گذاریم.

انواع LLM و GPT-Fyodor

در حالی که طبقه بندی می تواند بسیار پیچیده تر شود - شما می توانید گسترده مدل های زبان مبتنی بر Transformer را به سه دسته دسته بندی کنید:

  • مدل های مبتنی بر رمزگذار – آلبرت، برت، دیستیلبرت، روبرتا
  • مبتنی بر رمزگشا – GPT، GPT-2، GPT-3، TransformerXL
  • مدل های Seq2Seq - BART، mBART، T5

مبتنی بر رمزگذار مدل‌ها فقط از رمزگذار ترانسفورماتور در معماری خود استفاده می‌کنند (معمولاً پشته‌ای) و برای درک جملات (طبقه‌بندی، شناسایی موجودیت نام‌گذاری شده، پاسخ به سؤال) عالی هستند.

مبتنی بر رمزگشا مدل‌ها فقط از رمزگشای ترانسفورماتور در معماری خود استفاده می‌کنند (همچنین معمولاً انباشته شده) و برای پیش‌بینی آینده عالی هستند، که آنها را برای تولید متن مناسب می‌کند.

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)

برای ساختن یک مدل ترانسفورماتور فقط رمزگشا - شبیه سازی یک GPT کوچک، 5 خط کافی است. از آنجایی که ما مدل را بر روی رمان‌های فئودور داستایوفسکی آموزش خواهیم داد (که می‌توانید آن را با هر چیز دیگری جایگزین کنید، از ویکی‌پدیا گرفته تا نظرات ردیت) - به طور آزمایشی مدل را صدا می‌کنیم. GPT-Fyodor.

KerasNLP

ترفند یک GPT-Fyodor 5 خطی در این است KerasNLP، که توسط تیم رسمی Keras توسعه یافته است، به عنوان یک گسترش افقی برای Keras، که به شیوه واقعی Keras، هدف آن رساندن NLP با قدرت صنعتی به نوک انگشتان شماست، با لایه های جدید (رمزگذارها، رمزگشاها، جاسازی های توکن، جاسازی موقعیت، معیارها، توکنایزرها و غیره).

KerasNLP یک باغ وحش نمونه نیست. این بخشی از Keras (به عنوان یک بسته جداگانه) است که مانع ورود برای توسعه مدل NLP را کاهش می دهد، همانطور که مانع ورود برای توسعه یادگیری عمیق عمومی با بسته اصلی را کاهش می دهد.

توجه داشته باشید: تا زمان نگارش KerasNLP هنوز در حال تولید و در مراحل اولیه است. تفاوت های ظریف ممکن است در نسخه های بعدی وجود داشته باشد. نوشتن از نسخه استفاده می کند 0.3.0.

برای اینکه بتوانید از KerasNLP استفاده کنید، باید آن را از طریق نصب کنید pip:

$ pip install keras_nlp

و شما می توانید نسخه را با:

keras_nlp.__version__

پیاده سازی مدل GPT-Style با 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 را با بهترین روش ها، استانداردهای پذیرفته شده در صنعت و برگه تقلب شامل بررسی کنید. دستورات Google 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()

La custom_standardization() روش می تواند بسیار طولانی تر از این باشد. ما به سادگی تمام ورودی ها را کوچک کرده و جایگزین کرده ایم n با " ". اینجاست که می‌توانید بیشتر پیش‌پردازش خود را برای متن قرار دهید - و آن را از طریق گزینه اختیاری به لایه برداری ارائه کنید. 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)

La 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، ورودی را از طریق a رمزگذاری می کنیم TokenAndPositionEmbedding لایه، عبور در ما vocab_size, maxlen و embed_dim. همان embed_dim که این لایه خروجی و ورودی به TransformerDecoder خواهد بود در رسیور نگهداری می شود. در زمان نوشتن، رسیور به طور خودکار ابعاد ورودی را حفظ می کند، و به شما اجازه نمی دهد آن را در خروجی دیگری قرار دهید، اما به شما اجازه می دهد ابعاد پنهان را از طریق 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 لایه برای انتخاب کلمه بعدی در دنباله، با a 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 باید برای اهداف آموزشی به اندازه کافی خوب کار کند. با LLM ها - ثابت شده است که افزایش مقیاس یک استراتژی بسیار خوب است، و ترانسفورماتورها اجازه مقیاس بندی خوب را می دهند و آموزش مدل های بسیار بزرگ را امکان پذیر می کند.

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)

تعداد پارامترهای ما 400k افزایش می یابد:

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

بیشتر پارامترهای شبکه ما از TokenAndPositionEmbedding و Dense لایه های!

اعماق مختلف رمزگشا را امتحان کنید - از 1 تا تمام راه هایی که دستگاه شما می تواند کار کند و نتایج را یادداشت کنید. در هر صورت - ما تقریباً آماده آموزش مدل هستیم! بیایید یک callback سفارشی ایجاد کنیم که نمونه‌ای از متن را در هر دوره تولید می‌کند، بنابراین می‌توانیم ببینیم که مدل چگونه از طریق آموزش جملات را می‌آموزد.

پاسخ به تماس سفارشی

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 - تمرین کنندگان یادگیری عمیق می توانند از پیاده سازی ها برای ساخت سریع و آسان مدل ها استفاده کنند.

تمبر زمان:

بیشتر از Stackabuse