Zurück zur Startseite

MLOps auf kostenlosen GPUs: Verarbeitung von PDF-Archiven

Der Artikel behandelt zwei praktische Projekte: veterinäres Screening auf Basis von CV und Bildgenerierung im Stil des Russischen Reiches. Der Hauptfokus liegt auf technischen Lösungen für die Arbeit mit begrenzten Ressourcen: ZeroGPU, PyMuPDF, Helligkeitsvarianz-Filterung und erzwungene Speicherverwaltung.

MLOps ohne Budget: wie wir 9 GB vorrevolutionäre Zeitschriften verarbeitet haben
Advertisement 728x90

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 + accelerate mit 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.

Google AdInline article slot

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:

Google AdInline article slot
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.

Google AdInline article slot

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() und empty_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

Advertisement 728x90

Weiterlesen