Lesson 13 / 27

Distillation: Teaching a Small Model From a Large One

Use a strong model to label data that trains a cheaper one.

Copy the behaviour, not the size

Distillation trains a small student to imitate a larger teacher. A common LLM recipe: collect realistic inputs, have a strong model answer them (with checks), review a sample, and fine-tune a smaller model on those pairs. On a narrow task the student can approach the teacher at far lower cost and latency. Cautions: the student inherits the teacher's mistakes; check the provider's terms on training other models with outputs; measure on human-verified test data, not just agreement with the teacher; keep the data diverse.

A student helped by teacher labels, run

I ran this with Python, numpy 2.5.3 and scikit-learn 1.9.1, with fixed random seeds. It trains small classical models, not a language model: the mechanics (gradient descent, learning rate, overfitting, forgetting, low-rank updates) are the same ideas that apply to fine-tuning an LLM, but the numbers are not LLM results. The linear teacher scores 0.885 on held-out data. A small tree trained on 300 noisy real labels scores 0.723; given teacher labels for 4,700 more unlabelled inputs it scores 0.791, improving but staying below the teacher.

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier

rng = np.random.default_rng(6)
X = rng.normal(size=(8000, 12)); w = rng.normal(size=12)
y = (X @ w + rng.normal(size=8000) * 1.2 > 0).astype(int)      # noisy real labels
Xtr, ytr, Xte, yte = X[:300], y[:300], X[5000:], y[5000:]

teacher = LogisticRegression(max_iter=2000).fit(Xtr, ytr)                       # the "big" model trained on the labelled data
Xunl = X[300:5000]                                                               # plenty of UNLABELLED inputs
teacher_labels = teacher.predict(Xunl)

student_alone = DecisionTreeClassifier(max_depth=6, random_state=0).fit(Xtr, ytr)
student_distilled = DecisionTreeClassifier(max_depth=6, random_state=0).fit(np.vstack([Xtr, Xunl]), np.concatenate([ytr, teacher_labels]))
print("teacher                         :", round(teacher.score(Xte, yte), 3))
print("small student, real labels only :", round(student_alone.score(Xte, yte), 3))
print("small student + teacher labels  :", round(student_distilled.score(Xte, yte), 3))

Output:

teacher                         : 0.885
small student, real labels only : 0.723
small student + teacher labels  : 0.791

Sample and read teacher outputs

Read at least a few dozen teacher answers before training; every systematic error you skip becomes the student's habit.

Quick check: How should a distilled student be evaluated?

  • Only by how closely it copies the teacher
  • On human-verified test data, not only agreement with the teacher
  • Not at all
  • Only by model size
Answer

On human-verified test data, not only agreement with the teacher — Matching the teacher's errors is not the goal; being correct is.