От ViT и CLIP до GPT-4o и Gemini 2.0 — полный разбор: как модели видят, слышат и понимают мир сразу из нескольких источников, как работает fusion, какие задачи решаются и как их оценивать.
// фундаментальные концепции
Мультимодальность — способность модели принимать на вход и/или генерировать данные разных типов: текст, изображения, аудио, видео. Человек воспринимает мир именно так — через несколько сенсорных каналов одновременно. Мультимодальные LLM пытаются воспроизвести это свойство.
// ЗАДАЧИ МУЛЬТИМОДАЛЬНЫХ МОДЕЛЕЙ · пунктиром = генеративные задачи
// vision encoder · история
ViT (Dosovitskiy et al., Google, 2020) — первая успешная прямая адаптация трансформера к изображениям. Идея проста: разбить изображение на патчи фиксированного размера (16×16 пикселей), развернуть каждый в вектор, и подать как «токены» в стандартный трансформер-энкодер.
// VIT PIPELINE · изображение → патчи → linear projection → трансформер-энкодер → patch embeddings
CLIP обучается одновременно на изображениях и текстовых описаниях через contrastive learning. Два энкодера (Image Encoder и Text Encoder) учатся проецировать соответствующие пары в одну точку пространства эмбеддингов. Это даёт нулевую инициализацию для любых задач классификации без дообучения.
// CLIP TRAINING · контрастивное обучение на 400M пар — совместить изображения с описаниями
SigLIP (Zhai et al., Google, 2023) заменяет softmax-based contrastive loss на sigmoid loss, убирая зависимость от размера батча. Каждая пара (image, text) независимо классифицируется как «matching/not matching». Это ускоряет обучение и улучшает качество, особенно при малых батчах. SigLIP-SO400M используется в LLaVA-Next, PaliGemma.
// vision-language models
VLM (Vision-Language Model) — архитектура, объединяющая визуальный энкодер с языковой моделью. Ключевой вопрос: как именно соединить два пространства. Каждая из ключевых моделей предложила свой ответ.
// VLM TRAINING STAGES · поэтапное обучение от изолированных компонентов до end-to-end
// fusion architecture
Визуальные патч-токены проецируются в пространство LLM и конкатенируются с текстовыми. LLM видит image+text как единую последовательность. Примеры: LLaVA, InternVL.
Q-Former (BLIP-2) или Perceiver Resampler (Flamingo) — промежуточный модуль, извлекающий фиксированное число (32–64) ключевых токенов из тысяч патчей. Эффективнее по памяти.
Каждая модальность обрабатывается своей башней независимо, результаты сливаются перед итоговым классификатором. Хорошо для задач без генерации (classification, retrieval).
| Connector | Визуальных токенов | Модель | Плюс | Минус |
|---|---|---|---|---|
| Linear Projection | N_patches (~256–1024) | LLaVA-1.5 | Простота, полный визуальный контекст | Длинный контекст → дорого |
| MLP 2-layer | N_patches (~256) | LLaVA-1.5 | Чуть лучше alignment | Всё ещё много токенов |
| Q-Former | 32 фиксированных | BLIP-2, InstructBLIP | Фиксированный контекст, эффективно | Информация может теряться |
| Perceiver Resampler | 64–256 | Flamingo, Otter | Гибкое число запросов | Сложнее обучать |
| Dynamic Tiling | N × M_tiles × patches | LLaVA-Next, Qwen2.5-VL | Высокое разрешение! | Контекст растёт с разрешением |
Стандартный ViT обучен на 224×224 — это 14×14 = 196 патчей. Для документов и высококачественных изображений этого катастрофически мало. Dynamic Tiling (LLaVA-Next, Qwen2.5-VL) делит изображение на тайлы, каждый тайл кодируется отдельно:
import torch from PIL import Image import math def dynamic_tile_encode( image: Image.Image, vision_encoder, # ViT принимает 224×224 max_tiles: int = 12, # максимум тайлов tile_size: int = 224, ) -> torch.Tensor: """ Делит изображение на тайлы + добавляет уменьшенный thumbnail. Результат: (1 + num_tiles) × num_patches × D """ w, h = image.size # Определяем оптимальную сетку тайлов aspect = w / h num_tiles_w = min(int(math.ceil(w / tile_size)), max_tiles) num_tiles_h = min(int(math.ceil(h / tile_size)), max_tiles) # Ограничиваем общее число тайлов while num_tiles_w * num_tiles_h > max_tiles: if num_tiles_w > num_tiles_h: num_tiles_w -= 1 else: num_tiles_h -= 1 tiles = [] # 1. Thumbnail: всё изображение сжатое до 224×224 thumbnail = image.resize((tile_size, tile_size)) tiles.append(vision_encoder(thumbnail)) # глобальный контекст # 2. Локальные тайлы (перекрытие 10% для continuity) stride_w = w // num_tiles_w stride_h = h // num_tiles_h for row in range(num_tiles_h): for col in range(num_tiles_w): x0 = col * stride_w y0 = row * stride_h tile = image.crop((x0, y0, x0 + tile_size, y0 + tile_size)) tile_emb = vision_encoder(tile) tiles.append(tile_emb) # (1 + num_tiles) × 196 × D return torch.stack(tiles, dim=0) # Для 1080p (1920×1080) с tile_size=224: # num_tiles_w = 8, num_tiles_h = 4 → 32+1=33 тайла # 33 × 196 = 6468 visual tokens → дорого, но качественно # Qwen2.5-VL использует динамическое разрешение с сжатием
// audio modality
Аудио-модальность требует обработки сигнала во времени. Главные задачи: ASR (speech-to-text), TTS (text-to-speech), audio understanding (звуки, музыка, эмоции), multi-speaker обработка.
Whisper — seq2seq трансформер, обученный на 680k часов аудио из интернета. Входной сигнал конвертируется в log-mel spectrogram (80 mel bins, 25ms frames), кодируется Conv1D + трансформером. Декодер авторегрессивно генерирует текст.
import whisper import numpy as np # Загружаем модель (tiny/base/small/medium/large-v3) model = whisper.load_model("large-v3") # Транскрибируем аудио файл result = model.transcribe( "audio.mp3", language="ru", # явно указываем язык task="transcribe", # или "translate" для перевода word_timestamps=True, # время каждого слова temperature=0, # детерминизм ) print(result["text"]) # Доступ к сегментам с временными метками: for seg in result["segments"]: print(f"[{seg['start']:.1f}–{seg['end']:.1f}] {seg['text']}") # Через HuggingFace (рекомендуется для батчей): from transformers import pipeline asr = pipeline( "automatic-speech-recognition", model="openai/whisper-large-v3", chunk_length_s=30, # окно 30с для длинных записей stride_length_s=5, # перекрытие 5с batch_size=8, ) text = asr("long_audio.wav", return_timestamps=True)
Для задач понимания аудио используются генерализованные аудио-энкодеры, обученные не только на речи.
| Модель | Архитектура | Сильная сторона |
|---|---|---|
| Whisper | CNN + Transformer seq2seq | ASR, перевод речи |
| wav2vec 2.0 | CNN + Transformer (CTC) | Low-resource ASR |
| HuBERT | CNN + BERT (offline labels) | Speech SSL, emotion |
| CLAP | Audio encoder + Text encoder (CLIP-like) | Audio-text retrieval |
| Encodec | CNN streaming codec | Neural audio compression |
// video modality
Видео = изображения × время + аудио. Ключевая сложность: длинные видео могут содержать тысячи кадров. Обрабатывать все — слишком дорого. Нужен умный sampling.
// VIDEO ENCODING · три основных подхода к обработке видеоряда
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor from qwen_vl_utils import process_vision_info import torch model = Qwen2VLForConditionalGeneration.from_pretrained( "Qwen/Qwen2.5-VL-7B-Instruct", torch_dtype=torch.bfloat16, device_map="auto", ) processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct") # ── Видео через URL или локальный файл ─────────────────────── messages = [{ "role": "user", "content": [ { "type": "video", "video": "video.mp4", # или URL "max_pixels": 360 * 420, # ограничение разрешения "fps": 1.0, # 1 кадр в секунду }, {"type": "text", "text": "Опиши, что происходит на видео. Когда появляется человек?"}, ], }] text_prompt = processor.apply_chat_template(messages, tokenize=False) image_inputs, video_inputs = process_vision_info(messages) inputs = processor( text=[text_prompt], images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt", ).to(model.device) with torch.no_grad(): output_ids = model.generate(**inputs, max_new_tokens=512) output_text = processor.batch_decode( output_ids[:, inputs.input_ids.shape[1]:], skip_special_tokens=True )[0] print(output_text)
// omni-modal
Омни-модальные модели — системы, принимающие любую комбинацию текста, изображений, аудио и видео в одном промпте и способные генерировать ответ в любой из этих модальностей. Это текущий frontier AI.
Первая модель с нативной поддержкой всех модальностей (text, image, audio, video) в едином трансформере. Аудио не конвертируется в текст — модель работает напрямую с аудио-токенами. Поддерживает real-time voice с sub-300ms latency. Визуальная часть на основе ViT с динамическим разрешением.
Gemini разрабатывался с нуля как мультимодальный (в отличие от GPT-4 который изначально текстовый). Gemini 2.0 Flash: vision, audio (native), video, documents, code. Поддерживает до 2M tokens context (включая видео). Gemini 2.5 Pro — добавляет extended thinking.
Две компоненты: Thinker — LLM обрабатывающая все входные модальности и генерирующая текст; Talker — streaming autoregressive модель для генерации речи из текстового вывода Thinker. Модели работают параллельно — low latency TTS. Поддерживает video, audio, image, text ввод одновременно.
R1-Omni (2025): применяет GRPO-обучение (chain-of-thought reasoning) к мультимодальным задачам. Показывает улучшение на AV (audio-visual) emotion recognition через RL-trained reasoning chain перед ответом. Новый паттерн: reasoning + multimodality.
// OMNI ARCHITECTURES · три подхода: каскад, unified encoder, thinker-talker
// оценка
| Бенчмарк | Задачи | Примеров |
|---|---|---|
| MMBench | Perception, reasoning, knowledge — 20 категорий | 4.3k |
| MMMU | Университетские вопросы по 30 дисциплинам с изображениями | 11.5k |
| MMT-Bench | 162 задачи (VQA, OCR, science, chart, math) | 31k |
| MMStar | «Сложные» примеры без text-only shortcuts | 1.5k |
| MathVista | Математика с визуальным контекстом | 6.1k |
| DocVQA | Вопросы по документам (сканы, PDF) | 50k |
| ChartQA | Вопросы по графикам и диаграммам | 32k |
| Бенчмарк | Тип |
|---|---|
| AIR-Bench | Audio question answering (речь, звуки, музыка) |
| AudioBench | Audio understanding, instruction following |
| EgoSchema | Egocentric video QA, ~3 минуты видео |
| Video-MME | Video QA: short/medium/long clips |
| MVBench | 20 задач на temporal understanding |
Данные приведены по опубликованным техническим отчётам (2024–2025). MMMU — University-level multimodal understanding.
// практика
from transformers import PaliGemmaForConditionalGeneration, PaliGemmaProcessor from PIL import Image import torch # PaliGemma: SigLIP-SO400M + Gemma 2B — отличен для document tasks model = PaliGemmaForConditionalGeneration.from_pretrained( "google/paligemma-3b-mix-448", # 448 = высокое разрешение torch_dtype=torch.bfloat16, device_map="auto", ) processor = PaliGemmaProcessor.from_pretrained("google/paligemma-3b-mix-448") def extract_from_document(image_path: str, question: str) -> str: image = Image.open(image_path).convert("RGB") inputs = processor( images=image, text=question, return_tensors="pt", ).to(model.device, torch.bfloat16) with torch.inference_mode(): generated = model.generate(**inputs, max_new_tokens=256, do_sample=False) # Убираем входные токены из вывода output = processor.decode( generated[0][inputs.input_ids.shape[1]:], skip_special_tokens=True ) return output # Примеры использования на PDF/скан документах: questions = [ "Извлеки все суммы из таблицы. Верни JSON.", "Какова итоговая сумма в строке Total?", "Кто подписал этот договор?", "Что написано в колонке 'Описание работ'?", "ocr", # PaliGemma понимает прямые задачи: ocr, detect, caption ] for q in questions: answer = extract_from_document("invoice.png", q) print(f"Q: {q}\nA: {answer}\n")
import sounddevice as sd import numpy as np import whisper, edge_tts from openai import OpenAI import asyncio, tempfile # Загружаем модели asr_model = whisper.load_model("base") # быстрее medium, для real-time llm = OpenAI() history = [] async def voice_turn(): """Один цикл: записать → распознать → ответить → озвучить.""" # 1. Запись аудио (VAD в реальном приложении) print("Говорите...") audio = sd.rec(int(5 * 16000), samplerate=16000, channels=1, dtype='float32') sd.wait() # 2. ASR: float32 array → текст audio_flat = audio.flatten() result = asr_model.transcribe(audio_flat, language="ru", fp16=False) user_text = result["text"].strip() print(f"Пользователь: {user_text}") if not user_text: return # 3. LLM генерация history.append({"role": "user", "content": user_text}) resp = llm.chat.completions.create( model="gpt-4o-mini", messages=history, max_tokens=200, temperature=0.7, ) assistant_text = resp.choices[0].message.content history.append({"role": "assistant", "content": assistant_text}) print(f"Ассистент: {assistant_text}") # 4. TTS через edge-tts (Microsoft, бесплатно) communicate = edge_tts.Communicate(assistant_text, voice="ru-RU-SvetlanaNeural") with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f: await communicate.save(f.name) # воспроизводим через pygame или sounddevice asyncio.run(voice_turn())
CXR (рентген груди), патология, дерматология. Модели: BioViL-T (CLIP-based), Med-PaLM M, LLaVA-Med. Задачи: report generation, VQA, anomaly detection. Требует fine-tuning на доменных данных — общие VLM слабы.
Qwen-VL или PaliGemma для распознавания товаров на полке, генерации описаний для e-commerce. Поиск «найди похожий товар» = CLIP-based retrieval + multimodal reranking.
Video-LLM для ответов на вопросы по видеозаписям. «Когда человек взял предмет?» → temporal grounding. EfficientSAM + Video-LLaVA. Основная проблема: latency при длинных видео.
Извлечение данных из договоров, счетов, финансовой отчётности. GOT-OCR2 (OCR) → PaliGemma/Qwen-VL (structured extraction) → валидация через схему. Hit@1 на RVL-CDIP > 95% с современными VLM.