5-regelige GPT-stijl tekstgeneratie in Python met TensorFlow/Keras

Transformers, hoewel ze in 2017 zijn uitgebracht, zijn de afgelopen jaren pas echt populair geworden. Met de verspreiding van de technologie via platforms zoals HuggingFace, NLP en Grote taalmodellen (LLM's) toegankelijker dan ooit zijn geworden.

Maar toch - zelfs met alle hype om hen heen en met veel theorie-georiรซnteerde handleidingen, er zijn niet veel aangepaste implementaties online en de bronnen zijn niet zo gemakkelijk beschikbaar als bij sommige andere netwerktypes, die al langer bestaan. Hoewel u uw werkcyclus kunt vereenvoudigen door een vooraf gebouwde Transformer van HuggingFace (het onderwerp van een andere handleiding) te gebruiken, kunt u voelen hoe het werkt door er zelf een te bouwen, voordat je het abstraheert door een bibliotheek. We zullen ons hier concentreren op bouwen in plaats van theorie en optimalisatie.

In deze handleiding bouwen we een Autoregressief taalmodel naar tekst genereren. We zullen ons concentreren op de praktische en minimalistische/beknopte aspecten van het laden van gegevens, het splitsen, vectoriseren, het bouwen van een model, het schrijven van een aangepaste callback en training/inferentie. Elk van deze taken kan worden omgezet in meer gedetailleerde handleidingen, dus we houden de implementatie als een generieke, zodat er ruimte is voor aanpassing en optimalisatie, afhankelijk van uw eigen dataset.

Soorten LLM's en GPT-Fyodor

Hoewel categorisatie veel ingewikkelder kan worden, kunt u dat wel breed categoriseer op Transformer gebaseerde taalmodellen in drie categorieรซn:

  • Encoder-gebaseerde modellen โ€“ ALBERT, BERT, DistillBERT, RoBERTa
  • Op decoder gebaseerd โ€“ GPT, GPT-2, GPT-3, TransformatorXL
  • Seq2Seq-modellen โ€“ BART, mBART, T5

Encoder-gebaseerd modellen gebruiken alleen een Transformer-encoder in hun architectuur (meestal gestapeld) en zijn geweldig voor het begrijpen van zinnen (classificatie, benoemde entiteitsherkenning, vragen beantwoorden).

Op decoder gebaseerd modellen gebruiken alleen een Transformer-decoder in hun architectuur (ook meestal gestapeld) en zijn geweldig voor toekomstige voorspellingen, waardoor ze geschikt zijn voor het genereren van tekst.

Seq2Seq modellen combineren zowel encoders als decoders en zijn geweldig in het genereren, samenvatten en vooral vertalen van tekst.

De GPT-modellenfamilie, die de afgelopen jaren veel tractie heeft gekregen, zijn op decoders gebaseerde transformatormodellen en zijn geweldig in het produceren van mensachtige tekst, getraind op grote hoeveelheden gegevens en een prompt gegeven als een nieuwe zaad voor generatie beginnen. Bijvoorbeeld:

generate_text('the truth ultimately is')

Die onder de motorkap deze prompt in een GPT-achtig model invoert en produceert:

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

Dit is in feite een kleine spoiler aan het einde van de gids! Een andere kleine spoiler is de architectuur die die tekst produceerde:

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 regels is alles wat nodig is om een โ€‹โ€‹transformatormodel met alleen decoder te bouwen - een kleine GPT simuleren. Aangezien we het model zullen trainen op de romans van Fjodor Dostojevski (die je kunt vervangen door iets anders, van Wikipedia tot Reddit-opmerkingen) - zullen we het model voorlopig noemen GPT-Fjodor.

KerasNLP

De truc voor een 5-lijns GPT-Fyodor ligt in: KerasNLP, dat is ontwikkeld door het officiรซle Keras-team, als een horizontale uitbreiding op Keras, dat op ware Keras-manier tot doel heeft om industrie-sterke NLP binnen handbereik te brengen, met nieuwe lagen (encoders, decoders, token-inbeddingen, position-inbeddingen, metrieken, tokenizers, enz.).

KerasNLP is geen modeldierentuin. Het is een onderdeel van Keras (als een apart pakket), dat de toetredingsdrempel voor de ontwikkeling van NLP-modellen verlaagt, net zoals het de toetredingsdrempel voor algemene ontwikkeling van diep leren met het hoofdpakket verlaagt.

Opmerking: Op het moment van schrijven wordt KerasNLP nog steeds geproduceerd, en in een vroeg stadium. In toekomstige versies kunnen subtiele verschillen aanwezig zijn. De beschrijving gebruikt versie 0.3.0.

Om KerasNLP te kunnen gebruiken, moet je het installeren via pip:

$ pip install keras_nlp

En u kunt de versie verifiรซren met:

keras_nlp.__version__

Een model in GPT-stijl implementeren met Keras

Laten we beginnen met het importeren van de bibliotheken die we gaan gebruiken - TensorFlow, Keras, KerasNLP en NumPy:

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

Data laden

Laten we een paar van Dostojevski's romans inladen - een zou veel te kort zijn voor een model om te passen, zonder een behoorlijke hoeveelheid overfitting vanaf de vroege stadia. We zullen gracieus de onbewerkte tekstbestanden gebruiken van Project Gutenberg, vanwege de eenvoud van het werken met dergelijke gegevens:

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:]

