Engineering-Praxis

Core-ML-Modelle auf einem Cloud-Mac im App-Bundle prüfen

Core-ML-Modelle auf einem Cloud-Mac im App-Bundle prüfen

Ein Bildklassifizierungsmodell funktioniert bei lokalen Tests, und auch die Archivierung wird erfolgreich abgeschlossen. In der ausgelieferten App fehlt die Ressource jedoch bei der ersten Inferenz. Die Ursache liegt meist nicht im Modellalgorithmus: Häufig wurde das Modell dem Target nicht hinzugefügt, von einem Skript in das falsche Verzeichnis kopiert oder eine ältere Version mit demselben Namen ist noch im App-Bundle vorhanden. Eine Cloud-Mac-Pipeline muss deshalb mehr prüfen als nur die Frage, ob sich das Projekt bauen lässt. Sie muss außerdem sicherstellen, dass der Modellquellbestand separat kompiliert werden kann und dass das ausgelieferte Bundle tatsächlich die vorgesehenen Dateien enthält.

Modellvalidierung in zwei Prüfungen aufteilen

Die erste Prüfung verarbeitet die Datei .mlmodel oder .mlpackage direkt. Damit lassen sich Probleme mit dem Modellformat, der Toolchain und dem Ausgabeverzeichnis ausschließen. Die zweite Prüfung untersucht nach Abschluss der Xcode-Archivierung die .app und bestätigt, dass das kompilierte .mlmodelc-Verzeichnis tatsächlich im auszuliefernden Target enthalten ist. Beide Prüfungen müssen getrennt bleiben: Wird nur xcodebuild ausgeführt, lässt sich bei einem Fehler nur schwer schnell feststellen, ob die Ursache in der Modellkompilierung, der Target-Zugehörigkeit oder der Phase zum Kopieren der Ressourcen liegt.

Legen Sie vor Beginn das Entwicklerverzeichnis fest und protokollieren Sie die Toolversionen:

set -euo pipefail

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
xcodebuild -version
xcrun --find coremlcompiler
swift --version

Sind auf ArmVMS mehrere Xcode-Versionen installiert, muss jede Aufgabe DEVELOPER_DIR ausdrücklich setzen, statt sich auf den Zustand von xcode-select in einem interaktiven Terminal zu verlassen. Die Umgebungsvariablen der Pipeline können sich von denen einer manuellen Anmeldesitzung unterscheiden. Ein expliziter Pfad verbessert daher die Reproduzierbarkeit.

Gegenstand der Modellvalidierung sollte „das mit der angegebenen Xcode-Toolchain erzeugte, konkrete App-Bundle“ sein – nicht ein Build-Verzeichnis, das auf irgendeinem Rechner zufällig einmal erfolgreich erstellt wurde.

Core-ML-Modell zuerst separat kompilieren

Verwenden Sie für jedes Modell ein eigenes Ausgabeverzeichnis. Mehrere Aufgaben dürfen nicht dasselbe temporäre Verzeichnis gemeinsam nutzen. Das folgende Skript unterstützt sowohl .mlmodel- als auch .mlpackage-Eingaben:

MODEL_PATH="${1:?model path required}"
OUTPUT_ROOT="${2:-$PWD/build/coreml}"
MODEL_NAME="$(basename "$MODEL_PATH")"
MODEL_NAME="${MODEL_NAME%.*}"
MODEL_OUTPUT="$OUTPUT_ROOT/$MODEL_NAME"

rm -rf "$MODEL_OUTPUT"
mkdir -p "$MODEL_OUTPUT"

xcrun coremlcompiler compile "$MODEL_PATH" "$MODEL_OUTPUT"

find "$MODEL_OUTPUT" -type f -print0 |
  sort -z |
  xargs -0 shasum -a 256 > "$MODEL_OUTPUT.files.sha256"

du -sk "$MODEL_OUTPUT"

Nach erfolgreicher Ausführung sind mindestens drei Punkte zu prüfen: Das Ausgabeverzeichnis ist nicht leer, die Dateiliste lässt sich erzeugen und die Größe ist nicht null. Verwenden Sie das Kompilierungsverzeichnis nicht unverändert als dauerhaften Cache, da sein Inhalt von der Xcode-Version, dem Modellinhalt und dem Verhalten des Compilers abhängt. Soll es wiederverwendet werden, muss der Cache-Schlüssel gemeinsam aus dem Hash des Modellquellbestands, der Ausgabe von xcodebuild -version und der Version des Build-Skripts gebildet werden.

