ereniko commited on
Commit
7db7816
·
verified ·
1 Parent(s): ebccfa3

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +345 -19
README.md CHANGED
@@ -1,24 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
- tags:
3
- - text-generation
4
- - from-scratch
5
- - experimental
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  ---
7
 
8
- # Ivme-Conversate-S-v1-Base
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- Sub-10M parameter language model, trained single-epoch on ~836M tokens across
11
- 12 diverse sources (web/edu, dialogue, code, math, reasoning, science, etc).
12
 
13
- Architecture: factorized + untied token embeddings, grouped-query attention,
14
- DIFF Transformer V2 attention, nGPT hypersphere-normalized residual stream,
15
- SwiGLU FFN, immediate block-wise weight sharing, learnable meta/register
16
- tokens, RoPE.
17
 
18
- - 9,545,840 parameters
19
- - vocab_size=8000, d_model=256
20
- - 14 unique layers x 2 share_factor
21
- = 28 effective depth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  ## Usage
24
 
@@ -29,9 +340,24 @@ model = AutoModelForCausalLM.from_pretrained(
29
  "ivmelabs/Ivme-Conversate-S-v1-Base", trust_remote_code=True
30
  )
31
  tok = AutoTokenizer.from_pretrained("ivmelabs/Ivme-Conversate-S-v1-Base")
 
 
 
 
