File size: 18,888 Bytes
a3d8a06 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | ---
language:
- ru
tags:
- wiki
- ru_wiki
- wikipedia
---
# SunboxDevLLM - Русскоязычная языковая модель
Модель доступна для использования: **[chat.sunbox.dev](https://chat.sunbox.dev)**
Репозиторий: **[GitHub](https://github.com/it-efrem/llm)**
Decoder-only Transformer языковая модель, построенная полностью с нуля — BPE-токенизатор, архитектура модели, цикл обучения и генерация — на Python + PyTorch (~2500 строк кода).
## Архитектура
| Компонент | Решение | Обоснование |
| ------------ | ----------------------------------- | --------------------------------------------------- |
| Нормализация | **RMSNorm** (pre-norm) | Стандарт LLaMA/Mistral, ~10-15% быстрее LayerNorm |
| Позиции | **RoPE** (split + cat, без complex) | Лучшая экстраполяция длины, совместимость с MPS/AMP |
| FFN | **SwiGLU** (3 проекции) | Выше качество при том же вычислительном бюджете |
| Attention | **F.scaled_dot_product_attention** | Flash Attention на CUDA, math fallback на MPS |
| Генерация | **KV cache** (prefill + decode) | O(S) на новый токен вместо O(S²) |
| Веса | **Weight tying** (embed ↔ lm_head) | Экономия ~24.6M параметров |
| AMP | autocast + GradScaler | CUDA и MPS (PyTorch 2.2+) |
## Конфигурации модели
| Вариант | d_model | heads | layers | ff_dim | Параметры |
| -------- | ------- | ----- | ------ | ------ | --------- |
| **Full** | 768 | 12 | 12 | 2048 | ~110M |
## Установка
```bash
# Клонировать репозиторий и перейти в корень проекта (имя папки может отличаться)
cd llm
# Создать виртуальное окружение
python3 -m venv .venv
source .venv/bin/activate
# Установить зависимости
pip install -r requirements.txt
```
Чекпоинты весов (`models/*.pt`) не коммитятся (см. `.gitignore`). Для примеров ниже положите файлы `base_step_15000.pt` и `chat_step_500.pt` в каталог `models/` или передайте свой путь в `--checkpoint`. Для BPE нужен `data/vocab.json` из того же обучения (или `--vocab`), если путь из чекпоинта на этой машине не существует.
### Зависимости
| Пакет | Обязательный | Назначение |
| -------------------- | :----------: | --------------------------------------------------- |
| `torch>=2.2.0` | да | PyTorch (CUDA / MPS / CPU) |
| `numpy>=1.20` | да | Memmap для pretokenized-корпусов |
| `regex>=2023.0` | да | Unicode-aware pre-tokenization (GPT-2 паттерн) |
| `transformers>=4.20` | нет | Адаптер HuggingFace-токенизатора (`--tokenizer`) |
| `tokenizers>=0.15` | нет | Быстрое BPE-обучение через Rust (`train_bpe_hf.py`) |
| `gensim>=4.0` | нет | Парсинг дампа Wikipedia (`data/wiki_download.py`) |
## Быстрый старт
### Полный пайплайн одной командой
```bash
bash run_train.sh
```
Скрипт выполнит: обучение BPE-токенизатора → pre-training на Wikipedia → fine-tuning на чат-корпусе.
### Пошаговый запуск
#### 1. Обучение BPE-токенизатора
```bash
# Собственная реализация (чистый Python, ~20-50x быстрее наивного алгоритма)
python train_bpe.py --corpus data/wiki_text/corpus.txt --vocab-size 32000
# Или через HuggingFace tokenizers (Rust, ~100-1000x быстрее на больших корпусах)
python train_bpe_hf.py --corpus data/wiki_text/corpus.txt --vocab-size 32000
```
#### 2. Pretokenization корпуса (для больших датасетов)
Для корпусов >1 GB рекомендуется pretokenization — после неё обучение стартует мгновенно:
```bash
python pretokenize_corpus.py \
--corpus data/wiki_text/corpus_15gb.txt \
--output data/corpus_15gb.bin \
--workers 8
```
#### 3. Pre-training
```bash
# На текстовом корпусе (токенизация при старте)
python train.py --corpus data/wiki_text/corpus.txt \
--batch-size 32 --max-steps 100000 --save-every 5000
# На pretokenized-корпусе (мгновенный старт)
python train.py --pretokenized data/corpus_15gb.bin \
--batch-size 64 --grad-accum 4 --max-steps 30000
# С ранней остановкой по perplexity
python train.py --pretokenized data/corpus_15gb.bin \
--stop-at-perplexity 10.0
```
#### 4. Fine-tuning на чат-корпусе
```bash
# --init-from загружает только веса модели (свежий optimizer/scheduler)
python train.py --corpus data/wiki_text/corpus_chat.txt --chat \
--init-from models/base_step_15000.pt \
--lr 1e-4 --batch-size 16 --max-steps 2000 --save-every 250
# --resume загружает полное состояние (продолжение прерванного обучения)
python train.py --corpus data/wiki_text/corpus_chat.txt --chat \
--resume models/chat_step_500.pt \
--max-steps 3000
```
## Генерация текста
### Рекомендованные параметры
Параметры подобраны тестированием на `base_step_15000` (pre-trained) и `chat_step_500` (chat fine-tuned)
на 8 различных промптах по 9 конфигурациям каждый. Значения по умолчанию в коде (`GenerateConfig`)
установлены как универсальный компромисс: `temperature=0.5`, `repetition_penalty=1.15`.
| Режим | temperature | repetition_penalty | top_p | Комментарий |
| --------------------------- | :---------: | :----------------: | :---: | -------------------------------------------- |
| **Текст** (pre-trained) | 0.3 | 1.2 | — | Максимально связные дополнения, минимум шума |
| **Чат** (fine-tuned) | 0.5 | 1.1 | — | Лучший баланс точности и разнообразия |
| **Универсальный** (default) | 0.5 | 1.15 | — | Разумный компромисс для обоих режимов |
| Креативный | 0.7 | 1.15 | 0.9 | Больше разнообразия, выше риск галлюцинаций |
Примеры запуска с оптимальными параметрами:
```bash
# Дополнение текста (pre-trained checkpoint)
python generate.py --checkpoint models/base_step_15000.pt \
--prompt "Москва — столица" --temperature 0.3 --repetition-penalty 1.2 --stream
# Чат (fine-tuned checkpoint) — оптимальные параметры
python generate.py --checkpoint models/chat_step_500.pt \
--chat --temperature 0.5 --repetition-penalty 1.1
# Без явных параметров — используются defaults из GenerateConfig
python generate.py --checkpoint models/chat_step_500.pt \
--prompt "Искусственный интеллект — это" --stream
```
### Одиночный prompt
```bash
# Streaming (токены выводятся по одному)
python generate.py --checkpoint models/chat_step_500.pt \
--prompt "Москва — столица" --stream
# Полный ответ сразу (без --stream)
python generate.py --checkpoint models/chat_step_500.pt \
--prompt "Москва — столица" --max-tokens 100
```
### Prompt из файла
```bash
python generate.py --checkpoint models/chat_step_500.pt \
--prompt-file my_prompt.txt --stream
```
### Интерактивный чат
```bash
# Базовый режим (каждый вопрос независимый)
python generate.py --checkpoint models/chat_step_500.pt \
--chat --temperature 0.5 --repetition-penalty 1.1
# С сохранением контекста (sliding window по границам реплик)
python generate.py --checkpoint models/chat_step_500.pt \
--chat --keep-history
```
Пример сессии:
```
=== Chat Mode (type 'quit' to exit) ===
You: Что такое машинное обучение?
Assistant: Машинное обучение — раздел ИИ: алгоритмы, которые улучшают своё поведение
на основе данных без явного программирования каждого шага.
You: Что такое фотосинтез?
Assistant: Фотосинтез — процесс образования органических веществ из CO₂ и воды
на свету при участии хлорофилла. Выделяется кислород.
You: quit
```
### Параметры генерации
| Параметр | По умолчанию | Описание |
| ---------------------- | :----------: | --------------------------------------------------------- |
| `--temperature` | 0.5 | Температура сэмплирования (ниже = детерминированнее) |
| `--top-k` | — | Ограничение по top-k токенам |
| `--top-p` | — | Nucleus sampling (top-p) |
| `--max-tokens` | 256 | Максимум новых токенов |
| `--repetition-penalty` | 1.15 | Штраф за повторения (1.0 = выкл, рекомендуется 1.1-1.2) |
| `--stream` | off | Потоковый вывод токенов |
| `--chat` | off | Интерактивный чат-режим |
| `--keep-history` | off | Сохранение контекста диалога между репликами |
| `--vocab` | auto | Путь к vocab.json (если чекпоинт обучен на другой машине) |
## Параметры обучения
| Параметр CLI | Конфиг | По умолчанию | Описание |
| ---------------------- | -------------------- | :----------: | ---------------------------------- |
| `--max-steps` | `max_steps` | 23000 | Максимум шагов |
| `--batch-size` | `batch_size` | 32 | Размер батча |
| `--lr` | `learning_rate` | 6e-4 | Learning rate |
| `--grad-accum` | `grad_accum_steps` | 4 | Gradient accumulation |
| `--warmup` | `warmup_steps` | 2000 | Шаги warmup |
| `--save-every` | `save_every_steps` | 2000 | Частота сохранения |
| `--seq-len` | `max_seq_len` | 1024 | Длина контекста |
| `--no-amp` | `use_amp` | True | Отключить AMP |
| `--stop-at-loss` | `stop_at_loss` | — | Ранняя остановка по loss |
| `--stop-at-perplexity` | `stop_at_perplexity` | — | Ранняя остановка по val perplexity |
LR scheduler: linear warmup → cosine annealing (single cycle).
## Подготовка данных
### Скачивание Wikipedia
```bash
python data/wiki_download.py --max-mb 500 --corpus-file data/wiki_text/corpus.txt
```
### Очистка корпуса
Удаляет блоки EasyTimeline, галереи, лишние пустые строки:
```bash
python clean_corpus.py --input data/wiki_text/corpus.txt \
--output data/wiki_text/corpus_cleaned.txt --remove-gallery
# Только статистика без записи
python clean_corpus.py --dry-run
```
### Обрезка по размеру
```bash
python trim_corpus.py --input data/wiki_text/corpus_cleaned.txt \
--output data/wiki_text/corpus_500mb.txt --mb 500
```
### Генерация чат-корпуса
```bash
python data/wiki_text/generate_chat_corpus.py
```
Создаёт `corpus_chat.txt` из пула Q&A-пар на основе seed-данных (столицы, события, персоны и т.д.).
## Структура проекта
```
llm/
├── config.py # ModelConfig, TrainConfig, GenerateConfig, PathsConfig
├── train.py # CLI обучения
├── generate.py # CLI генерации и интерактивного чата
├── train_bpe.py # Обучение BPE (собственная реализация)
├── train_bpe_hf.py # Обучение BPE (HuggingFace Rust backend)
├── pretokenize_corpus.py # Pretokenization корпуса в .bin (multiprocessing)
├── clean_corpus.py # Очистка wiki-корпуса
├── trim_corpus.py # Обрезка корпуса по размеру
├── run_train.sh # Полный пайплайн одной командой
│
├── model/
│ ├── transformer.py # TransformerLM (блоки + lm_head + weight tying)
│ ├── block.py # DecoderBlock (pre-norm) + SwiGLUFFN
│ ├── attention.py # MultiHeadAttention (RoPE + SDPA + KV cache)
│ ├── rope.py # Rotary positional embeddings
│ ├── norm.py # RMSNorm
│ └── kv_cache.py # KV cache для генерации
│
├── tokenizer/
│ ├── vocab.py # Словарь (tuple pair keys, без overflow)
│ ├── bpe.py # BPETokenizer (pre-tokenize + merge ranks + LRU)
│ ├── bpe_train_fast.py # Быстрое обучение BPE (doubly-linked list + heap)
│ ├── pretokenizer.py # GPT-2 regex word splitter
│ └── hf_adapter.py # Адаптер HuggingFace-токенизатора
│
├── data/
│ ├── wiki_dataset.py # WikiChunkDataset + PretokenizedDataset (memmap)
│ ├── chat_dataset.py # ChatChunkDataset (маскирование loss на user-части)
│ ├── wiki_download.py # Скачивание/парсинг дампа Wikipedia
│ └── wiki_text/ # Корпуса и генераторы чат-данных
│
├── training/
│ ├── trainer.py # AMP, gradient accumulation, валидация, логирование
│ ├── checkpoint.py # Сохранение/загрузка полного состояния
│ └── scheduler.py # Warmup + CosineAnnealingWarmRestarts
│
├── models/ # *.pt в .gitignore — веса кладутся локально
│ ├── base_step_15000.pt # Pre-trained checkpoint (Wikipedia 15GB)
│ └── chat_step_500.pt # Fine-tuned checkpoint (chat corpus)
│
├── inference/
│ └── generator.py # KV cache prefill/decode, streaming, sampling
│
└── utils/
├── device.py # Детекция CUDA/MPS/CPU и платформенные настройки
└── log_setup.py # Конфигурация логирования
```
## Совместимость
| Платформа | Поддержка | Особенности |
| ----------------------------- | :-------: | ------------------------------------------------------ |
| **CUDA GPU** (A100, V100, T4) | полная | Flash Attention, GradScaler, pin_memory, num_workers=4 |
| **Apple MPS** (M1/M2/M3/M4) | полная | SDPA math fallback, unified memory, num_workers=0 |
| **CPU** | базовая | bfloat16 autocast где поддерживается |
## Отличия от оригинального llm
1. **CUDA-совместимость**: единый `get_device()` с приоритетом CUDA > MPS > CPU
2. **BPE без overflow**: tuple-ключи `(id_a, id_b)` вместо bit-shift (vocab > 65536)
3. **Быстрый BPE encode**: pre-tokenization (GPT-2 regex) + merge rankings + LRU cache
4. **RoPE вместо sinusoidal**: лучшая экстраполяция, без отдельного embedding-слоя
5. **RMSNorm вместо LayerNorm**: быстрее, соответствует LLaMA/Mistral
6. **SwiGLU вместо GELU FFN**: выше качество при том же compute
7. **KV cache при генерации**: O(S) на токен вместо O(S²) полного re-forward
8. **Weight tying**: embed и lm_head разделяют веса, экономия ~24.6M параметров
9. **Полное состояние чекпоинтов**: модель + optimizer + scheduler + scaler + step
10. **Grad norm до clipping**: точный мониторинг здоровья градиентов
11. **Pre-tokenized datasets**: O(1) `__getitem__`, корректное маскирование loss для чата
12. **GPT-2 инициализация**: `N(0, 0.02)` + residual scaling `0.02 / sqrt(2N)` |