Generación de texto estilo GPT de 5 líneas en Python con TensorFlow/Keras

Los transformadores, aunque se lanzaron en 2017, solo comenzaron a ganar una tracción significativa en los últimos años. Con la proliferación de la tecnología a través de plataformas como HuggingFace, NLP y Modelos de lenguaje grande (LLM) se han vuelto más accesibles que nunca.

Sin embargo, incluso con todo el bombo a su alrededor y con muchos guías orientadas a la teoría, no hay muchas implementaciones personalizadas en línea y los recursos no están tan disponibles como con otros tipos de redes, que han existido por más tiempo. Si bien podría simplificar su ciclo de trabajo mediante el uso de un transformador preconstruido de HuggingFace (el tema de otra guía), puede llegar a sentir cómo funciona construyendo uno usted mismo, antes de abstraerlo a través de una biblioteca. Nos centraremos en la construcción, en lugar de la teoría y la optimización aquí.

En esta guía, construiremos un Modelo de lenguaje autorregresivo a generar texto. Nos centraremos en los aspectos prácticos y minimalistas/concisos de cargar datos, dividirlos, vectorizarlos, construir un modelo, escribir una devolución de llamada personalizada y entrenamiento/inferencia. Cada una de estas tareas se puede dividir en guías más detalladas, por lo que mantendremos la implementación como genérica, dejando espacio para la personalización y optimización según su propio conjunto de datos.

Tipos de LLM y GPT-Fyodor

Si bien la categorización puede volverse mucho más compleja, puede en general clasificar los modelos de lenguaje basados ​​en Transformer en tres categorías:

  • Modelos basados ​​en codificador – ALBERT, BERT, DistilBERT, Roberta
  • Basado en decodificador – GPT, GPT-2, GPT-3, Transformador XL
  • Modelos Seq2Seq – BART, mBART, T5

Basado en codificador los modelos solo usan un codificador Transformer en su arquitectura (normalmente, apilado) y son excelentes para comprender oraciones (clasificación, reconocimiento de entidades nombradas, respuesta a preguntas).

Basado en decodificador los modelos solo usan un decodificador Transformer en su arquitectura (también normalmente apilado) y son excelentes para la predicción futura, lo que los hace adecuados para la generación de texto.

Sec2Seq Los modelos combinan codificadores y decodificadores y son excelentes para generar texto, resumir y, lo que es más importante, traducir.

La familia de modelos GPT, que ganó mucha tracción en los últimos años, son modelos de transformadores basados ​​en decodificadores y son excelentes para producir texto similar al humano, entrenados en grandes corpus de datos y con un aviso como un nuevo semilla inicial para la generación. Por ejemplo:

generate_text('the truth ultimately is')

Que debajo del capó alimenta este mensaje en un modelo similar a GPT y produce:

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

¡Este es, de hecho, un pequeño spoiler del final de la guía! Otro pequeño spoiler es la arquitectura que produjo ese texto:

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 líneas es todo lo que se necesita para construir un modelo de transformador de solo decodificador, simulando un GPT pequeño. Dado que entrenaremos el modelo en las novelas de Fyodor Dostoyevsky (que puede sustituir con cualquier otra cosa, desde Wikipedia hasta los comentarios de Reddit), tentativamente llamaremos al modelo GPT-Fiodor.

Keras PNL

El truco para un GPT-Fyodor de 5 líneas radica en Keras PNL, desarrollado por el equipo oficial de Keras, como una extensión horizontal de Keras, que al más puro estilo de Keras, tiene como objetivo poner al alcance de su mano la NLP más sólida de la industria, con nuevas capas (codificadores, decodificadores, incrustaciones de tokens, incrustaciones de posición, métricas, tokenizadores, etc.).

KerasNLP no es un zoológico modelo. Es una parte de Keras (como un paquete separado), que reduce la barrera de entrada para el desarrollo del modelo NLP, al igual que reduce la barrera de entrada para el desarrollo general de aprendizaje profundo con el paquete principal.

Nota: Al momento de escribir, KerasNLP todavía se está produciendo y en las primeras etapas. Las diferencias sutiles pueden estar presentes en futuras versiones. El artículo está utilizando la versión 0.3.0.

Para poder usar KerasNLP, deberá instalarlo a través de pip:

$ pip install keras_nlp

Y puedes verificar la versión con:

keras_nlp.__version__

Implementación de un modelo de estilo GPT con Keras

Comencemos importando las bibliotecas que usaremos: TensorFlow, Keras, KerasNLP y NumPy:

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

Cargando datos

Carguemos algunas de las novelas de Dostoyevsky: una sería demasiado corta para que se ajuste a un modelo, sin un poco de sobreajuste desde las primeras etapas en adelante. Usaremos con gracia los archivos de texto sin procesar de Proyecto Gutenberg, debido a la sencillez de trabajar con tales datos:

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

