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.
{"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.
{"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.
{"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.
{"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.
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
Converteer elke HuggingFace dataset naar JSONL-formaat voor gebruik met andere trainingspipelines. De to_json-methode schrijft elk voorbeeld als een aparte JSON-regel.
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')
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.
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}')
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.
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')
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.
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)
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.
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)
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.