Cómo crear un modelo de lenguaje: entender el proceso y entrenar uno pequeño
Qué aprende un LLM, cuándo usar RAG o fine-tuning y qué implica entrenar desde cero. Entrena, mide y guarda una pequeña red original.

¿Qué significa crear mi LLM?
Usar un modelo existente con instrucciones crea una aplicación. RAG aporta documentos al preguntar. Fine-tuning modifica pesos existentes; entrenar desde cero empieza con pesos aleatorios. Son objetivos y costes distintos.
Aquí entenderás las opciones y entrenarás una red diminuta que predice caracteres, guarda pesos y genera texto. Es un MLP educativo, no un Transformer grande ni un chat útil. Verás datos, predicción, error, actualización y medición, sin confundir una API con un modelo entrenado por ti.
1. Elegir según la necesidad
Para manuales cambiantes evalúa RAG. Para formato consistente o conducta especializada compara prompts y fine-tuning; no garantizan verdad. Para aprender sirve este pequeño experimento. Un LLM general de producción exige investigación e infraestructura mucho mayores.
Un asistente de eventos necesita procedimientos y fuentes actuales. Un clasificador JSON puede empezar con prompting y validación. Este ejercicio no recrea razonamiento multilingüe general. Define una tarea medible antes del GPU.
2. Vocabulario esencial
El tokenizer convierte texto en IDs; aquí uno por carácter. El embedding representa IDs con números aprendidos. Los parámetros se actualizan. Contexto es la historia visible. Los logits pasan por softmax a probabilidades; la pérdida penaliza predecir mal el siguiente carácter. Los gradientes indican cómo reducir el error.
Un batch agrupa ejemplos, un paso actualiza y una época recorre los datos. Aquí se muestrean lotes: 400 pasos no son 400 épocas. Un checkpoint guarda pesos; inferencia los usa sin cambiarlos. El muestreo cambia variedad, no añade conocimiento.
3. El proceso completo
Define tarea y evaluación, reúne datos con derechos, elimina secretos y duplicados y divide entrenamiento/validación/test sin solapamiento. Elige tokenizer y arquitectura, estima memoria y cómputo, controla pérdida, guarda versiones y evalúa sobre datos reservados. Versiona datos, código y configuración.
Un asistente puede necesitar instrucciones, alineamiento y seguridad. Servirlo requiere autenticación, límites y vigilancia. No hay presupuesto universal: parámetros, tokens, optimizador, precisión, secuencia y hardware importan. No se promete un LLM productivo por un precio fijo.
4. Qué contiene el modelo
Contexto de ocho caracteres, embeddings de 12 dimensiones, capa tanh de 48 unidades y salida sobre el vocabulario. El corpus suministrado produce 6.303 parámetros. Usa NumPy y CPU con gradientes explícitos; no implementa atención ni Transformer.
Los datos son frases originales ficticias. Los tests comprueban menor pérdida de entrenamiento, probabilidades finitas que suman uno, error ante caracteres desconocidos y predicciones iguales tras recargar. No miden conocimiento general.
5. Instalar, entrenar y guardar
Extrae en una carpeta nueva. Requiere Python 3.9+ y NumPy. Instalar usa el registro; entrenar no requiere modelos descargados, cuenta, clave ni GPU. Se permite NumPy 2.x y las cifras pueden variar.
Ejecuta tests y model.py: imprime parámetros, pérdidas y muestra; guarda tiny-model.npz en la carpeta actual. Usa primero el corpus dado. Si lo cambias, conserva espacio para padding y verifica derechos. No entrenes con secretos o conversaciones privadas.
# Python 3.9+; create an isolated environment in the extracted folder.
python3 -m venv .venv
# macOS/Linux (Windows: .venv\Scripts\activate)
. .venv/bin/activate
python -m pip install -r requirements.txt
python -m unittest discover -p 'test_*.py'
python model.py --steps 400
# Output checkpoint: tiny-model.npz6. Interpretar las cifras
La ejecución local pasó de aproximadamente 3.3004 a 0.9185 en 400 pasos. Mejoró la predicción del corpus, pero genera incoherencias: es esperable a esta escala.
La pérdida usa el entrenamiento, no datos reservados. Memorizar no demuestra generalización. Más pasos pueden sobreajustar. Prepara validación no solapada y compara ambas pérdidas con prefijos fijos. Un número menor no demuestra utilidad ni verdad.
Measured local run, seed 7, supplied corpus, 400 training steps:
parameters: 6303
training_loss_before: 3.3004
training_loss_after: 0.9185
sample begins: seatimin whean sert tes s vitg ...
Interpretation: the model learned local character patterns.
It still produces nonsense and has not demonstrated general knowledge.
These are training-set measurements, not held-out quality scores.7. Recargar y generar
La inferencia carga pesos y muestrea sin actualizarlos. Avanza una ventana de ocho caracteres. Los desconocidos producen error claro. El archivo NumPy se carga sin pickle; aun así verifica la procedencia de los modelos.
# The training objective: predict the NEXT character.
# Input: "seating "
# Target: the following character in the training text.
# In the supplied model, each input contains exactly 8 characters.
# Generation uses the last 8 characters to predict a distribution,
# samples one character, appends it, then repeats.
from model import load
model = load("tiny-model.npz")
print(model.generate("seat", count=120, seed=42))7b. Usar el modelo desde una aplicación local
Después de entrenar, server.py carga tiny-model.npz y ofrece POST /generate local. Envía prefix="seat" y count=40: devuelve generated_text y educational=true. Es texto educativo, no consejo fiable.
El entrenamiento sigue en Python/NumPy. Las pestañas llaman al mismo modelo, no lo entrenan en PHP o navegador. El servidor limita entradas y solo escucha localhost; producción requiere framework, autenticación y controles adecuados.
# First train the model with the instructions above.
# Keep your Python virtual environment active.
python server.py
# Keep this terminal open. The API listens only on 127.0.0.1:8765.
# Browser demo: open http://127.0.0.1:8765/
# In ANOTHER terminal, from this same project folder:
# Choose your client (Python needs the same environment).
python client.py
# OR: Node.js 22+
node client.mjs
# OR: PHP 8.1+ with HTTP URL wrappers enabled
php client.php7c. Elegir el lenguaje cliente
Elige pestaña y ejecuta el archivo correspondiente. Python, Node.js y PHP usan URL local completa. JavaScript funciona en http://127.0.0.1:8765/ servido por la demo; /generate mantiene el mismo origen. No lo pegues en otra web. Pulsa Generate 40 characters.
Todos mandan el mismo JSON y reciben la misma muestra determinista. educational=true no demuestra comprensión. Si falla, comprueba entrenamiento, servidor y puerto. Carácter desconocido o count fuera de 1..200 devuelve error legible.
import json
from urllib.request import Request,urlopen
request=Request('http://127.0.0.1:8765/generate',
data=json.dumps({'prefix':'seat','count':40}).encode(),
headers={'Content-Type':'application/json'},method='POST')
with urlopen(request,timeout=10) as response:
result=json.load(response)
print(json.dumps(result,indent=2))// Node.js 22+. First run the Python model and local server.
const response = await fetch('http://127.0.0.1:8765/generate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({prefix: 'seat', count: 40}),
signal: AbortSignal.timeout(10_000),
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || 'Generation failed');
console.log(JSON.stringify(result, null, 2));// Run on http://127.0.0.1:8765/ served by this local project.
// That page already contains <button id="generate"> and <pre id="result">.
document.querySelector('#generate').addEventListener('click', async () => {
const output = document.querySelector('#result');
try {
const response = await fetch('/generate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({prefix: 'seat', count: 40}),
signal: AbortSignal.timeout(10_000),
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || 'Generation failed');
output.textContent = JSON.stringify(result, null, 2);
} catch (error) {
output.textContent = error.message;
}
});<?php
// PHP 8.1+ with HTTP URL wrappers enabled; run the Python server first.
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode(['prefix' => 'seat', 'count' => 40]),
'timeout' => 10,
'ignore_errors' => true,
'follow_location' => 0,
]]);
$raw = file_get_contents('http://127.0.0.1:8765/generate', false, $context);
if ($raw === false) throw new RuntimeException('Local server unavailable');
$result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if (isset($result['error'])) throw new RuntimeException($result['error']);
echo json_encode($result, JSON_PRETTY_PRINT).PHP_EOL;{
"model": "tiny-character-mlp",
"educational": true,
"generated_text": "seatimin whean sert tes s vitg bemovge peans"
}8. Relación con un Transformer real
El Transformer autorregresivo también predice tokens, pero con más profundidad y datos. La atención combina posiciones permitidas y la máscara causal impide leer el futuro. Añade posición, bloques feed-forward, residuos y normalización. Este MLP no implementa esas partes.
Continúa con un Transformer causal separado y tests de tensores/máscara o el curso Stanford. Para un producto compara un modelo preentrenado con licencia adecuada. LoRA adapta pesos mediante actualizaciones de rango bajo; no crea un modelo fundacional desde nada. Verifica derechos y evaluación.
9. IA como profesora y revisora
El primer prompt exige explicar y ejecutar con cifras y muestra. Decir que tu propio ChatGPT está listo sería falso. El segundo prepara una adaptación real sin iniciar un entrenamiento caro de forma implícita.
Act as a patient machine-learning teacher. Inspect this tiny NumPy character
language model and explain its data flow before modifying anything.
Identify the vocabulary, eight-character context, embeddings, hidden layer,
softmax probabilities, next-character targets, loss and gradient update.
Run the local tests, train with 400 steps, save the checkpoint and reload it.
Report actual parameter count, training loss before/after and a generated sample.
Explain why reduced training loss is not evidence of held-out quality or truth.
Do not describe this 6,303-parameter educational MLP as a large Transformer,
a chat assistant or a production LLM. Do not download a model or private dataset.
Propose one next learning experiment with a measurable success criterion.10. Resultado y siguiente paso
Has entrenado, medido, guardado, recargado y generado. Distingues modelo, aplicación, RAG y ajuste. Cambia una variable con evaluación reservada y compara la referencia. Para negocio empieza por la necesidad y compara modelos existentes y RAG.
Before proposing a REAL model adaptation, ask for the task, licensed base model,
data rights, deployment language, evaluation set, hardware and budget.
Compare RAG, prompting, LoRA fine-tuning and training from scratch for that task.
If fine-tuning is justified, propose an isolated reproducible experiment:
base-model revision, tokenizer, non-overlapping train/validation/test splits,
training configuration, checkpoint policy, metrics and comparison with baseline.
Include privacy leakage, memorization and unsupported-answer evaluations.
State estimates and untested assumptions explicitly. Do not start an expensive
training job, publish weights or use private data without the agreed scope.Referencias oficiales y lecturas
- Stanford CS336 · Language Modeling from Scratch
- Vaswani et al. · Attention Is All You Need
- Hugging Face PEFT · LoRA
- NumPy · Random generator
- Python · Local teaching HTTP server
- Node.js · fetch
- PHP · HTTP context options
Llévalo a tu proyecto
Descarga los archivos, guarda la guía o entrega un encargo listo a tu asistente.
Preparar con IA
Copia este prompt inglés en tu asistente. Incluye el artículo, los archivos disponibles y los pasos de preparación.
Ver el prompt de preparación
Read this tutorial as reference material:
https://nethanelfitoussi.com/article/create-language-model-explained?lang=en
Goal: apply the documented workflow to my development project.
Read the existing project instructions, inspect the relevant files and explain the smallest useful setup.
Available starter files:
https://nethanelfitoussi.com/api/media/08d5b33c-5d92-4495-ae4a-729eb570d94d.zip
Download and inspect these files in a temporary folder before using them. Do not overwrite existing project instructions.
If I have not supplied a project folder, ask which development project to use. Do not invent a repository URL.
Additional project setup instructions:
Extract the supplied ZIP into a NEW folder. Read README.md before installing or running code.
Act as a patient machine-learning teacher. Inspect this tiny NumPy character
language model and explain its data flow before modifying anything.
Identify the vocabulary, eight-character context, embeddings, hidden layer,
softmax probabilities, next-character targets, loss and gradient update.
Run the local tests, train with 400 steps, save the checkpoint and reload it.
Report actual parameter count, training loss before/after and a generated sample.
Explain why reduced training loss is not evidence of held-out quality or truth.
Do not describe this 6,303-parameter educational MLP as a large Transformer,
a chat assistant or a production LLM. Do not download a model or private dataset.
Propose one next learning experiment with a measurable success criterion.
Before proposing a REAL model adaptation, ask for the task, licensed base model,
data rights, deployment language, evaluation set, hardware and budget.
Compare RAG, prompting, LoRA fine-tuning and training from scratch for that task.
If fine-tuning is justified, propose an isolated reproducible experiment:
base-model revision, tokenizer, non-overlapping train/validation/test splits,
training configuration, checkpoint policy, metrics and comparison with baseline.
Include privacy leakage, memorization and unsupported-answer evaluations.
State estimates and untested assumptions explicitly. Do not start an expensive
training job, publish weights or use private data without the agreed scope.
Adapt example paths, commands and acceptance criteria to this repository rather than assuming they match.
Give me a short plan, then make the agreed local changes and run the relevant checks. Report actual results and anything not verified. Stop before credentials, paid services, production changes, push, merge or deployment.

