Text Generation
Transformers
Safetensors
GGUF
English
qwen3
search
rag
question-answering
on-device
tiny
muon
text-generation-inference
Instructions to use ahkamboh/tiny-search-reader-60M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ahkamboh/tiny-search-reader-60M with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ahkamboh/tiny-search-reader-60M")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("ahkamboh/tiny-search-reader-60M") model = AutoModelForCausalLM.from_pretrained("ahkamboh/tiny-search-reader-60M", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use ahkamboh/tiny-search-reader-60M with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf ahkamboh/tiny-search-reader-60M:Q4_K_M # Run inference directly in the terminal: llama cli -hf ahkamboh/tiny-search-reader-60M:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf ahkamboh/tiny-search-reader-60M:Q4_K_M # Run inference directly in the terminal: llama cli -hf ahkamboh/tiny-search-reader-60M:Q4_K_M
Build from source code
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-app # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama serve -hf ahkamboh/tiny-search-reader-60M:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama cli -hf ahkamboh/tiny-search-reader-60M:Q4_K_M
Use Docker
docker model run hf.co/ahkamboh/tiny-search-reader-60M:Q4_K_M
- Llama new
- LM Studio
- Jan
- vLLM
How to use ahkamboh/tiny-search-reader-60M with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ahkamboh/tiny-search-reader-60M" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahkamboh/tiny-search-reader-60M", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/ahkamboh/tiny-search-reader-60M:Q4_K_M
- SGLang
How to use ahkamboh/tiny-search-reader-60M 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 "ahkamboh/tiny-search-reader-60M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahkamboh/tiny-search-reader-60M", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'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 "ahkamboh/tiny-search-reader-60M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ahkamboh/tiny-search-reader-60M", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Ollama
How to use ahkamboh/tiny-search-reader-60M with Ollama:
ollama run hf.co/ahkamboh/tiny-search-reader-60M:Q4_K_M
- Unsloth Desktop
- Docker Model Runner
How to use ahkamboh/tiny-search-reader-60M with Docker Model Runner:
docker model run hf.co/ahkamboh/tiny-search-reader-60M:Q4_K_M
- Lemonade
How to use ahkamboh/tiny-search-reader-60M with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull ahkamboh/tiny-search-reader-60M:Q4_K_M
Run and chat with the model
lemonade run user.tiny-search-reader-60M-Q4_K_M
List all available models
lemonade list
- Atomic Chat
Add comparison with other models, speed charts and graphs
Browse files- .gitattributes +4 -0
- README.md +102 -1
- assets/accuracy_vs_size.png +3 -0
- assets/by_kind.png +0 -0
- assets/make_charts.py +243 -0
- assets/speed.png +3 -0
- assets/trick_questions.png +3 -0
- assets/versions.png +3 -0
- eval/results.md +86 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
gguf/tiny-search-reader-60M-Q4_K_M.gguf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
gguf/tiny-search-reader-60M-Q8_0.gguf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
gguf/tiny-search-reader-60M-Q4_K_M.gguf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
gguf/tiny-search-reader-60M-Q8_0.gguf filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
assets/accuracy_vs_size.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/speed.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/trick_questions.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
assets/versions.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -24,6 +24,12 @@ It does not search and it is not a chatbot. Your app runs the search and passes
|
|
| 24 |
|
| 25 |
On 258 real Google questions it is right 79.8% of the time (206/258). About one answer in five is wrong.
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
## What it does
|
| 28 |
|
| 29 |
Made-up results, real output (the demo in `searchreader.py`):
|
|
@@ -47,6 +53,7 @@ result names a recent winner. On the tests it catches 41 of 50 trick questions (
|
|
| 47 |
| `gguf/tiny-search-reader-60M-Q4_K_M.gguf` | 41.0 MB | for llama.cpp; smaller, slightly different answers |
|
| 48 |
| `searchreader.py` | | prompt builder and answer helpers for transformers and llama-cpp-python |
|
| 49 |
| `eval/` | | the test questions, gold answers, this model's answers, grading code and full results |
|
|
|
|
| 50 |
| `LICENSE` | | Apache-2.0 |
|
| 51 |
|
| 52 |
## How to run
|
|
@@ -210,6 +217,8 @@ Earlier versions from this project, same tests (Colab GPU runs, bf16):
|
|
| 210 |
| v3a | 77.1% (199) | 74.7% | 65.0% |
|
| 211 |
| v3.1a | 79.8% (206) | 75.9% | 61.7% |
|
| 212 |
|
|
|
|
|
|
|
| 213 |
Paired tests (McNemar): against v2a, v3.1a fixes 17 google_big questions and breaks 6 (p=0.035), and on the "latest"
|
| 214 |
questions fixes 13 and breaks 1 (p=0.002). Against v3a it fixes 22 and breaks 15 of the 258 (p=0.32, CPU fp32), so
|
| 215 |
that gain is within noise.
|
|
@@ -239,6 +248,63 @@ Caveats:
|
|
| 239 |
- About a quarter of the test questions (65 of 258) are about Pakistan.
|
| 240 |
- Nothing was measured on a phone.
|
| 241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
## Phone files
|
| 243 |
|
| 244 |
| file | size | all 258 (llama.cpp, CPU) | answers same as fp32 | 449-token prompt + 24 tokens | average test question | peak RSS |
|
|
@@ -252,6 +318,40 @@ roughly 0.5 to 0.9 s per question; that is an estimate, not a measurement. Q8_0
|
|
| 252 |
difference matters: it gave the same answers under Homebrew llama.cpp and llama-cpp-python 0.3.36, while Q4_K_M
|
| 253 |
changed 4 of 258 answers between the two.
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
## Training
|
| 256 |
|
| 257 |
### Architecture
|
|
@@ -318,7 +418,8 @@ Synthetic categories:
|
|
| 318 |
`not found` once and the other five times gave a nearby time or number, such as `8` for "How many books does the
|
| 319 |
Harbor Street library have?" from a result saying the library is open from 8 am to 8 pm.
|
| 320 |
- "Latest" and "current" answers depend on the dates in the results. The model does not know today's date; it picks
|
| 321 |
-
the result that looks newest. Latest-version (6/14) and most-recent-winner (14/26) questions are its weakest kinds
|
|
|
|
| 322 |
- 512 tokens of context, about five Google-length results.
|
| 323 |
- No chat and no instructions: one question in, a few words out, in the prompt format above only.
|
| 324 |
|
|
|
|
| 24 |
|
| 25 |
On 258 real Google questions it is right 79.8% of the time (206/258). About one answer in five is wrong.
|
| 26 |
|
| 27 |
+
That is level with Qwen3-0.6B (207/258), a model ten times its size, and on a laptop CPU it answers about 5 times
|
| 28 |
+
faster. A SmolLM2-135M fine-tuned on the same training data scores a little higher (214/258). Details are in
|
| 29 |
+
[Compared with other models](#compared-with-other-models).
|
| 30 |
+
|
| 31 |
+

|
| 32 |
+
|
| 33 |
## What it does
|
| 34 |
|
| 35 |
Made-up results, real output (the demo in `searchreader.py`):
|
|
|
|
| 53 |
| `gguf/tiny-search-reader-60M-Q4_K_M.gguf` | 41.0 MB | for llama.cpp; smaller, slightly different answers |
|
| 54 |
| `searchreader.py` | | prompt builder and answer helpers for transformers and llama-cpp-python |
|
| 55 |
| `eval/` | | the test questions, gold answers, this model's answers, grading code and full results |
|
| 56 |
+
| `assets/` | | the charts on this page and `make_charts.py`, which draws them |
|
| 57 |
| `LICENSE` | | Apache-2.0 |
|
| 58 |
|
| 59 |
## How to run
|
|
|
|
| 217 |
| v3a | 77.1% (199) | 74.7% | 65.0% |
|
| 218 |
| v3.1a | 79.8% (206) | 75.9% | 61.7% |
|
| 219 |
|
| 220 |
+

|
| 221 |
+
|
| 222 |
Paired tests (McNemar): against v2a, v3.1a fixes 17 google_big questions and breaks 6 (p=0.035), and on the "latest"
|
| 223 |
questions fixes 13 and breaks 1 (p=0.002). Against v3a it fixes 22 and breaks 15 of the 258 (p=0.32, CPU fp32), so
|
| 224 |
that gain is within noise.
|
|
|
|
| 248 |
- About a quarter of the test questions (65 of 258) are about Pakistan.
|
| 249 |
- Nothing was measured on a phone.
|
| 250 |
|
| 251 |
+
## Compared with other models
|
| 252 |
+
|
| 253 |
+
Same 258 questions and the same grading, all on Colab GPU in bf16 with greedy decoding.
|
| 254 |
+
|
| 255 |
+
- Zero-shot models got the question and the numbered results in their own chat template (Qwen3 with thinking off),
|
| 256 |
+
with one instruction: "Answer the question using only the numbered search results below. Reply with just the
|
| 257 |
+
answer in a few words. If the results do not answer the question, reply exactly: not found".
|
| 258 |
+
- The two extractive models (deepset's SQuAD 2.0 readers) pick an answer span from the five results.
|
| 259 |
+
- SmolLM2-135M (fine-tuned) is the base SmolLM2-135M trained for 2 epochs on exactly the rows this model was
|
| 260 |
+
trained on.
|
| 261 |
+
|
| 262 |
+
The last column compares each model with this one question by question: questions only that model got right
|
| 263 |
+
against questions only this model got right, with the exact McNemar test (p below 0.05 means a real difference).
|
| 264 |
+
|
| 265 |
+
| model | how it was run | parameters | all 258 | trick questions caught (of 50) | "latest" questions (of 60) | only it right vs only this model right |
|
| 266 |
+
|---|---|---:|---:|---:|---:|---|
|
| 267 |
+
| Qwen3-4B | zero-shot | 4.02B | 91.5% (236) | 44 | 52 | 42 vs 12, p<0.001 |
|
| 268 |
+
| Qwen3-1.7B | zero-shot | 1.72B | 84.5% (218) | 26 | 54 | 39 vs 27, p=0.175 |
|
| 269 |
+
| SmolLM2-135M, fine-tuned on this model's data | fine-tuned | 135M | 82.9% (214) | 32 | 47 | 33 vs 25, p=0.358 |
|
| 270 |
+
| Qwen3-0.6B | zero-shot | 596M | 80.2% (207) | 30 | 45 | 37 vs 36, p=1.000 |
|
| 271 |
+
| **tiny-search-reader-60M** | trained for this task | 62M | **79.8% (206)** | **41** | 37 | |
|
| 272 |
+
| RoBERTa-base SQuAD2 | extractive | 124M | 71.3% (184) | 29 | 39 | 26 vs 48, p=0.014 |
|
| 273 |
+
| MiniLM SQuAD2 | extractive | 33M | 69.4% (179) | 27 | 35 | 22 vs 49, p=0.002 |
|
| 274 |
+
| SmolLM2-360M-Instruct | zero-shot | 362M | 65.9% (170) | 0 | 44 | 25 vs 61, p<0.001 |
|
| 275 |
+
| Qwen2.5-0.5B-Instruct | zero-shot | 494M | 65.1% (168) | 0 | 40 | 26 vs 64, p<0.001 |
|
| 276 |
+
| SmolLM2-135M-Instruct | zero-shot | 135M | 59.7% (154) | 0 | 40 | 21 vs 73, p<0.001 |
|
| 277 |
+
|
| 278 |
+
What the table shows:
|
| 279 |
+
|
| 280 |
+
- It is clearly better than both SQuAD 2.0 readers and the three instruct models below 0.6B. It ties Qwen3-0.6B
|
| 281 |
+
(37 questions against 36). Qwen3-1.7B gets 12 more right, which is not a significant gap on 258 questions.
|
| 282 |
+
Qwen3-4B, 65 times larger, is clearly better.
|
| 283 |
+
- It catches 41 of the 50 trick questions, more than every model here except Qwen3-4B (44). The three instruct
|
| 284 |
+
models below 0.6B never replied `not found`.
|
| 285 |
+
- It is weak on the 60 "latest / current / most recent" questions: 37 right, fewer than every generative model
|
| 286 |
+
tested. Qwen3-1.7B (54) and Qwen3-4B (52) are significantly better there (p of 0.001 or less).
|
| 287 |
+
- SmolLM2-135M fine-tuned on the same rows scores 214 against 206. That gap is not significant (p=0.358), but on the
|
| 288 |
+
latest questions it is (47 against 37, p=0.021), and it catches fewer trick questions (32 against 41). It has 2.2
|
| 289 |
+
times the parameters, and its base was pretrained on about 2 trillion tokens, against 10 billion here. So most of
|
| 290 |
+
the result comes from the training data, and a base model with more pretraining makes better use of it.
|
| 291 |
+
|
| 292 |
+

|
| 293 |
+
|
| 294 |
+

|
| 295 |
+
|
| 296 |
+
Caveats for this comparison:
|
| 297 |
+
|
| 298 |
+
- The zero-shot models were pretrained on the open web and may know some answers without reading the results.
|
| 299 |
+
This model's training held out every test question and topic.
|
| 300 |
+
- One fixed prompt was used for every zero-shot model. Another prompt could raise or lower their scores.
|
| 301 |
+
- The Qwen and SmolLM2 model cards suggest sampling; greedy decoding was used for every model so the run is
|
| 302 |
+
repeatable.
|
| 303 |
+
- google25 is this model's dev set, and google_fresh and google_big guided the v3.1 data (see the caveats above),
|
| 304 |
+
which favours this model a little.
|
| 305 |
+
|
| 306 |
+
Per-set scores, google_big by kind and every paired test are in [`eval/results.md`](eval/results.md).
|
| 307 |
+
|
| 308 |
## Phone files
|
| 309 |
|
| 310 |
| file | size | all 258 (llama.cpp, CPU) | answers same as fp32 | 449-token prompt + 24 tokens | average test question | peak RSS |
|
|
|
|
| 318 |
difference matters: it gave the same answers under Homebrew llama.cpp and llama-cpp-python 0.3.36, while Q4_K_M
|
| 319 |
changed 4 of 258 answers between the two.
|
| 320 |
|
| 321 |
+
### Speed in tokens per second
|
| 322 |
+
|
| 323 |
+
Measured with llama.cpp on an Apple M1 Pro, CPU only (no GPU, no Metal), on a real 449-token test prompt plus a
|
| 324 |
+
24-token answer, median of 5 runs. "Reading" is the prompt pass, "writing" is generating the answer.
|
| 325 |
+
|
| 326 |
+
| file | threads | reading (tokens/s) | writing (tokens/s) | 449-token prompt + 24-token answer |
|
| 327 |
+
|---|---:|---:|---:|---:|
|
| 328 |
+
| `tiny-search-reader-60M-Q8_0.gguf` | 1 | 558 | 135 | 982 ms |
|
| 329 |
+
| `tiny-search-reader-60M-Q8_0.gguf` | 2 | 1,100 | 210 | 522 ms |
|
| 330 |
+
| `tiny-search-reader-60M-Q8_0.gguf` | 4 | 1,884 | 305 | 317 ms |
|
| 331 |
+
| `tiny-search-reader-60M-Q4_K_M.gguf` | 1 | 455 | 154 | 1,142 ms |
|
| 332 |
+
| `tiny-search-reader-60M-Q4_K_M.gguf` | 2 | 894 | 244 | 601 ms |
|
| 333 |
+
| `tiny-search-reader-60M-Q4_K_M.gguf` | 4 | 1,559 | 362 | 354 ms |
|
| 334 |
+
|
| 335 |
+
A typical test question has about 317 prompt tokens and a 5-token answer, so most of the time goes into reading the
|
| 336 |
+
results: 168 ms per question with Q8_0 at 4 threads. These numbers come from Homebrew's prebuilt llama.cpp; in one
|
| 337 |
+
check a llama.cpp built on this machine was about twice as fast, so treat them as a floor for this Mac.
|
| 338 |
+
|
| 339 |
+
### Compared with Qwen3-0.6B
|
| 340 |
+
|
| 341 |
+
Same Mac, same llama.cpp build (Homebrew build 11146), `llama-bench -p 449 -n 24 -r 5 -ngl 0` (all layers on the CPU),
|
| 342 |
+
both files Q8_0. llama-bench measures writing from an empty context, so its writing numbers are higher than in the
|
| 343 |
+
table above, where the answer is written after the 449-token prompt.
|
| 344 |
+
|
| 345 |
+
| model | file | parameters | reading (tokens/s), 1 / 4 threads | writing (tokens/s), 1 / 4 threads | 449-token prompt + 24-token answer, 4 threads |
|
| 346 |
+
|---|---:|---:|---:|---:|---:|
|
| 347 |
+
| tiny-search-reader-60M | 70 MB | 62M | 575 / 1,900 | 310 / 561 | 0.28 s |
|
| 348 |
+
| Qwen3-0.6B | 634 MB | 596M | 89 / 340 | 82 / 132 | 1.50 s |
|
| 349 |
+
|
| 350 |
+
On this machine it answers about 5 times faster than Qwen3-0.6B and its file is 9 times smaller. Accuracy against
|
| 351 |
+
Qwen3-0.6B is in [Compared with other models](#compared-with-other-models).
|
| 352 |
+
|
| 353 |
+

|
| 354 |
+
|
| 355 |
## Training
|
| 356 |
|
| 357 |
### Architecture
|
|
|
|
| 418 |
`not found` once and the other five times gave a nearby time or number, such as `8` for "How many books does the
|
| 419 |
Harbor Street library have?" from a result saying the library is open from 8 am to 8 pm.
|
| 420 |
- "Latest" and "current" answers depend on the dates in the results. The model does not know today's date; it picks
|
| 421 |
+
the result that looks newest. Latest-version (6/14) and most-recent-winner (14/26) questions are its weakest kinds, and every other
|
| 422 |
+
generative model tested did better on the 60 "latest" questions.
|
| 423 |
- 512 tokens of context, about five Google-length results.
|
| 424 |
- No chat and no instructions: one question in, a few words out, in the prompt format above only.
|
| 425 |
|
assets/accuracy_vs_size.png
ADDED
|
Git LFS Details
|
assets/by_kind.png
ADDED
|
assets/make_charts.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Model-card charts for tiny-search-reader-60M, in the style of the Hugging Face blog charts: warm page, bold rounded
|
| 2 |
+
title, grey subtitle, a white rounded card with a soft shadow, a mono source line. Every number is copied from
|
| 3 |
+
runs/baselines-summary.txt (4 Oct 2026) or the README tables; nothing is computed here except percentages."""
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
|
| 7 |
+
import matplotlib
|
| 8 |
+
matplotlib.use("Agg")
|
| 9 |
+
import matplotlib.pyplot as plt
|
| 10 |
+
from matplotlib import font_manager as fm
|
| 11 |
+
from matplotlib.lines import Line2D
|
| 12 |
+
from matplotlib.patches import FancyBboxPatch
|
| 13 |
+
from matplotlib.ticker import FixedLocator, FuncFormatter, NullLocator
|
| 14 |
+
|
| 15 |
+
OUT = sys.argv[1] if len(sys.argv) > 1 else "assets"
|
| 16 |
+
os.makedirs(OUT, exist_ok=True)
|
| 17 |
+
|
| 18 |
+
TITLE = fm.FontProperties(fname="/System/Library/Fonts/Supplemental/Arial Rounded Bold.ttf")
|
| 19 |
+
for f in ("/System/Library/Fonts/Avenir Next.ttc", "/System/Library/Fonts/Menlo.ttc"):
|
| 20 |
+
fm.fontManager.addfont(f)
|
| 21 |
+
BODY, MONO = "Avenir Next", "Menlo"
|
| 22 |
+
plt.rcParams.update({"font.family": BODY, "font.size": 11, "axes.unicode_minus": False})
|
| 23 |
+
|
| 24 |
+
PAGE, CARD, EDGE = "#F7F5EF", "#FFFFFF", "#E6E1D6"
|
| 25 |
+
INK, GREY, LIGHT, GRID = "#1F1D1A", "#77726A", "#B9B3A8", "#EEEAE2"
|
| 26 |
+
OURS, OURS_SOFT = "#FF9D00", "#FFD9A0"
|
| 27 |
+
QWEN, OTHER, EXTRACT = "#5B6B8C", "#A9A396", "#8FA7A0"
|
| 28 |
+
W, H, DPI = 10.0, 6.0, 170
|
| 29 |
+
SOURCE = "source: 258 real Google questions with their top results · greedy decoding · 4 Oct 2026"
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def card_figure(title, subtitle, source=SOURCE, legend=None):
|
| 33 |
+
"""Page + title + card. Returns (fig, card box in figure fractions: x0, y0, x1, y1)."""
|
| 34 |
+
fig = plt.figure(figsize=(W, H), dpi=DPI, facecolor=PAGE)
|
| 35 |
+
fig.text(0.5 / W, 1 - 0.42 / H, title, fontproperties=TITLE, fontsize=21, color=INK, va="top")
|
| 36 |
+
fig.text(0.5 / W, 1 - 0.92 / H, subtitle, fontsize=12.5, color=GREY, va="top")
|
| 37 |
+
x0, y0, x1, y1 = 0.42, 0.62, W - 0.42, H - 1.42 # inches
|
| 38 |
+
t = fig.dpi_scale_trans
|
| 39 |
+
for i, a in enumerate((0.035, 0.025, 0.015)): # soft shadow, three layers
|
| 40 |
+
g = 0.03 + 0.03 * i
|
| 41 |
+
fig.patches.append(FancyBboxPatch((x0 - g / 2, y0 - 0.06 - g / 2), x1 - x0 + g, y1 - y0 + g,
|
| 42 |
+
boxstyle="round,pad=0,rounding_size=0.2", transform=t,
|
| 43 |
+
facecolor="#3A2E1A", edgecolor="none", alpha=a, zorder=-3))
|
| 44 |
+
fig.patches.append(FancyBboxPatch((x0, y0), x1 - x0, y1 - y0, boxstyle="round,pad=0,rounding_size=0.18",
|
| 45 |
+
transform=t, facecolor=CARD, edgecolor=EDGE, linewidth=1.1, zorder=-2))
|
| 46 |
+
fig.text(0.5 / W, 0.24 / H, source, fontfamily=MONO, fontsize=8.6, color=GREY, va="center")
|
| 47 |
+
if legend:
|
| 48 |
+
x = x1 - 0.25
|
| 49 |
+
for label, color, marker in reversed(legend):
|
| 50 |
+
tx = fig.text(x / W, (y1 - 0.32) / H, label, fontsize=10.5, color=INK, ha="right", va="center")
|
| 51 |
+
fig.canvas.draw()
|
| 52 |
+
wpx = tx.get_window_extent().width / DPI
|
| 53 |
+
m = {"●": "o", "◆": "D", "■": "s"}[marker]
|
| 54 |
+
fig.lines.append(Line2D([(x - wpx - 0.17) / W], [(y1 - 0.32) / H], transform=fig.transFigure,
|
| 55 |
+
marker=m, markersize=8 if m != "D" else 7, linestyle="none",
|
| 56 |
+
markerfacecolor="white" if m == "D" else color, markeredgecolor=color,
|
| 57 |
+
markeredgewidth=1.8 if m == "D" else 0))
|
| 58 |
+
x -= wpx + 0.62
|
| 59 |
+
return fig, (x0 / W, y0 / H, x1 / W, y1 / H)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def style(ax, grid="y"):
|
| 63 |
+
ax.set_facecolor("none")
|
| 64 |
+
for s in ("top", "right", "left"):
|
| 65 |
+
ax.spines[s].set_visible(False)
|
| 66 |
+
ax.spines["bottom"].set_color(LIGHT)
|
| 67 |
+
ax.tick_params(colors=GREY, length=0, labelsize=10.5)
|
| 68 |
+
if grid:
|
| 69 |
+
ax.grid(axis=grid, color=GRID, linewidth=1)
|
| 70 |
+
ax.set_axisbelow(True)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def save(fig, name):
|
| 74 |
+
path = os.path.join(OUT, name)
|
| 75 |
+
fig.savefig(path, dpi=DPI, facecolor=PAGE)
|
| 76 |
+
plt.close(fig)
|
| 77 |
+
print(path)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
# ---------- 1. accuracy against size ----------
|
| 81 |
+
MODELS = [ # name, params (M), right of 258, traps caught of 50, kind
|
| 82 |
+
("tiny-search-reader-60M", 62.0, 206, 41, "ours"),
|
| 83 |
+
("SmolLM2-135M fine-tuned\non the same data", 134.5, 214, 32, "ft"),
|
| 84 |
+
("SmolLM2-135M-Instruct", 134.5, 154, 0, "zs"),
|
| 85 |
+
("SmolLM2-360M-Instruct", 361.8, 170, 0, "zs"),
|
| 86 |
+
("Qwen2.5-0.5B-Instruct", 494.0, 168, 0, "zs"),
|
| 87 |
+
("Qwen3-0.6B", 596.0, 207, 30, "qwen"),
|
| 88 |
+
("Qwen3-1.7B", 1720.6, 218, 26, "qwen"),
|
| 89 |
+
("Qwen3-4B", 4022.5, 236, 44, "qwen"),
|
| 90 |
+
("MiniLM SQuAD2", 33.2, 179, 27, "ex"),
|
| 91 |
+
("RoBERTa-base SQuAD2", 124.1, 184, 29, "ex"),
|
| 92 |
+
]
|
| 93 |
+
pct = lambda n, d=258: 100 * n / d
|
| 94 |
+
|
| 95 |
+
fig, (cx0, cy0, cx1, cy1) = card_figure(
|
| 96 |
+
"Qwen3-0.6B accuracy at a tenth of the size",
|
| 97 |
+
"Right answers on 258 real Google questions, given the question and its search results",
|
| 98 |
+
legend=[("this model", OURS, "●"), ("trained on the same data", OURS, "◆"),
|
| 99 |
+
("zero-shot LLM", QWEN, "●"), ("extractive QA", EXTRACT, "■")])
|
| 100 |
+
ax = fig.add_axes([cx0 + 0.075, cy0 + 0.12, cx1 - cx0 - 0.11, cy1 - cy0 - 0.25])
|
| 101 |
+
style(ax, grid="both")
|
| 102 |
+
ax.set_xscale("log")
|
| 103 |
+
ax.set_xlim(24, 6500)
|
| 104 |
+
ax.set_ylim(55, 95)
|
| 105 |
+
ax.xaxis.set_major_locator(FixedLocator([30, 60, 100, 300, 600, 1000, 3000]))
|
| 106 |
+
ax.xaxis.set_minor_locator(NullLocator())
|
| 107 |
+
ax.xaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v / 1000:g}B" if v >= 1000 else f"{v:g}M"))
|
| 108 |
+
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:g}%"))
|
| 109 |
+
ax.set_xlabel("parameters (log scale)", color=GREY, fontsize=10.5, labelpad=6)
|
| 110 |
+
q = [m for m in MODELS if m[4] == "qwen"]
|
| 111 |
+
ax.plot([m[1] for m in q], [pct(m[2]) for m in q], color=QWEN, alpha=0.35, linewidth=2, zorder=2)
|
| 112 |
+
LABEL_POS = { # offsets in points, alignment
|
| 113 |
+
"tiny-search-reader-60M": ((0, 17), "center"), "SmolLM2-135M fine-tuned\non the same data": ((10, 4), "left"),
|
| 114 |
+
"SmolLM2-135M-Instruct": ((9, -3), "left"), "SmolLM2-360M-Instruct": ((8, 6), "left"),
|
| 115 |
+
"Qwen2.5-0.5B-Instruct": ((8, -12), "left"), "Qwen3-0.6B": ((9, -8), "left"), "Qwen3-1.7B": ((-9, 7), "right"),
|
| 116 |
+
"Qwen3-4B": ((-9, 6), "right"), "MiniLM SQuAD2": ((9, -4), "left"), "RoBERTa-base SQuAD2": ((9, 2), "left"),
|
| 117 |
+
}
|
| 118 |
+
for name, p, right, _, kind in MODELS:
|
| 119 |
+
y = pct(right)
|
| 120 |
+
if kind == "ours":
|
| 121 |
+
ax.scatter(p, y, s=340, color=OURS, alpha=0.25, zorder=3, linewidths=0)
|
| 122 |
+
ax.scatter(p, y, s=120, color=OURS, zorder=4, edgecolors="white", linewidths=1.5)
|
| 123 |
+
elif kind == "ft":
|
| 124 |
+
ax.scatter(p, y, s=110, marker="D", color="white", edgecolors=OURS, linewidths=2, zorder=4)
|
| 125 |
+
elif kind == "ex":
|
| 126 |
+
ax.scatter(p, y, s=80, marker="s", color=EXTRACT, zorder=4, edgecolors="white", linewidths=1)
|
| 127 |
+
else:
|
| 128 |
+
ax.scatter(p, y, s=85, color=QWEN if kind == "qwen" else OTHER, zorder=4, edgecolors="white", linewidths=1)
|
| 129 |
+
(dx, dy), ha = LABEL_POS[name]
|
| 130 |
+
shown = "this model" if kind == "ours" else name
|
| 131 |
+
ax.annotate(f"{shown} {y:.1f}%", (p, y), xytext=(dx, dy), textcoords="offset points", ha=ha, va="center",
|
| 132 |
+
fontsize=10 if kind != "ours" else 11, color=INK if kind in ("ours", "ft") else GREY,
|
| 133 |
+
fontweight="bold" if kind == "ours" else "normal")
|
| 134 |
+
ax.annotate("", xy=(560, pct(207)), xytext=(70, pct(206)), arrowprops=dict(arrowstyle="-", color=OURS,
|
| 135 |
+
linestyle=(0, (3, 3)), linewidth=1.3), zorder=2)
|
| 136 |
+
ax.text(230, pct(206) - 1.0, "same score, 10x fewer parameters", color="#C77700", fontsize=10, ha="center",
|
| 137 |
+
va="top")
|
| 138 |
+
save(fig, "accuracy_vs_size.png")
|
| 139 |
+
|
| 140 |
+
# ---------- 2. trick questions ----------
|
| 141 |
+
traps = sorted(MODELS, key=lambda m: (m[3], m[2]))
|
| 142 |
+
fig, (cx0, cy0, cx1, cy1) = card_figure(
|
| 143 |
+
"Says \"not found\" when the answer is not there",
|
| 144 |
+
"Trick questions caught, out of 50: the search results do not hold the answer")
|
| 145 |
+
ax = fig.add_axes([cx0 + 0.27, cy0 + 0.06, cx1 - cx0 - 0.33, cy1 - cy0 - 0.11])
|
| 146 |
+
style(ax, grid="x")
|
| 147 |
+
ax.spines["bottom"].set_visible(False)
|
| 148 |
+
names = ["SmolLM2-135M fine-tuned (same data)" if m[4] == "ft" else m[0] for m in traps]
|
| 149 |
+
colors = [OURS if m[4] == "ours" else OURS_SOFT if m[4] == "ft" else QWEN if m[4] == "qwen"
|
| 150 |
+
else EXTRACT if m[4] == "ex" else OTHER for m in traps]
|
| 151 |
+
bars = ax.barh(range(len(traps)), [m[3] for m in traps], color=colors, height=0.62, zorder=3)
|
| 152 |
+
ax.set_yticks(range(len(traps)), names, fontsize=10.5, color=INK)
|
| 153 |
+
for t, m in zip(ax.get_yticklabels(), traps):
|
| 154 |
+
if m[4] == "ours":
|
| 155 |
+
t.set_fontweight("bold")
|
| 156 |
+
ax.set_xlim(0, 50)
|
| 157 |
+
ax.xaxis.set_major_locator(FixedLocator([0, 10, 20, 30, 40, 50]))
|
| 158 |
+
for i, m in enumerate(traps):
|
| 159 |
+
label = f"{m[3]}" if m[3] else "0 (always answers something)"
|
| 160 |
+
ax.text(m[3] + 0.7, i, label, va="center", fontsize=10.5, color=INK if m[3] else GREY,
|
| 161 |
+
fontweight="bold" if m[4] == "ours" else "normal")
|
| 162 |
+
save(fig, "trick_questions.png")
|
| 163 |
+
|
| 164 |
+
# ---------- 3. speed ----------
|
| 165 |
+
fig, (cx0, cy0, cx1, cy1) = card_figure(
|
| 166 |
+
"About 5x faster than Qwen3-0.6B, from a 9x smaller file",
|
| 167 |
+
"llama.cpp on an Apple M1 Pro, CPU only, 4 threads, both files Q8_0",
|
| 168 |
+
source="source: llama-bench -p 449 -n 24 -r 5 -ngl 0 · Homebrew llama.cpp build 11146 · 4 Oct 2026",
|
| 169 |
+
legend=[("tiny-search-reader-60M", OURS, "■"), ("Qwen3-0.6B", QWEN, "■")])
|
| 170 |
+
PANELS = [("reading the results", "tokens per second, higher is better", 1900, 340, "{:,.0f}"),
|
| 171 |
+
("writing the answer", "tokens per second, higher is better", 561, 132, "{:,.0f}"),
|
| 172 |
+
("one question, 449 + 24 tokens", "seconds, lower is better", 0.28, 1.50, "{:.2f} s"),
|
| 173 |
+
("model file", "megabytes, lower is better", 70, 634, "{:,.0f} MB")]
|
| 174 |
+
pw, ph = (cx1 - cx0 - 0.08) / 2, (cy1 - cy0 - 0.13) / 2
|
| 175 |
+
for i, (head, unit, ours, qwen, fmt) in enumerate(PANELS):
|
| 176 |
+
col, row = i % 2, i // 2
|
| 177 |
+
ax = fig.add_axes([cx0 + 0.04 + col * pw + 0.012, cy0 + 0.03 + (1 - row) * ph, pw - 0.05, ph - 0.03])
|
| 178 |
+
style(ax, grid=None)
|
| 179 |
+
ax.spines["bottom"].set_visible(False)
|
| 180 |
+
ax.set_xticks([])
|
| 181 |
+
ax.set_yticks([])
|
| 182 |
+
top = max(ours, qwen) * 1.32
|
| 183 |
+
ax.set_xlim(0, top)
|
| 184 |
+
ax.barh([1, 0], [ours, qwen], color=[OURS, QWEN], height=0.62)
|
| 185 |
+
for y, v in ((1, ours), (0, qwen)):
|
| 186 |
+
ax.text(v + top * 0.015, y, fmt.format(v), va="center", fontsize=12, color=INK,
|
| 187 |
+
fontweight="bold" if y == 1 else "normal")
|
| 188 |
+
ax.set_ylim(-0.5, 2.5)
|
| 189 |
+
ax.text(0, 2.2, head, fontsize=12.5, color=INK, fontweight="bold", va="center")
|
| 190 |
+
ax.text(0, 1.62, unit, fontsize=9.5, color=GREY, va="center")
|
| 191 |
+
save(fig, "speed.png")
|
| 192 |
+
|
| 193 |
+
# ---------- 4. versions ----------
|
| 194 |
+
fig, (cx0, cy0, cx1, cy1) = card_figure(
|
| 195 |
+
"Four versions, 69% to 80%",
|
| 196 |
+
"Each version of this model on the same 258 questions (Colab GPU runs, bf16)",
|
| 197 |
+
legend=[("all 258 questions", OURS, "●"), ("the 60 latest / current / most recent questions", QWEN, "●")])
|
| 198 |
+
ax = fig.add_axes([cx0 + 0.07, cy0 + 0.1, cx1 - cx0 - 0.2, cy1 - cy0 - 0.23])
|
| 199 |
+
style(ax)
|
| 200 |
+
xs = [0, 1, 2, 3]
|
| 201 |
+
ax.set_xticks(xs, ["v1.1a", "v2a", "v3a", "v3.1a\n(this release)"], fontsize=11, color=INK)
|
| 202 |
+
ax.set_xlim(-0.35, 3.35)
|
| 203 |
+
ax.set_ylim(30, 95)
|
| 204 |
+
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:g}%"))
|
| 205 |
+
allv, latest = [69.4, 73.3, 77.1, 79.8], [41.7, 65.0, 61.7]
|
| 206 |
+
ax.plot(xs, allv, color=OURS, linewidth=3, marker="o", markersize=9, markeredgecolor="white", markeredgewidth=1.5,
|
| 207 |
+
zorder=4)
|
| 208 |
+
ax.plot(xs[1:], latest, color=QWEN, linewidth=3, marker="o", markersize=9, markeredgecolor="white",
|
| 209 |
+
markeredgewidth=1.5, zorder=4)
|
| 210 |
+
for x, v in zip(xs, allv):
|
| 211 |
+
ax.text(x, v - 4.6, f"{v:.1f}%", ha="center", fontsize=11, color=INK, fontweight="bold" if x == 3 else "normal")
|
| 212 |
+
for x, v in zip(xs[1:], latest):
|
| 213 |
+
ax.text(x, v - 5.2, f"{v:.1f}%", ha="center", fontsize=11, color=QWEN)
|
| 214 |
+
ax.axhline(80.2, color=LIGHT, linewidth=1.2, linestyle=(0, (4, 3)), zorder=2)
|
| 215 |
+
ax.text(-0.3, 80.9, "Qwen3-0.6B, zero-shot: 80.2%", ha="left", va="bottom", fontsize=9.5, color=GREY)
|
| 216 |
+
ax.text(0, 36, "not measured", ha="center", fontsize=9.5, color=LIGHT)
|
| 217 |
+
save(fig, "versions.png")
|
| 218 |
+
|
| 219 |
+
# ---------- 5. by kind ----------
|
| 220 |
+
KINDS = ["fact", "unit", "trick\n(\"not found\")", "live value", "latest\nversion", "most recent\nwinner"]
|
| 221 |
+
N = [34, 25, 28, 35, 14, 26]
|
| 222 |
+
ROWS = [("tiny-search-reader-60M", [30, 20, 24, 29, 6, 14], OURS),
|
| 223 |
+
("SmolLM2-135M fine-tuned on the same data", [31, 23, 21, 32, 9, 18], OURS_SOFT),
|
| 224 |
+
("Qwen3-0.6B, zero-shot", [31, 23, 19, 31, 11, 16], QWEN)]
|
| 225 |
+
fig, (cx0, cy0, cx1, cy1) = card_figure(
|
| 226 |
+
"Weakest on \"latest\" questions",
|
| 227 |
+
"google_big (162 questions) by kind, % right",
|
| 228 |
+
legend=[(n.split(",")[0].replace(" fine-tuned on the same data", " fine-tuned"), c, "■") for n, _, c in ROWS])
|
| 229 |
+
ax = fig.add_axes([cx0 + 0.07, cy0 + 0.12, cx1 - cx0 - 0.1, cy1 - cy0 - 0.25])
|
| 230 |
+
style(ax)
|
| 231 |
+
ax.set_ylim(0, 105)
|
| 232 |
+
ax.yaxis.set_major_locator(FixedLocator([0, 25, 50, 75, 100]))
|
| 233 |
+
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:g}%"))
|
| 234 |
+
bw = 0.26
|
| 235 |
+
for j, (name, right, color) in enumerate(ROWS):
|
| 236 |
+
xs = [i + (j - 1) * bw for i in range(len(KINDS))]
|
| 237 |
+
vals = [100 * r / n for r, n in zip(right, N)]
|
| 238 |
+
ax.bar(xs, vals, width=bw * 0.92, color=color, zorder=3)
|
| 239 |
+
for x, v, r, n in zip(xs, vals, right, N):
|
| 240 |
+
ax.text(x, v + 1.5, f"{r}", ha="center", fontsize=8.8, color=INK if j == 0 else GREY,
|
| 241 |
+
fontweight="bold" if j == 0 else "normal")
|
| 242 |
+
ax.set_xticks(range(len(KINDS)), [f"{k}\nof {n}" for k, n in zip(KINDS, N)], fontsize=10, color=INK)
|
| 243 |
+
save(fig, "by_kind.png")
|
assets/speed.png
ADDED
|
Git LFS Details
|
assets/trick_questions.png
ADDED
|
Git LFS Details
|
assets/versions.png
ADDED
|
Git LFS Details
|
eval/results.md
CHANGED
|
@@ -105,6 +105,92 @@ v3a and v3.1a on CPU, PyTorch fp32:
|
|
| 105 |
|
| 106 |
Both were part of the score used to pick v3.1a, so they are dev numbers.
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
## Caveats
|
| 109 |
|
| 110 |
- The sets are small. One question is 0.4 points of the 258 total and 0.6 points of google_big.
|
|
|
|
| 105 |
|
| 106 |
Both were part of the score used to pick v3.1a, so they are dev numbers.
|
| 107 |
|
| 108 |
+
## Other models on the same questions (Colab bf16, 4 Oct 2026)
|
| 109 |
+
|
| 110 |
+
Run on one RTX PRO 6000, bf16, greedy. v3.1a here is the Colab bf16 run (123/162 on google_big, against 124 on CPU
|
| 111 |
+
fp32), so every row is on the same footing.
|
| 112 |
+
|
| 113 |
+
- Zero-shot: one user message (no system message) in each model's chat template, Qwen3 with `enable_thinking=False`,
|
| 114 |
+
at most 32 new tokens. The message is the instruction "Answer the question using only the numbered search results
|
| 115 |
+
below. Reply with just the answer in a few words. If the results do not answer the question, reply exactly: not
|
| 116 |
+
found", then `Search results:` with the same numbered results v3.1a sees, then `Question:` and the question.
|
| 117 |
+
- Post-processing, the same for every generative model: keep the new tokens up to the first end-of-turn token, drop
|
| 118 |
+
a `<think>...</think>` block, keep the first non-empty line, strip spaces, quotes and one trailing period. A reply
|
| 119 |
+
that contains the words "not found" becomes exactly `not found`.
|
| 120 |
+
- Extractive: the five results as `title: text` lines are the context; the span the QA pipeline picks is the answer,
|
| 121 |
+
and an empty answer is `not found`.
|
| 122 |
+
- Fine-tune: HuggingFaceTB/SmolLM2-135M, 2 epochs on exactly v3.1a's training rows (258,542 examples), AdamW lr 1e-4,
|
| 123 |
+
batch 32, warmup 100 steps then linear decay, context 512, 27 minutes.
|
| 124 |
+
|
| 125 |
+
| model | kind | parameters | google25 | google_heldout | google_fresh | google_big | all 258 | google_big clean (144) | google_big latest (60) |
|
| 126 |
+
|---|---|---:|---:|---:|---:|---:|---:|---:|---:|
|
| 127 |
+
| v3.1a (this model) | trained for this task | 62.0M | 22/24 | 44/53 | 17/19 | 123/162 | 206 (79.8%) | 107 | 37 |
|
| 128 |
+
| Qwen3-4B | zero-shot | 4.02B | 21/24 | 50/53 | 17/19 | 148/162 | 236 (91.5%) | 132 | 52 |
|
| 129 |
+
| Qwen3-1.7B | zero-shot | 1.72B | 19/24 | 47/53 | 15/19 | 137/162 | 218 (84.5%) | 120 | 54 |
|
| 130 |
+
| SmolLM2-135M fine-tuned on v3.1a's rows | fine-tune | 134.5M | 20/24 | 45/53 | 15/19 | 134/162 | 214 (82.9%) | 116 | 47 |
|
| 131 |
+
| Qwen3-0.6B | zero-shot | 596.0M | 17/24 | 46/53 | 13/19 | 131/162 | 207 (80.2%) | 115 | 45 |
|
| 132 |
+
| deepset/roberta-base-squad2 | extractive | 124.1M | 15/24 | 42/53 | 14/19 | 113/162 | 184 (71.3%) | 98 | 39 |
|
| 133 |
+
| deepset/minilm-uncased-squad2 | extractive | 33.2M | 15/24 | 43/53 | 16/19 | 105/162 | 179 (69.4%) | 94 | 35 |
|
| 134 |
+
| SmolLM2-360M-Instruct | zero-shot | 361.8M | 14/24 | 40/53 | 8/19 | 108/162 | 170 (65.9%) | 94 | 44 |
|
| 135 |
+
| Qwen2.5-0.5B-Instruct | zero-shot | 494.0M | 18/24 | 39/53 | 7/19 | 104/162 | 168 (65.1%) | 90 | 40 |
|
| 136 |
+
| SmolLM2-135M-Instruct | zero-shot | 134.5M | 14/24 | 37/53 | 6/19 | 97/162 | 154 (59.7%) | 83 | 40 |
|
| 137 |
+
|
| 138 |
+
google_big clean leaves out the 18 google_big questions that v1.1's training rows ask word for word. google_big
|
| 139 |
+
latest is its 60 latest / current / most recent questions.
|
| 140 |
+
|
| 141 |
+
Verdicts over all 258 questions:
|
| 142 |
+
|
| 143 |
+
| model | answered correct (of 208) | missed (said `not found`) | wrong | trap caught (of 50) | trap fooled |
|
| 144 |
+
|---|---:|---:|---:|---:|---:|
|
| 145 |
+
| v3.1a | 165 | 12 | 31 | 41 | 9 |
|
| 146 |
+
| Qwen3-4B | 192 | 0 | 16 | 44 | 6 |
|
| 147 |
+
| Qwen3-1.7B | 192 | 0 | 16 | 26 | 24 |
|
| 148 |
+
| SmolLM2-135M fine-tuned | 182 | 4 | 22 | 32 | 18 |
|
| 149 |
+
| Qwen3-0.6B | 177 | 10 | 21 | 30 | 20 |
|
| 150 |
+
| roberta-base-squad2 | 155 | 22 | 31 | 29 | 21 |
|
| 151 |
+
| minilm-uncased-squad2 | 152 | 13 | 43 | 27 | 23 |
|
| 152 |
+
| SmolLM2-360M-Instruct | 170 | 0 | 38 | 0 | 50 |
|
| 153 |
+
| Qwen2.5-0.5B-Instruct | 168 | 0 | 40 | 0 | 50 |
|
| 154 |
+
| SmolLM2-135M-Instruct | 154 | 0 | 54 | 0 | 50 |
|
| 155 |
+
|
| 156 |
+
google_big by kind (right / questions):
|
| 157 |
+
|
| 158 |
+
| model | fact (34) | unit (25) | trick (28) | live (35) | version (14) | winner (26) |
|
| 159 |
+
|---|---:|---:|---:|---:|---:|---:|
|
| 160 |
+
| v3.1a | 30 | 20 | 24 | 29 | 6 | 14 |
|
| 161 |
+
| Qwen3-4B | 33 | 25 | 24 | 34 | 12 | 20 |
|
| 162 |
+
| Qwen3-1.7B | 33 | 23 | 13 | 34 | 12 | 22 |
|
| 163 |
+
| SmolLM2-135M fine-tuned | 31 | 23 | 21 | 32 | 9 | 18 |
|
| 164 |
+
| Qwen3-0.6B | 31 | 23 | 19 | 31 | 11 | 16 |
|
| 165 |
+
| roberta-base-squad2 | 26 | 19 | 16 | 29 | 8 | 15 |
|
| 166 |
+
| minilm-uncased-squad2 | 26 | 20 | 14 | 26 | 8 | 11 |
|
| 167 |
+
| SmolLM2-360M-Instruct | 32 | 20 | 0 | 30 | 8 | 18 |
|
| 168 |
+
| Qwen2.5-0.5B-Instruct | 31 | 21 | 0 | 31 | 8 | 13 |
|
| 169 |
+
| SmolLM2-135M-Instruct | 30 | 18 | 0 | 28 | 5 | 16 |
|
| 170 |
+
|
| 171 |
+
Paired against v3.1a, question by question. Each cell is "only v3.1a right / only the other model right, exact
|
| 172 |
+
McNemar p"; `*` marks p < 0.05.
|
| 173 |
+
|
| 174 |
+
| model | google25 | google_heldout | google_fresh | google_big | all 258 | google_big clean | google_big latest |
|
| 175 |
+
|---|---|---|---|---|---|---|---|
|
| 176 |
+
| Qwen3-4B | 2/1, 1.000 | 1/7, 0.070 | 2/2, 1.000 | 7/32, 0.000* | 12/42, 0.000* | 5/30, 0.000* | 2/17, 0.001* |
|
| 177 |
+
| Qwen3-1.7B | 4/1, 0.375 | 3/6, 0.508 | 4/2, 0.688 | 16/30, 0.054 | 27/39, 0.175 | 15/28, 0.066 | 0/17, 0.000* |
|
| 178 |
+
| SmolLM2-135M fine-tuned | 3/1, 0.625 | 5/6, 1.000 | 3/1, 0.625 | 14/25, 0.108 | 25/33, 0.358 | 14/23, 0.188 | 3/13, 0.021* |
|
| 179 |
+
| Qwen3-0.6B | 6/1, 0.125 | 5/7, 0.774 | 5/1, 0.219 | 20/28, 0.312 | 36/37, 1.000 | 18/26, 0.291 | 8/16, 0.152 |
|
| 180 |
+
| roberta-base-squad2 | 7/0, 0.016* | 7/5, 0.774 | 4/1, 0.375 | 30/20, 0.203 | 48/26, 0.014* | 27/18, 0.233 | 7/9, 0.804 |
|
| 181 |
+
| minilm-uncased-squad2 | 7/0, 0.016* | 6/5, 1.000 | 2/1, 1.000 | 34/16, 0.015* | 49/22, 0.002* | 28/15, 0.066 | 11/9, 0.824 |
|
| 182 |
+
| SmolLM2-360M-Instruct | 8/0, 0.008* | 8/4, 0.388 | 9/0, 0.004* | 36/21, 0.063 | 61/25, 0.000* | 32/19, 0.092 | 4/11, 0.118 |
|
| 183 |
+
| Qwen2.5-0.5B-Instruct | 5/1, 0.219 | 9/4, 0.267 | 10/0, 0.002* | 40/21, 0.020* | 64/26, 0.000* | 36/19, 0.030* | 7/10, 0.629 |
|
| 184 |
+
| SmolLM2-135M-Instruct | 8/0, 0.008* | 10/3, 0.092 | 11/0, 0.001* | 44/18, 0.001* | 73/21, 0.000* | 40/16, 0.002* | 7/10, 0.629 |
|
| 185 |
+
|
| 186 |
+
(0.000 means below 0.0005.)
|
| 187 |
+
|
| 188 |
+
Caveats for this comparison: the zero-shot models were pretrained on the open web and may know some answers without
|
| 189 |
+
reading the results, while v3.1a's training held out every test question and topic. google25 is v3.1a's dev set
|
| 190 |
+
and google_fresh / google_big guided the v3.1 data, which favours v3.1a a little. The Qwen and SmolLM2 model cards
|
| 191 |
+
suggest sampling; greedy is used for every model so the run is deterministic. Qwen3-4B is a reference at about 65
|
| 192 |
+
times v3.1a's size.
|
| 193 |
+
|
| 194 |
## Caveats
|
| 195 |
|
| 196 |
- The sets are small. One question is 0.4 points of the 258 total and 0.6 points of google_big.
|