Как вы делаете image retrieval по тексту с высокой точностью?
Краткий тезис
Высокоточный image retrieval по тексту]] строится на CLIP (ViT-L/14) с fine-tuning на целевой домен через contrastive loss и hard negative mining. Для финального ранжирования топ-100 используется reranking с cross-encoder, а query expansion через LLM повышает recall. Комбинация этих методов даёт точность, близкую к человеческой, в задачах поиска изображений.
1. Термин: Image retrieval по тексту (text-to-image retrieval)
Image retrieval по тексту — это задача поиска изображений, наиболее релевантных текстовому запросу пользователя. В контексте RAG (Retrieval-Augmented Generation) такая возможность позволяет системе отвечать на вопросы, требующие визуальной информации: «Покажи примеры барочной архитектуры» или «Найди изображения дефектов на производственной линии».
Зачем нужна высокая точность
- В RAG-системе ошибка retrieval приводит к неверному ответу LLM.
- Пользователь ожидает, что первое же изображение будет релевантным.
- В промышленных сценариях (медицина, безопасность) ложные срабатывания недопустимы.
2. Основной подход: CLIP (Contrastive Language–Image Pre-training)
CLIP — модель от OpenAI, обученная на 400 млн пар (текст, изображение) с contrastive loss. Она состоит из двух encoder'ов:
- Text encoder (обычно Transformer) — преобразует текст в вектор.
- Image encoder (обычно ViT — Vision Transformer) — преобразует изображение в вектор той же размерности.
Принцип работы
- Все тексты и изображения проецируются в общее embedding space.
- Для пары (текст, изображение) loss максимизирует косинусную близость между их эмбеддингами и минимизирует близость с другими парами в батче.
- В inference текст запроса эмбеддится, ищутся ближайшие соседи среди эмбеддингов изображений (через FAISS или Milvus).
Почему CLIP — стандарт
- Zero-shot способность: работает на новых категориях без дообучения.
- Масштабируемость: ViT-L/14 даёт 768-мерные эмбеддинги, достаточные для точного поиска.
- Открытые реализации: OpenCLIP, Hugging Face transformers.
3. Fine-tuning на домене
Для высокой точности на специфическом домене (медицина, промышленность, мода) необходима адаптация. Fine-tuning проводится на датасете из троек:
- Query (текстовый запрос)
- Positive image (релевантное изображение)
- Negative image (нерелевантное изображение, желательно сложное)
Loss: Contrastive loss (InfoNCE) — для каждой пары (query, positive) увеличиваем сходство, для (query, negative) уменьшаем.
import torch
import torch.nn.functional as F
def contrastive_loss(query_emb, pos_emb, neg_emb, temperature=0.07):
# query_emb, pos_emb, neg_emb: [batch_size, dim]
pos_sim = F.cosine_similarity(query_emb, pos_emb) / temperature
neg_sim = F.cosine_similarity(query_emb, neg_emb) / temperature
# softmax: хотим, чтобы pos_sim был больше neg_sim
loss = -torch.log(torch.exp(pos_sim) / (torch.exp(pos_sim) + torch.exp(neg_sim)))
return loss.mean()
Практические советы
- Использовать pretrained CLIP как инициализацию, fine-tune только последние слои или LoRA.
- Размер батча 256–1024 для стабильного контрастивного обучения.
- Аугментации изображений (повороты, обрезка) для устойчивости.
4. Hard negative mining
Hard negative — это изображение, которое похоже на запрос по эмбеддингу, но нерелевантно. Без них модель может путать близкие категории (например, «красное платье» vs «красная скатерть»).
Методы получения hard negatives
- Offline mining после каждой эпохи пересчитываем эмбеддинги всех изображений, для каждого запроса выбираем top-k ближайших нерелевантных.
- Online mining внутри батча используем hardest negative (максимальное сходство среди негативов).
- Cross-batch mining храним очередь эмбеддингов из предыдущих батчей.
Реализация online mining
# внутри батча: query_emb, image_emb — все эмбеддинги
sim_matrix = torch.matmul(query_emb, image_emb.T) # [batch, batch]
# диагональ — positive пары
pos_sim = sim_matrix.diag()
# для каждого запроса hardest negative — максимальное сходство среди остальных
neg_sim = sim_matrix.clone()
neg_sim.fill_diagonal_(-float('inf'))
hard_neg_sim = neg_sim.max(dim=1).values
loss = contrastive_loss(pos_sim, hard_neg_sim, temperature=0.07)
Влияние на точность hard negative mining может поднять recall@10 на 5–15% на сложных доменах.
5. Reranking с cross-encoder
Bi-encoder (CLIP) эффективен для первого этапа поиска (top-100), но теряет тонкие нюансы. Cross-encoder обрабатывает пару (текст, изображение) совместно, давая более точную оценку релевантности.
Архитектура reranker
- Берём CLIP ViT-L как image encoder и CLIP text encoder.
- Конкатенируем их выходы (или используем cross-attention) и добавляем линейный слой для бинарной классификации (релевантно/нерелевантно).
- Обучаем на тех же данных, что и bi-encoder, но с парными входами.
Процесс
- Bi-encoder (CLIP) выдаёт top-100 кандидатов.
- Cross-encoder ранжирует эти 100 пар заново.
- Выдаём top-10 после reranking.
Сравнение bi-encoder vs cross-encoder
| Характеристика | Bi-encoder (CLIP) | Cross-encoder |
|---|---|---|
| Скорость | Быстро (O(N) для индекса) | Медленно (O(N) на каждую пару) |
| Точность | Средняя | Высокая |
| Использование | Первичный поиск | Ранжирование малого набора |
| Масштабирование | FAISS, Milvus | Только для top-k |
Практика cross-encoder может быть отдельной моделью (например, BLIP-2 или ALBEF), но часто используют дообученный CLIP с головой.
6. Query expansion через LLM
Query expansion — генерация нескольких вариантов запроса для повышения recall. LLM (например, GPT-4) перефразирует исходный запрос, добавляет синонимы, уточняет контекст.
Пример:
- Исходный запрос: «красное платье с цветочным принтом»
- Варианты:
- «платье красного цвета с узором из цветов»
- «красное платье floral print»
- «вечернее платье красное с цветами»
Реализация
def expand_query(query, llm):
prompt = f"Generate 3 alternative search queries for: '{query}'. Return as a list."
response = llm.generate(prompt)
variants = parse_list(response)
return [query] + variants
Поиск все варианты эмбеддятся, результаты объединяются (ранговая сумма или union). Это увеличивает recall@k на 10–20%.
7. Дополнительные техники повышения точности
- Hybrid search: комбинировать эмбеддинги CLIP с BM25 по текстовым метаданным (теги, описания). Взвешенная сумма скоринга.
- Post-processing удаление дубликатов, фильтрация по confidence threshold.
- Multi-modal embeddings использовать модели вроде ImageBind (Meta) или GIT (Microsoft), которые объединяют текст, изображение, аудио.
- Ensemble несколько моделей CLIP (разные ViT размеры) усредняют эмбеддинги.
8. Метрики оценки
Для image retrieval по тексту используются те же метрики, что и для текстового retrieval:
| Метрика | Описание | Целевое значение |
|---|---|---|
| Recall@k | Доля релевантных изображений в top-k | >0.9 для k=10 |
| Precision@k | Доля релевантных среди top-k | >0.8 для k=10 |
| MRR | Средний обратный ранг первого релевантного | >0.95 |
| NDCG@k | Учитывает порядок релевантности | >0.9 |
Оффлайн-метрики считаются на размеченном датасете (gold standard). Для онлайн-оценки используют user feedback (клики, время просмотра).
9. Инструменты и библиотеки
- OpenCLIP — открытая реализация CLIP с поддержкой ViT-L/14, ViT-H/14.
- FAISS — библиотека для быстрого поиска ближайших соседей (IVF, HNSW).
- Milvus — векторная БД для production.
- Hugging Face Transformers — загрузка предобученных CLIP.
- PyTorch Lightning — для fine-tuning.
- Reranker — можно использовать CrossEncoder из библиотеки sentence-transformers.
10. Проблемы и ограничения
- Domain shift CLIP, обученный на интернет-данных, плохо работает на медицинских или спутниковых снимках. Требуется fine-tuning.
- Scalability для миллионов изображений нужен эффективный индекс (FAISS IVF, HNSW).
- Latency cross-encoder замедляет ответ. Оптимизация: использовать только для top-50, кэшировать результаты.
- Hard negative mining требует вычислительных ресурсов (пересчёт эмбеддингов).
- Query expansion может внести шум, если LLM генерирует нерелевантные варианты.
Пет-проект для закрепления
Задача Разработать систему поиска изображений товаров по текстовому описанию для интернет-магазина одежды.
Инструменты
- Python, PyTorch, OpenCLIP, FAISS, Hugging Face Transformers.
- Датасет: Fashion200k или собрать 10k пар (описание, изображение) с маркетплейса.
- LLM для query expansion: GPT-4 (API) или локальная модель (Mistral 7B).
Шаги:
- Загрузить предобученный CLIP ViT-L/14.
- Подготовить датасет: для каждого описания выбрать 1 positive (соответствующее изображение) и 3 hard negatives (похожие, но другие товары).
- Fine-tune CLIP с contrastive loss и online hard negative mining.
- Построить FAISS индекс для всех изображений.
- Реализовать pipeline:
- Query expansion (3 варианта).
- Bi-encoder поиск (top-100).
- Cross-encoder reranking (top-10).
- Оценить recall@10, MRR на тестовом наборе.
Ожидаемый результат
- Recall@10 > 0.85, MRR > 0.9.
- Система отвечает на запросы вроде «синее платье с длинным рукавом» за <200 мс.
- Сравнение с baseline (без fine-tuning) показывает прирост точности на 20%.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 5 | Оценка качества retrieval в RAG |
| 10 | Self-RAG и когда его использовать |
| 15 | Fine-tuning эмбеддингов для RAG |
| 20 | Выбор модели эмбеддингов |
| 25 | Multi-modal RAG (текст + изображения) |
| 30 | Query expansion в RAG |
Навигация
- Предыдущий: 553
- Следующий: 555
- Индекс: 00. Индекс разборов