Каково это сегодня — с нуля собрать и обучить собственную визуально-языковую модель?
Именно этот вопрос и подтолкнул меня попробовать самому. Я взял базовую языковую модель, подключил к ней визуальный бэкбон и несколько часов обучал всё это на арендованной GPU в RunPod, пытаясь добиться от модели осмысленных описаний изображений.
Я собрал собственную визуально-языковую модель на базе небольшой языковой модели Baguettotron и визуального энкодера InternViT, используя стандартную схему ViT–MLP–LLM. Весь эксперимент занял около $200 на одной GPU: модель с 628 млн параметров научилась описывать изображения и отвечать на простые вопросы по ним. В статье — пошаговый разбор архитектуры, трёх этапов обучения, выбора датасетов и решений, которые пришлось принимать по ходу эксперимента. Я покажу, какие подходы сработали, где модель начала ошибаться и почему попытка добавить рассуждения через дополнительное дообучение ухудшила качество ответов. Получился практический разбор того, что сегодня можно сделать с открытыми моделями и ограниченным бюджетом.TL;DR:
Сегодня скачать небольшие и при этом хорошие визуальные энкодеры и языковые модели довольно просто. Обычно не хватает другого — понятного рецепта, как собрать всё это в единую систему. Конечно, я опирался на уже существующие работы:
-
Hugging Face выпустили немало моделей и руководств по созданию собственных визуально-языковых моделей. SmolVLM-500M показал, что нужно, чтобы создать небольшую, конкурентоспособную и полностью воспроизводимую VLM. А статья Train Your Own Encoder-Free VLM in $100 предлагает полный рецепт обучения на одной GPU от начала до конца, включая бюджет.
-
nanoVLM — намеренно учебная и минималистичная библиотека, где архитектурные решения объяснены максимально прозрачно. Вы выбираете нужные компоненты, а остальное берёт на себя фреймворк.
-
LFM2.5-VL-450M стала для меня одним из ориентиров при работе над семейством визуально-языковых моделей такого масштаба.
В этой работе я постарался своими силами собрать что-то действительно работающее, используя только открытые материалы, модели и техники. Это одновременно пошаговый рецепт, который можно повторить, и честный журнал всех ошибок, которые я успел допустить по пути.
Всё открыто: код и веса модели доступны на GitHub и Hugging Face.
Примечание об ИИ-ассистенте: на протяжении разработки я использовал Claude — особенно на этапе первоначального брейншторма и настройки проекта, а также при оценке модели и подведении итогов. Соответствующие файлы и решения, которые мы принимали вместе, находятся в репозитории проекта.
Мотивация: модель, которая мне нравилась, но ничего не видела
Идея выросла из Baguettotron — чисто текстовой модели. И, если честно, меня зацепила не только сама модель, но и то, как Pleias её представили. Данные, веса, история обучения, логика архитектурных решений — всё открыто и всё можно изучить. Такие релизы встречаются реже, чем хотелось бы, и именно на такой основе мне хотелось строить дальше.
Baguettotron — небольшая модель для рассуждений (Small Reasoning Model) на 321 млн параметров, необычная сразу в двух отношениях. Во-первых, своей архитектурой: 80 слоёв при скрытой размерности 576. По словам авторов, это самая глубокая SLM среди моделей такого масштаба — отсюда и «багет» в названии. Во-вторых, и это гораздо интереснее, своими данными.
Baguettotron обучали на 200 млрд токенов из SYNTH — полностью синтетического универсального датасета, построенного на основе 50 000 ключевых статей Википедии. Никакого веб-скрейпинга. Эту фразу легко пропустить, поэтому сформулирую иначе: в принципе, вы можете прочитать весь корпус предобучения этой модели. Можно посмотреть, что именно подали на вход, и попытаться понять, почему на выходе получилось именно то, что получилось. Для области, где стандартное описание датасета звучит как «обучено на большом корпусе текстов из интернета», это совсем другой уровень прозрачности.
Не хватало только одного: модель ничего не видела. Поэтому я решил дать ей глаза 👀
Вот что получилось после прохождения всего пайплайна — реальный диалог с готовой моделью, жадное декодирование, запуск на ноутбуке:

user: Опиши изображение.
assistant: На изображении два кота сидят на диване. Один кот опустил голову, лапы подняты.
Два кота, диван и даже поза — и всё это от модели на 628 млн параметров, обучение которой обошлось примерно в $200, а ответ на ноутбуке занимает около секунды! Массово генерировать alt-тексты она, конечно, не станет, но она действительно смотрит на изображение, а весь путь, благодаря которому это стало возможным, можно проследить и изучить.
Архитектура
|
Изображение (448×448) Всего: ~628 млн параметров |
Я использовал «стандартную» схему ViT–MLP–LLM в формулировке InternVL: замороженный визуальный энкодер, небольшую MLP со случайно инициализированными весами, которая проецирует визуальные признаки в пространство эмбеддингов языковой модели, и саму LLM.
Энкодер и проектор
Я выбрал InternViT-300M-448px-V2.5 вместо универсального CLIP- или SigLIP-энкодера. Решающим аргументом стало предупреждение самих авторов в карточке модели: «По нашему опыту, серия InternViT V2.5 лучше подходит для построения MLLM, чем для традиционных задач компьютерного зрения».
Энкодер превращает изображение 448×448 в 1024 патч-токена, а это довольно много. Прогонять 1024 визуальных токена через 80-слойную языковую модель для каждого изображения при таком бюджете слишком дорого. К тому же они буквально забивают текст: короткий вопрос рядом с тысячей токенов изображения превращается почти в статистическую погрешность. Поэтому перед проектором я их сжимаю.
Операция pixel unshuffle с коэффициентом 2 сворачивает каждую область 2×2 в один токен с вчетверо большей канальной размерностью. В результате 1024 токена × 1024d превращаются в 256 токенов × 4096d — никаких параметров и никакого обучения, просто перестановка данных, где пространственное разрешение обменивается на глубину каналов. Затем двухслойная MLP преобразует 4096 → 576, чтобы привести представления к скрытой размерности Baguettotron.
И здесь ничего оригинального. SmolVLM — ближайшая опубликованная модель к тому, что собирал я, — активно использует такое сжатие, унаследовав его от Idefics3. InternVL делает то же самое по той же причине. Как пишут авторы, которые заодно разработали и сам энкодер: «мы применили операцию pixel unshuffle, сократив число визуальных токенов до четверти от исходного количества».
Сам проектор состоит из двух линейных слоёв и GELU — всего около 2,7 млн параметров, которые обучаются со случайной инициализации. Всё остальное — уже предобученные компоненты, опубликованные другими людьми. Только поэтому весь проект вообще укладывается в такой бюджет.
Обучение
Три этапа, все на одной H100 SXM, арендованной в RunPod. Общие расходы — около $200.
|
Этап |
Что обучается |
Данные |
Время |
|
1 — согласование представлений |
только проектор (~2,7M) |
LLaVA-CC3M-Pretrain-595K |
~5 ч |
|
2 — дообучение на инструкциях |
проектор + LLM (~324M) |
The Cauldron + 10% SYNTH |
~60 ч |
|
3 — SFT на данных с рассуждениями |
проектор + LLM |
R1-Vision-Reasoning-Instructions |
~7 ч |
Этап 1 — учим визуальные токены согласовываться с пространством языковой модели
Проектор инициализируется случайными весами, поэтому в самом начале он фактически подаёт в языковую модель шум. На первом этапе ViT и LLM полностью заморожены, а обучаются только эти 2,7 млн параметров на 595 тыс. пар «изображение — подпись» из LLaVA-CC3M, по двухэтапной схеме, которую предложили в LLaVA.
Задача здесь — согласование представлений, а не развитие новых способностей модели. По сути, мы учим одну небольшую матрицу выдавать векторы, которые языковая модель уже умеет интерпретировать. Пять часов обучения, при этом веса самой LLM вообще не меняются.
Этап 2 — учим модель отвечать
Теперь вместе с проектором размораживается и вся языковая модель, а для обучения используется The Cauldron — 47 поднаборов данных с визуальными инструкциями, смешанных с 10% чисто текстовых данных SYNTH, чтобы избежать катастрофического забывания.
Здесь я принял два решения.
Оставить ViT замороженным. При 324 млн обучаемых параметров и 60 часах обучения тратить часть этого бюджета ещё и на визуальный энкодер означало бы урезать всё остальное. Сам энкодер и так был хорош; узким местом оставалось отображение его представлений в пространство моей LLM.
Предварительно подставлять </think>. Baguettotron изначально стремится генерировать цепочку рассуждений. Но в The Cauldron таких цепочек нет вообще. Поэтому вместо того, чтобы бороться с поведением базовой модели, я начинал каждый ответ ассистента при обучении с закрывающего токена </think> — по сути, говоря модели на её собственном языке: «сейчас не рассуждай, просто ответь». На инференсе процессор делает это автоматически, так что режимы обучения и использования совпадают.
Именно этой предварительной подстановки не хватало в моей первой попытке, из-за чего модель вообще не заработала. Скорее всего, я слишком сильно отклонился от распределения данных, которое Baguettotron видел во время собственного обучения, и расплатился за эту ошибку впустую потраченным запуском. Обидно.
Этап 3 — учим модель рассуждать о том, что она видит
Baguettotron изначально умеет рассуждать, поэтому я хотел вернуть эту способность, которую фактически подавил на The Cauldron, где цепочки рассуждений отсутствуют. Для этого я добавил третий этап: SFT примерно на 150 тыс. примеров только с правильными ответами из R1-Vision-Reasoning-Instructions. В них есть цепочки <think> в стиле DeepSeek-R1, связанные с изображениями.
К сожалению, чтобы действительно закрепить такое поведение, этому этапу потребовалось бы намного больше данных и времени на обучение. Как видно из результатов ниже, после третьего этапа модель во время рассуждений галлюцинировала почти всегда — если не вообще всегда.
Оценка и результаты
Для оценки мне хотелось чего-то быстрого, что позволило бы хотя бы качественно понять, способна ли модель распознавать, что изображено на входной картинке. Я также попробовал немного усложнить задачу — задал несколько «сложных» вопросов и проверил рассуждение, но результат оказался откровенно провальным. Скорее всего, чтобы получить действительно качественную модель, пригодную для чего-то большего, чем простое описание изображений, нужно значительно больше обучения. Но это уже выходило за рамки эксперимента.
Для этапов 1 и 2 я просто взял несколько изображений из COCO и визуально оценил качество ответов. А для сравнения этапов 2 и 3 использовал шесть изображений из отложенной выборки RealWorldQA, четыре типа вопросов и четыре набора параметров декодирования. Ответы оценивала по шкале от 1 до 5 визуально-языковая модель Claude Opus 5.
Этап 1 против этапа 2
Чтобы проверить, работают ли вообще первые два этапа, я взял четыре фотографии и дал обоим чекпоинтам один и тот же промпт:
После первого этапа описания получаются короче и менее содержательными, а после второго — лучше сформулированными, более длинными и фактическими: «автобус едет по дороге» вместо бессмысленного «украшение», «два кота» вместо одного в первом примере, «медведь сидит на траве» вместо никем не запрошенного «хорошего друга».
Именно такой прогресс от этих двух этапов и ожидается. На первом этапе обучались всего 2,7 млн параметров, поэтому модель учится прежде всего понимать, что находится на изображении, и почти не учится нормально формулировать законченные ответы. На втором этапе языковая модель размораживается — и вместе с этим возвращается грамматика.
Но стоит потребовать от модели на втором этапе чего-то длиннее короткого ответа, и она начинает выдумывать детали:
user: Подробно опиши изображение.
assistant: На этом изображении два кота сидят на белом диване. У них закрыты уши, а второй кот держит фонарик.
Диван на самом деле розовый, уши никто не закрывает, а «фонарик» — это лежащий рядом с котами пульт от телевизора. Модель видит, что там находится какой-то вытянутый предмет, не может понять, что именно, и додумывает и сам объект, и его связь с котом. На это стоит обратить внимание: это не случайный шум. Предложение грамматически корректно, правдоподобно построено и говорит в целом о нужных объектах. Ошибки скрываются в деталях — а именно такие ошибки сложнее всего ловить автоматически.
Этап 2: более «сложные» вопросы
Описывать изображения — это одно. А что будет, если действительно задать модели вопрос? Вот три примера, где она справилась, и три, где нет. Примерно такого соотношения удачных и неудачных ответов я от неё и ожидал:
|
вопрос |
ответ |
|
|
Что это за животное? |
Медведь. |
✅ |
|
Что написано на знаке? |
STOP. |
✅ |
|
Какой главный объект на изображении? |
Главный объект — красный автобус. |
✅ |
|
На изображении есть кот? |
Нет. |
❌ их два |
|
Какого цвета знак? |
Жёлтого. |
❌ он красный |
|
Какой номер маршрута у автобуса? |
Да. |
❌ |
Хорошие ответы действительно хороши. На вопрос «Что это за животное?» модель отвечает «Медведь» — то есть делает ровно то, что от неё требуется. А ответ «STOP» на вопрос «Что написано на знаке?» меня даже удивил. Хотя я не могу сказать наверняка, действительно ли модель прочитала буквы или просто распознала красный восьмиугольный знак. На вопрос о номере автобусного маршрута — то есть уже про настоящее OCR по мелкому тексту — она отвечает «Да.», что скорее говорит в пользу второго варианта.
Плохие ответы тоже интересны по-своему. Ответить «Нет» на вопрос «На изображении есть кот?» при фотографии двух спящих котов — это не небольшая ошибка. При этом тот же вопрос в формулировке «Что это за животное?», который идеально сработал для медведя, на изображении с котами тоже даёт ответ «Да». То есть дело не в том, что модель вообще не видит котов. Гораздо тревожнее другое: её ответ слишком сильно зависит от того, как именно сформулирован вопрос.
Этап 2 против этапа 3
Шесть изображений из отложенной выборки RealWorldQA, четыре типа вопросов и четыре набора параметров декодирования для каждого режима. Каждый ответ Claude Opus 5 оценивал по шкале от 1 до 5. Я успел получить 244 результата из 288, прежде чем остановил прогон. Выборка небольшая, поэтому все выводы ниже скорее показывают тенденцию, чем дают окончательный ответ.
|
модель |
средняя оценка |
|
этап 2, прямой ответ |
2,17 |
|
этап 3, режим рассуждения |
1,25 |
Разница — примерно три стандартные ошибки: SFT на данных с рассуждениями сделал модель более связной, но менее точной. Для модели на 628 млн параметров цепочки рассуждений по изображениям оказались недостаточно хорошо обучены, поэтому в основном они добавляли уверенные и хорошо структурированные галлюцинации. Беглость выросла, а привязка ответа к изображению ухудшилась.
В том же прогоне я сравнил четыре набора параметров декодирования: greedy, conservative, creative и конфигурацию в стиле Qwen. Они получили соответственно 2,17 / 1,88 / 1,88 / 1,88 при стандартной ошибке около 0,25–0,30. То есть разница укладывается в шум, поэтому в релизе я оставил жадное декодирование — просто потому, что оно детерминированное.
С учётом этих результатов я решил опубликовать только модель после второго этапа. Код третьего этапа лежит в репозитории и полностью готов к запуску. Если кому-то по какой-то причине понадобятся и его веса, я могу их выложить, но на сегодняшний день эта модель практически непригодна для использования.
Выводы
Сегодня можно самостоятельно собрать «работающую» визуально-языковую модель примерно за цену хорошего ужина на четверых. Для меня это главный итог эксперимента, и, думаю, здесь действительно есть что-то новое: ещё несколько лет назад подобная задача была уделом исследовательских лабораторий. А теперь, когда я знаю, чего точно не стал бы делать повторно, стоимость, вероятно, можно сократить ещё примерно вдвое.
Главные выводы для себя:
-
Не отходить без необходимости от проверенной архитектуры. ViT–MLP–LLM хорошо задокументирована и работает. Лучше потратить запас оригинальности на данные и оценку, а не на способ соединения компонентов.
-
Перебалансировать визуальную часть. Я бы уменьшил размерность визуального бэкбона и вместо этого повысил входное разрешение. Думаю, обучение получилось бы проще и быстрее, а модель смогла бы использовать больше информации, которая действительно есть на изображениях.
-
При таком бюджете этап с рассуждениями лучше пропустить. Чтобы вернуть это поведение, нужно гораздо больше ресурсов, чем потратил я. Либо LLM изначально обучается с таким режимом, либо, как в моём случае, лучше просто остановиться после второго этапа. Но попробовать всё равно было интересно.
Если подвести итог без прикрас: получить хорошие описания изображений можно за несколько часов и относительно небольшие деньги. А вот модель, способная по-настоящему конкурировать с существующими решениями, даже при таком размере требует значительно больше данных и ресурсов, чем один арендованный GPU на пару дней.
Но пройти весь путь было интересно: взять чисто текстовую модель и всего через несколько часов заставить её выдавать связные описания изображений.

В процессе создания собственной VLM важно не только собрать архитектуру и обучить модель, но и понимать, как оценивать её ответы, работать с современными LLM и разбираться в ограничениях таких систем. На бесплатных уроках от преподавателей Otus можно глубже погрузиться в эти темы, задать вопросы и заодно узнать, как устроено обучение на курсах:
-
22 сентября, 18:00. «Ландшафт современного NLP: от эмбеддингов и классических ML-методов до современных LLM». Записаться
-
1 октября, 20:00. «Проверка ответов LLM и борьба с галлюцинациями через промпт-инжиниринг». Записаться
Больше открытых уроков сентября смотрите в дайджесте.
Автор: kmoseenk
- Запись добавлена: 15.09.2026 в 18:25
- Оставлено в


