Distill Your Docs: an 8B Teacher, a 0.6B Student, and What Actually Transfers

Adham Sersour
1 min read
Distill Your Docs: an 8B Teacher, a 0.6B Student, and What Actually Transfers

An 8B reasoning model summarizes and labels documents for a retrieval index very well, at 10 seconds per document. I distilled the task into two 0.6B students on a Mac, each taught by a different 8B teacher. One became a perfect classifier, the other a faithful writer. Neither got both, and that is the real lesson.

Model Distillation

An 8B reasoning model summarizes and labels documents for a retrieval index very well, at 10 seconds per document. A 0.6B model answers in under a second but classifies documents at chance level. So I made two different 8B teachers each write a textbook, and trained a 0.6B student on each, on a Mac, in about twelve minutes per student. One student became a perfect classifier. The other became a faithful writer. Neither became both, and that is the finding.

8x

faster than the 8B teacher

100%

ground-truth classification (base model: 36%)

~12 min

of training, 4.1 GB, one laptop

🎯 The task that eats 10 seconds per document

Every document that enters a RAG platform (retrieval-augmented generation: search finds the relevant documents, and an LLM answers from them) needs the same treatment before it reaches the index. The pipeline must produce a faithful 3-sentence summary plus five categorical facets: what kind of section this is, how specific to the company it is, how numeric, how forward-looking, and for which audience. Everything comes out as one JSON object, so retrieval can filter and rank on these fields. It is the most repetitive LLM task in the whole pipeline. It is also one of the easiest to get subtly wrong: when a summary drifts away from its document, every search that retrieves it returns wrong information. To study this end to end, I built the enrichment stage on a public corpus of corporate filings: 250 section excerpts from real SEC 10-K annual reports. This is the kind of dense, formal document that financial and legal RAG products process every day.

An 8B model, meaning 8 billion parameters, does this job well. I used deepseek-r1:8b, a reasoning model: it writes out intermediate thinking steps, called the reasoning trace, before giving its final answer, and it runs locally through ollama. Its summaries pass a strict faithfulness check 98% of the time. Faithful has a precise meaning here: a separate judge model reads the document and the summary, and confirms that every claim in the summary is supported by the document (the protocol section shows how I tested the judge itself). The summaries also capture each section's central point every time, and the model identifies the section type correctly on 98.4% of the 250 documents. The price is latency: 10.1 seconds per document at the median on my Mac, and over 12 seconds at the 90th percentile when the reasoning trace runs long. At thousands of documents, that is the bottleneck of the whole ingestion pipeline. A hosted API would be faster, but it bills per document, forever. And the documents leave your machine, which many corpora cannot accept.

The obvious reaction is the one you probably just had: this is ONE narrow, repetitive task, so why not run a much smaller model? A 0.6B model, 13 times smaller, answers in well under a second on the same hardware. If it can do this single job, the latency problem disappears.

What this experiment shows

A complete local distillation pipeline: an 8B teacher generates the training data from a public document corpus, a 0.6B student absorbs the task via QLoRA, a light form of fine-tuning explained in brick 3, in about twelve minutes on Apple Silicon, and an evaluation harness with a ground-truth check and a negative control tells you honestly what transferred. If you want to reproduce it step by step, the GitHub repository walks you through every brick, and every number below comes from those scripts.

🧪 The small model alone: a surprise, then a wall

So I tried exactly that, and the first result is a genuine surprise. I gave the base 0.6B model (Qwen3-0.6B, compressed to 4-bit precision so it fits in very little memory) the same instructions as the teacher, on 49 held-out documents: documents set aside from the start and kept out of all training, so no model has an unfair advantage on them. It produced valid JSON 95.9% of the time, and its summaries passed the faithfulness check at 0.83, meaning 83% of them passed. Judge scores appear as fractions of 1 from here on. Filing prose is formal and predictable, so even a small model can copy its shape. If the task were "summarize these documents into JSON", you could almost stop here.

