Нейронные сети Мультимодальные модели: речь, зрение и язык в одной сети
0%

Мультимодальные модели: речь, зрение и язык в одной сети

Мультимодальные модели: речь, зрение и язык в одной сети

Весь трек до этого места двигался по модальностям раздельно: свёрточные сети работали с картинками, рекуррентные и трансформеры — с текстом, генеративные модели порождали изображения. Реальность так не устроена. Пользователь присылает фотографию счётчика и спрашивает голосом, сколько платить. Служба поддержки хочет расшифровку звонка вместе со скриншотом ошибки. Модерация должна понимать мем, где текст безобиден, а картинка — нет.

Мультимодальная модель — это не «три модели в контейнере». Это одна сеть, внутри которой разные модальности живут в общем пространстве представлений и потому могут влиять друг на друга: слово «полосатый» сдвигает интерпретацию пикселей, а интонация — интерпретацию слов. Эта статья про то, как такое пространство строится, чем платят за каждый его вариант и что ломается при выкатке.

1. Общий принцип: всё превращается в последовательность векторов

Главная идея, из которой следует остальное, обманчиво проста. Трансформеру безразлично, что стоит за входным вектором. Ему нужна последовательность элементов размерности $d$ — дальше работает внимание. Значит, задача сводится к одному вопросу: как из сырой модальности нарезать токены.

Модальность Чем режем Сколько позиций получается
Текст BPE-токенизатор ≈ 0,75 токена на слово
Изображение патчи 14×14 или 16×16 пикселей (ViT) 336×336 → 576 патчей
Аудио окна 25 мс с шагом 10 мс → мел-спектрограмма → свёртки stride 2 30 с → 1500 позиций
Видео кадры (1–2 fps) × патчи, плюс временная ось 10 с при 1 fps → 10 × 576
Аудио для генерации нейрокодек (RVQ), 8 кодовых книг 50–75 токенов на секунду на книгу

Как изображение, звук и текст превращаются в одну последовательность токенов

Из этой таблицы следует главный практический вывод статьи, который стоит усвоить раньше всех архитектурных деталей: картинка стоит как страница текста, а звук — как несколько страниц. Одна фотография в разрешении 336×336 занимает 576 позиций контекста, примерно как 430 слов. Десять минут аудио в лоб — это 30 000 позиций, что не влезает никуда и стоит квадратично. Все ухищрения дальше — пулинг патчей, Q-Former, тайлинг, нарезка по речевой активности — придуманы ради экономии этого бюджета.

def context_budget(images: int = 0, image_res: int = 336, patch: int = 14,
                   tiles: int = 1, audio_sec: float = 0.0,
                   words: int = 0) -> dict:
    """Во сколько позиций контекста обойдётся мультимодальный запрос."""
    per_image = (image_res // patch) ** 2 * tiles       # тайлинг умножает линейно
    img_tokens = images * per_image
    audio_tokens = int(audio_sec * 50)                  # Whisper: 1 позиция ≈ 20 мс
    text_tokens = int(words * 1.33)
    total = img_tokens + audio_tokens + text_tokens
    return {"картинки": img_tokens, "звук": audio_tokens,
            "текст": text_tokens, "всего": total}


print(context_budget(images=4, tiles=5, audio_sec=60, words=200))
# {'картинки': 11520, 'звук': 3000, 'текст': 266, 'всего': 14786}

Четыре скриншота в высоком разрешении и минута речи — почти 15 тысяч позиций. Это уже не «добавим картинку в промпт», это архитектурное решение с ценником.

2. Таксономия: три разные задачи под одним словом

Слово «мультимодальность» покрывает три несводимые друг к другу постановки.

Границы важны: сопоставление учит два энкодера класть похожие объекты рядом (это разобрано в статье об эмбеддингах), понимание прикручивает энкодер к языковой модели, чтобы та рассуждала о содержимом, а генерация требует декодера в целевой модальности. Модель, отлично ищущая картинки по тексту, может быть неспособна ответить «сколько на фото людей» — CLIP не считает объекты, он сравнивает.

3. Как соединяют модальности: четыре стратегии

Стратегия Плюсы Минусы Когда выбирать
Раздельные башни эмбеддинги считаются заранее, поиск за миллисекунды нет рассуждения, только близость поиск, дедупликация, zero-shot метки
Projector дёшево обучается (часы на 8 картах), переиспользует готовую LLM картинка съедает контекст 90 % прикладных VLM
Cross-attention контекст не раздувается, LLM не деградирует на тексте сложнее, нужны изменения в LLM много изображений в одном запросе, видео
Единый словарь одна модель для всего, генерация в любой модальности дорого обучать, потери на квантизации фронтир, any-to-any

Рецепт projector победил по прагматике: он описан в LLaVA (Liu et al., 2023) и состоит буквально из двух-трёх строк — взять выходы предпоследнего слоя ViT, прогнать через MLP в размерность LLM и вставить на место специального токена-плейсхолдера.

import torch
import torch.nn as nn

class VisionProjector(nn.Module):
    """MLP-проектор: d_vision -> d_llm. Ровно это и есть «мультимодальность» в LLaVA-1.5."""
    def __init__(self, d_vision: int = 1024, d_llm: int = 4096):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(d_vision, d_llm),
                                 nn.GELU(),
                                 nn.Linear(d_llm, d_llm))

    def forward(self, patch_feats: torch.Tensor) -> torch.Tensor:
        # patch_feats: [B, 576, d_vision] -> [B, 576, d_llm]
        return self.net(patch_feats)