We hebben gewoon alle bestanden gedownload, ze doorgenomen en op elkaar gezet. Dit omvat enige diversiteit in de gebruikte taal, terwijl het toch duidelijk Fjodor blijft! Voor elk bestand hebben we de eerste 10 tekens overgeslagen, wat ongeveer de gemiddelde lengte is van het voorwoord en de Gutenberg-intro, dus we hebben een grotendeels intacte hoofdtekst van het boek voor elke iteratie. Laten we eens kijken naar enkele willekeurige 500 tekens in de texts teken nu:


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'

Laten we de tekenreeks in zinnen scheiden voordat we andere bewerkingen uitvoeren:

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

We hebben 69k zinnen. Wanneer u de vervangt n tekens met spaties en tel de woorden:

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

Opmerking: Over het algemeen wilt u minimaal een miljoen woorden in een dataset hebben, en idealiter veel meer dan dat. We werken met een paar megabytes aan gegevens (~5 MB), terwijl taalmodellen vaker worden getraind op tientallen gigabytes aan tekst. Dit maakt het natuurlijk heel gemakkelijk om de tekstinvoer te overfitten en moeilijk te generaliseren (hoge perplexiteit zonder overfitting, of lage perplexiteit met veel overfitting). Neem de resultaten met een korreltje zout.

Laten we deze echter opsplitsen in a opleiding, proef en bevestiging set. Laten we eerst de lege strings verwijderen en de zinnen door elkaar schudden:


text_list = list(filter(None, text_list))

import random
random.shuffle(text_list)

Vervolgens maken we een verdeling van 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):]

Dit is een eenvoudige, maar effectieve manier om een โ€‹โ€‹splitsing tussen trein-test-validatie uit te voeren. Laten we eens kijken naar 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', ...

Tijd voor standaardisatie en vectorisatie!

Tekstvectorisatie

Netwerken begrijpen geen woorden - ze begrijpen cijfers. We willen de woorden tokeniseren:

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

Omdat zinnen in lengte verschillen, wordt er meestal links of rechts opvulling toegevoegd om ervoor te zorgen dat alle zinnen die worden ingevoerd dezelfde vorm hebben. Stel dat onze langste zin 5 woorden (tokens) lang is. In dat geval zou de Wall-E-zin worden opgevuld met twee nullen, dus we zorgen voor dezelfde invoervorm:

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

Traditioneel werd dit gedaan met behulp van een TensorFlow Tokenizer en Keras' pad_sequences() methoden - echter een veel handigere laag, TextVectorization, kan worden gebruikt, wat tokeniseert en vult uw invoer in, zodat u het vocabulaire en de grootte ervan kunt extraheren, zonder de vocab vooraf te kennen!

Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!

