JSONL para datos de entrenamiento IA
Una guía completa para preparar datos de entrenamiento JSONL para IA y aprendizaje automático. Cubre fine-tuning de OpenAI, Anthropic Claude, datasets de HuggingFace, validación de datos y pipelines de datos a gran escala.
Última actualización: febrero de 2026
Por qué JSONL es el estándar para datos de entrenamiento IA
JSONL (JSON Lines) se ha convertido en el estándar de facto para datos de entrenamiento de IA y aprendizaje automático. Todos los principales proveedores de IA, desde OpenAI hasta Anthropic y HuggingFace, utilizan JSONL como su formato principal para datasets de fine-tuning. La razón es simple: JSONL almacena un ejemplo por línea, lo que hace trivial transmitir datos durante el entrenamiento, mezclar ejemplos, contar registros, dividir en conjuntos de entrenamiento y prueba, y agregar nuevas muestras sin reescribir todo el archivo.
A diferencia de un único arreglo JSON que debe parsearse completamente antes de usarse, los archivos JSONL se pueden procesar línea por línea. Esto importa cuando tus datasets de entrenamiento crecen a millones de ejemplos que abarcan gigabytes. En esta guía, aprenderás los formatos JSONL exactos esperados por OpenAI, Anthropic Claude y HuggingFace, cómo validar y limpiar tus datos, y cómo preparar datasets a gran escala para ejecuciones de entrenamiento en producción.
Formato JSONL para fine-tuning de OpenAI
OpenAI usa JSONL tanto para fine-tuning como para la Batch API. Para hacer fine-tuning de modelos de chat como GPT-4o-mini, cada línea debe contener un arreglo messages con la estructura estándar de completado de chat. Los tres roles, system, user y assistant, definen la conversación que el modelo debe aprender a reproducir.
Cada línea es un ejemplo de entrenamiento. El mensaje system establece el comportamiento, el mensaje user es la entrada y el mensaje assistant es la salida objetivo que el modelo debe aprender.
{"messages": [{"role": "system", "content": "You are a helpful customer support agent for an e-commerce platform."}, {"role": "user", "content": "Where is my order #12345?"}, {"role": "assistant", "content": "I can see that order #12345 was shipped on February 10th via FedEx. The tracking number is FX987654321. Based on the latest update, it should arrive by February 14th."}]}{"messages": [{"role": "system", "content": "You are a helpful customer support agent for an e-commerce platform."}, {"role": "user", "content": "I want to return my purchase."}, {"role": "assistant", "content": "I would be happy to help with your return. Could you please provide the order number and the reason for the return? Our return policy allows returns within 30 days of delivery for most items."}]}
Para conversaciones multi-turno, incluye el historial completo de la conversación. El modelo aprende de cada turno del assistant en el arreglo messages. Así es como enseñas al modelo a manejar preguntas de seguimiento.
{"messages": [{"role": "system", "content": "You are a Python programming tutor."}, {"role": "user", "content": "What is a list comprehension?"}, {"role": "assistant", "content": "A list comprehension is a concise way to create lists in Python. Instead of writing a for loop to build a list, you can write it in one line: [expression for item in iterable if condition]."}, {"role": "user", "content": "Can you show me an example?"}, {"role": "assistant", "content": "Sure! Here is an example that creates a list of squares for even numbers:\n\nsquares = [x**2 for x in range(10) if x % 2 == 0]\n# Result: [0, 4, 16, 36, 64]\n\nThis is equivalent to:\nsquares = []\nfor x in range(10):\n if x % 2 == 0:\n squares.append(x**2)"}]}
OpenAI requiere al menos 10 ejemplos de entrenamiento, pero recomienda 50-100 para mejoras notables. Todos los mensajes deben tener los campos role y content. El mensaje system es opcional pero recomendado para un comportamiento consistente. Usa la CLI de OpenAI para validar tu archivo antes de subirlo: openai tools fine_tunes.prepare_data -f training.jsonl
Formato de entrenamiento de Anthropic Claude
Anthropic usa JSONL para hacer fine-tuning de modelos Claude. El formato utiliza turnos human y assistant en una conversación estructurada. Aunque el formato exacto depende del método de fine-tuning y la versión de la API, el patrón central sigue la estructura de la Messages API.
Cada línea contiene una conversación con un prompt de sistema y mensajes alternados user/assistant. El modelo aprende a producir las respuestas del assistant dado el contexto de la conversación.
{"system": "You are a medical information assistant. Provide accurate, general health information while always recommending consulting a healthcare professional.", "messages": [{"role": "user", "content": "What are the symptoms of vitamin D deficiency?"}, {"role": "assistant", "content": "Common symptoms of vitamin D deficiency include fatigue and tiredness, bone pain and muscle weakness, mood changes including depression, slow wound healing, and frequent infections. However, many people with mild deficiency may not notice symptoms. A blood test is the only way to confirm your vitamin D levels. I recommend speaking with your doctor who can order the appropriate test and recommend supplementation if needed."}]}{"system": "You are a medical information assistant. Provide accurate, general health information while always recommending consulting a healthcare professional.", "messages": [{"role": "user", "content": "Is it safe to take ibuprofen daily?"}, {"role": "assistant", "content": "Taking ibuprofen daily for extended periods can carry risks including stomach ulcers and gastrointestinal bleeding, increased risk of heart attack and stroke, kidney problems with long-term use, and potential interactions with other medications. For occasional pain relief, short-term use at recommended doses is generally considered safe for most adults. However, if you find yourself needing daily pain relief, please consult your doctor. They can evaluate the underlying cause and recommend a safer long-term treatment plan."}]}
Para datos de entrenamiento multi-turno, incluye la conversación completa con mensajes alternados de user y assistant. El modelo aprende el flujo de conversación y cómo mantener el contexto a través de los turnos.
{"system": "You are a cooking assistant specializing in Italian cuisine.", "messages": [{"role": "user", "content": "How do I make carbonara?"}, {"role": "assistant", "content": "Authentic carbonara uses just five ingredients: spaghetti, guanciale (cured pork cheek), eggs, Pecorino Romano cheese, and black pepper. The key technique is tempering the egg mixture with the hot pasta water so it creates a creamy sauce without scrambling."}, {"role": "user", "content": "Can I use bacon instead of guanciale?"}, {"role": "assistant", "content": "While guanciale gives the most authentic flavor, pancetta is a closer substitute. Bacon works in a pinch but its smoky flavor changes the dish significantly. If using bacon, choose an unsmoked variety. Whichever you use, cut it into small pieces and render the fat slowly over medium-low heat until crispy."}]}
Datasets de HuggingFace con JSONL
La biblioteca datasets de HuggingFace soporta nativamente JSONL como formato de entrada. Puedes cargar archivos JSONL locales, transmitir datasets remotos y convertir entre formatos fácilmente. JSONL es el formato recomendado para compartir datasets en el HuggingFace Hub.
Usa la biblioteca datasets para cargar archivos JSONL en un objeto Dataset. Esto proporciona acceso eficiente con mapeo en memoria, división train/test integrada e integración perfecta con la Trainer API.
from datasets import load_dataset# Load a local JSONL filedataset = load_dataset('json', data_files='training.jsonl')print(dataset)# DatasetDict({# train: Dataset({# features: ['messages', 'system'],# num_rows: 5000# })# })# Load with train/test splitdataset = load_dataset('json', data_files={'train': 'train.jsonl','test': 'test.jsonl'})# Stream a large remote datasetdataset = load_dataset('json',data_files='https://example.com/large_dataset.jsonl',streaming=True)for example in dataset['train']:print(example)break
Convierte cualquier dataset de HuggingFace a formato JSONL para usarlo con otros pipelines de entrenamiento. El método to_json escribe cada ejemplo como una línea JSON separada.
from datasets import load_dataset# Load a dataset from the Hubdataset = load_dataset('squad', split='train')# Export to JSONLdataset.to_json('squad_train.jsonl')print(f'Exported {len(dataset)} examples')# Export with specific columnsdataset.select_columns(['question', 'context', 'answers']).to_json('squad_filtered.jsonl')# Process and exportdef format_for_finetuning(example):return {'messages': [{'role': 'user', 'content': example['question']},{'role': 'assistant', 'content': example['answers']['text'][0]}]}formatted = dataset.map(format_for_finetuning, remove_columns=dataset.column_names)formatted.to_json('squad_chat_format.jsonl')
Validación y limpieza de datos
La calidad de los datos de entrenamiento impacta directamente en el rendimiento del modelo. JSON inválido, campos faltantes, ejemplos excesivamente largos y entradas duplicadas pueden degradar los resultados del fine-tuning. Siempre valida y limpia tus archivos JSONL antes de iniciar una ejecución de entrenamiento.
Este script valida cada línea de tu archivo JSONL contra problemas comunes: JSON inválido, campos requeridos faltantes, contenido vacío y longitud de tokens. Ejecútalo antes de subir el archivo para detectar problemas temprano.
import jsonimport sysfrom collections import Counterdef validate_training_data(path: str) -> dict:"""Validate a JSONL file for AI fine-tuning."""stats = Counter()errors = []with open(path, 'r', encoding='utf-8') as f:for line_num, line in enumerate(f, 1):line = line.strip()if not line:continuestats['total'] += 1# Check valid JSONtry:data = json.loads(line)except json.JSONDecodeError as e:errors.append(f'Line {line_num}: Invalid JSON - {e}')stats['invalid_json'] += 1continue# Check messages field existsif 'messages' not in data:errors.append(f'Line {line_num}: Missing "messages" field')stats['missing_messages'] += 1continuemessages = data['messages']# Check message structurefor i, msg in enumerate(messages):if 'role' not in msg:errors.append(f'Line {line_num}, msg {i}: Missing "role"')stats['missing_role'] += 1if 'content' not in msg:errors.append(f'Line {line_num}, msg {i}: Missing "content"')stats['missing_content'] += 1elif not msg['content'].strip():errors.append(f'Line {line_num}, msg {i}: Empty content')stats['empty_content'] += 1# Check has at least one assistant messageroles = [m.get('role') for m in messages]if 'assistant' not in roles:errors.append(f'Line {line_num}: No assistant message')stats['no_assistant'] += 1stats['valid'] += 1return {'stats': dict(stats), 'errors': errors[:50]}result = validate_training_data('training.jsonl')print(f"Total: {result['stats'].get('total', 0)}")print(f"Valid: {result['stats'].get('valid', 0)}")if result['errors']:print(f"\nFirst {len(result['errors'])} errors:")for err in result['errors']:print(f' {err}')
Los ejemplos de entrenamiento duplicados desperdician cómputo y pueden sesgar el modelo hacia patrones sobrerrepresentados. Este script elimina duplicados exactos basándose en el hash del contenido de cada línea.
import jsonimport hashlibdef deduplicate_jsonl(input_path: str, output_path: str) -> dict:"""Remove duplicate training examples from a JSONL file."""seen_hashes = set()total = 0unique = 0with open(input_path, 'r') as fin, open(output_path, 'w') as fout:for line in fin:line = line.strip()if not line:continuetotal += 1# Hash the normalized JSON to catch formatting differencesdata = json.loads(line)canonical = json.dumps(data, sort_keys=True)content_hash = hashlib.sha256(canonical.encode()).hexdigest()if content_hash not in seen_hashes:seen_hashes.add(content_hash)fout.write(json.dumps(data) + '\n')unique += 1duplicates = total - uniqueprint(f'Total: {total}, Unique: {unique}, Removed: {duplicates}')return {'total': total, 'unique': unique, 'duplicates': duplicates}deduplicate_jsonl('training.jsonl', 'training_deduped.jsonl')
Preparación de datos a gran escala
Los datasets de entrenamiento en producción a menudo contienen cientos de miles o millones de ejemplos. A esta escala, necesitas pipelines automatizados para mezclar, dividir y fragmentar tus datos JSONL. Una preparación adecuada previene problemas de entrenamiento como el olvido catastrófico y garantiza experimentos reproducibles.
Este script mezcla tus datos aleatoriamente y los divide en conjuntos de entrenamiento y prueba. La mezcla es crítica porque los archivos JSONL a menudo se generan en orden, y entrenar con datos ordenados puede llevar a una mala generalización.
import jsonimport randomfrom pathlib import Pathdef prepare_training_data(input_path: str,output_dir: str,test_ratio: float = 0.1,seed: int = 42) -> dict:"""Shuffle and split JSONL into train/test sets."""random.seed(seed)output = Path(output_dir)output.mkdir(parents=True, exist_ok=True)# Load all examplesexamples = []with open(input_path, 'r') as f:for line in f:line = line.strip()if line:examples.append(json.loads(line))# Shufflerandom.shuffle(examples)# Splitsplit_idx = int(len(examples) * (1 - test_ratio))train_data = examples[:split_idx]test_data = examples[split_idx:]# Write output filestrain_path = output / 'train.jsonl'test_path = output / 'test.jsonl'for data, path in [(train_data, train_path), (test_data, test_path)]:with open(path, 'w') as f:for example in data:f.write(json.dumps(example) + '\n')print(f'Train: {len(train_data)} examples -> {train_path}')print(f'Test: {len(test_data)} examples -> {test_path}')return {'train': len(train_data), 'test': len(test_data)}prepare_training_data('all_examples.jsonl','./prepared_data/',test_ratio=0.1,seed=42)
Cuando los datasets son demasiado grandes para un solo archivo o necesitas distribuir el entrenamiento entre múltiples GPUs, fragmenta el archivo JSONL en partes más pequeñas. Cada fragmento puede procesarse de forma independiente.
import jsonfrom pathlib import Pathdef shard_jsonl(input_path: str,output_dir: str,shard_size: int = 50000) -> int:"""Split a large JSONL file into smaller shards."""output = Path(output_dir)output.mkdir(parents=True, exist_ok=True)shard_num = 0line_count = 0current_file = Nonewith open(input_path, 'r') as fin:for line in fin:line = line.strip()if not line:continueif line_count % shard_size == 0:if current_file:current_file.close()shard_path = output / f'shard_{shard_num:04d}.jsonl'current_file = open(shard_path, 'w')shard_num += 1current_file.write(line + '\n')line_count += 1if current_file:current_file.close()print(f'Created {shard_num} shards from {line_count} examples')return shard_numshard_jsonl('large_dataset.jsonl', './shards/', shard_size=50000)
Valida tus datos de entrenamiento online
Usa nuestras herramientas gratuitas en el navegador para validar, formatear y convertir tus datos de entrenamiento JSONL antes de subirlos a OpenAI, Anthropic o HuggingFace.