Kapitel 3

🗜️Optimierung: kleiner und schneller

Kompression reduziert Speicher, Bandbreite und oft auch Latenz und Energie – mit etwas Genauigkeitsverlust. coremltools bietet drei Grundverfahren, einzeln oder kombiniert, direkt auf dem .mlpackage (datenfrei) oder schon in PyTorch (mit Kalibrierdaten oder Feintuning). 📎 Optimization Overview

🧰Die Verfahren

FP16

Jedes Gewicht als 16-Bit-Gleitkommazahl. Standard bei convert_to="mlprogram".

w₁₆ = round_fp16(w)
📱 iOS 15 · macOS 12

Lineare Quantisierung (Int8/Int4)

Werte werden auf ein gleichmäßiges Ganzzahlraster abgebildet. Standard in coremltools: symmetrisch (Nullpunkt 0), eine Skala je Ausgabekanal.

q = clip(round(w / scale), −127, 127) · w ≈ scale · q
📱 Int8: iOS 16 · Int4 & per_block: iOS 18

Palettisierung

k-means sucht 2ⁿ Zentren (Lookup-Tabelle); jedes Gewicht speichert nur noch einen n-Bit-Index. n ∈ {1, 2, 3, 4, 6, 8}.

w ≈ LUT[index]
📱 iOS 16 (3 Bit, gruppierte/INT8-LUTs: iOS 18)

Pruning

Betragskleine Gewichte werden 0 und dünn besetzt gespeichert: Bitmaske + nur die Nicht-Null-Werte. Auch n:m- und Block-Sparsity.

w = 0, falls |w| unter der Schwelle
📱 iOS 16 (kombiniert mit Quantisierung/Palette: iOS 18)

📏Größenrechner: Parameter × Bits

Wie groß werden die Gewichte? Nutzlast inklusive Skalen, Lookup-Tabellen und Bitmasken.
FP32
ab iOS 15 / macOS 12 (ML Program)
97,5 MiB
32,00 Bit/Gewicht
FP16
ab iOS 15 / macOS 12 (ML Program)
48,7 MiB
16,00 Bit/Gewicht
Int8 pro Kanal
ab iOS 16 / macOS 13
24,4 MiB
8,00 Bit/Gewicht
Int4 pro Block (32)
ab iOS 18 / macOS 15
13,7 MiB
4,50 Bit/Gewicht
Palette 6 Bit (1 LUT)
ab iOS 16 / macOS 13
18,3 MiB
6,00 Bit/Gewicht
Palette 4 Bit (1 LUT)
ab iOS 16 / macOS 13
12,2 MiB
4,00 Bit/Gewicht
Palette 4 Bit (64 LUTs)
ab iOS 18 / macOS 15
12,2 MiB
4,00 Bit/Gewicht
Pruning 50 % (+16-Bit-Werte)
ab iOS 16 / macOS 13
27,4 MiB
9,00 Bit/Gewicht
Pruning 75 % (+16-Bit-Werte)
ab iOS 16 / macOS 13
15,2 MiB
5,00 Bit/Gewicht
Gewichte (Parameter × Bits)Metadaten: Skalen (FP16), LUTs, Bitmaske