Laten we ons aanpassen en passen TextVectorization laag:

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

De custom_standardization() methode kan veel langer worden dan dit. We hebben alle invoer in kleine letters gezet en vervangen n Met " ". Dit is waar u de meeste van uw voorbewerking voor tekst kunt invoeren - en deze aan de vectorisatielaag kunt leveren via de optionele standardize argument. Als je eenmaal adapt() de laag naar de tekst (NumPy-array of lijst met teksten) - u kunt de woordenschat en de grootte daar vandaan halen:

vocab_size = len(vocab)
vocab_size 

Ten slotte, om woorden te de-tokeniseren, maken we een index_lookup woordenboek:

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

Het brengt alle tokens in kaart ([1, 2, 3, 4, ...]) naar woorden in het vocabulaire (['a', 'the', 'i', ...]). Door een sleutel (tokenindex) in te voeren, kunnen we het woord gemakkelijk terugkrijgen. U kunt nu de vectorize_layer() op elke invoer en observeer de gevectoriseerde zinnen:

vectorize_layer(['hello world!'])

Wat resulteert in:

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

Hallo heeft de index van 1 terwijl de wereld de index van heeft 7509! De rest is de opvulling van de maxlen we hebben berekend.

We hebben de middelen om tekst te vectoriseren - laten we nu datasets maken van text_train, text_test en text_valid, waarbij onze vectorisatielaag wordt gebruikt als een conversiemedium tussen woorden en vectoren die in GPT-Fyodor kunnen worden ingevoerd.

Gegevensset maken

We maken een tf.data.Dataset voor elk van onze sets, met behulp van from_tensor_slices() en het verstrekken van een lijst van, nou ja, tensorplakken (zinnen):

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)

Eenmaal gemaakt en geschud (opnieuw, voor de goede orde) - kunnen we een preprocessing-functie (vectorisatie en sequentiesplitsing) toepassen:

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)

De preprocess_text() functie breidt eenvoudig uit met de laatste dimensie, vectoriseert de tekst met behulp van onze vectorize_layer en creรซert de inputs en doelen, gecompenseerd door een enkel token. Het model zal gebruik maken van [0..n] afleiden n+1, wat een voorspelling oplevert voor elk woord, rekening houdend met alle woorden daarvoor. Laten we eens kijken naar een enkele invoer in een van de datasets:

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

Als we de geretourneerde invoer en doelen onderzoeken, in batches van 64 (met een lengte van elk 30), kunnen we duidelijk zien hoe ze worden gecompenseerd door รฉรฉn:

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

Eindelijk - het is tijd om het model te bouwen!

Model definitie

We zullen hier gebruik maken van KerasNLP-lagen. Na een Input, we coderen de invoer via a TokenAndPositionEmbedding laag, passerend in onze vocab_size, maxlen en embed_dim. Hetzelfde embed_dim dat deze laag uitvoer en invoert in de TransformerDecoder zal zijn bewaard in de decoder. Vanaf het moment van schrijven handhaaft de decoder automatisch de invoerdimensionaliteit en kunt u deze niet in een andere uitvoer projecteren, maar u kunt wel de latente dimensies definiรซren via de intermediate_dim argument.

We vermenigvuldigen de inbeddingsdimensies met twee voor de latente weergave, maar u kunt deze hetzelfde houden of een getal gebruiken dat losstaat van de inbeddingsdimensies:

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

Bovenop de decoder hebben we een Dense laag om het volgende woord in de reeks te kiezen, met a softmax activering (die de kansverdeling voor elk volgend token produceert). Laten we eens kijken naar de samenvatting van het model:

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 stapelt veel decoders - GPT-2 Small heeft 12 gestapelde decoders (117M parameters), terwijl GPT-2 Extra Large 48 gestapelde decoders heeft (1.5B parameters). Ons single-decodermodel met een bescheiden 13M-parameters zou goed genoeg moeten werken voor educatieve doeleinden. Met LLM's is gebleken dat opschalen een buitengewoon goede strategie is, en Transformers zorgen voor een goede schaling, waardoor het mogelijk is om extreem grote modellen te trainen.