def merge_multimodal(input_ids, text_embed_fn, image_feats, image_token_id):
    """Подменяем эмбеддинги плейсхолдеров на признаки патчей.

    input_ids: [B, T], где image_token_id повторён 576 раз подряд.
    image_feats: [B, 576, d_llm] — выход проектора.
    Сложность: O(B*T*d) по времени и памяти — это просто копирование.
    """
    embeds = text_embed_fn(input_ids)              # [B, T, d_llm]
    mask = (input_ids == image_token_id)           # [B, T]
    embeds[mask] = image_feats.reshape(-1, embeds.shape[-1]).to(embeds.dtype)
    return embeds

Дальше в LLM ничего не меняется: обычное причинное внимание по смешанной последовательности. Модель «видит» ровно потому, что часть её входных векторов пришла не из таблицы эмбеддингов, а из зрительного энкодера.

4. Выравнивание пространств и modality gap

Чтобы подмена работала, пространства должны быть согласованы. Есть два способа их согласовать.

Контрастно. CLIP (Radford et al., 2021) обучает две башни на 400 млн пар «картинка — подпись» симметричным InfoNCE: правильная пара должна быть ближе, чем все остальные в батче. Детали лосса разобраны в статье про эмбеддинги; здесь важны два прикладных следствия. Во-первых, качество линейно зависит от размера батча — негативы берутся из него же, поэтому CLIP обучают с батчами в десятки тысяч, а это сразу отправляет нас к распределённому обучению. Во-вторых, SigLIP заменяет softmax-нормировку попарной сигмоидой и снимает зависимость от гигантского батча — сегодня это дефолт для энкодера в VLM.

Генеративно. Проектор обучается на задаче «опиши картинку»: градиент от языковой потери проходит через LLM в проектор и подстраивает его выход под то, что LLM умеет читать. Никакого контрастного лосса, никаких негативов.

Тонкость, о которую спотыкаются при отладке: даже у хорошо обученного CLIP изображения и тексты лежат в разных конусах общего пространства — это явление называется modality gap и подробно разобрано в Mind the Gap (Liang et al., 2022). Косинус между картинкой и её собственной подписью может быть 0,3, а между двумя случайными подписями — 0,6. Отсюда правило: сравнивать можно только внутри пары модальностей и только по ранжированию, абсолютные пороги косинуса переносить с текст-текста на текст-картинку нельзя.

5. Речь: три способа превратить звук в текст

Распознавание речи — самая инженерно зрелая мультимодальная задача, и её стоит разобрать отдельно, потому что она задаёт словарь для всего остального.

Проблема в основе: аудио — 1500 позиций, текст — 20 токенов, и выравнивания между ними нет. Три семейства решений отличаются именно тем, как они справляются с этим.

5.1 CTC: сумма по всем выравниваниям

