anekdot-gpt — анекдоты, написанные трансформером с нуля
Учебный GPT (архитектура Karpathy «Let's build GPT» / nanoGPT-стиль), обученный с нуля — без предобучения — на ~109 000 анекдотах anekdot.ru.
Показатели
| Параметры | 15.9M |
| Val loss (next-token CE) | 3.308 (лучший шаг 38 750 из 40 000) |
| Perplexity | ≈ 27 |
| Словарь | 4353 (byte-level BPE, свой токенизатор, <|anek|> = 256) |
| Контекст | 256 токенов |
Конфигурация
{
"vocab_size": 4353, "n_embd": 384, "num_heads": 6,
"block_size": 256, "n_layer": 8, "dropout": 0.2
}
Конфиг вшит в чекпоинт (model_config); optimizer_state вырезан при
упаковке (191 → 64 МБ).
Использование
Модель не совместима с transformers напрямую — это собственный класс
GPTLanguageModelV2 (flash SDPA, GELU, bias-free, weight tying,
nanoGPT-инициализация). Загрузка:
import torch
from model import GPTLanguageModelV2 # класс из репозитория russian-hokku
from tokenizer import Tokenizer # там же
ckpt = torch.load("anekdot_v2.pt", map_location="cpu", weights_only=False)
model = GPTLanguageModelV2(**ckpt["model_config"]).eval()
model.load_state_dict(ckpt["model_state"])
tok = Tokenizer()
tok.load("anekdot_bpe.json")
sep = tok.special_tokens["<|anek|>"]
idx = torch.tensor([[sep]]) # или tok.encode("— Доктор, я жить буду?")
out = model.generate(idx, 250, temperature=0.9, top_k=40, stop_token_id=sep)
row = out[0].tolist()
end = row.index(sep, 1)
print(tok.decode(row[1:end]).strip())
Данные и фильтры
Парет-дамп anekdot.ru: ≥ 30 голосов, ≥ 75 % одобрения читателей,
20–1500 символов, нормализация и дедупликация; сплит по-шуточно 90/10.
Разделитель шуток — маркер <|anek|>.
Ограничения (честно)
- 15.9M параметров, обученных с нуля на ~6M уникальных токенов: локально связный, глобально бормочущий текст. Это плакатное «правдоподобие анекдота», а не юмор уровня человека.
- Температура ≤ 0.8 заметно связнее; жадный сэмплинг зацикливается.
- Возможны грубости и абсурд — корпус таков.
Лицензия apache-2.0 относится к коду; модель учебная, данные происходят из публичного архива anekdot.ru.