R&D · VPL

VPL: конструктор архитектур и что им намеряно.

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

0.0170
разброс val_loss одной и той же замороженной модели при исходной метрике: 15 замеров, 1.5473–1.6034. Больше почти всех различий в исходной таблице
n=3
сидов на конфигурацию в перепроверке (в исходном поиске — один прогон на конфигурацию)
6.4σ
крупнейший подтверждённый эффект: MoE-FFN против плотного GELU при равном числе шагов (−0.040 val_loss). По бюджету в секундах этот же механизм был отброшен

инструмент

Что такое VPL

Узлы на холсте — это тензорные операции: linear, matmul, softmax, rope, alibi_bias, causal_mask, split_heads/merge_heads, rmsnorm, conv1d, ssm, а также готовые составные: attention_block, moe_ffn, memory. Соединяешь их портами, жмёшь Build — сервер компилирует граф в настоящую модель и обучает её. Всего 35 типов узлов; вложенные подграфы позволяют свернуть кусок схемы в один узел.

Главное свойство: ошибки формы всплывают при сборке, а не в середине forward-прохода. У каждого порта есть тип, и движок выводит формы по всему графу до того, как создан хоть один параметр. Несходящаяся размерность — это фраза с номером узла, а не стек-трейс из недр PyTorch.

Q headssplit_headsK headssplit_headsV headssplit_headsQ@K ᵀmatmul× 1/√dscaleмаскаcausal_masksoftmaxsoftmaxattn@Vmatmul эти пять узлов и есть составной sdpa — один узел вместо пяти

Внимание, собранное из примитивов — так это выглядит на холсте. Порты типизированы, и формы выводятся по всей цепочке до того, как создан хоть один параметр. Заменить softmax на что-то другое, убрать маску или вписать ALiBi между matmul и маской — правка одного узла; в составном sdpa для этого пришлось бы лезть в код.

Каждый прогон записывается сам: кривые обучения и валидации, число параметров, токены, оценка FLOPs, скорость, норма градиента, пиковая память, сид, расписание LR. Результат попадает в группу сравнения — ключ из корпуса, длины окна, бюджета, LR, токенизатора, протокола оценки и каузальности. Числа из разных групп несопоставимы, и страница результатов никогда не кладёт их в один список: это то, что мешает сравнить 15-секундный прогон с 1200-шаговым и назвать разницу архитектурой.

почему это важно Всё, что ниже, — следствие этой дисциплины. Оба разворота выводов на этой странице нашлись не потому, что кто-то заметил странное число, а потому что измерение было воспроизведено.

библиотека

Что уже собрано

Шаблон — это сохранённый граф: открывается на холст одной кнопкой, ставится в очередь на обучение, и его замеры копятся отдельно от него самого. Сейчас в библиотеке 19 именованных блоков и ещё 50 конфигураций архитектурного поиска (growth-demo: …), результаты которого — ниже на этой странице.

Числа во второй колонке — сколько узлов в графе. Разброс от 2 до 176 при одном и том же движке и есть ответ на вопрос, зачем нужны и составные узлы, и примитивы: одну и ту же архитектуру можно собрать крупными блоками или разложить до отдельных операций, и второе — единственный способ поменять то, что внутри.

ШаблонУзловЧто в нём интересного
Настоящие архитектуры — собраны по опубликованным описаниям, каждая обучается как есть
GPT-220хрестоматийный pre-LN блок: LayerNorm → MHA → residual → GELU-MLP
LLaMA / LLaMA-224RMSNorm вместо LayerNorm, RoPE, SwiGLU вместо плотного FFN
Mistral20то же плюс скользящее окно внимания и GQA 8/2
Mixtral 8×7B16оконное GQA, но SwiGLU заменён разрежённым MoE top-2 из 8
Gemma226норма с обеих сторон каждого подблока — 4 RMSNorm вместо 2
Falcon / GPT-NeoX19внимание и MLP считаются параллельно из одного входа, а не последовательно
PaLM19параллельный блок + MQA + RoPE + SwiGLU одновременно
Grok-1176GQA 6:1, 8 экспертов top-2, сэндвич-нормы; 8 слоёв, 19.7 млн параметров
BERT19тот же GPT-2, но без causal_mask — и на этом ловится утечка будущего
Механизмы внимания, разобранные до примитивов — то же, что делает sdpa, но каждой операцией отдельно
RoPE self-attention16проекции, разбиение на головы, поворот Q/K, маска, softmax, сборка
GQA108 голов запросов на 2 головы ключей — K/V транслируются вверх
MQA10предельный случай: одна общая K/V-голова, кэш меньше в 8 раз
ALiBi15штраф за расстояние прямо в логитах вместо поворота Q/K
Гибриды и не-внимание
MAC целиком из примитивов27включая саму рекуррентность быстровесовой памяти — совпадает с эталоном побитово
Conv1D + SSM3последовательность без внимания вообще: свёртка и модель пространства состояний
Внимание + память + MoE3три составных узла подряд — сколько проводов нужно реальному гибриду
RoPE + MoE18ручное внимание, за ним разрежённый FFN со своим residual
Учебные
Стартовый для роста2два узла с одним портом — на них показывается функционально-сохраняющий рост
Вложенный подграф2два одинаковых подграфа в цепи; профилирование заходит внутрь каждого

важное следствие BERT-блок отличается от GPT-2 ровно одним отсутствующим узлом — маской. Его val_loss выходит 0.0204 при случайном угадывании 4.17, потому что модель видит ответ. Это не результат, а проверка детектора: такой граф помечается как некаузальный и физически не может попасть в один рейтинг с остальными.

перепроверка

Что показала детерминированная метрика

Исходная оценка усредняла 10 случайно выбранных батчей валидации, из-за чего val_loss был случайной величиной даже при неизменных весах. Прямой замер: одна обученная модель, 15 вызовов подряд, веса между ними не менялись — значения от 1.5473 до 1.6034, стандартное отклонение 0.0170. После перехода на фиксированные равномерные окна по всему валидационному сплиту те же 15 замеров дают одинаковый результат до четвёртого знака (стандартное отклонение 0.0000).

Ниже — конфигурации, перезамеренные новой метрикой на трёх фиксированных сидах каждая. Условия одинаковы: Shakespeare, seq_len=128, lr=0.005, шедулер constant, бюджет 60 секунд.

КонфигурацияParamsval_loss (n=3)Было (шумная метрика)
depth=6, GQA(kv=2), RoPE291,5841.5857 ± 0.00681.5978
depth=6, MHA(kv=4), RoPE316,5441.5902 ± 0.01341.5840
depth=6, MQA(kv=1), RoPE279,1041.5975 ± 0.00331.5955
depth=8, MQA(kv=1), RoPE — прежний «чемпион»366,5921.6084 ± 0.00581.6016
1.581.601.62

Конфигурация, объявленная лучшей по исходной таблице (val_loss 1.5737), при усреднении по трём сидам оказалась худшей из четырёх. Значение 1.5737 воспроизвести не удалось ни разу: диапазон её собственных прогонов — 1.6017–1.6118.

Единственное различие внутри этой четвёрки, превышающее шум, — глубина: depth=6 против depth=8 даёт разрыв 0.0227 при стандартной ошибке 0.0052, то есть 4.4 стандартных ошибки. Различия между MQA, GQA и MHA остаются в пределах 1–3 стандартных ошибок и на трёх сидах неразделимы.

перепроверка

Равное число шагов вместо равного времени

Все числа выше получены при бюджете в 60 секунд. Такой бюджет измеряет «что лучше за минуту» — величину, зависящую от стоимости шага, от конкретной машины и от того, что ещё на ней считается. Ниже — те же вопросы при бюджете в 1200 шагов: сколько бы ни стоил шаг, все конфигурации получают одинаковое число обновлений весов. Такой замер вдобавок воспроизводим на другом железе. 15 конфигураций, по три сида, детерминированная метрика, lr=0.005.

Конфигурацияval_loss (n=3, 1200 шагов)Что проверяется
depth=6, MQA + MoE-FFN1.5634 ± 0.0055тип FFN
depth=6, dim=961.5773 ± 0.0020ширина
depth=8, MQA1.5839 ± 0.0051глубина
depth=7, MQA1.5900 ± 0.0036глубина
depth=6, MHA(kv=4)1.5905 ± 0.0091разделение голов
depth=6, GQA(kv=2)1.5968 ± 0.0106разделение голов
depth=6, MQA(kv=1)1.6033 ± 0.0092базовая конфигурация
depth=6, MQA + SwiGLU (param-matched)1.6088 ± 0.0039тип FFN
depth=5, MQA1.6228 ± 0.0143глубина
depth=6, ALiBi вместо RoPE1.6431 ± 0.0056позиционное кодирование
depth=4, MQA1.6475 ± 0.0098глубина
depth=3, MQA1.6800 ± 0.0136глубина
depth=2, эффективный блок + память1.7108 ± 0.0094память
depth=2, тот же блок без памяти1.7227 ± 0.0085изолированный контроль
depth=2, Falcon-блок (параллельный residual)1.7879 ± 0.0145топология
1.551.681.81
что нельзя прочитать из этой таблицы