Simplemente descargamos todos los archivos, los revisamos y los concatenamos uno encima del otro. ¡Esto incluye cierta diversidad en el lenguaje utilizado, sin dejar de mantenerlo claramente Fyodor! Para cada archivo, hemos saltado los primeros 10k caracteres, que es aproximadamente la longitud promedio del prefacio y la introducción de Gutenberg, por lo que nos queda un cuerpo del libro prácticamente intacto para cada iteración. Echemos un vistazo a algunos 500 caracteres aleatorios en el texts cadena ahora:


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'

Separemos la cadena en oraciones antes de realizar cualquier otro procesamiento:

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

Tenemos 69k oraciones. Cuando reemplazas el n caracteres con espacios en blanco y cuente las palabras:

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

Nota: Por lo general, querrá tener al menos un millón de palabras en un conjunto de datos e, idealmente, mucho más que eso. Estamos trabajando con unos pocos megabytes de datos (~5 MB), mientras que los modelos de lenguaje se entrenan más comúnmente en decenas de gigabytes de texto. Naturalmente, esto hará que sea muy fácil sobreajustar la entrada de texto y difícil de generalizar (alta perplejidad sin sobreajuste, o baja perplejidad con mucho sobreajuste). Toma los resultados con pinzas.

Sin embargo, dividámoslos en un la formación, test y validación establecer. Primero, eliminemos las cadenas vacías y mezclemos las oraciones:


text_list = list(filter(None, text_list))

import random
random.shuffle(text_list)

Luego, haremos una división 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):]

Esta es una forma simple pero efectiva de realizar una división de validación de prueba de entrenamiento. Echemos un vistazo a 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', ...

¡Es hora de la estandarización y la vectorización!

Vectorización de texto

Las redes no entienden de palabras, entienden de números. Querremos tokenizar las palabras:

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

Además, dado que las oraciones difieren en longitud, el relleno generalmente se agrega a la izquierda o a la derecha para garantizar la misma forma en todas las oraciones que se ingresan. Digamos que nuestra oración más larga tiene 5 palabras (tokens). En ese caso, la oración de Wall-E se rellenaría con dos ceros, por lo que aseguramos la misma forma de entrada:

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

Tradicionalmente, esto se hacía usando un TensorFlow Tokenizer y Keras' pad_sequences() métodos – sin embargo, una capa mucho más práctica, TextVectorization, se puede usar, lo que tokeniza y rellena su entrada, lo que le permite extraer el vocabulario y su tamaño, ¡sin saber el vocabulario por adelantado!

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Vamos a adaptar y encajar un TextVectorization capa:

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

El custom_standardization() El método puede ser mucho más largo que esto. Simplemente hemos puesto en minúsculas todas las entradas y reemplazado n " ". Aquí es donde realmente puede poner la mayor parte de su preprocesamiento de texto y suministrarlo a la capa de vectorización a través de la opción standardize argumento. Una vez tú adapt() la capa al texto (arreglo NumPy o lista de textos): puede obtener el vocabulario, así como su tamaño desde allí:

vocab_size = len(vocab)
vocab_size 

Finalmente, para destokenizar palabras, crearemos un index_lookup diccionario:

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

Mapea todos los tokens ([1, 2, 3, 4, ...]) a palabras del vocabulario (['a', 'the', 'i', ...]). Al pasar una clave (índice de token), podemos recuperar fácilmente la palabra. Ahora puede ejecutar el vectorize_layer() en cualquier entrada y observe las oraciones vectorizadas:

vectorize_layer(['hello world!'])

Lo que resulta en:

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

hola tiene el indice de 1 mientras que el mundo tiene el índice de 7509! El resto es el relleno del maxlen hemos calculado.

Tenemos los medios para vectorizar texto; ahora, creemos conjuntos de datos a partir de text_train, text_test y text_valid, utilizando nuestra capa de vectorización como medio de conversión entre palabras y vectores que se pueden alimentar a GPT-Fyodor.

Creación de conjuntos de datos

Estaremos creando un tf.data.Dataset para cada uno de nuestros conjuntos, utilizando from_tensor_slices() y proporcionando una lista de, bueno, cortes de tensor (oraciones):

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 vez creado y mezclado (nuevamente, por si acaso), podemos aplicar una función de preprocesamiento (vectorización y división de secuencias):

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)

El preprocess_text() simplemente se expande por la última dimensión, vectoriza el texto usando nuestro vectorize_layer y crea las entradas y los objetivos, compensados ​​por un solo token. El modelo utilizará [0..n] inferir n+1, produciendo una predicción para cada palabra, teniendo en cuenta todas las palabras anteriores. Echemos un vistazo a una sola entrada en cualquiera de los conjuntos de datos:

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

Al investigar las entradas y los objetivos devueltos, en lotes de 64 (con una longitud de 30 cada uno), podemos ver claramente cómo se compensan con 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, ¡es hora de construir el modelo!

Definición de modelo