Connectionist Temporal Classification (Graves et al., 2006) предлагает предсказывать по символу на каждый кадр, добавив пустой символ «бланк». Любая последовательность кадров, которая после схлопывания повторов и удаления бланков даёт целевой текст, считается допустимым выравниванием; вероятность текста — сумма по всем таким путям:

$$P(y \mid x) = \sum_{\pi \in \mathcal{B}^{-1}(y)} \prod_{t=1}^{T} P(\pi_t \mid x)$$

Сумма считается динамическим программированием за $O(T \cdot U)$, где $U$ — длина текста. Плата за простоту — условная независимость кадров: CTC не моделирует $P(y_t \mid y_{<t})$, поэтому охотно пишет «превет» и нуждается во внешней языковой модели при декодировании.

5.2 RNN-T: то же самое, но со стримингом

RNN-Transducer добавляет к акустическому энкодеру предсказывающую сеть по уже выданным символам и объединяющую сеть. Условная независимость снимается, при этом модель остаётся строго причинной по времени — её можно кормить звуком по 200 мс и получать текст на лету. Это стандарт голосовых ассистентов и телефонии.

5.3 Attention seq2seq: Whisper

Whisper (Radford et al., 2022) — обычный encoder-decoder трансформер: энкодер съедает 30-секундное окно лог-мел-спектрограммы, декодер авторегрессивно порождает текст, а задача (язык, перевод, таймстемпы) задаётся специальными токенами в начале. Обучен на 680 тысячах часов слабо размеченного веб-аудио — отсюда устойчивость к шуму и акцентам, ради которой его и берут.

Подход Стриминг Внешняя LM Слабое место Типичное применение
CTC да обычно нужна орфография, нет контекста лёгкие on-device модели
RNN-T да опционально сложное обучение, память ассистенты, телефония
Attention (Whisper) нет (окна по 30 с) не нужна галлюцинации на тишине офлайн-расшифровка

Практические грабли, каждая стоит пары дней:

  • Галлюцинации на тишине. Whisper на длинном молчании уверенно выдаёт «Субтитры сделал DimaTorzok» и подобные артефакты обучающих данных. Лечится VAD-предфильтром и порогом по no_speech_prob — не постобработкой текста.
  • Нарезка по времени, а не по паузам режет слова пополам и портит соседние сегменты. Резать надо по речевой активности с перекрытием; см. подход WhisperX.
  • WER скрывает то, что важно. Word Error Rate считает все слова равными, а бизнесу важны имена, суммы и артикулы. Заведите отдельную метрику на сущностях.
  • Домен решает больше архитектуры. Список терминов, имён и товаров, поданный через biasing или переранжирование гипотез, обычно даёт больший выигрыш, чем переход на модель вдвое крупнее.
def wer(reference: str, hypothesis: str) -> float:
    """Word Error Rate = расстояние Левенштейна по словам / число слов эталона.

    Время O(n*m), память O(min(n,m)) при построчной свёртке.
    """
    r, h = reference.split(), hypothesis.split()
    prev = list(range(len(h) + 1))
    for i, rw in enumerate(r, start=1):
        cur = [i] + [0] * len(h)
        for j, hw in enumerate(h, start=1):
            cost = 0 if rw == hw else 1
            cur[j] = min(prev[j] + 1,        # удаление
                         cur[j - 1] + 1,     # вставка
                         prev[j - 1] + cost)  # замена
        prev = cur
    return prev[-1] / max(len(r), 1)


print(round(wer("перевести десять тысяч рублей", "перевести 10 000 рублей"), 2))  # 1.0

Обратите внимание на последнюю строку: формально WER = 1.0, то есть «полный провал», хотя смысл распознан идеально. Без нормализации чисел, регистра и пунктуации метрика измеряет не качество, а форматирование.

Обратная задача — синтез речи — устроена сегодня как языковое моделирование над токенами нейрокодека (EnCodec, VALL-E): текст превращается в последовательность акустических кодов, кодек-декодер восстанавливает волну. То есть и здесь всё сводится к «нарезать модальность на токены».

6. VLM: как собирают модель, которая видит

Стандартный рецепт (LLaVA-1.5, Qwen-VL и почти все открытые VLM) состоит из трёх блоков и двух стадий обучения.

