English translation is not available yet. Showing Russian content.

Как работает мультимодальный RAG с unified retrieval (один индекс для текста и изображений)?

Краткий тезис

Unified retrieval в мультимодальном RAG — это подход, при котором тексты и изображения кодируются в единое векторное пространство с помощью мультимодальной модели (например, CLIP), и все векторы хранятся в одном индексе. Запрос (текст) преобразуется в тот же эмбеддинг, и поиск возвращает как текстовые, так и визуальные результаты. При генерации ответа изображения могут быть переданы LLM через caption или напрямую, если модель поддерживает vision. Такой подход упрощает архитектуру, но часто уступает по качеству раздельным индексам с последующим fusion reranking.


1. Термины и основы

Мультимодальный RAG — система, которая умеет отвечать на вопросы, используя не только текст, но и изображения, таблицы, диаграммы. Unified retrieval (retrieval|единый поиск) означает, что все модальности индексируются в одной векторной базе данных, а не в отдельных.

CLIP (Contrastive Language–Image Pre-training) — модель OpenAI, обученная на парах «текст-изображение» с контрастивной потерей. Она создаёт общее эмбеддинг-пространство, где близкие по смыслу текст и картинка имеют похожие векторы. Это ключевой компонент retrieval|unified retrieval.

Unified embedding space — пространство, в котором и текст, и изображения представлены векторами одинаковой размерности (например, 512 или 768). Косинусное расстояние между векторами текста и изображения отражает их семантическую близость.

Векторный индекс — структура данных (например, HNSW, IVF) для быстрого поиска ближайших соседей. В retrieval|unified retrieval один индекс содержит векторы всех модальностей.


2. Архитектура unified retrieval

Система состоит из трёх этапов: индексация, поиск, генерация.

[Текстовые документы] ──┐
                        ├──> CLIP encoder ──> [Векторы] ──> Единый индекс (Qdrant/Weaviate)
[Изображения] ──────────┘

[Запрос пользователя] ──> CLIP text encoder ──> [Вектор запроса] ──> Поиск по индексу ──> [Результаты: текст + изображения]

[Результаты] ──> LLM (с поддержкой vision или через caption) ──> Ответ

2.1 Индексация

  1. Все текстовые чанки пропускаются через encoder|текстовый энкодер CLIP (например, ViT-B/32 для текста).
  2. Все изображения пропускаются через encoder|визуальный энкодер CLIP (та же модель, но другая ветвь).
  3. Полученные векторы (обычно нормализованные) сохраняются в одну коллекцию векторной БД. Каждый вектор помечается метаданными: тип модальности (text/image), исходный текст или путь к изображению, caption (если есть).

Пример кода (индексация с CLIP и Qdrant):

from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance, PointStruct

# Загружаем CLIP-модель (текстовый энкодер)
model = SentenceTransformer('clip-ViT-B-32')

client = QdrantClient("localhost", port=6333)
client.recreate_collection(
    collection_name="unified",
    vectors_config=VectorParams(size=512, distance=Distance.COSINE)
)

# Текстовые чанки
texts = ["Кошка сидит на стуле", "Собака бежит по парку"]
text_vectors = model.encode(texts)

# Изображения (загружаем и кодируем через CLIP image encoder)
from PIL import Image
image_paths = ["cat.jpg", "dog.jpg"]
image_vectors = []
for path in image_paths:
    img = Image.open(path)
    vec = model.encode(img)  # clip-ViT-B-32 поддерживает и изображения
    image_vectors.append(vec)

# Создаём точки с метаданными
points = []
for i, vec in enumerate(text_vectors):
    points.append(PointStruct(id=i, vector=vec.tolist(), payload={"type": "text", "content": texts[i]}))
offset = len(texts)
for i, vec in enumerate(image_vectors):
    points.append(PointStruct(id=offset+i, vector=vec.tolist(), payload={"type": "image", "path": image_paths[i]}))

client.upsert(collection_name="unified", points=points)

2.2 Поиск

  1. Запрос пользователя (текст) кодируется тем же текстовым энкодером CLIP.
  2. Выполняется поиск ближайших соседей в едином индексе.
  3. Возвращаются top-k результатов — среди них могут быть как текстовые чанки, так и изображения.

Пример поиска

query = "Животные на природе"
query_vec = model.encode(query).tolist()
results = client.search(
    collection_name="unified",
    query_vector=query_vec,
    limit=5
)
for res in results:
    print(res.payload["type"], res.score, res.payload.get("content", res.payload.get("path")))

2.3 Генерация ответа

После получения результатов нужно передать их LLM. Есть два подхода:

  • Caption-based: для каждого изображения используется заранее сохранённый caption (или генерируется на лету моделью типа BLIP). В контекст LLM подаётся текст caption вместо самого изображения.
  • Vision-language model: если LLM поддерживает изображения (например, GPT-4V, LLaVA), можно передать само изображение (как base64 или URL) вместе с текстовыми чанками.

Выбор зависит от возможностей LLM и требований к latency. Caption-based проще и быстрее, но теряет визуальные детали.


3. Проблемы и ограничения unified retrieval

  • Разная природа модальностей: текст и изображения имеют разную статистику. CLIP обучен на парах, но не идеально выравнивает все возможные запросы. Например, запрос «красный автомобиль» может найти изображение красной машины, но пропустить текстовое описание, где слово «красный» не упоминается.
  • Дисбаланс модальностей: если в индексе много текста и мало изображений, поиск может быть смещён в сторону текста (или наоборот).
  • Качество unified retrieval часто ниже, чем раздельных индексов с fusion reranking. Исследования (например, работы по мультимодальному RAG) показывают, что separate retrieval + позднее слияние даёт более высокие метрики.
  • Сложность настройки: выбор модели эмбеддинга (CLIP, ALBEF, BridgeTower) влияет на качество. Для специфических доменов может потребоваться fine-tuning.

4. Альтернатива: separate indices + fusion reranking

Вместо одного индекса можно построить два отдельных: для текста (с текстовым эмбеддером, например, all-MiniLM-L6-v2) и для изображений (с визуальным эмбеддером, например, ResNet или ViT). При поиске:

  1. Запрос кодируется отдельно для каждой модальности (или используется один эмбеддер, но разные индексы).
  2. Выполняется поиск в каждом индексе.
  3. Результаты объединяются и переранжируются с помощью fusion reranking (например, комбинация косинусных расстояний, или использование кросс-энкодера).

Сравнение unified vs separate

КритерийUnified retrievalSeparate indices + fusion
Сложность индексацииОдин индекс, прощеДва индекса, больше движущихся частей
Качество поискаНиже (из-за разной природы)Выше (можно оптимизировать каждую модальность)
ГибкостьОграничена одной моделью эмбеддингаМожно использовать разные эмбеддеры (текстовый + визуальный)
RerankingНе требуется (или простой)Требуется fusion/reranking
СкоростьОдин запрос к БДДва запроса + reranking (медленнее)

5. Инструменты и модели

  • CLIP (OpenAI) — де-факто стандарт для unified embedding. Доступен через sentence-transformers или open_clip.
  • BridgeTower, ALBEF, VLMO — более новые мультимодальные эмбеддеры, часто дают лучшее выравнивание.
  • Qdrant, Weaviate, Pinecone — векторные БД, поддерживающие единую коллекцию для разных типов данных.
  • LLaVA, GPT-4V, Gemini — vision-language модели для генерации ответа с изображениями.

6. Когда выбирать unified retrieval?

Unified retrieval оправдан, если:

  • Требуется минимальная сложность системы (один индекс, один эмбеддер).
  • Модальности хорошо выравнены (например, в датасете изображения всегда имеют качественные caption).
  • Допустимо небольшое снижение качества ради скорости и простоты.

В production-системах чаще используют separate indices с fusion, так как качество важнее.


Пет-проект для закрепления

Задача: Создать мультимодального RAG-бота, который отвечает на вопросы по каталогу товаров (текстовые описания + изображения). Использовать unified retrieval с CLIP и Qdrant.

Инструменты: Python, sentence-transformers, qdrant-client, Pillow, transformers (для captioning, опционально).

Шаги:

  1. Собрать датасет: 50-100 товаров с текстовым описанием и изображением.
  2. Установить Qdrant (локально через Docker).
  3. Написать скрипт индексации: для каждого товара закодировать текст и изображение через CLIP, сохранить в одну коллекцию с метаданными (type, content, path).
  4. Реализовать поиск: по текстовому запросу искать top-5 результатов.
  5. Генерация: для найденных изображений использовать предварительно сохранённый caption (или генерировать через BLIP), передать в LLM (например, через OpenAI API) вместе с текстовыми чанками.
  6. Протестировать на нескольких запросах, сравнить с вариантом separate indices.

Ожидаемый результат: Работающий прототип, который на запрос «красное платье» возвращает ответ, основанный как на текстовых описаниях, так и на изображениях. Можно оценить hit rate и субъективное качество.


Связь с другими вопросами

ВопросТема
554Мультимодальный RAG с separate retrieval
556Fusion reranking в мультимодальном RAG
557Архитектура Agentic RAG
558Инструменты для мультимодального RAG
559Оценка мультимодального RAG
560Безопасность мультимодального RAG

Навигация