tinydistill 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tinydistill-0.3.1 → tinydistill-0.3.3}/.gitignore +1 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/PKG-INFO +216 -190
- {tinydistill-0.3.1 → tinydistill-0.3.3}/README.md +205 -188
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/run.sh +9 -0
- tinydistill-0.3.3/examples/banking77/students/all-MiniLM-L12-v2-20-epochs.json +146 -0
- tinydistill-0.3.3/examples/banking77/students/all-MiniLM-L12-v2.json +142 -0
- tinydistill-0.3.3/examples/banking77/students/all-MiniLM-L6-v2-10-epochs.json +146 -0
- tinydistill-0.3.3/examples/banking77/students/all-MiniLM-L6-v2-20-epochs.json +146 -0
- tinydistill-0.3.3/examples/banking77/students/all-mpnet-base-v2.json +142 -0
- tinydistill-0.3.3/examples/banking77/students/bge-small-en-v1.5.json +142 -0
- tinydistill-0.3.3/examples/banking77/students/static.json +146 -0
- tinydistill-0.3.3/examples/headline.md +5 -0
- tinydistill-0.3.3/examples/update_readme.py +66 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/pyproject.toml +10 -3
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/__init__.py +1 -1
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/analyse.py +26 -12
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/cli.py +109 -46
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/evaluate.py +20 -1
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/export.py +31 -6
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/hub.py +18 -5
- tinydistill-0.3.3/src/tinydistill/plots.py +326 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/progress.py +25 -9
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/static.py +3 -1
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/student.py +80 -37
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/web.py +8 -4
- tinydistill-0.3.3/tests/browser/build_tiny_site.py +49 -0
- tinydistill-0.3.3/tests/browser/check_page.mjs +44 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_cli.py +148 -38
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_export.py +16 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_loss.py +30 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_progress.py +5 -3
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_serve.py +4 -2
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_task.py +1 -1
- tinydistill-0.3.1/examples/update_readme.py +0 -20
- {tinydistill-0.3.1 → tinydistill-0.3.3}/LICENSE +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-1.5b-8shot.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-1.5b-8shot.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-1.5b.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-1.5b.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-7b-8shot.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-7b-8shot.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-7b.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/banking77-7b.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/students.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/task.yaml +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/banking77/teachers.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-1.5b-8shot.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-1.5b-8shot.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-1.5b.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-1.5b.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-7b-20-epochs.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-7b-20-epochs.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-7b.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-7b.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-ensemble.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-ensemble.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-synthetic.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/financial-tweets-synthetic.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/run.sh +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/task.yaml +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/financial-tweets/teachers.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/go-emotions/go-emotions-1.5b.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/go-emotions/go-emotions-1.5b.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/go-emotions/run.sh +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/go-emotions/task.yaml +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/vllm-smoke.json +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/vllm-smoke.md +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/examples/vllm-smoke.sh +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/data.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/serve.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/shortlist.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/suggest.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/synth.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/task.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/teacher.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/src/tinydistill/tune.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/conftest.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_cache.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_scoring.py +0 -0
- {tinydistill-0.3.1 → tinydistill-0.3.3}/tests/test_vllm.py +0 -0
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tinydistill
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Distil an LLM classification prompt into a small, fast model that runs on a CPU.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Andreas-Hum/tinydistill
|
|
6
6
|
Author: Andreas-Hum
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: inference,knowledge distillation,llm,onnx,text classification
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
12
|
Classifier: Operating System :: OS Independent
|
|
11
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
17
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
18
|
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
14
20
|
Requires-Dist: onnx>=1.15
|
|
15
21
|
Requires-Dist: onnxruntime>=1.17
|
|
16
22
|
Requires-Dist: pyyaml>=6
|
|
17
|
-
Requires-Dist: torch>=2.
|
|
23
|
+
Requires-Dist: torch>=2.6
|
|
18
24
|
Requires-Dist: transformers>=4.56
|
|
19
25
|
Provides-Extra: 4bit
|
|
20
26
|
Requires-Dist: accelerate>=0.30; extra == '4bit'
|
|
@@ -22,216 +28,113 @@ Requires-Dist: bitsandbytes>=0.43; extra == '4bit'
|
|
|
22
28
|
Provides-Extra: dev
|
|
23
29
|
Requires-Dist: build>=1.2; extra == 'dev'
|
|
24
30
|
Requires-Dist: datasets>=2.18; extra == 'dev'
|
|
31
|
+
Requires-Dist: matplotlib>=3.7; extra == 'dev'
|
|
25
32
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
26
33
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
27
34
|
Provides-Extra: hf
|
|
28
35
|
Requires-Dist: datasets>=2.18; extra == 'hf'
|
|
36
|
+
Provides-Extra: plot
|
|
37
|
+
Requires-Dist: matplotlib>=3.7; extra == 'plot'
|
|
29
38
|
Description-Content-Type: text/markdown
|
|
30
39
|
|
|
31
40
|
# tinydistill
|
|
32
41
|
|
|
33
|
-
|
|
42
|
+
<a href="https://pypi.org/project/tinydistill/"><img src="https://img.shields.io/pypi/v/tinydistill.svg" alt="PyPI version"></a>
|
|
43
|
+
<a href="https://pypi.org/project/tinydistill/"><img src="https://img.shields.io/pypi/pyversions/tinydistill.svg" alt="Supported Python versions"></a>
|
|
44
|
+
<a href="https://github.com/Andreas-Hum/tinydistill/actions/workflows/ci.yml"><img src="https://github.com/Andreas-Hum/tinydistill/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
45
|
+
<a href="https://pepy.tech/projects/tinydistill"><img src="https://static.pepy.tech/badge/tinydistill/month" alt="Downloads per month"></a>
|
|
46
|
+
<a href="https://github.com/Andreas-Hum/tinydistill/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Andreas-Hum/tinydistill.svg" alt="MIT licence"></a>
|
|
47
|
+
<a href="https://colab.research.google.com/github/Andreas-Hum/tinydistill/blob/main/notebooks/quickstart.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open in Colab"></a>
|
|
48
|
+
<a href="https://andreas-hum.github.io/tinydistill/"><img src="https://img.shields.io/badge/demo-live-2a78d6.svg" alt="Live demo"></a>
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
You describe the task in a short YAML file (labels, how to say each label, an instruction prompt) and give it
|
|
37
|
-
unlabelled texts. An instruction-tuned LLM labels them with soft targets, a small encoder learns to imitate
|
|
38
|
-
those targets, and the result is exported to ONNX (optionally int8) for fast CPU inference.
|
|
50
|
+
**Most of a large model's quality on one task, many times faster, on a CPU.**
|
|
39
51
|
|
|
40
|
-
|
|
52
|
+
`tinydistill` turns an LLM classification prompt into a small classifier by knowledge distillation. Describe
|
|
53
|
+
the task in a short YAML file and give it unlabelled texts: an instruction-tuned LLM labels them with soft
|
|
54
|
+
targets, a small encoder learns to imitate them, and the result is exported to ONNX (optionally int8) for fast
|
|
55
|
+
inference on a CPU, in a browser or behind an HTTP server. It continues my bachelor's project on inference
|
|
56
|
+
optimisation by distilling LLMs.
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<a href="https://andreas-hum.github.io/tinydistill/">
|
|
60
|
+
<picture>
|
|
61
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Andreas-Hum/tinydistill/main/docs/images/demo-dark.png">
|
|
62
|
+
<img alt="The live demo: a 23 MB Banking77 classifier running in the browser, showing the word pieces and what each layer of the model would answer" src="https://raw.githubusercontent.com/Andreas-Hum/tinydistill/main/docs/images/demo-light.png" width="640">
|
|
63
|
+
</picture>
|
|
64
|
+
</a>
|
|
65
|
+
</p>
|
|
66
|
+
<p align="center"><i>The <a href="https://andreas-hum.github.io/tinydistill/">live demo</a>: a 23 MB student distilled from Qwen2.5-7B runs in your
|
|
67
|
+
browser and shows what happens inside it.</i></p>
|
|
68
|
+
|
|
69
|
+
## Highlights
|
|
70
|
+
|
|
71
|
+
- **No labels needed.** Any instruction-tuned LLM on the Hugging Face Hub labels your texts by scoring every
|
|
72
|
+
label in one forward pass: no text generation, no answer parsing ([how](#how-the-label-scoring-works)).
|
|
73
|
+
- **Small, fast students.** A 23 MB MiniLM or an 8 MB static-embedding model, more accurate than its 7B teacher
|
|
74
|
+
on Banking77 and over 200x faster on a CPU than the teacher on a GPU ([results](#results)).
|
|
75
|
+
- **Any teacher.** Local models in full precision or 4 bit, ensembles of several model families, or a remote
|
|
76
|
+
[vLLM](https://github.com/vllm-project/vllm) server ([choosing the teacher](#choosing-the-teacher)).
|
|
77
|
+
- **Honest numbers.** Every run reports the accuracy, latency and size of teacher and student, with gold-only
|
|
78
|
+
baselines, upper bounds and several seeds.
|
|
79
|
+
- **A few labels go a long way.** Gold examples can go into the teacher's prompt and the student's loss
|
|
80
|
+
([a few gold labels](#a-few-gold-labels)).
|
|
81
|
+
- **Tools around the loop.** Tune the setup on a dev split, see where it goes wrong, pick texts to label,
|
|
82
|
+
route unsure texts to the LLM and retrain on them ([improving a run](#improving-a-run)).
|
|
83
|
+
- **Deploy anywhere.** ONNX and int8, `tinydistill serve`, a static web page, or the Hugging Face Hub and
|
|
84
|
+
`Predictor("hf:user/name")` ([deploying](#deploying-cascade-server-browser)).
|
|
85
|
+
|
|
86
|
+
## Installation
|
|
41
87
|
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
K["A few gold labels<br/>per class"] -.-> B
|
|
48
|
-
K -.-> C
|
|
49
|
-
B -- "soft labels<br/>(ensembled)" --> C["Small encoder<br/>student"]
|
|
50
|
-
C --> D["ONNX / int8<br/>export"]
|
|
51
|
-
D --> E["Fast CPU<br/>inference"]
|
|
52
|
-
D --> W["Browser · server ·<br/>Hugging Face Hub"]
|
|
53
|
-
E -.-> F["Cascade: unsure<br/>texts to the LLM"]
|
|
88
|
+
```sh
|
|
89
|
+
pip install tinydistill # label, train, export, predict
|
|
90
|
+
pip install "tinydistill[hf]" # + Hugging Face datasets as input (hf:<dataset>:<split>)
|
|
91
|
+
pip install "tinydistill[hf,4bit]" # + 4-bit teachers with bitsandbytes (CUDA)
|
|
92
|
+
pip install "tinydistill[plot]" # + charts of a run (matplotlib)
|
|
54
93
|
```
|
|
55
94
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
student's loss, and a cascade that hands the student's least confident texts to the LLM. The exported model
|
|
60
|
-
runs on a CPU, in a [browser](#deploying-cascade-server-browser), behind `tinydistill serve`, or from the
|
|
61
|
-
Hugging Face Hub.
|
|
62
|
-
|
|
63
|
-
## Results
|
|
64
|
-
|
|
65
|
-
Teachers are `Qwen/Qwen2.5-1.5B-Instruct` and `Qwen/Qwen2.5-7B-Instruct` (4 bit), zero-shot or with 8 gold
|
|
66
|
-
examples per class in the prompt; the student is `sentence-transformers/all-MiniLM-L6-v2` unless a table says
|
|
67
|
-
otherwise. Quality is measured on gold-labelled test texts that no model trains on, and students are trained
|
|
68
|
-
with 3 seeds (mean ± standard deviation). Every table below is filled by the task's `run.sh` from its saved
|
|
69
|
-
results files in [`examples/`](https://github.com/Andreas-Hum/tinydistill/tree/main/examples); the text only
|
|
70
|
-
quotes them.
|
|
71
|
-
|
|
72
|
-
**Try it:** the [live demo](https://andreas-hum.github.io/tinydistill/) runs the Banking77 student (7B 8-shot
|
|
73
|
-
teacher, int8, 23 MB) in your browser and shows what happens inside it: the word pieces, what each layer would
|
|
74
|
-
answer, where the classifier looks, which words matter, and the teacher's soft labels next to the student's.
|
|
95
|
+
Python 3.11 or newer, on Linux, macOS or Windows. Labelling with a large teacher wants a GPU; training and
|
|
96
|
+
inference are fine on a CPU. For a CPU-only PyTorch, install it first:
|
|
97
|
+
`pip install torch --index-url https://download.pytorch.org/whl/cpu`.
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
Intent detection with 77 fine-grained classes ([Banking77](https://huggingface.co/datasets/PolyAI/banking77),
|
|
79
|
-
CC BY 4.0), reproduced by [`examples/banking77/run.sh`](https://github.com/Andreas-Hum/tinydistill/blob/main/examples/banking77/run.sh).
|
|
80
|
-
One row per teacher; the student columns are the deployed int8 model:
|
|
81
|
-
|
|
82
|
-
<!-- results: banking77/teachers.md -->
|
|
83
|
-
| Run | Teacher | Teacher accuracy | Student | Parameters (M) | Accuracy | Macro-F1 | Agreement with teacher | Deployed as | Latency, batch 1 (ms) | Latency, batch 32 (ms) | Size (MB) |
|
|
84
|
-
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
85
|
-
| banking77-1.5b | zero-shot `Qwen/Qwen2.5-1.5B-Instruct` | 0.404 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.386 ± 0.004 | 0.313 ± 0.005 | 0.766 ± 0.004 | ONNX int8 | 1.2 | 37.3 | 23 |
|
|
86
|
-
| banking77-1.5b-8shot | 8-shot `Qwen/Qwen2.5-1.5B-Instruct` | 0.513 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.485 ± 0.009 | 0.406 ± 0.007 | 0.706 ± 0.002 | ONNX int8 | 1.1 | 36.4 | 23 |
|
|
87
|
-
| banking77-7b | zero-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.629 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.618 ± 0.003 | 0.579 ± 0.005 | 0.817 ± 0.007 | ONNX int8 | 1.4 | 39.2 | 23 |
|
|
88
|
-
| banking77-7b-8shot | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.729 ± 0.011 | 0.697 ± 0.015 | 0.800 ± 0.003 | ONNX int8 | 1.4 | 40.2 | 23 |
|
|
89
|
-
|
|
90
|
-
All latencies on the CPU with 2 threads.
|
|
91
|
-
<!-- /results -->
|
|
92
|
-
|
|
93
|
-
The student follows its teacher, so the teacher sets the ceiling: a bigger model and 8 examples per class
|
|
94
|
-
each help a lot. The full table for the best teacher, with the student trained on the same 616 gold labels
|
|
95
|
-
alone (baseline) and on all 10,003 gold labels (upper bound), and the cascade:
|
|
96
|
-
|
|
97
|
-
<!-- results: banking77/banking77-7b-8shot.md -->
|
|
98
|
-
| Model | Device | Accuracy | Macro-F1 | Agreement with teacher | Latency, batch 1 (ms) | Latency, batch 32 (ms) | Throughput (texts/s) | Size (MB) | Speed-up, batch 1 |
|
|
99
|
-
|---|---|---|---|---|---|---|---|---|---|
|
|
100
|
-
| teacher, 8-shot, 4-bit | cuda | 0.783 | 0.779 | n/a | 264.0 | 7434.4 | 4.3 | 5443 | 1.0x |
|
|
101
|
-
| student, PyTorch | cpu | 0.729 ± 0.011 | 0.697 ± 0.015 | 0.800 ± 0.003 | 4.4 | 89.3 | 358.5 | 91 | 60.3x |
|
|
102
|
-
| student, ONNX fp32 | cpu | 0.727 | 0.696 | 0.801 | 3.4 | 90.9 | 352.2 | 91 | 77.6x |
|
|
103
|
-
| student, ONNX int8 | cpu | 0.726 | 0.694 | 0.797 | 1.4 | 40.2 | 795.7 | 23 | 186.3x |
|
|
104
|
-
| baseline: student on 8 gold labels per class, no teacher | cpu | 0.490 ± 0.014 | 0.449 ± 0.018 | 0.473 ± 0.015 | n/a | n/a | n/a | n/a | n/a |
|
|
105
|
-
| upper bound: student on all gold labels | cpu | 0.914 ± 0.005 | 0.914 ± 0.005 | 0.775 ± 0.001 | n/a | n/a | n/a | n/a | n/a |
|
|
106
|
-
|
|
107
|
-
Task `banking77`, 3080 gold-labelled test texts. Teacher: `Qwen/Qwen2.5-7B-Instruct`. Student: `sentence-transformers/all-MiniLM-L6-v2`. The teacher's prompt shows 8 gold training examples per class (616 texts). The student learns from the teacher's soft labels on all 10003 training texts, plus cross-entropy on 8 gold labels per class (616 texts, weight 1.0). The baseline is the same student trained with cross-entropy on those 616 gold labels only, without the teacher. The upper bound is the same student trained with cross-entropy on all 10003 gold training labels and no teacher; its speed equals the student's. Values with ± are the mean and standard deviation over 3 training seeds; the ONNX rows are the exported first-seed model.
|
|
108
|
-
CPU rows use 2 threads. Latency is the median over repeated runs. Size is the teacher's weights as loaded and the students' model files. The teacher packs several texts into one forward pass; it is timed at batch 32 only when that takes under three minutes, otherwise its throughput is 1 / batch-1 latency. Speed-up is relative to the teacher, 8-shot, 4-bit on cuda.
|
|
109
|
-
|
|
110
|
-
### Cascade: student first, unsure texts to the teacher
|
|
111
|
-
|
|
112
|
-
| Share sent to the teacher | Student confidence threshold | Accuracy | Macro-F1 | Mean latency per text (ms) |
|
|
113
|
-
|---|---|---|---|---|
|
|
114
|
-
| 0% | 0.080 | 0.726 | 0.694 | 1.4 |
|
|
115
|
-
| 10% | 0.231 | 0.744 | 0.718 | 27.8 |
|
|
116
|
-
| 20% | 0.302 | 0.750 | 0.730 | 54.2 |
|
|
117
|
-
| 30% | 0.355 | 0.764 | 0.748 | 80.6 |
|
|
118
|
-
| 50% | 0.440 | 0.778 | 0.769 | 133.4 |
|
|
119
|
-
| 100% | 1.000 | 0.783 | 0.779 | 265.4 |
|
|
120
|
-
|
|
121
|
-
The student answers every text and passes its least confident ones to the teacher. Thresholds are set on the test texts to hit each share; in deployment, set them on a validation split. Latency is the student's batch-1 latency plus the share times the batch-1 latency of the teacher, 8-shot, 4-bit on cuda.
|
|
122
|
-
<!-- /results -->
|
|
123
|
-
|
|
124
|
-
The distilled student is far better than the same student trained on the few gold labels alone, and runs on a
|
|
125
|
-
CPU in about a millisecond. Sending its least confident texts to the teacher closes most of the remaining gap
|
|
126
|
-
to the teacher.
|
|
127
|
-
|
|
128
|
-
Other students, the same teacher and soft labels. Students train for 5 epochs unless the name says otherwise.
|
|
129
|
-
`static` is the static-embedding student (`--student static`: token vectors from MiniLM-L6, averaged, no
|
|
130
|
-
attention), trained for 20 epochs at learning rate 1e-3:
|
|
131
|
-
|
|
132
|
-
<!-- results: banking77/students.md -->
|
|
133
|
-
| Run | Teacher | Teacher accuracy | Student | Parameters (M) | Accuracy | Macro-F1 | Agreement with teacher | Deployed as | Latency, batch 1 (ms) | Latency, batch 32 (ms) | Size (MB) |
|
|
134
|
-
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
135
|
-
| banking77-7b-8shot | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.729 ± 0.011 | 0.697 ± 0.015 | 0.800 ± 0.003 | ONNX int8 | 1.4 | 40.2 | 23 |
|
|
136
|
-
| all-MiniLM-L12-v2 | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L12-v2` | 33 | 0.743 ± 0.011 | 0.710 ± 0.016 | 0.815 ± 0.004 | ONNX int8 | 3.3 | 79.2 | 34 |
|
|
137
|
-
| all-MiniLM-L12-v2-20-epochs | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L12-v2` | 33 | 0.817 ± 0.001 | 0.814 ± 0.001 | 0.875 ± 0.002 | ONNX int8 | 3.2 | 82.0 | 34 |
|
|
138
|
-
| all-MiniLM-L6-v2-10-epochs | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.808 ± 0.003 | 0.801 ± 0.002 | 0.860 ± 0.004 | ONNX int8 | 1.1 | 38.1 | 23 |
|
|
139
|
-
| all-MiniLM-L6-v2-20-epochs | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.813 ± 0.002 | 0.811 ± 0.003 | 0.868 ± 0.003 | ONNX int8 | 1.2 | 36.3 | 23 |
|
|
140
|
-
| all-mpnet-base-v2 | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `sentence-transformers/all-mpnet-base-v2` | 110 | 0.814 ± 0.004 | 0.805 ± 0.007 | 0.863 ± 0.001 | ONNX int8 | 7.3 | 220.4 | 110 |
|
|
141
|
-
| bge-small-en-v1.5 | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `BAAI/bge-small-en-v1.5` | 33 | 0.739 ± 0.008 | 0.706 ± 0.011 | 0.815 ± 0.004 | ONNX int8 | 2.7 | 75.2 | 34 |
|
|
142
|
-
| static | 8-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.783 | `static` | 8 | 0.803 ± 0.001 | 0.801 ± 0.001 | 0.824 ± 0.001 | ONNX int8 | 0.1 | 5.1 | 8 |
|
|
143
|
-
|
|
144
|
-
All latencies on the CPU with 2 threads.
|
|
145
|
-
<!-- /results -->
|
|
146
|
-
|
|
147
|
-
Training longer matters more than a bigger student: with 20 epochs instead of 5, the 23M-parameter MiniLM-L6
|
|
148
|
-
student is as accurate as the 110M-parameter mpnet, at a sixth of its latency, and more accurate than its own
|
|
149
|
-
7B teacher. Learning from the teacher's soft labels on 10,000 texts, plus the 616 gold labels, smooths out
|
|
150
|
-
some of the teacher's individual mistakes. The static student is about a point behind the MiniLM students
|
|
151
|
-
trained as long, at a tenth of a millisecond per text and 8 MB. Ten epochs get most of the way (MiniLM-L6:
|
|
152
|
-
0.808), so 10 is the default from 0.3.1. On the easier financial-tweets task, 20 epochs give the same accuracy
|
|
153
|
-
as 5 ([`financial-tweets-7b-20-epochs.md`](https://github.com/Andreas-Hum/tinydistill/blob/main/examples/financial-tweets/financial-tweets-7b-20-epochs.md)).
|
|
154
|
-
The other tables on this page were trained for 5 epochs, the default until 0.3.1.
|
|
155
|
-
|
|
156
|
-
### Financial tweets: an easier task
|
|
157
|
-
|
|
158
|
-
Sentiment of financial news tweets, 3 classes
|
|
159
|
-
([Twitter Financial News Sentiment](https://huggingface.co/datasets/zeroshot/twitter-financial-news-sentiment),
|
|
160
|
-
MIT licence per the dataset card), reproduced by
|
|
161
|
-
[`examples/financial-tweets/run.sh`](https://github.com/Andreas-Hum/tinydistill/blob/main/examples/financial-tweets/run.sh).
|
|
162
|
-
The validation split is the test set. The rows include an ensemble of three model families (Qwen2.5-7B,
|
|
163
|
-
Mistral-7B and Phi-3.5-mini, all 4 bit) and a student trained only on texts the teacher wrote itself
|
|
164
|
-
(`tinydistill synth`, no real training texts):
|
|
165
|
-
|
|
166
|
-
<!-- results: financial-tweets/teachers.md -->
|
|
167
|
-
| Run | Teacher | Teacher accuracy | Student | Parameters (M) | Accuracy | Macro-F1 | Agreement with teacher | Deployed as | Latency, batch 1 (ms) | Latency, batch 32 (ms) | Size (MB) |
|
|
168
|
-
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
169
|
-
| financial-tweets-1.5b | zero-shot `Qwen/Qwen2.5-1.5B-Instruct` | 0.537 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.514 ± 0.004 | 0.538 ± 0.004 | 0.810 ± 0.004 | ONNX int8 | 1.6 | 85.1 | 23 |
|
|
170
|
-
| financial-tweets-1.5b-8shot | 8-shot `Qwen/Qwen2.5-1.5B-Instruct` | 0.314 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.295 ± 0.003 | 0.276 ± 0.003 | 0.870 ± 0.006 | ONNX int8 | 1.8 | 77.2 | 23 |
|
|
171
|
-
| financial-tweets-7b | zero-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.832 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.825 ± 0.007 | 0.768 ± 0.009 | 0.870 ± 0.005 | ONNX int8 | 2.5 | 105.7 | 23 |
|
|
172
|
-
| financial-tweets-ensemble | teacher ensemble of 3, zero-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct,mistralai/Mistral-7B-Instruct-v0.3,microsoft/Phi-3.5-mini-instruct` | 0.842 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.828 ± 0.006 | 0.778 ± 0.005 | 0.884 ± 0.007 | ONNX int8 | 1.6 | 99.4 | 23 |
|
|
173
|
-
| financial-tweets-synthetic | zero-shot, 4-bit `Qwen/Qwen2.5-7B-Instruct` | 0.832 | `sentence-transformers/all-MiniLM-L6-v2` | 23 | 0.596 ± 0.037 | 0.571 ± 0.027 | 0.615 ± 0.045 | ONNX int8 | 1.6 | 110.4 | 23 |
|
|
174
|
-
|
|
175
|
-
All latencies on the CPU with 2 threads.
|
|
176
|
-
<!-- /results -->
|
|
177
|
-
|
|
178
|
-
Here the 7B teacher is close to the student trained on all gold labels, and the student is close to the
|
|
179
|
-
teacher. The ensemble adds a little. Few-shot examples hurt on this skewed task (see
|
|
180
|
-
[Limitations](#limitations)). The student that saw only about 1,500 texts the teacher wrote itself is far
|
|
181
|
-
behind the one trained on real tweets: generated texts are much less varied than real ones, so `synth` is a
|
|
182
|
-
way to start without data, not a replacement for it. Full tables per run are in
|
|
183
|
-
[`examples/financial-tweets/`](https://github.com/Andreas-Hum/tinydistill/tree/main/examples/financial-tweets).
|
|
184
|
-
|
|
185
|
-
### GoEmotions: multi-label
|
|
186
|
-
|
|
187
|
-
Reddit comments with any of 27 emotions or neutral
|
|
188
|
-
([GoEmotions](https://huggingface.co/datasets/google-research-datasets/go_emotions), Apache 2.0), reproduced
|
|
189
|
-
by [`examples/go-emotions/run.sh`](https://github.com/Andreas-Hum/tinydistill/blob/main/examples/go-emotions/run.sh).
|
|
190
|
-
The teacher answers one yes/no question per label; accuracy is the share of texts with exactly the right set
|
|
191
|
-
of labels.
|
|
192
|
-
|
|
193
|
-
<!-- results: go-emotions/go-emotions-1.5b.md -->
|
|
194
|
-
| Model | Device | Accuracy | Macro-F1 | Micro-F1 | Agreement with teacher | Latency, batch 1 (ms) | Latency, batch 32 (ms) | Throughput (texts/s) | Size (MB) | Speed-up, batch 1 |
|
|
195
|
-
|---|---|---|---|---|---|---|---|---|---|---|
|
|
196
|
-
| teacher, zero-shot | cpu | 0.004 | 0.191 | 0.186 | n/a | 9990.9 | n/a | 0.1 | 6175 | 1.0x |
|
|
197
|
-
| teacher, zero-shot | cuda | 0.004 | 0.191 | 0.186 | n/a | 74.3 | 2131.6 | 15.0 | 3087 | 134.5x |
|
|
198
|
-
| student, PyTorch | cpu | 0.002 ± 0.001 | 0.111 ± 0.004 | 0.141 ± 0.002 | 0.015 ± 0.002 | 5.4 | 116.3 | 275.2 | 91 | 1857.3x |
|
|
199
|
-
| student, ONNX fp32 | cpu | 0.002 | 0.106 | 0.141 | 0.016 | 4.8 | 115.7 | 276.6 | 91 | 2072.9x |
|
|
200
|
-
| student, ONNX int8 | cpu | 0.002 | 0.108 | 0.141 | 0.016 | 2.0 | 66.3 | 482.8 | 23 | 5026.0x |
|
|
201
|
-
| baseline: student on 8 gold labels per class, no teacher | cpu | 0.000 ± 0.000 | 0.000 ± 0.000 | 0.000 ± 0.000 | 0.007 ± 0.000 | n/a | n/a | n/a | n/a | n/a |
|
|
202
|
-
| upper bound: student on all gold labels | cpu | 0.225 ± 0.021 | 0.034 ± 0.019 | 0.320 ± 0.034 | 0.007 ± 0.000 | n/a | n/a | n/a | n/a | n/a |
|
|
203
|
-
|
|
204
|
-
Task `go-emotions`, 5427 gold-labelled test texts. Teacher: `Qwen/Qwen2.5-1.5B-Instruct`. Student: `sentence-transformers/all-MiniLM-L6-v2`. The student learns from the teacher's soft labels on all 10000 training texts, plus cross-entropy on 8 gold labels per class (223 texts, weight 1.0). The baseline is the same student trained with cross-entropy on those 223 gold labels only, without the teacher. The upper bound is the same student trained with cross-entropy on all 10000 gold training labels and no teacher; its speed equals the student's. Values with ± are the mean and standard deviation over 3 training seeds; the ONNX rows are the exported first-seed model.
|
|
205
|
-
CPU rows use 2 threads. Latency is the median over repeated runs. Size is the teacher's weights as loaded and the students' model files. The teacher packs several texts into one forward pass; it is timed at batch 32 only when that takes under three minutes, otherwise its throughput is 1 / batch-1 latency. Speed-up is relative to the teacher, zero-shot on cpu.
|
|
206
|
-
<!-- /results -->
|
|
207
|
-
|
|
208
|
-
This is a hard setting for a 1.5B teacher: it answers yes far too often, so exact-set accuracy is near zero,
|
|
209
|
-
and the student inherits that. The run shows that multi-label tasks work end to end, not that a small
|
|
210
|
-
teacher is good at them; use a larger teacher.
|
|
211
|
-
|
|
212
|
-
### A vLLM teacher agrees with the local one
|
|
99
|
+
## Quickstart
|
|
213
100
|
|
|
214
|
-
[
|
|
215
|
-
|
|
101
|
+
To try a distilled model before training your own, the Banking77 student from the [results](#results) is on
|
|
102
|
+
the [Hugging Face Hub](https://huggingface.co/Shroomy/tinydistill-banking77-minilm):
|
|
216
103
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|---|---|---|---|
|
|
220
|
-
| 300 | 100.0% | 0.611 | 0.0900 |
|
|
104
|
+
```python
|
|
105
|
+
from tinydistill import Predictor # pip install tinydistill
|
|
221
106
|
|
|
222
|
-
|
|
223
|
-
|
|
107
|
+
clf = Predictor("hf:Shroomy/tinydistill-banking77-minilm")
|
|
108
|
+
clf.describe(["I still haven't received my new card", "My card got stolen"])
|
|
109
|
+
# [{'label': 'card_arrival', 'confidence': 0.4672}, {'label': 'lost_or_stolen_card', 'confidence': 0.9921}]
|
|
110
|
+
```
|
|
224
111
|
|
|
225
|
-
|
|
112
|
+
To distil your own:
|
|
226
113
|
|
|
227
114
|
```sh
|
|
228
|
-
pip install "tinydistill[hf]"
|
|
229
115
|
curl -LO https://raw.githubusercontent.com/Andreas-Hum/tinydistill/main/examples/banking77/task.yaml
|
|
230
116
|
tinydistill run task.yaml --data hf:PolyAI/banking77:train --test hf:PolyAI/banking77:test \
|
|
231
117
|
--out runs/banking77 --int8
|
|
232
118
|
tinydistill predict runs/banking77/onnx "I still haven't received my new card"
|
|
233
119
|
```
|
|
234
120
|
|
|
121
|
+
The same from Python, with the exported model:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from tinydistill import Predictor
|
|
125
|
+
|
|
126
|
+
clf = Predictor("runs/banking77/onnx") # or a model on the Hub: Predictor("hf:user/name")
|
|
127
|
+
clf.predict(["I still haven't received my new card"]) # a list of label names
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Or try it without installing anything, on a free Colab GPU:
|
|
131
|
+
|
|
132
|
+
| Notebook | What it shows |
|
|
133
|
+
|---|---|
|
|
134
|
+
| [Quickstart](https://colab.research.google.com/github/Andreas-Hum/tinydistill/blob/main/notebooks/quickstart.ipynb) | distil a sentiment classifier in about ten minutes, with charts of what the student learned |
|
|
135
|
+
| [Your own task](https://colab.research.google.com/github/Andreas-Hum/tinydistill/blob/main/notebooks/your_task.ipynb) | write a task for your labels, check the teacher, distil on your texts, find the mistakes, download the model |
|
|
136
|
+
| [Deploy](https://colab.research.google.com/github/Andreas-Hum/tinydistill/blob/main/notebooks/deploy.ipynb) | the model from Python, behind an HTTP server, in a web page, and on the Hugging Face Hub |
|
|
137
|
+
|
|
235
138
|
Labelling the full dataset with the default teacher wants a GPU. For a quick look on a CPU, add
|
|
236
139
|
`--teacher Qwen/Qwen2.5-0.5B-Instruct --limit 200 --test-limit 200`. `--limit` takes the first rows, and the
|
|
237
140
|
Banking77 splits are sorted by intent, so a limited run only sees a few intents.
|
|
@@ -267,6 +170,69 @@ teacher at all). See `tinydistill <command> --help`.
|
|
|
267
170
|
Long steps print their progress, speed and time left every few seconds and write it to `RUN/status.json`;
|
|
268
171
|
`tinydistill status RUN` reads it from another terminal and tells you whether the process is still alive.
|
|
269
172
|
|
|
173
|
+
## Results
|
|
174
|
+
|
|
175
|
+
Banking77 intent detection: 77 fine-grained classes, 3,080 held-out test texts. The students learn from the
|
|
176
|
+
teacher's soft labels on 10,003 training texts plus 8 gold labels per class. Latency is per text, one at a
|
|
177
|
+
time:
|
|
178
|
+
|
|
179
|
+
<!-- results: headline.md -->
|
|
180
|
+
| Model | Runs on | Test accuracy | Latency per text | Size | Speed-up |
|
|
181
|
+
|---|---|---|---|---|---|
|
|
182
|
+
| Teacher: `Qwen/Qwen2.5-7B-Instruct`, 4-bit, 8 examples per class in the prompt | RTX 4090 | 0.783 | 264 ms | 5.4 GB | 1x |
|
|
183
|
+
| Student: MiniLM-L6, int8 ONNX | CPU, 2 threads | 0.814 | 1.2 ms | 23 MB | 214x |
|
|
184
|
+
| Student: static embeddings, int8 ONNX | CPU, 2 threads | 0.802 | 0.1 ms | 8 MB | 3244x |
|
|
185
|
+
<!-- /results -->
|
|
186
|
+
|
|
187
|
+
- The students are **more accurate than their teacher**: learning its soft labels across 10,000 texts smooths
|
|
188
|
+
out some of its individual mistakes.
|
|
189
|
+
- On a CPU they are **hundreds of times faster** than the teacher on a GPU, at a fraction of its size.
|
|
190
|
+
- **The teacher sets the ceiling.** With a 1.5B teacher, or without examples in its prompt, the students are
|
|
191
|
+
much weaker; the same student trained on all 10,003 gold labels reaches 0.914.
|
|
192
|
+
|
|
193
|
+
[RESULTS.md](https://github.com/Andreas-Hum/tinydistill/blob/main/RESULTS.md) has every run: four teachers, five student models trained for 5 to 20 epochs, the gold-only baseline,
|
|
194
|
+
the upper bound and the cascade on Banking77, a teacher ensemble and synthetic texts on financial tweets,
|
|
195
|
+
multi-label GoEmotions, and a vLLM check. Every number comes from a saved results file written by the
|
|
196
|
+
scripts in [`examples/`](https://github.com/Andreas-Hum/tinydistill/tree/main/examples).
|
|
197
|
+
|
|
198
|
+
## How it works
|
|
199
|
+
|
|
200
|
+
```mermaid
|
|
201
|
+
flowchart LR
|
|
202
|
+
A["Task prompt +<br/>unlabelled texts"] --> B["LLM teacher(s)<br/>Qwen · Mistral · Phi · ..."]
|
|
203
|
+
S["Synthetic texts<br/>(synth)"] -.-> B
|
|
204
|
+
V["vLLM server"] -.-> B
|
|
205
|
+
K["A few gold labels<br/>per class"] -.-> B
|
|
206
|
+
K -.-> C
|
|
207
|
+
B -- "soft labels<br/>(ensembled)" --> C["Small encoder<br/>student"]
|
|
208
|
+
C --> D["ONNX / int8<br/>export"]
|
|
209
|
+
D --> E["Fast CPU<br/>inference"]
|
|
210
|
+
D --> W["Browser · server ·<br/>Hugging Face Hub"]
|
|
211
|
+
E -.-> F["Cascade: unsure<br/>texts to the LLM"]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each step is one command: `tinydistill label` (teacher), `train` (student), `export` (ONNX / int8) and
|
|
215
|
+
`predict` (inference), or `tinydistill run` for all of them. Dashed parts are optional: synthetic texts when
|
|
216
|
+
there is no unlabelled data, a vLLM server as the teacher, a few gold labels for the teacher's prompt and the
|
|
217
|
+
student's loss, and a cascade that hands the student's least confident texts to the LLM. The exported model
|
|
218
|
+
runs on a CPU, in a [browser](#deploying-cascade-server-browser), behind `tinydistill serve`, or from the
|
|
219
|
+
Hugging Face Hub.
|
|
220
|
+
|
|
221
|
+
## Guide
|
|
222
|
+
|
|
223
|
+
### Training the student
|
|
224
|
+
|
|
225
|
+
The student trains for `--epochs` passes over the training texts (default 10) on the KL divergence to the
|
|
226
|
+
teacher's soft labels, plus cross-entropy on any gold labels (`--gold-per-class`, `--gold-file`). On Banking77,
|
|
227
|
+
training longer mattered more than a bigger student: MiniLM-L6 reaches 0.729 after 5 epochs and 0.813 after 20
|
|
228
|
+
([RESULTS.md](https://github.com/Andreas-Hum/tinydistill/blob/main/RESULTS.md#banking77-the-hard-case)).
|
|
229
|
+
|
|
230
|
+
To choose the number of epochs from the data, `--val-share 0.05` holds out 5% of the training texts (texts
|
|
231
|
+
without gold labels, so no labels are lost), measures the loss on them after every epoch, keeps the best epoch
|
|
232
|
+
and stops after `--patience` epochs (default 3) without improvement. No gold labels are needed: the held-out
|
|
233
|
+
loss is measured against the teacher's soft labels. `--seeds N` trains every student N times and reports the
|
|
234
|
+
mean and standard deviation.
|
|
235
|
+
|
|
270
236
|
**Static student.** `--student static` (or `static:<encoder>`) trains a student with no attention at all: one
|
|
271
237
|
vector per token, averaged, then a linear layer, started from the encoder's own token representations (as in
|
|
272
238
|
Model2Vec). It is much smaller and faster than a transformer student. It ignores word order, so it can miss
|
|
@@ -320,6 +286,11 @@ visitor's browser with no server at all. Below the answer, an "Inside the model"
|
|
|
320
286
|
(tokens, the answer after each layer, attention, probabilities, which words matter, the teacher's soft labels
|
|
321
287
|
for the examples and the cascade), so the page also explains distillation. Serve the folder with any static host (GitHub Pages works).
|
|
322
288
|
|
|
289
|
+
**Hugging Face Hub.** `tinydistill push RUN/onnx --repo user/name` uploads the model with a model card. Anyone
|
|
290
|
+
can then use it without the run directory: `Predictor("hf:user/name")` in Python, or
|
|
291
|
+
`tinydistill predict hf:user/name "some text"`, `serve hf:user/name` and `web hf:user/name --out site` on the
|
|
292
|
+
command line.
|
|
293
|
+
|
|
323
294
|
### A few gold labels
|
|
324
295
|
|
|
325
296
|
If some training texts have gold labels, they can help in three places. All of them use the same K randomly
|
|
@@ -350,6 +321,30 @@ results show the mean and standard deviation.
|
|
|
350
321
|
adds them to the training texts and trains, exports and evaluates a new student on the same test set, so
|
|
351
322
|
the student learns the inputs it found hard in production.
|
|
352
323
|
|
|
324
|
+
### Charts and metrics
|
|
325
|
+
|
|
326
|
+
`tinydistill plot RUN` (with the `plot` extra) writes the charts of a run to `RUN/plots/`; in Python every
|
|
327
|
+
chart is a function in `tinydistill.plots` that returns a matplotlib figure, so it shows inline in a notebook:
|
|
328
|
+
|
|
329
|
+
| Chart | What it shows |
|
|
330
|
+
|---|---|
|
|
331
|
+
| `soft_labels` | for a few test texts, the teacher's probabilities (the soft labels) next to the student's |
|
|
332
|
+
| `speed_accuracy` | accuracy against latency for the teacher and every form of the student, with the baseline and upper bound |
|
|
333
|
+
| `training` | the loss per epoch on the training texts and, with `--val-share`, on the held-out texts and the kept epoch |
|
|
334
|
+
| `per_label` | F1 of every label for teacher and student, the teacher's weakest first |
|
|
335
|
+
| `confusions` | the confusion matrix over the labels involved in the most mistakes |
|
|
336
|
+
| `cascade` | accuracy as the student passes more of its least confident texts to the teacher |
|
|
337
|
+
|
|
338
|
+
<p align="center">
|
|
339
|
+
<img alt="Soft labels on three Banking77 test texts: the teacher's probabilities next to the student's" src="https://raw.githubusercontent.com/Andreas-Hum/tinydistill/main/docs/images/banking77-soft-labels.png" width="49%">
|
|
340
|
+
<img alt="Accuracy against latency on Banking77: the students on a CPU, the teacher on a GPU, the baseline and the upper bound" src="https://raw.githubusercontent.com/Andreas-Hum/tinydistill/main/docs/images/banking77-speed-accuracy.png" width="49%">
|
|
341
|
+
</p>
|
|
342
|
+
|
|
343
|
+
Every run's results report accuracy and macro-F1, agreement with the teacher, latency at batch 1 and 32,
|
|
344
|
+
throughput and size; tasks with more than five labels also get top-3 accuracy, multi-label tasks micro-F1, and
|
|
345
|
+
ordered labels the mean distance in label steps. `tinydistill analyse RUN` adds a page of per-label scores and
|
|
346
|
+
example mistakes.
|
|
347
|
+
|
|
353
348
|
### From Python
|
|
354
349
|
|
|
355
350
|
The exported model:
|
|
@@ -470,7 +465,16 @@ The student minimises KL(teacher || student) over the training texts, plus `--ce
|
|
|
470
465
|
cross-entropy on the K gold labels per class chosen by `--gold-per-class`, if any (standard knowledge
|
|
471
466
|
distillation, Hinton et al., 2015).
|
|
472
467
|
|
|
473
|
-
##
|
|
468
|
+
## When to use tinydistill, and when not
|
|
469
|
+
|
|
470
|
+
Use it when an LLM prompt already classifies your texts well enough, but it is too slow, too expensive or too
|
|
471
|
+
large to run on every text: tinydistill keeps most of its quality in a model that runs in milliseconds on a CPU.
|
|
472
|
+
Do not use it when the LLM itself is not good at the task (the student inherits its mistakes), when you have
|
|
473
|
+
enough gold labels to train a classifier directly, or when the task needs generated text rather than a choice
|
|
474
|
+
among labels.
|
|
475
|
+
|
|
476
|
+
### Limitations
|
|
477
|
+
|
|
474
478
|
|
|
475
479
|
- **The teacher caps the student.** The student learns to imitate the teacher, mistakes included. A small LLM
|
|
476
480
|
choosing zero-shot among many fine-grained labels can be weak: Qwen2.5-0.5B-Instruct, for example, often
|
|
@@ -495,7 +499,7 @@ distillation, Hinton et al., 2015).
|
|
|
495
499
|
own hardware.
|
|
496
500
|
- ONNX export uses PyTorch's TorchScript-based exporter.
|
|
497
501
|
|
|
498
|
-
##
|
|
502
|
+
## Contributing
|
|
499
503
|
|
|
500
504
|
```sh
|
|
501
505
|
python -m venv .venv && . .venv/bin/activate
|
|
@@ -508,8 +512,30 @@ The tests run on a CPU in a few seconds with tiny random models from `hf-interna
|
|
|
508
512
|
scores against a direct computation, the distillation loss and metrics against hand-computed values, cache
|
|
509
513
|
resumption, that the ONNX export reproduces the PyTorch logits, and the whole CLI end to end.
|
|
510
514
|
|
|
511
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to propose changes, [SECURITY.md](SECURITY.md) for reporting
|
|
512
|
-
vulnerabilities and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for the code of conduct.
|
|
515
|
+
See [CONTRIBUTING.md](https://github.com/Andreas-Hum/tinydistill/blob/main/CONTRIBUTING.md) for how to propose changes, [SECURITY.md](https://github.com/Andreas-Hum/tinydistill/blob/main/SECURITY.md) for reporting
|
|
516
|
+
vulnerabilities and [CODE_OF_CONDUCT.md](https://github.com/Andreas-Hum/tinydistill/blob/main/CODE_OF_CONDUCT.md) for the code of conduct.
|
|
517
|
+
|
|
518
|
+
## Citation
|
|
519
|
+
|
|
520
|
+
If tinydistill helps your work, please cite it:
|
|
521
|
+
|
|
522
|
+
```bibtex
|
|
523
|
+
@software{tinydistill,
|
|
524
|
+
author = {Andreas-Hum},
|
|
525
|
+
title = {tinydistill: distil an LLM classification prompt into a small, fast model},
|
|
526
|
+
url = {https://github.com/Andreas-Hum/tinydistill},
|
|
527
|
+
year = {2026},
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## Star history
|
|
532
|
+
|
|
533
|
+
<a href="https://www.star-history.com/#Andreas-Hum/tinydistill&Date">
|
|
534
|
+
<picture>
|
|
535
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Andreas-Hum/tinydistill&type=Date&theme=dark">
|
|
536
|
+
<img alt="Star history of tinydistill" src="https://api.star-history.com/svg?repos=Andreas-Hum/tinydistill&type=Date" width="600">
|
|
537
|
+
</picture>
|
|
538
|
+
</a>
|
|
513
539
|
|
|
514
540
|
## References
|
|
515
541
|
|