Generazione di testo in stile GPT a 5 righe in Python con TensorFlow/Keras

I Transformers, anche se rilasciati nel 2017, hanno iniziato a guadagnare una trazione significativa solo negli ultimi due anni. Con la proliferazione della tecnologia attraverso piattaforme come HuggingFace, NLP e Large Language Models (LLM) sono diventati più accessibili che mai.

Eppure, anche con tutto il clamore che li circonda e con molti guide orientate alla teoria, non ci sono molte implementazioni personalizzate online e le risorse non sono prontamente disponibili come con altri tipi di rete, che esistono da più tempo. Sebbene tu possa semplificare il tuo ciclo di lavoro utilizzando un Transformer pre-costruito da HuggingFace (l'argomento di un'altra guida), puoi arrivare a sentire come funziona costruendone uno tu stesso, prima di astrarlo via attraverso una libreria. Ci concentreremo sulla costruzione, piuttosto che sulla teoria e sull'ottimizzazione qui.

In questa guida creeremo un Modello di linguaggio autoregressivo a generare testo. Ci concentreremo sugli aspetti pratici e minimalisti/concisi del caricamento dei dati, della suddivisione, della vettorizzazione, della costruzione di un modello, della scrittura di una callback personalizzata e dell'addestramento/inferenza. Ognuna di queste attività può essere trasformata in guide più dettagliate, quindi manterremo l'implementazione come generica, lasciando spazio alla personalizzazione e all'ottimizzazione a seconda del tuo set di dati.

Tipi di LLM e GPT-Fyodor

Mentre la categorizzazione può diventare molto più complicata, puoi farlo in linea di massima classificare i modelli linguistici basati su Transformer in tre categorie:

  • Modelli basati su encoder – ALBERT, BERT, DistilBERT, Roberta
  • Basato su decoder – GPT, GPT-2, GPT-3, TrasformatoreXL
  • Modelli Seq2Seq –BART, MBART, T5

Basato su codificatore i modelli utilizzano solo un codificatore Transformer nella loro architettura (in genere, in pila) e sono ottimi per comprendere le frasi (classificazione, riconoscimento di entità con nome, risposta alle domande).

Basato su decoder i modelli utilizzano solo un decoder Transformer nella loro architettura (in genere anche impilato) e sono ottimi per la previsione futura, il che li rende adatti alla generazione di testo.

Seq2Seq i modelli combinano sia codificatori che decodificatori e sono ottimi per la generazione di testo, la sintesi e, soprattutto, la traduzione.

La famiglia di modelli GPT, che ha guadagnato molta popolarità negli ultimi due anni, sono modelli di trasformatori basati su decoder e sono ottimi per produrre testo simile a quello umano, addestrati su grandi corpora di dati e forniti di un prompt come nuovo iniziando il seme per la generazione. Per esempio:

generate_text('the truth ultimately is')

Che sotto il cofano alimenta questo prompt in un modello simile a GPT e produce:

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

Questo è, infatti, un piccolo spoiler dalla fine della guida! Un altro piccolo spoiler è l'architettura che ha prodotto quel testo:

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 linee sono tutto ciò che serve per costruire un modello di trasformatore solo decoder, simulando un piccolo GPT. Dal momento che addestreremo il modello sui romanzi di Fyodor Dostoevsky (che puoi sostituire con qualsiasi altra cosa, da Wikipedia ai commenti di Reddit), chiameremo provvisoriamente il modello GPT-Fëdor.

Keras NLP

Il trucco per un GPT-Fyodor a 5 linee sta nel Keras NLP, che è sviluppato dal team ufficiale di Keras, come estensione orizzontale di Keras, che in vero stile Keras, mira a portare a portata di mano la PNL di forza del settore, con nuovi livelli (codificatori, decodificatori, incorporamenti di token, incorporamenti di posizione, metriche, tokenizzatori, ecc.).

KerasNLP non è uno zoo modello. È una parte di Keras (come pacchetto separato), che abbassa la barriera all'ingresso per lo sviluppo del modello NLP, così come abbassa la barriera all'ingresso per lo sviluppo generale del deep learning con il pacchetto principale.

Nota: Al momento della scrittura, KerasNLP è ancora in produzione e nelle prime fasi. Sottili differenze potrebbero essere presenti nelle versioni future. Il writeup utilizza la versione 0.3.0.

Per poter utilizzare KerasNLP, dovrai installarlo tramite pip:

$ pip install keras_nlp

E puoi verificare la versione con:

keras_nlp.__version__

Implementazione di un modello in stile GPT con Keras

Iniziamo importando le librerie che useremo: TensorFlow, Keras, KerasNLP e NumPy:

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

Caricamento dati

Carichiamo alcuni dei romanzi di Dostoevskij: uno sarebbe troppo corto per adattarsi a un modello, senza un bel po' di overfitting dalle prime fasi in poi. Useremo con grazia i file di testo non elaborati da Project Gutenberg, per la semplicità di lavorare con tali dati:

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

