Kapitel 6

🩺Tipps & Fehlersuche

Die typischen Stolpersteine – einige davon beim Erzeugen dieser App wirklich aufgetreten (markiert mit „echt“) – und wie man sie löst.

🧯Häufige Probleme

▸torch.export im falschen Dialektecht aufgetreten
NotImplementedError: Conversion for models with only ATEN or EDGE dialect is supported/tested. Provided Dialect: TRAINING. Run '.run_decompositions({})' on your exported PyTorch Model prior to conversion.
Ursache: torch.export.export liefert (hier mit torch 2.7.0) ein ExportedProgram im TRAINING-Dialekt.
Lösung: ep = torch.export.export(model, (x,)).run_decompositions({}) – danach ist der Dialekt ATEN und ct.convert(ep) läuft.
▸Nicht unterstützte PyTorch-Operation
Konvertierung bricht mit einem Hinweis auf eine unbekannte/nicht implementierte Op ab.
Ursache: Für diese Op gibt es (noch) keine Übersetzung nach MIL.
Lösung: Composite Op schreiben: mit @register_torch_op eine Funktion registrieren, die die Op aus vorhandenen MIL-Ops (mb.*) zusammensetzt.
▸Datenabhängiger Kontrollfluss
Konvertiertes Modell rechnet nur für die beim Tracen genutzte Eingabe richtig.
Ursache: torch.jit.trace zeichnet nur den tatsächlich durchlaufenen Pfad auf – Schleifen/Bedingungen, die von Daten abhängen, verallgemeinern nicht.
Lösung: Kontrollfluss aus dem Modell herausziehen, torch.export mit dynamic_shapes probieren oder Varianten getrennt konvertieren.
▸Dropout/BatchNorm im Trainingsmodus
Ausgaben weichen stark und zufällig ab.
Ursache: Modell wurde nicht mit eval() in den Auswertungsmodus gesetzt.
Lösung: model.eval() vor trace/export – steht ausdrücklich im coremltools-Guide.
▸Eingang ist plötzlich Float16echt aufgetreten
In Xcode steht als Eingangstyp MultiArray (Float16), obwohl FP32 gedacht war.
Ursache: Beobachtet mit coremltools 9.0: ohne dtype in ct.TensorType und mit Ziel iOS17 wurde der Eingang FLOAT16 (siehe Golden-File mit RangeDim/EnumeratedShapes). FP16-Ein-/Ausgänge sind ab iOS 16 möglich.
Lösung: dtype explizit setzen: ct.TensorType(name="pixels", shape=(1, 64), dtype=np.float32).
▸Unbeschränkter RangeDim
Konvertierung zu mlprogram schlägt mit flexibler Form fehl.
Ursache: Unbeschränkte Bereiche (upper_bound=-1) sind bei ML Programs nicht erlaubt.
Lösung: ct.RangeDim(lower_bound=1, upper_bound=64) setzen oder EnumeratedShapes (bis 128 Formen, schneller).
▸LogisticRegression (multinomial) aus scikit-learnecht aufgetreten
Log-Meldung „Currently "One Vs Rest" is the only supported multiclass option.“ – danach bricht die Konvertierung ab.
Ursache: Der scikit-learn-Konverter unterstützt bei logistischer Regression nur One-vs-Rest; scikit-learn 1.5 nutzt standardmäßig multinomial.
Lösung: Anderes Modell (z. B. Entscheidungsbaum) oder One-vs-Rest verwenden.
▸Ungetestete PyTorch-Versionecht aufgetreten
Warnung „Torch version 2.7.1 has not been tested with coremltools. You may run into unexpected errors. Torch 2.7.0 is the most recent version that has been tested.“
Ursache: coremltools 9.0 ist gegen PyTorch 2.7 getestet.
Lösung: In einer eigenen venv genau die getestete Version installieren (hier torch==2.7.0).
▸NaN oder Inf in der Ausgabe
Core ML liefert NaN/Inf, PyTorch nicht – häufig bei FP16 (größte Zahl 65504).
Ursache: Überlauf in einer Zwischenrechnung bei reduzierter Genauigkeit.
Lösung: MLModelValidator (experimentell, coremltools ≥ 8.3) findet die verursachenden Ops; betroffene Teile mit compute_precision FLOAT32 konvertieren.
▸predict() in Python schlägt fehl
Unter Linux/Docker lässt sich das konvertierte Modell nicht ausführen.
Ursache: coremltools nutzt für predict() das Core-ML-Framework – das gibt es nur unter macOS.
Lösung: Konvertieren geht auch unter Linux, testen auf einem Mac (oder direkt im Xcode-Vorschau-Tab).
Python

