Как я строил LLM с нуля на TypeScript: взгляд изнутри

#LLM#TypeScript#transformers#AI coding

Когда решил разобраться, как работают трансформеры в деталях, столкнулся с тем, что большинство туториалов и реализаций — на Python. Это классика, но для меня Python — это чаще black box: я запускаю код, поправляю параметры, но не всегда понимаю, что именно происходит “под капотом”. Чтобы снять этот вопрос, решил построить LLM (large language model) с нуля — но не на Python, а на TypeScript.

Почему TypeScript? В первую очередь, чтобы выжать максимум понимания. Переписывая идеи трансформеров в другом языке, приходится буквально заново осваивать нюансы реализации, а не просто копировать чужие паттерны. Кроме того, TypeScript — это не только фронтенд-инструмент, а удобный язык с типизацией, который помогает ловить ошибки на этапе компиляции. В итоге получилась поэтапная реализация, где разобраны основные части: даталоадеры, эмбеддинги, attention.

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

Зачем строить LLM на TypeScript?

Если вы когда-то пытались копаться в исходниках Hugging Face или OpenAI, то знаете: часто там десятки тысяч строк, куча оптимизаций, GPU-ускорение и сложные зависимости. С одной стороны — удобно, с другой — сложно разобраться, как именно работает attention или position encoding.

TypeScript подходит, чтобы:

В итоге это отличный способ понять, почему именно так устроен трансформер, как он обучается и почему attention — ключ к работе модели.

Основные этапы и что сделано

Даталоадеры и подготовка данных

Первый блок — загрузка и подготовка текста. На практике я делал минимальный токенайзер, который превращает текст в числа (индексы слов или subword). В TypeScript пришлось аккуратно обрабатывать строки и создать эффективный batch loader, который формирует пакеты данных для обучения.

Это оказалось не тривиально, потому что нужно было учесть:

Сделал класс DataLoader, который умеет выдавать mini-batches с нужным форматированием.

Эмбеддинги

Дальше — эмбеддинги. Это преобразование токенов в векторы фиксированной размерности. Здесь всё стандартно: создал слой эмбеддинга как матрицу весов, которая обучается вместе с моделью. В TypeScript это просто двумерный массив с градиентами (градиенты считаю вручную, без автодифа).

Практическая боль — отсутствие готовых библиотек для работы с матрицами и градиентами. В итоге написал простейшие операции с векторами и матрицами (умножение, сложение, транспозиция).

Внимание (Attention)

Самая интересная часть — механизм attention. Он позволяет модели фокусироваться на разных частях входа.

Реализовал scaled dot-product attention:

function scaledDotProductAttention(
  q: number[][],
  k: number[][],
  v: number[][]
): number[][] {
  const dk = k[0].length;
  const scores = matMul(q, transpose(k)).map(row =>
    row.map(score => score / Math.sqrt(dk))
  );
  const weights = softmax(scores);
  return matMul(weights, v);
}

Здесь пришлось самостоятельно писать матричные операции и softmax, следить, чтобы не было переполнений и сделать маскирование для будущих токенов при генерации.

Обратное распространение (backpropagation)

Пожалуй, самый сложный этап. В TypeScript нет автодифа, поэтому все градиенты считаю вручную. Это сильно тормозит, но зато точно понимаешь, как считаются производные и как градиенты идут по слоям.

Процесс напоминает написание своего маленького фреймворка для обучения нейросетей.

Где TypeScript помогает, а где мешает

Плюсы

Минусы

Что дальше и кому это полезно

Если вы senior dev и хотите понять, как устроена архитектура LLM, такая реализация — отличный способ снять “магические” слои. TypeScript здесь — инструмент для глубокого погружения, а не для продакшена.

Для тех, кто хочет добавить vibe coding в AI-разработку, это упражнение помогает прочувствовать, что происходит “под капотом”. Можно экспериментировать с разными вариантами attention, менять размер эмбеддинга, исследовать влияние position encoding.

Однако если цель — быстро сделать что-то, что работает и масштабируется — лучше использовать проверенные Python-библиотеки и GPU. Но если хочется прокачать понимание и получить hands-on experience с минимальными зависимостями — эта дорога стоит того.

Итог

Переписывать LLM на TypeScript — это не про скорость и удобство, а про понимание. Это заставляет разбираться в деталях, ловить нюансы и не упускать из виду, как устроена модель внутри.

Если хотите попробовать что-то подобное — начните с простого трансформера, реализуйте даталоадер и эмбеддинги, затем attention, и постепенно переходите к обучению. Со временем можно добавить agentic workflows или встроить модель в веб-интерфейс для общения.

В моем опыте это не просто упражнение, а способ выйти за рамки шаблонных туториалов и почувствовать модель, а не просто использовать её как черный ящик. Рекомендую всем, кто хочет не просто юзать AI, а понимать его на уровне кода.


Попробуй сам: Cursor — AI-редактор для разработчиков.


Источник: https://book.humanexperiences.io/