Языковая Модель без магии: Крошечная Language Model на чистом Node.js. autograd.. autograd. backpropagation.. autograd. backpropagation. JavaScript.. autograd. backpropagation. JavaScript. machine learning.. autograd. backpropagation. JavaScript. machine learning. machinelearning.. autograd. backpropagation. JavaScript. machine learning. machinelearning. nodejs.. autograd. backpropagation. JavaScript. machine learning. machinelearning. nodejs. self-attention.. autograd. backpropagation. JavaScript. machine learning. machinelearning. nodejs. self-attention. sft.. autograd. backpropagation. JavaScript. machine learning. machinelearning. nodejs. self-attention. sft. нейронная сеть.
Схема Языковой Модели

Схема Языковой Модели

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-слоя:

Матрицы весов AFTER и DELTA из logs/training-log.txt

Матрицы весов AFTER и DELTA из logs/training-log.txt

Что показывает запуск

Сначала программа выводит ответы модели до обучения:

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 × Wq
K = X × Wk
V = X × Wv

score = 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.jsNeuron, 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.

Автор: Maktordev

Источник