JSONL voor AI-trainingsdata

Een uitgebreide gids voor het voorbereiden van JSONL-trainingsdata voor AI en machine learning. Behandelt OpenAI fine-tuning, Anthropic Claude, HuggingFace datasets, datavalidatie en grootschalige datapipelines.

Laatst bijgewerkt: februari 2026

Waarom JSONL de standaard is voor AI-trainingsdata

JSONL (JSON Lines) is de de facto standaard geworden voor AI- en machine learning-trainingsdata. Elke grote AI-aanbieder, van OpenAI tot Anthropic tot HuggingFace, gebruikt JSONL als hun primaire formaat voor fine-tuning datasets. De reden is eenvoudig: JSONL slaat één voorbeeld per regel op, waardoor het triviaal is om data te streamen tijdens training, voorbeelden te shufflen, records te tellen, te splitsen in train- en testsets, en nieuwe samples toe te voegen zonder het hele bestand te herschrijven.

In tegenstelling tot een enkele JSON-array die volledig moet worden geparseerd voor gebruik, kunnen JSONL-bestanden regel voor regel worden verwerkt. Dit is van belang wanneer je trainingsdatasets groeien tot miljoenen voorbeelden van gigabytes. In deze gids leer je de exacte JSONL-formaten die OpenAI, Anthropic Claude en HuggingFace verwachten, hoe je je data valideert en opschoont, en hoe je grootschalige datasets voorbereidt voor productie-trainingsruns.

OpenAI Fine-tuning JSONL-formaat

OpenAI gebruikt JSONL voor zowel fine-tuning als de Batch API. Voor het fine-tunen van chatmodellen zoals GPT-4o-mini moet elke regel een messages-array bevatten met de standaard chat completion-structuur. De drie rollen, system, user en assistant, definiëren het gesprek dat het model moet leren reproduceren.

Elke regel is één trainingsvoorbeeld. Het system-bericht stelt het gedrag in, het user-bericht is de invoer, en het assistant-bericht is de doeluitvoer die het model moet leren.

Chat Completion-trainingsformaat
{"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."}]}

Voor multi-turn gesprekken neem je de volledige gespreksgeschiedenis op. Het model leert van elke assistant-beurt in de messages-array. Zo leer je het model om vervolgvragen te behandelen.

Multi-turn gespreks-training
{"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 vereist minimaal 10 trainingsvoorbeelden, maar beveelt 50-100 aan voor merkbare verbeteringen. Alle berichten moeten role- en content-velden hebben. Het system-bericht is optioneel maar aanbevolen voor consistent gedrag. Gebruik de OpenAI CLI om je bestand te valideren voor het uploaden: openai tools fine_tunes.prepare_data -f training.jsonl

Anthropic Claude-trainingsformaat

Anthropic gebruikt JSONL voor het fine-tunen van Claude-modellen. Het formaat gebruikt human- en assistant-beurten in een gestructureerd gesprek. Hoewel het exacte formaat afhangt van de fine-tuning methode en API-versie, volgt het kernpatroon de Messages API-structuur.

Elke regel bevat een gesprek met een system prompt en afwisselende user/assistant-berichten. Het model leert de assistant-antwoorden te produceren op basis van de gesprekscontext.

Claude Messages-formaat
{"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."}]}

Voor multi-turn trainingsdata neem je het volledige gesprek op met afwisselende user- en assistant-berichten. Het model leert de gespreksstroom en hoe het context behoudt over meerdere beurten.

Multi-turn Claude-gesprekken
{"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."}]}

HuggingFace Datasets met JSONL

De datasets-bibliotheek van HuggingFace ondersteunt JSONL native als invoerformaat. Je kunt lokale JSONL-bestanden laden, remote datasets streamen en eenvoudig converteren tussen formaten. JSONL is het aanbevolen formaat voor het delen van datasets op de HuggingFace Hub.

Gebruik de datasets-bibliotheek om JSONL-bestanden te laden in een Dataset-object. Dit biedt efficiënte memory-mapped toegang, ingebouwde train/test-splitsing en naadloze integratie met de Trainer API.

JSONL Datasets laden
from datasets import load_dataset
# Load a local JSONL file
dataset = load_dataset('json', data_files='training.jsonl')
print(dataset)
# DatasetDict({
# train: Dataset({
# features: ['messages', 'system'],
# num_rows: 5000
# })
# })
# Load with train/test split
dataset = load_dataset('json', data_files={
'train': 'train.jsonl',
'test': 'test.jsonl'
})
# Stream a large remote dataset
dataset = load_dataset(
'json',
data_files='https://example.com/large_dataset.jsonl',
streaming=True
)
for example in dataset['train']:
print(example)
break

Converteer elke HuggingFace dataset naar JSONL-formaat voor gebruik met andere trainingspipelines. De to_json-methode schrijft elk voorbeeld als een aparte JSON-regel.

Exporteren naar JSONL
from datasets import load_dataset
# Load a dataset from the Hub
dataset = load_dataset('squad', split='train')
# Export to JSONL
dataset.to_json('squad_train.jsonl')
print(f'Exported {len(dataset)} examples')
# Export with specific columns
dataset.select_columns(['question', 'context', 'answers']).to_json(
'squad_filtered.jsonl'
)
# Process and export
def 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')

