# Xenith Трансформер на чистом C++: обучение и генерация текста на CPU, без внешних зависимостей. Всё в одном бинарнике. ``` make # собрать -> bin/xenith ./bin/xenith --help ``` ## Что внутри Архитектура — как у LLaMA: pre-norm, RMSNorm, RoPE, SwiGLU, связанные эмбеддинги (tied weights), каузальное внимание. Обучение — вручную написанный обратный проход + AdamW. Никаких фреймворков. | файл | что делает | |---|---| | `xenith/core/tensor.h` | матрицы, три формы GEMM, RMSNorm, RNG, пул потоков | | `xenith/core/model.cpp` | конфигурация, инициализация, прямой проход, RoPE, Adam | | `xenith/core/backward.cpp` | обратный проход (ручной, без автомиффов) | | `xenith/core/trainer.cpp` | цикл обучения, расписание lr, чекпоинты, валидация | | `xenith/core/generate.cpp` | генерация с KV-кэшем, temperature / top-k / top-p | | `xenith/core/checkpoint.cpp` | формат `.xnh`, токенизатор, сохранение/загрузка | | `xenith/core/gradcheck.cpp` | численная проверка градиентов + бенчмарк | ## Быстрый старт ```bash # 1) собрать модель из своего текста ./bin/xenith new models/my.xnh --corpus data.txt \ --vocab 512 --embd 128 --layers 6 --heads 8 --ctx 256 # 2) обучить ./bin/xenith train models/my.xnh --corpus data.txt \ --out models/my_trained.xnh --steps 5000 --batch 16 --lr 3e-4 # 3) сгенерировать ./bin/xenith gen models/my_trained.xnh --prompt "Привет" --n 200 --temp 0.8 ``` ## Команды ### new — создать модель Читает корпус, строит по нему словарь, инициализирует веса случайно. ``` --corpus PATH текст (обязательно) --out PATH файл модели (по умолчанию model.xnh) --vocab N размер словаря (512) --embd N ширина эмбеддинга (128) --layers N число слоёв (4) --heads N число голов (4). embd должен делиться на heads --ctx N максимальный контекст (128) --ffn N ширина FFN (0 = 4*embd) --rope N база RoPE (10000) --seed N зерно инициализации (1337) --untied отдельная матрица выхода вместо связанной с эмбеддингом ``` Словарь двухуровневый: частые слова берутся целиком, остальное режется на символы. Так словарь остаётся маленьким, а любой текст кодируется без потерь — модель может учить и слова, и буквы. ### train — обучить ``` --corpus PATH текст (обязательно) --out PATH куда сохранить (по умолчанию перезаписывает входной файл) --steps N шагов (1000) --batch N батч (8) --block N длина окна (64) --lr F скорость (3e-4) --wd F weight decay (0.01) --clip F клип нормы градиента (1.0) --warmup N прогрев (100) --seed N зерно (1337) --log-every N как часто печатать (50) --ckpt-every N промежуточный чекпоинт каждые N шагов (0 = нет) --val-every N валидация каждые N шагов (0 = нет) --val-tokens N размер валидации (20000) --threads N потоков (0 = все ядра) --resume продолжить с сохранённого step ``` Прогрев + косинусное затухание до `lr * 0.1`. Промежуточные чекпоинты пишутся как `models/my.xnh.step1500` — можно откатиться, если loss развалился. ### gen — сгенерировать ``` --prompt STR стартовый текст --n N сколько токенов (200) --temp F температура; 0 = жадный выбор (0.8) --top-k N top-k (40), 0 = выкл --top-p F top-p (0.95), 1.0 = выкл --seed N зерно --no-stream не печатать в процессе --batch "a;b;c" несколько промптов через ; --show-tokens показать id токенов ``` ### info / gradcheck / bench ```bash ./bin/xenith info models/my.xnh # конфигурация и словарь ./bin/xenith gradcheck # градиенты против численных ./bin/xenith bench models/my.xnh # скорость ``` `gradcheck` стоит запускать после любой правки в `model.cpp` / `backward.cpp` — он ловит ошибку в обратном проходе за секунды. ## Формат модели Один файл `.xnh` содержит и словарь, и веса, и состояние оптимизатора. При загрузке проверяется каждая размерность тензора, так что модель, собранная с другими настройками, отвергается с понятной ошибкой, а не молча портит вывод. Совместимость с `models/test_model.bfr` (старый формат) **не** поддерживается: там была другая раскладка весов. ## Что учесть - **Скорость.** ~7600 ток/с на 4 ядрах для 4 слоёв × 64 измерения. С ростом модели скорость падает квадратично по `embd` и линейно по слоям. Замеряйте своим `bench`. - **Форма входа.** `text`, `code`, `html` — чем однороднее корпус, тем меньше словарь и тем быстрее обучение. - **Количество данных.** Модель на 0.25 млн параметров хочет хотя бы несколько мегабайт текста, иначе будет только запоминать. - **Токенизатор** — слова + символы, не BPE. Для морфологически богатых языков это проще, но экономнее BPE приходится платить длиной последовательности.