From e65f8902e0026fcf5e03b6907a0601019786dd6e Mon Sep 17 00:00:00 2001 From: calvin-archastro Date: Fri, 2 Oct 2026 10:56:18 -0700 Subject: [PATCH 1/2] Add AUC explainer film and size score decode buffer by duration videos/auc-ml: an intuition-first narrated explainer of AUC (11:48). - Title card after the cold-open hook; end card with the takeaway. - Diagrams build on the sentence that explains them: one natural Kokoro take per scene, sentence onsets measured from the take's own pauses (prepare-timing.mjs, sentence-bounds.mjs), exposed as lineStarts(). - Original quiet score (src/score.ts) ducked under speech, varied per lesson part, resolving A -> D onto the end card. - Fonts with full Latin coverage (Schibsted Grotesk, Fragment Mono, Instrument Serif); the shared showcase Space Grotesk subset lacks A-Z. - verify_math.py checks every on-screen number with exact fractions. packages/cli: the reverb decode buffer was capped at 256 MB, so any score longer than about 11 minutes failed with "Cannot decode for room processing". Size it from the score duration. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/cli/src/music-render.ts | 3 +- pnpm-lock.yaml | 25 + videos/auc-ml/README.md | 56 ++ videos/auc-ml/SOURCES.md | 37 + videos/auc-ml/clapper-voices.lock.json | 16 + videos/auc-ml/clapper.json | 7 + videos/auc-ml/finalize.mjs | 47 + videos/auc-ml/package.json | 22 + videos/auc-ml/prepare-timing.mjs | 101 ++ videos/auc-ml/sentence-bounds.mjs | 39 + videos/auc-ml/src/beats.json | 164 ++++ videos/auc-ml/src/bookends.tsx | 219 +++++ videos/auc-ml/src/contexts.tsx | 768 +++++++++++++++ videos/auc-ml/src/data.ts | 40 + ...Cr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2 | Bin 0 -> 7260 bytes ...Cr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2 | Bin 0 -> 900 bytes .../4iCr6K5wfMRRjxp0DA6-2CLnB4NHhqcL71Q.woff2 | Bin 0 -> 15176 bytes ...5SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2 | Bin 0 -> 46864 bytes ...PQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2 | Bin 0 -> 20844 bytes videos/auc-ml/src/fonts/fonts.css | 149 +++ ...BRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2 | Bin 0 -> 15040 bytes ...tNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2 | Bin 0 -> 7828 bytes ...s2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2 | Bin 0 -> 8444 bytes ...tNs2ka5fXjeivQ4LroWlx-6zAjjH7Motmp5g.woff2 | Bin 0 -> 15684 bytes videos/auc-ml/src/index.tsx | 131 +++ videos/auc-ml/src/narration.ts | 22 + videos/auc-ml/src/plan.ts | 23 + videos/auc-ml/src/probe.json | 165 ++++ videos/auc-ml/src/roc.tsx | 916 ++++++++++++++++++ videos/auc-ml/src/score.ts | 199 ++++ videos/auc-ml/src/theme.css | 133 +++ videos/auc-ml/src/timing.json | 720 ++++++++++++++ videos/auc-ml/src/visuals.tsx | 459 +++++++++ videos/auc-ml/transcript.md | 87 ++ videos/auc-ml/tsconfig.json | 1 + videos/auc-ml/verify_math.py | 37 + 36 files changed, 4585 insertions(+), 1 deletion(-) create mode 100644 videos/auc-ml/README.md create mode 100644 videos/auc-ml/SOURCES.md create mode 100644 videos/auc-ml/clapper-voices.lock.json create mode 100644 videos/auc-ml/clapper.json create mode 100644 videos/auc-ml/finalize.mjs create mode 100644 videos/auc-ml/package.json create mode 100644 videos/auc-ml/prepare-timing.mjs create mode 100644 videos/auc-ml/sentence-bounds.mjs create mode 100644 videos/auc-ml/src/beats.json create mode 100644 videos/auc-ml/src/bookends.tsx create mode 100644 videos/auc-ml/src/contexts.tsx create mode 100644 videos/auc-ml/src/data.ts create mode 100644 videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2 create mode 100644 videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2 create mode 100644 videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB4NHhqcL71Q.woff2 create mode 100644 videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2 create mode 100644 videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2 create mode 100644 videos/auc-ml/src/fonts/fonts.css create mode 100644 videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2 create mode 100644 videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2 create mode 100644 videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2 create mode 100644 videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjjH7Motmp5g.woff2 create mode 100644 videos/auc-ml/src/index.tsx create mode 100644 videos/auc-ml/src/narration.ts create mode 100644 videos/auc-ml/src/plan.ts create mode 100644 videos/auc-ml/src/probe.json create mode 100644 videos/auc-ml/src/roc.tsx create mode 100644 videos/auc-ml/src/score.ts create mode 100644 videos/auc-ml/src/theme.css create mode 100644 videos/auc-ml/src/timing.json create mode 100644 videos/auc-ml/src/visuals.tsx create mode 100644 videos/auc-ml/transcript.md create mode 100644 videos/auc-ml/tsconfig.json create mode 100644 videos/auc-ml/verify_math.py diff --git a/packages/cli/src/music-render.ts b/packages/cli/src/music-render.ts index ffccea7..d16faf1 100644 --- a/packages/cli/src/music-render.ts +++ b/packages/cli/src/music-render.ts @@ -182,7 +182,8 @@ export async function renderScore( const decoded = spawnSync( resolveFfmpeg(), ["-v", "error", "-i", stem, "-f", "f32le", "-ar", "48000", "-ac", "2", "pipe:1"], - { maxBuffer: 256 * 1024 * 1024 }, + // f32 stereo at 48 kHz, plus headroom: long scores exceed a fixed cap. + { maxBuffer: Math.max(256 * 1024 * 1024, Math.ceil((compiled.durationSeconds + 2) * 48000 * 8)) }, ); if (decoded.status !== 0) throw new Error(`Cannot decode ${t.id} for room processing`); const n = decoded.stdout.length / 8, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 280ed74..64320fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,31 @@ importers: specifier: ^19.0.0 version: 19.2.5(@types/react@19.2.18) + videos/auc-ml: + dependencies: + '@archastro/clapper': + specifier: workspace:* + version: link:../../packages/cli + '@archastro/clapper-core': + specifier: workspace:* + version: link:../../packages/core + '@archastro/clapper-music': + specifier: workspace:* + version: link:../../packages/music + react: + specifier: ^19.2.0 + version: 19.2.8 + react-dom: + specifier: ^19.2.0 + version: 19.2.8(react@19.2.8) + devDependencies: + '@types/react': + specifier: ^19.0.0 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.5(@types/react@19.2.18) + videos/cat-ballet: dependencies: '@archastro/clapper': diff --git a/videos/auc-ml/README.md b/videos/auc-ml/README.md new file mode 100644 index 0000000..3e52b73 --- /dev/null +++ b/videos/auc-ml/README.md @@ -0,0 +1,56 @@ +# AUC in machine learning + +An intuition-first narrated explanation, 12:23.5 at 1920×1080 / 30 fps. One synthetic +six-payment example connects thresholds, ROC geometry, pairwise ranking and average +precision, then returns for the closing comparison (7/9 vs 29/36). A short survey covers +partial and multiclass AUC and other curves that share the letters. + +- Composition: `auc-ml`; entry: `src/index.tsx`. Title card after the cold-open hook; end card closes. +- Narration: Kokoro `af_heart`, speed 1, locked in `clapper-voices.lock.json`. One cue **per sentence**, + placed from measured takes, so each diagram step lands on the sentence that explains it + (`lineStarts()` in `src/plan.ts`). +- Score: `src/score.ts`, a quiet 60 BPM piano/viola/cello bed with harp glints at part changes. + It dips under speech by automation derived from the same timing. +- Type: Schibsted Grotesk / Fragment Mono / Instrument Serif (`src/fonts`, full Latin coverage). +- [Research and conventions](SOURCES.md) · [full transcript](transcript.md). +- `python3 videos/auc-ml/verify_math.py` checks every on-screen number with exact fractions. + +## Retiming after a script edit + +```fish +node prepare-timing.mjs probe # src/probe.json, one cue per sentence +pnpm exec clapper narration render src/probe.json -o out/probe-lines +node prepare-timing.mjs # src/timing.json, chapters, transcript +``` + +Unchanged sentences reuse cached takes. Scenes, narration and score all read `src/timing.json`. + +- Preview: `pnpm --dir videos/auc-ml preview`. +- Render: `pnpm --dir videos/auc-ml render` (adds native MP4 chapter metadata → `out/auc-ml.mp4`). + +## Chapters + +| Start | Chapter | +| --- | --- | +| 00:00 | Who should be reviewed first? | +| 00:34 | Scores → thresholds → curve → area | +| 01:05 | A score gives an ordering | +| 01:36 | One threshold gives one decision | +| 02:06 | Two rates, two different denominators | +| 02:38 | Lower the threshold. Trace the tradeoff. | +| 03:05 | Width × height, added across the curve | +| 03:37 | Area is also a ranking game | +| 04:06 | Each strip is one column of comparisons | +| 04:36 | The probability behind ROC-AUC | +| 05:08 | Equal scores mean no ranking preference | +| 05:40 | 1 is perfect. 0.5 is the chance reference. | +| 06:10 | Same ordering. Same AUC. Different numbers. | +| 06:40 | A small false-positive rate can mean many alarms | +| 07:21 | Precision asks about the flagged pile | +| 07:58 | Reward precision when a new positive is found | +| 08:30 | PR-AUC and AP are not interchangeable labels | +| 09:09 | Partial AUC: only the region you operate in | +| 09:40 | Multiclass AUC needs a decomposition and an average | +| 10:26 | Same letters, different curves | +| 11:14 | Keep the curve, the data, and the decision separate | +| 11:43 | Ask five questions when someone says “AUC” | diff --git a/videos/auc-ml/SOURCES.md b/videos/auc-ml/SOURCES.md new file mode 100644 index 0000000..49469e4 --- /dev/null +++ b/videos/auc-ml/SOURCES.md @@ -0,0 +1,37 @@ +# AUC in machine learning — source and scope + +A narrated, intuition-first explanation for a software engineer comfortable with +basic algebra. Core ROC and AP derivations use one original six-payment example. +Other ML contexts are explicitly new examples or a map of variants, not claims +that every use of “AUC” shares one definition. No pharmacokinetic content. + +Primary/official references checked 2026-09-15: + +1. [Fawcett, An introduction to ROC analysis (2006)](https://www.math.ucdavis.edu/~saito/data/roc/fawcett-roc.pdf): threshold sweeps, ROC geometry, ranking interpretation, chance reference and deployment cautions. +2. [scikit-learn ROC-AUC API](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_auc_score.html): score inputs, OvR/OvO, macro/weighted aggregation and standardized partial AUC. +3. [scikit-learn average precision API](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.average_precision_score.html): recall-weighted non-interpolated AP and distinction from linear trapezoidal PR area. +4. [scikit-learn metric guide](https://scikit-learn.org/stable/modules/model_evaluation.html): ROC/PR definitions, averaging and score-based evaluation. +5. [Davis & Goadrich, The Relationship Between Precision-Recall and ROC Curves (2006)](https://research.cs.wisc.edu/techreports/2006/TR1551.pdf): relationships and interpolation caveats. The original worked numbers below are independently calculated. +6. [COCO official evaluator](https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py): detection matching, interpolated precision, 101 recall thresholds and IoU thresholds .50:.05:.95. +7. [scikit-survival cumulative/dynamic AUC](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.metrics.cumulative_dynamic_auc.html): event-by-horizon cases, event-free controls, censoring-aware weights and time aggregation. Use the standard FPR/TPR ROC axes from ref1; the API page's background sentence about specificity is not our axis definition. +8. [scikit-learn ROC cross-validation example](https://scikit-learn.org/stable/auto_examples/model_selection/plot_roc_crossval.html): out-of-sample evaluation and variation across splits. + +9. [TensorFlow AUC metric](https://www.tensorflow.org/api_docs/python/tf/keras/metrics/AUC): fixed-threshold discretization can approximate ROC/PR area; exact rank invariance does not promise invariant coarse-grid estimates. + +## Original numerical examples + +- Sorted scores: A+ .9, B− .8, C+ .7, D+ .6, E− .4, F− .1. +- At cutoff .65: TP2, FP1, FN1, TN2; TPR2/3, FPR1/3, precision2/3. +- ROC points: (0,0),(0,1/3),(1/3,1/3),(1/3,2/3),(1/3,1),(2/3,1),(1,1). +- ROC area: 7/9; positive-negative wins: 7/9, no ties. +- Non-interpolated AP: (1 + 2/3 + 3/4)/3 = 29/36. +- Linear trapezoidal area of raw PR points, with endpoint (recall0,precision1): 55/72. +- Partial raw ROC area over FPR[0,.1]: 1/30; maximum raw area .1. McClish-standardized (scikit-learn `max_fpr=0.1`): ½(1+(1/30−.005)/(.1−.005)) ≈ .649, hand-computed. +- Prevalence example: fixed TPR.8/FPR.1 gives precision80/90 for100P/100N, but8/107 for10P/990N. Invariance is conditional on keeping class-conditional scoring behavior fixed. +- Interpolated precision envelope (max precision at recall ≥ r): 1 to recall 1/3, then 3/4. +- Retrieval callback: relevant results at ranks 1, 3, 4 give the same AP, 29/36. +- Separate multiclass illustration (labelled as three fraud types): per-class areas .95,.80,.60, supports80,15,5. Macro47/60≈.78333; support-weighted.91. +- Detection illustration: equal100×100 boxes offset20 horizontally have IoU2/3. Matching passes a.5 IoU cutoff and fails.75. + +`verify_math.py` checks these independently with exact rational arithmetic. +The six-item sample teaches mechanics; it cannot establish real model quality. diff --git a/videos/auc-ml/clapper-voices.lock.json b/videos/auc-ml/clapper-voices.lock.json new file mode 100644 index 0000000..cad01eb --- /dev/null +++ b/videos/auc-ml/clapper-voices.lock.json @@ -0,0 +1,16 @@ +{ + "schema": 1, + "engine": { + "model": "kokoro-82m-v1-q8", + "revision": "1939ad2a8e416c0acfeecc08a694d14ef25f2231", + "runtime": "478f7432db253cbd8e530daca9641885f62e7c529a6a9e1ab528564aee39bc0b", + "renderer": 1 + }, + "narrators": { + "guide": { + "voice": "af_heart", + "speed": 1, + "embedding": "d583ccff3cdca2f7fae535cb998ac07e9fcb90f09737b9a41fa2734ec44a8f0b" + } + } +} diff --git a/videos/auc-ml/clapper.json b/videos/auc-ml/clapper.json new file mode 100644 index 0000000..3223109 --- /dev/null +++ b/videos/auc-ml/clapper.json @@ -0,0 +1,7 @@ +{ + "runtime": "0.4.0", + "entry": "src/index.tsx", + "composition": "auc-ml", + "narration": "src/narration.ts", + "score": "src/score.ts" +} diff --git a/videos/auc-ml/finalize.mjs b/videos/auc-ml/finalize.mjs new file mode 100644 index 0000000..c36a95d --- /dev/null +++ b/videos/auc-ml/finalize.mjs @@ -0,0 +1,47 @@ +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { resolveFfmpeg } from "@archastro/clapper"; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const chapters = JSON.parse(fs.readFileSync(path.join(root, "out/chapters.json"))); +const escape = (s) => s.replace(/[\\=;#\n]/g, (c) => "\\" + c); +const lines = [";FFMETADATA1", "title=AUC in machine learning — intuition and mathematical rigor"]; +for (const c of chapters) + lines.push( + "[CHAPTER]", + "TIMEBASE=1/1000", + `START=${Math.round(c.startSeconds * 1000)}`, + `END=${Math.round((c.startSeconds + c.durationSeconds) * 1000)}`, + `title=${escape(c.title)}`, + ); +const metadata = path.join(root, "out/chapters.ffmeta"); +fs.writeFileSync(metadata, lines.join("\n") + "\n"); +const r = spawnSync( + resolveFfmpeg(), + [ + "-hide_banner", + "-loglevel", + "error", + "-i", + path.join(root, "out/render.mp4"), + "-i", + metadata, + "-map", + "0", + "-map_metadata", + "1", + "-map_chapters", + "1", + "-c", + "copy", + "-movflags", + "+faststart", + "-y", + path.join(root, "out/auc-ml.mp4"), + ], + { stdio: "inherit" }, +); +if (r.status !== 0) throw Error("Chapter mux failed"); +console.log("Chaptered MP4: out/auc-ml.mp4"); diff --git a/videos/auc-ml/package.json b/videos/auc-ml/package.json new file mode 100644 index 0000000..5c016f0 --- /dev/null +++ b/videos/auc-ml/package.json @@ -0,0 +1,22 @@ +{ + "name": "auc-ml", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "preview": "clapper preview", + "render": "clapper render -c auc-ml --crf 20 -o out/render.mp4 && node finalize.mjs", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@archastro/clapper-core": "workspace:*", + "@archastro/clapper": "workspace:*", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "@archastro/clapper-music": "workspace:*" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0" + } +} diff --git a/videos/auc-ml/prepare-timing.mjs b/videos/auc-ml/prepare-timing.mjs new file mode 100644 index 0000000..93d1e2c --- /dev/null +++ b/videos/auc-ml/prepare-timing.mjs @@ -0,0 +1,101 @@ +// Narration timing from natural paragraph takes. +// node prepare-timing.mjs probe → src/probe.json (one cue per scene, generous windows) +// clapper narration render src/probe.json -o out/probe-para +// node prepare-timing.mjs → src/timing.json, out/chapters.json, transcript.md +// Each scene is one take (Kokoro's own sentence rhythm). Sentence onsets inside the take +// come from its pauses (sentence-bounds.mjs), so scenes can animate on the sentence that +// explains each step (lineStarts in src/plan.ts). +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { sentenceOnsets } from "./sentence-bounds.mjs"; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const beats = JSON.parse(fs.readFileSync(path.join(root, "src/beats.json"))); +export const sentences = (text) => text.split(/(?<=[.?!])\s+(?=[A-Z“"(])/).filter(Boolean); + +const LEAD = 0.6; // scene start → take start (the take carries ~0.3 s of its own lead-in) +const HOLD = { default: 1.8, math: 3.0 }; // take end → cut +const math = new Set([ + "area", + "pairs", + "bridge", + "rigor", + "ties", + "imbalance", + "ap", + "conventions", + "partial", + "multiclass", +]); + +if (process.argv[2] === "probe") { + let at = 0; + const cues = []; + for (const b of beats) { + if (!b.text) continue; + const window = Math.max(40, Math.ceil(b.text.split(/\s+/).length / 1.5) + 10); + cues.push({ id: b.id, narrator: "guide", text: b.text, at, duration: window }); + at += window; + } + const probe = { + title: "AUC in machine learning — paragraph probe", + narrators: { guide: { voice: "af_heart", speed: 1 } }, + cues, + }; + fs.writeFileSync(path.join(root, "src/probe.json"), JSON.stringify(probe, null, 2) + "\n"); + console.log(`${cues.length} paragraph cues → src/probe.json`); + process.exit(0); +} + +const measured = JSON.parse(fs.readFileSync(path.join(root, "out/probe-para/manifest.json"))); +const takes = new Map(); +const takeDir = path.join(root, ".clapper/narration/takes"); +for (const f of fs.readdirSync(takeDir).filter((f) => f.endsWith(".json"))) { + const t = JSON.parse(fs.readFileSync(path.join(takeDir, f))); + takes.set(t.sha256, t); +} +const timing = {}; +const chapters = []; +let at = 0; +for (const b of beats) { + let seconds = b.seconds, + take, + lines = []; + if (b.text) { + const c = measured.cues.find((c) => c.id === b.id); + assert.ok(c, `No measured take for ${b.id}`); + assert.equal(c.text, b.text, `Stale measured text for ${b.id}`); + const file = takes.get(c.sha256)?.file; + assert.ok(file, `Take audio for ${b.id} not in the narration cache`); + const parts = sentences(b.text); + const r = sentenceOnsets(file, parts.length); + lines = r.onsets.map((o, k) => { + const end = k + 1 < r.onsets.length ? r.onsets[k + 1] : r.voiceEnd; + // Plausibility: speech runs ~5–12 cs per character; a misplaced boundary breaks that. + const rate = ((end - o) / parts[k].length) * 100; + assert.ok( + rate > 4 && rate < 13, + `${b.id} sentence ${k}: implausible boundary (${rate.toFixed(1)} cs/char)`, + ); + return { at: Math.round((LEAD + o) * 100) / 100, dur: Math.round((end - o) * 100) / 100 }; + }); + take = { at: LEAD, dur: c.durationSeconds }; + seconds = Math.ceil((LEAD + c.durationSeconds + (math.has(b.id) ? HOLD.math : HOLD.default)) * 2) / 2; + } + timing[b.id] = { seconds, take, lines }; + if (b.title) chapters.push({ id: b.id, title: b.title, startSeconds: at, durationSeconds: seconds }); + at += seconds; +} +fs.writeFileSync(path.join(root, "src/timing.json"), JSON.stringify(timing, null, 2) + "\n"); +fs.mkdirSync(path.join(root, "out"), { recursive: true }); +fs.writeFileSync(path.join(root, "out/chapters.json"), JSON.stringify(chapters, null, 2) + "\n"); +const stamp = (s) => `${String(Math.floor(s / 60)).padStart(2, "0")}:${(s % 60).toFixed(1).padStart(4, "0")}`; +fs.writeFileSync( + path.join(root, "transcript.md"), + chapters + .map((c) => `## ${stamp(c.startSeconds)} — ${c.title}\n\n${beats.find((x) => x.id === c.id).text}\n`) + .join("\n"), +); +console.log(`Film: ${at}s (${(at / 60).toFixed(2)} min), ${chapters.length} chapters.`); diff --git a/videos/auc-ml/sentence-bounds.mjs b/videos/auc-ml/sentence-bounds.mjs new file mode 100644 index 0000000..7f47ae2 --- /dev/null +++ b/videos/auc-ml/sentence-bounds.mjs @@ -0,0 +1,39 @@ +// Sentence onsets inside one paragraph take: the N−1 longest pauses separate N sentences. +import fs from "node:fs"; +export function sentenceOnsets(file, n, rate = 24000) { + const buf = fs.readFileSync(file); + const x = new Float32Array(buf.buffer, buf.byteOffset, buf.length / 4); + const hop = rate / 100; // 10 ms frames + const env = []; + for (let i = 0; i + hop <= x.length; i += hop) { + let s = 0; + for (let j = i; j < i + hop; j++) s += x[j] * x[j]; + env.push(10 * Math.log10(s / hop + 1e-12)); + } + const peak = Math.max(...env), + floor = peak - 38; + const voiced = env.map((v) => v > floor); + const first = voiced.indexOf(true), + last = voiced.lastIndexOf(true); + const gaps = []; + for (let i = first, start = -1; i <= last; i++) { + if (!voiced[i] && start < 0) start = i; + if (voiced[i] && start >= 0) { + gaps.push({ start: start / 100, end: i / 100, len: (i - start) / 100 }); + start = -1; + } + } + const chosen = [...gaps] + .sort((a, b) => b.len - a.len) + .slice(0, n - 1) + .sort((a, b) => a.start - b.start); + if (chosen.length !== n - 1) + throw new Error(`${file}: found ${chosen.length + 1} sentences, expected ${n}`); + return { + onsets: [first / 100, ...chosen.map((g) => g.end)], + voiceEnd: last / 100, + pauses: chosen.map((g) => g.len), + weakest: chosen.length ? Math.min(...chosen.map((g) => g.len)) : 0, + nextGap: gaps.length >= n ? ([...gaps].sort((a, b) => b.len - a.len)[n - 1]?.len ?? 0) : 0, + }; +} diff --git a/videos/auc-ml/src/beats.json b/videos/auc-ml/src/beats.json new file mode 100644 index 0000000..b04bc6a --- /dev/null +++ b/videos/auc-ml/src/beats.json @@ -0,0 +1,164 @@ +[ + { + "id": "intro", + "chapter": "01 / THE IDEA", + "title": "Who should be reviewed first?", + "sub": "Six payments. One fraud model. A small review budget.", + "text": "A fraud model scores every payment. You can only review a few today, so the highest scores go first. But how do you know that ordering is any good, before you pick a cutoff? One number answers that: the area under a curve, or A U C. You'll compute it by hand from these six payments, then see why the same three letters can mean very different things." + }, + { + "id": "title", + "seconds": 7 + }, + { + "id": "map", + "chapter": "01 / THE WHOLE JOURNEY", + "title": "Scores → thresholds → curve → area", + "sub": "One complete picture before we open the pieces.", + "text": "Here is the whole journey. On held-out examples, the model produces scores. A threshold turns each score into a yes or no decision. Move the threshold, and each setting gives one point. Plot those points as a curve, then summarize the curve with its area. We'll use the same six payments for two kinds of curve, so you can see exactly what changes." + }, + { + "id": "scores", + "chapter": "02 / BUILD ROC", + "title": "A score gives an ordering", + "sub": "Synthetic held-out example: three frauds (+), three legitimate payments (−).", + "text": "Here are the six payments, sorted from highest score to lowest. Plus means actual fraud; minus means legitimate. The scores only need to put payments in order; they need not be calibrated probabilities. Notice one mistake: payment B is legitimate, yet it ranks above two frauds, C and D. Keep B in mind. Its mistake will become a missing piece of area." + }, + { + "id": "threshold", + "chapter": "02 / BUILD ROC", + "title": "One threshold gives one decision", + "sub": "Flag a payment when its score is at least 0.65.", + "text": "Set the threshold at zero point six five. We flag A, B, and C. A and C are frauds we caught: true positives. B is a legitimate payment we flagged: a false positive. D is a fraud we missed: a false negative. And E and F are correctly left alone: true negatives. One threshold gives one confusion matrix, not yet a curve." + }, + { + "id": "rates", + "chapter": "02 / BUILD ROC", + "title": "Two rates, two different denominators", + "sub": "Each rate divides by a different group of payments.", + "text": "The true positive rate asks: of all the actual frauds, what fraction did we catch? Two out of three. The false positive rate asks: of all the legitimate payments, what fraction did we wrongly flag? One out of three. An R O C curve, short for receiver operating characteristic, plots false positive rate across and true positive rate up. Our threshold becomes this single point." + }, + { + "id": "sweep", + "chapter": "02 / BUILD ROC", + "title": "Lower the threshold. Trace the tradeoff.", + "sub": "The threshold is a control knob, not an axis on this plot.", + "text": "Start above the highest score: nothing is flagged, so both rates are zero. Now lower the threshold past one payment at a time. Each fraud steps the curve up by a third; each legitimate payment steps it right by a third. At the bottom, everything is flagged, and we reach one, one. That staircase is the R O C curve." + }, + { + "id": "area", + "chapter": "02 / THE AREA", + "title": "Width × height, added across the curve", + "sub": "AUC = the area under this ROC curve, inside the unit square.", + "text": "Now shade the area under the curve. Each legitimate payment adds a vertical strip, one third wide. The heights are one third, one, and one. So the area is one ninth plus one third plus one third: seven ninths, about zero point seven eight. The empty corner above the first strip is payment B's mistake. And A U C is this area, not the accuracy at any one threshold." + }, + { + "id": "pairs", + "chapter": "03 / THE DEEPER INTUITION", + "title": "Area is also a ranking game", + "sub": "Pair every fraud with every legitimate payment.", + "text": "There's a second way to get the same answer. Pair every fraud with every legitimate payment, and give a point whenever the fraud scores higher. Three frauds times three legitimate payments makes nine pairs. A beats all three. C and D each beat two, and both lose to B. Seven wins out of nine: exactly seven ninths again." + }, + { + "id": "bridge", + "chapter": "03 / WHY THE TWO VIEWS AGREE", + "title": "Each strip is one column of comparisons", + "sub": "A strip's height = the fraction of frauds that outrank that legitimate payment.", + "text": "Why do they agree? Take legitimate payment B, scored zero point eight. Only one of the three frauds outranks it, so its strip is one third tall. Legitimate payments E and F are outranked by all three frauds, so their strips are full height. Averaging the strip heights counts exactly the winning pairs. That's the bridge from area to probability." + }, + { + "id": "rigor", + "chapter": "03 / MATHEMATICAL STATEMENT", + "title": "The probability behind ROC-AUC", + "sub": "Assume larger scores mean more likely to be fraud.", + "text": "Here is the exact statement. Pick one random fraud and one random legitimate payment. R O C area is the probability that the fraud scores higher, with ties counting half. On a finite sample, that is just the fraction of winning pairs, like our seven out of nine. And because the horizontal axis is false positive rate, that is what the area integrates over, not the threshold." + }, + { + "id": "ties", + "chapter": "03 / TIED SCORES", + "title": "Equal scores mean no ranking preference", + "sub": "Separate tiny example: one fraud and one legitimate payment, both scored 0.50.", + "text": "Ties need a rule. Take a separate tiny example: one fraud and one legitimate payment, both scored zero point five. Any threshold flags both or neither, so the R O C curve jumps straight from zero, zero to one, one. That diagonal has area one half, the same as giving the tied pair half credit. So a model that gives everything the same score has an A U C of one half." + }, + { + "id": "baselines", + "chapter": "03 / READ THE NUMBER", + "title": "1 is perfect. 0.5 is the chance reference.", + "sub": "Chance is an expectation, not a guarantee for every small random sample.", + "text": "A perfect ranking climbs to the top before moving right, and fills the whole square. Random ordering has an expected area of one half. A completely reversed ranking scores zero. Below one half can signal a flipped score direction, but don't flip a model just because of your test set. And an A U C of zero point eight does not mean eighty percent of predictions are correct." + }, + { + "id": "calibration", + "chapter": "03 / WHAT AUC LEAVES OUT", + "title": "Same ordering. Same AUC. Different numbers.", + "sub": "AUC measures ranking, not probability calibration.", + "text": "Now square every score. The numbers change, but the order doesn't, so the R O C curve and its area stay at seven ninths. A U C can't tell you which set of numbers makes better probabilities. A fixed cutoff has to move too: zero point six five becomes its square, about zero point four two. Check calibration separately, and pick the threshold from the costs of mistakes." + }, + { + "id": "imbalance", + "chapter": "04 / WHY PRECISION–RECALL EXISTS", + "title": "A small false-positive rate can mean many alarms", + "sub": "Same detector, two populations.", + "text": "Suppose a threshold catches eighty percent of frauds and flags ten percent of legitimate payments. With a hundred of each, that's eighty true alarms and ten false ones: about eighty nine percent precision. Now take ten frauds among nine hundred ninety legitimate payments. The same rates give eight true alarms and ninety nine false ones: only seven and a half percent precision. The R O C point never moved: eighty percent, ten percent. Precision did, because prevalence, the Greek letter pi, changes what the flagged pile is made of." + }, + { + "id": "pr", + "chapter": "04 / THE SECOND CURVE", + "title": "Precision asks about the flagged pile", + "sub": "Recall = TP / actual frauds. Precision = TP / everything flagged.", + "text": "Back to our six payments. Recall is just another name for the true positive rate. Precision asks a different question: of the payments we flagged, how many are really fraud? A precision recall curve plots recall across and precision up, as we sweep the same threshold. Each fraud we find raises recall; each legitimate payment we flag drags precision down. Before anything is flagged, precision is undefined, so plots usually start the curve at one." + }, + { + "id": "ap", + "chapter": "04 / AVERAGE PRECISION, WORKED", + "title": "Reward precision when a new positive is found", + "sub": "Frauds appear at ranks 1, 3 and 4.", + "text": "A common summary is average precision, or A P. Each new fraud raises recall by one third. At those moments, precision is one, then two thirds, then three quarters. Weight each by its recall step and add them up. Twenty-nine over thirty-six, about zero point eight one. Same predictions as our R O C area of seven ninths, but a different question, and a different number." + }, + { + "id": "conventions", + "chapter": "04 / NAMES THAT HIDE CHOICES", + "title": "PR-AUC and AP are not interchangeable labels", + "sub": "Same points, different conventions.", + "text": "Average precision sums recall steps times precision: zero point eight oh six. Join the raw precision recall points with straight lines and take trapezoids, and you get zero point seven six four. Those straight lines aren't even a valid interpolation in precision recall space. Other benchmarks use an interpolated envelope: at each recall, the best precision at that recall or higher. Here it lifts the middle step from two thirds to three quarters. And on large samples, a random ranker's baseline precision is the prevalence: one half here, only because half our payments are fraud. Always name the convention." + }, + { + "id": "partial", + "chapter": "05 / BEYOND THE FULL CURVE", + "title": "Partial AUC: only the region you operate in", + "sub": "Full ROC-AUC can hide differences at very low false-positive rates.", + "text": "Say you can tolerate almost no false alarms. Partial A U C keeps only false positive rates up to zero point one. Here the raw area is one thirtieth, out of a possible one tenth. Scikit-learn's max F P R setting rescales that so one half is chance and one is perfect: about zero point six five. Always state the range, and whether it was rescaled." + }, + { + "id": "multiclass", + "chapter": "05 / MANY CLASSES", + "title": "Multiclass AUC needs a decomposition and an average", + "sub": "Illustrative: three fraud types with 80, 15 and 5 examples.", + "text": "Now suppose a second model sorts confirmed frauds into three types. One versus rest turns each class into its own binary problem, with its own R O C area; one versus one compares pairs of classes instead. The macro average weights the classes equally: about zero point seven eight. Weighting by class size gives zero point nine one, because the biggest class is the easiest. Same model, two honest numbers with different priorities. And when an example can carry several labels, a micro average pools every example label pair into one curve; that is not an average of areas." + }, + { + "id": "letters", + "chapter": "05 / OTHER CURVES CALLED AUC", + "title": "Same letters, different curves", + "sub": "Before trusting the number, read the axes and the matching rule.", + "text": "Outside classification, the same letters show up on different curves. In search, average precision rewards relevant results near the top, and mean average precision averages it over queries. With three relevant results, at ranks one, three, and four, it's our twenty-nine over thirty-six again. Object detection first matches each predicted box to a true one by their overlap, called I O U. These two boxes overlap by two thirds: a match at one half, a miss at three quarters. Only then does it build precision recall curves, so Coco's A P is not an R O C area. Survival models compute an A U C at each time horizon t. A machine that failed by t is a case, one still running at t is a control, and one last seen before t is unknown, not a negative. And area under a learning curve measures performance across a training budget, not ranking at all." + }, + { + "id": "practice", + "chapter": "06 / USE IT WITHOUT GETTING FOOLED", + "title": "Keep the curve, the data, and the decision separate", + "sub": "Scores → held-out curve → summary + uncertainty → an operating threshold.", + "text": "In practice, feed the metric scores, not hard labels, and make sure larger means more likely positive. Evaluate on held-out data, and report uncertainty that matches how you sampled. Look at the curve and at important subgroups, not just one number. Then choose a threshold from your costs and your prevalence. No kind of A U C chooses that for you." + }, + { + "id": "recap", + "chapter": "06 / THE MENTAL MODEL", + "title": "Ask five questions when someone says “AUC”", + "sub": "Which curve? Which positive? Which population? Which range? Which average?", + "text": "Same six payments, two curves, and two different numbers: seven ninths, and twenty-nine over thirty-six. Neither is wrong; they answer different questions. R O C area asks whether frauds outrank legitimate payments. Precision recall asks how clean the flagged pile stays as recall grows. So when you hear A U C, ask: which curve, which positive class, which population, which range, and which average?" + }, + { + "id": "end", + "seconds": 10 + } +] diff --git a/videos/auc-ml/src/bookends.tsx b/videos/auc-ml/src/bookends.tsx new file mode 100644 index 0000000..0faa912 --- /dev/null +++ b/videos/auc-ml/src/bookends.tsx @@ -0,0 +1,219 @@ +import { AP_STEPS, ROC } from "./data"; +import { appear, C, ease, Plot, SVG, Text } from "./visuals"; + +/** Title card and end card: the film's identity, outside the lesson chrome. */ +export function Bookends({ id, f, duration }: { id: string; f: number; duration: number }) { + const out = id === "end" ? 1 - ease((f - (duration - 30)) / 30) : 1 - ease((f - (duration - 14)) / 14); + if (id === "title") { + const draw = ease((f - 10) / 75) * 6, + word = appear(f, 34, 22), + line = appear(f, 58, 18), + sub = appear(f, 76, 18); + return ( +
+
+ A visual lesson · machine learning metrics +
+
+ AUC +
+
+ Area under the curve +
+
+ What it measures, how to compute it by hand, and when the same letters mean something else. +
+
+ + + +
+
+ ); + } + // End card. + // The two curves from the recap stay on screen across the cut as the anchor. + const a = appear(f, 4, 18), + b = appear(f, 24, 18), + c = 1, + d = appear(f, 70, 18); + return ( + // A slow push-in keeps the closing hold alive. +
{ + const s = 1.5 * (1 + 0.025 * ease(f / duration)); + return `translate(${960 - 640 * s}px, ${540 - 360 * s}px) scale(${s})`; + })(), + }} + > +
+ AUC +
+
+ is a question, not just a number. +
+
+ + + + ROC area 7/9 + + + + AP 29/36 + + +
+
+ {["Which curve?", "Which positive?", "Which population?", "Which range?", "Which average?"].map( + (q) => ( + {q} + ), + )} +
+
+ Synthetic six-payment example; illustrative numbers, not measured model performance. Narration: + synthetic voice (Kokoro, af_heart). Sources: Fawcett 2006; Davis & Goadrich 2006; scikit-learn, + COCO and scikit-survival documentation. Made with Clapper. +
+
+ ); +} diff --git a/videos/auc-ml/src/contexts.tsx b/videos/auc-ml/src/contexts.tsx new file mode 100644 index 0000000..3b56548 --- /dev/null +++ b/videos/auc-ml/src/contexts.tsx @@ -0,0 +1,768 @@ +import { AP_STEPS, PR, ROC, STATES } from "./data"; +import type { LineAt } from "./roc"; +import { Arrow, appear, C, Caption, Card, clamp, Eq, ease, In, Plot, SVG, Text } from "./visuals"; + +type SceneProps = { id: string; f: number; duration: number; L: LineAt }; + +/** A population as a dot field: positives first, flagged dots solid. */ +function Field({ + x, + y, + cols, + pos, + neg, + tp, + fp, + r, + gap, + flag, +}: { + x: number; + y: number; + cols: number; + pos: number; + neg: number; + tp: number; + fp: number; + r: number; + gap: number; + flag: number; +}) { + const dots = []; + for (let i = 0; i < pos + neg; i++) { + const isPos = i < pos, + flagged = isPos ? i < tp : i - pos < fp, + cx = x + (i % cols) * gap, + cy = y + Math.floor(i / cols) * gap, + on = flagged ? flag : 0; + dots.push( + 0.5 ? C.pos : "#9fcbbd") : on > 0.5 ? C.neg : "#ddd5c8"} + />, + ); + } + return {dots}; +} + +export function Contexts({ id, f, L }: SceneProps) { + const at = (k: number, o = 0, fr = 0) => L(k, o, fr); + const A = (k: number, o = 0, dur = 14) => appear(f, at(k, o), dur); + + if (id === "imbalance") { + const panels = [ + { + x: 70, + k: 1, + title: "100 frauds + 100 legitimate", + pos: 100, + neg: 100, + tp: 80, + fp: 10, + cols: 20, + r: 6, + gap: 17, + pct: "88.9%", + }, + { + x: 650, + k: 2, + title: "10 frauds + 990 legitimate", + pos: 10, + neg: 990, + tp: 8, + fp: 99, + cols: 50, + r: 3.4, + gap: 10.6, + pct: "7.5%", + }, + ]; + return ( + <> + + + + Same detector: catches 80% of frauds, flags 10% of legitimate + + + {panels.map((v) => { + const show = 0.22 * A(0, 0.6) + 0.78 * A(v.k, 0.2), + flag = A(v.k === 1 ? 1 : 3, v.k === 1 ? 1.8 : 0.6, 12), + res = appear(f, v.k === 1 ? at(1, 0, 0.75) : at(3, 0, 0.75), 14); // lands as the percentage is spoken + const prec = v.tp / (v.tp + v.fp); + return ( + + + {v.title} + + + + + flagged pile: + + + {v.tp} fraud + + + + {v.fp} legitimate + + + + + precision {v.pct} + + + + ); + })} + + + + ROC point unchanged + + + + + + ); + } + + if (id === "pr") { + const k = clamp((f - at(3, 1.4)) / (at(5, 0) - at(3, 1.4))) * 6, + n = Math.floor(k + 1e-6), + s = STATES[n]; + const pts = PR.slice(1); + const startConv = A(5, 0.6); + return ( + <> + + + + Flag the top n payments + + + {STATES.slice(1).map((v, i) => ( + + + + top {v.n} + + + recall {v.tp}/3 + + + precision {v.tp}/{v.n} + + + ))} + = 1} + axes={A(3, 0)} + > + + + + + plotting convention + + + + + {n + ? `recall ${(s.tpr).toFixed(2)} · precision ${(s.precision).toFixed(2)}` + : "nothing flagged yet"} + + + + A fraud found raises recall. A legitimate payment flagged lowers precision. + + + With nothing flagged, precision is undefined; the curve's start at 1 is a convention. + + + ); + } + + if (id === "ap") { + const ranks = [ + { r: "1", p: "1/1", v: 1 }, + { r: "3", p: "2/3", v: 2 / 3 }, + { r: "4", p: "3/4", v: 3 / 4 }, + ]; + return ( + <> + + + {ranks.map((v, i) => { + const p = appear(f, at(2, 0, [0.38, 0.62, 0.85][i]), 16); + return ( + + + + + +1/3 + + + + ); + })} + + + + New fraud found at rank… + + + {ranks.map((v, i) => ( + + + + rank {v.r} + + + precision {v.p} + + + × 1/3 + + + ))} + + + + Same predictions: ROC area 7/9 ≈ 0.778, AP 29/36 ≈ 0.806. Different questions. + + + ); + } + + if (id === "conventions") { + const env: [number, number][] = [ + [0, 1], + [1 / 3, 1], + [1 / 3, 3 / 4], + [1, 3 / 4], + ]; + const prPts = PR as [number, number][]; + return ( + <> + + + + Average precision (steps) + + + + `${i ? "L" : "M"}${120 + a * 400} ${66 + (1 - b) * 234}`) + .join(" ")} + stroke={C.gold} + strokeWidth="3" + strokeDasharray="8 6" + fill="none" + /> + + interpolated envelope + + + + + + + random baseline = prevalence (1/2 here) + + + + + 29/36 ≈ 0.806 + + + + + Trapezoids (straight lines) + + + + 55/72 ≈ 0.764 + + + + + ✗ not valid in PR space + + + + + + Same points, different conventions. Always name the one behind the number. + + + ); + } + + if (id === "partial") { + const X = 100, + Y = 40, + W = 480, + H = 320, + band = 0.1; + return ( + <> + + + + + + + FPR ≤ 0.1 + + + + + + + + + + raw partial area + + + 1/30 ≈ 0.033 + + + out of a possible 0.1 (the outlined band) + + + + + scikit-learn max_fpr=0.1 (McClish): + + + + + + Report the FPR range and whether the area was standardized. + + + ); + } + + if (id === "multiclass") { + const classes = [ + { c: "Card theft", auc: 0.95, support: 80, col: C.pos }, + { c: "Account takeover", auc: 0.8, support: 15, col: C.blue }, + { c: "Refund abuse", auc: 0.6, support: 5, col: C.purple }, + ]; + const macroFocus = A(2, 0) * (1 - 0.65 * A(3, 0)); + return ( + <> + + {classes.map((v, i) => ( + + + + + {`vs rest: AUC ${v.auc.toFixed(2)}`} + + + + ))} + + + macro: equal weight + + + + + weighted: by class size + + + + + + Multilabel · micro average + + + pools every (example, label) score into one curve, which is not an average of per-label areas + + + + + + + ); + } + + if (id === "letters") { + const P = [ + { + k: 1, + x: 70, + y: 20, + title: "Search & recommendations", + sub: "AP per query; mAP averages queries", + c: C.blue, + }, + { k: 3, x: 660, y: 20, title: "Object detection", sub: "match boxes by IoU, then PR", c: C.gold }, + { k: 6, x: 70, y: 218, title: "Survival / time-to-event", sub: "an AUC at each horizon t", c: C.pos }, + { k: 8, x: 660, y: 218, title: "Learning curves", sub: "performance across a budget", c: C.purple }, + ]; + const w = 550, + h = 186; + return ( + + {P.map((p) => ( + + + + + {p.title} + + + {p.sub} + + + ))} + {/* Retrieval: ranks 1, 3, 4 relevant — the AP example again. */} + + {[1, 0, 1, 1, 0, 0].map((v, i) => ( + + + + {i + 1} + + + ))} + + + + AP 29/36 + + + {/* Detection: two 100×100 boxes, offset 20 → IoU 2/3. */} + + + + + + IoU = 2/3 + + + + + match at 0.5 · not at 0.75 + + + + {/* Survival: horizon line; a censored machine is unknown. */} + + + + t + + {[ + { e: 150, ev: true, l: "case", c: C.pos }, + { e: 260, ev: false, l: "unknown", c: C.gold }, + { e: 470, ev: false, l: "control", c: C.blue }, + ].map((m, i) => ( + + + + + {m.l} + + + ))} + + {/* Learning curve: area across a budget axis. */} + + {(() => { + const x0 = 700, + y0 = 392, + ww = 300, + hh = 92; + const pts = Array.from({ length: 31 }, (_, i) => [i / 30, 0.25 + 0.7 * (1 - Math.exp(-i / 8))]); + const d = pts.map(([a, b], i) => `${i ? "L" : "M"}${x0 + a * ww} ${y0 - b * hh}`).join(" "); + return ( + <> + + + + + budget → + + + ); + })()} + + + ); + } + + if (id === "practice") { + const steps = [ + { t: "Scores", s: "not hard labels; larger = more positive", c: C.ink }, + { t: "Held-out data", s: "uncertainty that matches your sampling", c: C.blue }, + { t: "The whole curve", s: "and important subgroups", c: C.pos }, + { t: "A threshold", s: "from costs and prevalence", c: C.gold }, + ]; + return ( + <> + + {steps.map((v, i) => ( + + + + + {i < 3 && } + + ))} + + + No kind of AUC chooses the threshold for you. + + + + + ); + } + + if (id === "recap") { + const qs = ["Which curve?", "Which positive?", "Which population?", "Which range?", "Which average?"]; + return ( + + + + + 7/9 + + + + 29/36 + + + + + neither is wrong + + + + + Do frauds outrank legitimate payments? + + + + + How clean is the flagged pile as recall grows? + + + {qs.map((q, i) => { + const p = appear(f, at(4, 1.4) + i * 21, 12); + return ( + + + + {q} + + + ); + })} + + ); + } + return null; +} diff --git a/videos/auc-ml/src/data.ts b/videos/auc-ml/src/data.ts new file mode 100644 index 0000000..600a856 --- /dev/null +++ b/videos/auc-ml/src/data.ts @@ -0,0 +1,40 @@ +export const ITEMS = [ + { id: "A", score: 0.9, positive: true }, + { id: "B", score: 0.8, positive: false }, + { id: "C", score: 0.7, positive: true }, + { id: "D", score: 0.6, positive: true }, + { id: "E", score: 0.4, positive: false }, + { id: "F", score: 0.1, positive: false }, +]; +export const CUTS = [1, 0.85, 0.75, 0.65, 0.5, 0.25, 0]; +export const STATES = Array.from({ length: 7 }, (_, n) => { + const tp = ITEMS.slice(0, n).filter((x) => x.positive).length, + fp = n - tp; + return { + n, + tp, + fp, + fn: 3 - tp, + tn: 3 - fp, + tpr: tp / 3, + fpr: fp / 3, + precision: n ? tp / n : 1, + threshold: CUTS[n], + }; +}); +export const ROC = STATES.map((s) => [s.fpr, s.tpr] as [number, number]); +export const PR = STATES.map((s) => [s.tpr, s.precision] as [number, number]); +export const AP_STEPS = [ + [0, 1], + [1 / 3, 1], + [1 / 3, 2 / 3], + [2 / 3, 2 / 3], + [2 / 3, 3 / 4], + [1, 3 / 4], +] as [number, number][]; +export const POS = ITEMS.filter((x) => x.positive), + NEG = ITEMS.filter((x) => !x.positive); +export const trap = (p: [number, number][]) => + p.slice(1).reduce((a, [x, y], i) => a + ((x - p[i][0]) * (y + p[i][1])) / 2, 0); +export const AUC = trap(ROC), + AP = (1 + 2 / 3 + 3 / 4) / 3; diff --git a/videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2 b/videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..920f91f8f33c9891ca2b2523cacfc29024e6e096 GIT binary patch literal 7260 zcmV-i9HZlRPew8T0RR91032KZ4gdfE06=H}02~AW0RR9100000000000000000000 z0000Qf&d$bU>tY`U;u>#5eN#@0K;_)g8%>lHUcCAg?a=a1%z$~i%=WtH52Z5*f;NZIX zW!^-EY$fpiJk9C*304TAr3FH;U=bu2F_OEOUJi{DAwjH&)u66(rd>Nzn3&OtwNvXH z{{Q2k-ShtV60X?l0tb{do&+9{#8E-F*(b{&ki-+fy8G5%XI|WFq7d=+Uzpm;IDvo* z#1(q4tbEw=@J9M2fL@d)Z*M1zIUib2S>F>L?#DhMWv z#gbNUI;V$86PN6I7Xbx4QQ7nF-~X@Ech$A@0<2#|7h#v|bWf+Xli!mR>*?vm&R57% zz{y8_wo`CED<55y3p5c-JzY^Q0c=1k4NG}M9%&e+o^CxI*0mJbWco5ikUE5OP~4^p z)YmACMu(`HlkZ-IP@;Lh_*2WXlTLbza}`+_W5k#>T>tKK{bLhoG6491rV^3_Ns$U+ zNQY#~f#fNGtXB+SVnSFdAXTa%b?PBpd`Ke!L_`dcQbJS+h?X8=WQJHdAS4RXrXAAB z1Mvw!!lICv7^K$#WXLFF%s6DyG-TE$$QD~63wA;dI0!lHDC7=zpu5Z6=pOJOIs^gL zkVJRz+QUNHP^I$>>ROVPXRK|-TcF0+#n`+Yj-~{8XX5}kh z!LpW0OF;ko(=WdBg-=XA?=Alo&v_!F&s9+mxz8QWy53c)ft`9x1$IQ5*;f$xgV|w= z8Dpn3D5jf78wV2&W$lYK83W3xJT_Lb-FkU4Xo^*%!M}a)TOV8y;Uhd~rNmsU_hLp0 zyzww26q0_^Ur^2(1Br_^F3yF#G%s%$!dWlZO$5EyN^WpM7JeE!U5k-Lwar zHWft*Aw`PFaZwo&$(|(U=p~@{mmRbBFwfD8=G@&#)1}FjBVU2_N|Y+cR)edNfQXEe ziiVzvnVn0k4jw+C-w5lL2z0@%Mb~S0Vf<(8azES%=Rv%i1G(_Fu%Pg!#f-|TCsBr3q7 zc8$j)8RaTcp<2BL%|w(8teo6}A~reVl@(32XZPVDdICL#`O|p+SXhf@tys6`*u`vD zR~7xi1FA&@nAqy@G!c?eGjX8!g!Q(KUS6RL^fLGs|6bjUd26;DI=x(OUwiED0V@{? z-s}I*U;6Kp|Kz*(I|H7d1DATFkIyQ2huIV_$w$(+(Yd9)%T6S7=0hJwO5LzYYk z6=Vrs@ybF4exq6K>T>y~Nx$B>Mu`oKl28~7vM>Q8de*W@aYbz}22BOfK9+|<_=EY- z9RU<1gqlNcMhbBXZOJ%xrRylxGa*`C@8m-jd|^<#Mm!0oX|sXN8kRW- z@5a(#6VpqsmAcYo9?C~eyx~h{&(>z8(pD0#ez2zN8H+&Jc(_lc>&gnQKuJ&?5nfBx zY6UWkfQ+RWW{hhIw@MZE1I)QSmYa5pS$f<106u%ie)i>N4sqUDts@EH)S~kzn^Uhn z%W-v7NWB`cR}WFZ$MgrQExeMio~6=*vCXE5Vw`u0;>x@P4XQyGy>n3xoVwj7o$vyDjisqnCHNkvXEvCCQd7IgU)% zuMJ5KiDX$qtQc)byrhq^z6HWUl^oc$f9|n4V^l}=WDYpU>ApB2 zvfGRiN5VOKb7~ILAeoU_FwUta$<;h>w*3&^oK#f`3GXBPK={FOj*_gYh(qfx#F*JW zQl(SHTh5H+;27K^mq`m^$;UsQ0*=`gJAfR;+8}~2=%A`~VeP$@a?^FI0;F#qHq_EQ z>{5Y5Nnu=oB%fv8;*T-vy_2aJUlbBfsmbZ_6_FvN+^6jYZztEUH-71zR@K3p?EW^k z^bsmf0-rs4sb-?9_z7ga#*=k+(%CEbtRs7LK5$L@BUJQirT0af(CA8r+1{KZ$SjPx|gf z@Jl>Cx(6APeSU^ry3v1@r=ttE+K`#3RhY=e`y)O3K{9oHDxRwD?dou!#HI_1WWJ!V%b)Ou zrJpIRq?Pko=Xj^|ifB}NWr+STc7B0e_;$Rz?ZDhdxQK4xeYPyhtQCY0*G0_#CFguQ zU6QlQr%2g(4i)Amh+lNg$j_siWdEKnp^Tbt#TPeB>aO3^+UnU7%pdfp>uo}j$9ro2 zNN}%1YQ)MtlDw?X(>)J#K^h-~HU1 z5SWcc!!tp*$j_E2bOg3DUmLoy*WU||d6&J)LdAyvinFgA`n1#52D3}vxRpE$f)}iI z!JxPC?676GzM~`H{ZiX_k;4$G9nH0`IIE2r;|Tj5nXp?dw{8i@NU3ZZtFig4pYEZ~P4s-0<*wbv=vC*U(p&5l>Ke6qgCT>CSIbS=zQuL9C7pgkQ{7a(x2a0U`m3g}3#qFl&qsyS zGLaBIy_NU`g3?YxC>Y9{rINkFy+KRdAe^<@vCVOdEifDp2;)tOenKC8&$VJQDcvz^ z#MH3WVQHKR8%=$f;|Q&h5R`XGuu?ZJ5@LV5bv0lmp1+{-*yS3R#;7qmL(jpE%ai9H zzvV1kWH={+9(!CTn00i>Huh-@&ZxOzr+Xe*%wbDUS#%FaXLKj3*_?9S-<2u?`8(nf zt+bAtQpxu0_945bEzTTpa0S+gv0<~r+BnzR>r=b#A1KTx1~j&Oo$F)j*LkEnG%yng z#jCAVYpT-T>3v7&zIAph%)EEJ?zVfK_s$;6xMw0W%uXt~Ca(En0@nQh$EAP> z!`l9%18do8^5Mb@;~tb+&t}JqD>4;|@0J{>FG^74j2W6XI_yoG!lq6~+>o_9(v(Em zEtp!wbPF-2r&$=Wtp^h_-5-50$FPo^4C+-~K6jGUbs&fdtin2<^8fo~uF8Iu|81^4 zX+OXD>xyEhae}GN!f~->sJ;7@lsh^oRijjlS67uco*gLG<=`s0zTf5X1gNSN+>%W4 zmn7{K%`27Cha~IosWk5RE#Duqwl<&CoIK=$#5#uA*6gU4Y9DDc9&`<2bdc8VZ7`O$ z(;bljY397xZ=~Os1-=a(q17t?j23C{;SCAcT|Fn=Q)Ym4=G?RGhu7 z-F@Msrp6Mn>9Ke*obQG9d0K90A_o}i>ptV*6B&;Nh_$&1_Zk9uS8 z=IL*RX9JP`yZb{#$PglW@pM}|A*gqJhE$Z=(Ar0)#fXR@;(6(l>%~T%T5aTsudDmz z9G4RqCMR&<2b?3R8y>&P$GI{bb)`Bb4s*glDBagN@yc;xQi^}nnb9>cVo7#B`_O{< zAkI*kGKtH93Mg(g`0q6rZ&!w+5>r7llayC>|FE-Zw_N`i7LeWi9KF$qXBs+F)h&L% zK$`hliCtdi{^uxnHuIraq2}DHTh=J;#>xdb(f{3j-~IlpoGJW(-{z-qDpgCpYU4EE z9wmQ3r)r3~6)g=G_TGIvx7MC7GxF=1rVqIdkFGaXbBfH5{F8^jh68?_pgw!h9C-mV zWAk?scb=e5bgv5I*-=@7MQh?m=iO{E9?)dA{5{d#u=%IYuC27Fpy;vPMO+bq%W#`q z;Gnn8bVt1-(N6Z8t$3563BTfWg`CtGbWT-@BSqfU(?AT(_Peu{J{`P3%6)`SP8zE8 zQKszw-2ZE2{k3Ya8?O9NB@sKzqq3p4%k9j5!d;e^{Zj+cV_ny=dxOo%_iuZKSot|S zulzmY@zV`Gu9k+;_Rh&>L)-vfm{#Q;>A(dKBr%m@Zv-R!6EV7*>LrxY7Q0SXV)*WD z{Jy|-<%{@rW9O6NhBt|SUOqetbMJ&@R%29okxNz2-4L`2qwv7)myS*!q)n0ij#H{rso;g* z`IuptD8vVq!P2a3W>7h_79S=I<3Eq7er8C`zALkt$*N>#%D%apyBI$M?}Wxj`piD! z2YG-aX~)%)jFF4uT;wm}`dKe)m-R&fBnauW3Q;I54tD?y%#8 z{t|J?^$MJ*kVOgqcNDJhYP_V@EBE5k<|m@ny=q*I!|gA`dSZ)F$pd_jy`vy%mmfPi z0trj~nA#Qx`g(ULTq$RtopmR&!>=u;Eqc}avShW(1-q2dwGNe9bvO|G>|b7W9dF8u z=jO)q$`2Jzz9h})F=toOv(}!JW<|BRv|qD};M_wwx*VWiJ)v6^>>tSC;`!?;O;2Q$ z+)+{Ua&y5;Yl^-w=Hz^omtoD!O@%wxb#$quuzp=(zVxj(GaBEjVT+CMTIH|a_c zS?sO_9V@laShm>HkX#<`(?|)tOvMde2WIEo+Jnm1V-O9Q!ZxIE@4lTT;T}4zz@7;0 zswT`xoYi4on#my3!TPl!X?(LL@k(iPtWBJE0x-D_G_S!J|IiWO#cA)dD_f;2hbZEH zr8mf`c&Jh7#>{ED#5xd{*F3muh3t& z=8`QZ+z)h=0XCa!=y*v-ly;^X@DPkZ5!4NcZ?Dw-mwNZ2J^XB=AbtUxFk0^~8lq`H zt(l&U-O=Vyw`E)sP>VnhwWPp2Y}J0Cz2hd=pO1nm@PQsgrd+WKb#;3Ymjr-Oi~`)N zxxE-_y5l7Do6VB8%PzJSI*CN;H4~zulD0txVll+vx!E&A)qk>wxw!6A2-W zTb!dBL83ZX1EWAGM^OmoU0;dvd51wpoqWnaWL&Kno`bD2DaW2tHWL zu|S?=fDd(G11`cQGJxA4BET71Awqe*3veDD!#RGPh0pZSFuj+1=MYdbB-uf!`@^e- z%GVx6hsxIgxF0Gaj;xOzr^$%IIGx-F@dS15iYKD@;z?=&Bi{v9JYZJA9p`w zR6M6+iOjQs_A4E#Xb#Mg{d45HV36GDkYPP0U_O@tDBe- znj1DvL7c?|xh4%jnaanF$clSev z3{>+cMa0WQaKWHKJ?`oAMYW)TX|-s+9n#_oO+fhXt^^EuK4^%J5>O(r{a1l1;p<37 zDNxfIl#0?21M2)K9c7?Q;FQ(hvgO1pH-35Y74(_L)+)5l`kE?I999XY(uA?P;2&km z6IG!yaaGu=)u>Hcoq8PHz_OgE8YKOqw!n#;iFTZL--GTg_Xr&GtU_ zw4Vatqz;8wb~EjRATdEDBPgb16?!Dg#)MFL11uPo3|LTM8xX3$C!$^?HAqCSc^31< zJg?==Iisieqo|+~c~R(f?!GC|2@Gz-1ygC3_EAVSlVvm0NPbU|aXo0o?g2MQ=}sF8 z&uZ_wOzw>P=1llfgPeLr6B(a$T1E?Mdc9vIZU1ZcHD)#NILb0tETo% z4Y5O!IFQ_NAgvtg)8Q@4CSs$+3}uQ!D(z|p6cwt}RR_76Si4o{u~T+KEcUvgVN{J9 zZp^A#)>5e#v`D*ZKbYY-&BTcf7cTXME_8{P^eCU&M|zZ>^htm1uPJa_G-rz+k+igh z-imSm57=hXtlE6wewQ0J9-JMi_r@U)lkd?Hd*WfFy<8|(wW`ev5jxy0nkbO-G@59} qEY0Cq?QHO`xMtlmaqU4ce?Q;9i8(t+$3;C|lpzLfchk&@%yIx#yFhCI literal 0 HcmV?d00001 diff --git a/videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2 b/videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e0f855ab47698237e06affab01bb5839b20c0c2b GIT binary patch literal 900 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
    (f=>rL$LAl2z69uPWtW+b6YI4lp$9 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 literal 0 HcmV?d00001 diff --git a/videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2 b/videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..42fd93cbe48b7c9c08e4e9ccbf3a32a25290ce49 GIT binary patch literal 20844 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 H00000FmD`) literal 0 HcmV?d00001 diff --git a/videos/auc-ml/src/fonts/fonts.css b/videos/auc-ml/src/fonts/fonts.css new file mode 100644 index 0000000..4af8baa --- /dev/null +++ b/videos/auc-ml/src/fonts/fonts.css @@ -0,0 +1,149 @@ +/* cyrillic-ext */ +@font-face { + font-family: "Fragment Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./4iCr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* latin-ext */ +@font-face { + font-family: "Fragment Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./4iCr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Fragment Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./4iCr6K5wfMRRjxp0DA6-2CLnB4NHhqcL71Q.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Instrument Serif"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(./jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Instrument Serif"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(./jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjjH7Motmp5g.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Instrument Serif"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./jizBRFtNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Instrument Serif"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2) format("woff2"); + unicode-range: + U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Schibsted Grotesk"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(./Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2) format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2 b/videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..17653042acca7f87268c8c29dc8c0cc094c0b124 GIT binary patch literal 15040 zcmY+qQ-Cf!uqE2I`)k{_ZQHhO+qP}n-fiu+ZQI<9`_G&+54T>TmsLqtlBz5Z1#xB| zV4(kudjJUG|CF)o|JBX@r|d|EeadL4Ft>% zLJAu02u27VMhO{)hr{j(zKuyRk2e6TvBEl&1jQF0YNRfq)Geh#_tQ^0Au=?-7qu7# zm!zc1Xh!{P=LRXPl3WaM4)GAo1qEtB{E+H-<*?bQ?c(QZ< zwe5Xn@Gl0eok`rz1ZT?-xQ`qn7I_1NJadwYUDF9Nx58sAD&K<8Q1|MhszT#H&!=KJ z0JpI(qb!IFoni(|Dp)LPB*^H|!+hiHEyrW4ul~6+^r5l?nw)8jC$ey%S{$XV3P$%;)@6zgM$>$$;BA(!uorEBWh;{w@R3q0{=yI5Of=oiG7yq*VhM6&FFc?a6-QH8nYb?LFzogMs3y0{91+@y1dda^rZ|^5o<2!3s^2k~;Edn@#(xhCxxV$(I zkynUJQXioLs2(6eH2VOwT4hx$oBd)3luGL=RiC;}>Xpga6xd!J3k98R~({*Sl=P!tKIEil~>(!YG^ToPn>0s)>|g*u-H;6z`fg}<{sR~{Gw*l zY9#j?Ia)jR`b7r;k!I2|gcO-1cKIHM-);H|tF)Xm(!E2^2b1e-Q&E39J4n!!sa)8r zGny#=0O!CQqiK?`bk`jB%gu6Ufl#YA`z0dJqaF`S073UD~F0F{|vYI-H0xgCGuCtF(I#k&* zsqz`gKS%Twgn@i1#?cWhj2N~6F2uudxm(*n1J2DI7)R5<-L{UD$tMb)1yhGZ`Tc)8 ztw(`Az)6*IK%?74H<5A5xd*Z)q*?90|E%2@;KY!iiQH4jGk5%tDRR=r-%@2US$c9I zg9`NMqLdJ)!iZ3cji*UVi-O+{Dx~OcPr&H%Eclz%L6gqi*KFN4ifNiz!Kap*QP2X6 z%t3`oC7>Cke)G;gfPc`wluWj#BJQ=Gu7KDcoyE?4^5HC8R?WG51E05D zE6^PLs8yz2qmYQBbUWhr^Ck-{3_FgMYs+F!lxuoNoy}m-UwFSG5<& zk%wxwpzL2!2IKVj&8QS`{BTgGDtxv1^|W(&68n6Xa!#DRunqSdpo}RjG@hc4yYtdJ zab2OFU{1->Wpw{qOx$E#<%k6Y3eURL@A*P;)~>38qOtiZZ!NY94G}H42}aLFr|bZ z4)mg^mpyDcA`Ov!WDXdknq7qMS_P!!Op9`SRXGn$bu!L|&YYVveQyy%h;2iCxGPW- zLM9^WLkG7cR{Rsz;nIWJa&62s&?my}A9ESD9~puJWc%4kHK3|eBi@EYS?K~uH7l0e z3GG(NM#dFYXKx6uGh%Q>zHF_%=c2x*>qlzx+%G3#uy#k$Q1AC2pkYaNrhn#&3<~9Hqc3Pr;LfMOTWx=wg6Z@yuu4T zH#A&63uZ)LEj&gnOc5XGVU)80x|;F7A^CYEB4nT#BLNBTlB{}t$?NA@+DBioU^DK;IdRx+l zb60;udyY-{DjMkN0#`T@lI}>BotA14*>+FN)LEUEG44+Wlg?H*-5|omg(^7JwuQLg z2`||k4nVd%HdBSwrF%Od^~Hf2xIrU zD7MNl#MKFK2Yp<){`R)5x!cBNQ-&&q@ggAz8+UC9=dR;#jwVvyPNdYazP#+OSG7Wp5HHnpQov)(8;wgdk`6OO>y-@Mf`u?=XPw8fWOgo%y!o& zP)xGq(lfS*8yIQT5f(Mgz`W}G9=Rb$KFx&LCmOgU!Wp9SD&ac0o#?omj|Uccw4H(n z1fx?`7PgXZoAYRKGqh4;w&IXtoBujZ>{I`~1xMt_UU6f*HX~TRZKy^q0xy`zx={dRF@Y#Losqz7q~u&g-Jn4S}LQJMxs#} zgot6U%Igvrn_@}8DQk)4no!!Ra9CrXq`D<_iexMxh9tAdl+rRKt#SE_1aj5>y7na0*@f!lJYlS!pCR9>XZt+UJZ?8^0ZE{N@&Y_0k)k-LOK z#3_)Chc9sAax&!BvTmKQlJlGa^7K;4Ws)cP&V*P>d|HmLd-(s4Oz+^gKT zmb_<*h$7m~q{mX7R=`~9QA=f<^E5l+BT{mcVp;su%TUjamegFYm?Z8n<#(0dj+9b- z)K}wRojK~ec_lkZ<@|OUr)nCg+feBAMhTn5ay_9@v^1yaT^yaIl4-@LJ<4c!nZ3*= z(@8p_6OGBJ+L%wO1k0#btd()96^4_<<#L+PW7O?7-Bc^I*3RAMMNSJnHYv11>e>!A z;+(d0YZ**1T5){JOGe$HcyuLFJhn3*vJ>-q`CmIq7Pf`_&YF5cEQ`q!EVOY_*&c`5qxPY2IPaKD4VZ3CE^F%&S ztN)LqJ#_Z}H(+2%8AgTqGq{Vu6dVwL})o4qH|{}N6ryH4^5EK|0Sa8NijStT_YCMLQvK`xGjt^~C&68|F~ zR2VnA3X|}E@lM?ahos0Pn^GxvNnj`2=bH}E$oyZx8px`(rP6woD)qwuZ*@ZOSs$}k zQINrekVKKAi#nO#P9Dxb_47#T}-#cK>@bp9}u~^B) z&hU4oj*w>ibzIKJN5>M`DYS&-1{sfLc~oTH7kgfJzX+is!J)Ch(coQi@fk))#L;3n zA5ch}8`JX~|KGmx`DH7U_*<$QCaqSzQrRG`wwK>Ov(^N=LFc1?NWsnyCX5ed4KdC? zQm^yfOeh+`tx465Fg`FcG&Ts&FhW6iJe)zm1g!;vOsW~F=p0Qcg{g%6LgZ`7#=Q`O z)b*DCSw8uRy=|0 z!mJOJ#eHSO>-m2$%0)EdPq>9*|U@XIB~R=?{m;G5DJ{tawPyC$Qyt6FkM;qA9Bnu()pAB>la zgO_ImTO#-f<_%#!S<$S8myy%Z8Oeto2po1DAgT$C#{WGs>Qu9w(Wup` z)Jy)*MIX`M-GwFK=Ws3RNHiUd#jc4eo@q}j#nIqq|C=2kngtrki@f(*%alY30a|Xa z`}wiQUmJ?vskC|HDlPn6Nsi0?y?p81qYG;qQP@QNxR7g`>OUHnCux`2p*QO}zDZ^! zR^U*sfZ+D;EyDu@uq~?vZa1`7o9|Cb!8E0K8n3?80Yil)e!l`iB@s%1i|4RijF9x` zmDvv2zzLDFj&_ly?3)NrOQ>4n6DM>@E&?tEy!fD?U@#3Wy^*ln&*;j*>O;Na0FIKo~kb| zVPMLB1>FC>$#Z}EzkZ)E0N z(H@dDJ3Gpx6RXiT&~E5TtNuqi?XWl1i~0W;$94IvT2C7o#0ebQ-et7|d#faumL%r$(ry4iKNiv_5CvKO?l5o6y%aI`0v?)?% zg%TO|Wx`rBW3zw+KM6G(acLsN{O@jBfKm-3D<*G|UsAS&UNT{#EH8}^-$xOR72je^ zM@^Z`uVK|$-8U6h#+Cyv67No`ACyDRZ^U#9=MdTwb||UvX{AIpHeS5Xw9Es0NMgb? z20MCSilzBwS8HtJM)7%w78Mfmo#vae#bAjFir~)Dt&g6H6Cqj3#4Hb@MWQ1ipg}W= zZlcFm7@@YFj*^E68BGfeW#HPNo3HBB%0pu`bYJBwDP%1BaMsh7R^#Nq1(0Nd-ut`L zroRo9PibZEdeF?~BVv$1Qy6tl^Ln42zVRW)!Gt~CCmskUjuFnMXaFrxOR40H;EIxn zYx_uZ!immRDYryNt5KQ_V^!8_>Ql3K&C@HDHl&Q-ii-+)ww(#h*P&?j4?-4{&! z1~-hqGg7mKg@}~{P*L+WkilG*t*djR9UY(X7lcX>RcjtZV2ko5-fg&MVjG8gZ(iF) zx9n&r8&X=ex+ik z!GVfm;bFEoZR_VIw6|Dn57cPL*_S*?jb+2U^?@#xsTn~fAsS;VaUE|PG}DkjCqE4< z^&`sJ?R(x9EE*QD+qk+I{+`Ebb zGCp0RfUWCN-12y=C(-I84M+$eoPIKA4QWTP7iACMzuL#*saf0dPY$p3a>nKH!}n+H zrJt?~=SU{%+WD&0DyYR6-qSM(v5R>z5q9T7)H?ut3?&B%#{q-AnAlryDjG8W@l`G5 z%c%zm#t@KWXGEB9Qq`5LG{YNh5G4VUXi`KsEelpypcNHS=gGm_*w&T!3Yk5{@{>p~ z_xX&0Y9tN)dXsCx^+NW0ZwwywGs&OHVfye-)BHaHJVok(kVCak)OQ7_t22br+? ziBg88kbyXFCwatl4zfKvEBjw!0MCBVXzkCS->y#22wUs3y2`+~nuV{=$s!R%=s7nV zbz~gw42m$Rj1>>c0!4n22aQOT%`2YHX2MKe-?fN>q7JkIF_%hKoESK>koJC^`{j@@ zI#ehv`Bi5}ja5C)5~-QR`ZM~Mk_P$@SsO!BgOqe z-c#=B?>A;l8q;Frmm)ppAy){RjoP9p5jiTKWc+G7nZ)SJqbG(kFoi448-QXKX$)@B zt;$5&mrmST(PVJJ{GkNqVJId0UMmIhbS3Dn#-e**QeMq6*mVt1zE@h)vNUQC4Atv1r*u|Ik;PLpi@&Rs zyRt>8OhyU^2-*-mgTk>*vf1agLKOiaAGW%&4`okr8On2?>{`}oJE;(p-E~OJmrCrO za!Dc+R2EM3p#6@1+I%SfY|~+#l`^9V0bLIb$rg*?WB8!W9e;SwJ$?pc7{_kAjbwK- zvCZ?(v;9?kp)S+9-AmC4KkIAWJ?M;mwzRIpn`;%8gr;CW+_8(fRMDeRV^enOaxl&Z zidjY?7<_dX;4k~w7`pF`;GNYHrb z1>Djxg-Ik34svU6+um%f#cMF}wfNWF)(=Ldr9q1gF8R(6j+BQIi8eutnxRY4qH4}P=u=ZyIjySP@NftT!!Jbwv1gl~H}H-wNrRU{Lr zD-XF~JQv-Rjm8W@c1geNF<8yiq~uZ@>bU};T}yKi+^z&ImE$8{Re9At#f^Pt@$l8% zOA}!~S=!3thFjB5r52->Q*qHo42*?(SC%zfS2dhGRB?1ZR&qR`NR;v&LXBitVeupPQG6oa!8KS-pm`*XbMN zOTJHL-NgXs#CAac^W~p-?znmq378tfSerJ>6xeq2NIUGeN;I|fz)^tD->W)Qp&MzQ zDde>hW&zZc{^ae}GxXbTMh0{eU6hV-Jw7uJD|1w;j2gxE>^M#H=DhFV78P2q%`DQc z1CwB*iY;Im>q>|;DP<3rfE5GvJBQ^Gr*){1{A_SAK8W+Tm#EjcS@_31M~H_G+_A+3 z>!_KGo7rS9A0V6wTBw;f=0lIEOu8>;0~${XHgN0K>|{R?mp+7>_|GtPkU($_NO$WG zzM2l%x=~QHP;SY!v74nUoHX!D1h@Sf>8(=Q)pI;|QNwTtq4^7^Kpb2vI2%{?s1F9q zb|6kIFMr)CUgZA8FywzKe4x&GNZ+d*rkvUFTBcd{c_%T6x2*CSfPKX(`7*BglKc4j zKcD3kPw__^!I%yy$8@j>k^xH-S`&dE}WYcy2$J3O2liXLwbq76!e>c z+NCP{Lo}3j{WfHX_4&@dY=F`of;Qx3822vpxv5IGEi;`RiBqk?hj;TH>v)8AWeFpx zM*Kd?J!zy4R*=29%Q_4T^>6Y`*&fNjT5hYqWD_YeihIA!=0BnJ{5IniRt$rKei$>$ z%7v=cs%o7G#1+4Yu=k(ZZYZIcs%C_&1OFgTheUCTFUJ%!yK>#tF??Vk$2tG?jeX4G z8_2w9*AG+g1O~`C@pM(zI#OkDj%d4>I-Y5oOmd&! zq}OAB+5KVeY^-Pu;|i>?jh!X-v?)R%Gt3sJZZcvsj(0ukBN3O)>&tt;<}TSu*s-ss zg~y+XQHlQZvzaD*xKi)@<>!UmAUldxgekXo3pXn=ZB5q0>WNvE&(ajuEio*+r%zn~ zTV!1-YaZEEA|_$E#t!-*^x$7Fe7A1mZtat@xfu?NP3-Pg47By=&$ut(i;jca71d*{j?pxJsHZDJl z7IR6RQAVb96IeATGk`wQ+*~djDr1CT?e}qomLEaGoALV#ig>+;(T~_#wyg2z>nZa+xBPL=P&ZSyN_yNhO_QD4HU-&52n=GvG57TISiFTN^wHc`caHhHv|C6f>+~C z)V-5nrmZ3NyRmiF2XpN?pR8ej%akO#{dy(C;tF(cBAEag;v=6ojGTQB59}Qqg#Cp2 zWw@=T>gHx%Q@Ib_d$@vdWv%F=_?NbA_?8T=l=!neai|?ljH+xSayBqd=mnhf^&9!g z<*I8RUlvVOB*9XL@Km?HKpHu&$G36yKH~nL15k9t9p# z%4|`^u`FkJbm*UtIfH*y8snYT0prGQEjP^>!_^7YK=%ws`Upk-pPezx){f~ZUv`_j3iCk63 zuoR&@0;cA0c7YvrV-{GPz)XloRYW03N~MQ?DAi3*XJXTGeD(k3WAjS-8DuM%R$v`y zvB=b{vjieowM;*#HJ@BuG@CuFXpz^g+DKQr``X`}K&$CL_1DLiP$-z=I&twy6ubGv z2j^mXSJ&Gpvgl5*)9zn8YU)vjq9Y=6c_{Mej?@>P{WSJoV|qa@V!HfvDL}Q8a`H*E zC%^w#r_NsgJ99B^#A_^^z*W(88)$C*JRgQfLx|EnJ;E0dch2&S@K=758>!{zIXRz1 zrIL7d`+GYAICleQQtIyIwR1&@7dY$VJ(<7hSoqGKG?$`!^i+Y(b$TQOUy|rH&pF9* z+6HdZvd_0~%;vD^5$6W~^K>NfK!A$*t7(ij25$kdTl2PI+ky%ER?)28Nzzlf46AJ z9~&K6)pVOoYL|AAw)-Q|ESS0igxt}5?x@Q*SLakcBup|SYlWa9UE9aEP&@T^C6Ega zkk?xsFflbg=M=059m%2;%9J@z#h>E-nf(lV(N?2MPQZ;;L)_S6?+SH>n?LtY%6-MR zA1n|atIylbXu8$_el1`y$8D?m@K7${Uv1J^sPPeJumz`%>Hp;?;b#V6PCJB%^1={YJbS*o%E_=P|zmB!k8{e z8ZK=hS=0S2jlWZ4y2zy7%&$bvn+GCbZE11y%I8MJnswy8cgfB@fGY{?N+B2R#sCv3 zMeP86@0CGg8EZ2i{`Js;G6;h8fl#4;C7t?KVV2LjYWafxYiea_3OsWafTRsE^EFCI zd2+WdEwmz@O!JETZv^j(GC1~+n)?j_l-Uos-UT0ik<-}Uoa7c_^pl?OnOCwKHzqmq zEWzIbZ)i_cWv&u5x?9}uVBQ|37Au;N`MCkbm2n=0mQD(>whg&4a<%=AX;rdVaN>BR z4xHRz)z|g?)6(n6FtK{eujPLMOc3i74i5Uh!vdSOd?3Qz*oO%!mL~N}ZwjCKf3UvZ zzsAdfTKt?O^h`-S31B=ZDs$-6&E%Y2J0G>w*$gz-zO?f#354~UF3fDCR;6bm*GjGx z+KAfL1gw{>sVCID$FW-^ZsES4NfzOR2nbv5_|wXhY>R`Dpg6doQhIH8{ng7@F< z0XnTfARa%xTVka5v>-5L! z#sGkWrii!l($F_k?q~TBlHRjcYj2jD$Ik-)+J@f;iv7g}l`?yG%bYxCExDKiGC=W( zGYIqgBl}h^K^N3a@R|sbyr;>s;|;Z%-%j6W0d`8nl{(#{lnRerSxjtH7XB?udjyw7`w(T~p+Yf01>)utsdD(g4=+j5ePW+VUKU2LHZn z=yyR=?Rp02=TFYSzEY1{x(CE(qy0XfRVWYHHlrI=z&H_eHG5*Ralr$<)}3jpn*Xy6 zCr`*0!vx*}Z~zQfk?yI2%ZfLm6gMoR|b^ro3`t zrzU+rQWc^HbqO=By8Tbb6p}D3XvxVFS5%&{LA=3!I(GbSjm)$wC>JUB)G#XYKF9B~ zJ!g@|1^}1@UQR;TEV!`IQ$iamV+(1{wpY<}B{Ovti@e49p)=unV|k?k{8}R>h2WCe zEsCCF9S&SR`^+#iGfHTxaz{X>7&GoKBX6iLQT^?@NKJx8%VXV1l$`j>8G@;rkrc zo9Xw+9u&C9MFuCHKt8hf`=MG$AWoY$Q8k++frIi0bb>b5|Lv%1a-i{r&rHqV{V%QnQc3%cph+g31o#hPsD3YqFfB$<; zbAF(y2o+UGBx#ZcJv}v_@A;{m?77H6VW)j064Qix6~JlQ$e`U{Y5X`ntRP@Lz;pA~ zlc+La#6LkBq%_&kqe8Q5OV9iD#IBWsQCdjTyn&&yj3d(Fg^+dQL1;8TDR#;b-<^$A zcKa|#Hp-|+qG>dAka?7Q1;3(_f{)KuR(zRhkRP4mTDffD0)!J8YuMjC*kh4Z4L9S8 z6ANwTa@I}`esP#QT$YQ3&k*h_f9-Pb7|Qn?Knb`__$nIiDzZV1zk4ruHW6{S54fSj zY`2Jn_`P!@zYMlhaJWA8SKY62~9^Pp@1#~3aUg7{lbqp-`jMGJu=DxJN$<3M~x>w4f!$pb6-k@;LT1zR!uNthF!X0;9h@ec;#*q-Wa;oDbVwfo)fRD5Lm=Uzj zI-?)G10uwvn0|dpV2+w0{rQ)N)?L z;a1l$5Ms(SjOgEz^~o4AuphTL2H*P(@|2Q!S;g=ERF)di#@sPnSNLYGIzq){`2 zp6zp*_l{PsC$yKd-<+3VsGAuSmRa{z#v5k6%i$E_)mJs?F3mkaP>Sm8EtNGca0I#$ zsxvPTdss3Nvl!g#9th$<#tZ)>v{NbFGXe6@svfJ4@J{-@jdT)fIZc71|2=#sM@EK6 zqu6!*0bv#XA_xs8c;Eg78~PQzoHcPtFUYyQH~B$%%bnpHd-9swE35?i#nV+ z;QZj_v8bItC(P&oVv;--$)VZTpxwan5o&ewbh;J|nPi@>8uc!V^Mu3IfS@e57H5d}PIxn`dL)iZaCLgwtCjQ0L% zvcs&VCWU*Sn2<09(|zX)hhd z2(D}}Bc#!B?KH-4*iunc6bV=8$Jb0{%LC;Aj?lKe5_eg&{jUly^i0vz;!+$6mAqM3 z8k|UK?)33;!r3fYSopCbVWHK~#O^U5jPtkZN5va2-pJEnpUm3Kp%&d&*GWQ0 z!arXyd-jYkAddItb!)}DRlC%vsbKu)uXzK=7{(IL3T$R z^0WpRN3{6GWP?5r^W&YiEle!H0KB>eJ0pn*T!T4BfD@NYR3D<6>n9CTZ_kkSv*zdo z?FoF8cQ&D6gjVsmO83OQZbFWkp1vj@+^RQot0+de8q7>x*B!?N?SO<@!nT5GnD zB@Whsn*$q>EImK71~FR{`72KTnaPy>yUF(K`xRbk)W;v+2X%+Yn#*UU-mNG7w0geh zO%1DPC@bR!#@lhA_iXeqFDM?UASo`VU*F|j!%fZC*A<@-{_q_dcf9zqt-H4pQ%@*} zKYW?un)I-9F9i|<2K>*tH24%U5V|$j;8Ga30a7+ufwR zNF(3GaT{rhgJey>rl^`f$b-&`1>X`#)r4FSQ8ei)ZH^8AvsRXTI+)M%GrDqfvl8v% zLY!#SNxGmm75(}=ftEV{+=EloC@c?`p+8GL3snWFt}Z@?NfjWTQwH;do~b!rRMOrp zkRAmp0s3fc90Ds*wg?)qyrIY)p$XY%F0KE~FkJU%KVGQ(WM_vM-vqSJVSDqIl<1rr zEC>=`0Dr9a`C$CWgi&}8P=Jn#zv$|}7u5SYh5-vN`>wsJO#OWjWT9N8OO{pz1`8VK z!V)L^dd_2Q7H5DE4S$j-E1zO9dw!fWme)vdq~q*LT0$S;S1M zgzzuDMDWj2&jEI24E4XE-OHtG-7Ukhw?p+G+uRnR-J*4m9uZt!GX8oGGiZFhg6b#- zpWB2FeEEfs`dkSsqX5`q*;ULkYmcV^3$8HZfTl!Z1U=eEM2MWv?gs^NWoh$<&YSz* zEe!>g9Sb}C7Te~$CXYmy6vTGNC_^&8KeoFn8v zlLAy_Lv0+p;9Prb%l5b4hF^hWLAxyWHrF*b1iUcqbBOwsQfn8o-a?L!F#)yv*xEd& zEEv~5-f8Q*ufTa;Rvu?p66lO%)E{nujYLw@8Q~Ky(J*^Y_ za2O#I_5av81O)o+rmsZKyzVRRSNxZy3=A>KAqlz~WDEz-Hn`Y7-;^$z{y-W(4Il9@ zaRoNBwf7uC_6|M%;#cp)cmN6oDr5sC?vXiJ(ixDj04`S%tXv`#b_nV0;|Vl9=V(Jb z{W*bQlnnsphDFK)`RcPO6e(nBXm@>86R*&ZyOrzjo-cD(bju3cBdcr~C{!6BUMhT7 zwka;ND;{NANM>7D-25-pH?FVbg#l8s2uLs2U$*+=s&nb_XXut7QWATb@#>mmarusa zhOlK;H`TmjrRsZ8u2`X8rfDyTs*c5f@!G-dGL7}-%HvN-*=(qEO9O;Tr9#9!OD6K? zazNMEQ3@OI(AJv1_y}E4n7u@2Pm<>c>M^A_k00;-Z=Pbw#%e)g{5M@f!Vb{-!#+lk zFW%=B;K<>qt_hQCX_|4$?$+_GpC6MDs3ss_LD>&W~KJiMw4*5~#_@+Ao^w`QY?0`tLG_pU*+tmWXoPSjxd;b^i>4#c&<(;N~ ziOh%4CO#1&glq+07IqS4W<=8B42mz=|XenKl)g|ujb@_rc#_8I0lYi-6>-68Pq zcn35qP=nrE6Jmd+{x!(o*(9^UhP_Bk{oFMx)uQM<7QB;Q;C)HoN3{K)xM{7SL&1`M z(6``c@)_)jpt|Kb;WR^^|5cg{I6Mzs-(C))a(`Z2w**CBE^g9thti5)6OELVUU?Or z(Mu|Hgy&34dB4@Q>jCRTPD&cv7N>w&+po7kn1K;gAW+Ai{|>+IbpQ!wsRPg-&#yak zAcH^G27kI)J2&2M@C`AE1A!nxAJm{PLA&pNCVyPRw0joFQm`K#y&{)L9)05CS}gh! z!b?I#NMrs%B2DfHoC&aIn(NLnFzxzH+}xuW9m=Pa7~HMs;lkwZGn}wTn-h|aNHF5p zvGEqdbwKLnD~&TX2PtZAlei*~X@QYAB(jRCrqLPgC_meVpP9fJl;iC9;EO@ zSnisfpA}}0RF--shC`RqJ-XtvgJidmjy~1JlV0L_O9D-*QL@%wwmIXIg!EacG$85) zDHRm1`FY^)k5e>c)w(~HgMUDhiFCU4_0|w-S;9fXoSC!9FPr~~2X;K~h~+P#+dje) zkRGvNh(Ax2Vgu45t$Mo7iNHY?q%Z_-PqXOlOLhNebU7@^Ucpi7>Fi*O(DgLVEvju> z`g*@bFpKu7fH?vlj~;??a2?v!r3JQ|Kr@SMNhh3r{*K4Pxo`SaR1Z4zuv6 zTC6HM+ev5Z_Iw)LBG<>r>w%NEpVW4{Sgn$X!LI8ztEy61Q+{F>&%^f|Md{)_e_%h6 zzOb|;Fo&yxE>REj@-SVsnV*)NoJn$V8BCIsTZXG1S67Zys%WS0k!ztBV`nXXj(?Gr zw%sh-LXd=w!@6Mpx^#`aXivm0fH&e{mw7JHs?Z;LIsLSgG<)kb&)spHvud?Utwe!} zDJb^0ExrmnDV*VkXD*RA`3G9JST5$tv)5_@k7un%?hS~%P3X8oIvo24VBCv~+|QP# zQ1jXm9~4%FeWB~ZUX$yR5~7Oz+ltjSleh<1UehX!{t>gzOLSn$T`T^ef#R zFBWaM*4&FR!La9ci_>nyJlJ0zJK>Sq#9QP+;NbD^P;kvD;XpZ0LjSOAr(sbcXz+iU z=2)u)A2a)q`cJZ~KAGZB1RR2ncj#{J@2D;X07(-h5j5=JSq1cL*(fhicFrW=P4D7Np1I`U_@3%vJP-T;q@)>YTFSt_6$ zmf2g?*)4rlt@*1Eb^Jr3Rdh9UHWMW?r70=sXwTMk+CK#lt2O= literal 0 HcmV?d00001 diff --git a/videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2 b/videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3ee1bf99e509367119f90bec172d17953ec35dff GIT binary patch literal 7828 zcmV;F9&6!uPew8T0RR9103MV84gdfE08HEf03JI40RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2 b/videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dd21ed1685aaefd7726a4a90c0e787860cfa6a3d GIT binary patch literal 8444 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 iZ) (b.chapter ? Number(b.chapter.slice(0, 2)) - 1 : -1); +// Rail segments are proportional to each part's running time. +const PART_SPANS = PARTS.map((_, p) => { + const ids = BEATS.filter((b) => partOf(b) === p).map((b) => b.id); + const start = Math.min(...ids.map((id) => SCENES.start(id))), + end = Math.max(...ids.map((id) => SCENES.start(id) + SCENES.duration(id))); + return { start, end }; +}); + +function Rail({ index }: { index: number }) { + const f = useFrame(), + global = SCENES.start(BEATS[index].id) + f, + part = partOf(BEATS[index]); + return ( +

    + {PARTS.map((name, p) => { + const s = PART_SPANS[p], + fill = Math.max(0, Math.min(1, (global - s.start) / (s.end - s.start))); + return ( +
    +
    + {name} +
    + ); + })} +
    + ); +} + +function Shot({ index }: { index: number }) { + const f = useFrame(), + b = BEATS[index], + d = SCENES.duration(b.id), + L = lineStarts(b.id); + // Soft scene transitions: the header settles in, the canvas rises; everything eases out at the cut. + const enter = interpolate(f, [0, 16], [0, 1], { easing: Easing.outCubic }), + exit = interpolate(f, [d - 10, d], [1, 0], { easing: Easing.inCubic }), + headIn = interpolate(f, [0, 18], [14, 0], { easing: Easing.outCubic }); + if (!b.chapter) return ; + return ( +
    +
    + {b.chapter} +
    +

    52 ? 37 : 42, + opacity: enter, + transform: `translateY(${headIn}px)`, + }} + > + {b.title} +

    +

    + {b.sub} +

    +
    + {b.id in ROC_IDS ? ( + + ) : ( + + )} +
    + +
    + ); +} +const ROC_IDS: Record = Object.fromEntries( + [ + "intro", + "map", + "scores", + "threshold", + "rates", + "sweep", + "area", + "pairs", + "bridge", + "rigor", + "ties", + "baselines", + "calibration", + ].map((id) => [id, true]), +); +function Film() { + return ( +
    + + + + {BEATS.map((b, i) => ( + + + + ))} + +
    + ); +} +registerRoot(() => ( + +)); diff --git a/videos/auc-ml/src/narration.ts b/videos/auc-ml/src/narration.ts new file mode 100644 index 0000000..c0f0f61 --- /dev/null +++ b/videos/auc-ml/src/narration.ts @@ -0,0 +1,22 @@ +import { defineNarration } from "@archastro/clapper-core/narration/models"; +import beats from "./beats.json"; +import { FPS, LINES, SCENES } from "./plan"; + +// One natural take per scene. Sentence onsets inside each take are measured +// (prepare-timing.mjs) so diagram steps can land on the sentence that explains them. +export default defineNarration({ + title: "AUC in machine learning — from intuition to mathematics", + narrators: { guide: { voice: "af_heart", speed: 1 } }, + cues: (beats as { id: string; text?: string }[]) + .filter((b) => b.text) + .map((b) => { + const take = LINES[b.id].take!; + return { + id: b.id, + narrator: "guide", + text: b.text!, + at: SCENES.start(b.id) / FPS + take.at, + duration: take.dur + 0.3, + }; + }), +}); diff --git a/videos/auc-ml/src/plan.ts b/videos/auc-ml/src/plan.ts new file mode 100644 index 0000000..a143b68 --- /dev/null +++ b/videos/auc-ml/src/plan.ts @@ -0,0 +1,23 @@ +import { defineScenes } from "@archastro/clapper-core"; +import beats from "./beats.json"; +import timing from "./timing.json"; +export const FPS = 30; +type Timing = Record< + string, + { seconds: number; take?: { at: number; dur: number }; lines: { at: number; dur: number }[] } +>; +const T = timing as Timing; +export const SCENES = defineScenes( + Object.fromEntries(beats.map((b) => [b.id, { seconds: T[b.id]?.seconds ?? 8 }])), + { fps: FPS }, +); +/** Scene-local frame at which sentence k of a scene's narration starts (k past the end → scene end). */ +export function lineStarts(id: string): (k: number, offsetSeconds?: number, fraction?: number) => number { + const lines = T[id]?.lines ?? []; + // `fraction` reaches into the sentence (0 = its first word, 1 = its last). + return (k, offset = 0, fraction = 0) => { + const at = k < lines.length ? lines[k].at + fraction * lines[k].dur : (T[id]?.seconds ?? 8); + return Math.round((at + offset) * FPS); + }; +} +export const LINES = T; diff --git a/videos/auc-ml/src/probe.json b/videos/auc-ml/src/probe.json new file mode 100644 index 0000000..93216ba --- /dev/null +++ b/videos/auc-ml/src/probe.json @@ -0,0 +1,165 @@ +{ + "title": "AUC in machine learning — paragraph probe", + "narrators": { + "guide": { + "voice": "af_heart", + "speed": 1 + } + }, + "cues": [ + { + "id": "intro", + "narrator": "guide", + "text": "A fraud model scores every payment. You can only review a few today, so the highest scores go first. But how do you know that ordering is any good, before you pick a cutoff? One number answers that: the area under a curve, or A U C. You'll compute it by hand from these six payments, then see why the same three letters can mean very different things.", + "at": 0, + "duration": 56 + }, + { + "id": "map", + "narrator": "guide", + "text": "Here is the whole journey. On held-out examples, the model produces scores. A threshold turns each score into a yes or no decision. Move the threshold, and each setting gives one point. Plot those points as a curve, then summarize the curve with its area. We'll use the same six payments for two kinds of curve, so you can see exactly what changes.", + "at": 56, + "duration": 52 + }, + { + "id": "scores", + "narrator": "guide", + "text": "Here are the six payments, sorted from highest score to lowest. Plus means actual fraud; minus means legitimate. The scores only need to put payments in order; they need not be calibrated probabilities. Notice one mistake: payment B is legitimate, yet it ranks above two frauds, C and D. Keep B in mind. Its mistake will become a missing piece of area.", + "at": 108, + "duration": 52 + }, + { + "id": "threshold", + "narrator": "guide", + "text": "Set the threshold at zero point six five. We flag A, B, and C. A and C are frauds we caught: true positives. B is a legitimate payment we flagged: a false positive. D is a fraud we missed: a false negative. And E and F are correctly left alone: true negatives. One threshold gives one confusion matrix, not yet a curve.", + "at": 160, + "duration": 52 + }, + { + "id": "rates", + "narrator": "guide", + "text": "The true positive rate asks: of all the actual frauds, what fraction did we catch? Two out of three. The false positive rate asks: of all the legitimate payments, what fraction did we wrongly flag? One out of three. An R O C curve, short for receiver operating characteristic, plots false positive rate across and true positive rate up. Our threshold becomes this single point.", + "at": 212, + "duration": 54 + }, + { + "id": "sweep", + "narrator": "guide", + "text": "Start above the highest score: nothing is flagged, so both rates are zero. Now lower the threshold past one payment at a time. Each fraud steps the curve up by a third; each legitimate payment steps it right by a third. At the bottom, everything is flagged, and we reach one, one. That staircase is the R O C curve.", + "at": 266, + "duration": 50 + }, + { + "id": "area", + "narrator": "guide", + "text": "Now shade the area under the curve. Each legitimate payment adds a vertical strip, one third wide. The heights are one third, one, and one. So the area is one ninth plus one third plus one third: seven ninths, about zero point seven eight. The empty corner above the first strip is payment B's mistake. And A U C is this area, not the accuracy at any one threshold.", + "at": 316, + "duration": 56 + }, + { + "id": "pairs", + "narrator": "guide", + "text": "There's a second way to get the same answer. Pair every fraud with every legitimate payment, and give a point whenever the fraud scores higher. Three frauds times three legitimate payments makes nine pairs. A beats all three. C and D each beat two, and both lose to B. Seven wins out of nine: exactly seven ninths again.", + "at": 372, + "duration": 49 + }, + { + "id": "bridge", + "narrator": "guide", + "text": "Why do they agree? Take legitimate payment B, scored zero point eight. Only one of the three frauds outranks it, so its strip is one third tall. Legitimate payments E and F are outranked by all three frauds, so their strips are full height. Averaging the strip heights counts exactly the winning pairs. That's the bridge from area to probability.", + "at": 421, + "duration": 50 + }, + { + "id": "rigor", + "narrator": "guide", + "text": "Here is the exact statement. Pick one random fraud and one random legitimate payment. R O C area is the probability that the fraud scores higher, with ties counting half. On a finite sample, that is just the fraction of winning pairs, like our seven out of nine. And because the horizontal axis is false positive rate, that is what the area integrates over, not the threshold.", + "at": 471, + "duration": 55 + }, + { + "id": "ties", + "narrator": "guide", + "text": "Ties need a rule. Take a separate tiny example: one fraud and one legitimate payment, both scored zero point five. Any threshold flags both or neither, so the R O C curve jumps straight from zero, zero to one, one. That diagonal has area one half, the same as giving the tied pair half credit. So a model that gives everything the same score has an A U C of one half.", + "at": 526, + "duration": 58 + }, + { + "id": "baselines", + "narrator": "guide", + "text": "A perfect ranking climbs to the top before moving right, and fills the whole square. Random ordering has an expected area of one half. A completely reversed ranking scores zero. Below one half can signal a flipped score direction, but don't flip a model just because of your test set. And an A U C of zero point eight does not mean eighty percent of predictions are correct.", + "at": 584, + "duration": 56 + }, + { + "id": "calibration", + "narrator": "guide", + "text": "Now square every score. The numbers change, but the order doesn't, so the R O C curve and its area stay at seven ninths. A U C can't tell you which set of numbers makes better probabilities. A fixed cutoff has to move too: zero point six five becomes its square, about zero point four two. Check calibration separately, and pick the threshold from the costs of mistakes.", + "at": 640, + "duration": 56 + }, + { + "id": "imbalance", + "narrator": "guide", + "text": "Suppose a threshold catches eighty percent of frauds and flags ten percent of legitimate payments. With a hundred of each, that's eighty true alarms and ten false ones: about eighty nine percent precision. Now take ten frauds among nine hundred ninety legitimate payments. The same rates give eight true alarms and ninety nine false ones: only seven and a half percent precision. The R O C point never moved: eighty percent, ten percent. Precision did, because prevalence, the Greek letter pi, changes what the flagged pile is made of.", + "at": 696, + "duration": 70 + }, + { + "id": "pr", + "narrator": "guide", + "text": "Back to our six payments. Recall is just another name for the true positive rate. Precision asks a different question: of the payments we flagged, how many are really fraud? A precision recall curve plots recall across and precision up, as we sweep the same threshold. Each fraud we find raises recall; each legitimate payment we flag drags precision down. Before anything is flagged, precision is undefined, so plots usually start the curve at one.", + "at": 766, + "duration": 60 + }, + { + "id": "ap", + "narrator": "guide", + "text": "A common summary is average precision, or A P. Each new fraud raises recall by one third. At those moments, precision is one, then two thirds, then three quarters. Weight each by its recall step and add them up. Twenty-nine over thirty-six, about zero point eight one. Same predictions as our R O C area of seven ninths, but a different question, and a different number.", + "at": 826, + "duration": 54 + }, + { + "id": "conventions", + "narrator": "guide", + "text": "Average precision sums recall steps times precision: zero point eight oh six. Join the raw precision recall points with straight lines and take trapezoids, and you get zero point seven six four. Those straight lines aren't even a valid interpolation in precision recall space. Other benchmarks use an interpolated envelope: at each recall, the best precision at that recall or higher. Here it lifts the middle step from two thirds to three quarters. And on large samples, a random ranker's baseline precision is the prevalence: one half here, only because half our payments are fraud. Always name the convention.", + "at": 880, + "duration": 76 + }, + { + "id": "partial", + "narrator": "guide", + "text": "Say you can tolerate almost no false alarms. Partial A U C keeps only false positive rates up to zero point one. Here the raw area is one thirtieth, out of a possible one tenth. Scikit-learn's max F P R setting rescales that so one half is chance and one is perfect: about zero point six five. Always state the range, and whether it was rescaled.", + "at": 956, + "duration": 54 + }, + { + "id": "multiclass", + "narrator": "guide", + "text": "Now suppose a second model sorts confirmed frauds into three types. One versus rest turns each class into its own binary problem, with its own R O C area; one versus one compares pairs of classes instead. The macro average weights the classes equally: about zero point seven eight. Weighting by class size gives zero point nine one, because the biggest class is the easiest. Same model, two honest numbers with different priorities. And when an example can carry several labels, a micro average pools every example label pair into one curve; that is not an average of areas.", + "at": 1010, + "duration": 76 + }, + { + "id": "letters", + "narrator": "guide", + "text": "Outside classification, the same letters show up on different curves. In search, average precision rewards relevant results near the top, and mean average precision averages it over queries. With three relevant results, at ranks one, three, and four, it's our twenty-nine over thirty-six again. Object detection first matches each predicted box to a true one by their overlap, called I O U. These two boxes overlap by two thirds: a match at one half, a miss at three quarters. Only then does it build precision recall curves, so Coco's A P is not an R O C area. Survival models compute an A U C at each time horizon t. A machine that failed by t is a case, one still running at t is a control, and one last seen before t is unknown, not a negative. And area under a learning curve measures performance across a training budget, not ranking at all.", + "at": 1086, + "duration": 113 + }, + { + "id": "practice", + "narrator": "guide", + "text": "In practice, feed the metric scores, not hard labels, and make sure larger means more likely positive. Evaluate on held-out data, and report uncertainty that matches how you sampled. Look at the curve and at important subgroups, not just one number. Then choose a threshold from your costs and your prevalence. No kind of A U C chooses that for you.", + "at": 1199, + "duration": 51 + }, + { + "id": "recap", + "narrator": "guide", + "text": "Same six payments, two curves, and two different numbers: seven ninths, and twenty-nine over thirty-six. Neither is wrong; they answer different questions. R O C area asks whether frauds outrank legitimate payments. Precision recall asks how clean the flagged pile stays as recall grows. So when you hear A U C, ask: which curve, which positive class, which population, which range, and which average?", + "at": 1250, + "duration": 53 + } + ] +} diff --git a/videos/auc-ml/src/roc.tsx b/videos/auc-ml/src/roc.tsx new file mode 100644 index 0000000..4138796 --- /dev/null +++ b/videos/auc-ml/src/roc.tsx @@ -0,0 +1,916 @@ +import { ITEMS, NEG, POS, ROC, STATES } from "./data"; +import { + Arrow, + appear, + C, + Caption, + Card, + Chip, + clamp, + Eq, + ease, + In, + Plot, + Scores, + SVG, + Text, +} from "./visuals"; + +export type LineAt = (k: number, offsetSeconds?: number, fraction?: number) => number; +type SceneProps = { id: string; f: number; duration: number; L: LineAt }; +const slot = (i: number) => 76 + i * 186; // chip x for rank i + +/** Fraud × legitimate pair grid. `shown(i, j)` is 0..1 mark visibility, `hi(j)` highlights a column. */ +export function Pairs({ + x = 770, + y = 92, + cell = 84, + cellsIn = () => 1, + shown = () => 1, + hi = () => 0, + headers = 1, +}: { + x?: number; + y?: number; + cell?: number; + cellsIn?: (i: number, j: number) => number; + shown?: (i: number, j: number) => number; + hi?: (j: number) => number; + headers?: number; +}) { + const g = cell + 8; + return ( + + + + legitimate payment (−) + + {NEG.map((n, j) => ( + + {n.id} {n.score.toFixed(1)} + + ))} + {POS.map((p, i) => ( + + {p.id} {p.score.toFixed(1)} + + ))} + + {POS.map((p, i) => + NEG.map((n, j) => { + const win = p.score > n.score, + s = shown(i, j), + h = hi(j), + inP = cellsIn(i, j); + return ( + + 0 ? C.gold : C.line} + strokeWidth={1 + h * 2.5} + /> + + + {win ? "✓" : "✗"} + + + ); + }), + )} + + ); +} + +export function RocScenes({ id, f, L }: SceneProps) { + const at = (k: number, o = 0, fr = 0) => L(k, o, fr); + const A = (k: number, o = 0, dur = 14) => appear(f, at(k, o), dur); + + if (id === "intro") { + // Unsorted arrival → sorted queue → truth revealed → the one-number promise. + const order = [3, 5, 0, 4, 1, 2]; // display slot of rank i before sorting + const sort = ease((f - at(1, 1.2)) / 40), + drop = 150 * (1 - ease((f - at(3, 0)) / 26)); // queue sits centred until the AUC row arrives + return ( + <> + + + + + REVIEW FIRST + + + REVIEW LAST + + + + {ITEMS.map((item, i) => { + const p = appear(f, 8 + order[i] * 5, 16); + const xx = slot(order[i]) + (slot(i) - slot(order[i])) * sort, + // Chips moving right arc up, chips moving left arc down, so crossing scores stay legible. + lift = Math.sin(Math.PI * sort) * Math.sign(slot(order[i]) - slot(i)) * 64; + return ( + + + + ); + })} + + + B is legitimate, yet it ranks above two frauds. + + + + + + AUC + + + area under the curve + + + one number for the quality of the whole ordering + + + + + You'll compute it by hand from these six payments. + + ); + } + + if (id === "map") { + const cards = [ + { t: "Scores", s: "A .90 · B .80 · …", c: C.ink }, + { t: "Threshold", s: "flag if score ≥ t", c: C.gold }, + { t: "Curve", s: "one point per threshold", c: C.pos }, + { t: "Area", s: "one number for the curve", c: C.pos }, + ]; + return ( + + {cards.map((v, i) => ( + + + + + + + {i < 3 && } + + ))} + + + Same six payments, two kinds of curve + + + ROC curve + + + precision–recall curve + + + + ); + } + + if (id === "scores") { + const focus = A(3, 0.6, 18); + return ( + + {ITEMS.map((item, i) => ( + + + + ))} + + + + + higher score = more suspicious + + + order matters; the numbers need not be probabilities + + + + + + + + B outranks C and D + + + + + + → this mistake becomes missing area + + + + ); + } + + if (id === "threshold") { + const cutX = slot(3) - 13; + const cells = [ + { k: 2, label: "TP", who: "A, C", n: 2, col: 0, row: 0, c: C.pos }, + { k: 3, label: "FP", who: "B", n: 1, col: 0, row: 1, c: C.neg }, + { k: 4, label: "FN", who: "D", n: 1, col: 1, row: 0, c: C.pos }, + { k: 5, label: "TN", who: "E, F", n: 2, col: 1, row: 1, c: C.neg }, + ]; + const gx = 520, + gy = 240, + cw = 260, + ch = 82; + return ( + <> + + (i < 3 ? appear(f, at(1, 0.3) + i * 6, 10) : 0)} /> + + + + threshold 0.65 + + + + + flagged + + + not flagged + + + actual fraud + + + + actually legit − + + + {cells.map((v) => { + const p = A(v.k, 0.4), + x = gx + v.col * (cw + 10), + y = gy + v.row * (ch + 10); + return ( + + + + + + {v.label} + + + {v.who} + + + {v.n} + + + + ); + })} + + One threshold → one confusion matrix. Not yet a curve. + + ); + } + + if (id === "rates") { + const dots = (list: typeof ITEMS, hit: (id: string) => boolean, x: number, y: number, p: number) => + list.map((it, i) => ( + + + + {it.id} + + + )); + const pt = A(5, 0.3, 18); + return ( + <> + + + + Of the actual frauds, how many did we catch? + + + {dots(POS, (i) => i !== "D", 108, 80, A(0, 1.4))} + + + Of the legitimate payments, how many did we flag? + + + {dots(NEG, (i) => i === "B", 108, 250, A(2, 1.6))} + + + + + + (1/3, 2/3) + + + + + + + ROC = receiver operating characteristic: FPR across, TPR up. + + + ); + } + + if (id === "sweep") { + // The walk spans the two sentences that explain it, finishing on "At the bottom". + const k = clamp((f - at(2, 0.2)) / (at(3, 0.6) - at(2, 0.2))) * 6.3, + n = Math.min(6, Math.floor(k + 1e-6)), + step = n === 0 ? 0 : n - 1 + ease(clamp((k - n) / 0.3)), + s = STATES[n], + rowY = (i: number) => 52 + i * 52, + lineY = rowY(0) - 4 + step * 52; // rests in the gap below the last flagged row + return ( + <> + + + + + ROC curve + + + {ITEMS.map((item, i) => ( + + + + {item.id} {item.positive ? "+" : "−"} {item.score.toFixed(2)} + + + {i < n ? (item.positive ? "↑ up 1/3" : "→ right 1/3") : "not flagged"} + + + ))} + + + + threshold + + + + {`TPR ${s.tp}/3 · FPR ${s.fp}/3`} + + + {n === 0 + ? "above 0.90" + : n === 6 + ? "below 0.10" + : `between ${ITEMS[n - 1].score.toFixed(2)} and ${ITEMS[n].score.toFixed(2)}`} + + + Fraud (+): up 1/3. Legitimate (−): right 1/3. Both rates finish at 1. + + ); + } + + if (id === "area" || id === "bridge") { + const bridge = id === "bridge", + W = bridge ? 400 : 460, + H = bridge ? 230 : 300, + X = 100, + Y = 50; + const heights = [1 / 3, 1, 1]; + const fillOf = (j: number) => (bridge ? 1 : appear(f, at(2, 0.2) + j * 30, 18)); + const hiOf = (j: number) => (bridge ? (j === 0 ? A(1, 0.3) * (1 - A(3, 0)) : A(3, 0.4)) : 0); + return ( + <> + + + {heights.map((h, j) => ( + + + + + + ))} + {!bridge && ( + + + + + B's mistake + + + 2/9 missing + + + )} + + {bridge ? ( + <> + (j === 0 ? A(1, 0.3) * (1 - A(3, 0)) : A(3, 0.4))} /> + + + 1/3 tall + + + column B: 1 win of 3 + + + + + columns E, F: 3 wins of 3 + + + + ) : ( + <> + {["1/3 × 1/3 = 1/9", "1/3 × 1 = 1/3", "1/3 × 1 = 1/3"].map((t, j) => ( + + + {t} + + + {["strip B", "strip E", "strip F"][j]} + + + ))} + + + + = 7/9 ≈ 0.778 + + + + + + width 1/3 + + + + )} + + {bridge && ( + + )} + + {bridge + ? "Strip height = fraction of frauds that outrank that legitimate payment." + : "AUC is this area, not the accuracy at any one threshold. Both axes are fractions, so it is unitless."} + + + ); + } + + if (id === "pairs") { + const wins = POS.reduce( + (a, p, i) => a + NEG.reduce((b, n, j) => b + (p.score > n.score ? (markP(i, j) > 0.5 ? 1 : 0) : 0), 0), + 0, + ); + function markP(i: number, j: number) { + return i === 0 ? appear(f, at(3, 0.2) + j * 6, 10) : appear(f, at(4, 0.2) + (i - 1) * 14 + j * 5, 10); + } + return ( + <> + + + + 3 frauds + + + × 3 legitimate + + + = 9 pairs + + + Math.max(0.35 * A(0, 0.6), appear(f, at(2, 0.4) + (i * 3 + j) * 3, 10))} + shown={markP} + hi={(j) => (j === 0 ? A(4, 1.8) : 0)} + /> + + + {wins} {wins === 1 ? "win" : "wins"} + + + + + ↑ both losses: B + + + + + 7 / 9 pairs + + + = ROC area 7/9 + + + + + ); + } + + if (id === "rigor") { + const focus = (k: number) => A(k, 0) * (1 - 0.6 * A(k + 1, 0)); + return ( + <> + + + + + > ? + + + + random fraud + + + random legitimate + + + + S_-)+\tfrac12\,P(S_+=S_-)`} /> + + + + ); + } + + if (id === "ties") { + const draw = ease((f - at(2, 1.2)) / 40); + return ( + <> + + + + + + + + 1/2 + + + credit for the tied pair + + + + + + area 1/2 + + + + + All scores tied → AUC = 1/2 (when both classes are present). + + + ); + } + + if (id === "baselines") { + const panels = [ + { + name: "Perfect ranking", + a: "AUC = 1", + points: [ + [0, 0], + [0, 1], + [1, 1], + ], + c: C.pos, + }, + { + name: "Random order", + a: "expected AUC = 0.5", + points: [ + [0, 0], + [1, 1], + ], + c: C.gold, + }, + { + name: "Reversed ranking", + a: "AUC = 0", + points: [ + [0, 0], + [1, 0], + [1, 1], + ], + c: C.neg, + }, + ]; + return ( + <> + + {panels.map((v, i) => { + const p = A(i, 0.2), + d = ease((f - at(i, 0.4)) / 40) * (v.points.length - 1); + return ( + + + {v.name} + + + + {v.a} + + + ); + })} + + + Below 0.5 may mean a flipped score; don't flip a model because of the test set. + + + AUC 0.8 ≠ “80% of predictions are correct.” + + + ); + } + + if (id === "calibration") { + const cutX = slot(3) - 13, + cut = A(3, 0.2); + return ( + <> + + + + + {ITEMS.map((_, i) => ( + + ))} + + + s → s² + + + + + + Same order → same ROC curve → AUC still 7/9 + + + + + + + + cutoff 0.65 → 0.65² = 0.4225 + + + + + Check calibration separately; choose the threshold from the costs of mistakes. + + + ); + } + return null; +} diff --git a/videos/auc-ml/src/score.ts b/videos/auc-ml/src/score.ts new file mode 100644 index 0000000..1fe3a60 --- /dev/null +++ b/videos/auc-ml/src/score.ts @@ -0,0 +1,199 @@ +import { defineScore, type Note, note } from "@archastro/clapper-music"; +import beats from "./beats.json"; +import timing from "./timing.json"; + +// A quiet bed for a narrated lesson. 60 BPM, so one beat is one second and every +// position below reads directly in film seconds. Music rises in the title card, +// the breaths between chapters and the end card, and sits well under speech. +type Timing = Record; +type Beat = { id: string; chapter?: string }; +const T = timing as Timing; +const B = beats as Beat[]; +const scenes: { id: string; part: number; start: number; seconds: number; speech?: [number, number] }[] = []; +{ + let t = 0, + part = 0; + for (const b of B) { + const s = T[b.id] ?? { seconds: 8, lines: [] }; + if (b.chapter) part = Number(b.chapter.slice(0, 2)) - 1; + const first = s.lines[0], + last = s.lines.at(-1); + scenes.push({ + id: b.id, + part, + start: t, + seconds: s.seconds, + speech: first && last ? [t + first.at, t + last.at + last.dur] : undefined, + }); + t += s.seconds; + } +} +const TOTAL = scenes.reduce((a, s) => a + s.seconds, 0); +const sceneAt = (t: number) => [...scenes].reverse().find((s) => s.start <= t) ?? scenes[0]; +const startOf = (id: string) => scenes.find((s) => s.id === id)?.start ?? 0; + +const NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; +const midi = (p: string) => { + const m = /^([A-G]#?)(-?\d)$/.exec(p)!; + return NAMES.indexOf(m[1]) + 12 * (Number(m[2]) + 1); +}; + +// Dmaj9 → Bm11 → Gmaj7(#11) → Asus: slow and open, no strong cadence until the end card. +const HARMONY = [ + { bass: "D2", pad: ["A3", "D4", "F#4"], top: ["E5", "F#5", "A5"] }, + { bass: "B2", pad: ["F#3", "D4", "E4"], top: ["D5", "F#5", "A5"] }, + { bass: "G2", pad: ["B3", "D4", "F#4"], top: ["C#5", "F#5", "B5"] }, + { bass: "A2", pad: ["E3", "A3", "D4"], top: ["B4", "E5", "A5"] }, +]; +const BAR = 8; // beats (= seconds) per chord +// Part 4 (precision–recall) lifts a whole step: a new curve, a new colour. +const shift = (t: number) => (sceneAt(t).part === 3 ? 2 : 0); +const END = startOf("end"); +const LOOP_END = END - 4; // then an explicit A → D cadence on the end card + +const piano: Note[] = [], + strings: Note[] = [], + bass: Note[] = [], + harp: Note[] = []; +// The piano figure changes with each part of the lesson instead of every bar: [offset, top index, length]. +const FIGURES = [ + [ + [0, 0, 3], + [1.5, 1, 2.5], + [3, 2, 4], + ], // intuition: rising broken chord + [ + [0, 2, 3], + [2, 1, 3], + [4, 0, 3.5], + ], // build ROC: the same notes, falling + [ + [0, 0, 3], + [3, 2, 4], + ], // ranking math: sparser, leaves room for formulas + [ + [0, 1, 2], + [1, 2, 2], + [2.5, 0, 3], + ], // precision–recall: a new shape in the new key + [[0, 2, 4]], // other contexts: one bell-like note per chord + [ + [0, 0, 3], + [1.5, 1, 2.5], + [3, 2, 4], + ], // use it: the opening figure returns +]; +for (let i = 0; i * BAR < LOOP_END; i++) { + const h = HARMONY[i % 4], + at = i * BAR, + len = Math.min(BAR, LOOP_END - at), + s = shift(at), + scene = sceneAt(at), + // Softer under the opening hook and the one-note "bells" of the survey part. + v = 36 + (i % 2) * 4 - (scene.part === 0 ? 5 : scene.part === 4 ? 8 : 0), + p = (name: string) => midi(name) + s; + for (const [dt, idx, d] of FIGURES[scene.part]) + if (dt < len) piano.push(note(p(h.top[idx]), at + dt, Math.min(d, len - dt), v - idx * 2)); + if (i % 2 && scene.part !== 2 && len > 5) + piano.push(note(p(h.pad[2]), at + 5, Math.min(3, len - 5), v - 8)); + // Strings rest under the dense survey of other curves, then return. + // Rest only for bars wholly inside it, so the pad is back as soon as the next scene starts. + if (scene.id !== "letters" || sceneAt(at + len - 0.01).id !== "letters") + strings.push(...h.pad.map((n) => note(p(n), at, len, 44))); + bass.push(note(p(h.bass), at, len, 40)); +} +// Cadence: A (with its third) for four seconds, resolving to D as the end card lands. +strings.push(...["E3", "A3", "C#4"].map((n) => note(n, LOOP_END, 4, 46))); +bass.push(note("A2", LOOP_END, 4, 42)); +piano.push(note("C#5", LOOP_END + 0.5, 3, 34), note("E5", LOOP_END + 2, 2, 32)); +strings.push(...["D3", "A3", "F#4"].map((n) => note(n, END, 9, 48))); +bass.push(note("D2", END, 9, 44)); +piano.push(...["D4", "F#4", "A4", "D5"].map((n, k) => note(n, END + 0.1 + k * 0.05, 6, 44 - k * 2))); +harp.push(...["D4", "A4", "D5", "F#5", "A5"].map((n, k) => note(n, END + 0.3 + k * 0.22, 6, 54 - k * 3))); + +// Title card: a rising figure that lands on the wordmark. +const title = startOf("title"); +["D5", "F#5", "A5", "C#6", "E6"].forEach((n, k) => + harp.push(note(n, title + 0.4 + k * 0.32, 3.5, 58 - k * 3)), +); +// Each new part gets one soft harp glint on its first cut, taken from the chord sounding then. +B.forEach((b, i) => { + if (i === 0 || !b.chapter || b.chapter.slice(0, 2) === B[i - 1].chapter?.slice(0, 2)) return; + const t = startOf(b.id), + h = HARMONY[Math.floor(t / BAR) % 4]; + harp.push(note(midi(h.top[2]) + shift(t), t, 4, 46)); +}); + +// Ducking: full on the title and end cards, a breath between chapters, low under speech. +const DUCK = 0.3, + GAP = 0.6, + CARD = 1.4, + RAMP = 0.7; +const auto: { at: number; value: number }[] = [ + { at: 0, value: 0 }, + { at: 0.6, value: GAP }, +]; +for (const s of scenes) { + if (!s.speech) { + auto.push( + { at: s.start + 0.3, value: CARD }, + { at: s.start + s.seconds - (s.id === "end" ? 4 : 1.2), value: CARD }, + ); + continue; + } + const [a, b] = s.speech; + auto.push({ at: Math.max(a - RAMP, auto.at(-1)!.at + 0.05), value: GAP }); + auto.push({ at: a, value: DUCK }); + auto.push({ at: b + 0.2, value: DUCK }); + auto.push({ at: b + 0.2 + RAMP * 1.5, value: GAP }); +} +auto.push({ at: TOTAL - 0.05, value: 0 }); +// Deduplicate positions that collide when one scene's speech follows closely after another's. +const gain = auto.sort((x, y) => x.at - y.at).filter((p, i, a) => i === 0 || p.at - a[i - 1].at > 0.04); + +export default defineScore({ + title: "AUC — study bed", + tempo: 60, + meter: [4, 4], + seed: 7, + length: TOTAL + 6, + tail: 4, + tracks: [ + { + id: "piano", + instrument: "vsupright1", + gain: 0.75, + reverb: 0.45, + pan: -0.12, + clips: [{ notes: piano }], + humanize: { timing: 0.012, velocity: 4 }, + gainAutomation: gain, + }, + { + id: "strings", + instrument: "viola-ens-sus-vib-quiet", + gain: 0.17, + reverb: 0.5, + pan: 0.15, + clips: [{ notes: strings }], + gainAutomation: gain, + }, + { + id: "bass", + instrument: "cello-ens-sus-vib-quiet", + gain: 0.12, + reverb: 0.35, + clips: [{ notes: bass }], + gainAutomation: gain, + }, + { + id: "harp", + instrument: "harp", + gain: 0.6, + reverb: 0.55, + pan: 0.1, + clips: [{ notes: harp }], + gainAutomation: gain, + }, + ], +}); diff --git a/videos/auc-ml/src/theme.css b/videos/auc-ml/src/theme.css new file mode 100644 index 0000000..171dcd9 --- /dev/null +++ b/videos/auc-ml/src/theme.css @@ -0,0 +1,133 @@ +@import "./fonts/fonts.css"; +* { + box-sizing: border-box; +} +.film { + position: absolute; + inset: 0; + background: #f4f1e8; + color: #193239; + overflow: hidden; + font-family: "Schibsted Grotesk", sans-serif; + -webkit-font-smoothing: antialiased; +} +/* Faint paper grid: a quiet reference surface for every diagram. */ +.film::before { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(#193239 1px, transparent 1px), linear-gradient(90deg, #193239 1px, transparent 1px); + background-size: 48px 48px; + opacity: 0.035; +} +.stage { + width: 1280px; + height: 720px; + position: absolute; + transform: scale(1.5); + transform-origin: 0 0; +} +.chapter { + position: absolute; + left: 76px; + top: 30px; + color: #0b6d60; + font: + 500 15px "Fragment Mono", + monospace; + letter-spacing: 1.4px; + text-transform: uppercase; +} +.title { + position: absolute; + left: 76px; + top: 54px; + width: 1130px; + margin: 0; + line-height: 1.08; + font-size: 42px; + font-weight: 650; + letter-spacing: -1.1px; +} +.subtitle { + position: absolute; + left: 77px; + top: 112px; + max-width: 1120px; + font-size: 22px; + line-height: 1.25; + color: #3d5156; + margin: 0; +} +.canvas { + position: absolute; + top: 168px; + left: 0; + width: 1280px; + height: 490px; +} +.caption { + position: absolute; + left: 76px; + right: 76px; + top: 432px; + padding: 7px 16px; + background: #e6ece2; + border-left: 4px solid #0b6d60; + font-size: 24px; + line-height: 1.22; +} +.equation .katex-display { + margin: 0; +} +.equation .katex { + font-size: 1em; +} +/* Film-wide chapter rail: where we are in the lesson. */ +.rail { + position: absolute; + left: 76px; + right: 76px; + bottom: 10px; + height: 30px; + display: flex; + gap: 8px; +} +.rail .seg { + position: relative; + flex: 1; + font: + 500 18px "Fragment Mono", + monospace; + letter-spacing: 0.5px; + text-transform: uppercase; + color: #8a9894; + padding-top: 8px; +} +.rail .seg::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: #d6ddd6; +} +.rail .seg .fill { + position: absolute; + top: 0; + left: 0; + height: 2px; + background: #0b6d60; +} +.rail .seg.active { + color: #0b6d60; +} +.serif { + font-family: "Instrument Serif", serif; + font-weight: 400; +} +.mono { + font-family: "Fragment Mono", monospace; +} diff --git a/videos/auc-ml/src/timing.json b/videos/auc-ml/src/timing.json new file mode 100644 index 0000000..88d655d --- /dev/null +++ b/videos/auc-ml/src/timing.json @@ -0,0 +1,720 @@ +{ + "intro": { + "seconds": 25, + "take": { + "at": 0.6, + "dur": 22.525 + }, + "lines": [ + { + "at": 0.9, + "dur": 2.74 + }, + { + "at": 3.64, + "dur": 4.27 + }, + { + "at": 7.91, + "dur": 4.17 + }, + { + "at": 12.08, + "dur": 4.68 + }, + { + "at": 16.76, + "dur": 5.91 + } + ] + }, + "title": { + "seconds": 7, + "lines": [] + }, + "map": { + "seconds": 26, + "take": { + "at": 0.6, + "dur": 23.35 + }, + "lines": [ + { + "at": 0.88, + "dur": 2.1 + }, + { + "at": 2.98, + "dur": 3.6 + }, + { + "at": 6.58, + "dur": 3.99 + }, + { + "at": 10.57, + "dur": 3.45 + }, + { + "at": 14.02, + "dur": 4.38 + }, + { + "at": 18.4, + "dur": 5.05 + } + ] + }, + "scores": { + "seconds": 27, + "take": { + "at": 0.6, + "dur": 24.425 + }, + "lines": [ + { + "at": 0.91, + "dur": 3.92 + }, + { + "at": 4.83, + "dur": 3.99 + }, + { + "at": 8.82, + "dur": 5.77 + }, + { + "at": 14.59, + "dur": 5.64 + }, + { + "at": 20.23, + "dur": 1.7 + }, + { + "at": 21.93, + "dur": 2.6 + } + ] + }, + "threshold": { + "seconds": 26.5, + "take": { + "at": 0.6, + "dur": 23.75 + }, + "lines": [ + { + "at": 0.89, + "dur": 2.91 + }, + { + "at": 3.8, + "dur": 2.1 + }, + { + "at": 5.9, + "dur": 3.1 + }, + { + "at": 9, + "dur": 4.13 + }, + { + "at": 13.13, + "dur": 3.3 + }, + { + "at": 16.43, + "dur": 3.94 + }, + { + "at": 20.37, + "dur": 3.5 + } + ] + }, + "rates": { + "seconds": 30, + "take": { + "at": 0.6, + "dur": 27.125 + }, + "lines": [ + { + "at": 0.89, + "dur": 5.75 + }, + { + "at": 6.64, + "dur": 1.53 + }, + { + "at": 8.17, + "dur": 6.7 + }, + { + "at": 14.87, + "dur": 1.66 + }, + { + "at": 16.53, + "dur": 8.64 + }, + { + "at": 25.17, + "dur": 2.1 + } + ] + }, + "sweep": { + "seconds": 24.5, + "take": { + "at": 0.6, + "dur": 21.65 + }, + "lines": [ + { + "at": 0.87, + "dur": 5.18 + }, + { + "at": 6.05, + "dur": 3.51 + }, + { + "at": 9.56, + "dur": 6.07 + }, + { + "at": 15.63, + "dur": 4 + }, + { + "at": 19.63, + "dur": 2.16 + } + ] + }, + "area": { + "seconds": 29.5, + "take": { + "at": 0.6, + "dur": 25.75 + }, + "lines": [ + { + "at": 0.91, + "dur": 2.45 + }, + { + "at": 3.36, + "dur": 4.33 + }, + { + "at": 7.69, + "dur": 2.79 + }, + { + "at": 10.48, + "dur": 7 + }, + { + "at": 17.48, + "dur": 4.66 + }, + { + "at": 22.14, + "dur": 3.74 + } + ] + }, + "pairs": { + "seconds": 25.5, + "take": { + "at": 0.6, + "dur": 21.825 + }, + "lines": [ + { + "at": 0.93, + "dur": 2.79 + }, + { + "at": 3.72, + "dur": 6 + }, + { + "at": 9.72, + "dur": 4.17 + }, + { + "at": 13.89, + "dur": 1.75 + }, + { + "at": 15.64, + "dur": 3.22 + }, + { + "at": 18.86, + "dur": 3.07 + } + ] + }, + "bridge": { + "seconds": 27, + "take": { + "at": 0.6, + "dur": 23.25 + }, + "lines": [ + { + "at": 0.91, + "dur": 1.67 + }, + { + "at": 2.58, + "dur": 3.79 + }, + { + "at": 6.37, + "dur": 4.77 + }, + { + "at": 11.14, + "dur": 6.08 + }, + { + "at": 17.22, + "dur": 3.88 + }, + { + "at": 21.1, + "dur": 2.25 + } + ] + }, + "rigor": { + "seconds": 29.5, + "take": { + "at": 0.6, + "dur": 25.75 + }, + "lines": [ + { + "at": 0.9, + "dur": 2.3 + }, + { + "at": 3.2, + "dur": 4.23 + }, + { + "at": 7.43, + "dur": 5.9 + }, + { + "at": 13.33, + "dur": 5.76 + }, + { + "at": 19.09, + "dur": 6.76 + } + ] + }, + "ties": { + "seconds": 30, + "take": { + "at": 0.6, + "dur": 26.375 + }, + "lines": [ + { + "at": 0.92, + "dur": 1.72 + }, + { + "at": 2.64, + "dur": 6.98 + }, + { + "at": 9.62, + "dur": 7.46 + }, + { + "at": 17.08, + "dur": 5.28 + }, + { + "at": 22.36, + "dur": 4.14 + } + ] + }, + "baselines": { + "seconds": 27, + "take": { + "at": 0.6, + "dur": 24.25 + }, + "lines": [ + { + "at": 0.88, + "dur": 5.11 + }, + { + "at": 5.99, + "dur": 3.72 + }, + { + "at": 9.71, + "dur": 3.09 + }, + { + "at": 12.8, + "dur": 6.56 + }, + { + "at": 19.36, + "dur": 5.01 + } + ] + }, + "calibration": { + "seconds": 28, + "take": { + "at": 0.6, + "dur": 25.25 + }, + "lines": [ + { + "at": 0.91, + "dur": 2.01 + }, + { + "at": 2.92, + "dur": 6.39 + }, + { + "at": 9.31, + "dur": 4.65 + }, + { + "at": 13.96, + "dur": 7.15 + }, + { + "at": 21.11, + "dur": 4.31 + } + ] + }, + "imbalance": { + "seconds": 39, + "take": { + "at": 0.6, + "dur": 34.925 + }, + "lines": [ + { + "at": 0.88, + "dur": 6.7 + }, + { + "at": 7.58, + "dur": 6.57 + }, + { + "at": 14.15, + "dur": 4.34 + }, + { + "at": 18.49, + "dur": 6.74 + }, + { + "at": 25.23, + "dur": 4.42 + }, + { + "at": 29.65, + "dur": 5.4 + } + ] + }, + "pr": { + "seconds": 33.5, + "take": { + "at": 0.6, + "dur": 30.775 + }, + "lines": [ + { + "at": 0.88, + "dur": 2.09 + }, + { + "at": 2.97, + "dur": 4.03 + }, + { + "at": 7, + "dur": 5.92 + }, + { + "at": 12.92, + "dur": 6.4 + }, + { + "at": 19.32, + "dur": 6.34 + }, + { + "at": 25.66, + "dur": 5.22 + } + ] + }, + "ap": { + "seconds": 29.5, + "take": { + "at": 0.6, + "dur": 25.6 + }, + "lines": [ + { + "at": 0.88, + "dur": 3.5 + }, + { + "at": 4.38, + "dur": 3.24 + }, + { + "at": 7.62, + "dur": 4.73 + }, + { + "at": 12.35, + "dur": 3.28 + }, + { + "at": 15.63, + "dur": 4.2 + }, + { + "at": 19.83, + "dur": 5.86 + } + ] + }, + "conventions": { + "seconds": 45.5, + "take": { + "at": 0.6, + "dur": 41.575 + }, + "lines": [ + { + "at": 0.92, + "dur": 5.57 + }, + { + "at": 6.49, + "dur": 7.86 + }, + { + "at": 14.35, + "dur": 5.66 + }, + { + "at": 20.01, + "dur": 7.19 + }, + { + "at": 27.2, + "dur": 3.98 + }, + { + "at": 31.18, + "dur": 9.11 + }, + { + "at": 40.29, + "dur": 1.41 + } + ] + }, + "partial": { + "seconds": 28.5, + "take": { + "at": 0.6, + "dur": 24.85 + }, + "lines": [ + { + "at": 0.9, + "dur": 3.12 + }, + { + "at": 4.02, + "dur": 5.32 + }, + { + "at": 9.34, + "dur": 4.37 + }, + { + "at": 13.71, + "dur": 8.55 + }, + { + "at": 22.26, + "dur": 2.72 + } + ] + }, + "multiclass": { + "seconds": 43.5, + "take": { + "at": 0.6, + "dur": 39.675 + }, + "lines": [ + { + "at": 0.91, + "dur": 4.73 + }, + { + "at": 5.64, + "dur": 10.21 + }, + { + "at": 15.85, + "dur": 5.09 + }, + { + "at": 20.94, + "dur": 6.02 + }, + { + "at": 26.96, + "dur": 3.9 + }, + { + "at": 30.86, + "dur": 8.93 + } + ] + }, + "letters": { + "seconds": 60.5, + "take": { + "at": 0.6, + "dur": 58.025 + }, + "lines": [ + { + "at": 0.93, + "dur": 4.55 + }, + { + "at": 5.48, + "dur": 7.93 + }, + { + "at": 13.41, + "dur": 6.54 + }, + { + "at": 19.95, + "dur": 6.37 + }, + { + "at": 26.32, + "dur": 6.33 + }, + { + "at": 32.65, + "dur": 6.47 + }, + { + "at": 39.12, + "dur": 4.51 + }, + { + "at": 43.63, + "dur": 9.14 + }, + { + "at": 52.77, + "dur": 5.32 + } + ] + }, + "practice": { + "seconds": 26, + "take": { + "at": 0.6, + "dur": 23.275 + }, + "lines": [ + { + "at": 0.93, + "dur": 6.97 + }, + { + "at": 7.9, + "dur": 5.59 + }, + { + "at": 13.49, + "dur": 4.11 + }, + { + "at": 17.6, + "dur": 3.77 + }, + { + "at": 21.37, + "dur": 2.01 + } + ] + }, + "recap": { + "seconds": 29.5, + "take": { + "at": 0.6, + "dur": 26.975 + }, + "lines": [ + { + "at": 0.9, + "dur": 6.86 + }, + { + "at": 7.76, + "dur": 3.27 + }, + { + "at": 11.03, + "dur": 4.6 + }, + { + "at": 15.63, + "dur": 4.93 + }, + { + "at": 20.56, + "dur": 6.53 + } + ] + }, + "end": { + "seconds": 10, + "lines": [] + } +} diff --git a/videos/auc-ml/src/visuals.tsx b/videos/auc-ml/src/visuals.tsx new file mode 100644 index 0000000..31a792f --- /dev/null +++ b/videos/auc-ml/src/visuals.tsx @@ -0,0 +1,459 @@ +import { Latex } from "@archastro/clapper-core/latex"; +import type { CSSProperties, ReactNode } from "react"; +import { ITEMS, ROC } from "./data"; +export const C = { + bg: "#f4f1e8", + paper: "#fffefa", + ink: "#193239", + muted: "#3d5156", + faint: "#8a9894", + line: "#d6ddd6", + pos: "#006657", + posSoft: "#d9ede6", + neg: "#a3402b", + negSoft: "#f5ded5", + gold: "#8a5a00", + goldSoft: "#f6e7c4", + blue: "#24558e", + blueSoft: "#e0e9f4", + purple: "#624776", +}; +export const clamp = (x: number) => Math.max(0, Math.min(1, x)); +export const ease = (x: number) => { + const p = clamp(x); + return p * p * (3 - 2 * p); +}; +export const easeOut = (x: number) => 1 - (1 - clamp(x)) ** 3; +/** Eased 0→1 from frame `at` over `dur` frames. */ +export const appear = (f: number, at: number, dur = 14) => easeOut((f - at) / dur); +/** Fractional tick labels without vulgar-fraction glyphs (not in the house fonts). */ +export const fracLabel = (v: number) => + v === 0 + ? "0" + : v === 1 + ? "1" + : Math.abs(v - 1 / 3) < 1e-9 + ? "1/3" + : Math.abs(v - 2 / 3) < 1e-9 + ? "2/3" + : String(v); + +/** Fade + rise wrapper for SVG groups. */ +export function In({ + f, + at, + dur = 14, + dy = 10, + children, +}: { + f: number; + at: number; + dur?: number; + dy?: number; + children: ReactNode; +}) { + const p = appear(f, at, dur); + return ( + + {children} + + ); +} +/** Same, for HTML overlays. */ +export function HIn({ + f, + at, + dur = 14, + style, + children, +}: { + f: number; + at: number; + dur?: number; + style?: CSSProperties; + children: ReactNode; +}) { + const p = appear(f, at, dur); + return
    {children}
    ; +} +export function Text({ + x, + y, + children, + size = 24, + color = C.ink, + anchor = "start", + weight = 560, + mono = false, + opacity, +}: { + x: number; + y: number; + children: ReactNode; + size?: number; + color?: string; + anchor?: "start" | "middle" | "end"; + weight?: number; + mono?: boolean; + opacity?: number; +}) { + return ( + + {children} + + ); +} +export function Card({ + x, + y, + w = 300, + h = 115, + title, + sub, + color = C.pos, + fill = C.paper, +}: { + x: number; + y: number; + w?: number; + h?: number; + title: ReactNode; + sub?: ReactNode; + color?: string; + fill?: string; +}) { + return ( + + + + + {title} + + {sub && ( + +
    + {sub} +
    +
    + )} +
    + ); +} +export function Arrow({ + x1, + y1, + x2, + y2, + color = C.muted, + p = 1, + width = 2.5, +}: { + x1: number; + y1: number; + x2: number; + y2: number; + color?: string; + p?: number; + width?: number; +}) { + if (p <= 0) return null; + const ex = x1 + (x2 - x1) * p, + ey = y1 + (y2 - y1) * p; + const a = Math.atan2(y2 - y1, x2 - x1), + s = 11; + return ( + + + + + ); +} +export function Eq({ + x = 90, + y = 350, + w = 1100, + tex, + size = 30, + p = 1, + color = C.ink, +}: { + x?: number; + y?: number; + w?: number; + tex: string; + size?: number; + p?: number; + color?: string; +}) { + if (p <= 0) return null; + return ( +
    + + {tex} + +
    + ); +} +export function SVG({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} +/** One payment chip. */ +export function Chip({ + item, + x, + y, + w = 150, + h = 104, + value, + truth = 1, + flagged = 0, + dim = 0, + label = true, +}: { + item: (typeof ITEMS)[number]; + x: number; + y: number; + w?: number; + h?: number; + value?: number; + truth?: number; + flagged?: number; + dim?: number; + label?: boolean; +}) { + const c = item.positive ? C.pos : C.neg, + soft = item.positive ? C.posSoft : C.negSoft; + return ( + + + + + {label && ( + <> + + {item.id} + + + {item.positive ? "+ fraud" : "− legit"} + + + )} + + {(value ?? item.score).toFixed(2)} + + + ); +} +/** The six payments in rank order. */ +export function Scores({ + x = 76, + y = 40, + gap = 186, + w = 160, + squared = false, + truth = 1, + flagged = () => 0, + dim = () => 0, +}: { + x?: number; + y?: number; + gap?: number; + w?: number; + squared?: boolean; + truth?: number; + flagged?: (i: number) => number; + dim?: (i: number) => number; +}) { + return ( + + {ITEMS.map((item, i) => ( + + ))} + + ); +} +/** Path through points, revealed up to a fractional vertex index. */ +export function partialPath( + pts: [number, number][], + upto: number, + px: (v: number) => number, + py: (v: number) => number, +) { + if (upto <= 0 || pts.length === 0) return { d: "", end: pts[0] ?? [0, 0] }; + const n = Math.min(Math.floor(upto), pts.length - 1), + frac = upto - n; + const shown = pts.slice(0, n + 1).map((p) => [...p] as [number, number]); + if (frac > 0 && n + 1 < pts.length) { + const a = pts[n], + b = pts[n + 1]; + shown.push([a[0] + (b[0] - a[0]) * frac, a[1] + (b[1] - a[1]) * frac]); + } + return { + d: shown.map(([a, b], i) => `${i ? "L" : "M"}${px(a)} ${py(b)}`).join(" "), + end: shown.at(-1)!, + }; +} +export function Plot({ + x = 96, + y = 46, + w = 440, + h = 290, + points = ROC, + upto = points.length - 1, + color = C.pos, + fill = 0, + diagonal = 0, + xLabel = "False-positive rate", + yLabel = "True-positive rate", + dot = true, + ticks = [0, 1 / 3, 2 / 3, 1], + axes = 1, + children, +}: { + x?: number; + y?: number; + w?: number; + h?: number; + points?: [number, number][]; + upto?: number; + color?: string; + fill?: number; + diagonal?: number; + xLabel?: string; + yLabel?: string; + dot?: boolean; + ticks?: number[]; + axes?: number; + children?: ReactNode; +}) { + const px = (v: number) => x + v * w, + py = (v: number) => y + (1 - v) * h, + { d, end } = partialPath(points, upto, px, py); + return ( + + + + {ticks.map((v) => ( + + + + {fracLabel(v)} + + + {fracLabel(v)} + + + ))} + {yLabel && ( + + {yLabel} ↑ + + )} + {xLabel && ( + + {xLabel} → + + )} + + {diagonal > 0 && ( + + )} + {fill > 0 && d && ( + + )} + {children} + {d && ( + + )} + {dot && d && ( + + )} + + + ); +} +export function Caption({ + children, + color = C.pos, + p = 1, +}: { + children: ReactNode; + color?: string; + p?: number; +}) { + if (p <= 0) return null; + return ( +
    + {children} +
    + ); +} diff --git a/videos/auc-ml/transcript.md b/videos/auc-ml/transcript.md new file mode 100644 index 0000000..4cdac69 --- /dev/null +++ b/videos/auc-ml/transcript.md @@ -0,0 +1,87 @@ +## 00:00.0 — Who should be reviewed first? + +A fraud model scores every payment. You can only review a few today, so the highest scores go first. But how do you know that ordering is any good, before you pick a cutoff? One number answers that: the area under a curve, or A U C. You'll compute it by hand from these six payments, then see why the same three letters can mean very different things. + +## 00:32.0 — Scores → thresholds → curve → area + +Here is the whole journey. On held-out examples, the model produces scores. A threshold turns each score into a yes or no decision. Move the threshold, and each setting gives one point. Plot those points as a curve, then summarize the curve with its area. We'll use the same six payments for two kinds of curve, so you can see exactly what changes. + +## 00:58.0 — A score gives an ordering + +Here are the six payments, sorted from highest score to lowest. Plus means actual fraud; minus means legitimate. The scores only need to put payments in order; they need not be calibrated probabilities. Notice one mistake: payment B is legitimate, yet it ranks above two frauds, C and D. Keep B in mind. Its mistake will become a missing piece of area. + +## 01:25.0 — One threshold gives one decision + +Set the threshold at zero point six five. We flag A, B, and C. A and C are frauds we caught: true positives. B is a legitimate payment we flagged: a false positive. D is a fraud we missed: a false negative. And E and F are correctly left alone: true negatives. One threshold gives one confusion matrix, not yet a curve. + +## 01:51.5 — Two rates, two different denominators + +The true positive rate asks: of all the actual frauds, what fraction did we catch? Two out of three. The false positive rate asks: of all the legitimate payments, what fraction did we wrongly flag? One out of three. An R O C curve, short for receiver operating characteristic, plots false positive rate across and true positive rate up. Our threshold becomes this single point. + +## 02:21.5 — Lower the threshold. Trace the tradeoff. + +Start above the highest score: nothing is flagged, so both rates are zero. Now lower the threshold past one payment at a time. Each fraud steps the curve up by a third; each legitimate payment steps it right by a third. At the bottom, everything is flagged, and we reach one, one. That staircase is the R O C curve. + +## 02:46.0 — Width × height, added across the curve + +Now shade the area under the curve. Each legitimate payment adds a vertical strip, one third wide. The heights are one third, one, and one. So the area is one ninth plus one third plus one third: seven ninths, about zero point seven eight. The empty corner above the first strip is payment B's mistake. And A U C is this area, not the accuracy at any one threshold. + +## 03:15.5 — Area is also a ranking game + +There's a second way to get the same answer. Pair every fraud with every legitimate payment, and give a point whenever the fraud scores higher. Three frauds times three legitimate payments makes nine pairs. A beats all three. C and D each beat two, and both lose to B. Seven wins out of nine: exactly seven ninths again. + +## 03:41.0 — Each strip is one column of comparisons + +Why do they agree? Take legitimate payment B, scored zero point eight. Only one of the three frauds outranks it, so its strip is one third tall. Legitimate payments E and F are outranked by all three frauds, so their strips are full height. Averaging the strip heights counts exactly the winning pairs. That's the bridge from area to probability. + +## 04:08.0 — The probability behind ROC-AUC + +Here is the exact statement. Pick one random fraud and one random legitimate payment. R O C area is the probability that the fraud scores higher, with ties counting half. On a finite sample, that is just the fraction of winning pairs, like our seven out of nine. And because the horizontal axis is false positive rate, that is what the area integrates over, not the threshold. + +## 04:37.5 — Equal scores mean no ranking preference + +Ties need a rule. Take a separate tiny example: one fraud and one legitimate payment, both scored zero point five. Any threshold flags both or neither, so the R O C curve jumps straight from zero, zero to one, one. That diagonal has area one half, the same as giving the tied pair half credit. So a model that gives everything the same score has an A U C of one half. + +## 05:07.5 — 1 is perfect. 0.5 is the chance reference. + +A perfect ranking climbs to the top before moving right, and fills the whole square. Random ordering has an expected area of one half. A completely reversed ranking scores zero. Below one half can signal a flipped score direction, but don't flip a model just because of your test set. And an A U C of zero point eight does not mean eighty percent of predictions are correct. + +## 05:34.5 — Same ordering. Same AUC. Different numbers. + +Now square every score. The numbers change, but the order doesn't, so the R O C curve and its area stay at seven ninths. A U C can't tell you which set of numbers makes better probabilities. A fixed cutoff has to move too: zero point six five becomes its square, about zero point four two. Check calibration separately, and pick the threshold from the costs of mistakes. + +## 06:02.5 — A small false-positive rate can mean many alarms + +Suppose a threshold catches eighty percent of frauds and flags ten percent of legitimate payments. With a hundred of each, that's eighty true alarms and ten false ones: about eighty nine percent precision. Now take ten frauds among nine hundred ninety legitimate payments. The same rates give eight true alarms and ninety nine false ones: only seven and a half percent precision. The R O C point never moved: eighty percent, ten percent. Precision did, because prevalence, the Greek letter pi, changes what the flagged pile is made of. + +## 06:41.5 — Precision asks about the flagged pile + +Back to our six payments. Recall is just another name for the true positive rate. Precision asks a different question: of the payments we flagged, how many are really fraud? A precision recall curve plots recall across and precision up, as we sweep the same threshold. Each fraud we find raises recall; each legitimate payment we flag drags precision down. Before anything is flagged, precision is undefined, so plots usually start the curve at one. + +## 07:15.0 — Reward precision when a new positive is found + +A common summary is average precision, or A P. Each new fraud raises recall by one third. At those moments, precision is one, then two thirds, then three quarters. Weight each by its recall step and add them up. Twenty-nine over thirty-six, about zero point eight one. Same predictions as our R O C area of seven ninths, but a different question, and a different number. + +## 07:44.5 — PR-AUC and AP are not interchangeable labels + +Average precision sums recall steps times precision: zero point eight oh six. Join the raw precision recall points with straight lines and take trapezoids, and you get zero point seven six four. Those straight lines aren't even a valid interpolation in precision recall space. Other benchmarks use an interpolated envelope: at each recall, the best precision at that recall or higher. Here it lifts the middle step from two thirds to three quarters. And on large samples, a random ranker's baseline precision is the prevalence: one half here, only because half our payments are fraud. Always name the convention. + +## 08:30.0 — Partial AUC: only the region you operate in + +Say you can tolerate almost no false alarms. Partial A U C keeps only false positive rates up to zero point one. Here the raw area is one thirtieth, out of a possible one tenth. Scikit-learn's max F P R setting rescales that so one half is chance and one is perfect: about zero point six five. Always state the range, and whether it was rescaled. + +## 08:58.5 — Multiclass AUC needs a decomposition and an average + +Now suppose a second model sorts confirmed frauds into three types. One versus rest turns each class into its own binary problem, with its own R O C area; one versus one compares pairs of classes instead. The macro average weights the classes equally: about zero point seven eight. Weighting by class size gives zero point nine one, because the biggest class is the easiest. Same model, two honest numbers with different priorities. And when an example can carry several labels, a micro average pools every example label pair into one curve; that is not an average of areas. + +## 09:42.0 — Same letters, different curves + +Outside classification, the same letters show up on different curves. In search, average precision rewards relevant results near the top, and mean average precision averages it over queries. With three relevant results, at ranks one, three, and four, it's our twenty-nine over thirty-six again. Object detection first matches each predicted box to a true one by their overlap, called I O U. These two boxes overlap by two thirds: a match at one half, a miss at three quarters. Only then does it build precision recall curves, so Coco's A P is not an R O C area. Survival models compute an A U C at each time horizon t. A machine that failed by t is a case, one still running at t is a control, and one last seen before t is unknown, not a negative. And area under a learning curve measures performance across a training budget, not ranking at all. + +## 10:42.5 — Keep the curve, the data, and the decision separate + +In practice, feed the metric scores, not hard labels, and make sure larger means more likely positive. Evaluate on held-out data, and report uncertainty that matches how you sampled. Look at the curve and at important subgroups, not just one number. Then choose a threshold from your costs and your prevalence. No kind of A U C chooses that for you. + +## 11:08.5 — Ask five questions when someone says “AUC” + +Same six payments, two curves, and two different numbers: seven ninths, and twenty-nine over thirty-six. Neither is wrong; they answer different questions. R O C area asks whether frauds outrank legitimate payments. Precision recall asks how clean the flagged pile stays as recall grows. So when you hear A U C, ask: which curve, which positive class, which population, which range, and which average? diff --git a/videos/auc-ml/tsconfig.json b/videos/auc-ml/tsconfig.json new file mode 100644 index 0000000..c8e5eef --- /dev/null +++ b/videos/auc-ml/tsconfig.json @@ -0,0 +1 @@ +{ "extends": "../../tsconfig.base.json", "compilerOptions": { "noEmit": true }, "include": ["src"] } diff --git a/videos/auc-ml/verify_math.py b/videos/auc-ml/verify_math.py new file mode 100644 index 0000000..3dd6b44 --- /dev/null +++ b/videos/auc-ml/verify_math.py @@ -0,0 +1,37 @@ +from fractions import Fraction as F +import json +from pathlib import Path +scores=[F(9,10),F(8,10),F(7,10),F(6,10),F(4,10),F(1,10)] +y=[1,0,1,1,0,0] +pos=[s for s,l in zip(scores,y) if l];neg=[s for s,l in zip(scores,y) if not l] +def pair_auc(p,n):return sum(F(a>b)+F(1,2)*F(a==b) for a in p for b in n)/(len(p)*len(n)) +roc=[(F(0),F(0))];pr=[(F(0),F(1))];tp=fp=0;ap=F(0) +for k,l in enumerate(y,1): + tp+=l;fp+=1-l;roc.append((F(fp,3),F(tp,3)));pr.append((F(tp,3),F(tp,k))) + if l:ap+=F(1,3)*F(tp,k) +def trap(points):return sum((x1-x0)*(y0+y1)/2 for (x0,y0),(x1,y1) in zip(points,points[1:])) +chosen=[i for i,s in enumerate(scores) if s>=F(65,100)] +assert chosen==[0,1,2] and sum(y[i] for i in chosen)==2 +assert F(80,90)==F(8,9) and F(8,8+99)==F(8,107) +assert pair_auc(pos,neg)==trap(roc)==F(7,9) +assert ap==F(29,36) +assert trap(pr)==F(55,72) +assert pair_auc([s*s for s in pos],[s*s for s in neg])==F(7,9) +assert pair_auc([-s for s in pos],[-s for s in neg])==F(2,9) +assert pair_auc([F(1,2)],[F(1,2)])==F(1,2) +assert F(1,10)*F(1,3)==F(1,30) +assert sum([F(95,100),F(80,100),F(60,100)])/3==F(47,60) +assert F(95,100)*F(80,100)+F(80,100)*F(15,100)+F(60,100)*F(5,100)==F(91,100) +assert F(8000,20000-8000)==F(2,3) +# Standardized partial AUC (McClish, as scikit-learn max_fpr): 0.5*(1+(A-min)/(max-min)), min = a^2/2. +a=F(1,10);pa=F(1,30);std=F(1,2)*(1+(pa-a*a/2)/(a-a*a/2)) +assert abs(float(std)-0.649)<0.0005 +# Interpolated precision envelope used on screen: max precision at recall >= r. +env={r:max(p for rr,p in pr[1:] if rr>=r) for r in (F(1,3),F(2,3),F(1))} +assert env=={F(1,3):F(1),F(2,3):F(3,4),F(1):F(3,4)} +# Retrieval callback: relevant at ranks 1,3,4 of 6 is the same AP. +assert ap==F(1,3)*(F(1,1)+F(2,3)+F(3,4)) +# Imbalance: identical ROC point in both populations. +assert (F(80,100),F(10,100))==(F(8,10),F(99,990)) +result={'roc_auc':str(trap(roc)),'pair_auc':str(pair_auc(pos,neg)),'average_precision':str(ap),'linear_pr_auc':str(trap(pr)),'roc':[[float(x),float(y)] for x,y in roc],'pr':[[float(x),float(y)] for x,y in pr],'checks':'threshold counts, pair equality, AP, trapezoids, ties, reversal, monotone transform, partial area, averaging, IoU, standardized partial AUC, interpolated envelope, retrieval AP, prevalence-invariant ROC point'} +out=Path(__file__).parent/'out';out.mkdir(exist_ok=True);(out/'math-verification.json').write_text(json.dumps(result,indent=2)+'\n');print(json.dumps(result,indent=2)) From bb60904cd1668226e6cb311721e57d62d2660fdd Mon Sep 17 00:00:00 2001 From: calvin-archastro Date: Fri, 2 Oct 2026 12:22:33 -0700 Subject: [PATCH 2/2] Inventory the AUC film's font copies in the license manifest check:public requires every woff2 to have a license entry. The film reuses Fragment Mono, Instrument Serif and Schibsted Grotesk files already inventoried for intern-promo; add the copied paths with matching hashes. Co-Authored-By: Claude Opus 5.5 (1M context) --- third-party/fonts/manifest.json | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/third-party/fonts/manifest.json b/third-party/fonts/manifest.json index 8272fcc..c28f691 100644 --- a/third-party/fonts/manifest.json +++ b/third-party/fonts/manifest.json @@ -66,6 +66,18 @@ { "file": "videos/intern-promo/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB4NHhqcL71Q.woff2", "sha256": "4f4dc27f4a770c0d02fde800daa836c8adc0d1e423b28da74baaf0d1cc3ab96c" + }, + { + "file": "videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB41HhqcL71QxtQ.woff2", + "sha256": "e3085219252209a8d4128f90ff6d793315c752f73a979082d1d85d9cb46f63a4" + }, + { + "file": "videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB45HhqcL71QxtQ.woff2", + "sha256": "ed108107e74221cc5e163f12c6b35ba3e75e7615283828d6862c7a0784e1b0c9" + }, + { + "file": "videos/auc-ml/src/fonts/4iCr6K5wfMRRjxp0DA6-2CLnB4NHhqcL71Q.woff2", + "sha256": "4f4dc27f4a770c0d02fde800daa836c8adc0d1e423b28da74baaf0d1cc3ab96c" } ] }, @@ -90,6 +102,22 @@ { "file": "videos/intern-promo/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2", "sha256": "60c06664b5a95c7de6cc3e00d1f9034d78bd1e40b564016b241674449a067d4d" + }, + { + "file": "videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zUTjnTLgNs.woff2", + "sha256": "60c06664b5a95c7de6cc3e00d1f9034d78bd1e40b564016b241674449a067d4d" + }, + { + "file": "videos/auc-ml/src/fonts/jizBRFtNs2ka5fXjeivQ4LroWlx-6zsTjnTLgNuZ5w.woff2", + "sha256": "a8c4bd7cd7073180e740d2d83a616b5cb0845579b73207eeafeae8532e70c901" + }, + { + "file": "videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjgn7Motmp5r61.woff2", + "sha256": "a04fc7ed18a8037149ce0bfda58076709d8e0840e136ed00abbdc196b7992443" + }, + { + "file": "videos/auc-ml/src/fonts/jizHRFtNs2ka5fXjeivQ4LroWlx-6zAjjH7Motmp5g.woff2", + "sha256": "6ee678c33f388dd7ba59700ebea635deb98821baafd817b09891f7927177f702" } ] }, @@ -106,6 +134,14 @@ { "file": "videos/intern-promo/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2", "sha256": "e3b56e90510a84ac0ed465b822e112983eaf58e37436bf769681c31f77b1f3a7" + }, + { + "file": "videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9GayojdSFO.woff2", + "sha256": "e3b56e90510a84ac0ed465b822e112983eaf58e37436bf769681c31f77b1f3a7" + }, + { + "file": "videos/auc-ml/src/fonts/Jqz55SSPQuCQF3t8uOwiUL-taUTtap9IayojdSFOd1I.woff2", + "sha256": "f5df746dbc2eccec6d22fdaaa2d253cd6915a30ce9f6ba86138bb6151c7254db" } ] },