max / audiofiles
5 files changed,
+76 insertions,
-11 deletions
| @@ -186,13 +186,46 @@ | |||
| 186 | 186 | was written before the first run. At family resolution both measured families clear | |
| 187 | 187 | it (below). Accuracy is not the property that decides this, though: the layer's | |
| 188 | 188 | model is the user's own library, so the question is whether it gives the *same* | |
| 189 | - | answer as that library fills, and no metric for that exists yet. | |
| 189 | + | answer as that library fills. `layer-stability` measures that half, and at the | |
| 190 | + | weight the app actually builds the answer holds still (1.1% worst consecutive flip | |
| 191 | + | rate). It is measured over two families, so read it as a floor. | |
| 190 | 192 | 3. **The layer would inherit the global 0.85 regardless.** `include_policy` is `false` | |
| 191 | 193 | in `afcl_gen`, so the calibrated per-class thresholds are not exported and no | |
| 192 | 194 | measured number survives the trip into a shipped artifact. | |
| 193 | 195 | ||
| 194 | 196 | All three are tracked in the audiofiles GoingsOn project. | |
| 195 | 197 | ||
| 198 | + | ### What the layer is worth, and when | |
| 199 | + | ||
| 200 | + | It is a cold-start feature. That is a real thing to be, and it is narrower than | |
| 201 | + | "a classifier ships with the app" sounds. | |
| 202 | + | ||
| 203 | + | `layer-stability` compares the index the app builds (the user's own labels at 1.0 | |
| 204 | + | beside the bundled layer at 0.5) against the same user labels with no layer at all, | |
| 205 | + | on a held-out probe. Measured 2026-08-07 at `DEFAULT_K` = 5, 1049 drum one-shots, | |
| 206 | + | 178 probe samples of which 69 carry a filename no starter rule fires on. That 69 is | |
| 207 | + | the population the layer exists to serve, since the rest are already answered for | |
| 208 | + | free: | |
| 209 | + | ||
| 210 | + | | user labels | accuracy with the layer | without it | delta | | |
| 211 | + | |---|---|---|---| | |
| 212 | + | | 0 | 98.6% | nothing to answer with | cold start | | |
| 213 | + | | 18 | 98.6% | 91.3% | +7.2 | | |
| 214 | + | | 88 | 100% | 97.1% | +2.9 | | |
| 215 | + | | 352 | 100% | 98.6% | +1.4 | | |
| 216 | + | ||
| 217 | + | The layer answers a library it has never seen at 98.6%, and a user who has tagged a | |
| 218 | + | few dozen samples is getting most of what it has to give. By a few hundred labels | |
| 219 | + | their own tags carry the library and the layer is worth about a point: at 352 labels | |
| 220 | + | it changes one answer in 69. | |
| 221 | + | ||
| 222 | + | Two things not to oversell from this. The cold-start window overlaps something the | |
| 223 | + | app already does without any classifier: browse axes give a new library structure | |
| 224 | + | from zero labels and zero exemplars (`af-browse-axes`). And the measurement is two | |
| 225 | + | families on a drums-only corpus. More classes plausibly widen the window, since the | |
| 226 | + | layer's advantage over a thin user library should grow with the number of things to | |
| 227 | + | be wrong about, so this table is a result at a resolution, not a permanent property. | |
| 228 | + | ||
| 196 | 229 | ### Retracted: runs 1 and 2 answer the wrong question | |
| 197 | 230 | ||
| 198 | 231 | The two tables below grade **seven specific drum instruments**. Both halves of that |
| @@ -1087,6 +1087,24 @@ | |||
| 1087 | 1087 | .color(theme::content_muted()), | |
| 1088 | 1088 | ); | |
| 1089 | 1089 | }); | |
| 1090 | + | // The bundled layer is a head start, not an ongoing second | |
| 1091 | + | // opinion, and the measurement says so plainly: on the samples | |
| 1092 | + | // filenames do not answer it is worth +7.2 points of accuracy at | |
| 1093 | + | // 18 of the user's own labels and +1.4 at 352. Saying that here | |
| 1094 | + | // is cheaper than letting someone conclude it later and wonder | |
| 1095 | + | // what broke. Only the bundled layer gets the line; a layer | |
| 1096 | + | // someone imported is theirs to judge. | |
| 1097 | + | if layer.source.as_deref() == Some("bundled") { | |
| 1098 | + | ui.label( | |
| 1099 | + | egui::RichText::new( | |
| 1100 | + | "A starting point for a library with no tags yet. It counts for \ | |
| 1101 | + | less as you tag your own, and by a few hundred tags yours carry \ | |
| 1102 | + | the library.", | |
| 1103 | + | ) | |
| 1104 | + | .small() | |
| 1105 | + | .color(theme::content_muted()), | |
| 1106 | + | ); | |
| 1107 | + | } | |
| 1090 | 1108 | // Row B: weight + remove (with inline two-step confirm, destructive, no undo). | |
| 1091 | 1109 | ui.horizontal(|ui| { | |
| 1092 | 1110 | ui.label( |
| @@ -5,6 +5,13 @@ | |||
| 5 | 5 | //! answers are seen and decided on; nothing it proposes reaches a library until | |
| 6 | 6 | //! someone here says so. | |
| 7 | 7 | //! | |
| 8 | + | //! This screen matters most on a library with few tags, because that is when the | |
| 9 | + | //! bundled layer has something to add: measured at +7.2 points of accuracy at 18 | |
| 10 | + | //! of the user's own labels and +1.4 at 352, on the samples filenames do not | |
| 11 | + | //! answer (`docs/ml_classifier.md`, "What the layer is worth, and when"). It does | |
| 12 | + | //! not stop being useful, since the user's own labels feed the same k-NN and the | |
| 13 | + | //! queue keeps working off their tagging, but the bundled layer's share of it fades. | |
| 14 | + | //! | |
| 8 | 15 | //! # Grouped by tag, and that is the whole design | |
| 9 | 16 | //! | |
| 10 | 17 | //! "Are these 340 all kicks?" is a question one scan of a list answers. The same |
| @@ -20,9 +20,9 @@ | |||
| 20 | 20 | ||
| 21 | 21 | /// Default neighbor count. | |
| 22 | 22 | /// | |
| 23 | - | /// 5, not the 15 shipped until 2026-08-07. Selected under an outer fold — each fold | |
| 24 | - | /// picks k by inner CV over its own training corpus, then scores the fold it never | |
| 25 | - | /// saw — and all five folds chose 5. Worth 2.6 points of macro top-1 at family | |
| 23 | + | /// 5, not the 15 shipped until 2026-08-07. Selected under an outer fold, where each | |
| 24 | + | /// fold picks k by inner CV over its own training corpus and then scores the fold it | |
| 25 | + | /// never saw; all five folds chose 5. Worth 2.6 points of macro top-1 at family | |
| 26 | 26 | /// resolution, and the queue-stability measurement that decides the ship question | |
| 27 | 27 | /// (`layer-stability`, deployment weight) is unchanged at 1.1%. The measurements it | |
| 28 | 28 | /// does worsen (index composition, index growth, accepted-tag feedback) were over |
| @@ -3,13 +3,13 @@ | |||
| 3 | 3 | //! **Behind the `bundled-layer` feature, which is off.** Nothing here is compiled | |
| 4 | 4 | //! into a default build. The layer as generated covers drums and nothing else | |
| 5 | 5 | //! (clap, cymbal, hihat, kick, snare, tom, percussion), so it would answer for a | |
| 6 | - | //! bass or a vocal with the nearest drum it knows, and there is as yet no harness | |
| 7 | - | //! measuring whether the 35-d feature vector separates instrument families at | |
| 8 | - | //! all. A default classifier that is confidently wrong on a user's library is | |
| 9 | - | //! worse than no default: it puts wrong tags on their samples with the app's | |
| 10 | - | //! authority behind it. The machinery below is complete and tested; what it | |
| 11 | - | //! waits on is a broader corpus and a measurement that justifies shipping. | |
| 12 | - | //! Tracked in the audiofiles GoingsOn project. | |
| 6 | + | //! bass or a vocal with the nearest drum it knows. A default classifier that is | |
| 7 | + | //! confidently wrong on a user's library is worse than no default: it puts wrong | |
| 8 | + | //! tags on their samples with the app's authority behind it. The machinery below | |
| 9 | + | //! is complete and tested, and both halves of the measurement now exist | |
| 10 | + | //! (`layer-eval` for whether the answer is right, `layer-stability` for whether | |
| 11 | + | //! it is the same answer next month); what it waits on is a corpus that reaches | |
| 12 | + | //! past drums. Tracked in the audiofiles GoingsOn project. | |
| 13 | 13 | //! | |
| 14 | 14 | //! A fresh vault has no labelled exemplars, so k-NN has nothing to answer with | |
| 15 | 15 | //! until the user has tagged enough of their own library to seed it. The bundled | |
| @@ -17,6 +17,13 @@ | |||
| 17 | 17 | //! labelled corpus, carried as feature vectors and tags with no audio and no | |
| 18 | 18 | //! hashes, which is the whole reason a `.afcl` can ship at all. | |
| 19 | 19 | //! | |
| 20 | + | //! Day one is close to the whole of it, and the copy elsewhere should say so. | |
| 21 | + | //! Measured 2026-08-07: on the samples no filename rule answers, the layer is | |
| 22 | + | //! worth +7.2 points of accuracy at 18 user labels, +2.9 at 88, and +1.4 at 352, | |
| 23 | + | //! where it changes one answer in 69. It answers a library it has never seen at | |
| 24 | + | //! 98.6%. Full table in `docs/ml_classifier.md`, "What the layer is worth, and | |
| 25 | + | //! when"; two families only, so more classes could widen the window. | |
| 26 | + | //! | |
| 20 | 27 | //! Generated by `cargo run --release -p audiofiles-bench -- afcl` and checked in | |
| 21 | 28 | //! at `assets/official.afcl` beside this crate. It is a build artifact, not a | |
| 22 | 29 | //! fixture: regenerate it deliberately. |