mini-tron-50 (SFT) (Russian and English)
English
Translator's note: This is a translation from Russian. Model outputs in the examples below are rendered in English, but originally generated in Russian.
A 50M-parameter GPT-2-style language model trained from scratch on a Russian SFT corpus as a chatbot. Illustrates what can be squeezed out of a model this size without pretraining on raw text.
TL;DR
- Architecture: 10 layer × 8 head × 512 emb (GPT-2 style), 47.85M params
- Trained from scratch: SFT on ~1 GB of chat data (1.7M dialogs), 1 epoch
- Hardware: 1× RTX 3050 Laptop (4 GB VRAM), 13 hours
- Tokenizer: SentencePiece BPE, 32k vocab, custom-trained on the corpus
- Format: ChatML with special tokens
<|system|>,<|user|>,<|assistant|>,<|endoftext|> - Loss masking: only on assistant tokens (standard SFT trick)
- Status: SFT phase successful; KTO phase attempted and failed (see below)
The model has not reached its ceiling — this is one of its key features. It was trained for exactly one epoch over the corpus (Chinchilla-optimal for 50M), and the loss curves show that validation loss had not yet plateaued. Further training or continued training on new data will likely yield noticeable improvement.
Architecture
GPTConfig(
n_layer = 10,
n_head = 8,
n_embd = 512,
block_size = 1024,
vocab_size = 32000,
bias = False,
dropout = 0.1,
)
Standard GPT-2 without bias. Tied embeddings (transformer.wte shared with lm_head). Attention uses F.scaled_dot_product_attention (flash-attn under the hood on Ampere+).
Capabilities
| Capability | Quality |
|---|---|
| Russian grammar | ✅ flawless (cases, agreement, syntax) |
| Chat format (responds as assistant) | ✅ stable |
| Markdown structure (lists, bold, headers) | ✅ imitates GPT-4 style |
| Self-identification ("I am an AI assistant") | ✅ says the right words |
| EOS completion | ✅ usually stops on its own |
| Local coherence (1–2 sentences) | ⚠️ somewhat meaningful in places |
| Response strictly on prompt topic | ⚠️ hears trigger words, not substance |
| Facts and precise knowledge | ❌ hallucinations |
| Arithmetic | ❌ imitates calculation without performing it |
| Multi-step reasoning | ❌ |
| Code (syntax + semantics) | ❌ form is correct, does not work |
Known failure modes
Canned responses to simple questions:
you> Do you like cats or dogs? bot> Hello! I'm glad I could help you today. If you have any questions, feel free to contact us. Good luck!A memorized template tail of ChatGPT-style responses.
Tutorial walls on any open-ended prompt:
you> Tell me about yourself bot> ### Step 1: Define the task ### Step 2: ... ### ConclusionToken loops on out-of-distribution prompts:
you> Which is greater: 17 or 71? bot> To calculate... + 112 - 112 + 112 + 112 - 112 + 112 ... [50+ repeats]Semantic mush in a fully literate wrapper:
you> What is 7 times 8? bot> 5! = (5 × 8) / 8 = 120. Now divide 120 by 8... So, there are 140 ways to choose 7 times 8 in total.The model imitates the genre of school arithmetic without performing the operation itself.
These failure modes are not cured by SFT, but by preference learning + increasing model size.
Training data
Source: big-russian-dataset (HuggingFace) — Russian SFT corpus.
| Split | Dialogs | After filtering |
|---|---|---|
| train | 1.71M | 1,709,621 (99.9%) |
| val | 18.5k | 10,396 (56%) |
Filter: overall_score ≥ 6 AND safety ≥ 8 AND pii_leak = 0.
In train, the dataset authors had already cleaned out the garbage — there are no records with score < 6, so the filter passes almost everything. In val, the score spread of 1–10 was left intentionally for evaluation on hard examples.
Volume in tokens: ~1.04 GB of tokens in train.bin, of which ~603M tokens are under loss (assistant + EOT, 57.7%).
Training parameters
# AdamW
learning_rate = 3e-4 # cosine decay → min_lr=3e-5
weight_decay = 0.1
beta1, beta2 = 0.9, 0.95
grad_clip = 1.0
# Schedule
warmup_iters = 200
max_iters = 16000 # ~1 epoch
lr_decay_iters = 16000
# Batch
batch_size = 2
gradient_accumulation_steps = 32 # effective batch = 64 sequences
block_size = 1024
# tokens per iter = 65,536
# System
dtype = 'bfloat16'
compile = False
Learning curve
iter 0 loss 10.49 (≈ ln(32000), random initialization)
iter 500 loss ~5 (warmup complete, LR at peak)
iter 5500 loss ~2.4 (first saved checkpoint)
iter 11500 loss ~1.7 (third)
iter 14500 loss ~1.5 (best val_loss ~ 1.8)
iter 16000 loss ~1.45 (max_iters reached)
Train-val gap at the end ~1.7 nats — a healthy value for SFT on a small model.
How to use
The model is distributed in nanoGPT (Karpathy) format. Not directly compatible with transformers.AutoModelForCausalLM without conversion (weight names differ from GPT-2 standard, specifically no bias).
Option 1 — via nanoGPT repository (Python)
git clone https://github.com/karpathy/nanoGPT.git
cd nanoGPT
# copy ckpt.pt, tokenizer.model, meta.pkl here
python chat.py --out_dir=<path-to-checkpoint-dir>
The chat.py from this release provides an interactive REPL with commands to change temperature, top_p, top_k, and repetition_penalty on the fly.
Option 2 — via pre-built Go single-binary
This release includes a pre-built nanogpt-chat.exe (~97 MB) — a single binary with embedded weights, tokenizer, and config. Runs without Python and without GPU. Performance: ~36 tok/sec on a 4-core CPU.
./nanogpt-chat.exe
REPL commands:
/help help
/show current parameters
/reset reset dialog history
/system <text> change system prompt
/temp <float> temperature (>0)
/top_p <float> nucleus sampling
/top_k <int> top-k
/rep <float> repetition penalty
/preset <name> creative | balanced | precise
/quit exit
All commands have single-letter aliases (/h, /s, /r, /q, /t, /p, /k, ...).
Option 3 — conversion to HuggingFace transformers
Not provided in this release. Requires renaming weights to GPT-2 conventions, adding null biases, and building GPT2LMHeadModel with a custom tokenizer (SentencePiece, not GPT-2 BPE). If someone writes a converter — PRs welcome.
Examples
you> Hello
bot> Hello! I'm ready to help you achieve your goals. Have a great day!
you> Explain in simple words what a black hole is.
bot> First, let's break this down step by step.
### Step 1: Studying a black hole
- This means that a black hole is part of a black hole.
- It can have different sizes...
[grammatically correct, but content is nonsense — this is the 50M ceiling on facts]
you> Write bubble sort in Python.
bot> Here is an example of bubble sort in Python:
```python
def merge_sort(arr):
"""Performs bubble sort in sorted order."""
sorted_array = {}
for i in range(len(arr)):
if arr[i] < sorted_array[i - 1]:
return merge_sort(arr[i], reverse=True)
return sorted_array
[function name merge_sort instead of bubble_sort, dictionary instead of two loops — form is correct, implementation is meaningless]
Good prompts for this model are short, conversational, and do not require precise facts:
```text
you> Hi, how are you?
you> My name is Alex. What's yours?
you> Name three fruits.
you> Tell me a short fairy tale.
Limitations
- Knowledge: the model does not contain reliable facts. Do not ask about dates, names, numbers, geography, biology, or medicine. Any response is an imitation of the reference genre, not real information.
- Reasoning: multi-step logic is unavailable. Arithmetic is imitated without execution. Code is syntactically plausible but does not work.
- Length: the model was trained with
block_size=1024. Long dialogs (>800 tokens in history) are truncated from the beginning — the model "forgets" earlier utterances. - Languages: Russian only. On English prompts it will attempt to respond, but quality is worse.
- Safety: the model was trained only on the filtered part of the dataset (
safety ≥ 8), but has no special alignment — behavior on overtly harmful prompts is not guaranteed.
What didn't work
After SFT, an attempt was made at preference learning via KTO to suppress known failure modes. Both attempts (β=0.1 and β=0.03) produced a completely destroyed model — coherent responses turned into semantic garbage. Detailed root-cause analysis is in 04_kto_attempts.md of the accompanying report.
In short: a combination of (a) a bug in the loss implementation (missing clamp(z_ref, 0)) and (b) asymmetric difficulty between chosen data and self-generated rejected. After fixing the bug, degradation still remained, just slower.
A curious side-effect: after KTO, the model drifted not just into noise, but into an "aphoristic-philosophical" register — a recognizable stylistic tail of the distribution that KTO did not suppress.
Only the SFT checkpoint is published in this release; KTO weights are not included.
Fine-tuning opportunities
The model is not at its ceiling. Several directions for continuation:
- Continued SFT on an expanded corpus. Especially — add a corpus with factual knowledge (e.g., excerpts from Wikipedia) and code. Every ~30% of new data is worth giving ~1–2 epochs.
- Pre-training on raw text (if you want to push below the 50M quality floor). 1–5 GB of Russian OSCAR/CulturaX before SFT may give a significant boost.
- Distillation from an external large model. The current dataset is already a distill, but generating new responses from Claude / GPT-4o-mini / Yandex YandexGPT on the same prompts will give style diversity.
- Preference learning (DPO/KTO) with external rejected (not self-generated). For example, low-score responses from the val split of the same dataset.
- Scale up to 100–200M params with the same hyperparameters and the same corpus. Strongly non-linear quality boost.
Release files
| File | Size | Description |
|---|---|---|
ckpt.pt |
553 MB | nanoGPT checkpoint (model + optimizer state + config) |
tokenizer.model |
930 KB | SentencePiece tokenizer (BPE 32k) |
meta.pkl |
<1 KB | special token IDs + vocab_size |
nanogpt-chat.exe (opt.) |
97 MB | Go single-binary with embedded model |
model_card.md |
this file |
If you only want inference — tokenizer.model + ckpt.pt is sufficient.
Citation / acknowledgments
@misc{mini-tron-50,
title = {mini-tron-50: 50M Russian chat model trained from scratch},
author = {Impi},
year = {2026},
note = {Educational baseline; nanoGPT architecture}
}
Resources used:
- nanoGPT by Andrej Karpathy — foundation of architecture and training loop
big-russian-dataset— training corpus
License
Apache 2.0 — for code and weights of this model.
Russian
50M-параметровая GPT-2-style языковая модель, обученная с нуля на русском SFT-корпусе как чат-бот. Иллюстрирует что можно выжать из модели такого размера без претрейна на сыром тексте.
TL;DR
- Architecture: 10 layer × 8 head × 512 emb (GPT-2 style), 47.85M params
- Trained from scratch: SFT на ~1 ГБ chat-данных (1.7M диалогов), 1 эпоха
- Hardware: 1× RTX 3050 Laptop (4 ГБ VRAM), 13 часов
- Tokenizer: SentencePiece BPE, 32k vocab, custom-trained на корпусе
- Format: ChatML с спецтокенами
<|system|>,<|user|>,<|assistant|>,<|endoftext|> - Loss masking: только на assistant-токенах (стандартный SFT-trick)
- Status: SFT-фаза успешна; KTO-фаза проведена и провалилась (см. ниже)
Модель не достигла своего потолка — это одна из её особенностей. Она тренировалась ровно одну эпоху по корпусу (Chinchilla-оптимум для 50M), и контурные кривые показывают что улучшения val_loss ещё не выходили на плато. Дальнейшая тренировка или продолжение на новых данных вероятно даст ощутимое улучшение.
Архитектура
GPTConfig(
n_layer = 10,
n_head = 8,
n_embd = 512,
block_size = 1024,
vocab_size = 32000,
bias = False,
dropout = 0.1,
)
Стандартный GPT-2 без bias. Tied embeddings (transformer.wte shared с lm_head).
Attention использует F.scaled_dot_product_attention (flash-attn под капотом
на Ampere+).
Что модель умеет
| Способность | Качество |
|---|---|
| Грамматика русского | ✅ безупречно (падежи, согласования, синтаксис) |
| Chat-формат (отвечает в роли ассистента) | ✅ устойчиво |
| Markdown-структура (списки, bold, заголовки) | ✅ имитирует GPT-4-стиль |
| Самоидентификация ("я ИИ-ассистент") | ✅ говорит правильные слова |
| Завершение по EOS | ✅ обычно сама останавливается |
| Локальная связность 1-2 предложения | ⚠️ местами осмысленно |
| Ответ строго по теме промпта | ⚠️ слышит триггерные слова, не суть |
| Факты и точные знания | ❌ галлюцинации |
| Арифметика | ❌ имитирует подсчёт без него |
| Многошаговый reasoning | ❌ |
| Code (синтаксис + семантика) | ❌ форма правильная, не работает |
Известные failure modes
Canned-ответы на простые вопросы:
you> Любишь котов или собак? bot> Привет! Я рад, что смог помочь вам сегодня. Если у вас есть вопросы, не стесняйтесь обращаться к нам. Удачи!Заученный шаблонный хвост ChatGPT-style ответов.
Tutorial-простыни на любой открытый промпт:
you> Расскажи о себе bot> ### Шаг 1: Определение задачи ### Шаг 2: ... ### ЗаключениеToken-loops на промптах вне распределения:
you> Что больше: 17 или 71? bot> Чтобы посчитать... + 112 - 112 + 112 + 112 - 112 + 112 ... [50+ повторов]Семантическая каша в полностью грамотной обёртке:
you> Сколько будет 7 умножить на 8? bot> 5! = (5 × 8) / 8 = 120. Теперь разделим 120 на 8... Итак, всего будет 140 способов выбрать 7 умножить на 8.Модель имитирует жанр школьной арифметики, не выполняя саму операцию.
Эти failure modes лечатся не SFT, а preference learning'ом + увеличением размера модели.
Тренировочные данные
Источник: big-russian-dataset (HuggingFace) — русскоязычный SFT-корпус.
| Сплит | Диалогов | После фильтра |
|---|---|---|
| train | 1.71M | 1,709,621 (99.9%) |
| val | 18.5k | 10,396 (56%) |
Фильтр: overall_score ≥ 6 AND safety ≥ 8 AND pii_leak = 0.
В train авторы датасета сами уже почистили мусор — там нет записей со score < 6, поэтому фильтр пропускает почти всё. В val разброс score 1-10 оставлен специально для оценки на трудных примерах.
Объём в токенах: ~1.04 ГБ токенов в train.bin, из них ~603M токенов под loss (assistant + EOT, 57.7%).
Тренировочные параметры
# AdamW
learning_rate = 3e-4 # cosine decay → min_lr=3e-5
weight_decay = 0.1
beta1, beta2 = 0.9, 0.95
grad_clip = 1.0
# Schedule
warmup_iters = 200
max_iters = 16000 # ~1 эпоха
lr_decay_iters = 16000
# Batch
batch_size = 2
gradient_accumulation_steps = 32 # effective batch = 64 sequences
block_size = 1024
# tokens per iter = 65,536
# System
dtype = 'bfloat16'
compile = False
Кривая обучения
iter 0 loss 10.49 (≈ ln(32000), стартовая случайная инициализация)
iter 500 loss ~5 (warmup закончен, LR на peak)
iter 5500 loss ~2.4 (первый saved checkpoint)
iter 11500 loss ~1.7 (третий)
iter 14500 loss ~1.5 (best val_loss ~ 1.8)
iter 16000 loss ~1.45 (max_iters достигнут)
train-val gap к концу ~1.7 nats — здоровое значение для SFT на small model.
Как использовать
Модель распространяется в формате nanoGPT (Karpathy). Не совместима напрямую
с transformers.AutoModelForCausalLM без конвертации (имена весов отличаются от
GPT-2-стандарта, в частности нет bias).
Вариант 1 — через nanoGPT-репозиторий (Python)
git clone https://github.com/karpathy/nanoGPT.git
cd nanoGPT
# скопировать ckpt.pt, tokenizer.model, meta.pkl сюда
python chat.py --out_dir=<path-to-checkpoint-dir>
chat.py из этого релиза предоставляет интерактивный REPL с командами для
изменения temperature, top_p, top_k, repetition_penalty на лету.
Вариант 2 — через готовый Go single-binary
В этом релизе есть pre-built nanogpt-chat.exe (~97 МБ) — single-binary с
встроенными весами, токенайзером, конфигом. Запускается без Python и без
GPU. Performance: ~36 ток/сек на 4-ядерном CPU.
./nanogpt-chat.exe
Команды REPL:
/help справка
/show текущие параметры
/reset сбросить историю диалога
/system <текст> сменить system-prompt
/temp <float> temperature (>0)
/top_p <float> nucleus sampling
/top_k <int> top-k
/rep <float> repetition_penalty
/preset <name> creative | balanced | precise
/quit выход
Все команды имеют однобуквенные алиасы (/h, /s, /r, /q, /t, /p, /k, ...).
Вариант 3 — конвертация в HuggingFace transformers
Не предусмотрена в релизе. Требует переименования весов под GPT-2 conventions,
добавления nullевых bias, и сборки GPT2LMHeadModel с custom tokenizer
(SentencePiece, не GPT-2 BPE). Если кто-то напишет конвертер — буду рад PR.
Примеры
you> Привет
bot> Привет! Я готов помочь вам в достижении ваших целей. Хорошего дня!
you> Объясни простыми словами, что такое черная дыра.
bot> Для начала, давайте разберемся с этим вопросом шаг за шагом.
### Шаг 1: Изучение черной дыры
- Это означает, что черная дыра является частью черной дыры.
- Она может иметь разные размеры...
[грамотно, но содержательно бредово -- это потолок 50M на фактах]
you> Напиши сортировку пузырьком на Python.
bot> Вот пример сортировки пузырьком на Python:
```python
def merge_sort(arr):
"""Выполняет сортировку пузырьком в отсортированном порядке."""
sorted_array = {}
for i in range(len(arr)):
if arr[i] < sorted_array[i - 1]:
return merge_sort(arr[i], reverse=True)
return sorted_array
[имя функции merge_sort вместо bubble_sort, словарь вместо двух циклов -- форма правильная, реализация бессмысленная]
Хорошие промпты для этой модели — короткие, диалоговые, не требующие точных
фактов:
```text
you> Привет, как дела?
you> Меня зовут Лёша. А тебя?
you> Назови три фрукта.
you> Расскажи короткую сказку.
Ограничения
- Знания: модель не содержит достоверных фактов. Не стоит спрашивать о датах, именах, числах, географии, биологии, медицине. Любой ответ — имитация жанра справки, а не реальная информация.
- Reasoning: многошаговая логика недоступна. Арифметика — имитируется без выполнения. Code — синтаксически правдоподобен, но не работает.
- Длина: модель тренировалась с
block_size=1024. Длинные диалоги (>800 токенов в истории) обрезаются с начала — модель «забывает» ранние реплики. - Языки: только русский. На английских промптах попытается отвечать, но качество хуже.
- Безопасность: модель тренировалась только на отфильтрованной части
датасета (
safety ≥ 8), но не имеет специального alignment — на откровенно вредных промптах поведение не гарантировано.
Что не получилось
После SFT была попытка preference-learning'а через KTO для подавления
известных failure modes. Обе попытки (β=0.1 и β=0.03) дали полностью
разрушенную модель — связные ответы превратились в семантический мусор.
Подробный root-cause анализ — в 04_kto_attempts.md сопровождающего отчёта.
Кратко: комбинация (a) бага в реализации loss (отсутствие clamp(z_ref, 0))
и (b) asymmetric difficulty между chosen-данными и self-generated rejected.
После исправления бага деградация всё равно осталась, просто медленнее.
Любопытный side-effect: после KTO модель уходила не просто в шум, а в «афористически-философский» регистр — узнаваемый стилистический хвост распределения, который KTO не давила.
В этом релизе публикуется только SFT-чекпоинт, KTO-веса не включены.
Возможности дообучения
Модель не на потолке. Несколько направлений для продолжения:
- Continued SFT на расширенном корпусе. Особенно — добавить корпус с фактическими знаниями (например, выжимки из Википедии) и кодом. Каждые ~30% новых данных стоит давать ~1-2 эпохи.
- Pre-training на сыром тексте (если хочется уйти ниже 50M-потолка качества). 1-5 ГБ русского OSCAR/CulturaX перед SFT может дать значительный буст.
- Distillation от внешней большой модели. Текущий датасет уже дистилл, но генерация новых ответов от Claude / GPT-4o-mini / Yandex YandexGPT на тех же промптах даст разнообразие стилей.
- Preference learning (DPO/KTO) с внешними rejected (не self-generated). Например, low-score ответы из val того же датасета.
- Scale up до 100-200M params с теми же гиперпараметрами и тем же корпусом. Сильно нелинейный буст качества.
Файлы релиза
| Файл | Размер | Описание |
|---|---|---|
ckpt.pt |
553 МБ | nanoGPT-checkpoint (модель + optimizer state + config) |
tokenizer.model |
930 КБ | SentencePiece-токенайзер (BPE 32k) |
meta.pkl |
<1 КБ | спецтокены ID + vocab_size |
nanogpt-chat.exe (опц.) |
97 МБ | Go single-binary с встроенной моделью |
model_card.md |
этот файл |
Если хочется только inference — tokenizer.model + ckpt.pt достаточно.
Citation / благодарности
@misc{mini-tron-50,
title = {mini-tron-50: 50M Russian chat model trained from scratch},
author = {Impi},
year = {2026},
note = {Educational baseline; nanoGPT architecture}
}
Использованные ресурсы:
- nanoGPT by Andrej Karpathy — основа архитектуры и тренировочного цикла
big-russian-dataset— обучающий корпус
Лицензия
Apache 2.0 — на код и веса этой модели.
- Downloads last month
- -