ThingsAI commited on
Commit
52135a8
·
verified ·
1 Parent(s): 63fcf32

Quark-50M-v2: 43.8M Italian-first LM (loss 0.9409551193519028)

Browse files
Files changed (6) hide show
  1. README.md +119 -102
  2. model.pt +3 -0
  3. model.py +90 -0
  4. special_tokens_map.json +8 -0
  5. tokenizer.json +0 -0
  6. tokenizer_config.json +8 -32
README.md CHANGED
@@ -1,124 +1,141 @@
1
  ---
2
  language:
 
3
  - en
4
- - code
5
  license: apache-2.0
6
  tags:
7
- - smol
8
- - pretraining
9
- - instruct
10
- - 50M
11
  - causal-lm
12
- - gqa
13
- - swiglu
14
- - rmsnorm
15
- datasets:
16
- - HuggingFaceTB/smollm-corpus
17
- metrics:
18
- - perplexity
19
- model-index:
20
- - name: Quark-50m-Instruct
21
- results: []
22
  pipeline_tag: text-generation
23
  ---
24
 
25
- # Quark-50m-Instruct
26
 
27
- **Quark-50m-Instruct** is a small (≈56M parameters) decoder-only language model, fine-tuned for instruction following.
28
- It is built on the same architecture of “SmolLM” family and was fully pretrained on 5 billion tokens from
29
- [HuggingFaceTB/smollm‑corpus](https://huggingface.co/datasets/HuggingFaceTB/smollm-corpus).
30
 
31
- - **Model type:** Causal Language Model (LLaMA‑style decoder)
32
- - **Architecture:** GQA · SwiGLU · RMSNorm · RoPE · Weight‑tying
33
- - **Pretraining tokens:** 5 B
34
- - **Fine‑tuning:** Instruction‑tuned (details below)
35
- - **Creators:** [OvercastLab](https://huggingface.co/OvercastLab) (research & development lab for ML/AI)
36
- - **Release date:** 22 April 2026
37
 
38
- ## Model Summary
39
 
40
- Quark-50m-Instruct is designed to be an efficient assistant that can run on consumer GPUs (e.g., RTX 3070 with 8 GB VRAM)
41
- and even on CPU for light workloads. It is **not** competitive with large models on knowledge‑intensive tasks,
42
- but it excels at:
 
 
43
 
44
- - Simple conversational tasks
45
- - Code generation and explanation (Python)
46
- - Short text rewriting and summarisation
47
- - On‑device / edge inference
48
 
49
- The architecture closely follows the efficient‑small‑LM blueprint popularised by SmolLM:
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- | Component | Details |
52
- |-------------|-------------------------------|
53
- | Vocab size | 49,152 |
54
- | Hidden size | 384 |
55
- | Layers | 24 |
56
- | Attention | Grouped Query (6 Q heads, 2 KV heads) |
57
- | FFN | SwiGLU with 1,024 intermediate |
58
- | Position | RoPE (θ = 10,000) |
59
- | Normalisation | RMSNorm (pre‑block) |
60
 
61
- Total trainable parameters: **≈48 M** (with weight tying).
62
 
63
- ### Benchmark Evaluation Metrics
 
 
 
 
 
 
 
 
64
 
65
- | Category | Benchmark | Metric | Score / Value | Status |
66
- | :--- | :--- | :--- | :---: | :---: |
67
- | **Linguistics & Grammar** | BLiMP | Accuracy | 68.12% | Success |
68
- | **Commonsense & Reasoning** | PIQA | Normalized Accuracy | 57.83% | Success |
69
- | | COPA | Accuracy | 57.00% | Success |
70
- | | BoolQ | Accuracy | 52.17% | Success |
71
- | | WinoGrande | Accuracy | 47.36% | Success |
72
- | | HellaSwag | Normalized Accuracy | 28.49% | Success |
73
- | | RACE | Accuracy | 26.41% | Success |
74
- | | CommonsenseQA | Accuracy | 20.31% | Success |
75
- | **Academic & Knowledge** | SciQ | Normalized Accuracy | 49.00% | Success |
76
- | | ARC-Easy | Normalized Accuracy | 36.49% | Success |
77
- | | MMLU | Accuracy | 25.64% | Success |
78
- | | ARC-Challenge | Normalized Accuracy | 25.17% | Success |
79
- | | OpenBookQA | Normalized Accuracy | 25.40% | Success |
80
- | **Language Modeling** | LAMBADA | Accuracy | 15.87% | Success |
81
- | | WikiText-2 | Word Perplexity | 251.76 | Success |
82
 
83
- *Note: The Arithmetic benchmark failed due to outdated script support (`arithmetic.py`), and SocialIQA failed due to a registration tag error (`siqa`). Total baseline execution completed successfully for all other 15 tasks.*
84
-
85
-
86
- ## Uses
87
-
88
- ### Direct Use
89
- The model can be used via the 🤗 Transformers library for standard text generation.
90
- It expects chat‑formatted input (see example below).
91
-
92
- ### Downstream Use
93
- Because of the open Apache‑2.0 license, you may fine‑tune Quark-50m‑Instruct on your own data for
94
- domain‑specific tasks – for instance, a customer‑support bot, a code reviewer, or a story writer.
95
-
96
- ### Limitations
97
- - Limited world knowledge (stopped at mid‑2025 pretraining data).
98
- - Short context window (2,048 tokens).
99
- - Small size means it can make more factual mistakes than larger models.
100
-
101
- ## How to Get Started
102
 
103
  ```python
104
- from transformers import AutoTokenizer, AutoModelForCausalLM
105
-
106
- model_name = "ThingAI/Quark-50m-Instruct"
107
-
108
- tokenizer = AutoTokenizer.from_pretrained(model_name)
109
- model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
110
-
111
- messages = [
112
- {"role": "system", "content": "You are Quark, a helpful assistant."},
113
- {"role": "user", "content": "Explain group query attention in one sentence."}
114
- ]
115
-
116
- inputs = tokenizer.apply_chat_template(
117
- messages,
118
- tokenize=True,
119
- add_generation_prompt=True,
120
- return_tensors="pt"
121
- ).to(model.device)
122
-
123
- outputs = model.generate(inputs, max_new_tokens=128)
124
- print(tokenizer.decode(outputs[0], skip_special_tokens=True))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  language:
3
+ - it
4
  - en
 
5
  license: apache-2.0
6
  tags:
7
+ - italian
 
 
 
8
  - causal-lm
9
+ - small-language-model
10
+ - trained-from-scratch
11
+ - chatml
12
+ - conversational
 
 
 
 
 
 
13
  pipeline_tag: text-generation
14
  ---
15
 
16
+ # Quark-50M-v2
17
 
18
+ **43.8M parameter Italian-first bilingual language model, trained from scratch by ThingAI.**
 
 
19
 
20
+ Quark-50M is an ultra-compact causal language model that speaks fluent Italian. Designed as a proof-of-concept for small, efficient, Italian-centric AI.
 
 
 
 
 
21
 
22
+ ## Highlights
23
 
24
+ - **43.8M parameters** runs on any device, even CPU
25
+ - **Italian-first** trained on 60% Italian data (books, Wikipedia, web)
26
+ - **ChatML format** — `<|im_start|>user`/`<|im_start|>assistant`
27
+ - **Custom tokenizer** — 16k BPE, optimized for Italian (4.15 chars/token)
28
+ - **Trained from scratch** — architecture, tokenizer, and training pipeline all custom
29
 
30
+ ## Architecture
 
 
 
31
 
32
+ | Component | Value |
33
+ |-----------|-------|
34
+ | Parameters | 43.8M |
35
+ | Vocabulary | 16,384 (BPE) |
36
+ | Dimensions | 512 |
37
+ | Layers | 12 |
38
+ | Heads | 8 (4 KV heads, GQA) |
39
+ | FFN | 1,408 (SwiGLU) |
40
+ | Context | 2,048 tokens |
41
+ | Normalization | RMSNorm |
42
+ | Position | RoPE |
43
+ | Weight Tying | Yes |
44
 
45
+ ## Training
 
 
 
 
 
 
 
 
46
 
47
+ **Pretraining:** 5B tokens on a curated mix:
48
 
49
+ | Dataset | Weight | Type |
50
+ |---------|--------|------|
51
+ | PleIAs/Italian-PD | 25% | 171K Italian books (public domain) |
52
+ | FineWeb-2 Italian | 20% | Cleaned, deduplicated web |
53
+ | Wikipedia IT | 15% | Encyclopedia |
54
+ | Cosmopedia | 15% | Synthetic educational |
55
+ | SmolLM-Corpus | 10% | Curated mix |
56
+ | StarCoder Python | 8% | Code |
57
+ | OpenWebMath | 7% | Mathematics |
58
 
59
+ **SFT:** Fine-tuned on [quattro-chiacchiere](https://huggingface.co/datasets/ThingAI/quattro-chiacchiere), a synthetic Italian Q&A dataset generated with [Alembic](https://github.com/skein-labs/Alembic).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ ## Usage
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  ```python
64
+ import torch
65
+ from huggingface_hub import hf_hub_download
66
+ from transformers import PreTrainedTokenizerFast
67
+
68
+ # Load
69
+ ckpt_path = hf_hub_download("ThingAI/Quark-50M", "model.pt")
70
+ model_py = hf_hub_download("ThingAI/Quark-50M", "model.py")
71
+ tok_file = hf_hub_download("ThingAI/Quark-50M", "tokenizer.json")
72
+
73
+ # Tokenizer
74
+ tokenizer = PreTrainedTokenizerFast(tokenizer_file=tok_file)
75
+ tokenizer.eos_token = "<|endoftext|>"
76
+
77
+ # Model
78
+ import importlib.util
79
+ spec = importlib.util.spec_from_file_location("model", model_py)
80
+ mod = importlib.util.module_from_spec(spec)
81
+ spec.loader.exec_module(mod)
82
+
83
+ ckpt = torch.load(ckpt_path, map_location="cpu", weights_only=False)
84
+ cfg = mod.ModelConfig(**ckpt["model_cfg"])
85
+ model = mod.Quark(cfg).eval()
86
+ model.load_state_dict(ckpt["model"])
87
+
88
+ # Chat
89
+ prompt = "<|im_start|>user\nQual è la capitale d'Italia?<|im_end|>\n<|im_start|>assistant\n"
90
+ ids = tokenizer.encode(prompt, return_tensors="pt")
91
+ with torch.no_grad():
92
+ for _ in range(100):
93
+ logits = model(ids)[1][:, -1, :].float()
94
+ nxt = logits.argmax(-1, keepdim=True)
95
+ if nxt.item() == tokenizer.convert_tokens_to_ids("<|im_end|>"): break
96
+ ids = torch.cat([ids, nxt], -1)
97
+ print(tokenizer.decode(ids[0], skip_special_tokens=True))
98
+ # La capitale d'Italia è Roma.
99
+ ```
100
+
101
+ ## Examples
102
+
103
+ ```
104
+ Tu: Qual è la capitale d'Italia?
105
+ Quark: La capitale d'Italia è Roma.
106
+
107
+ Tu: Chi sei?
108
+ Quark: Sono Quark, piacere di conoscerti.
109
+
110
+ Tu: Come ti chiami?
111
+ Quark: Mi chiamo Quark, piacere di conoscerti.
112
+ ```
113
+
114
+ ## Limitations
115
+
116
+ - **43.8M parameters** — cannot perform complex reasoning or long-form generation
117
+ - **Factual accuracy** — may hallucinate facts, especially on niche topics
118
+ - **SFT dataset** — currently limited; more data will improve reliability
119
+ - **No safety training** — not recommended for production without guardrails
120
+
121
+ ## Related
122
+
123
+ - [Quark3Tokenizer](https://huggingface.co/ThingAI/Quark3Tokenizer) — the tokenizer
124
+ - [quattro-chiacchiere](https://huggingface.co/datasets/ThingAI/quattro-chiacchiere) — the SFT dataset
125
+ - [Alembic](https://github.com/skein-labs/Alembic) — the dataset distillation tool
126
+ - [Glyph](https://huggingface.co/ThingAI/Glyph) — multi-task text classifier by ThingAI
127
+
128
+ ## Citation
129
+
130
+ ```bibtex
131
+ @misc{quark50m,
132
+ author = {ThingAI},
133
+ title = {Quark-50M-v2: Italian-First Small Language Model},
134
+ year = {2026},
135
+ url = {https://huggingface.co/ThingAI/Quark-50M}
136
+ }
137
+ ```
138
+
139
+ ## License
140
+
141
+ Apache 2.0
model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6b7c2bae538e8df09945d4feb450fc6784194f243a6855408a2b5276773657ec
3
+ size 175196411
model.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quark-50M model definition — standalone."""
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from dataclasses import dataclass
7
+
8
+ @dataclass
9
+ class ModelConfig:
10
+ vocab_size: int = 16384; d_model: int = 512; n_heads: int = 8
11
+ n_kv_heads: int = 4; n_layers: int = 12; d_ff: int = 1408
12
+ head_dim: int = 64; max_seq_len: int = 2048; rope_theta: float = 10000.0
13
+ rms_eps: float = 1e-5; qkv_bias: bool = False; dropout: float = 0.0
14
+
15
+ class RMSNorm(nn.Module):
16
+ def __init__(self, dim, eps=1e-5):
17
+ super().__init__(); self.eps = eps; self.scale = nn.Parameter(torch.ones(dim))
18
+ def forward(self, x):
19
+ return (x.float() * x.float().pow(2).mean(-1, keepdim=True).add(self.eps).rsqrt()).to(x.dtype) * self.scale
20
+
21
+ class RotaryEmbedding(nn.Module):
22
+ def __init__(self, head_dim, max_seq_len, theta=10000.0):
23
+ super().__init__()
24
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2).float() / head_dim))
25
+ self.register_buffer("inv_freq", inv_freq, persistent=False); self._build(max_seq_len)
26
+ def _build(self, seq_len):
27
+ t = torch.arange(seq_len, device=self.inv_freq.device).float()
28
+ freqs = torch.outer(t, self.inv_freq); emb = torch.cat([freqs, freqs], dim=-1)
29
+ self.register_buffer("cos_cache", emb.cos()[None, None], persistent=False)
30
+ self.register_buffer("sin_cache", emb.sin()[None, None], persistent=False); self._max = seq_len
31
+ @staticmethod
32
+ def _rot(x):
33
+ x1, x2 = x.chunk(2, dim=-1); return torch.cat([-x2, x1], dim=-1)
34
+ def forward(self, q, k):
35
+ T = q.size(2)
36
+ if T > self._max: self._build(T)
37
+ c, s = self.cos_cache[:,:,:T], self.sin_cache[:,:,:T]
38
+ return q*c + self._rot(q)*s, k*c + self._rot(k)*s
39
+
40
+ class GQA(nn.Module):
41
+ def __init__(self, cfg):
42
+ super().__init__()
43
+ self.n_heads, self.n_kv_heads = cfg.n_heads, cfg.n_kv_heads
44
+ self.n_groups, self.head_dim = cfg.n_heads // cfg.n_kv_heads, cfg.head_dim
45
+ self.q_proj = nn.Linear(cfg.d_model, cfg.n_heads * cfg.head_dim, bias=cfg.qkv_bias)
46
+ self.k_proj = nn.Linear(cfg.d_model, cfg.n_kv_heads * cfg.head_dim, bias=cfg.qkv_bias)
47
+ self.v_proj = nn.Linear(cfg.d_model, cfg.n_kv_heads * cfg.head_dim, bias=cfg.qkv_bias)
48
+ self.o_proj = nn.Linear(cfg.n_heads * cfg.head_dim, cfg.d_model, bias=False)
49
+ self.rope = RotaryEmbedding(cfg.head_dim, cfg.max_seq_len, cfg.rope_theta)
50
+ def forward(self, x):
51
+ B, T, _ = x.shape
52
+ q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
53
+ k = self.k_proj(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
54
+ v = self.v_proj(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
55
+ q, k = self.rope(q, k)
56
+ if self.n_groups > 1:
57
+ B_r, _, T_r, D_r = k.shape
58
+ k = k[:,:,None,:,:].expand(B_r, self.n_kv_heads, self.n_groups, T_r, D_r).reshape(B_r, self.n_heads, T_r, D_r)
59
+ v = v[:,:,None,:,:].expand(B_r, self.n_kv_heads, self.n_groups, T_r, D_r).reshape(B_r, self.n_heads, T_r, D_r)
60
+ return self.o_proj(F.scaled_dot_product_attention(q, k, v, is_causal=True).transpose(1, 2).contiguous().view(B, T, -1))
61
+
62
+ class Block(nn.Module):
63
+ def __init__(self, cfg):
64
+ super().__init__()
65
+ self.norm_attn = RMSNorm(cfg.d_model, cfg.rms_eps); self.attn = GQA(cfg)
66
+ self.norm_ffn = RMSNorm(cfg.d_model, cfg.rms_eps)
67
+ self.gate = nn.Linear(cfg.d_model, cfg.d_ff, bias=False)
68
+ self.up = nn.Linear(cfg.d_model, cfg.d_ff, bias=False)
69
+ self.down = nn.Linear(cfg.d_ff, cfg.d_model, bias=False)
70
+ def forward(self, x):
71
+ x = x + self.attn(self.norm_attn(x))
72
+ h = self.norm_ffn(x); x = x + self.down(F.silu(self.gate(h)) * self.up(h))
73
+ return x
74
+
75
+ class Quark(nn.Module):
76
+ def __init__(self, cfg):
77
+ super().__init__(); self.cfg = cfg
78
+ self.embed_tokens = nn.Embedding(cfg.vocab_size, cfg.d_model)
79
+ self.layers = nn.ModuleList([Block(cfg) for _ in range(cfg.n_layers)])
80
+ self.norm = RMSNorm(cfg.d_model, cfg.rms_eps)
81
+ self.lm_head = nn.Linear(cfg.d_model, cfg.vocab_size, bias=False)
82
+ self.lm_head.weight = self.embed_tokens.weight
83
+ def forward(self, ids, labels=None):
84
+ x = self.embed_tokens(ids)
85
+ for layer in self.layers: x = layer(x)
86
+ logits = self.lm_head(self.norm(x))
87
+ loss = None
88
+ if labels is not None:
89
+ loss = F.cross_entropy(logits.view(-1, self.cfg.vocab_size), labels.view(-1), ignore_index=-100)
90
+ return loss, logits
special_tokens_map.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<|im_start|>",
3
+ "eos_token": "<|endoftext|>",
4
+ "pad_token": "<|pad|>",
5
+ "additional_special_tokens": [
6
+ "<|im_end|>"
7
+ ]
8
+ }
tokenizer.json CHANGED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json CHANGED
@@ -1,34 +1,10 @@
1
  {
2
- "add_prefix_space": false,
3
- "backend": "tokenizers",
4
- "bos_token": "<|endoftext|>",
5
- "clean_up_tokenization_spaces": false,
6
  "eos_token": "<|endoftext|>",
7
- "errors": "replace",
8
- "extra_special_tokens": [
9
- "<|endoftext|>",
10
- "<|im_start|>",
11
- "<|im_end|>",
12
- "<repo_name>",
13
- "<reponame>",
14
- "<file_sep>",
15
- "<filename>",
16
- "<gh_stars>",
17
- "<issue_start>",
18
- "<issue_comment>",
19
- "<issue_closed>",
20
- "<jupyter_start>",
21
- "<jupyter_text>",
22
- "<jupyter_code>",
23
- "<jupyter_output>",
24
- "<jupyter_script>",
25
- "<empty_output>"
26
- ],
27
- "is_local": true,
28
- "local_files_only": false,
29
- "model_max_length": 1000000000000000019884624838656,
30
- "pad_token": "<|endoftext|>",
31
- "tokenizer_class": "GPT2Tokenizer",
32
- "unk_token": "<|endoftext|>",
33
- "vocab_size": 49152
34
- }
 
1
  {
2
+ "tokenizer_class": "PreTrainedTokenizerFast",
3
+ "bos_token": "<|im_start|>",
 
 
4
  "eos_token": "<|endoftext|>",
5
+ "pad_token": "<|pad|>",
6
+ "model_max_length": 4096,
7
+ "chat_template": "{% for message in messages %}<|im_start|>{{ message['role'] }}\n{{ message['content'] }}<|im_end|>\n{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}",
8
+ "add_bos_token": false,
9
+ "add_eos_token": false
10
+ }