Haremos uso de las capas de KerasNLP aquí. Después de un Input, codificaremos la entrada a través de un TokenAndPositionEmbedding capa, pasando en nuestra vocab_size, maxlen y embed_dim. Lo mismo embed_dim que esta capa da salida y entrada al TransformerDecoder se mostrarán retenido en el decodificador. Al momento de escribir, el Decodificador mantiene automáticamente la dimensionalidad de entrada y no le permite proyectarla en una salida diferente, pero le permite definir las dimensiones latentes a través de la intermediate_dim argumento.

Multiplicaremos las dimensiones de incrustación por dos para la representación latente, pero puede mantenerlo igual o usar un número separado de las dimensiones de incrustación:

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

En la parte superior del decodificador, tenemos un Dense capa para elegir la siguiente palabra en la secuencia, con una softmax activación (que produce la distribución de probabilidad para cada token siguiente). Veamos el resumen del modelo:

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 apila muchos decodificadores: GPT-2 Small tiene 12 decodificadores apilados (117M de parámetros), mientras que GPT-2 Extra Large tiene 48 decodificadores apilados (1.5B de parámetros). Nuestro modelo de decodificador único con unos humildes parámetros de 13M debería funcionar lo suficientemente bien para fines educativos. Con LLM, la ampliación ha demostrado ser una estrategia extremadamente buena, y los transformadores permiten una buena ampliación, lo que hace factible entrenar modelos extremadamente grandes.

GPT-3 tiene un "pobre" 175B parámetros. El equipo de Google Brain entrenó un modelo de parámetros de 1.6 T para realizar una investigación de escasez y mantener el cálculo al mismo nivel que los modelos mucho más pequeños.

De hecho, si aumentamos el número de decodificadores de 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)

Nuestro recuento de parámetros se incrementaría en 400k:

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

La mayoría de los parámetros en nuestra red provienen de la TokenAndPositionEmbedding y Dense ¡capas!

Pruebe diferentes profundidades del decodificador, desde 1 hasta todo lo que su máquina puede manejar y observe los resultados. En cualquier caso, ¡estamos casi listos para entrenar al modelo! Vamos a crear una devolución de llamada personalizada que producirá una muestra de texto en cada época, para que podamos ver cómo el modelo aprende a formar oraciones a través del entrenamiento.

Devolución de llamada personalizada

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

Entrenando el modelo

¡Por fin, hora de entrenar! Vamos a tirar en nuestro train_dataset y validation_dataset con las devoluciones de llamada en su lugar:

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

La muestra eligió una oración desafortunada que comienza con la cita final y la cita inicial, pero aún produce resultados interesantes durante el entrenamiento:

# 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

Empieza con:

“Qué no hubieras sido yo hubiera sido el mismo”…

Lo que realmente no tiene mucho sentido. Al final de las diez épocas cortas, produce algo similar a:

"¿Qué quieres decir con que es el hombre más común de un hombre, por supuesto"...

Si bien la segunda oración todavía no tiene mucho sentido, es mucho más sensata que la primera. Un entrenamiento más prolongado con más datos (con pasos de preprocesamiento más complejos) produciría mejores resultados. Solo lo hemos entrenado en 10 épocas con un alto abandono para combatir el tamaño pequeño del conjunto de datos. Si se dejara entrenando durante mucho más tiempo, produciría un texto muy parecido al de Fyodor, porque habría memorizado grandes porciones de él.

Nota: Dado que la salida es bastante detallada, puede ajustar el verbose argumento mientras ajusta el modelo para reducir la cantidad de texto en pantalla.

Inferencia del modelo

Para realizar la inferencia, querremos replicar la interfaz del TextSampler – un método que acepta una semilla y un response_length (max_tokens). Usaremos los mismos métodos que dentro de la muestra:

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

Ahora, puede ejecutar el método en muestras nuevas:

generate_text('the truth ultimately is')


generate_text('the truth ultimately is')

¿Mejorar los resultados?

Entonces, ¿cómo se pueden mejorar los resultados? Hay algunas cosas bastante procesables que podrías hacer:

  • Limpieza de datos (limpie los datos de entrada más meticulosamente, simplemente recortamos un número aproximado desde el principio y eliminamos los caracteres de nueva línea)
  • Obtener más datos (solo trabajamos con unos pocos megas de datos de texto)
  • Escale el modelo junto con los datos (¡apilar decodificadores no es difícil!)

Conclusión

Si bien la canalización de preprocesamiento es minimalista y se puede mejorar, la canalización descrita en esta guía produjo un modelo de estilo GPT decente, con solo 5 líneas de código necesarias para construir un transformador personalizado solo de decodificador, ¡utilizando Keras!

Los transformadores son populares y ampliamente aplicables para el modelado de secuencias genéricas (y muchas cosas se pueden expresar como secuencias). Hasta ahora, la principal barrera de entrada era una implementación engorrosa, pero con KerasNLP, los profesionales del aprendizaje profundo pueden aprovechar las implementaciones para crear modelos de forma rápida y sencilla.

Sello de tiempo:

Mas de Abuso de pila