Три нижние строки — конфигурации из двух блоков, а не из шести. Их место внизу объясняется глубиной, а не механизмом; сравнивать Falcon-блок с depth=6-строками нельзя. Осмысленно только их сравнение между собой (пара «с памятью / без памяти» — изолированный контроль, отличающийся ровно одним узлом).

перепроверка

Ширина: однофакторный свип

В исходном поиске ширина менялась одновременно с глубиной, поэтому вывод «шире — лучше» не был однофакторным. Здесь глубина зафиксирована на 4, меняется только dim; внимание (MHA 4 головы + RoPE), FFN (плотный GELU ×4), LR и бюджет 60 с — общие. Бюджет здесь по времени, то есть вывод относится к вопросу «что лучше за минуту»; при равном числе шагов ширина не перепроверялась.

dimParamsШагов/60сval_loss (n=3)
64216,57620141.5957 ± 0.0063
96472,32021851.5538 ± 0.0111
128826,36820941.5494 ± 0.0027
1921,829,37618911.5480 ± 0.0188
2563,225,60019021.5370 ± 0.0091
1.521.561.60

перепроверка

Что не подтвердилось

дефект измерения

Утечка целевой переменной в двунаправленном блоке

При сплошной проверке всех сохранённых шаблонов шаблон «BERT-style block (bidirectional)» показал val_loss 0.0204. При словаре из 65 символов случайное угадывание даёт ln(65) ≈ 4.17, а обученные модели здесь дают около 1.5 — то есть результат невозможен.

Причина: шаблон намеренно двунаправленный (нет узла причинной маски, softmax считается по сырым скорам), а обучение идёт на предсказание следующего токена. Позиция i видит токен i+1, который и является её собственной целью, и модель просто считывает ответ. Ошибки в шаблоне нет — он честно описан как encoder-only; ошибка была в том, что инструмент принял такую комбинацию молча и записал результат в общую таблицу, где 0.02 навсегда осталось бы недостижимым «лучшим».

Добавлена проверка при каждой сборке: вторая половина входной последовательности возмущается, и если предсказания в первой половине сдвигаются — граф не причинный. Проверка эмпирическая, а не структурная (не «ищем узел маски»), поэтому ловит и не-каузальную свёртку, и неверно заданную рекуррентность. Такие прогоны выделяются в отдельную группу сравнения и не могут конкурировать с причинными.

исходный поиск

39 конфигураций: исходная таблица

измерено метрикой, признанной невоспроизводимой

Все значения ниже получены оценкой по 10 случайным батчам, разброс которой на неизменной модели составляет 0.0170 — больше, чем почти любая разница в этой таблице. Каждая строка — один прогон, без повторов по сидам. Таблица оставлена как исходные данные: то, что правдоподобный рейтинг из 39 позиций оказался преимущественно шумом, проверяемо только пока эти числа на виду. Как рейтинг архитектур её читать нельзя.