32
  ```
33
 
34
- Note: this architecture has no KV-cache -- `forward()` recomputes attention
35
- over the full sequence each call, so `.generate()` works but is O(n^2) rather
36
- than the O(n) a cached model gets. Fine for short generations, not tuned for
37
- long-form serving.
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ivme-Conversate-S-v1-Base
2
+
3
+ **Codename: Small Apple 1**
4
+
5
+ A sub-10M parameter language model trained from scratch as an experiment in
6
+ extreme data efficiency: how much can a genuinely tiny, architecturally
7
+ unusual model learn from a single, unrepeated pass over a small, deliberately
8
+ diverse token budget?
9
+
10
+ This is not a production model. It is a research artifact from one long,
11
+ mostly-nocturnal debugging session, documented here in full — including the
12
+ mistakes, because the mistakes are half of what makes the result legible.
13
+
14
+ ---
15
+
16
+ ## TL;DR
17
+
18
+ - **9,545,840 parameters.** Sub-10M, on purpose.
19
+ - **~836M training tokens, single epoch, no repetition.** The entire point
20
+ of the experiment was testing what a tiny model learns from one clean pass
21
+ over diverse data, not what it memorizes from many passes over less.
22
+ - **12 domain sources** — web/edu text, dialogue, code, math, instructions,
23
+ reasoning traces, science QA — deliberately diverse rather than a few
24
+ large homogeneous corpora.
25
+ - **Architecturally exotic on purpose**: factorized + untied embeddings,
26
+ GQA, real DIFF Transformer V2 attention, nGPT hypersphere normalization,
27
+ immediate block-wise weight sharing, learnable meta tokens, RoPE.
28
+ - **Benchmarked with EleutherAI's lm-evaluation-harness**, strictly, no
29
+ custom scoring logic: ARC Easy, WikiText2, BLiMP (all 67 subtasks).
30
+ - Trained on an NVIDIA B300, benchmarked on an L40S, served here via Modal +
31
+ Hugging Face `trust_remote_code`.
32
+
33
  ---
34
+
35
+ ## Why this exists
36
+
37
+ The starting question was simple: **is diversity a substitute for scale, at
38
+ the very bottom of the parameter range?** Conventional wisdom for tiny
39
+ language models (TinyStories, SmolLM, MobileLLM) leans toward simple, clean,
40
+ high-signal text over broad diversity — diversity is usually treated as a
41
+ mid-model-size lever, something you can afford once you have enough capacity
42
+ to actually exploit it. This project deliberately tested the opposite bet at
43
+ the smallest end of the range: 12 genuinely different domains, one epoch,
44
+ under 10M parameters, and see what happens.
45
+
46
+ A companion question, layered on afterward: **how far can you push an
47
+ architecture away from the field's converged defaults before the tooling
48
+ itself starts fighting you?** The answer, documented below in the incident
49
+ log, is "further than you'd expect works at all, but every fused kernel,
50
+ every `torch.compile` mode, and every auto-batching heuristic in the modern
51
+ training stack quietly assumes you're using something closer to a standard
52
+ Transformer." Going exotic on purpose means paying for it in engineering
53
+ time, not GPU-hours.
54
+
55
  ---
56
 
57
+ ## Architecture
58
+
59
+ | Component | Choice | Why |
60
+ |---|---|---|
61
+ | Embeddings | **Factorized, untied** — separate small-rank input/output projections, not shared | Untied embeddings were a deliberate constraint from the start (avoiding the safetensors weight-tying headaches of the prior model, V2). Factorization (via a bottleneck rank `r=48`) is what makes untying affordable at this parameter budget — a naive untied embedding at vocab=8000 would have eaten the entire parameter budget on its own. |
62
+ | Attention | **GQA** (4:1 query:kv head ratio) + **DIFF Transformer V2** | GQA was a fixed requirement from the outset. DIFF V2 was chosen deliberately for its documented training-stability and loss benefits over a standard Transformer — see the DIFF V2 section below for what "V2" specifically means here and why it matters. |
63
+ | Normalization | **nGPT** — every vector (embeddings, attention/FFN outputs, residual stream) constrained to a unit hypersphere; the residual update itself is a "move along the sphere" operation controlled by per-channel learnable "eigen learning rate" scale parameters, rather than standard LayerNorm/RMSNorm | Chosen for its reported 4–20x reduction in training steps needed to reach a given loss — directly relevant when the entire training budget is a single, unrepeated epoch. |
64
+ | FFN | SwiGLU, fused single gate+up projection | Standard, efficient, one fewer matmul than the naive two-projection form. |
65
+ | Positional encoding | RoPE, applied at full head_dim | DIFF V2 doesn't split head_dim (unlike V1), so RoPE is applied normally, no half-dimension bookkeeping required. |
66
+ | Depth | 14 unique transformer blocks, each executed twice (**immediate block-wise weight sharing**) → 28 effective layers of depth at the parameter cost of 14 | Motivated by MobileLLM's finding that depth-over-width is the more parameter-efficient lever for small models, and that immediate block-wise sharing recovers accuracy with no parameter cost. |
67
+ | Register tokens | 4 learnable "meta tokens" prepended to every sequence, dropped before the output head | A cheap (a few thousand parameters), Hymba-inspired addition: gives the model a place to accumulate global context without burdening ordinary attention to do all of that summarization from scratch. |
68
+
69
+ **Parameter breakdown:** vocab (factorized embedding + head) 792,576 · body
70
+ 8,752,240 · meta tokens 1,024 · **total 9,545,840**.
71
 
72
+ ### On DIFF Transformer V2, specifically
 
73
 
74
+ This deserves its own note because getting it right — and initially getting
75
+ it *wrong* was one of the more instructive parts of this project.
 
 
76
 
77
+ DIFF attention computes attention as the *difference* of two softmax maps,
78
+ which cancels noise and produces measurably lower language-modeling loss than
79
+ a standard Transformer at equal parameter count. The original formulation
80
+ (V1) splits each attention head's dimension in half to form the two
81
+ subtraction branches, which works, but means the resulting Q/K tensors don't
82
+ share a dimension with V — an awkward shape for modern fused attention
83
+ kernels (FlashAttention, SDPA's fused backends), which expect Q, K, and V to
84
+ share a last dimension.
85
+
86
+ **V2** solves this architecturally rather than by working around the shape
87
+ mismatch: it doubles the number of *query heads* instead of splitting
88
+ head_dim, keeps K/V unchanged, and uses a single fused attention call whose
89
+ `2h` output heads are then split — critically, by **interleaving**
90
+ (`heads[0::2]`, `heads[1::2]`), not by halving the head list. The reference
91
+ implementation is explicit that halving is a "Wrong Implementation": paired
92
+ heads must share the same GQA group (the same K/V), and under standard
93
+ head-to-group assignment, interleaved heads do and halved heads don't. Get
94
+ this backwards and training is measurably less stable.
95
+
96
+ This model was, for a while during development, running V1's shape-split
97
+ math zero-padded into V2's shape contract — mathematically valid, verified
98
+ exact, and genuinely functional, but not actually V2's real mechanism, and
99
+ paying real overhead (two attention calls instead of one) for no benefit.
100
+ Rebuilt correctly before the final training run: one fused call, no padding,
101
+ natively FlashAttention-compatible, λ as a per-token per-head
102
+ sigmoid-projected value rather than V1's global exponential form.
103
+
104
+ ---
105
+
106
+ ## Data
107
+
108
+ **~836M tokens, one epoch, twelve sources**, deliberately mixed rather than
109
+ dominated by one or two large corpora:
110
+
111
+ | Domain | Source | Notes |
112
+ |---|---|---|
113
+ | General web/educational | FineWeb-Edu (`sample-10BT` config) | Largest single slice, ~30% of budget — the breadth anchor, kept a minority share on purpose. |
114
+ | Dialogue | SODA | All dialogue routed through SODA alone after `daily_dialog` and `facebook/empathetic_dialogues` turned out to be unloadable — see incident log. |
115
+ | Narrative | TinyStories | Small slice, kept modest to avoid duplicating what a different model in the same family already leaned on heavily. |
116
+ | Code | `nampdn-ai/tiny-codes` (gated) + `b-mc2/sql-create-context` | |
117
+ | Encyclopedic | `rahular/simple-wikipedia` | |
118
+ | Explanatory/textbook | `nampdn-ai/tiny-orca-textbooks` (gated) | **Only the `textbook` field was used** — a probe of the raw data found the `question`/`response` fields in this dataset were frequently topically unrelated to the textbook content itself (e.g. a "problem-solving scenarios" textbook paired with an unrelated movie-plot question), so those fields were excluded rather than trained on as noise. |
119
+ | Instructions | `HuggingFaceH4/no_robots` + `databricks-dolly-15k` | |
120
+ | Math | `microsoft/orca-math-word-problems-200k` | |
121
+ | Reasoning | `openbmb/UltraInteract_sft` | Rows treated independently; the dataset's `parent_id` tree structure wasn't resolved during packing. |
122
+ | Science | `allenai/sciq` | Multiple-choice distractor fields explicitly excluded from the training text — only the question, correct answer, and supporting explanation were used, to avoid training on unlabeled wrong answers. |
123
+
124
+ **Tokenizer:** custom 8,000-token byte-level BPE, trained on the collected
125
+ mix itself (not reused from an existing model) — deliberately compact, since
126
+ at this parameter scale the vocabulary/output-head cost is the single
127
+ largest lever on how much of the parameter budget is left for the
128
+ transformer body itself.
129
+
130
+ **Packing:** flat, memory-mapped token stream, chunked into fixed-length
131
+ windows, shuffled once into a single training permutation with no
132
+ overlapping or repeated windows — one token, seen exactly once, with no
133
+ mechanism by which the training loop could double back over data already
134
+ covered.
135
+
136
+ ---
137
+
138
+ ## Training
139
+
140
+ | | |
141
+ |---|---|
142
+ | Hardware | NVIDIA B300 (via Modal), single GPU |
143
+ | Precision | bf16 autocast |
144
+ | Attention kernel | Pinned FlashAttention-2 (via Hugging Face `kernels`), with an explicit compute-capability gate that falls back cleanly to unrestricted SDPA on hardware below Ampere |
145
+ | Compile | `torch.compile`, regional (each of the 14 unique blocks compiled once, reused via the block-sharing execution order), `mode="reduce-overhead"` |
146
+ | Optimizer | Muon (body matrices, 2D+) + AdamW (embeddings, norm scales, biases) — split via `torch.optim.Muon`, now native to recent PyTorch |
147
+ | Data residency | Entire ~836M-token packed corpus loaded onto GPU VRAM once at startup (under 1% of a B300's 288GB), eliminating per-batch host transfer for the whole run |
148
+ | Batch size | Auto-probed at startup via a doubling-then-binary-search OOM sweep, with a safety margin |
149
+ | Epochs | **Exactly one.** Enforced structurally — the training loop walks a single fixed permutation and stops at the end rather than wrapping around, so it cannot silently repeat data even under a scheduling miscalculation. |
150
+ | Throughput | Settled around ~230K tokens/sec at steady state after warmup/compile overhead |
151
+ | Wall-clock | ~55–65 minutes for the full epoch once the pipeline was fully debugged |
152
+
153
+ ---
154
+
155
+ ## Results
156
+
157
+ Evaluated with **EleutherAI's `lm-evaluation-harness`, strictly** — via the
158
+ officially-supported path of wrapping this model in a minimal
159
+ `transformers.PreTrainedModel` shim and passing it directly to `HFLM`, so
160
+ every actual loglikelihood computation, batching decision, and metric
161
+ aggregation is the harness's own tested code, not a reimplementation. 0-shot
162
+ throughout. Run on an L40S with auto-batching.
163
+
164
+ ### ARC Easy
165
+ | Metric | Score |
166
+ |---|---|
167
+ | `acc` | 26.8% |
168
+ | `acc_norm` | 27.6% |
169
+
170
+ For context: random chance on a 4-option multiple-choice task is 25%. This
171
+ model is barely above chance on ARC Easy — a genuine, honest result for a
172
+ 9.5M-parameter, single-epoch model. ARC Easy requires a level of factual/
173
+ scientific-reasoning generalization this model's capacity and training
174
+ budget simply weren't built to reach.
175
+
176
+ ### WikiText2
177
+ | Metric | Score |
178
+ |---|---|
179
+ | Word perplexity | 5,174 |
180
+ | Byte perplexity | 4.95 |
181
+ | Bits per byte | 2.31 |
182
+
183
+ High word-level perplexity is expected here for a structural reason, not
184
+ just a capability one: the model's vocabulary was trained on this project's
185
+ own 12-source mix, not on WikiText2's specific text distribution, and
186
+ perplexity is highly sensitive to vocabulary/domain match. Byte-level
187
+ perplexity (which is vocabulary-independent) is the more informative number
188
+ of the two for a model with a from-scratch, non-standard tokenizer.
189
+
190
+ ### BLiMP (all 67 subtasks, aggregate)
191
+ | Metric | Score |
192
+ |---|---|
193
+ | `acc` (aggregate, `sample_count=67,000`) | **59.2%** |
194
+
195
+ This is the most interesting result of the three, because the per-subtask
196
+ breakdown is legible rather than uniform:
197
+
198
+ - **Near-ceiling** on several subtasks: `principle_A_case_1` (99.8%),
199
+ `sentential_negation_npi_licensor_present` (99.0%),
200
+ `principle_A_domain_1` (98.3%), `wh_questions_subject_gap_long_distance`
201
+ (98.2%). These cluster around **local binding/agreement and simple
202
+ long-distance dependency patterns** — the kind of structure that shows up
203
+ constantly, in a short window, across almost any register of English text.
204
+ - **Near-floor** on a distinct cluster: `only_npi_licensor_present` (3.0%),
205
+ `matrix_question_npi_licensor_present` (3.0%),
206
+ `only_npi_scope` (16.7%), `wh_vs_that_with_gap_long_distance` (8.7%).
207
+ These are almost entirely **negative polarity item (NPI) licensing**
208
+ phenomena — a genuinely subtle syntactic dependency that requires tracking
209
+ a licensing context across a clause, not just local agreement.
210
+
211
+ Read together: the model learned real local grammatical structure — subject/
212
+ verb agreement, reflexive binding, some long-distance filler-gap patterns —
213
+ robustly, from a single pass over diverse data. It essentially did not learn
214
+ NPI licensing at all. That's a specific, falsifiable, and genuinely
215
+ interesting finding about what a tiny, single-epoch, diversity-first model
216
+ generalizes and what it doesn't, rather than a vague "it's a small model so
217
+ of course it's bad at everything" shrug.
218
+
219
+ ### Qualitative sample
220
+
221
+ > **Prompt:** "Once upon a time"
222
+ >
223
+ > **Continuation:** "...you can be excited that her friends: he was a happy
224
+ > total with an sorry most little a is and let from the number of fors.
225
+ > Mivean: Just get your time about what you can was you in from, to be many
226
+ > in the time, in which time at the number of: Rotes: That? Ining a likeing,
227
+ > her person and"
228
+
229
+ Locally plausible (consistent capitalization of name-like tokens, dialogue-
230
+ style colon formatting clearly picked up from the SODA/no_robots portions of
231
+ the training mix, roughly English sentence rhythm) and globally incoherent —
232
+ exactly consistent with the BLiMP findings above and with the model's
233
+ perplexity: it has learned surface statistics and some local syntax, not
234
+ compositional semantics.
235
+
236
+ ---
237
+
238
+ ## What broke, and what that says about the field
239
+
240
+ This section exists because the debugging process turned out to be as
241
+ informative as the results — most of the field's tooling (FlashAttention,
242
+ `torch.compile`, SDPA's backend selection, `lm-evaluation-harness`'s `HFLM`)
243
+ is built around standard-architecture assumptions, and pushing outside them
244
+ surfaces real, specific incompatibilities rather than vague friction.
245
+
246
+ - **DIFF attention's shape contract genuinely fights fused kernels** unless
247
+ implemented as V2 specifically intends (see architecture section above).
248
+ The V1-style workaround was mathematically valid but paid a real,
249
+ measurable performance tax for it.
250
+ - **A GPU-resident dataset optimization introduced a genuine PyTorch
251
+ kernel-coverage gap**: CUDA's advanced-indexing kernels don't support
252
+ `uint16` tensors (`"index_cuda" not implemented for 'UInt16'"`), even
253
+ though basic transfer operations do. Fixed by storing the token array as
254
+ `int64` on GPU instead — a 4x memory cost that's still trivially small in
255
+ absolute terms (under 3% of a B300's VRAM) for a corpus this size.
256
+ - **`SDPBackend.FLASH_ATTENTION` is a generic label, not a pinned kernel
257
+ version** — PyTorch's dispatcher silently resolves it to whatever
258
+ FlashAttention generation the GPU's compute capability supports, which on
259
+ Blackwell-class hardware meant FA4, empirically measured to *regress*
260
+ throughput for this model's specific shape profile (small head_dim, small
261
+ batches — the opposite of what FA4's warp-specialization and TMEM
262
+ pipelining are tuned for). Fixed by pinning FlashAttention-2 specifically
263
+ via Hugging Face's `kernels` library, with an explicit device
264
+ compute-capability gate so the same code correctly falls back to
265
+ unrestricted SDPA on hardware (like a T4) below FlashAttention's Ampere+
266
+ floor entirely.
267
+ - **A silent, autocast-related dtype leak**: several raw `nn.Parameter`
268
+ tensors in the model (the register/meta tokens, and the nGPT
269
+ "eigen-learning-rate" scale parameters) never pass through an
270
+ autocast-eligible operation, so multiplying them against bf16 activations
271
+ silently promotes the *result* back to fp32 — with no error anywhere in
272
+ the chain, only surfacing when that fp32 tensor eventually reached a
273
+ kernel with a hard bf16-only assertion. The fix that actually held was an
274
+ explicit, unconditional dtype cast immediately before that kernel call,
275
+ rather than chasing every individual upstream leak point.
276
+ - **The single most consequential bug**: an early version of the nGPT weight
277
+ renormalization step applied the hypersphere constraint to *every* linear
278
+ layer in the model, including the final output/vocabulary projection.
279
+ Constraining that layer's weight norm directly caps the maximum logit
280
+ magnitude the model can ever produce for any single token — which caps how
281
+ confidently softmax can ever predict anything — which puts a hard,
282
+ unmovable floor on achievable loss. This was diagnosed by the standard
283
+ sanity check of confirming the model could trivially overfit a tiny fixed
284
+ batch (it couldn't, capping at a stubborn ~2.1 loss no matter how long it
285
+ trained); excluding the output head and token embedding from
286
+ renormalization restored full learning capacity immediately. This is the
287
+ reason the model that produced the results above needed a full retraining
288
+ run partway through the project.
289
+ - **`torch.compile(mode="max-autotune")` was tried and reverted** based on
290
+ the run's own evidence: its Triton-kernel autotuning search consistently
291
+ found nothing faster than plain cuBLAS `mm` at every matmul shape tested,
292
+ while still paying minutes of upfront compile cost for the search — a net
293
+ loss for a single-epoch run where that cost is never amortized.
294
+ - **`from_pretrained()`'s fast/meta-device init path silently skips
295
+ non-persistent buffer computation**, which left this model's RoPE cache as
296
+ uninitialized memory (NaN logits) on every reload until the buffers were
297
+ made persistent instead — a documented `transformers` behavior, not a bug
298
+ in this codebase, but one that would have made the published model
299
+ unloadable if it hadn't been caught before pushing.
300
+
301
+ None of these were guesses that happened to work. Each had a measured
302
+ number, a real error message, or a directly reproduced failure behind it —
303
+ and several were tried, found wrong or insufficient, and revised at least
304
+ once. That iteration is the actual cost of the architecture being genuinely
305
+ exotic rather than a light reskin of a standard Transformer.
306
+
307
+ ---
308
+
309
+ ## Limitations
310
+
311
+ - **No KV-cache.** `forward()` recomputes attention over the full sequence
312
+ on every call. `.generate()` works but scales roughly quadratically with
313
+ output length rather than linearly — fine for short samples, not built for
314
+ long-form serving.
315
+ - **Single epoch, no repetition, by design.** This model has seen each
316
+ training token exactly once. It has not been given the opportunity to
317
+ reinforce patterns through repeated exposure the way most small-model
318
+ training recipes do.
319
+ - **8,000-token custom vocabulary**, trained on this project's own data mix.
320
+ Perplexity comparisons against models using larger, more standard
321
+ vocabularies (GPT-2 BPE, etc.) are not directly comparable at the
322
+ word-level; the byte-level numbers above are the fairer cross-model
323
+ comparison point.
324
+ - **NPI licensing and similar long-range/scope-sensitive syntactic
325
+ phenomena are essentially unlearned**, per the BLiMP breakdown above. This
326
+ is a specific, known gap, not a general disclaimer.
327
+ - **This is an experimental architecture with no prior published
328
+ implementation combining all of its pieces** (factorized+untied
329
+ embeddings, GQA, DIFF V2, nGPT, block-sharing, meta tokens, together).
330
+ Treat it as a research artifact, not a production-hardened model family.
331
+
332
+ ---
333
 
