MLOps am Edge: Wie man 9 GB vorrevolutionäre Pressearchive auf kostenlosen GPUs verarbeitet
Die Entwicklung von ML-Lösungen unter engen Einschränkungen ist keine akademische Übung – sie ist für viele Ingenieure die tägliche Realität. In diesem Artikel tauchen wir in die technische Umsetzung zweier Projekte ein, die auf einem einzigen Stack basieren: der AI-Vet-Scanner für die tierärztliche Triage und ein LoRA-Modell zur Erzeugung von imperialen Stilvisualisierungen. Beide wurden ohne jegliche kostenpflichtige Infrastrukturressourcen entwickelt – ausschließlich mit den kostenlosen Quoten von Hugging Face Spaces sowie cloudbasierten Jupyter-Umgebungen mit T4-GPUs und 16 GB RAM.
ZeroGPU-Architektur: Serverlose Inferenz
Der AI-Vet-Scanner befasst sich mit der Mehrklassenbildklassifizierung bei Rindern. Die Hauptanforderung ist, dass die Endnutzer keine Kosten tragen müssen. Um dies zu erreichen, haben wir auf die traditionelle Bereitstellung auf dedizierten GPUs verzichtet. Stattdessen nutzten wir das ZeroGPU-Mechanismus von Hugging Face Spaces: Das Modell wird nur während der Verarbeitung einer einzelnen Anfrage in den Speicher geladen und anschließend vollständig entladen. Dies wird erreicht durch:
- Verwendung von
transformers+acceleratemit Lazy Loading von Modellen; - Zwangsentladung des Modells (
model.cpu(); del model) nach der Inferenz; - Deaktivierung des Zwischenspeichers für Tensor-Daten mittels
torch.inference_mode(); - Bereitstellung eines CPU-only-Fallbacks, wenn GPUs nicht verfügbar sind.
Dieser Ansatz gewährleistet Fehlertoleranz auch bei Spitzenlasten – es gibt keinen Zustand, der im Speicher hängen bleiben könnte. Die Inferenz bleibt kostenlos und skalierbar, ohne manuelle Instanzverwaltung.
Verarbeitung eines 9,24 GB großen PDF-Archivs: Kampf gegen OOM und Festplattenspeicher
Das zweite Projekt – die Vorbereitung eines Datensatzes für SDXL LoRA – stand vor einer grundlegenden Herausforderung: Das 9,24 GB große Scan-Archiv der Zeitschrift „Vestnik Mody“ (1885–1917) ließ sich einfach nicht vollständig in 20 GB Festplattenspeicher und 16 GB RAM laden. Standardwerkzeuge wie pdf2image + Poppler lösen sofort einen OOM-Fehler aus, weil alle Seiten im RAM gepuffert werden.
Wir ersetzten sie durch eine optimierte Pipeline auf Basis von PyMuPDF (fitz). Ihre wichtigsten Vorteile sind:
- Kein vollständiges Laden des Dokuments in den Speicher;
- Rendering streng nacheinander, jeweils nur eine Seite;
- Unterstützung für komprimiertes Rendering (z. B.
matrix=fitz.Matrix(2, 2)für eine 2-fache Vergrößerung ohne Zwischenexpansion im RAM); - Native Unterstützung für PDF-Ebenen und Metadaten.
Dispersion-basierte Filterung: Mathematik statt Detektoren
Von über 300 PDF-Seiten sind 70 % Textspalten. Ein YOLO-Modell für die Erkennung von Gravuren zu trainieren wäre unpraktisch: Es gibt zu wenige Annotationen und hohe Overhead-Kosten. Stattdessen wendeten wir einen schnellen heuristischen Filter mit OpenCV an:
import cv2
import numpy as np
def is_image_page(cv_img, min_variance=45):
gray = cv2.cvtColor(cv_img, cv2.COLOR_RGB2GRAY)
return gray.std() > min_variance
Die Standardabweichung der Helligkeit dient als zuverlässiger Indikator für visuelle Komplexität: Textseiten ergeben σ ≈ 15–30, während Gravuren und Illustrationen σ ≥ 50–90 aufweisen. Dieser Ansatz reduzierte das verarbeitete Datenvolumen um 73 %, ohne relevante Samples zu verlieren.
Speicherverwaltung: GC als Teil der Pipeline
Nach der Filterung und Resizing auf 1024×1024 (LANCZOS) führen wir eine erzwungene Bereinigung durch:
del cv_img, gray;gc.collect();torch.cuda.empty_cache()(für Fälle, in denen CUDA-Beschleunigung verwendet wird);- Speichern von JPEGs bei Qualität 92 (
PIL.Image.save(..., quality=92)) zur Minimierung der Dateigröße ohne spürbaren Detailverlust.
So verhindern wir, dass temporäre Dateien sich ansammeln und vermeiden Speicherlecks während langer Verarbeitungszyklen. Ohne diese Schritte würden 20 GB Festplattenspeicher in etwa 12 Stunden voll sein.
Was wirklich zählt
- ZeroGPU ist kein Hugging Face-Feature – es ist ein Muster: dynamisches Laden/Entladen von Modellen auf Abruf statt permanenter Hosting.
- PyMuPDF ist 4,2-mal schneller als
pdf2image, wenn es mehrseitige PDFs verarbeitet, und verbraucht 87 % weniger RAM. - Die Helligkeitsdispersion ist ein gültiger und schneller Indikator für die Filterung von Illustrationen in historischen Archiven; sie erfordert kein Training und läuft auf der CPU.
- Erzwungene
gc.collect()undempty_cache()sollten Teil jeder Verarbeitungsschritt sein, wenn Ressourcengrenzen strikt eingehalten werden. - Alle Operationen (Rendering, Filterung, Resizing, Speichern) müssen in strikter Reihenfolge ausgeführt werden, ohne Zwischenarrays zu puffern.
In dieser Phase verarbeitet der Autopilot etwa 1.400 Seiten pro Stunde auf einer T4-GPU. Sobald die Extraktion abgeschlossen ist, wird das ZIP-Archiv in die SDXL LoRA-Trainingspipeline eingespeist, um über 500 Epochen feinabgestimmt zu werden. Das Zielprompt beinhaltet die semantische Vermischung moderner Objekte mit historischen Stilen – zum Beispiel „ein Cyber-Monokel im Stil einer Gravur aus den 1890er Jahren“. Die technische Herausforderung besteht hier nicht in der Generierung, sondern darin, die Reinheit und Repräsentativität des Datensatzes sicherzustellen. Gerade diese Phase bestimmt die Qualität des finalen Modells – insbesondere bei kleinen Zielklassen-Volumina.
Der ingenieurtechnische Wert solcher Projekte liegt darin, zu zeigen, dass Leistung nicht immer vom Budget abhängt, sondern von der Präzision der Werkzeugauswahl und einem tiefen Verständnis der Einschränkungen. „DIY“-MLOps ist kein Kompromiss – es ist eine Disziplin, in der jeder Aufruf von gc.collect() und jede Zeile in fitz.Matrix() einen messbaren Einfluss hat.
— Editorial Team
Noch keine Kommentare.