Eingaben für generierte Modelle zusätzlich prüfen

In einigen Projekten wird das Modell vor dem Build mit Python oder einem Konvertierungswerkzeug erzeugt. In diesem Fall müssen zunächst die Eingabedateien validiert werden, bevor coremlcompiler aufgerufen wird. Der Generierungsschritt muss in ein neues Verzeichnis schreiben. Erst nach vollständigem Abschluss wird dieses atomar an den vereinbarten Pfad verschoben. So kann Xcode kein unvollständiges Zwischenergebnis einlesen.

Folgende Felder sollten im Manifest gespeichert werden:

Feld Zweck
Relativer Modellpfad Modelle der Haupt-App von Modellen für Erweiterungen unterscheiden
SHA-256 der Quelldatei Feststellen, ob sich die Eingabe tatsächlich geändert hat
Xcode-Version Unterschiede zwischen kompilierten Artefakten erklären
Größe nach der Kompilierung Unbeabsichtigt hinzugefügte Ressourcen erkennen
Target-Name Target Membership prüfen

Endgültiges Produkt im Archiv kontrollieren

Nachdem die separate Kompilierung erfolgreich war, erstellen Sie ein sauberes Archiv. Damit keine Rückstände einer vorherigen Aufgabe eingelesen werden, erhalten DerivedData und der Archivpfad jeweils ein ausschließlich für diesen Lauf vorgesehenes Verzeichnis:

RUN_ROOT="$PWD/build/model-validation"
DERIVED_DATA="$RUN_ROOT/DerivedData"
ARCHIVE_PATH="$RUN_ROOT/App.xcarchive"

rm -rf "$RUN_ROOT"
mkdir -p "$RUN_ROOT"

xcodebuild \
  -workspace Example.xcworkspace \
  -scheme Example \
  -configuration Release \
  -destination "generic/platform=iOS" \
  -derivedDataPath "$DERIVED_DATA" \
  -archivePath "$ARCHIVE_PATH" \
  archive

Suchen Sie anschließend das App-Bundle und exportieren Sie die Modellliste:

APP_PATH="$(find "$ARCHIVE_PATH/Products/Applications" -maxdepth 1 -name '*.app' -print -quit)"
test -n "$APP_PATH"

find "$APP_PATH" -type d -name '*.mlmodelc' -print |
  LC_ALL=C sort > "$RUN_ROOT/bundled-models.txt"

test -s "$RUN_ROOT/bundled-models.txt"

Beschränken Sie die Suche dabei nicht auf das Stammverzeichnis der Haupt-App. Ein Modell kann zu einer Erweiterung gehören oder von einem Ressourcen-Bundle bereitgestellt werden. Das Prüfsystem sollte den vollständigen relativen Pfad protokollieren und anschließend anhand der Projektvorgaben feststellen, welche Pfade zulässig sind. Werden zwei gleichnamige Verzeichnisse gefunden, obwohl nur ein Modell erwartet wird, ist sofort zu prüfen, ob die Haupt-App, eine Erweiterung und ein Abhängigkeits-Bundle jeweils eine eigene Kopie enthalten.

Eine verlässliche Baseline statt Schätzungen verwenden

Die Modellgröße hängt von der Struktur und den Kompilierungswerkzeugen ab. Eine pauschale Regel wie „bei einer Zunahme um mehr als einen einheitlichen Prozentsatz fehlschlagen“ ist daher ungeeignet. Zuverlässiger ist es, für jedes Modell eine geprüfte Baseline zu speichern und sowohl einen absoluten als auch einen proportionalen Grenzwert festzulegen. Bei kleinen Modellen ist die absolute Änderung aussagekräftiger, während bei großen Modellen die prozentuale Änderung stärker berücksichtigt werden sollte.

Für die Baseline-Datei genügt ein einfaches tabulatorgetrenntes Format:

Classifier.mlmodelc 8421376
Embedding.mlmodelc  27156480

Der Vergleich muss außerdem hinzugefügte, entfernte und umbenannte Modelle berücksichtigen. Ein neues Modell ist nicht automatisch ein Fehler, sein Zweck muss jedoch zwingend im Änderungsprotokoll dokumentiert werden. Wird ein Modell entfernt, muss der Build blockiert werden, sofern das Manifest nicht ebenfalls geprüft und aktualisiert wurde. Eine Umbenennung ist als „alten Eintrag entfernen und neuen Eintrag hinzufügen“ zu behandeln, damit historische Daten nicht fälschlicherweise übernommen werden.

Das Manifest sollte als Build-Artefakt gespeichert werden. Vertrauliche Konfigurationen, die nicht zum Modellquellbestand gehören, dürfen jedoch nicht hochgeladen werden. Wird das Modell in einem kontrollierten Download-Schritt bezogen, speichern Sie nur den Hash, den logischen Namen und den endgültigen Pfad im Bundle. Download-URLs mit Token dürfen nicht im Protokoll ausgegeben werden.

Häufige Fehler und Prüfungen vor der Auslieferung

Lässt sich ein Modell separat kompilieren, ist aber nicht im App-Bundle enthalten, prüfen Sie zuerst Target Membership, Copy Bundle Resources, Abhängigkeiten von Ressourcen-Bundles und bedingte Build-Einstellungen. Ist das Modell in Debug vorhanden, fehlt jedoch in Release, vergleichen Sie die Ressourcenphasen sowie die Ein- und Ausgabelisten benutzerdefinierter Skripte für beide Konfigurationen.

Tauchen mehrere Modelle mit demselben Namen auf, erstellen Sie zunächst eine vollständige Übersicht ihrer relativen Pfade, statt direkt eines davon zu löschen. Die Haupt-App und eine Erweiterung können tatsächlich jeweils eine eigene Kopie benötigen. Problematisch ist nur eine nicht vorgesehene Duplizierung. Nimmt die Archivgröße plötzlich zu, prüfen Sie außerdem, ob das gesamte Generierungsverzeichnis zur Ressourcenphase hinzugefügt wurde. Dadurch könnten das ursprüngliche Modell, temporäre Dateien und kompilierte Artefakte gemeinsam in die App gelangen.

Führen Sie die Abnahme vor der Auslieferung in dieser Reihenfolge durch:

  1. DEVELOPER_DIR festlegen und die Xcode-Version protokollieren.
  2. coremlcompiler separat in einem isolierten Verzeichnis ausführen.
  3. Ein Hash-Manifest für den Modellquellbestand und das kompilierte Ergebnis erzeugen.
  4. Mit vollständig neuem DerivedData ein Release-Archiv erstellen.
  5. Alle .mlmodelc-Verzeichnisse innerhalb der .xcarchive erfassen.
  6. Zulässige Pfade, erwartete Anzahl und geprüfte Größen-Baselines vergleichen.
  7. Manifest, Befehlsausgaben und Archivkennung für spätere Vergleiche speichern.

Dieses Verfahren bewertet weder die Genauigkeit des Modells noch ersetzt es End-to-End-Inferenztests. Es behandelt ein vorgelagertes Problem, das sich besser automatisieren lässt: Ist das Modell kompilierbar, wurde es korrekt in das Bundle aufgenommen und hat sich der ausgelieferte Inhalt ohne nachvollziehbare Erklärung geändert? Werden diese Ergebnisse in einem maschinenlesbaren Manifest festgehalten, lassen sich zur Laufzeit fehlende Modelle bereits während der Archivierung erkennen.

Häufig gestellte Fragen

Warum reicht ein erfolgreicher Xcode-Build nicht aus?

Ein erfolgreicher Build bestätigt nur einen fehlerfreien Prozessabschluss. Ob das erwartete Modell tatsächlich im ausgelieferten App-Bundle liegt, muss durch eine Prüfung der mlmodelc-Pfade im Archiv belegt werden.

Ab welcher Größenänderung sollte der Build fehlschlagen?

Für jedes Modell sollte eine geprüfte Basisgröße gespeichert werden. Eine kombinierte Grenze aus absolutem und prozentualem Zuwachs ist zuverlässiger als derselbe feste Prozentsatz für alle Modelle.

Sind zwei gleichnamige Modelle im Bundle immer ein Fehler?

Sie sind mindestens ein Prüfsignal. Falls sie nicht zu getrennten Erweiterungszielen gehören, sollten Copy Bundle Resources, Paketabhängigkeiten und erzeugende Skripte auf doppelte Kopiervorgänge geprüft werden.

ArmVMS Cloud-Mac

Wählen Sie für Ihren nächsten Build einen exklusiven physischen Apple-Silicon-Knoten

Mieten Sie ArmVMS M4 und ArmVMS M4 Pro tage-, wochen-, monats- oder quartalsweise. Konfiguration, Knoten und Zusatzoptionen werden vor der Bestellung einzeln bestätigt.

Konfiguration auswählen und Bestellung erstellen