334
  ## Usage
335
 
 
340
  "ivmelabs/Ivme-Conversate-S-v1-Base", trust_remote_code=True
341
  )
342
  tok = AutoTokenizer.from_pretrained("ivmelabs/Ivme-Conversate-S-v1-Base")
343
+
344
+ ids = tok("Once upon a time", return_tensors="pt").input_ids
345
+ out = model.generate(ids, max_new_tokens=80, do_sample=True, temperature=0.8, top_k=40)
346
+ print(tok.decode(out[0]))
347
  ```
348
 
349
+ `trust_remote_code=True` is required this is a genuinely custom
350
+ architecture, not one of `transformers`' built-in model classes.
351
+
352
+ ---
353
+
354
+ ## Acknowledgments
355
+
356
+ Architecture choices draw on: MobileLLM (depth-over-width, immediate
357
+ block-wise weight sharing), the DIFF Transformer V2 work from
358
+ Microsoft/UniLM, nGPT (NVIDIA), Hymba (meta/register tokens), and the general
359
+ small-language-model efficiency literature (TinyStories, SmolLM, the
360
+ BabyLM challenge, and the L20-Edu-135M single-GPU training study, which
361
+ served as a useful real-world throughput benchmark during development even
362
+ though it uses a substantially more conventional architecture than this
363
+ model does).