Блоки. Зрительный энкодер (SigLIP или CLIP ViT-L, ~300 млн параметров) → проектор (MLP, десятки млн) → языковая модель (7–70 млрд). Соотношение размеров объясняет всю экономику: обучается почти всегда только проектор и, на второй стадии, LLM.

Стадия 1 — выравнивание. Замораживаем и энкодер, и LLM, учим только проектор на парах «картинка — подпись». Цель — научить проектор говорить на языке эмбеддингов LLM. Дёшево: сотни GPU-часов.

Стадия 2 — инструкционная настройка. Размораживаем LLM (или вешаем LoRA), учим на диалогах с изображениями: вопросы, рассуждения, OCR, таблицы. Именно здесь модель учится отвечать, а не описывать.

Разрешение — главный рычаг качества на документах и скриншотах. 336×336 недостаточно, чтобы прочитать текст на скриншоте, а поднять разрешение в лоб нельзя: число патчей растёт квадратично. Отсюда приёмы: тайлинг (AnyRes — картинка режется на несколько плиток плюс уменьшенная копия целиком), динамическое разрешение (Qwen2-VL), пулинг патчей и Q-Former из BLIP-2, сжимающий картинку до 32 обучаемых запросов вместо 576 патчей.

Что ломается при обучении VLM:

  • Катастрофическое забывание текста. Разморозили LLM на картиночных данных — просели чисто текстовые метрики. Лечение: подмешивать 20–40 % текстовых данных и держать LR для LLM на порядок ниже, чем для проектора.
  • Слепота к деталям. Модель уверенно отвечает по «общему виду» картинки, потому что обучающие подписи описывали общий вид. Нужны данные с координатами, счётом объектов, чтением мелкого текста.
  • Позиционное кодирование. Патчи — двумерная сетка, а LLM ждёт одномерную ось. Наивная развёртка теряет геометрию; отсюда 2D-RoPE и его варианты.

7. Оценка: где мультимодальные метрики врут

Что меряем Метрика Чем обманывает
Кросс-модальный поиск Recall@K, mAP зависит от размера базы; R@1 на 1000 и на 10 млн — разные числа
Речь WER, CER нормализация; равный вес всех слов
VQA точность по exact match штрафует синонимы и развёрнутые ответы
Описания CIDEr, BLEU коррелируют с человеком слабо
Галлюцинации POPE легко переобучиться на «нет»
Инструкции LLM-as-judge предвзятость к длине и к своему семейству моделей

Два правила, которые экономят месяцы. Первое: публичные бенчмарки протекли в обучающие данные. Веб-скрейпы содержат сами тесты, и высокий балл может означать запоминание. Второе: собственный размеченный набор из 300–500 примеров вашего домена информативнее любого рейтинга — методика построения таких наборов разобрана в Оценке и бенчмарках.

Отдельный тест, который стоит завести всегда: negative control. Подайте вопрос про объект, которого на картинке нет. Модель, отвечающая «да, он красный», не понимает изображение — она угадывает по языковому приору. Это самый быстрый способ отличить рабочую VLM от красивой демонстрации.

8. Продакшн: где на самом деле уходит время и деньги

Мультимодальный сервис ломается не там, где текстовый.

Препроцессинг — не бесплатно. Декодирование JPEG 12 Мп, ресайз, нормализация, для аудио — ресемплинг и мел-спектрограмма: десятки миллисекунд CPU на запрос. На загруженном сервисе это становится узким местом раньше GPU. Выносите в отдельный пул воркеров, кэшируйте по хешу файла, для аудио считайте спектрограмму на GPU.

Префилл доминирует. Как показано в разделе про инференс LLM, стоимость складывается из префилла и декодирования. У VLM 2900 зрительных токенов префилла против 150 токенов ответа — то есть 90 % вычислений происходит до первого символа. Практические следствия: кэшируйте зрительные признаки для повторно используемых изображений, снижайте разрешение там, где не нужен OCR, и не считайте стоимость запроса «по длине ответа».

Батчинг сложнее. Изображения разного размера и аудио разной длины не батчатся наивно. Работающая схема — бакеты по длине: сортируем очередь по числу токенов, собираем батч из близких. Общая механика continuous batching описана в статье про инференс, сюда добавляется только группировка по размеру входа.

