Как вы делаете RAG для изображений (image retrieval without text)?
Краткий тезис
RAG для изображений без текста строится на мультимодальных эмбеддингах, чаще всего с помощью модели CLIP, которая переводит и изображения, и текст в единое векторное пространство. Все изображения индексируются в векторной БД (FAISS, Pinecone). Запрос может быть как текстовым описанием, так и другим изображением — его эмбеддинг вычисляется той же моделью, после чего выполняется поиск по косинусной близости. Для мультимодального RAG (текст + изображения) применяется reranking|late fusion — объединение результатов двух независимых retrieval-каналов.
1. Термин: Image Retrieval без текста
Image retrieval — задача поиска изображений, релевантных запросу. Классический подход требует текстовых меток или аннотаций. «Без текста» означает, что у самих изображений в базе нет текстовых описаний — только визуальное содержание. Решение — использовать мультимодальные эмбеддинги, которые кодируют и изображения, и текст в общее пространство, позволяя сравнивать их напрямую.
Ключевая идея: если модель обучена совмещать модальности (например, CLIP), то эмбеддинг изображения и эмбеддинг текстового запроса будут близки, если изображение соответствует запросу. Таким образом, retrieval возможен без единого слова в базе изображений.
2. Модель CLIP: основа современного image retrieval
CLIP (Contrastive Language-Image Pre-training) — модель от OpenAI, обученная на 400 млн пар (изображение, текст) с использованием контрастивной потери (contrastive loss). Она состоит из двух энкодеров: Image Encoder (обычно ViT или ResNet) и Text Encoder (Transformer). На выходе — нормализованные векторы фиксированной размерности (например, 512 или 768).
Принцип работы
- Для каждой пары (изображение, текст) в батче считается косинусная близость между эмбеддингами.
- Модель учится увеличивать близость для правильных пар и уменьшать для неправильных (InfoNCE loss).
- После обучения эмбеддинги разных модальностей выровнены: изображение кота и текст «кот» имеют похожие векторы.
Почему CLIP подходит для image retrieval без текста:
- Эмбеддинги изображений можно вычислить один раз и сохранить.
- Запрос может быть текстом (например, «красная машина») — его эмбеддинг сравнивается с эмбеддингами изображений.
- Запрос может быть другим изображением — его эмбеддинг вычисляется Image Encoder'ом, и поиск идёт среди эмбеддингов изображений (image-to-image retrieval).
3. Индексация изображений в векторной БД
Процесс подготовки базы изображений:
- Извлечение эмбеддингов каждое изображение прогоняется через Image Encoder CLIP (или другую мультимодальную модель). Получаем вектор фиксированной размерности.
- Нормализация эмбеддинги нормализуются (L2-норма), чтобы косинусная близость равнялась скалярному произведению.
- Загрузка в векторную БД используется FAISS (Facebook AI Similarity Search), Pinecone, Weaviate или Milvus. Для больших коллекций применяется ANN (Approximate Nearest Neighbors) — индексы типа IVF, HNSW, PQ.
- Метаданные: вместе с эмбеддингом хранятся ссылка на файл, возможно, теги или дата создания — для пост-фильтрации.
Пример кода (FAISS):
import torch
import clip
import faiss
import numpy as np
model, preprocess = clip.load("ViT-B/32")
device = "cuda"
# Загружаем изображения, получаем эмбеддинги
image_embeddings = []
for img_path in image_paths:
image = preprocess(Image.open(img_path)).unsqueeze(0).to(device)
with torch.no_grad():
emb = model.encode_image(image).cpu().numpy()
image_embeddings.append(emb)
image_embeddings = np.vstack(image_embeddings).astype('float32')
faiss.normalize_L2(image_embeddings)
# Строим индекс
index = faiss.IndexFlatIP(image_embeddings.shape[1]) # Inner Product = cosine similarity
index.add(image_embeddings)
# Сохраняем
faiss.write_index(index, "image_index.faiss")
4. Retrieval: текстовый запрос
Если пользователь вводит текст (например, «закат на море»):
- Текст кодируется Text Encoder'ом CLIP → получаем вектор запроса.
- Вектор нормализуется.
- Выполняется поиск в FAISS: index.search(query_vector, k=10).
- Возвращаются top-k изображений с наибольшей косинусной близостью.
Важно модель CLIP обучена на естественном языке, поэтому запросы лучше формулировать как описания, а не отдельные слова. Например, вместо «кошка» лучше «фотография кошки, сидящей на подоконнике».
5. Retrieval: запрос-изображение (image-to-image)
Когда пользователь загружает изображение и хочет найти похожие:
- Изображение-запрос кодируется Image Encoder'ом CLIP.
- Полученный эмбеддинг сравнивается со всеми эмбеддингами в базе (косинусная близость).
- Возвращаются наиболее похожие изображения.
Этот сценарий часто используется в системах поиска по визуальному сходству (например, поиск товаров по фото). CLIP здесь работает как visual backbone, но можно использовать и специализированные модели (например, DINOv2), которые дают более чистые визуальные признаки без текстового выравнивания. Однако CLIP удобен тем, что единая модель работает и для текстовых, и для визуальных запросов.
6. Ранжирование и пост-обработка
После получения top-k кандидатов можно применить дополнительные этапы:
- Re-ranking использовать более тяжёлую модель (например, ViT-L/14 CLIP или даже мультимодальный LLM) для переоценки близости.
- Фильтрация по метаданным если известны дата, автор, категория — отсеять неподходящие.
- Deduplication удалить дубликаты (почти одинаковые изображения).
- Query expansion: для текстового запроса можно сгенерировать несколько синонимов, усреднить эмбеддинги (или объединить результаты).
Пример re-ranking с помощью косинусной близости на более точной модели:
# После первого прохода (быстрый индекс) берём top-100
# Пересчитываем эмбеддинги через более качественный энкодер
fine_model, _ = clip.load("ViT-L/14")
# ... пересчёт и сортировка
7. Мультимодальный RAG: объединение текста и изображений
Часто требуется RAG-система, которая работает и с текстовыми документами, и с изображениями. Возможны две стратегии:
| Стратегия | Описание | Плюсы | Минусы |
|---|---|---|---|
| Early fusion | Эмбеддинги текста и изображений объединяются в одном векторном пространстве (например, через CLIP) и хранятся в одной БД. | Простота, единый поиск | Потеря специфики модальностей, сложность с разными размерностями |
| Late fusion | Два независимых retrieval: текстовый (через text embeddings) и визуальный (через image embeddings). Результаты объединяются (ранжирование, конкатенация). | Каждый канал оптимизирован под свою модальность, гибкость | Два индекса, больше latency |
Late fusion — более распространённый подход в production. Этапы:
- Текстовый запрос идёт в текстовый индекс (например, на основе Sentence-BERT).
- Тот же запрос (или его часть) идёт в визуальный индекс (CLIP image embeddings).
- Результаты объединяются: можно взять top-k из каждого канала и перемешать, или использовать weighted sum scores.
Пример кода (псевдо):
text_results = text_index.search(text_emb, k=5)
image_results = image_index.search(text_emb, k=5) # text_emb от CLIP
combined = merge(text_results, image_results, weights=[0.7, 0.3])
8. Проблемы и ограничения
- Domain shift CLIP обучен на интернет-данных, может плохо работать на специфических доменах (медицина, спутниковые снимки). Решение — fine-tune CLIP на своих данных.
- Размер эмбеддинга для миллионов изображений хранение векторов (512 float32 = 2KB на изображение) может быть затратным. Используют Product Quantization (PQ) для сжатия.
- Скорость ANN-индексы (HNSW) дают субсекундный поиск для 10M+ векторов, но требуют настройки параметров.
- Качество эмбеддингов CLIP не идеален для тонких визуальных различий (например, разные породы собак). Альтернативы: SigLIP, OpenCLIP, BLIP-2.
- Отсутствие текстовых меток если нужно искать по конкретным атрибутам (например, «фото с красным автомобилем 2020 года»), CLIP может не справиться — требуется fine-tuning или добавление метаданных.
9. Альтернативы CLIP
| Модель | Особенность | Когда использовать |
|---|---|---|
| DINOv2 | Self-supervised, только визуальные признаки | Чистый image-to-image поиск, без текстовых запросов |
| BLIP-2 | Мультимодальный encoder-decoder, лучше понимает сложные запросы | Когда нужен высококачественный retrieval + генерация |
| SigLIP | Аналогичен CLIP, но с сигмоидной loss, часто лучше | Если CLIP даёт плохие результаты |
| ImageBind | Шестимодальная модель (изображение, текст, аудио, глубина и др.) | Мультимодальные запросы (например, «найди изображение, похожее на этот звук») |
Пет-проект для закрепления
Задача Создать сервис поиска изображений по текстовому описанию (image retrieval without text) на основе CLIP и FAISS.
Инструменты
- Python, PyTorch, CLIP (openai/clip-vit-base-patch32)
- FAISS (faiss-cpu или faiss-gpu)
- Streamlit (интерфейс)
- Датасет: Unsplash Lite (25k изображений) или собственные фото
Шаги:
- Скачать датасет изображений (например, Unsplash Lite).
- Написать скрипт для извлечения эмбеддингов через CLIP (использовать батчи, GPU).
- Нормализовать эмбеддинги и построить FAISS индекс (IndexFlatIP для точности или HNSW для скорости).
- Создать Streamlit-приложение: поле ввода текста, кнопка поиска, вывод top-5 изображений с косинусной близостью.
- Добавить возможность загрузить своё изображение для image-to-image поиска.
- (Опционально) Реализовать re-ranking через ViT-L/14.
- Оценить качество: вручную проверить 20 запросов, посчитать HR@5.
Ожидаемый результат Работающий веб-интерфейс, который по тексту «закат в горах» показывает релевантные фото, а по загруженному изображению кота — похожих котов.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 1 | Проектирование RAG-системы |
| 5 | Оценка качества retrieval |
| 7 | Уменьшение latency |
| 10 | Self-RAG |
| 540 | Мультимодальные эмбеддинги |
| 542 | Agentic RAG с изображениями |
Навигация
- Предыдущий: 540
- Следующий: 542
- Индекс: 00. Индекс разборов