GPT-3 heeft een "mager" 175B-parameters. Het team van Google Brain heeft een 1.6T-parametermodel getraind om sparsity-onderzoek uit te voeren en tegelijkertijd de berekening op hetzelfde niveau te houden als veel kleinere modellen.

Als we trouwens het aantal decoders zouden verhogen van 1 naar 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)

Onze parametertelling zou met 400k worden verhoogd:

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

De meeste parameters in ons netwerk komen van de TokenAndPositionEmbedding en Dense lagen!

Probeer verschillende dieptes van de decoder uit - van 1 tot helemaal uw machine aankan en noteer de resultaten. In ieder geval โ€“ we zijn bijna klaar om het model te trainen! Laten we een aangepaste callback maken die voor elk tijdperk een voorbeeld van tekst produceert, zodat we kunnen zien hoe het model door middel van training leert om zinnen te vormen.

Aangepast terugbellen

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

Het model trainen

Eindelijk tijd om te trainen! Laten we in onze gooien train_dataset en validation_dataset met de callbacks op zijn plaats:

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

De sampler koos een ongelukkige zin die begint met het eindcitaat en het begincitaat, maar het levert nog steeds interessante resultaten op tijdens het trainen:

# 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

Het begint met:

"Wat was je niet geweest, ik was hetzelfde geweest"...

Wat niet echt zin heeft. Tegen het einde van de tien korte tijdperken produceert het iets in de trant van:

โ€œHoe bedoel je dat is natuurlijk de meest gewone man van een manโ€โ€ฆ

Hoewel de tweede zin nog steeds niet zo logisch is, is hij veel verstandiger dan de eerste. Een langere training op meer gegevens (met meer ingewikkelde voorverwerkingsstappen) zou betere resultaten opleveren. We hebben het alleen getraind op 10 tijdperken met een hoge uitval om de kleine datasetgrootte te bestrijden. Als het veel langer zou blijven trainen, zou het zeer Fjodor-achtige tekst produceren, omdat het grote delen ervan zou hebben onthouden.

Opmerking: Omdat de uitvoer vrij uitgebreid is, kun je de verbose argument tijdens het aanpassen van het model om de hoeveelheid tekst op het scherm te verminderen.

Modelinferentie

Om gevolgtrekking uit te voeren, willen we de interface van de repliceren TextSampler โ€“ een methode die een seed en a . accepteert response_length (max_tokens). We gebruiken dezelfde methoden als in de sampler:

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

Nu kunt u de methode op nieuwe voorbeelden uitvoeren:

generate_text('the truth ultimately is')


generate_text('the truth ultimately is')

Resultaten verbeteren?

Dus, hoe kunt u de resultaten verbeteren? Er zijn een aantal behoorlijk bruikbare dingen die je zou kunnen doen:

  • Gegevens opschonen (schoon de invoergegevens nauwkeuriger op, we hebben zojuist een geschat aantal vanaf het begin bijgesneden en nieuwe regeltekens verwijderd)
  • Krijg meer gegevens (we werkten slechts met een paar megabytes aan tekstgegevens)
  • Schaal het model samen met de gegevens (decoders stapelen is niet moeilijk!)

Conclusie

Hoewel de voorverwerkingspijplijn minimalistisch is en verbeterd kan worden, produceerde de pijplijn die in deze gids wordt beschreven een fatsoenlijk GPT-achtig model, met slechts 5 regels code die nodig zijn om een โ€‹โ€‹aangepaste transformator voor alleen decoders te bouwen, met behulp van Keras!

Transformatoren zijn populair en breed toepasbaar voor generieke sequentiemodellering (en veel dingen kunnen worden uitgedrukt als sequenties). Tot nu toe was de belangrijkste toetredingsdrempel een omslachtige implementatie, maar met KerasNLP kunnen beoefenaars van deep learning de implementaties gebruiken om snel en gemakkelijk modellen te bouwen.

Tijdstempel:

Meer van Stapelmisbruik