Fehlende PyTorch-Op selbst übersetzen (Composite Op)

from coremltools.converters.mil.frontend.torch.torch_op_registry import (
    _TORCH_OPS_REGISTRY, register_torch_op)
from coremltools.converters.mil.frontend.torch.ops import _get_inputs
from coremltools.converters.mil import Builder as mb

del _TORCH_OPS_REGISTRY["selu"]        # nur nötig, wenn eine vorhandene Übersetzung ersetzt wird

@register_torch_op
def selu(context, node):
    x = _get_inputs(context, node, expected=1)[0]
    x = mb.elu(x=x, alpha=1.6732632423543772)
    x = mb.mul(x=x, y=1.0507009873554805, name=node.name)
    context.add(x)

mlmodel = ct.convert(traced, inputs=[...])
ℹ️ Beispiel wörtlich nach dem coremltools-Guide „Composite Operators“ (selu).

⚖️PyTorch vs. Core ML vergleichen

Wie groß dürfen Abweichungen sein? Echte Werte des Lehrmodells:
✔ echte coremltools-Ausgabemax |p_CoreML − p_PyTorch| über 40 Testbilder × 10 Klassen (Softmax-Wahrscheinlichkeiten), CPU_ONLY
FP32
1.2e-7
FP16
7.8e-4
FP16 (torch.export)
7.8e-4
Int8
1.8e-3
Palette 4 Bit
7.0e-2
Int4
1.9e-1
Pruning 50 %
9.8e-1
1e-81e-61e-41e-21e0

Senkrechte Striche: typische Toleranzen 1e-6 (FP32) und 1e-3 (FP16). Die Nachkommastellen hängen von Modell, Eingabe und Recheneinheit ab – auf GPU/Neural Engine sind andere Werte zu erwarten.

💡Tipps für Performance, Größe, Datenschutz und Pflege