Abbiamo semplicemente scaricato tutti i file, li abbiamo esaminati e li abbiamo concatenati uno sopra l'altro. Ciò include una certa diversità nella lingua utilizzata, pur mantenendola distintamente Fyodor! Per ogni file, abbiamo saltato i primi 10 caratteri, che è circa la lunghezza media della prefazione e dell'introduzione di Gutenberg, quindi per ogni iterazione rimane un corpo del libro in gran parte intatto. Diamo un'occhiata ad alcuni 500 caratteri casuali nel file texts stringa ora:


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'

Separiamo la stringa in frasi prima di eseguire qualsiasi altra elaborazione:

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

Abbiamo 69 frasi. Quando sostituisci il n caratteri con spazi bianchi e contare le parole:

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

Nota: In genere vorrai avere almeno un milione di parole in un set di dati e, idealmente, molto di più. Stiamo lavorando con pochi megabyte di dati (~5 MB) mentre i modelli linguistici sono più comunemente addestrati su decine di gigabyte di testo. Ciò, naturalmente, renderà davvero facile sovraadattare il testo immesso e difficile da generalizzare (elevata perplessità senza overfitting o bassa perplessità con molto overfitting). Prendi i risultati con le pinze.

Tuttavia, dividiamoli in a formazione, test ed convalida impostare. Per prima cosa, rimuoviamo le stringhe vuote e mescoliamo le frasi:


text_list = list(filter(None, text_list))

import random
random.shuffle(text_list)

Quindi, faremo una divisione 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):]

Questo è un modo semplice ma efficace per eseguire una divisione di convalida del test del treno. Diamo un'occhiata 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', ...

Tempo di standardizzazione e vettorizzazione!

Vettorizzazione del testo

Le reti non capiscono le parole, capiscono i numeri. Vorremo tokenizzare le parole:

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

Inoltre, poiché le frasi differiscono in lunghezza, il riempimento viene in genere aggiunto a sinistra o a destra per garantire la stessa forma nelle frasi alimentate. Supponiamo che la nostra frase più lunga sia lunga 5 parole (gettoni). In tal caso, la frase Wall-E verrebbe riempita di due zeri, quindi assicuriamo la stessa forma di input:

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

Tradizionalmente, questo veniva fatto usando un TensorFlow Tokenizer e Keras' pad_sequences() metodi – tuttavia, uno strato molto più pratico, TextVectorization, può essere utilizzato, che tokenizza ed riempie il tuo input, permettendoti di estrarre il vocabolario e le sue dimensioni, senza conoscere il vocabolario in anticipo!

Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!

Adattiamo e adattiamo a TextVectorization strato:

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

I custom_standardization() il metodo può diventare molto più lungo di questo. Abbiamo semplicemente minuscolo tutto l'input e sostituito n con " ". È qui che puoi davvero inserire la maggior parte della tua preelaborazione per il testo e fornirla al livello di vettorizzazione tramite l'opzione standardize discussione. Una volta tu adapt() il livello del testo (array NumPy o elenco di testi) - puoi ottenere il vocabolario e le sue dimensioni da lì:

vocab_size = len(vocab)
vocab_size 

Infine, per de-tokenizzare le parole, creeremo un index_lookup dizionario:

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

Mappa tutti i token ([1, 2, 3, 4, ...]) alle parole del vocabolario (['a', 'the', 'i', ...]). Passando una chiave (indice token), possiamo facilmente recuperare la parola. Ora puoi eseguire il vectorize_layer() su qualsiasi input e osserva le frasi vettorializzate:

vectorize_layer(['hello world!'])

Che si traduce 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)>

Ciao ha l'indice di 1 mentre il mondo ha l'indice di 7509! Il resto è l'imbottitura per il maxlen abbiamo calcolato.

Abbiamo i mezzi per vettorializzare il testo: ora creiamo set di dati da text_train, text_test ed text_valid, utilizzando il nostro livello di vettorizzazione come mezzo di conversione tra parole e vettori che possono essere inseriti in GPT-Fyodor.

Creazione di set di dati

Creeremo un tf.data.Dataset per ciascuno dei nostri set, utilizzando from_tensor_slices() e fornendo un elenco di, beh, fette tensoriali (frasi):

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)

Una volta creato e mischiato (di nuovo, per buona misura), possiamo applicare una funzione di preelaborazione (vettorizzazione e divisione della sequenza):

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)

I preprocess_text() la funzione si espande semplicemente dell'ultima dimensione, vettorializza il testo usando il nostro vectorize_layer e crea gli input e gli obiettivi, compensati da un singolo token. Il modello utilizzerà [0..n] inferire n+1, producendo una previsione per ogni parola, tenendo conto di tutte le parole precedenti. Diamo un'occhiata a una singola voce in uno qualsiasi dei set di dati:

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

Esaminando gli input e gli obiettivi restituiti, in lotti di 64 (con una lunghezza di 30 ciascuno), possiamo vedere chiaramente come vengono compensati di uno:

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

Finalmente – è il momento di costruire il modello!

Definizione del modello

