RAG für Bilder: Vision models und visuelle Suche
Umfassender Leitfaden zur Integration von Bildern in Ihr RAG-System: Vision models, multimodal embeddings, Indexierung und visuelle Suche mit GPT-4V, Claude Vision und CLIP.
RAG für Bilder: Vision-Modelle und visuelle Suche
Die traditionellen RAG-Systeme beschränken sich auf Text. Dabei sind viele Unternehmensinformationen visuell: Produktfotos, Screenshots, Grafiken, technische Zeichnungen. Image RAG erlaubt es, in diesen visuellen Inhalten mit derselben Präzision wie bei einem textuellen RAG zu indexieren und zu suchen.
Warum Bilder in den RAG integrieren?
Visuelle Daten im Unternehmen
- E-commerce : 70% der Kaufentscheidungen werden durch Produktbilder beeinflusst
- Support technique : Screenshots beschleunigen die Lösung von Tickets um 60%
- Documentation : Eine Grafik ist oft mehr wert als eine Seite Text
- Conformité : Baustellenfotos, Zustandsdokumentationen, visuelle Belege
Konkrete Anwendungsfälle
| Secteur | Usage | Exemple de requête |
|---|---|---|
| E-commerce | Recherche visuelle | "Trouve des robes similaires à cette photo" |
| Immobilier | Analyse de biens | "Montre-moi des cuisines équipées modernes" |
| Support IT | Diagnostic | "Quel est ce message d'erreur ?" |
| Manufacturing | Contrôle qualité | "Cette pièce présente-t-elle un défaut ?" |
Architektur eines Image RAG
Gesamtübersicht
┌─────────────────────────────────────────────────────────────┐
│ IMAGE RAG PIPELINE │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Image │───▶│ Vision │───▶│ Embedding │ │
│ │ Input │ │ Model │ │ (CLIP/SigLIP) │ │
│ └──────────┘ └──────────────┘ └──────────────────┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌──────────────┐ ┌──────────────────┐ │
│ │ │ Description │ │ Vector Store │ │
│ │ │ textuelle │ │ (Qdrant/Pine) │ │
│ │ └──────────────┘ └──────────────────┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌─────────────────────────────────┐ │
│ └────────▶│ Retrieval multimodal │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Génération (VLM/LLM) │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Die zwei Ansätze
1. Description + RAG textuel
- Das Vision-Model beschreibt das Bild in Text
- Der Text wird klassisch indexiert
- Einfacher, verliert jedoch visuelle Informationen
2. Native multimodale Embeddings
- Das Bild wird direkt in einen Vektor konvertiert
- Bewahrt die vollständige visuelle Information
- Ermöglicht Bild-zu-Bild-Suche
Vision-Modelle für den RAG
Proprietäre Modelle
| Modèle | Résolution max | Coût | Forces |
|---|---|---|---|
| GPT-4V | 2048x2048 | $0.00765/image (low) | Raisonnement complexe, OCR excellent |
| Claude 3.5 Sonnet Vision | 8192x8192 | $0.003/image | Analyse détaillée, sécurité |
| Gemini 1.5 Pro | Illimité | $0.001315/image | Multi-images, contexte long |
Open-Source-Modelle
| Modèle | Params | VRAM | Usage |
|---|---|---|---|
| LLaVA 1.6 | 34B | 24GB | Description générale |
| CogVLM2 | 19B | 16GB | Compréhension fine |
| InternVL2 | 76B | 48GB | Performance SOTA |
| Qwen-VL-Max | 72B | 48GB | Multilingue |
Multimodale Embedding-Modelle
| Modèle | Dimension | Langues | Open source |
|---|---|---|---|
| CLIP (OpenAI) | 512/768 | EN principalement | Oui |
| SigLIP | 384-1152 | Multilingue | Oui |
| Jina CLIP v2 | 1024 | 89 langues | Oui |
| Cohere Embed v3 | 1024 | 100+ langues | Non |
Praktische Implementierung
Étape 1 : Extraction et description des images
DEVELOPERpythonimport base64 from openai import OpenAI def describe_image_for_rag(image_path: str, context: str = "") -> dict: """ Erzeugt eine RAG-optimierte Beschreibung eines Bildes. """ client = OpenAI() # Das Bild in base64 kodieren with open(image_path, "rb") as f: image_data = base64.b64encode(f.read()).decode("utf-8") # Den MIME-Typ bestimmen mime_type = "image/jpeg" if image_path.endswith((".jpg", ".jpeg")) else "image/png" prompt = """Analyse cette image pour un système RAG. Fournis: 1. **Description générale** (2-3 phrases) 2. **Éléments clés** (liste à puces des objets/concepts importants) 3. **Texte visible** (tout texte lisible dans l'image) 4. **Métadonnées suggérées** (catégorie, tags pertinents) Sois exhaustif mais concis. L'objectif est de permettre la recherche textuelle sur cette image.""" if context: prompt += f"\n\nContexte additionnel: {context}" response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "user", "content": [ {"type": "text", "text": prompt}, { "type": "image_url", "image_url": { "url": f"data:{mime_type};base64,{image_data}", "detail": "high" } } ] } ], max_tokens=1000 ) description = response.choices[0].message.content return { "image_path": image_path, "description": description, "model": "gpt-4o", "tokens_used": response.usage.total_tokens }
Étape 2 : Embeddings multimodaux avec CLIP
DEVELOPERpythonimport torch from PIL import Image from transformers import CLIPProcessor, CLIPModel class MultimodalEmbedder: def __init__(self, model_name: str = "openai/clip-vit-large-patch14"): self.device = "cuda" if torch.cuda.is_available() else "cpu" self.model = CLIPModel.from_pretrained(model_name).to(self.device) self.processor = CLIPProcessor.from_pretrained(model_name) def embed_image(self, image_path: str) -> list[float]: """Erzeugt ein Embedding für ein Bild.""" image = Image.open(image_path).convert("RGB") inputs = self.processor(images=image, return_tensors="pt").to(self.device) with torch.no_grad(): embedding = self.model.get_image_features(**inputs) # Für Kosinusähnlichkeit normalisieren embedding = embedding / embedding.norm(dim=-1, keepdim=True) return embedding.cpu().squeeze().tolist() def embed_text(self, text: str) -> list[float]: """Erzeugt ein Embedding für Text (im selben Raum wie die Bilder).""" inputs = self.processor(text=[text], return_tensors="pt", padding=True).to(self.device) with torch.no_grad(): embedding = self.model.get_text_features(**inputs) embedding = embedding / embedding.norm(dim=-1, keepdim=True) return embedding.cpu().squeeze().tolist() def compute_similarity(self, image_path: str, text: str) -> float: """Berechnet die Ähnlichkeit Bild-zu-Text.""" img_emb = torch.tensor(self.embed_image(image_path)) txt_emb = torch.tensor(self.embed_text(text)) return (img_emb @ txt_emb).item()
Étape 3 : Indexation dans Qdrant
DEVELOPERpythonfrom qdrant_client import QdrantClient from qdrant_client.models import ( VectorParams, Distance, PointStruct, Filter, FieldCondition, MatchValue ) class ImageRAGIndex: def __init__(self, collection_name: str = "image_rag"): self.client = QdrantClient(url="http://localhost:6333") self.collection_name = collection_name self.embedder = MultimodalEmbedder() def create_collection(self, vector_size: int = 768): """Erstellt die Collection mit zwei Vektorräumen.""" self.client.recreate_collection( collection_name=self.collection_name, vectors_config={ # Visuelles Embedding (CLIP) "visual": VectorParams( size=vector_size, distance=Distance.COSINE ), # Textuelles Embedding (Beschreibung) "textual": VectorParams( size=1536, # Ada-002 oder ähnlich distance=Distance.COSINE ) } ) def index_image( self, image_id: str, image_path: str, description: str, text_embedding: list[float], metadata: dict = None ): """Indexiert ein Bild mit seinen beiden Embeddings.""" visual_embedding = self.embedder.embed_image(image_path) point = PointStruct( id=hash(image_id) % (2**63), vector={ "visual": visual_embedding, "textual": text_embedding }, payload={ "image_id": image_id, "image_path": image_path, "description": description, **(metadata or {}) } ) self.client.upsert( collection_name=self.collection_name, points=[point] ) def search_by_text(self, query: str, limit: int = 5) -> list[dict]: """Suche mittels textueller Anfrage.""" query_embedding = self.embedder.embed_text(query) results = self.client.search( collection_name=self.collection_name, query_vector=("visual", query_embedding), # CLIP text -> visual limit=limit ) return [ { "image_path": r.payload["image_path"], "description": r.payload["description"], "score": r.score } for r in results ] def search_by_image(self, image_path: str, limit: int = 5) -> list[dict]: """Suche nach ähnlichen Bildern.""" query_embedding = self.embedder.embed_image(image_path) results = self.client.search( collection_name=self.collection_name, query_vector=("visual", query_embedding), limit=limit ) return [ { "image_path": r.payload["image_path"], "description": r.payload["description"], "score": r.score } for r in results ]
Étape 4 : Génération avec contexte visuel
DEVELOPERpythondef generate_with_images( query: str, retrieved_images: list[dict], client: OpenAI ) -> str: """ Erzeugt eine Antwort unter Verwendung der abgerufenen Bilder als Kontext. """ # Den multimodalen Inhalt vorbereiten content = [ { "type": "text", "text": f"""Tu es un assistant qui répond aux questions en utilisant les images fournies comme source d'information. Question de l'utilisateur: {query} Images disponibles:""" } ] # Jedes Bild mit seiner Beschreibung hinzufügen for i, img in enumerate(retrieved_images[:3], 1): # Max 3 Bilder with open(img["image_path"], "rb") as f: img_data = base64.b64encode(f.read()).decode("utf-8") content.append({ "type": "text", "text": f"\n**Image {i}** (score: {img['score']:.2f}):\n{img['description']}" }) content.append({ "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{img_data}", "detail": "low" # Tokens sparen } }) content.append({ "type": "text", "text": "\n\nRéponds à la question en te basant uniquement sur ces images. Si les images ne permettent pas de répondre, dis-le clairement." }) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": content}], max_tokens=1000 ) return response.choices[0].message.content
Erweiterte Optimierungen
Chunking hochauflösender Bilder
Für sehr große Bilder (Pläne, Schaubilder) in Kacheln schneiden:
DEVELOPERpythonfrom PIL import Image def tile_large_image(image_path: str, tile_size: int = 512, overlap: int = 64): """Zerteilt ein großes Bild in überlappende Kacheln.""" img = Image.open(image_path) width, height = img.size tiles = [] for y in range(0, height - overlap, tile_size - overlap): for x in range(0, width - overlap, tile_size - overlap): box = (x, y, min(x + tile_size, width), min(y + tile_size, height)) tile = img.crop(box) tiles.append({ "tile": tile, "position": (x, y), "original_size": (width, height) }) return tiles
Hybride Suche Bild + Text
DEVELOPERpythondef hybrid_image_search( query: str, text_embedding: list[float], index: ImageRAGIndex, alpha: float = 0.7 # Gewicht visuell vs. textuell ) -> list[dict]: """Kombiniert visuelle und textuelle Suche.""" # Visuelle Suche (CLIP) visual_results = index.search_by_text(query, limit=20) # Textuelle Suche (auf den Beschreibungen) text_results = index.client.search( collection_name=index.collection_name, query_vector=("textual", text_embedding), limit=20 ) # Scores mit RRF zusammenführen combined_scores = {} for rank, r in enumerate(visual_results): img_id = r["image_path"] combined_scores[img_id] = combined_scores.get(img_id, 0) + alpha / (rank + 60) for rank, r in enumerate(text_results): img_id = r.payload["image_path"] combined_scores[img_id] = combined_scores.get(img_id, 0) + (1 - alpha) / (rank + 60) # Nach kombiniertem Score sortieren sorted_results = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True) return [{"image_path": path, "score": score} for path, score in sorted_results[:5]]
Benchmarks und Kosten
Retrieval-Performance
| Méthode | Precision@5 | Recall@10 | Latence |
|---|---|---|---|
| Description seule | 0.72 | 0.81 | 50ms |
| CLIP seul | 0.78 | 0.85 | 30ms |
| Hybride | 0.84 | 0.91 | 80ms |
Kosten pro indexiertem Bild
| Étape | Coût estimé | Notes |
|---|---|---|
| Description GPT-4V | $0.01-0.03 | Selon taille et détail |
| Embedding CLIP | $0 (local) | GPU recommandé |
| Stockage Qdrant | ~$0.0001 | Par vecteur/mois |
Vergleich der Embedding-Modelle
| Modèle | Zero-shot accuracy | Multilingue | Vitesse |
|---|---|---|---|
| CLIP ViT-L/14 | 75.5% | Non | Rapide |
| SigLIP So400m | 83.1% | Oui | Moyen |
| Jina CLIP v2 | 81.2% | Oui | Rapide |
Fallstricke und Lösungen
Problème 1 : Images avec peu de contenu visuel
Symptôme : Screenshots mit viel Text werden von CLIP schlecht indexiert.
Solution : Explizites OCR + textuelle Indexierung.
DEVELOPERpythonimport pytesseract def extract_text_from_image(image_path: str) -> str: """Extrahiert Text aus einem Bild mittels OCR.""" img = Image.open(image_path) text = pytesseract.image_to_string(img, lang='fra+eng') return text.strip()
Problème 2 : Doublons visuels
Symptôme : Mehrere nahezu identische Bilder verschmutzen die Ergebnisse.
Solution : Deduplizierung per Ähnlichkeit.
DEVELOPERpythondef deduplicate_images(embeddings: list, threshold: float = 0.95): """Entfernt zu ähnliche Bilder.""" keep = [] for i, emb in enumerate(embeddings): is_duplicate = False for j in keep: similarity = cosine_similarity(emb, embeddings[j]) if similarity > threshold: is_duplicate = True break if not is_duplicate: keep.append(i) return keep
Problème 3 : Contexte visuel vs textuel contradictoire
Symptôme : Die generierte Beschreibung widerspricht dem Bild.
Solution : Kreuzvalidierung und Vertrauensscore.
Integration mit Ailog
Ailog unterstützt nativ die Indexierung von Bildern in euren Knowledge Bases:
- Upload : Bilder per Drag & Drop in die Oberfläche ziehen
- Analyse automatique : Vision-Model zur Inhaltsextraktion
- Indexation hybride : Visuelle + textuelle Embeddings
- Recherche unifiée : Eine einzige Anfrage für Text und Bilder
Essayez l'Image RAG sur Ailog - Keine Konfiguration erforderlich.
FAQ
Verwandte Guides
Tags
Verwandte Artikel
RAG Multimodal: Bilder, PDFs und über den Text hinaus
Erweitern Sie Ihr RAG über den Text hinaus: Indexierung von Bildern, Extraktion von PDFs, Tabellen und Grafiken für einen wirklich umfassenden Assistenten.
Grundlagen des Retrievals: Wie die RAG-Suche funktioniert
Beherrschen Sie die Grundlagen des Retrievals in RAG-Systemen: Embeddings, vector search, chunking und indexing für relevante Ergebnisse.
Magento: Intelligenter Katalog-Assistent
Einen AI-Assistenten auf Magento bereitstellen, um in komplexen Katalogen zu navigieren, Produkte zu empfehlen und das B2B- und B2C-Erlebnis zu verbessern.