Then you look at the part retrieval actually filters on, and you hit the wall. Here is a simple test. When I built the corpus, I took each excerpt from a known part of the report: the business section, the risk factors, or the financial analysis. So for every excerpt, the right answer to "which section is this?" is known in advance. No teacher, no judge, just a direct check. The base model gets it right on 36.2% of documents. Random guessing among three choices gives 33%. The other facets have no ground truth to check against, so I score them differently: I compare each label to the one the 8B teacher gives on the same document. That measures imitation rather than truth, a distinction that will matter later in this article. By this measure the picture barely improves. The numeric density label, a three-way choice between low, medium and high, matches the teacher's on 36.2% of documents (the same number as the section score, by coincidence). The forward-looking label, also a three-way choice, matches on 27.7%. The one exception is specificity, a two-way choice, where the base model reaches 74.5%. The JSON parses perfectly, and most of the labels inside are close to random. For a retrieval index, facets like these are worse than no facets, because filters silently return the wrong documents.

The failure moved On clean, formal documents the small model's weakness is not the FORMAT (95.9% valid JSON) and not even basic summarizing (0.83 faithful). It is the STRUCTURED JUDGMENT: nearly every facet a retrieval pipeline would filter on comes out near chance. Parseable is not the same as usable.

🧨 Few-shot: the standard fix that makes it worse

Your next idea is probably the standard one: put a few worked examples in the prompt. Few-shot prompting is the usual fix when a model follows the task poorly, and it costs nothing but prompt tokens. Here is what it did at 0.6B. Valid JSON fell from 95.9% to 42.9%. Faithfulness collapsed from 0.83 to 0.29. Latency tripled, because every request re-reads the examples. Then I read the summaries, and found the most instructive failure of the whole experiment.

The contamination finding At 0.6B, the examples in the prompt leak into the output instead of guiding it. One of the two worked examples in the prompt was about PQ Group Holdings, a specialty chemicals supplier. Only 21 of the 49 few-shot outputs parsed at all, and the judge can only score a summary it can read. Of those 21, 14 described the example instead of the document they were given, most of them naming PQ Group outright on documents about banks, biotechs, and a food delivery platform.
Cut view of the context window: the worked example about PQ Group Holdings leaks past the 0.6B model straight into the returned summary; 14 of 21 summaries describe the example

Here is a real pair from the runs. The document is a bank's risk factors section. The summary the model returned describes the chemicals company from the worked example, including the businesses it sold.

The document it was given

text
ITEM 1A. RISK FACTORS
The material risks and
uncertainties that management
believes affect us are described
below... [a regional bank's
risk factors]

The summary it returned

text
"PQ Group Holdings Inc. operates
as an integrated global supplier
of specialty catalysts and
chemicals. The company recently
sold two business segments..."

Why does this happen? A 0.6B model struggles to keep two things separate: the examples it should imitate, and the document it should process. Inside its context window, the example already looks like a perfect answer, so under pressure the model copies it. And this is not bad luck in a single run. I ran the experiment twice, with two completely different pairs of examples, and the leak followed the examples both times, word for word. To be clear, this run does not test larger models; on those, few-shot examples are widely reported to work as intended. But at this scale, the technique quietly destroyed the one property a retrieval index cannot give up: the summary must describe the right document.

So prompting cannot rescue the small model on this task: zero-shot classifies at chance, and few-shot poisons the summaries too. The examples need to move out of the context window and into the weights. That is precisely what distillation does.


🏗️ Distillation: the big model writes the textbook

The idea is simple. Instead of showing the small model examples at inference time, you have the big model write a training set once, and you fine-tune the small model on it. The teacher's behavior gets compressed into the student's weights. At inference time the student sees only the document, with no examples left to leak. The diagram below shows the whole pipeline; each label in it gets its own brick in the walkthrough that follows.

The distillation pipeline: a public document corpus, 250 excerpts, teacher generation at temperature 0, chat JSONL split grouped by company, QLoRA rank 32 fine-tune, 0.6B student

Brick 1: the teacher writes the answers. I ran deepseek-r1:8b at temperature 0 over the training documents. For each document, it writes the exact JSON object I want the student to produce. Temperature 0 matters: it forces the model to give its most confident answer every time, so the dataset is the teacher at its most reliable. The reasoning trace is removed; only the final JSON is kept as the training target. The few outputs that broke the schema (3 of 250) were dropped rather than taught.

