ML-Service

Bilderkennung mit SigLIP 2

Python/FastAPI-Sidecar für Image-Classification. Übernimmt Embedding (SigLIP 2 SO400M, 1152-dim), Qdrant-I/O, LinearSVC-Training und Klassifikation mit kNN-Fallback. Läuft neben dem Node.js core-backend im privaten Netzwerk.

Verantwortlichkeiten

Der ML-Service ist bewusst schmal geschnitten und stateless bzgl. Persistenz:

  • Frames/Bilder mit SigLIP 2 SO400M in 1152-dim Unit-Vektoren einbetten.
  • Vektoren in eine Qdrant-Collection pro Schule upserten.
  • Deduplizierung einer Collection via Farthest-Point-Sampling.
  • Training eines kalibrierten LinearSVC aus einer Qdrant-Collection; Rückgabe des joblib-Blobs.
  • Klassifikation eines neuen Bildes mit Qdrant-Similarity-Gate, LinearSVC-Top-1/Top-k-Prediction und kNN-Fallback.
Nicht im Scope
  • ffmpeg-Frame-Extraktion (liegt im core-backend).
  • Sharpness-Filterung (liegt im core-backend).
  • Persistenz der Classifier-Blobs (liegt in PostgreSQL).
  • Authentifizierung (Service ist im privaten Netzwerk erreichbar).

HTTP-Endpunkte

MethodePfadZweck
GET/healthReadiness + Pipeline-Konfiguration
POST/embed-framesBatch-Embedding hochgeladener Frames
POST/dedupCollection deduplizieren (FPS)
POST/trainKlassifikator trainieren (base64-joblib in JSON)
POST/load-classifierKlassifikator-Blob unter cache_key im RAM cachen
POST/classifyEinzelbild klassifizieren
DELETE/collections/{name}Qdrant-Collection löschen
DELETE/collections/{name}/classes/{label}Alle Vektoren einer Klasse entfernen

Blob-tragende Endpunkte (/embed-frames, /classify, /load-classifier) nutzen multipart/form-data. Ausführliche Endpunkt-Karten finden sich in der API-Referenz.

/classify Entscheidungs-Flow

  1. Bild einbetten (optional mit Center-Crop).
  2. Qdrant-NN-Suche. Ist die beste Similarity < SIMILARITY_GATE, liefert die Antwort unknown.
  3. Bei mehr als einer Klasse: predict_topk mit MIN_CONFIDENCE_RATIO(Default 1.5). Die Ratio wird verwendet, weil die Softmax-Fallback-Verteilung bei vielen Klassen (z. B. 37) auch für sichere Predictions nur wenige Prozentpunkte Vorsprung liefert.
  4. Bei genau einer Klasse: Diese Klasse mit Confidence aus der Qdrant-Similarity zurückgeben.
  5. Sonst kNN-Fallback über die Qdrant-Treffer mit absolutem MIN_CONFIDENCE_GAP (in Cosinus-pp), da Cosinus in [0,1] beschränkt ist.

Konfiguration (Env-Vars)

Alle Variablen sind optional (siehe config.py).

VariableDefaultBeschreibung
QDRANT_HOSTlocalhostQdrant gRPC-Host
QDRANT_PORT6333Qdrant-Port
ML_DEVICEautoAuto, cpu, cuda, mps
EMBED_BATCH_SIZE32Batch-Größe für SigLIP-Embedding-Calls
CLASSIFIER_CACHE_MAX32LRU-Größe für Classifier-Cache im Prozess
CROP_MODEcenterNone oder 87,5% Center-Crop
AUG_PRESEToffOff, light, medium, strong
AUG_COUNT0Augmentierungen pro Quell-Frame
SIMILARITY_GATE0.75Cosinus-Schwelle zum Akzeptieren einer Prediction
MIN_CONFIDENCE_RATIO1.5LinearSVC: minimales Top-1/Top-2-Verhältnis
MIN_CONFIDENCE_GAP5kNN-Fallback: minimaler Cosinus-pp Gap
NORM_LOW / NORM_HIGH0.55 / 0.95Bereich zur Normalisierung von Cosinus auf [0,100]

Lokales Setup

bash
cd ml-service
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000

Qdrant muss laufen (das Backend-Workspace liefert npm run start:qdrant als Docker-Container).

Docker

bash
docker build -t daz-ml-service .
docker run --rm -p 8000:8000 \
  -e QDRANT_HOST=host.docker.internal \
  daz-ml-service
Modell-Download
Der erste Call an /embed-* oder /classify lädt ca. 3,2 GB Modell-Gewichte von HuggingFace. In Produktion sollte der HuggingFace-Cache gemountet oder ins Image gebacken werden.

Projekt-Layout

text
ml-service/
├── main.py                  FastAPI-App: Lifespan, Dependencies, Router
├── schemas.py               Pydantic Request/Response-Modelle
├── embedder.py              SigLIP 2 SO400M Wrapper
├── classifier.py            LinearSVC + Kalibrierung + Single-Class-Fallback
├── preprocessing.py         Center-Crop + Augmentierungs-Presets
├── qdrant_repo.py           Qdrant-Client (upsert / scroll / FPS / search)
├── config.py                Env-basierte Konfiguration
├── services/
│   ├── classifier_cache.py  LRU-Cache + resolve_classifier
│   └── classify.py          Framework-freie Klassifikations-Pipeline
├── requirements.txt
└── Dockerfile

Beziehung zum core-backend

text
mobile app ──HTTPS──> core-backend ──HTTP──> ml-service ──gRPC──> Qdrant
                     │                      │
                     └─ Postgres ─> Classifier-Blobs + Fachbegriffe
KomponenteVerantwortung
core-backendVideo-Upload, ffmpeg-Frame-Extraktion, Sharpness-Filter, Classifier-Blob-Storage (Postgres), Auth, RBAC, Übersetzungen
ml-serviceSigLIP-2-Inferenz, LinearSVC-Training, Qdrant als Vektorstore, LRU-Cache für kürzlich genutzte Classifier-Blobs