Ausgangslage
Für ArchivBlick sollen rund 150.000 Bilder aus einem SharePoint-Archiv automatisch beschrieben, kategorisiert und durchsuchbar gemacht werden. Die zentrale Frage: Welches Vision-Modell liefert die beste Qualität bei vertretbaren Kosten — und passt in eine einzelne Consumer-GPU?
Erste Versuche liefen lokal auf einem Mac mini M2 Pro mit Ollama und dem LLaVA-7B-Modell. Die Ergebnisse waren brauchbar, aber mit durchschnittlich 60 Zeichen pro Beschreibung viel zu knapp für eine sinnvolle semantische Suche. Außerdem dauerte die Verarbeitung eines einzelnen Bildes ~3 Sekunden — bei 150k Bildern wären das über 5 Tage Dauerlauf.
Die Lösung: Ein GPU-Server bei Trooper.AI mieten, dort leistungsfähigere Modelle laufen lassen und die Ergebnisse zurück auf den Mac mini transferieren.
Hardware & Setup
Trooper.AI als Plattform
Trooper.AI ist ein deutscher GPU-Mietservice, bei dem man Server stunden- oder wochenweise buchen kann. Der Server kommt mit vorinstallierten Templates — für unseren Fall: vLLM OpenAPI (der Inference-Server) und Jupyter Notebook (für schnelle Tests).
Wichtige Besonderheiten, die wir gelernt haben:
- SSH-Port ist randomisiert (5-stellig, 10000–59999), nicht Port 22
- Kein UFW installieren! — Der Server hat eine native Network-Firewall davor
- Tailscale funktioniert immer, auch wenn der öffentliche SSH-Port gelegentlich blockiert
- Templates laufen als systemd-Services — Debugging über
journalctl -u <name> -f - Immer in tmux arbeiten — SSH-Verbindungsabbrüche töten sonst laufende Jobs
Software-Stack
| Komponente | Version | Zweck |
|---|---|---|
| vLLM | v0.20.1 | Inference-Server mit OpenAI-kompatibler API |
| Python | 3.10+ | Batch-Scripts mit asyncio + aiohttp |
| Tailscale | aktuell | VPN-Zugang (WireGuard), umgeht Firewall-Probleme |
| tmux | — | Persistente Terminal-Sessions für Langzeit-Jobs |
vLLM ist der entscheidende Baustein: Es bietet Continuous Batching, das heißt mehrere Anfragen werden gleichzeitig auf der GPU verarbeitet. Im Vergleich zu Ollama (sequenziell) ist das 5–10× schneller bei gleicher Hardware.
Getestete Modelle
Drei Generationen von Vision-Modellen wurden mit denselben 500 Testbildern verglichen: 400 Einzelfotos und 100 Filmstrips (Video-Thumbnails in einem Bild).
- Laufzeit: Ollama (Mac mini)
- Quantisierung: Q4_0
- VRAM: ~4 GB
- Ergebnis: ⌀ 60 Zeichen
- Geschwindigkeit: ~1 Bild/3s
- Nur Englisch
- Keine strukturierte Ausgabe
- Laufzeit: vLLM (Trooper)
- Quantisierung: BF16 (full)
- VRAM: ~16 GB
- Ergebnis: ⌀ 500 Zeichen
- Geschwindigkeit: ~30 Bilder/Min
- Deutsch möglich
- JSON-Output stabil
- Laufzeit: vLLM (Trooper)
- Quantisierung: AWQ 4-bit Marlin
- VRAM: ~18 GB Weights
- Ergebnis: ⌀ 773 Zeichen
- Geschwindigkeit: ~17 Bilder/Min
- Exzellentes Deutsch
- JSON-Output fehlerfrei
Warum AWQ statt FP8?
Das 32B-Modell in voller Präzision (FP16) benötigt ~64 GB VRAM — doppelt so viel wie unsere Karte hat. FP8-Quantisierung hätte ~32,5 GB gebraucht — das passt technisch, aber lässt keinen Raum für den KV-Cache, den vLLM für jede parallele Anfrage braucht.
AWQ 4-bit komprimiert die Weights auf ~18 GB und lässt damit ~14 GB für den KV-Cache übrig. Das reicht für 4 parallele Anfragen — genug für einen stabilen Batch-Betrieb.
AWQ 4-bit statt FP8 8-bit
FP8 hätte theoretisch bessere Qualität geliefert, passt aber nicht in 32 GB VRAM wenn gleichzeitig Batch-Verarbeitung laufen soll. AWQ 4-bit ist der pragmatische Kompromiss — und die Qualitätsunterschiede sind in der Praxis kaum messbar.
Optimierungen
Der Weg zum stabilen 32B-Batch war nicht geradlinig. Drei Iterationen waren nötig, bis alles fehlerfrei lief.
Iteration 1: Erster Start (50% Fehler)
Der erste 32B-Batch-Lauf startete mit den gleichen Einstellungen wie beim 7B: Concurrency 8, Timeout 120 Sekunden, Standard-AWQ-Kernels. Ergebnis: 50% der JSON-Anfragen liefen in Timeouts.
Iteration 2: AWQ Marlin entdeckt
In den vLLM-Logs fand sich ein Hinweis:
WARNING: Using AWQ quantization. For better performance,
consider using 'awq_marlin'. AWQ Marlin sind optimierte Matrix-Multiplikations-Kernels, die speziell für 4-bit-Gewichte entwickelt wurden. Sie nutzen die Tensor-Cores der GPU effizienter als die Standard-AWQ-Implementierung.
Iteration 3: Die drei Stellschrauben
| Parameter | Vorher | Nachher | Wirkung |
|---|---|---|---|
--quantization | awq | awq_marlin | ~2× schnellere Inference |
| Concurrency | 8 | 4 | Mehr KV-Cache pro Request |
| Timeout | 120s | 300s | Sicherheitspuffer für JSON |
Die finale vLLM-Konfiguration:
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-VL-32B-Instruct-AWQ \
--quantization awq_marlin \
--limit-mm-per-prompt '{"image": 1}' \
--max-num-seqs 4 \
--max-model-len 8192 \
--gpu-memory-utilization 0.92 \
--dtype float16 \
--port 8000 dtype muss float16 sein, nicht bfloat16
AWQ-quantisierte Modelle erwarten float16 als Datentyp. Mit bfloat16 (dem Standard bei vLLM) stürzt der Server beim Start ab. Das steht nicht prominent in der Dokumentation — man findet es erst in den Fehlermeldungen.
AWQ Marlin: Der unterschätzte Gamechanger
Der Wechsel von Standard-AWQ zu AWQ Marlin hat die Fehlerrate von 50% auf 0% gesenkt und die Geschwindigkeit verdoppelt — bei identischer Modellqualität. Die Marlin-Kernels sind keine Approximation, sondern einfach effizienterer Code für die gleiche Mathematik.
Ergebnisse im Vergleich
Erfolgsrate der Batch-Verarbeitung
Die Filmstrip-Fehler beim 32B entstehen durch das max-model-len von 8192 Tokens. Filmstrips sind große Bilder (6–12 Thumbnails nebeneinander), die mehr Tokens verbrauchen als Einzelfotos.
Geschwindigkeit (Bilder/Minute)
Textlänge (Ø Zeichen)
Die Textlänge allein ist kein Qualitätsmaß — aber sie zeigt, wie viel Detail das Modell wahrnimmt. LLaVA liefert Einzeiler wie „A lighthouse on a rocky coast". Qwen 7B beschreibt Szene, Stimmung und sichtbare Objekte. Qwen 32B ergänzt zusätzlich Tageszeit-Schätzungen, Perspektiven-Analyse und emotionale Einordnung.
Gesamtübersicht
| Eigenschaft | LLaVA 7B | Qwen 7B | Qwen 32B AWQ |
|---|---|---|---|
| Beschreibungslänge | ⌀ 60 Zeichen | ⌀ 500 Zeichen | ⌀ 773 Zeichen |
| Sprache | nur Englisch | Deutsch | exzellentes Deutsch |
| JSON-Output | nicht möglich | stabil (99,8%) | fehlerfrei (100%) |
| Filmstrip-Verständnis | schlecht | gut | sehr gut |
| Geschwindigkeit | ~20 Bilder/min | ~30 Bilder/min | ~17 Bilder/min |
| VRAM-Bedarf | ~4 GB | ~16 GB | ~18 GB + 14 GB KV |
| Hardware | Mac mini M2 Pro | RTX 4080 Super | RTX 4080 Super |
Qualitätsanalyse
Ein direkter Textvergleich zeigt den Qualitätssprung am besten. Hier dasselbe Foto (ein Leuchtturm an der Küste) durch alle drei Modelle:
A lighthouse on a cliff overlooking the ocean at dusk.
Das Bild zeigt eine Küstenlandschaft mit einem Leuchtturm, der auf einem felsigen Vorsprung steht. Der Himmel ist bewölkt und dunkel, was auf Dämmerung hindeutet. Im Vordergrund sind ein helles Muster auf dem Boden zu sehen. Die Stimmung ist ruhig und etwas geheimnisvoll.
Das Bild zeigt eine Szene bei Dämmerung oder frühem Abend, bei der ein Leuchtturm im Vordergrund steht. Der Leuchtturm ist weiß mit einem markanten roten Streifen und befindet sich auf einem Felsvorsprung über dem Meer. Der Himmel ist dunkel, mit einigen Wolken, die teilweise von einem schwachen Licht durchflutet sind. Im Vordergrund ist ein helles Muster auf dem Boden zu sehen, das möglicherweise ein Hubschrauberlandeplatz ist.
Der 32B erkennt Details, die dem 7B entgehen: den roten Streifen am Leuchtturm, interpretiert das Bodenmuster als möglichen Hubschrauberlandeplatz und analysiert die Lichtstimmung differenzierter.
Strukturierter JSON-Output (32B)
{
"kurzbeschreibung": "Ein stürmischer Ozean mit großen Wellen,
die sich an Felsen brechen.",
"ausfuehrlich": "Das Bild zeigt eine dynamische Meereslandschaft,
in der hohe Wellen sich an dunklen Felsen brechen...",
"objekte": ["Wellen", "Felsen", "Meer"],
"personen": "keine",
"umgebung": "Ozean, Felsküste, stürmische Bedingungen",
"stimmung": "dynamisch, energiegeladen, etwas bedrohlich",
"farben": ["Blau", "Weiß", "Dunkelbraun"],
"tageszeit": "Dämmerung",
"perspektive": "Mittel",
"tags": ["Ozean", "Wellen", "Felsen", "Sturm",
"Meereslandschaft", "Natur", "Küste"],
"text_im_bild": "kein Text"
} Dieses JSON-Schema wird bei 100% der Fotos fehlerfrei eingehalten. Der 7B erreicht hier 99,8%. LLaVA unterstützt kein strukturiertes Output.
Filmstrip-Verarbeitung
Filmstrips sind besonders anspruchsvoll: 6–12 Video-Thumbnails in einem einzigen Bild. Das Modell muss erkennen, dass es sich um eine Sequenz handelt, und die Entwicklung der Szene beschreiben.
Das Video zeigt eine Küstenlandschaft mit einem markanten Leuchtturm, der auf einer felsigen Halbinsel steht. Der Hintergrund ist geprägt von einem offenen, ruhigen Ozean unter einem klaren Himmel mit einigen Wolken. Die Szene bleibt über die einzelnen Frames hinweg weitgehend unverändert, mit dem Fokus auf der majestätischen Natur und der ruhigen Atmosphäre.
Live-Test: GoPro-Kurvenfotos
Neben der Batch-Verarbeitung wurde das 32B-Modell auch für eine ungewöhnliche Aufgabe eingesetzt: Die Analyse von Motorrad-Kurvenfotos einer GoPro-Helmkamera — inklusive Schätzung von Kurvenradius und Schräglage.
| Szenerie | Schmale Waldstraße mit Herbstlaub, Motorrad in Linkskurve |
| Kurvenwinkel | ca. 60–70° |
| Schräglage | ca. 20–25° |
| Besonderheiten | Nasses Laub auf der Fahrbahn erkannt, Herbstlicht-Stimmung |
| Szenerie | Bergstraße mit Panoramablick, Motorrad in Rechtskurve |
| Kurvenwinkel | ca. 45–55° |
| Schräglage | ca. 15–20° |
| Besonderheiten | Leitplanke und Gegenverkehr-Spur erkannt, alpine Landschaft |
Die Schätzungen sind keine exakten Messungen, aber sie zeigen, dass das Modell räumliche Geometrie versteht. Für eine semantische Suche im Archiv wäre das ausreichend genau.
Kosten & Wirtschaftlichkeit
Testlauf: 500 Bilder
| Modell | Laufzeit | Anteilige Kosten |
|---|---|---|
| Qwen 7B (500 Bilder, 4 Aufgaben) | ~20 min | ~0,19 € |
| Qwen 32B AWQ Marlin (500 Bilder, 4 Aufgaben) | ~35 min | ~0,33 € |
Hochrechnung: 150.000 Bilder
Option A: Qwen 7B
- Laufzeit: ~8–10 Stunden
- Kosten: ~5–6 €
- Qualität: ⌀ 500 Zeichen, gut
- Fehlerrate: ~0,4%
Option B: Qwen 32B AWQ
- Laufzeit: ~16–20 Stunden
- Kosten: ~10–12 €
- Qualität: ⌀ 773 Zeichen, sehr gut
- Fehlerrate: 0% (Fotos)
Beide Optionen sind wirtschaftlich — selbst der 32B-Lauf kostet weniger als ein einziges Cloud-API-Bild bei GPT-4o Vision.
Empfehlung für den Produktionslauf
Den 7B für den Erst-Durchlauf über alle 150k Bilder verwenden — schnell, günstig, zuverlässig. Anschließend selektiv den 32B für wichtige oder komplexe Bilder nachfahren lassen.
Erkenntnisse
AWQ Marlin ist nicht optional
Der Wechsel von Standard-AWQ zu Marlin-Kernels hat die Fehlerrate von 50% auf 0% gesenkt und die Geschwindigkeit verdoppelt. Das war nicht Tuning — das war der Unterschied zwischen „funktioniert nicht" und „funktioniert perfekt".
Concurrency muss zum Modell passen
Der 7B läuft stabil mit 40 parallelen Anfragen. Der 32B bricht bei 8 schon zusammen. Die richtige Concurrency hängt vom VRAM-Bedarf des KV-Cache pro Request ab — nicht von CPU oder Netzwerk.
vLLM >> Ollama für Batch
Ollama ist großartig für interaktive Nutzung, aber für Batch-Verarbeitung ist vLLM mit Continuous Batching 5–10× schneller. Bei 150k Bildern ist das der Unterschied zwischen 1 Tag und 1 Woche.
Organisatorische Lessons Learned
- Tailscale ist Pflicht: Der öffentliche SSH-Port war mehrfach nicht erreichbar. Tailscale funktioniert immer — und ist sicherer.
- tmux vor allem anderen: SSH-Verbindungen brechen ab. Ohne tmux: Job tot. Mit tmux: einfach reconnecten.
- Resume-Logik in jedem Batch-Script: Jedes Script prüft, welche Bilder schon verarbeitet sind. Ein Absturz bei Bild 80.000 kostet dann Sekunden, nicht Stunden.
- Keine UFW auf Trooper: Die Plattform hat eine eigene Firewall. UFW würde den Zugriff blockieren.
Ausblick & nächste Schritte
Produktionslauf 150k Bilder
7B-Modell über alle Bilder. Geschätzte Laufzeit: 8–10 Stunden. Kosten: ~5–6 €.
Embeddings erzeugen
BGE-M3 für Text-Embeddings, SigLIP-2 für Bild-Embeddings. Auf dem gleichen Trooper-Server.
Qdrant + Such-UI
Vektordatenbank auf dem Mac mini, Streamlit-UI für die Suche. Dauerbetrieb ohne GPU-Kosten.
Weitere Modelle testen
InternVL2.5-8B oder Gemma 3 12B könnten ein besseres Preis-Leistungs-Verhältnis bieten.
ArchivBlick ist ein aktives Projekt. Mehr dazu im ersten ArchivBlick-Artikel und auf der Projektseite.