Brick 2: the dataset. Each document and its JSON answer become one training example: the question (the instructions plus the document) and the answer (the teacher's JSON). After dropping the teacher's few broken outputs, the corpus splits into 173 training examples, 25 for validation, and 49 held out for testing. Two details decide whether you can trust the result.

The first detail: grade only the answer. It rests on one fact about how fine-tuning works. The model learns by predicting its training text one token at a time, and by default every token in the example counts in its score, the document included. Here each document runs about 4,000 characters and the JSON answer about 450, so roughly 88% of the training effort would go into learning to rewrite document text nobody asked for. One training flag fixes this: it masks the document tokens out of the score. The model still reads the document; those tokens just stop counting. Every bit of learning goes into the 450 characters that matter. The visual below shows where the effort goes.

The second detail: never test on a company you trained on. Split the data by company, not by document. If any part of Apple's report is in the training set, none of Apple's report can be in the test set, so the student only proves itself on companies it has never seen. My first split got this wrong: 45 of 50 test documents came from companies already seen in training, which quietly inflates the score. The numbers below use the corrected split.

Loss masking: the document tokens teach nothing, only the answer tokens carry the training signal; about 88% of each training example is document text that is masked out
json
{"messages": [
  {"role": "user", "content": "Summarize and label this document as one JSON object. <instructions> <document text>"},
  {"role": "assistant", "content": "{\"summary\": \"...\", \"section_type\": \"risk_factors\", \"specificity\": \"...\", ...}"}
]}

Brick 3: QLoRA on the 4-bit student. Full fine-tuning, even for a 0.6B model, is more than one narrow task needs. QLoRA takes a lighter route. In plain terms: the student's general knowledge stays frozen, and a thin trainable layer, added alongside the existing weights, learns what changes for this one task. Technically, the base model stays in its compressed 4-bit form and training only touches small adapter matrices. Their capacity is set by a dial called the rank: a higher rank gives the adapter more room to change the model's behavior. I used 32, a common middle setting. On my Mac, with MLX, the whole run takes about twelve minutes and peaks at 4.1 GB of memory. It trains comfortably while you make coffee, on the machine you already own.

bash
# 1. Build the corpus (streams a public HF dataset, ~2 min)
python3 scripts/build_corpus.py 250

# 2. Teacher generation (resume-safe, ~10 s/document for the reasoning teacher)
python3 scripts/gen_teacher.py deepseek-r1:8b teacher_r1

# 3. Dataset (chat JSONL, split grouped by company) + QLoRA training (~12 min)
python3 scripts/prepare_data.py
bash scripts/train.sh   # mlx_lm lora, rank 32, --mask-prompt, seed 42

# 4. Evaluation: schema validity, ground-truth accuracy, label agreement, latency
uv run python scripts/evaluate.py

Note what we did NOT do: reinforcement learning, preference data, synthetic augmentation, or a cloud GPU. One teacher pass and one masked supervised fine-tune. The question is what something this simple actually transfers.


📊 Does it work? The numbers

Every setup below ran on the same 49 held-out documents, all from companies the student never saw during training. Three metrics need a short definition first:

Section accuracy: Measured against ground truth. We already know which section each passage belongs to, so this number involves no teacher and no judge.
Faithful: A judge model confirmed that every claim in the summary is supported by the document.
Thesis: The summary captures the section’s central point rather than a side detail.

The judge also checks length and the absence of hype. Every setup passed those two nearly perfectly, so the table leaves them out.

All setups on the same 49 held-out documents, companies unseen in training (p50 latency; the teacher’s latency is measured over all 250 documents)

SetupValid JSONSection accuracy (ground truth)FaithfulThesisp50 latency
Teacher (8B reasoning)n/a (reference)95.9%0.981.0010.1 s
Student (0.6B + QLoRA)100%100%0.731.001.24 s
Base 0.6B, zero-shot95.9%36.2%0.831.000.74 s
Base 0.6B, few-shot42.9%33.3%0.290.482.35 s

Start with what the student got right: 100% valid JSON, and 100% ground-truth section accuracy, on documents from companies it has never seen. That last number is even better than its own teacher's. On these same 49 documents, the teacher misreads 2 and scores 95.9%.

How can a student beat its own teacher? Across the whole corpus the teacher misreads only 4 of 250 documents, and those mistakes are scattered noise, not a repeated pattern. Trained on 173 of the teacher's answers, the student absorbs the dominant pattern and skips the scattered exceptions, so on the test documents it gets right the 2 the teacher misses. A nudge rather than a leap, but a real one.

Two more facets tell the same story. On each facet we measure how often the student's label matches the teacher's, and on two of them that match rate jumped from near chance to strong. On specificity the student now agrees with the teacher 83.7% of the time, against 74.5% for the untrained base model. On numeric density it reaches 81.6%, against just 36.2% for the base model. Training moved both a long way.

Two other facets did not follow, and they belong in the same picture. Forward-looking only climbed to 49%, and audience level actually dropped below the base model's score. What moved into the weights is most of the structured judgment, not all of it. That is still the part no prompt could give the base model. And the student does all this at 1.24 seconds per document, 8 times faster than the teacher. In the latency plot below you will also spot a second student, which we come back to in the next section.

Latency strip plot: the teacher spreads from 6.8 to 14.8 seconds per document, both 0.6B students stay in a tight band around one second

Try it: 12 documents to enrich

Same task, measured speeds from the runs (time compressed 8x)

Student · 0.6B + QLoRA

0/12 documents

Teacher · 8B reasoning

0/12 documents

t = 0.0 s

One number in the table cuts against the rest, and it is worth understanding. The student's faithfulness is 0.73, while the plain base model scored 0.83. How can the untrained model write MORE faithfully? Because the base model, when it summarizes at all, writes short, careful sentences that say little. And sentences that say little are hard to get wrong. The student inherited its teacher's richer, more confident style, and on dense financial text every extra claim is a chance to slip. The judge caught it writing "operating profit" where the filing says operating revenue. It also called an operating ratio "higher" when the ratio improved by going down. Distillation transferred the teacher's writing behavior, including its habit of making strong claims. It did not transfer the teacher's knowledge, or its ability to check what it writes.

What transferred, in one sentence The student learned the structure completely: 100% valid JSON, 100% ground-truth classification, thesis 1.00. It also inherited its teacher's confident prose, and the factual slips that come with it (faithful 0.73 vs the base's careful 0.83). All of this at 8x the teacher's speed.