Показать таблицу — 39 строк, метрика с разбросом 0.0170
КонфигурацияParamsШагов/60сval_loss
depth=8, MQA, RoPE, lr=0.005 — перепроверено, см. выше366,5929901.5737
depth=6, MQA, RoPE, lr=0.005279,10412771.5806
depth=6, MQA, RoPE, lr=0.006279,10412781.6060
depth=7, MQA, RoPE, lr=0.005322,84811431.5872
depth=6, GQA(kv=2), RoPE, lr=0.005291,58412651.5846
depth=6, MQA, RoPE, lr=0.007279,10413111.5972
depth=6, MQA, RoPE, lr=0.01279,10412771.5885
depth=6, MQA, RoPE, lr=0.004279,10413511.6002
depth=6, dim=96 (ширина), lr=0.003612,19212871.6027
depth=6, MQA, RoPE, lr=0.003 (базовый чемпион)279,10412981.6022
depth=6, GQA(kv=2), RoPE, lr=0.003291,58412631.6072
depth=5, MQA, RoPE, lr=0.003235,36016571.6124
depth=7, MQA, RoPE, lr=0.003322,84810891.6223
depth=6, ALiBi вместо RoPE, lr=0.003279,10414221.6426
depth=3, MQA, RoPE, lr=0.003147,87224481.6370
depth=2, ALiBi вместо RoPE, lr=0.003104,12837451.6372
depth=4, MQA, RoPE, lr=0.003191,61618161.6433
depth=2, GQA(kv=2), RoPE, lr=0.003108,28832761.6468
depth=2, MoE-FFN вместо dense MLP303,16815731.6486
depth=6, MoE-FFN вместо dense MLP876,2245661.6912
depth=8, MQA, RoPE, lr=0.003366,5929961.6374
depth=2, MQA, RoPE, full context (без memory)104,12834091.6716
depth=2, MQA, RoPE, window=64 + fast-weight memory между блоками120,64025471.6649
depth=2, MQA, RoPE, window=64, без memory (изолированный контроль для строки выше «MQA + memory»)104,12832191.6640
depth=2, полный MHA (kv=4), RoPE116,60834391.6371
depth=1, Falcon-style parallel блок103,87234271.7062
depth=1, attn+memory слиты в 1 блок76,89634391.7369
depth=1, + pre-norm перед MLP тоже (полный pre-norm блок)66,62472771.7265
depth=1, + RoPE66,62454321.7249
depth=1, window=3266,62455451.7264
depth=1, window=6466,62455481.7208
depth=1, + MLP66,49674751.7171
depth=1, window=1666,62455741.7387
depth=1, MQA (без остальных изменений)60,38455771.7512
depth=1, голое attention (без norm/MLP)33,28091101.9696
depth=1, + pre-norm33,40889032.0037
depth=6, SSM (attention-free), гибрид attn+SSM200,640–221,47297–2082.02–2.39
1.561.792.02

Полные графы всех 39 конфигураций сохранены как шаблоны в библиотеке VPL (autoresearch/vpl_templates/, префикс growth-demo:).

наблюдения

По исходной таблице

Наблюдения ниже сделаны до перепроверки. Помечено, что из них устояло.

методика

Условия измерений

Корпус — Shakespeare (символьный токенизатор, vocab_size=65), seq_len=128, LR-шедулер constant. Каждая архитектура собиралась и обучалась напрямую через VPL: /build/train/start/eval.

Исходный поиск: dim=64 если не указано иное, один прогон на конфигурацию, оценка по 10 случайным батчам валидации.

Перепроверка: два прохода — при бюджете 60 секунд и при бюджете 1200 шагов. Три фиксированных сида на конфигурацию; сид задаётся до сборки модели и определяет как инициализацию весов, так и порядок обучающих батчей. Оценка — по фиксированным равномерно расположенным окнам всего валидационного сплита, то есть val_loss стал функцией модели, а не случайной величиной. Каждый записанный результат хранит протокол, которым он измерен, и результаты разных протоколов не смешиваются в одном сравнении.

два бюджета — два разных вопроса

«60 секунд» отвечает на вопрос «что лучше за минуту», «1200 шагов» — на вопрос «что лучше за одинаковое число обновлений весов». Оба законны, и всюду, где механизм меняет стоимость шага, они дают разные ответы (глубина, MoE-FFN). Число в секундах вдобавок не воспроизводимо: оно зависит от конкретной машины и от того, что ещё на ней считается — два одновременных прогона одной конфигурации получили 1142 и 990 шагов за те же 30 секунд, и val_loss разошёлся на 0.013. Бюджет в шагах от этого свободен: четыре одновременных прогона по 600 шагов дали разброс 0.0059 со значениями, перемешанными между исполнителями, а не отсортированными по ним. 60-секундные замеры на этой странице сделаны последовательно, 1200-шаговые — параллельно.

граница метода

Все выводы — для этого конкретного масштаба (tiny-модели, сотни тысяч — единицы миллионов параметров) и бюджета (60 секунд обучения). Ранжирование архитектур на этом масштабе не обязано переноситься на модели с миллиардами параметров и часами обучения — это демонстрация метода поиска через VPL, а не рекомендация по архитектуре для продакшена.

Полный протокол экспериментов (соглашения по разводке портов, чек-лист перед полным прогоном, формат логирования, требования к воспроизводимости) — в autoresearch/vpl_program.md репозитория.

ограничения

Методологические ограничения

Как менялась эта страница