Instructions to use ToetsChecker-Research/boterham with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ToetsChecker-Research/boterham with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ToetsChecker-Research/boterham") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("ToetsChecker-Research/boterham") model = AutoModelForCausalLM.from_pretrained("ToetsChecker-Research/boterham", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use ToetsChecker-Research/boterham with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ToetsChecker-Research/boterham" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ToetsChecker-Research/boterham", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/ToetsChecker-Research/boterham
- SGLang
How to use ToetsChecker-Research/boterham with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "ToetsChecker-Research/boterham" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ToetsChecker-Research/boterham", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "ToetsChecker-Research/boterham" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ToetsChecker-Research/boterham", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use ToetsChecker-Research/boterham with Docker Model Runner:
docker model run hf.co/ToetsChecker-Research/boterham
Boterham 0.5B
A Dutch language model of half a billion parameters, trained from scratch on open data only, for about € 120 of rented compute. It reads a text you give it, answers questions about that text, and says "Ik weet dit niet." when the answer is not there.
English
At a glance
| Parameters | 488.3M total; 447.4M excluding tied token embeddings |
| Architecture | Decoder-only Transformer, RMSNorm, GQA, RoPE, SwiGLU, QK-norm |
| Layers / hidden size | 26 / 1,280 |
| Attention heads | 20 query heads, 4 key/value heads |
| Context length | 2,048 tokens |
| Vocabulary | 32,002-token byte-level BPE, trained on the corpus itself |
| Pretraining | 8.0 billion tokens on one NVIDIA H100 |
| Instruction tuning | four rounds on one to four NVIDIA L4 GPUs |
| Training data | ToetsChecker-Research/beleg, public domain and open licences only |
| Compute cost | € 121.60 at Scaleway, of which € 100 was a free credit |
| Languages | Primarily Dutch; some English |
| License | Apache 2.0 |
This repository holds round 4 of the instruction tuning (November 2026).
The September release (round 1) is still available under the git tag
ronde-1: from_pretrained(model_id, revision="ronde-1").
The Hugging Face export uses the Qwen3ForCausalLM implementation because the
architectures are identical. Boterham does not use Qwen weights and is not a
fine-tune of Qwen; tokenizer and weights were trained from scratch.
The figures on this page have Dutch labels. Each one is described in the text next to it.
Results
EuroEval 18.0.0, ten iterations per task, with the 95% interval over the iterations. WikiLingua-nl ran on EuroEval 15.16.0 because that version still reports BERTScore, the metric behind the GPT-NL figure.
The fair opponent is GPT-NL after instruction tuning (epoch 2, "our data (IT)" in figure 21 of TNO report GPTNL-DEL-4002), because Boterham is instruction-tuned as well. GPT-NL has 26B parameters, about fifty times as many. Its base model is listed for reference.
| Task | Metric | Boterham 0.5B r4 | GPT-NL IT | GPT-NL base |
|---|---|---|---|---|
| ScaLA-nl (grammar) | MCC | 29.50 ± 3.00 | 1.18 | 19 |
| SQuAD-nl (reading) | EM | 36.62 ± 0.97 | 34.37 | 51 |
| CoNLL-nl (names) | micro-F1 w/o MISC | 49.46 ± 2.76 | 47.82 | ~36 |
| DBRD (sentiment) | MCC | 40.15 ± 4.15 | 85.36 | ~90 |
| WikiLingua-nl (summaries) | BERTScore | 56.80 | 61.97 | 61 |
| HellaSwag-nl (common sense) | MCC | 1.15 ± 0.75 | 3.71 | -2 |
| MMLU-nl (knowledge) | MCC | -2.13 ± 0.86 | 2.28 | 2 |
Boterham is ahead of the instruction-tuned GPT-NL on grammar, reading and names, and behind it on sentiment, summarization, common sense and knowledge. On MMLU-nl and HellaSwag-nl both models are at chance level (MCC 0 is guessing), so the difference there says nothing.
The chart below shows every round of Boterham on the same seven tasks. Each round of instruction data targeted what the previous round could not do. Round 3 added grammar judgements, which lifted ScaLA-nl above zero. Round 4 added name recognition in the CoNLL-nl format and sentiment, which is where those two tasks jump.
What it is good at, and what not
Good: a short answer to a question about a text you supply, and refusing with "Ik weet dit niet." when the text does not contain the answer.
Weak: summaries are short and mostly take the opening sentence of the text. Facts without a text in the prompt, open conversation, creative writing, code and anything after its training data are unreliable. Even on its strong tasks it gives plausible but wrong answers on free-form input. In our own test with eight questions on two Rijksoverheid.nl pages it answered two of the six answerable questions correctly, refused two it could have answered, gave two wrong answers, and refused both questions whose answer was not in the text. Check every answer against the text.
How to prompt it
The model was trained and measured on fixed instructions. It does best when you use them literally.
Reading question:
Beantwoord de vraag uitsluitend op basis van de tekst. Staat het antwoord er niet in, antwoord dan precies: Ik weet dit niet.
Tekst: {text on one line}
Vraag: {question}
Summary:
Document: {text on one line}
Schrijf een samenvatting van het bovenstaande document.
Put the text on one line (replace blank lines by spaces). With paragraph breaks the model tends to copy a whole paragraph instead of the answer. A cheap safeguard that removes most made-up answers: only accept an answer that appears literally in the text, and treat anything else as "Ik weet dit niet."
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "ToetsChecker-Research/boterham"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.float32)
tekst = "De vergadering begint vrijdag om 14.00 uur in Utrecht."
vraag = "Hoe laat begint de vergadering?"
messages = [{
"role": "user",
"content": (
"Beantwoord de vraag uitsluitend op basis van de tekst. Staat het "
"antwoord er niet in, antwoord dan precies: Ik weet dit niet.\n\n"
f"Tekst: {' '.join(tekst.split())}\n\nVraag: {vraag}"
),
}]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt")
with torch.inference_mode():
output = model.generate(**inputs, max_new_tokens=64, do_sample=False,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id)
raw = tokenizer.decode(output[0, inputs.input_ids.shape[1]:], skip_special_tokens=False)
antwoord = raw.split("<|einde_denken|>")[-1].split("<|einde|>")[0].strip()
print(antwoord if antwoord.rstrip(".") in tekst else "Ik weet dit niet.")
It runs on a laptop CPU: a question about a page of text takes two to six seconds on two cores.
Training data
The corpus has 10.2 billion tokens from 30 sources: Dutch case law, parliamentary proceedings, legislation, government publications, Wikimedia, EUR-Lex, Europarl, and open English books and articles. Everything is public domain under Dutch or Belgian copyright law, or under an open licence; there is no web crawl and no text generated by another model. Of the corpus 39% is Dutch; the training mix raised that to 68% by weighting.
Per document the dataset records source, licence and retrieval date. It is published separately as ToetsChecker-Research/beleg.
Training
Pretraining. 8.0 billion tokens on one H100, with warmup, a long stable phase and a decaying learning rate at the end. Final validation loss 1.835 (perplexity 6.3) on 49.3M tokens that were never trained on.
Instruction tuning. Four rounds, each continuing from the previous one. Loss counts only answer tokens.
| Round | Examples | Steps | Hardware |
|---|---|---|---|
| 1 | 575,211 | 4,786 (2 epochs) | 4x NVIDIA L4 |
| 2 | 315,248 | 1,006 (1 epoch) | 1x NVIDIA L4 |
| 3 | 210,122 | 1,213 (1 epoch) | 1x NVIDIA L4 |
| 4 | 222,316 | 1,247 (1 epoch) | 1x NVIDIA L4 |
Rounds 2 to 4 used a peak learning rate of 2e-5. All instruction data is
derived from the corpus or from human-written open sets (SQuAD-nl, OASST, Aya,
Dolly, Tatoeba) and checked afterwards: an extractive answer must appear
literally in its context, an abstention example must not contain the answer,
and examples that share a 13-gram with a EuroEval test item are removed. Round
4 is published as the instructie-r4 configuration of the dataset.
Cost and footprint
All training ran on rented GPUs at Scaleway in Paris: € 121.60 in total, of which a € 100 credit, so € 21.60 paid. GPT-NL received € 13.5 million in public funding.
Scaleway reports 2.0 kg CO2e and 1.4 litres of water for all of it, roughly 19 km by car and ten seconds of showering.
Files in this repository
model.safetensorsand the tokenizer files: the round 4 model.ronde4/beste.pt,config.json,log.csv: native PyTorch checkpoint and training log of round 4.sft-0.5B/,boterham-0.5B/: round 1 and pretraining checkpoints.figuren/: the figures on this page.
Limitations
A small experimental model with limited world knowledge. It can produce incorrect, made-up or biased text. Do not trust claims about people, dates, numbers, law, medicine or current events without the source in front of you. The context window of 2,048 tokens is short; texts longer than about 6,000 characters do not fit. Formal written Dutch dominates the training data, so casual language is weaker.
Nederlands
In het kort
Boterham 0.5B is een Nederlandstalig taalmodel van een half miljard parameters, vanaf nul getraind op uitsluitend open data, voor ongeveer € 120 aan gehuurde rekenkracht. Het leest een tekst die je meegeeft, beantwoordt vragen over die tekst, en zegt "Ik weet dit niet." als het antwoord er niet in staat.
Dit is ronde 4 van het finetunen (november 2026). De versie van september
(ronde 1) staat nog in de repository onder de git-tag ronde-1.
De export gebruikt Qwen3ForCausalLM omdat de architectuur gelijk is.
Boterham gebruikt geen Qwen-gewichten; tokenizer en gewichten zijn vanaf
nul getraind.
Scores
EuroEval 18.0.0, tien iteraties per taak. WikiLingua-nl op EuroEval 15.16.0, omdat die versie nog BERTScore geeft, de maat achter het getal van GPT-NL. De eerlijke tegenstander is GPT-NL na het finetunen (TNO-rapport GPTNL-DEL-4002, figuur 21), een model van 26 miljard parameters. Het scorebord bovenaan en de grafiek per ronde in de Engelse sectie laten dezelfde getallen zien.
| Taak | Maat | Boterham 0.5B r4 | GPT-NL IT | GPT-NL basis |
|---|---|---|---|---|
| ScaLA-nl (grammatica) | MCC | 29,50 ± 3,00 | 1,18 | 19 |
| SQuAD-nl (lezen) | EM | 36,62 ± 0,97 | 34,37 | 51 |
| CoNLL-nl (namen) | micro-F1 zonder MISC | 49,46 ± 2,76 | 47,82 | ~36 |
| DBRD (sentiment) | MCC | 40,15 ± 4,15 | 85,36 | ~90 |
| WikiLingua-nl (samenvatten) | BERTScore | 56,80 | 61,97 | 61 |
| HellaSwag-nl (gezond verstand) | MCC | 1,15 ± 0,75 | 3,71 | -2 |
| MMLU-nl (kennis) | MCC | -2,13 ± 0,86 | 2,28 | 2 |
Boterham staat voor op grammatica, lezen en namen, en achter op sentiment, samenvatten, gezond verstand en kennis. Op MMLU-nl en HellaSwag-nl zitten beide modellen op gokniveau.
Waar het goed in is, en waarin niet
Goed: een kort antwoord op een vraag over een tekst die je zelf meegeeft, en "Ik weet dit niet." als het antwoord er niet in staat.
Zwak: samenvattingen zijn kort en nemen meestal de openingszin over. Feiten zonder tekst erbij, vrij gesprek, creatief schrijven, code en alles van na de trainingsdata zijn onbetrouwbaar. Ook op zijn sterke taken geeft het op vrije invoer soms een fout antwoord dat er aannemelijk uitziet. In een eigen proef met acht vragen over twee pagina's van Rijksoverheid.nl gaf het op twee van de zes beantwoordbare vragen het goede antwoord, weigerde het er twee die wel te beantwoorden waren, gaf het twee foute antwoorden, en weigerde het terecht beide vragen waarvan het antwoord niet in de tekst stond. Controleer elk antwoord tegen de tekst.
Zo vraag je het
Gebruik letterlijk de opdrachten uit de Engelse sectie, met de tekst op één regel: met witregels erin neemt het model vaak een hele alinea over in plaats van het antwoord. Accepteer daarna alleen een antwoord dat letterlijk in de tekst staat; dat haalt de meeste verzonnen antwoorden weg.
Data, training en kosten
Het corpus telt 10,2 miljard tokens uit 30 bronnen: rechtspraak, Handelingen,
wetten, overheidspublicaties, Wikimedia, EUR-Lex, Europarl en open Engelse
boeken en artikelen. Alles is publiek domein of open gelicentieerd; er zit
geen geschraapt web in en geen tekst van een ander taalmodel. Het corpus is
voor 39% Nederlands, de trainingsmix voor 68%. Per document staan herkomst,
licentie en ophaaldatum in
ToetsChecker-Research/beleg;
de data van ronde 4 staat daar als configuratie instructie-r4.
Voortrainen: 8,0 miljard tokens op één H100, validatieverlies 1,835 (perplexiteit 6,3). Daarna vier finetunerondes op L4-kaarten (tabel in de Engelse sectie). Alles samen kostte € 121,60 bij Scaleway, waarvan € 100 tegoed, dus € 21,60 zelf betaald, en volgens Scaleway 2,0 kg CO2e en 1,4 liter water. GPT-NL kreeg € 13,5 miljoen overheidssteun.
Beperkingen
Een klein experimenteel model met beperkte wereldkennis. Het kan onjuiste, verzonnen of bevooroordeelde tekst geven. Vertrouw geen uitspraken over personen, datums, getallen, recht, geneeskunde of actualiteit zonder de bron ernaast. Het contextvenster van 2.048 tokens is kort: teksten boven ongeveer 6.000 tekens passen er niet in. Formeel geschreven Nederlands overheerst in de trainingsdata, dus spreektaal gaat minder goed.
- Downloads last month
- 98