Vereinfachung: alle Parameter als ein Gewichtstensor mit 1 024 Ausgabekanälen; in echten Modellen bleiben kleine Tensoren (unter weight_threshold, Standard 2 048 Elemente) unkomprimiert, dazu kommen Dateiköpfe und Ausrichtung. Parameterzahlen: 📎 torchvision Modell-Metadaten (num_pa… · Verfahren/OS: 📎 Optimization – What’s New (OS-Verfüg…📎 Optimization Overview

🎯Was mit den Gewichten passiert

Oben das Histogramm aller fc1-Gewichte des Lehrmodells mit den darstellbaren Werten, darunter eine Zeile vor und nach der Kompression.
🧪 Simulation im Browser

Zeile 0: scale = max|w| / 7 = 0.11891 → 15 mögliche Werte je Zeile (−7…7)

Histogramm aller 2 048 fc1-Gewichte · Striche = darstellbare Werte
−1.410+1.41
Zeile 0: original (grau) vs. komprimiert (farbig) – max. Fehler 0.0587

📊Echte Messwerte am Lehrmodell

Alle Varianten wurden mit coremltools.optimize erzeugt, gespeichert und mit Core ML auf macOS ausgewertet.
✔ echte coremltools-AusgabeDigitMLP, coremltools 9.0, Vorhersagen mit MLModel.predict (CPU_ONLY) auf macOS 27.0
VarianteMIL-Op für Gewichteweight.binGenauigkeit (360 Testbilder)max |Δ| zu PyTorch
FP32
const
9.984 B
96,7 %1.19e-7
FP16 (Standard)
const + cast
5.140 B
96,7 %7.84e-4
Int8 linear pro Kanal
OpLinearQuantizerConfig(mode="linear_symmetric", dtype="int8", granularity="per_channel", weight_threshold=256)
constexpr_affine_dequantize
3.432 B
96,7 %1.77e-3
Int4 linear pro Kanal
OpLinearQuantizerConfig(mode="linear_symmetric", dtype="int4", granularity="per_channel", weight_threshold=256) – Basis mit minimum_deployment_target=iOS18
constexpr_blockwise_shift_scale
2.024 B
97,2 %1.92e-1
Palette 4 Bit (k-means)
OpPalettizerConfig(nbits=4, mode="kmeans", granularity="per_tensor", weight_threshold=256)
constexpr_lut_to_dense
1.984 B
97,2 %7.00e-2
Pruning 50 %
OpMagnitudePrunerConfig(target_sparsity=0.5, weight_threshold=256)
constexpr_sparse_to_dense
5.672 B
81,4 %9.77e-1

Beim Lehrmodell sind die Gewichte winzig – Dateiköpfe und die unkomprimierten Biases fallen deshalb stark ins Gewicht (Int4 spart hier „nur“ 80 % statt 87,5 %). Int4 und Palette liegen auf den 360 Testbildern sogar minimal über FP32 (97,2 % statt 96,7 %) – Zufall bei zwei Bildern, kein Qualitätsgewinn. Pruning von 50 % ohne Nachtraining zerstört dieses kleine Netz dagegen deutlich. 📎 eigene Konvertierung

🧾Der Code dazu

Python

Gewichte komprimieren mit coremltools.optimize

✔ echte coremltools-Ausgabe
import coremltools.optimize as cto

# Int8 linear, symmetrisch, eine Skala je Ausgabekanal  (ab iOS 16)
cfg = cto.coreml.OptimizationConfig(global_config=cto.coreml.OpLinearQuantizerConfig(
    mode="linear_symmetric", dtype="int8", granularity="per_channel", weight_threshold=256))
m_int8 = cto.coreml.linear_quantize_weights(mlmodel, config=cfg)

# Int4 – braucht minimum_deployment_target=ct.target.iOS18
cfg4 = cto.coreml.OptimizationConfig(global_config=cto.coreml.OpLinearQuantizerConfig(
    mode="linear_symmetric", dtype="int4", granularity="per_channel", weight_threshold=256))
m_int4 = cto.coreml.linear_quantize_weights(mlmodel_ios18, config=cfg4)

# Palettisierung: 16 Clusterzentren (k-means) als Lookup-Tabelle  (ab iOS 16)
pcfg = cto.coreml.OptimizationConfig(global_config=cto.coreml.OpPalettizerConfig(
    nbits=4, mode="kmeans", granularity="per_tensor", weight_threshold=256))
m_pal = cto.coreml.palettize_weights(mlmodel, config=pcfg)

# Pruning: 50 % der betragskleinsten Gewichte auf 0  (ab iOS 16)
prcfg = cto.coreml.OptimizationConfig(global_config=cto.coreml.OpMagnitudePrunerConfig(
    target_sparsity=0.5, weight_threshold=256))
m_pruned = cto.coreml.prune_weights(mlmodel, config=prcfg)

⚡Neural Engine & Performance

💡
Aktivierungen quantisieren (W8A8)
Neben Gewichten lassen sich Int8-Aktivierungen erzeugen (ab iOS 17). Auf A17 Pro/M4 (z. B. iPhone 15 Pro) hat die Neural Engine erhöhten Int8-Durchsatz – sinnvoll nur, wenn das Modell (fast) ganz auf der NE läuft. Die API cto.coreml.experimental.linear_quantize_activations ist experimentell. 📎 Quantization Overview + API📎 Quantization Performance📎 Optimization – What’s New (OS-Verfüg…
✅
Welche Granularität wofür?
Int4 mit per_block (iOS 18) bringt vor allem auf der GPU Speicher- und Latenzgewinne; läuft das Modell auf der Neural Engine, empfiehlt der Guide per_channel. Palettisierte Gewichte werden ab iOS 17 eher „just in time“ entpackt, vor allem auf der NE. 📎 Quantization Performance📎 Palettization Performance
⚠️
Genauigkeit immer messen
Datenfreie Kompression reicht oft bis 8 Bit, je nach Modell auch 6 oder 4 Bit. Für höhere Kompression: Kalibrierdaten (z. B. GPTQ, SKM) oder Feintuning in PyTorch mit coremltools.optimize.torch. Unser Pruning-Beispiel zeigt, wie stark ein kleines Netz ohne Nachtraining leiden kann. 📎 Optimization Overview📎 Optimization – What’s New (OS-Verfüg…📎 eigene Konvertierung