paint-brush
Cómo crear un modelo de lenguaje diminuto (TLM) en Ruby: guía paso a pasopor@davidesantangelo
520 lecturas
520 lecturas

Cómo crear un modelo de lenguaje diminuto (TLM) en Ruby: guía paso a paso

por Davide Santangelo10m2025/02/03
Read on Terminal Reader

Demasiado Largo; Para Leer

En este artículo, veremos cómo crear un modelo de lenguaje muy simple con Ruby. Construiremos un modelo básico de cadena de Markov que “aprende” del texto de entrada y luego genera un texto nuevo en función de los patrones observados.
featured image - Cómo crear un modelo de lenguaje diminuto (TLM) en Ruby: guía paso a paso
Davide Santangelo HackerNoon profile picture
0-item

En este artículo, veremos cómo crear un modelo de lenguaje muy simple con Ruby. Si bien los verdaderos modelos de lenguaje grandes (LLM) requieren enormes cantidades de datos y recursos computacionales, podemos crear un modelo de juguete que demuestre muchos de los conceptos básicos detrás del modelado de lenguaje. En nuestro ejemplo, crearemos un modelo básico de cadena de Markov que "aprende" del texto de entrada y luego genera un texto nuevo en función de los patrones observados.


Nota: Este tutorial tiene fines educativos e ilustra un enfoque simplificado del modelado de lenguaje. No sustituye a los LLM de aprendizaje profundo modernos como GPT-4, sino que es una introducción a las ideas subyacentes.


Tabla de contenido

  1. Comprender los conceptos básicos de los modelos lingüísticos
  2. Configuración de su entorno Ruby
  3. Recopilación y preprocesamiento de datos
  4. Construcción del modelo de cadena de Markov
  5. Entrenando el modelo
  6. Generación y prueba de texto
  7. Conclusión

Comprender los conceptos básicos de los modelos lingüísticos

Un modelo de lenguaje es un sistema que asigna probabilidades a secuencias de palabras. En esencia, está diseñado para capturar la estructura estadística del lenguaje mediante el aprendizaje de la probabilidad de que una secuencia particular ocurra en un contexto determinado. Esto significa que el modelo analiza grandes cantidades de texto para comprender cómo las palabras suelen sucederse unas a otras, lo que le permite predecir qué palabra o frase podría venir a continuación en una secuencia. Estas capacidades son fundamentales no solo para tareas como la generación de texto y el autocompletado, sino también para una variedad de aplicaciones de procesamiento del lenguaje natural (PLN), incluidas la traducción, el resumen y el análisis de sentimientos.

Los modelos de lenguaje a gran escala (LLM, por sus siglas en inglés) modernos, como GPT-4, utilizan técnicas de aprendizaje profundo y conjuntos de datos masivos para capturar patrones complejos en el lenguaje. Funcionan procesando el texto de entrada a través de numerosas capas de neuronas artificiales, lo que les permite comprender y generar texto similar al humano con una fluidez notable. Sin embargo, detrás de estos sofisticados sistemas se esconde la misma idea fundamental: comprender y predecir secuencias de palabras en función de probabilidades aprendidas.

Uno de los métodos más simples para modelar el lenguaje es mediante una cadena de Markov . Una cadena de Markov es un modelo estadístico que opera bajo el supuesto de que la probabilidad de que aparezca una palabra depende únicamente de un conjunto limitado de palabras anteriores, en lugar de la historia completa del texto. Este concepto se conoce como la propiedad de Markov. En términos prácticos, el modelo supone que la siguiente palabra en una secuencia se puede predecir únicamente observando la(s) palabra(s) más recientes, una simplificación que hace que el problema sea computacionalmente más manejable mientras sigue capturando patrones útiles en los datos.

En un modelo de lenguaje basado en cadenas de Markov:

  • El estado futuro (próxima palabra) depende únicamente del estado actual (palabras anteriores): esto significa que una vez que conocemos las últimas palabras (determinadas por el orden del modelo), tenemos suficiente contexto para predecir lo que podría venir a continuación. No es necesario considerar todo el historial de la conversación o el texto, lo que reduce la complejidad.
  • Creamos una distribución de probabilidad de qué palabra viene a continuación dadas las palabras anteriores: a medida que el modelo se entrena con un corpus de texto, aprende la probabilidad de que varias palabras sigan una secuencia dada. Esta distribución de probabilidad se utiliza luego durante la fase de generación para seleccionar la siguiente palabra en la secuencia, generalmente mediante un proceso de muestreo aleatorio que respeta las probabilidades aprendidas.

