English | Русский
Tokenization → embeddings → causal Transformer → LM head → softmax → loss → backpropagation. Без TensorFlow, без PyTorch, и без hidden autograd.
Учебная causal language model без TensorFlow, PyTorch и ML-библиотек. Каждый скаляр, нейрон, градиент, attention score и шаг обновления весов реализован обычным JavaScript и доступен для просмотра в отладчике.
В проекте остался один сценарий и одна команда:
node src/train.js --generalize --adaptive-teach
Достаточно Node.js 18.19 или новее. Устанавливать зависимости не нужно.
Реальный фрагмент из logs/training-log.txt: матрицы AFTER и DELTA одного FFN-слоя:
Что показывает запуск
Сначала программа выводит ответы модели до обучения:
BEFORE TRAINING — random, usually wrong answers> can human read ? model: ? <unk> ... expected: human can read. [WRONG]> can cat read ? model: ? <unk> ... expected: cat cannot read. [WRONG]
Затем выполняются pre-training, SFT и adaptive SFT. В конце ответы становятся устойчивыми и правильными:
FINAL ANSWERS AFTER ADAPTIVE SFT> can human read ? model: human can read. [CORRECT]> can fish swim ? model: fish can swim. [CORRECT]> can bird fly ? model: bird can fly. [CORRECT]> can cat read ? model: cat cannot read. [CORRECT]Rehearsal controls preserved: 14/14.Stable criterion reached 11 times in a row.
Первоначальные ответы меняются, потому что веса инициализируются случайно. На тестовой машине полный запуск со скалярным autograd занимает около четырёх минут.
Единственный pipeline обучения
текст → word tokenization → token и position embeddings → два causal Transformer-блока → multi-head self-attention → FFN с двумя скрытыми слоями → LM head → softmax probabilities → следующий токен → cross-entropy loss → backpropagation → обновление весов Adam
Запуск состоит из трёх последовательных этапов.
1. Pre-training
Модель читает декларативные связи и предсказывает следующий токен:
human can read .fish can swim .bird can fly .dog cannot read .
Связь cat + read намеренно отсутствует.
2. Supervised fine-tuning
SFT обучает формату вопроса и ответа на 42 парах:
question : can fish swim ? answer : fish can swim .question : is bird able to fly ? answer : bird can fly .
Loss считается только для токенов ответа. Каждая эпоха проходит каждую позицию ответа, поэтому примеры не пропускаются случайной выборкой.
3. Adaptive SFT с rehearsal
Недостающая связь передаётся только как правильные target-токены:
['cat', 'cannot', 'read', '.']
Обучаются шесть связанных формулировок, например:
can cat read ?is cat able to read ?does the cat know how to read ?
Обучение останавливается, только когда:
-
все adaptive- и rehearsal-ответы правильны;
-
вероятность каждого правильного target-токена не ниже 95%;
-
полная проверка успешно проходит минимум 11 раз подряд.
Обучение только новым вопросам про кошку вызывало catastrophic forgetting: модель начинала возвращать ответ про кошку на посторонние вопросы. Rehearsal исправляет это, повторяя во время adaptive-этапа 14 ранее изученных связей can ... ?.
Шаг обучения простым кодом
Центральная операция намеренно оставлена короткой:
function learnOneToken({ model, optimizer, input, targetId }) { const loss = model.loss(input, targetId); optimizer.zeroGrad(); loss.backward(); optimizer.step(); return loss.data;}
Её смысл:
loss = -log(P(правильный следующий токен | предыдущие токены))
backward() вычисляет dLoss/dWeight для всех участвовавших весов. Затем Adam изменяет веса так, чтобы вероятность правильного токена стала выше.
Нейрон как экземпляр класса
Каждый нейрон вычисляет знакомую формулу:
output = activation(sum(input[i] × weight[i]) + bias)
Linear — обычный массив таких нейронов. Каждый вес и bias является объектом Value, поэтому операции создают вычислительный граф для backpropagation.
Self-attention и FFN
Для каждого токена attention создаёт Query, Key и Value:
Q = X × WqK = X × WkV = X × Wvscore = dot(Q, K) / sqrt(headSize)attention = softmax(score)output = attention × V
Causal-цикл рассматривает только текущую и предыдущие позиции, поэтому модель не видит будущий target. После attention каждый токен проходит через:
dModel → hidden ReLU → hidden ReLU → dModel
Оба подслоя окружены residual connections и LayerNorm.
Автоматический лог обучения
Каждый запуск автоматически создаёт:
logs/training-log.txt
Лог представляет собой последовательную ASCII-схему, а не сырой JSON:
текст -> tokenizer -> embeddings -> Transformer blocks -> LM head -> softmax | +-> pre-training -> SFT -> adaptive SFT+-- TRANSFORMER BLOCK 0 / FFN / HIDDEN 1| строка w[00] w[01] ... bias| neuron[0] +0.123456 -0.234567 ... +0.010000| ...| LARGEST CHANGE: neuron[3] / weight[7]| before +0.120000 -> after +0.180000 -> delta +0.060000
В нём по порядку записано каждое событие обучения, начальные матрицы, все матрицы после pre-training/SFT и adaptive SFT, а также точная delta-матрица каждого слоя. Строки подписаны как token[n], position[n] или neuron[n], поэтому изменение можно проследить до слоя, нейрона и номера веса.
Структура проекта
-
src/value.js— скалярный autograd и backpropagation. -
src/nn.js—Neuron,Linearи LayerNorm. -
src/model.js— embeddings, attention, Transformer-блоки, FFN и LM head. -
src/tokenizer.js— word-level tokenization. -
src/corpus.js— единственный набор для pre-training, SFT, adaptive и контроля. -
src/optimizer.js— Adam и gradient clipping. -
src/training-log.js— снимки модели и полная ASCII-трассировка матриц. -
src/train.js— единый читаемый pipeline обучения. -
test/model.test.js— проверки autograd, нейрона, токенизации и вероятностей.
Это настоящая language model?
Да по архитектуре и механике обучения, но нет по промышленному масштабу.
|
Этот проект |
Production LLM |
|---|---|
|
Word-level словарь из 24 токенов |
Десятки или сотни тысяч subword-токенов |
|
2 160 параметров |
Миллионы или миллиарды параметров |
|
Два Transformer-блока |
Десятки или сотни блоков |
|
Скалярный JavaScript autograd |
Тензорный autograd на GPU/TPU |
|
Маленький структурированный dataset |
Огромные подготовленные корпуса |
|
Узкое обученное поведение |
Широкий язык, знания и reasoning |
Основной причинный путь настоящий:
токен → embedding → attention → FFN → logits → probability → loss → gradient → новый вес → изменившееся поведение
Слово «tiny» описывает масштаб, а не другой смысл обучения.
Репозиторий: tiny-language-model-neuro-js.
ссылка на оригинал статьи https://habr.com/ru/articles/1063406/