Text Generation
Transformers
Safetensors
GGUF
English
qwen3
code
100m
conversational
text-generation-inference
Dmitriy-Zemskov commited on
Commit
3ab96f9
·
verified ·
1 Parent(s): 919563d

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +161 -0
README.md CHANGED
@@ -10,5 +10,166 @@ tags:
10
  - qwen3
11
  - 100m
12
  library_name: transformers
 
 
 
 
13
  ---
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  - qwen3
11
  - 100m
12
  library_name: transformers
13
+ datasets:
14
+ - HuggingFaceTB/smollm-corpus
15
+ - HuggingFaceTB/smol-smoltalk
16
+ - HuggingFaceTB/smoltalk
17
  ---
18
 
19
+ # CalmaCatCoder-Next-96M
20
+
21
+ A tiny **experimental** chat model (96.6M parameters) trained completely **from scratch on one consumer GPU**
22
+ (RTX 5060 Ti, 16 GB) by an enthusiast from ViorikaAI. It is a hobby project: a small calm, cat-themed
23
+ Python helper that knows who it is, speaks ChatML and writes very simple Python.
24
+
25
+ > **Please read the limitations below.** This is a learning project, not a useful assistant.
26
+ > It memorised a handful of templates and will confidently produce wrong code and wrong facts.
27
+
28
+ ## Model details
29
+
30
+ | | |
31
+ |---|---|
32
+ | Architecture | `Qwen3ForCausalLM` (Qwen3 architecture, **no Qwen weights**, random init) |
33
+ | Parameters | 96.6M (tied input/output embeddings) |
34
+ | Layers / hidden / FFN | 14 / 768 / 2304 |
35
+ | Attention | 12 heads, 4 KV heads, head_dim 64 |
36
+ | Context length | **1024 bytes** (not words) |
37
+ | Vocabulary | **256 tokens: one token = one UTF-8 byte** (no learned tokenizer) |
38
+ | Precision | bf16 weights |
39
+ | Language | English only |
40
+
41
+ ### The tokenizer is byte-level
42
+
43
+ Token id equals the byte value, so `"def "` becomes `[100, 101, 102, 32]` and one word costs several tokens.
44
+ This is why the context is short. `<|im_start|>` and `<|im_end|>` are **plain text made of bytes**, not special
45
+ tokens, so the model ends its answer by writing the literal text `<|im_end|>`.
46
+ Stop generation on that string (see the example below).
47
+
48
+ Chat format (the same text the model was trained on, available as `tokenizer.apply_chat_template`):
49
+
50
+ ```
51
+ <|im_start|>system
52
+ You are CalmaCatCoder-Next, a calm and friendly cat-themed coding assistant. You explain things step by step and say so when you are not sure.<|im_end|>
53
+ <|im_start|>user
54
+ Who are you?<|im_end|>
55
+ <|im_start|>assistant
56
+ ```
57
+
58
+ If you pass no system message, the one above is inserted automatically. Other system prompts were never seen
59
+ in training, so expect them to be mostly ignored.
60
+
61
+ ## Quick start
62
+
63
+ Requires `transformers>=4.51` (Qwen3 support). No `trust_remote_code` needed.
64
+
65
+ ```python
66
+ import torch
67
+ from transformers import AutoTokenizer, AutoModelForCausalLM, StoppingCriteria, StoppingCriteriaList
68
+
69
+ repo = "YOUR_USERNAME/CalmaCatCoder-Next-96M" # or a local folder
70
+ device = "cuda" if torch.cuda.is_available() else "cpu"
71
+ tok = AutoTokenizer.from_pretrained(repo)
72
+ model = AutoModelForCausalLM.from_pretrained(repo).to(device, dtype=torch.bfloat16).eval()
73
+
74
+ END = tok.encode("<|im_end|>") # the end marker is ordinary bytes
75
+ class StopOnEnd(StoppingCriteria):
76
+ def __call__(self, input_ids, scores, **kwargs):
77
+ return torch.tensor([input_ids[0, -len(END):].tolist() == END] * input_ids.shape[0],
78
+ device=input_ids.device)
79
+
80
+ messages = [{"role": "user", "content": "Who are you?"}]
81
+ prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
82
+ inputs = tok(prompt, return_tensors="pt").to(device)
83
+ with torch.no_grad():
84
+ out = model.generate(**inputs, max_new_tokens=300,
85
+ stopping_criteria=StoppingCriteriaList([StopOnEnd()]))
86
+ print(tok.decode(out[0, inputs["input_ids"].shape[1]:]).replace("<|im_end|>", "").strip())
87
+ ```
88
+
89
+ Sampling defaults live in `generation_config.json` (temperature 0.7, top-k 40). Keep prompt + answer under
90
+ 1024 bytes; the model never saw longer sequences.
91
+
92
+ ### GGUF / llama.cpp
93
+
94
+ The tokenizer has no BPE merges and a pre-tokenizer unknown to the stock converter, so
95
+ `convert_hf_to_gguf.py` fails on it. The `make_gguf.py` wrapper from the code repository patches this
96
+ at conversion time without modifying llama.cpp (details in its header):
97
+
98
+ ```bash
99
+ git clone https://github.com/ggml-org/llama.cpp
100
+ python make_gguf.py llama.cpp CalmaCatCoder-Next-96M calmacat-96m-f16.gguf f16 # or q8_0
101
+ ```
102
+
103
+ Because `<|im_end|>` is plain text, llama.cpp does not stop on it by itself. Save the ChatML prompt to a file
104
+ and use a reverse prompt:
105
+
106
+ ```bash
107
+ llama-completion -m calmacat-96m-f16.gguf -no-cnv --no-escape -f prompt.txt -r "<|im_end|>" -n 300 --temp 0.7 --top-k 40
108
+ ```
109
+
110
+ A warning `special_eos_id is not in special_eog_ids` is expected: the model has no EOS token.
111
+ Prefer f16/bf16 or q8_0; this model is so small that aggressive quantization will likely hurt it.
112
+
113
+ ## How it was trained
114
+
115
+ All of it ran on a single RTX 5060 Ti (16 GB) under Windows with a PyTorch nightly build.
116
+
117
+ **Stage 1: pre-training from scratch (1 epoch, ~7.6 hours).**
118
+ 29,908 steps × 32,768 byte-tokens ≈ 980M tokens, about 36k tokens/s.
119
+ AdamW (lr 6e-4, betas 0.9/0.95, weight decay 0.1), 300 warm-up steps, cosine decay to 10%,
120
+ gradient clipping 1.0, bf16 autocast, `torch.compile`.
121
+ Final validation loss 0.508 nats/byte on a held-out 2% of the mix. This number is **not comparable** with other
122
+ models: it is per byte, and the held-out part contains near-copies of the templated persona dialogues.
123
+
124
+ Data mix (~1 GB, target shares; the exact proportions depend on what each source could supply):
125
+
126
+ - Python code: `codeparrot/codeparrot-clean`, the largest share (about 60%)
127
+ - English text: `cosmopedia-v2` from `HuggingFaceTB/smollm-corpus` (15%)
128
+ - Math: `meta-math/MetaMathQA` (5%)
129
+ - ChatML dialogues: `HuggingFaceTB/smol-smoltalk`, only dialogues shorter than 1000 bytes (up to 20%)
130
+ - About 5 MB of synthetic persona dialogues generated by a script (identity, creator, greetings,
131
+ a few Python tasks)
132
+
133
+ **Stage 2: supervised fine-tuning (1 epoch, ~13 minutes).**
134
+ About 28k short ChatML dialogues (~17M tokens), loss computed on assistant tokens only, peak lr 1e-4.
135
+ Mixture of programmatically generated dialogues (identity and creator questions in many phrasings, greetings,
136
+ 18 small Python tasks, follow-ups such as "add a docstring", remembering a name within a chat),
137
+ `HuggingFaceTB/smoltalk` (`everyday-conversations`) and a subset of `smol-smoltalk`.
138
+ SFT validation loss was 0.123, which mostly reflects how templated that data is.
139
+
140
+ ## Intended use
141
+
142
+ Learning and curiosity: look at how a tiny model trained from scratch behaves, reuse the training recipe,
143
+ test tokenizers or inference tooling on a very small model.
144
+
145
+ **Not intended for** real coding help, factual questions, any decision that matters, or production use.
146
+
147
+ ## Limitations (observed, not hypothetical)
148
+
149
+ - **English only, almost only Python.** Other languages and programming languages are outside what it learned.
150
+ - **It memorised templates.** For a coding request it picks the closest of the ~18 tasks it knows and adapts it.
151
+ Real examples from testing:
152
+ - "give me a random python code" produced a function named `random_state` that simply reverses a string,
153
+ followed by an explanation copied from a different task.
154
+ - "Write a function that multiplies all numbers in a list" produced a list comprehension that filters even numbers.
155
+ - After "My name is Zorro." and one more turn, "What is my name?" was answered with a different name (Sam).
156
+ - **Facts are unreliable**, and it often confidently continues with plausible-looking nonsense.
157
+ - **Short memory.** 1024 bytes of context covers a couple of turns at most.
158
+ - **No safety training** of any kind. It is too small to be dangerous, but it has no guardrails either.
159
+ - Very low training loss does not mean good behaviour here: it mostly measures memorised templates.
160
+
161
+ ## Training data notes and license
162
+
163
+ Part of the training data (`cosmopedia-v2`, `smol-smoltalk`, `smoltalk` and the synthetic persona dialogues)
164
+ was generated by other language models, so this model inherits their style and any of their errors.
165
+ The code corpus consists of public repositories with mixed licenses.
166
+ Check each dataset card for its license before reusing the weights or the data mix.
167
+
168
+ License: TODO (choose after checking the dataset licenses above; `license: other` is set in the metadata until then).
169
+
170
+ ## Reproducing
171
+
172
+ Scripts (data preparation, pre-training, SFT, Hugging Face export, GGUF conversion): TODO link.
173
+ The weights were exported with `export_hf.py` (byte tokenizer, chat template and generation config included).
174
+
175
+