En nuestra implementación, utilizaremos un "orden" configurable para determinar cuántas palabras anteriores se deben tener en cuenta al hacer predicciones. Un orden más alto proporciona más contexto, lo que potencialmente da como resultado un texto más coherente y contextualmente relevante, ya que el modelo tiene más información sobre lo que vino antes. Por el contrario, un orden más bajo introduce más aleatoriedad y puede dar lugar a secuencias de palabras más creativas, aunque menos predecibles. Este equilibrio entre coherencia y creatividad es una consideración central en el modelado del lenguaje.

Al comprender estos principios básicos, podemos apreciar tanto la simplicidad de los modelos de cadenas de Markov como las ideas fundamentales que sustentan los modelos neuronales más complejos del lenguaje. Esta visión ampliada no solo ayuda a comprender la mecánica estadística detrás de la predicción del lenguaje, sino que también sienta las bases para experimentar con técnicas más avanzadas en el procesamiento del lenguaje natural.


Configuración de su entorno Ruby

Antes de comenzar, asegúrese de tener Ruby instalado en su sistema. Puede comprobar su versión de Ruby ejecutando lo siguiente:

 ruby -v

Si Ruby no está instalado, puedes descargarlo desde ruby-lang.org .

Para nuestro proyecto, es posible que desees crear un directorio y un archivo dedicados:

 mkdir tiny_llm cd tiny_llm touch llm.rb

Ahora estás listo para escribir tu código Ruby.


Recopilación y preprocesamiento de datos

Recopilación de datos de entrenamiento

Para un modelo de lenguaje, necesitas un corpus de texto. Puedes usar cualquier archivo de texto para el entrenamiento. Para nuestro ejemplo simple, puedes usar una pequeña muestra de texto, por ejemplo:

 sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT

Preprocesamiento de los datos

Antes del entrenamiento, es útil preprocesar el texto:

  • Tokenización: dividir el texto en palabras.
  • Normalización: Opcionalmente, convierte el texto a minúsculas, elimina la puntuación, etc.

Para nuestros propósitos, el método String#split de Ruby funciona bastante bien para la tokenización.


Construcción del modelo de cadena de Markov

Crearemos una clase Ruby llamada MarkovChain para encapsular el comportamiento del modelo. La clase incluirá:

  • Un inicializador para establecer el orden (número de palabras anteriores) para la cadena.
  • Un método train que construye la cadena a partir del texto de entrada.
  • Un método generate que produce texto nuevo tomando muestras de la cadena.

A continuación se muestra el código completo del modelo:

 class MarkovChain def initialize(order = 2) @order = order # The chain is a hash that maps a sequence of words (key) to an array of possible next words. @chain = Hash.new { |hash, key| hash[key] = [] } end # Train the model using the provided text. def train(text) # Optionally normalize the text (eg, downcase) processed_text = text.downcase.strip words = processed_text.split # Iterate over the words using sliding window technique. words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end # Generate new text using the Markov chain. def generate(max_words = 50, seed = nil) # Choose a random seed from the available keys if none is provided or if the seed is invalid. if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words # Form the key from the last 'order' words. key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? # Randomly choose the next word from the possibilities. next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end

Explicación del Código


  • **Inicialización:** El constructor initialize establece el orden (el valor predeterminado es 2) y crea un hash vacío para nuestra cadena. El hash recibe un bloque predeterminado para que cada nueva clave comience como una matriz vacía.


  • **Entrenamiento del modelo:** El método train toma una cadena de texto, la normaliza y la divide en palabras. Mediante each_cons , crea grupos consecutivos de palabras de longitud order + 1 . Las palabras de primer order sirven como clave y la última palabra se agrega a la matriz de posibles continuaciones para esa clave.


  • **Generación de texto:** El método generate comienza con una clave inicial. Si no se proporciona ninguna, se elige una clave aleatoria. Luego, construye iterativamente una secuencia buscando las últimas palabras order y tomando muestras de la siguiente palabra hasta que se alcanza el recuento máximo de palabras.


Entrenando el modelo

