Publish encoder-v4
Browse files- README.md +18 -13
- onnx/model-fp16.onnx +2 -2
- onnx/model-int8.onnx +2 -2
- onnx/model.onnx +2 -2
- onnx/parity.json +2 -2
README.md
CHANGED
|
@@ -49,7 +49,7 @@ Measured with `rukh eval encoder` on 2026-09-21, over
|
|
| 49 |
|
| 50 |
### Acceptance bars
|
| 51 |
|
| 52 |
-
The project set these two bars for the encoder in `
|
| 53 |
is where the release stands against them, and it is the same table whether the answer is
|
| 54 |
flattering or not.
|
| 55 |
|
|
@@ -99,10 +99,10 @@ How to read these numbers:
|
|
| 99 |
|
| 100 |
- the blunder F1 of the encoder and of the material baseline are measured on the same rows of the held-out 'val' split, which is drawn by game_id, never by position, and those rows are cut in two by game_id again: the 'tune' half chooses the threshold and the 'score' half is what gets reported
|
| 101 |
- the checkpoint carries no label-count curve: run `rukh train heads --curve` and evaluate one of its checkpoints to fill that table
|
| 102 |
-
- the encoder is +9.7 F1 points from the baseline;
|
| 103 |
-
- the value head is correlated against `tanh(cp / 400)`, the bounded score it is trained on, and not against raw `cp`, where a forced mate is worth ±9 99x and a few rows would decide Pearson for the whole set; the
|
| 104 |
- the baseline counts material (1/3/3/5/9) and mobility and looks one ply ahead at captures: it cannot see a positional sacrifice, and calls Fischer's 17...Be6 (Byrne-Fischer, 1956) a nine-point blunder
|
| 105 |
-
- the two blunder detectors do not see the same thing: the baseline is given the predecessor position and the move that was played, while the encoder is given only the resulting position and has to infer that something was thrown away. That is the comparison
|
| 106 |
- the blunder threshold 0.09617 was chosen on the 'tune' half (3,793 rows, 2,455 games) by maximising F1 there, and the reported numbers are measured on the 'score' half (3,660 rows, 2,368 games), which no threshold ever saw; the same rows at the fixed threshold 0.5 give an F1 of 0.0000 against 0.1855 tuned
|
| 107 |
- the material baseline is a hard yes/no rule: it has no threshold, so nothing was tuned on its side and it got no half to tune on. The margin therefore compares a model at its best operating point against a rule at its only one
|
| 108 |
- a blunder is rare (3.7 % of the labelled rows), so **accuracy is meaningless** here: a model that always answers "no blunder" scores 96.3 % without knowing anything about chess. F1 at an arbitrary threshold is nearly as bad, because an uncalibrated sigmoid can rank the positions well and still put every probability below 0.5: that number measures the operating point, not the representation. This is why the threshold is chosen on a `tune` half and the F1 is reported on a `score` half, and why ROC AUC and average precision, which no threshold can flatter, are reported next to it
|
|
@@ -140,26 +140,31 @@ similar positions (`rukh encoder embed --positions <parquet> --out <npy>`).
|
|
| 140 |
- `onnx/parity.json`
|
| 141 |
|
| 142 |
The ONNX graph returns **two** outputs, `value` and `blunder`, because that is all the demo's
|
| 143 |
-
evaluation bar and blunder alert need; `blunder` comes out as a probability
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
### How faithful the ONNX files are
|
| 148 |
|
| 149 |
Every exported file was run against the PyTorch checkpoint on 1000
|
| 150 |
-
held-out labelled positions, comparing the blunder decision at p >= 0.
|
| 151 |
repository is that measurement, as the exporter wrote it.
|
| 152 |
|
| 153 |
| File | Same blunder decision as PyTorch | Worst `value` drift |
|
| 154 |
|---|---|---|
|
| 155 |
| `model.onnx` (fp32) | 100.0 % | 4.44e-06 |
|
| 156 |
| `model-fp16.onnx` (fp16) | 100.0 % | 0.00164 |
|
| 157 |
-
| `model-int8.onnx` (int8) |
|
| 158 |
|
| 159 |
The bar the project set itself is 99.9 %.
|
| 160 |
-
|
| 161 |
-
model
|
| 162 |
-
|
|
|
|
| 163 |
|
| 164 |
## Training recipe
|
| 165 |
|
|
@@ -276,7 +281,7 @@ The pretraining checkpoint the heads started from: `checkpoints/encoder-mmm-v4-2
|
|
| 276 |
|
| 277 |
## In the course
|
| 278 |
|
| 279 |
-
- Built in [M3 · El encoder: los labs](https://lab.rukh.borjaglez.com/curso/m3/
|
| 280 |
- Measured in the [single results table](https://lab.rukh.borjaglez.com/proyecto/) as the stage `encoder-v4`: every stage of the course, the same suite, the same day.
|
| 281 |
- Bring it to the paths the configs read: `uv run rukh pull encoder-v4`.
|
| 282 |
|
|
|
|
| 49 |
|
| 50 |
### Acceptance bars
|
| 51 |
|
| 52 |
+
The project set these two bars for the encoder in `docs/acceptance.md` before any of this was trained. This
|
| 53 |
is where the release stands against them, and it is the same table whether the answer is
|
| 54 |
flattering or not.
|
| 55 |
|
|
|
|
| 99 |
|
| 100 |
- the blunder F1 of the encoder and of the material baseline are measured on the same rows of the held-out 'val' split, which is drawn by game_id, never by position, and those rows are cut in two by game_id again: the 'tune' half chooses the threshold and the 'score' half is what gets reported
|
| 101 |
- the checkpoint carries no label-count curve: run `rukh train heads --curve` and evaluate one of its checkpoints to fill that table
|
| 102 |
+
- the encoder is +9.7 F1 points from the baseline; docs/acceptance.md asks for at least +5
|
| 103 |
+
- the value head is correlated against `tanh(cp / 400)`, the bounded score it is trained on, and not against raw `cp`, where a forced mate is worth ±9 99x and a few rows would decide Pearson for the whole set; the docs/acceptance.md bar of 0.80 is read on Spearman
|
| 104 |
- the baseline counts material (1/3/3/5/9) and mobility and looks one ply ahead at captures: it cannot see a positional sacrifice, and calls Fischer's 17...Be6 (Byrne-Fischer, 1956) a nine-point blunder
|
| 105 |
+
- the two blunder detectors do not see the same thing: the baseline is given the predecessor position and the move that was played, while the encoder is given only the resulting position and has to infer that something was thrown away. That is the comparison docs/acceptance.md asks for, but it is not a level playing field
|
| 106 |
- the blunder threshold 0.09617 was chosen on the 'tune' half (3,793 rows, 2,455 games) by maximising F1 there, and the reported numbers are measured on the 'score' half (3,660 rows, 2,368 games), which no threshold ever saw; the same rows at the fixed threshold 0.5 give an F1 of 0.0000 against 0.1855 tuned
|
| 107 |
- the material baseline is a hard yes/no rule: it has no threshold, so nothing was tuned on its side and it got no half to tune on. The margin therefore compares a model at its best operating point against a rule at its only one
|
| 108 |
- a blunder is rare (3.7 % of the labelled rows), so **accuracy is meaningless** here: a model that always answers "no blunder" scores 96.3 % without knowing anything about chess. F1 at an arbitrary threshold is nearly as bad, because an uncalibrated sigmoid can rank the positions well and still put every probability below 0.5: that number measures the operating point, not the representation. This is why the threshold is chosen on a `tune` half and the F1 is reported on a `score` half, and why ROC AUC and average precision, which no threshold can flatter, are reported next to it
|
|
|
|
| 140 |
- `onnx/parity.json`
|
| 141 |
|
| 142 |
The ONNX graph returns **two** outputs, `value` and `blunder`, because that is all the demo's
|
| 143 |
+
evaluation bar and blunder alert need; `blunder` comes out as a probability. That probability is
|
| 144 |
+
**not calibrated**: a blunder is a few per cent of the labelled rows, so the head answers low and
|
| 145 |
+
never reaches 0.5. Its operating point was tuned on a held-out half and travels in the metadata as
|
| 146 |
+
`rukh_blunder_threshold`, which is what a reader should compare against -- 0.5 is a threshold this
|
| 147 |
+
head cannot cross, and a page using it shows an alert that never fires. `model-fp16.onnx` is for
|
| 148 |
+
WebGPU and `model-int8.onnx` for the WASM fallback. The metadata also carries `rukh_kind=encoder`
|
| 149 |
+
and `rukh_heads`.
|
| 150 |
|
| 151 |
### How faithful the ONNX files are
|
| 152 |
|
| 153 |
Every exported file was run against the PyTorch checkpoint on 1000
|
| 154 |
+
held-out labelled positions, comparing the blunder decision at p >= 0.0961714. `onnx/parity.json` in this
|
| 155 |
repository is that measurement, as the exporter wrote it.
|
| 156 |
|
| 157 |
| File | Same blunder decision as PyTorch | Worst `value` drift |
|
| 158 |
|---|---|---|
|
| 159 |
| `model.onnx` (fp32) | 100.0 % | 4.44e-06 |
|
| 160 |
| `model-fp16.onnx` (fp16) | 100.0 % | 0.00164 |
|
| 161 |
+
| `model-int8.onnx` (int8) | 98.7 % | 0.117 |
|
| 162 |
|
| 163 |
The bar the project set itself is 99.9 %.
|
| 164 |
+
|
| 165 |
+
`model-int8.onnx` does not reach it: it flips the blunder decision on 1.3 % of
|
| 166 |
+
positions, roughly one in 77. That is the file the WASM fallback loads, so a phone on the
|
| 167 |
+
int8 build gets a measurably different alert from the one in the results table above.
|
| 168 |
|
| 169 |
## Training recipe
|
| 170 |
|
|
|
|
| 281 |
|
| 282 |
## In the course
|
| 283 |
|
| 284 |
+
- Built in [M3 · El encoder: los labs](https://lab.rukh.borjaglez.com/curso/m3/11-labs-del-encoder/), the lesson that runs every command behind this repository.
|
| 285 |
- Measured in the [single results table](https://lab.rukh.borjaglez.com/proyecto/) as the stage `encoder-v4`: every stage of the course, the same suite, the same day.
|
| 286 |
- Bring it to the paths the configs read: `uv run rukh pull encoder-v4`.
|
| 287 |
|
onnx/model-fp16.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a0297a40964208925840a32dda80ef4ba32210ce4ae08a16f2c58cebd0abcad2
|
| 3 |
+
size 78745496
|
onnx/model-int8.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a620e0bee38c8f5ea269a46fb419e0b77f7abc4e25851a08c238bf54a17bce83
|
| 3 |
+
size 43393464
|
onnx/model.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:65d9b4cd2b6061d6d3b8036810f371dbb96783a22134e0c22f2d742a0ef3a019
|
| 3 |
+
size 156660375
|
onnx/parity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"version": 1,
|
| 3 |
"kind": "encoder",
|
| 4 |
-
"measures": "the blunder decision at p >= 0.
|
| 5 |
"positions": 1000,
|
| 6 |
"source": "validation-labels",
|
| 7 |
"exporter": "dynamo",
|
|
@@ -22,7 +22,7 @@
|
|
| 22 |
},
|
| 23 |
"int8": {
|
| 24 |
"file": "model-int8.onnx",
|
| 25 |
-
"agreement":
|
| 26 |
"max_abs_value_delta": 0.11746431887149811,
|
| 27 |
"max_abs_blunder_delta": 0.022291138768196106
|
| 28 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"version": 1,
|
| 3 |
"kind": "encoder",
|
| 4 |
+
"measures": "the blunder decision at p >= 0.0961714",
|
| 5 |
"positions": 1000,
|
| 6 |
"source": "validation-labels",
|
| 7 |
"exporter": "dynamo",
|
|
|
|
| 22 |
},
|
| 23 |
"int8": {
|
| 24 |
"file": "model-int8.onnx",
|
| 25 |
+
"agreement": 0.987,
|
| 26 |
"max_abs_value_delta": 0.11746431887149811,
|
| 27 |
"max_abs_blunder_delta": 0.022291138768196106
|
| 28 |
}
|