Безопасность расширяется. Изображение — это канал ввода, а значит канал инъекции: текст на картинке модель читает как инструкцию. Атака описана в Abusing Images and Sounds for Indirect Instruction Injection, защита — та же, что для текста: недоверие к содержимому входа, ограничение прав инструментов, проверка действий. Подробно — в статье про безопасность моделей и в ИИ-инженерии.

Деградация по модальностям. Если ASR-подсистема упала, сервис должен уметь работать в текстовом режиме, а не отдавать 500. Мультимодальный пайплайн — это цепочка из нескольких моделей, и её надёжность считается как произведение, а не как минимум.

9. Типичные ошибки

  • Считать, что картинка «почти бесплатна» в промпте. Она стоит сотни или тысячи позиций контекста и львиную долю вычислений.
  • Гнаться за разрешением вместо данных. Модель не читает мелкий текст чаще из-за того, что её этому не учили, а не из-за нехватки пикселей.
  • Переносить пороги косинуса между модальностями. Modality gap делает такие пороги бессмысленными.
  • Мерить ASR только по WER без нормализации. Числа, регистр и пунктуация дадут фиктивные ошибки и скроют настоящие.
  • Резать аудио по таймеру. Нарезка должна идти по паузам, с перекрытием.
  • Разморозить всё сразу. Проектор ещё не выровнен, а градиенты уже ломают LLM: получается модель, которая хуже исходной и на тексте, и на картинках.
  • Забыть про negative control в оценке. Без него не отличить понимание от языкового приора.
  • Обучать VLM с нуля. Почти всегда правильный ответ — взять готовый энкодер и готовую LLM и обучить проектор; это разница между сотнями GPU-часов и сотнями тысяч.

10. Мини-итог

  • Мультимодальность сводится к одному приёму: нарезать любую модальность на последовательность векторов размерности $d$ и отдать трансформеру.
  • Бюджет позиций — главный практический ограничитель: 576 токенов на картинку, 50 позиций на секунду аудио. Считайте его до выбора архитектуры.
  • Четыре способа соединения: раздельные башни (поиск), проектор (рабочая лошадь VLM), cross-attention (много изображений), единый словарь токенов (фронтир).
  • Выравнивание пространств бывает контрастным (CLIP/SigLIP) и генеративным (обучение проектора через языковую потерю); modality gap никуда не исчезает.
  • В речи три подхода: CTC (просто, стриминг, нужна LM), RNN-T (стриминг + контекст), attention seq2seq (Whisper: качество офлайн, галлюцинации на тишине).
  • VLM собирается за две стадии: выравнивание проектора, затем инструкционная настройка. Разрешение решается тайлингом, а не лобовым увеличением.
  • В проде дороже всего препроцессинг и префилл; изображение — полноценный канал инъекции.

Источники

Что дальше

На этом трек «Нейронные сети» завершён. Путь получился такой: от перцептрона и обратного распространения через свёртки, рекуррентность, трансформеры и LLM к масштабному обучению, деплою, безопасности и моделям, которые работают со всеми модальностями сразу. Если хочется освежить общую картину — вернитесь к карте трека.

Куда двигаться дальше:

  • Фундамент. Калибровка, сдвиг распределения, метрики и срезы — это классический ML, доведённый до предела. Систематически он разобран в треке Машинное обучение.
  • Алгоритмическая база. Чтобы уверенно оценивать сложность и писать эффективный инференс, полезно вернуться к Алгоритмам и Структурам данных.
  • Прикладной слой. Как строить продукты поверх готовых моделей — промптинг, RAG, агенты, дообучение: трек ИИ-инженерия.
  • Инженерия сервисов. ML-система на 90 % состоит из обычного бэкенда: очереди, таймауты, обсервабилити. См. Go и Архитектурные паттерны.
  • Данные. Пайплайны, качество и происхождение данных — Data Engineering.
  • Процесс. Как встроить evals и релизы моделей в работу команды — Управление продуктом и Управление проектами.

Полная карта портала с рекомендованным порядком изучения — в Роадмапе.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов