Неделя 20. Мультимодальность и длинный контекст
Учиться в приложении: тьютор, задачи с кодом →
Ядро: ViT, CLIP и контрастный лосс, проектор LLaVA, экстраполяция RoPE · Глубина: cross-attention и Q-Former, диффузия (05-ГЛУБИНА) · ≈ 10 ч ядро / 22 ч всё
Две темы с общим корнем: трансформеру всё равно, откуда пришли векторы последовательности. Мультимодальность отвечает на вопрос, **как превратить изображение или звук в токены. Длинный контекст отвечает на вопрос, как ведут себя позиции там, где модель их никогда не видела**.
Часть 1. ViT
На пальцах. Картинка 224×224 режется на квадраты 16×16: 14 по горизонтали, 14 по вертикали, всего 196 патчей.
В каждом патче 16·16·3 = 768 чисел, и линейный слой превращает их в вектор размера D.
Дальше картинка превращается в предложение из 196 «слов», и трансформер обрабатывает его как текст.
Картинка 448×448 даёт уже 784 «слова», вчетверо больше, а пар для внимания 784² / 196² = 16 раз больше.
- Изображение режется на патчи
P×P, каждый линейно проецируется вD. Это ровно свёртка с ядромPи шагомP. При 224×224 иP = 16получается 196 токенов - Вывод про стоимость: разрешение ×2 → токенов ×4 → внимание ×16. Поэтому мультимодальные модели так борются за число визуальных токенов
- Выученные позиционные эмбеддинги привязывают модель к разрешению (при смене разрешения нужна интерполяция). 2D-RoPE (позиция = строка и столбец) эту привязку снимает
- Необязательно: напиши на NumPy свёртку
conv2d(x, w, stride, padding)и проверь, что при ядре и шагеPбез паддинга она совпадает с нарезкой на патчи и линейным слоем. Размер выхода⌊(n + 2·pad − P)/stride⌋ + 1, гдеnсторона картинки, на собеседованиях спрашивают отдельно
Часть 1б. Свёртка против патчей: что модель знает до данных
На пальцах. Картинка 5×5, ядро 3×3, без паддинга, шаг 1. Ядро скользит по картинке и в каждой позиции
считает сумму 9 произведений; выход 3×3 (5 − 3 + 1 = 3 по каждой стороне). Параметров 9 (плюс сдвиг):
одно и то же ядро стоит во всех 9 позициях. Полносвязный слой с тем же входом (25 пикселей) и выходом (9 чисел)
держит 25·9 = 225 весов, и каждый выход смотрит на всю картинку. На картинке 224×224 с 3 каналами на входе
и 64 на выходе свёрточный слой 3×3 весит 3·3·3·64 = 1728 чисел при любом размере картинки, а полносвязный
слой с теми же входом и выходом около 150 528 · 3 211 264 ≈ 4.8·10¹¹.
- Свёртка встраивает два знания о картинках, индуктивное смещение (предпочтение, заложенное архитектурой
до всяких данных). Локальность: выход зависит только от окна
k×k. Общий вес по позициям: один и тот же детектор края работает в любом месте картинки. Отсюда эквивариантность к сдвигу: сдвинул вход, выход сдвинулся так же - Поле зрения (receptive field: область входа, от которой зависит выход) у стопки свёрток 3×3 с шагом 1 растёт
на 2 за слой, после
Lслоёв это2L + 1пикселей. Дальние связи CNN получает только глубиной или уменьшением разрешения - В библиотеках DL «свёртка» на деле кросс-корреляция: ядро не отражают. Для обучаемого ядра разницы нет
- ViT держит из этих знаний минимум. Патч-эмбеддинг это свёртка с ядром и шагом
P(общий вес по патчам), а дальше внимание с первого слоя связывает любые два патча, и о соседстве патчей модель узнаёт только из позиционных эмбеддингов, то есть из данных. Вывод: на ImageNet-1k (1.3 млн картинок) ViT без ухищрений уступает ResNet сравнимого размера, а после предобучения на сотнях миллионов картинок обгоняет её (Dosovitskiy et al., 2020). Слабое смещение оплачивается данными, зато не мешает, когда данных много. DeiT (Touvron et al., 2021) догоняет ResNet на одном ImageNet-1k сильными аугментациями и дистилляцией - Тот же довод для LLM: у трансформера над текстом почти нет встроенных знаний о порядке, кроме позиционного кодирования, и выигрывает он масштабом данных. Локальное окно внимания (неделя 7) возвращает локальность ради памяти и скорости, а не ради качества
- Задача тренажёра
conv2d_valid: свёртка одного канала без паддинга и проверка, что при шаге, равном размеру патча, она совпадает с нарезкой на патчи и скалярным произведением. Частотный взгляд на свёртку и его связь с RoPE: 05-ГЛУБИНА, раздел «Неделя 20. Свёртка и частоты»
Часть 2. CLIP и контрастное обучение
На пальцах. Батч из 4 пар: 4 картинки и 4 подписи. Считаем сходство каждой картинки с каждой подписью, получается таблица 4×4. Для картинки 1 правильная подпись первая, остальные три служат неправильными вариантами. Каждая строка таблицы это классификация на 4 класса, правильный ответ на диагонали. То же по столбцам: для подписи ищем её картинку. При батче 32 768, как у CLIP, у каждой строки 32 767 негативов.
- Контрастное обучение: сближать представления правильных пар и раздвигать неправильные.
Nпар (картинка, текст) → матрица сходствN×N, симметричная кросс-энтропия по строкам и столбцам, правильный ответ на диагонали - Вывод: это классификация, где классами служат остальные элементы батча. Размер батча задаёт число кандидатов: при B парах на пример приходится B − 1 негативов, поэтому CLIP учат большими батчами. SigLIP заменяет softmax на сигмоиду по каждой паре и снимает нормировку по всему батчу
Часть 3. Соединение модальностей
- Проектор (LLaVA): MLP переводит выход визуального энкодера в пространство эмбеддингов LLM, визуальные токены вставляются в последовательность как обычные
- Cross-attention (Flamingo): отдельные слои, где текст смотрит на изображение
- Q-Former (BLIP-2, «querying transformer»): фиксированное число обучаемых запросов сжимает изображение
в
Kтокенов. Общий компромисс: число визуальных токенов против детализации - Аудио: Whisper устроен как энкодер-декодер над лог-мел-спектрограммой (энергия звука по частотам во времени, в логарифмической шкале). Нейрокодеки (сети, сжимающие звук в поток дискретных кодов) дают дискретные аудио-токены, и звук можно генерировать как текст
Часть 3б. Генерация без авторегрессии
Картинки в мультимодальных системах почти всегда рисует не авторегрессия. Чтобы понять почему, сначала точно скажем, что такое LLM.
На пальцах. Словарь {а, б}, длина 2. Модель выдаёт p(а) = 0.6 и p(б | а) = 0.3. Тогда p(аб) = 0.6·0.3 = 0.18.
Вероятность любой из четырёх строк считается точно, умножением.
- Авторегрессионная LLM моделирует явную факторизацию
p(x) = Π_{t=1..T} p(x_t | x_<t)(цепное правило, верное для любого распределения). Сумма кросс-энтропий по позициям (неделя 4) равна−log p(x); средняя CE дополнительно делится на T. Цена: генерация занимаетTпоследовательных шагов - Остальные семейства иначе отвечают на два вопроса: что учит модель и как получить из неё образец
| Семейство | Что моделирует | Как сэмплирует | Правдоподобие |
|---|---|---|---|
| Авторегрессия | вероятность следующего токена при префиксе | токен за токеном, T шагов | точное |
| VAE (вариационный автоэнкодер) | кодер в латент z и декодер из z ~ N(0, I) в данные | взять z, один проход декодера | нижняя оценка (ELBO) |
| Диффузия | шумоподавитель: какой шум подмешан на уровне t | от шума, M шагов очищения | у стандартной DDPM вариационная граница; probability-flow ODE допускает численную оценку плотности |
| Normalizing flow | обратимое преобразование с вычислимым якобианом | преобразовать образец базового распределения | вычислимая плотность через замену переменных |
| Flow matching | поле скоростей v(x, t) от шума к данным | решить ОДУ, от 1 до десятков шагов | через ОДУ, дорого |
| GAN (генеративно-состязательная сеть) | генератор без плотности плюс дискриминатор | один проход генератора | нет |
| EBM (энергетическая модель) | энергию U(x), p(x) ∝ exp(−U(x)) | MCMC (блуждание, сходящееся к p), много шагов | до константы |
Softmax из недели 4 это EBM на конечном множестве: логиты служат минус энергиями, а статсумму можно посчитать.
Диффузия (DDPM, Ho et al., 2020). На пальцах. Один «пиксель» x₀ = 3, уровень шума с ᾱ = 0.64 (доля сигнала по дисперсии).
Зашумлённая версия: x_t = √0.64·3 + √0.36·ε = 2.4 + 0.6·ε, ε ~ N(0, 1). Выпало ε = −1, получили x_t = 1.8.
Модель видит 1.8 и номер уровня и угадывает ε. Угадала −1: чистое значение восстанавливается, (1.8 + 0.6)/0.8 = 3.
0.64 + 0.36 = 1, поэтому у данных с дисперсией 1 зашумлённая версия тоже с дисперсией 1, а при ᾱ_t → 0 распределение приближается к N(0, 1).
- Прямой процесс:
x_t = √ᾱ_t·x₀ + √(1 − ᾱ_t)·ε,ᾱ_tубывает от почти 1 до почти 0. Любой уровень получается одним шагом - Обучение: случайный уровень, случайный
ε, лосс‖ε − ε_θ(x_t, t)‖². Обычная MSE-регрессия, без состязания и без MCMC - Генерация: старт из
N(0, I), затемMраз (M: число шагов) вычесть предсказанный шум и подмешать немного свежего (в DDPM сотни шагов, быстрые сэмплеры доводят до десятков). Все позиции очищаются одновременно, порядка «слева направо» нет - Условие (текст) входит в шумоподавитель через cross-attention. Classifier-free guidance: смешивают предсказания с условием и без него и сдвигаются дальше в сторону условного
Латентная диффузия (Rombach et al., 2022). Картинка 512×512×3 это 786 432 числа. Автоэнкодер (VAE из таблицы) сжимает её
в латент 64×64×4, то есть в 16 384 числа, в 48 раз меньше, и диффузия идёт в латентах; декодер один раз возвращает пиксели.
Мелкие детали берёт на себя автоэнкодер, дорогая итеративная часть работает с сутью. Родственная идея: VQ-коды
(номера векторов из выученного словаря) как «визуальные токены», которые можно генерировать авторегрессией.
Маскированная диффузия для текста (MDLM, Sahoo et al., 2024; LLaDA, Nie et al., 2025). На пальцах. Шум для текста
не гауссиана, а маска. Уровень t = 0.5: из 8 токенов закрыто 4, модель предсказывает все 4 сразу, CE считается по закрытым.
Генерация: 8 масок, за 4 шага раскрываем по 2 токена, в которых модель увереннее всего.
- Отличие от BERT (энкодер, которого учат угадывать закрытые токены): там закрыта фиксированная доля, около 15%, а здесь уровень случаен от 0 до 1, и модель умеет стартовать с полностью закрытой строки, то есть генерировать. Плюсы: внимание в обе стороны, несколько токенов за шаг, правка середины. Минусы: привычного KV-кэша нет, правдоподобие только снизу
Flow matching (Lipman et al., 2023; rectified flow, Liu et al., 2023). На пальцах. Шум z = −2, данные x = 4.
Соединим прямой: x_t = (1 − t)·z + t·x; при t = 0.25 точка −0.5, скорость на всём пути x − z = 6.
Модель v_θ(x_t, t) учится MSE предсказывать эту скорость. Генерация: взять шум и решить dx/dt = v_θ(x, t) методом Эйлера.
Одной паре хватило бы одного шага −2 + 6 = 4, но прямые разных пар пересекаются, модель выучивает их среднее,
и пути изгибаются, поэтому шагов несколько. Диффузию тоже можно записать как ОДУ: flow matching просто выбирает пути попроще.
- Вывод: текст держится за авторегрессию из-за точного правдоподобия, KV-кэша и дискретности. Диффузия сильна там, где данные непрерывны и все позиции связаны (пиксели, звук). Маскированная диффузия пробует отвоевать текст параллельной генерацией
Часть 4. Длинный контекст
На пальцах. Голова H = 128, база Θ = 10 000. Самая быстрая пара RoPE поворачивается на 1 радиан за позицию:
полный круг каждые ~6 позиций, и за обучение она видит все углы. Самая медленная делает оборот примерно за 54 000 позиций.
При обучении на 4096 токенах она успевает повернуться лишь на ~27°. На позиции 16 384 угол будет ~108°, такого модель не видела.
Position interpolation делит позиции на 4: углы снова в пределах 27°,
но соседние токены теперь отличаются на четверть шага, в том числе у быстрых пар.
- Из недели 7: медленные пары RoPE за всё обучение не успевают сделать полный оборот. Вывод: за пределами обучающей длины ломаются именно они: их углы вне распределения, а быстрые пары видели все углы
- Position interpolation: сжать позиции
m → m·L/L', тогда углы в пределах виденного, но быстрые частоты теряют разрешение между соседями. УвеличениеΘ(NTK-aware) растягивает медленные частоты сильнее быстрых. YaRN задаёт разные правила для разных частотных диапазонов плюс поправка температуры внимания - Перплексия на длинном тексте не доказывает использования контекста. Нужны needle-in-a-haystack (спрятать факт в длинном тексте и спросить о нём) и RULER (набор синтетических задач на длинный контекст); «Lost in the Middle»: извлечение хуже из середины
Код → nanolm/vit.py: минимальный ViT-энкодер: patchify, CLS-токен (дополнительный токен,
чей выход служит представлением всей картинки),
выученные позиционные эмбеддинги, двунаправленное внимание. Проверка:
pytest tests/test_vit.py -v; ключевой тест test_linear_patch_embedding_equals_strided_conv:
линейный слой над вытянутыми патчами и Conv2d со stride = P дают одно и то же.
scripts/rope_extrapolation.py: обучить на окнах длины S, замерить перплексию
на S и 2S отдельно для позиций < S и ≥ S, затем повторить с базой Θ ×4 и ×8.
Средняя перплексия по окну разрыв прячет; видно его только по позициям.
Математика (трек D): D31: контрастный лосс и температура; D32: coupon collector.
Интервью-вопрос недели: «Модель обучена на 4k, нужен контекст 32k. Что делаешь?»
Структура: диагноз (медленные частоты RoPE вне распределения) → варианты
(PI, увеличение Θ, YaRN) → короткое дообучение на длинных документах →
стоимость (KV-кэш ×8; внимание: FlashAttention, GQA) → проверка не перплексией,
а поиском по позициям.
Источники: Dosovitskiy et al., ViT (2020); Radford et al., CLIP (2021); Liu et al., LLaVA (2023); Chen et al., Positional Interpolation (2023); Peng et al., YaRN (2023); Liu et al., Lost in the Middle (2023); Kingma & Welling, Auto-Encoding Variational Bayes (2013); Goodfellow et al., GAN (2014); Ho et al., DDPM (2020); Rombach et al., Latent Diffusion (2022); Lipman et al., Flow Matching (2023); Sahoo et al., MDLM (2024); Nie et al., LLaDA (2025); Touvron et al., DeiT (2021); Torralba, Isola, Freeman, Foundations of Computer Vision (2024), гл. 24 и 26.
Глубже: 05-ГЛУБИНА, разделы «★★ Недели 16 и 20. Генеративные модели за пределами авторегрессии», «Неделя 20. Свёртка и частоты» и «Что ещё добавить по мелочи».
Результаты недели
- Могу реализовать ViT-энкодер с нуля и посчитать число токенов и стоимость внимания для заданного разрешения.
- Могу записать лосс CLIP и объяснить, почему при B парах на пример приходится B − 1 негативов и почему нужны оба направления лосса.
- Могу сравнить проектор, cross-attention и Q-Former по числу токенов и стоимости за 3 минуты.
- Могу объяснить, какие частоты RoPE ломаются при экстраполяции, и показать это замером перплексии по позициям.
- Могу сравнить авторегрессию, VAE, диффузию, flow matching, GAN и EBM: что моделирует каждое семейство и как сэмплирует.
Самопроверка
- При фиксированном патче обе стороны картинки увеличили вдвое. Во сколько раз вырастут число патчей и число пар полного внимания? Почему это не обязательно такой же рост полного времени модели?
- Чем position interpolation отличается от увеличения
Θи что теряет каждый способ? - Почему хорошая перплексия на 32k ещё не значит, что модель использует контекст?
- Как авторегрессионная модель вычисляет правдоподобие строки по цепному правилу? Почему у стандартной DDPM обычно используют вариационную границу, а у probability-flow ODE возможна оценка плотности через интегрирование?
- Сколько параметров у свёрточного слоя 3×3 с 3 входными и 64 выходными каналами и от чего это число не зависит? Почему ViT уступает CNN на ImageNet-1k, но обгоняет после предобучения на сотнях миллионов картинок?
Mock-интервью недели (3 и 4 из 12). (3) ML coding, сессия B: генерация с KV-кэшем и проверка совпадения с полным проходом. (4) Дизайн эксперимента, сессия C: «Пользуется ли модель контекстом длиннее 32k или только не ломается на нём?»