🔁 The twist: a second, more careful teacher

That faithfulness trade raises a question worth an experiment of its own. If the student inherits its teacher's writing behavior, then a different teacher should produce a different student, even at the same model size. A recent study of on-device distillation measured exactly this effect at larger scale, and this article's protocol is modeled on it. So I replicated the move. I trained a second student, identical in every way: same 0.6B base, same QLoRA recipe, same corpus. The only change was the teacher. This time the training data was written by llama-3.1-8b-instruct, an 8B model with no reasoning traces and a more careful writing style. One consequence of rebuilding the dataset per teacher: each student gets its own train/test split, and the two test sets share only 7 of their 49 documents. Comparisons across the two students therefore hold the protocol constant, not the documents.

The result surprised me: the student of the instruct teacher scored 0.98 on faithfulness. That matches the level the 8B reasoning teacher itself reaches, from a model 13 times smaller, on unseen companies. Its summaries are shorter and plainer than the reasoning student's, and they almost never contain an error. Here are the two students on the SAME test document, a customer experience company's business overview:

Student of the reasoning teacher

text
"Sykes Enterprises provides
global customer experience
management services, including
full lifecycle solutions and
digital transformation
capabilities across various
industries. The company
operates..."

It is rich, names the scope, and keeps going, which means more information and more surface to slip on.

Student of the instruct teacher

text
"Sykes Enterprises is a leading
global provider of customer
experience management services,
multichannel demand generation,
and digital transformation."

It risks a single sentence and gets nothing wrong. The caution is inherited too.

So the careful teacher wins? Look at the labels before you decide. The instruct teacher turns out to be a lazy labeler. On all 250 documents, it assigned the same audience level every single time, and it labeled 249 of 250 documents as specific to their company. Its own ground-truth section accuracy is 72.4% across the 250 documents, far below the reasoning teacher's 98.4% on the same set. The student copied this collapse faithfully. It scores a perfect 100% agreement with its teacher on those two constant fields, but agreeing with a constant is not a skill. On the one facet we can score objectively, it reaches only 68.1%. The caution and the laziness were inherited together, exactly like the richness and the errors were in the other student.