Utilizzeremo i livelli KerasNLP qui. Dopo un Input, codificheremo l'input tramite a TokenAndPositionEmbedding strato, passando nel ns vocab_size, maxlen ed embed_dim. Lo stesso embed_dim che questo livello emette e immette nel file TransformerDecoder sarà conservato nel Decoder. Al momento della scrittura, il Decoder mantiene automaticamente la dimensionalità dell'input e non consente di proiettarla in un output diverso, ma consente di definire le dimensioni latenti attraverso il intermediate_dim discussione.

Moltiplichiamo le dimensioni di incorporamento per due per la rappresentazione latente, ma puoi mantenerlo uguale o utilizzare un numero staccato dai dim di incorporamento:

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

Sulla parte superiore del decoder, abbiamo a Dense layer per scegliere la parola successiva nella sequenza, con a softmax attivazione (che produce la distribuzione di probabilità per ogni token successivo). Diamo un'occhiata al riepilogo del modello:

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 impila molti decoder: GPT-2 Small ha 12 decoder impilati (117M parametri), mentre GPT-2 Extra Large ha 48 decoder impilati (1.5B parametri). Il nostro modello a decodificatore singolo con un modesto parametro di 13M dovrebbe funzionare abbastanza bene per scopi didattici. Con gli LLM, il ridimensionamento si è rivelato una strategia estremamente buona e i Transformers consentono un buon ridimensionamento, rendendo possibile addestrare modelli estremamente grandi.

GPT-3 ha un “magro” 175B parametri. Il team di Google Brain ha addestrato un modello di parametri 1.6T per eseguire ricerche sulla sparsità mantenendo i calcoli allo stesso livello di modelli molto più piccoli.

Infatti, se aumentassimo il numero dei decoder da 1 a 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)

Il nostro conteggio dei parametri verrebbe aumentato di 400:

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

La maggior parte dei parametri nella nostra rete provengono da TokenAndPositionEmbedding ed Dense strati!

Prova diverse profondità del decoder, da 1 a tutto il modo in cui la tua macchina può gestire e annota i risultati. In ogni caso – siamo quasi pronti per addestrare il modello! Creiamo un callback personalizzato che produca un campione di testo in ogni epoca, in modo da poter vedere come il modello impara a formare frasi attraverso l'addestramento.

Richiamata personalizzata

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

Formazione del modello

Finalmente è ora di allenarsi! Mettiamoci dentro il nostro train_dataset ed validation_dataset con le richiamate in atto:

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

Il campionatore ha scelto una frase sfortunata che inizia con la virgoletta finale e la virgoletta iniziale, ma produce comunque risultati interessanti durante l'allenamento:

# 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

Si inizia con:

“Cosa non eri stato io ero stato lo stesso”...

Il che non ha molto senso. Entro la fine delle dieci brevi epoche, produce qualcosa sulla falsariga di:

"Cosa vuoi dire che è l'uomo più ordinario di un uomo, naturalmente"...

Anche se la seconda frase non ha ancora molto senso, è molto più sensata della prima. Una formazione più lunga su più dati (con fasi di preelaborazione più complesse) produrrebbe risultati migliori. L'abbiamo addestrato solo su 10 epoche con dropout elevato per combattere le dimensioni ridotte del set di dati. Se fosse stato lasciato in allenamento per molto più tempo, produrrebbe un testo molto simile a Fyodor, perché ne avrebbe memorizzato grandi porzioni.

Nota: Poiché l'output è abbastanza dettagliato, puoi modificare il file verbose argomento durante l'adattamento del modello per ridurre la quantità di testo sullo schermo.

Inferenza del modello

Per eseguire l'inferenza, vorremo replicare l'interfaccia di TextSampler – un metodo che accetta un seme e a response_length (max_tokens). Useremo gli stessi metodi del campionatore:

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

Ora puoi eseguire il metodo su nuovi campioni:

generate_text('the truth ultimately is')


generate_text('the truth ultimately is')

Miglioramento dei risultati?

Quindi, come puoi migliorare i risultati? Ci sono alcune cose piuttosto attuabili che potresti fare:

  • Pulizia dei dati (pulisci i dati di input in modo più meticoloso, abbiamo appena tagliato un numero approssimativo dall'inizio e rimosso i caratteri di nuova riga)
  • Ottieni più dati (abbiamo lavorato solo con pochi megabyte di dati di testo)
  • Ridimensiona il modello insieme ai dati (staccare i decoder non è difficile!)

Conclusione

Sebbene la pipeline di preelaborazione sia minimalista e possa essere migliorata, la pipeline delineata in questa guida ha prodotto un modello in stile GPT decente, con solo 5 righe di codice necessarie per creare un trasformatore personalizzato solo per decoder, utilizzando Keras!

I trasformatori sono popolari e ampiamente applicabili per la modellazione di sequenze generiche (e molte cose possono essere espresse come sequenze). Finora, la principale barriera all'ingresso era un'implementazione ingombrante, ma con KerasNLP, i professionisti del deep learning possono sfruttare le implementazioni per creare modelli in modo rapido e semplice.

Timestamp:

Di più da Impilamento