יצירת טקסט בסגנון GPT 5 שורות ב-Python עם TensorFlow/Keras

רובוטריקים, למרות שיצאו בשנת 2017, החלו לצבור אחיזה משמעותית רק בשנתיים האחרונות. עם התפשטות הטכנולוגיה באמצעות פלטפורמות כמו HuggingFace, NLP ו מודלים של שפה גדולה (LLMs) הפכו נגישים מתמיד.

ובכל זאת - אפילו עם כל ההייפ סביבם ואיתם רב מדריכים מוכווני תיאוריה, אין הרבה יישומים מותאמים אישית באינטרנט, והמשאבים אינם זמינים כמו בכמה סוגי רשתות אחרים, שקיימים זמן רב יותר. אמנם אתה יכול לפשט את מחזור העבודה שלך על ידי שימוש בשנאי בנוי מראש מבית HuggingFace (הנושא של מדריך אחר) - אתה יכול להגיע אל להרגיש איך זה עובד על ידי בניית אחד בעצמך, לפני הפשטתו דרך ספרייה. אנו נתמקד כאן בבנייה, במקום בתיאוריה ובאופטימיזציה.

במדריך זה, נבנה א מודל שפה אוטורגרסיבי ל ליצור טקסט. אנו נתמקד בהיבטים המעשיים והמינימליסטיים/תמציתיים של טעינת נתונים, פיצולם, וקטוריזציה שלהם, בניית מודל, כתיבת התקשרות חוזרת מותאמת אישית והדרכה/הסקה. ניתן לחלק כל אחת מהמשימות הללו למדריכים מפורטים יותר, לכן נשאיר את היישום כגנרי, ונותיר מקום להתאמה אישית ואופטימיזציה בהתאם למערך הנתונים שלך.

סוגי LLMs ו-GPT-Fyodor

בעוד שסיווג יכול להיות הרבה יותר מורכב - אתה יכול בְּהַרְחָבָה סווגו מודלים של שפה מבוססי רובוטריקים לשלוש קטגוריות:

  • מודלים מבוססי מקודדים – ALBERT, BERT, DistilBERT, Roberta
  • מבוסס על מפענח - 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 קטן. מכיוון שאנו נלמד את הדוגמנית על הרומנים של פיודור דוסטויבסקי (שאותם תוכל להחליף בכל דבר אחר, מוויקיפדיה ועד להערות Reddit) - נכנה את המודל בהיסוס GPT-פיודור.

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(' ')) 

הערה: בדרך כלל תרצה שיהיו לפחות מיליון מילים במערך נתונים, ובאופן אידיאלי, הרבה הרבה יותר מזה. אנו עובדים עם כמה מגה-בייט של נתונים (~5MB) בעוד שמודלים של שפה מאומנים בדרך כלל על עשרות גיגה-בייט של טקסט. זה, באופן טבעי, יקל מאוד על התאמה יתר של קלט הטקסט וקשה להכליל (תמיהה גבוהה ללא התאמה יתרה, או תמיהה נמוכה עם הרבה התאמה יתרה). קח את התוצאות עם גרגר מלח.

עם זאת, בואו נחלק את אלה ל-a הַדְרָכָה, מבחן ו אימות מַעֲרֶכֶת. ראשית, הבה נסיר את המחרוזות הריקות ונערבב את המשפטים:


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, עם שיטות עבודה מומלצות, סטנדרטים מקובלים בתעשייה ודף רמאות כלול. תפסיק לגוגל פקודות 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 עם " ". זה המקום שבו אתה באמת יכול להכניס את רוב העיבוד המקדים שלך לטקסט - ולספק אותו לשכבת הוקטוריזציה באמצעות האופציונלי 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, נקודד את הקלט באמצעות 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 אמור לעבוד מספיק טוב למטרות חינוכיות. עם 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)

ספירת הפרמטרים שלנו תגדל ב-400k:

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 - מתרגלי למידה עמוקה יכולים למנף את ההטמעות לבניית מודלים במהירות ובקלות.

בול זמן:

עוד מ Stackabuse