Toleranzen realistisch wählen: FP32-Modelle weichen bei uns ≈ 1,2·10⁻⁷ ab, FP16 ≈ 7,8·10⁻⁴ (max. Betrag, Softmax-Wahrscheinlichkeiten, CPU_ONLY). Für FP16 also eher atol ≈ 10⁻³ als 10⁻⁶. 📎 eigene Konvertierung
Für Ursachen-Suche: MLModelComparator/TorchScriptMLModelComparator vergleichen Zwischenergebnisse Op für Op mit dem Quellmodell (experimentell, seit coremltools 8.3). 📎 Debugging And Performance Utilities📎 coremltools 8.3 Release Notes
Performance messen ohne App-Code: Modell in Xcode öffnen → Tab „Performance“ → Bericht mit Gerät und Recheneinheiten. Zeigt Lade-, Kompilier- und Vorhersagezeit (Median) und je Operation CPU/GPU/NE – aber keinen Speicher- und Energiebedarf. 📎 Analyzing a Core ML model’s performa…
Dieselbe Zuordnung im Code: MLComputePlan.load(contentsOf:configuration:) → deviceUsage(for:) und estimatedCost(of:) (ab iOS 17.4 / macOS 14.4). 📎 MLComputePlan📎 Analyzing a Core ML model’s performa…
Tiefer: „Open in Instruments“ aus dem Performance-Report heraus. 📎 Analyzing a Core ML model’s performa…
Metadaten pflegen: author, short_description, version, license und Beschreibungen je Eingang/Ausgang – Xcode zeigt sie an, coremltools ergänzt selbst Quell-Framework, Version und Datum (userDefined). 📎 MLModel Overview (Metadaten, Spec, p…📎 eigene Konvertierung
Ein- und Ausgänge nachträglich umbenennen (z. B. für schönere Swift-Namen): ct.utils.rename_feature(spec, "alt", "neu"). 📎 MLModel Utilities (Metadaten, rename…
Datenschutz: Läuft das Modell nur auf dem Gerät, braucht es keine Netzverbindung und die Daten bleiben beim Nutzer. 📎 Core ML (Framework-Übersicht)
Modell schützen: Xcode kann das eingebaute Modell beim Kompilieren verschlüsseln (Compiler-Flag im Build-Target). 📎 Encrypting a Model in Your App
Große heruntergeladene Modelle: kompiliertes .mlmodelc dauerhaft ablegen; iCloud-Backup bedenken (Caches oder isExcludedFromBackup). 📎 Downloading and Compiling a Model on…
Ein MLModel-Objekt nur von einem Thread/einer Queue gleichzeitig benutzen – oder je Thread eine eigene Instanz. 📎 MLModel
Kompression: Palettisierung und lineare Quantisierung sparen vor allem Speicher/Bandbreite; Beschleunigung hängt von Recheneinheit und OS ab (z. B. per_block Int4 eher GPU, auf der NE per_channel empfohlen). 📎 Quantization Performance📎 Palettization Performance
Pruning beschleunigt vor allem auf der Neural Engine bei hoher (≥ 75 %) oder blockstrukturierter Sparsity. 📎 Pruning Performance

🗓️Deployment-Matrix: ab welchem OS geht was?

Mindestversionen laut Apple-Doku bzw. coremltools-Guides. Das Deployment-Ziel der Konvertierung (minimum_deployment_target) muss mindestens so hoch sein wie das Feature verlangt.
FeatureiOSmacOSWerkzeugBeleg
Core ML (Framework), MLModel, MLMultiArray, Vision VNCoreMLRequest1110.13–📎 Core ML (Framework-Übersicht)📎 MLModel📎 MLMultiArray📎 VNCoreMLRequest
NeuralNetwork-Modelle (.mlmodel)1110.13–📎 Source and Conversion Formats
MLModelConfiguration, computeUnits .all/.cpuOnly/.cpuAndGPU, predictions(fromBatch:)1210.14–📎 MLModelConfiguration📎 MLComputeUnits📎 MLModel.predictions(fromBatch:)
Mindestziel der Unified Conversion API (ct.convert)1310.15coremltools ≥ 4📎 PyTorch Conversion Workflow
On-Device-Update MLUpdateTask (nur NeuralNetwork, kompiliertes .mlmodelc)1310.15–📎 MLUpdateTask📎 Personalizing a Model with On-Device…📎 Comparing ML Programs and Neural Net…
ML Program (.mlpackage, MIL) – Standard ab coremltools 7.01512coremltools ≥ 5📎 Source and Conversion Formats📎 Convert Models to ML Programs
MLShapedArray, MLModel.load(contentsOf:configuration:) async1512–📎 MLShapedArray📎 MLModel.load(contentsOf:configuratio…
.cpuAndNeuralEngine1613–📎 MLComputeUnits.cpuAndNeuralEngine
FP16-Ein-/Ausgänge (MLMultiArray Float16, GRAYSCALE_FLOAT16)1613–📎 Image Input and Output
Gewichtskompression: Int8 pro Kanal, Palette 1/2/4/6/8 Bit, Pruning1613coremltools ≥ 7 (optimize)📎 Optimization – What’s New (OS-Verfüg…📎 Palettization Overview + API📎 Pruning Overview + API
MLModel.compileModel(at:) async1613–📎 MLModel.compileModel(at:) async
Asynchrone Vorhersage (prediction(from:) async / completionHandler)1714–📎 MLModel predictionFromFeatures:compl…
Aktivierungs-Quantisierung W8A8 (NE-beschleunigt ab A17 Pro / M4)1714–📎 Optimization – What’s New (OS-Verfüg…📎 Quantization Overview + API
MLComputePlan (Zuordnung Operation → Recheneinheit im Code)17.414.4–📎 MLComputePlan
Int4-Gewichte, per_block-Skalen, Palette 3 Bit, gruppierte LUTs, INT8-LUTs, Pruning + Quantisierung/Palette kombiniert1815coremltools ≥ 8📎 Optimization – What’s New (OS-Verfüg…📎 coremltools 8.0 Release Notes
Stateful Models (MLState), Multifunction-Modelle, MLTensor1815coremltools ≥ 8📎 MLState📎 Stateful Models📎 MLTensor📎 coremltools 8.0 Release Notes
Mehrere Eingänge mit EnumeratedShapes (Guide nennt nur iOS 18)18––📎 Flexible Input Shapes
Vision: CoreMLRequest + CoreMLModelContainer (Swift-API)1815–📎 CoreMLRequest (Swift-Vision-API)📎 CoreMLModelContainer
Deployment-Ziel ct.target.iOS26 / macOS262626coremltools ≥ 9.0📎 coremltools 9.0 Release Notes
Core AI (neues Framework, .aimodel) – Ausblick, nicht Thema dieser App2727–📎 Core AI (neues Framework)