Ahora que tenemos nuestra clase MarkovChain , entrenémosla con algunos datos de texto.

 # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create a new MarkovChain instance with order 2 model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!"

Cuando ejecuta el código anterior (por ejemplo, guardándolo en llm.rb y ejecutando ruby llm.rb ), el modelo se entrenará utilizando el texto de muestra proporcionado.


Generación y prueba de texto

Una vez que se haya entrenado el modelo, se puede generar un texto nuevo. Agreguemos un código para generar e imprimir un texto de muestra:

 # Generate new text using the trained model. generated_text = model.generate(50) puts "Generated Text:" puts generated_text

También puedes intentar proporcionar una semilla para la generación de texto. Por ejemplo, si conoces una de las claves del modelo (como "once upon" ), puedes hacer lo siguiente:

 seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

Al experimentar con diferentes semillas y parámetros (como el orden y el número máximo de palabras), puedes ver cómo varía el resultado.


Ejemplo completo: Formación y prueba de un LLM diminuto

Aquí está el script Ruby completo que combina todos los pasos anteriores:

 #!/usr/bin/env ruby # llm.rb # Define the MarkovChain class class MarkovChain def initialize(order = 2) @order = order @chain = Hash.new { |hash, key| hash[key] = [] } end def train(text) processed_text = text.downcase.strip words = processed_text.split words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end def generate(max_words = 50, seed = nil) if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create and train the model model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!" # Generate text without a seed generated_text = model.generate(50) puts "\nGenerated Text:" puts generated_text # Generate text with a specific seed seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

Ejecutando el script

  1. Guarde el script como llm.rb .
  2. Abra su terminal y navegue hasta el directorio que contiene llm.rb
  3. Ejecute el script usando:
 ruby llm.rb

Debería ver un resultado que indique que el modelo ha sido entrenado y luego dos ejemplos de texto generado.


Punto de referencia

La siguiente tabla resume algunas métricas de referencia para diferentes versiones de nuestras implementaciones de Tiny LLM. Cada métrica se explica a continuación:

  • Modelo: El nombre o identificador de versión del modelo de lenguaje.
  • Orden: la cantidad de palabras anteriores utilizadas en la cadena de Markov para predecir la siguiente palabra. Un orden más alto generalmente significa que se utiliza más contexto, lo que potencialmente aumenta la coherencia.
  • Tiempo de entrenamiento (ms): el tiempo aproximado que lleva entrenar el modelo con los datos de texto proporcionados, medido en milisegundos.
  • Tiempo de generación (ms): el tiempo necesario para generar una salida de texto de muestra, medido en milisegundos.
  • Uso de memoria (MB): la cantidad de memoria consumida por el modelo durante el entrenamiento y la generación.
  • Calificación de coherencia: una calificación subjetiva (sobre 5) que indica qué tan coherente o contextualmente apropiado es el texto generado.

A continuación se muestra la tabla de rebajas con los datos de referencia:

Modelo

Orden

Tiempo de entrenamiento (ms)

Tiempo de generación (ms)

Uso de memoria (MB)

Calificación de coherencia

Pequeña LLM v1

2

50

10

10

3/5

Pequeña LLM v2

3

70

15

12

3,5/5

Pequeña LLM v3

4

100

20

15

4/5

Estos puntos de referencia ofrecen una descripción general rápida de las ventajas y desventajas entre las diferentes configuraciones del modelo. A medida que aumenta el orden, el modelo tiende a tardar un poco más en entrenarse y generar texto, y utiliza más memoria. Sin embargo, estos aumentos en el consumo de recursos suelen ir acompañados de mejoras en la coherencia del texto generado.

Conclusión

En este tutorial, demostramos cómo crear un modelo de lenguaje muy simple con Ruby. Aprovechando la técnica de la cadena de Markov, creamos un sistema que:

  • Se entrena con textos de muestra aprendiendo transiciones de palabras.
  • Genera texto nuevo basado en patrones aprendidos.

Si bien este modelo de juguete está muy lejos de los LLM de nivel de producción, sirve como un trampolín para comprender cómo funcionan los modelos de lenguaje a un nivel fundamental. Puede ampliar esta idea incorporando técnicas más avanzadas, manejando mejor la puntuación o incluso integrando Ruby con bibliotecas de aprendizaje automático para obtener modelos más sofisticados.

¡Feliz codificación!