Kapitel 7

🧪Simulation im Browser

Core ML läuft nicht im Browser. Hier rechnet deshalb eine TypeScript-Nachbildung desselben Mini-Modells – mit den echten Gewichten und den echten Kompressionsdaten aus coremltools. Wo möglich, steht die echte Core-ML-Vorhersage daneben.

✏️Ziffern-Labor

Ziffer zeichnen (Maus oder Finger) oder ein Testbild wählen. Jede Karte zeigt die Vorhersage einer Variante; Klick auf eine Karte zeigt ihre Vorwärtsrechnung.

✏️ 8×8-Eingabe

Testbild #1 (Label 7)
Pinsel:
Testbilder aus load_digits (nicht trainiert):
🧪 Simulation im Browser✔ echte coremltools-Ausgabe

🔬 Vorwärtsrechnung Schritt für Schritt – Int4 linear

Namen wie die Ops im echten MIL-Programm. Blau = positiv, Rot = negativ (Farbstärke relativ zum größten Betrag der Zeile).

pixels
Eingang [1, 64] · Rohwert/16
linear_0
fc1: W₁·x + b₁ → [1, 32]
relu
negative Werte → 0
linear_1
fc2: W₂·h + b₂ → [1, 10] (Logits)
softmax
Wahrscheinlichkeiten, Summe 1
🧪
Was hier wirklich passiert
Die Browser-Rechnung nutzt dieselben Gewichte wie das echte Modell. Int8, Int4 und Pruning werden hier bitgenau wie coremltools nachgerechnet, die 4-Bit-Palette verwendet die echte Lookup-Tabelle aus der Konvertierung. FP16 ist eine Nachbildung (alle Werte auf Float16 gerundet) – nicht bitgenau zur Core-ML-Laufzeit, aber in den Tests auf ≤ 2·10⁻³ an den echten Core-ML-Ausgaben. Die „Core ML echt“-Werte stammen aus MLModel.predict auf macOS (CPU_ONLY).

🕸️So sieht der MIL-Graph wirklich aus

Keine Handzeichnung: Op-Listen und model.mil stammen direkt aus der Konvertierung mit coremltools 9.0.
✔ echte coremltools-Ausgabe
  1. castcast_1→ fp16 [1, 64]
    x = pixels · dtype = pixels_to_fp16_dtype_0
  2. linearlinear_0_cast_fp16→ fp16 [1, 32]
    x = pixels_to_fp16 · weight = m_fc1_weight_to_fp16 · bias = m_fc1_bias_to_fp16
  3. reluinput_cast_fp16→ fp16 [1, 32]
    x = linear_0_cast_fp16
  4. linearlinear_1_cast_fp16→ fp16 [1, 10]
    x = input_cast_fp16 · weight = m_fc2_weight_to_fp16 · bias = m_fc2_bias_to_fp16
  5. softmaxop_14_cast_fp16→ fp16 [1, 10]
    x = linear_1_cast_fp16 · axis = var_12
  6. castvar_14_cast_to_fp32→ fp32 [1, 10]
    x = var_14 · dtype = var_14_cast_to_fp32_dtype_0
  7. classifyclassify_0→ string (Skalar), dict (Skalar)
    probabilities = var_14_cast_to_fp32 · classes = const_0

📋Steckbrief des Lehrmodells

ArchitekturLinear(64→32) → ReLU → Linear(32→10) → Softmax
Parameter2.410 (fc1 2 048 + 32, fc2 320 + 10)
Datenscikit-learn load_digits (UCI Optical Recognition of Handwritten Digits, 8×8, Werte 0–16)
Aufteilung1437 Training / 360 Test – train_test_split(test_size=0.2, random_state=0, stratify=y)
Trainingtorch.manual_seed(0), Adam lr=0.01, 300 Epochen Full-Batch, Cross-Entropy
GenauigkeitTraining 100,0 %, Test 96,7 %
Werkzeugecoremltools 9.0, torch 2.7.0, numpy 2.2.6, Python 3.12.14, macOS 27.0
🧪
Wie genau ist die Browser-Rechnung?
  • FP32: Abweichung zu PyTorch und Core ML ≤ 10⁻⁶ (Test).
  • Int8 / Int4 / Pruning: Ganzzahlen, Skalen und Gewichte bitgenau wie coremltools (Test), Ausgaben ≤ 10⁻⁵ zu Core ML.
  • Palette 4 Bit: echte LUT + Indizes aus dem Modell, Ausgaben ≤ 10⁻⁵ zu Core ML.
  • FP16: Nachbildung, ≤ 2·10⁻³ zu Core ML (CPU); die Laufzeit auf GPU/Neural Engine kann anders runden.
💡
Nachvollziehbar
Das Erzeugungsskript liegt im Repo unter scripts/golden/gen_golden.py, die Rohdaten unter tests/golden/. Die Tests vergleichen die Browser-Rechnung mit diesen Dateien. Ein erneuter Lauf des Skripts lieferte bitgleiche Ergebnisse – bis auf die k-means-Lookup-Tabelle der Palettisierung (Abweichung ≈ 10⁻⁷, k-means ist nicht streng deterministisch).