Venn diagram: the student of the reasoning teacher is a perfect classifier (100% but faithful 0.73), the student of the instruct teacher is a faithful writer (0.98 but classification 68%), and the intersection is empty: no student has both
The STRUCTURE holds with any teacher: Both students produce valid JSON on nearly every document (100% and 95.9%). The base model already parsed well; the point is that training kept that property where few-shot prompting crushed it to 42.9%. Format is the easy, robust part of distillation.
The HABITS transfer per teacher: The reasoning teacher passes on accurate classification and rich prose, along with its factual errors. The instruct teacher passes on careful, grounded writing, along with its degenerate labels. Each student is a compressed copy of its teacher’s habits, good and bad.

For the summary side of the pipeline, one composite number matters most: what fraction of documents yields an output that is both parseable and faithful?

Composite usable rate = valid JSON × faithful, each setup on its own held-out documents

SetupUsable rateHow it decomposes
Student of the instruct teacher0.9495.9% valid JSON × 0.98 faithful
Base 0.6B, zero-shot0.8095.9% valid JSON × 0.83 faithful
Student of the reasoning teacher0.73100% valid JSON × 0.73 faithful
Base 0.6B, few-shot0.1242.9% valid JSON × 0.29 faithful
Picking the teacher, not the size Picking the teacher is a PRODUCT decision, not a size decision. Both teachers are 8B. The reasoning teacher produced the perfect classifier (100% ground truth) whose prose slips; the instruct teacher produced the faithful writer (0.94 usable, beating everything) whose facets collapsed. Neither student got both capabilities, because neither teacher had both habits. Choose the teacher for the capability your task actually needs, then distill that.

🔬 Materials and methods

A result you cannot reproduce is an opinion. Every number in this article comes from the exact configuration below, and the scripts in the repository rebuild the whole thing from a public HuggingFace dataset, so there is nothing private to request.

Experimental protocol

ElementValue
Corpus250 section excerpts from public SEC 10-K annual reports (EDGAR-CORPUS on HuggingFace, Apache-2.0, 2020 test file): Item 1 business, Item 1A risk factors, Item 7 management’s discussion; balanced 82/80/88; capped at 4,096 characters
SplitGROUPED BY COMPANY (all sections of a filing stay in one split; zero company overlap, verified): 173-174 train / 25 validation / 49 held-out test
Teacher 1 (reasoning)deepseek-r1:8b via ollama, temperature 0 (p50 10.1 s per document)
Teacher 2 (instruct)llama-3.1-8b-instruct, same size, no reasoning traces, temperature 0 (p50 2.6 s)
Studentmlx-community/Qwen3-0.6B-4bit
Fine-tuningQLoRA rank 32, 600 iterations, batch size 2, learning rate 1e-5, seed 42, --mask-prompt (scores loss on the answer only); ~12 min, 4.1 GB peak
Taskone JSON object per document: 3-sentence summary + 5 categorical facets (section type, specificity, numeric density, forward-looking, audience level)
Ground-truth checksection_type accuracy is measured against the KNOWN section identity from sampling: an objective anchor, independent of any teacher or judge
Label metricmacro-average agreement (the five facets weighted equally) with the teacher’s own labels on the held-out documents (the teacher is the reference here: this measures imitation, not truth)
Summary judgeone frontier model (a top-tier hosted LLM), 4 binary checks per summary: faithful, thesis, length, no hype; 228 judgments across all setups (49 teacher + 49 student + 47 base + 21 few-shot + 47 second student + 15 negative control)
Negative control15 deliberately mismatched document/summary pairs fed to the judge: 0/15 rated faithful
Training-data hygieneteacher outputs that broke the output schema were dropped from training data (3 of 250 for the reasoning teacher, 2 of 250 for the instruct teacher)
Hardware / runtimeApple Silicon Mac, MLX for training and student inference, ollama for the teachers
Held fixedthe task prompt and temperature 0 everywhere; within one teacher’s pipeline, the same 49 test documents across setups. Each teacher’s pipeline draws its own split (the two test sets share 7 of their 49 documents), so comparisons across teachers hold the protocol constant, not the documents; each student is scored against its own teacher’s labels on its own split
Variedthe setup only: teacher, student, base zero-shot, base few-shot

