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

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.

Комментарии (0)