Datavalidatie en opschoning

De kwaliteit van trainingsdata heeft direct invloed op de modelprestaties. Ongeldige JSON, ontbrekende velden, te lange voorbeelden en dubbele vermeldingen kunnen allemaal de fine-tuning resultaten verslechteren. Valideer en schoon je JSONL-bestanden altijd op voordat je een trainingsrun start.

Dit script valideert elke regel van je JSONL-bestand op veelvoorkomende problemen: ongeldige JSON, ontbrekende verplichte velden, lege inhoud en tokenlengte. Voer het uit voor het uploaden om problemen vroegtijdig op te sporen.

JSONL Trainingsdata Validator
import json
import sys
from collections import Counter
def 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:
continue
stats['total'] += 1
# Check valid JSON
try:
data = json.loads(line)
except json.JSONDecodeError as e:
errors.append(f'Line {line_num}: Invalid JSON - {e}')
stats['invalid_json'] += 1
continue
# Check messages field exists
if 'messages' not in data:
errors.append(f'Line {line_num}: Missing "messages" field')
stats['missing_messages'] += 1
continue
messages = data['messages']
# Check message structure
for i, msg in enumerate(messages):
if 'role' not in msg:
errors.append(f'Line {line_num}, msg {i}: Missing "role"')
stats['missing_role'] += 1
if 'content' not in msg:
errors.append(f'Line {line_num}, msg {i}: Missing "content"')
stats['missing_content'] += 1
elif not msg['content'].strip():
errors.append(f'Line {line_num}, msg {i}: Empty content')
stats['empty_content'] += 1
# Check has at least one assistant message
roles = [m.get('role') for m in messages]
if 'assistant' not in roles:
errors.append(f'Line {line_num}: No assistant message')
stats['no_assistant'] += 1
stats['valid'] += 1
return {'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}')

Dubbele trainingsvoorbeelden verspillen rekenkracht en kunnen het model vertekenen naar oververtegenwoordigde patronen. Dit script verwijdert exacte duplicaten op basis van de inhoudshash van elke regel.

Deduplicatie-script
import json
import hashlib
def deduplicate_jsonl(input_path: str, output_path: str) -> dict:
"""Remove duplicate training examples from a JSONL file."""
seen_hashes = set()
total = 0
unique = 0
with open(input_path, 'r') as fin, open(output_path, 'w') as fout:
for line in fin:
line = line.strip()
if not line:
continue
total += 1
# Hash the normalized JSON to catch formatting differences
data = 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 += 1
duplicates = total - unique
print(f'Total: {total}, Unique: {unique}, Removed: {duplicates}')
return {'total': total, 'unique': unique, 'duplicates': duplicates}
deduplicate_jsonl('training.jsonl', 'training_deduped.jsonl')

Grootschalige datavoorbereiding

Productie-trainingsdatasets bevatten vaak honderdduizenden of miljoenen voorbeelden. Op deze schaal heb je geautomatiseerde pipelines nodig voor het shufflen, splitsen en sharden van je JSONL-data. Goede voorbereiding voorkomt trainingsproblemen zoals catastrophic forgetting en zorgt voor reproduceerbare experimenten.

Dit script shufflet je data willekeurig en splitst deze in train- en testsets. Shufflen is cruciaal omdat JSONL-bestanden vaak op volgorde worden gegenereerd, en trainen op geordende data kan leiden tot slechte generalisatie.

Train/Test-splitsing en shuffle-pipeline
import json
import random
from pathlib import Path
def 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 examples
examples = []
with open(input_path, 'r') as f:
for line in f:
line = line.strip()
if line:
examples.append(json.loads(line))
# Shuffle
random.shuffle(examples)
# Split
split_idx = int(len(examples) * (1 - test_ratio))
train_data = examples[:split_idx]
test_data = examples[split_idx:]
# Write output files
train_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
)

Wanneer datasets te groot zijn voor één bestand of je training over meerdere GPU's wilt verdelen, splits het JSONL-bestand in kleinere stukken. Elk shard kan onafhankelijk worden verwerkt.

Bestandssplitsing voor gedistribueerde training
import json
from pathlib import Path
def 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 = 0
line_count = 0
current_file = None
with open(input_path, 'r') as fin:
for line in fin:
line = line.strip()
if not line:
continue
if 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 += 1
current_file.write(line + '\n')
line_count += 1
if current_file:
current_file.close()
print(f'Created {shard_num} shards from {line_count} examples')
return shard_num
shard_jsonl('large_dataset.jsonl', './shards/', shard_size=50000)

Valideer je trainingsdata online

Gebruik onze gratis browsertools om je JSONL-trainingsdata te valideren, formatteren en converteren voordat je deze uploadt naar OpenAI, Anthropic of HuggingFace.

Klaar om je trainingsdata voor te bereiden?

Valideer, formatteer en inspecteer je JSONL-trainingsbestanden rechtstreeks in je browser. Geen uploads, geen registratie, 100% privé.

Veelgestelde vragen

JSONL-trainingsdata — OpenAI, Claude & Hugging Face forma...