Two protocol choices deserve a word. First, the summary judge is itself an LLM, which should make you suspicious by default. The negative control is the answer to that suspicion. I fed the judge 15 deliberately mismatched pairs (a real document with a summary of a different document), and it rated 0 of 15 as faithful. Second, the ground-truth section check exists because agreement with the teacher can reward a student for imitating a broken teacher, as the lazy labeler finding shows. Where an objective anchor is available, use it.


🧭 Honest limitations

173-174 training examples, one seed: This is a small corpus and a single training run per teacher (a seed fixes the run’s randomness; the study that inspired this protocol averaged three seeds, this one did not). These numbers carry more run-to-run uncertainty.
One document genre: SEC filings are formal, predictable prose. The base model’s decent zero-shot summaries (0.83 faithful, 95.9% JSON) may not survive noisier corpora; on messy web documents the case for distilling the summary side would likely be stronger, not weaker.
Label agreement uses the teacher as its reference: Agreement measures imitation, not truth: a student that copies its teacher’s mistake scores a point, and the lazy labeler finding shows how far that can go. That is why the section type check against ground truth matters most in the table.
A single judge, not a panel: The original study used a 3-judge panel; I used one frontier model. The negative control passed cleanly (0/15), but a single judge’s systematic biases remain unmeasured.
Latencies are one machine’s wall-clock: All timings are p50 on my Apple Silicon Mac, with MLX for the students and ollama for the teachers (two different runtimes). Your hardware will move the absolute numbers; the ratios should be more stable.
The contamination rate carries wide uncertainty: It rests on only 21 judged few-shot summaries. But I saw the same thing happen with two separate example documents: in both, the model copied the example company’s name straight into its own answer, word for word. The exact rate is noisy, but the effect is real.

🎉 What this means in practice

Distillation is not a general replacement for a big model. It is a compression of ONE task into a small one, and this experiment sharpens where it pays. If your task is summarizing clean, formal documents, measure the base model first: it may already be good enough, and the simplest option is to add no training at all. The picture changes the moment your pipeline needs structured judgment: facets, classifications, anything a filter will trust. There, the base model is a coin flip, and few-shot prompting makes it worse. Distillation is the cheapest reliable fix: twelve minutes of training moved section accuracy from 36% to 100% here.

Decision flowchart: high volume, stable format, and a latency, cost or privacy budget lead to distill; any no leads to keep prompting; the final box says to pick the teacher for the capability the task needs and audit its habits first

That final box, choosing the teacher, deserves a word. If faithfulness is what matters, distill a careful instruct teacher that sticks to the source; if classification is what matters, distill the teacher that actually classifies well. Audit the teacher's own habits on your labels before you spend the training run, because the student will copy the habits, not the intent. Measure the composite that matters to YOUR pipeline before you pick.

The whole experiment ran on one laptop, and that was the point: teacher generation, QLoRA training, evaluation, judging and controls, all local, all reproducible, about an afternoon end to end. You can point the corpus builder at your own documents and have a student model of your own by tonight. The same logic extends to richer enrichment: extracting entities and relations to feed a knowledge graph is the same shape of task, high volume and fixed format, and it is the natural next experiment.


📚 Sources

Everything in this article is reproducible from the GitHub repository: corpus building, teacher generation, dataset preparation with the split grouped by company, QLoRA training, evaluation, and the judge pass with its negative control.

GitHub repository: distill-your-docs: the full pipeline, the scripts for every brick above, and the scorecard the numbers in this article come from.
The corpus: EDGAR-CORPUS on HuggingFace (Apache-2.0): pre-extracted text of public SEC 10-K annual reports, section by section. Public, permissively licensed, and dense enough to make the task honest.
The protocol’s inspiration: A recent study of on-device distillation measured the same dependence on the teacher at larger scale, with a bigger corpus, multiple seeds, and a judge panel. This article replicates its core protocol on an independent public corpus and reaches the same reading: the structure transfers robustly, the habits transfer per teacher.
Related on this blog: Is Your Agent’s Memory Worth Its Tokens? applies the same discipline (local runs, controls, honest variance) to a different question.
React:

Comments

No comments yet. Be the first to comment!