Text Generation
Transformers
Safetensors
English
ivme_conversate_s
from-scratch
experimental
custom-architecture
causal-lm
small-language-model
custom_code
Eval Results (legacy)
Instructions to use IvmeLabs/Ivme-Conversate-N-v1-Base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use IvmeLabs/Ivme-Conversate-N-v1-Base with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="IvmeLabs/Ivme-Conversate-N-v1-Base", trust_remote_code=True)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("IvmeLabs/Ivme-Conversate-N-v1-Base", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use IvmeLabs/Ivme-Conversate-N-v1-Base with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "IvmeLabs/Ivme-Conversate-N-v1-Base" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IvmeLabs/Ivme-Conversate-N-v1-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/IvmeLabs/Ivme-Conversate-N-v1-Base
- SGLang
How to use IvmeLabs/Ivme-Conversate-N-v1-Base with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "IvmeLabs/Ivme-Conversate-N-v1-Base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IvmeLabs/Ivme-Conversate-N-v1-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "IvmeLabs/Ivme-Conversate-N-v1-Base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IvmeLabs/Ivme-Conversate-N-v1-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use IvmeLabs/Ivme-Conversate-N-v1-Base with Docker Model Runner:
docker model run hf.co/IvmeLabs/Ivme-Conversate-N-v1-Base
Update README.md
Browse files
README.md
CHANGED
|
@@ -1,24 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
---
|
| 7 |
|
| 8 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
12 diverse sources (web/edu, dialogue, code, math, reasoning, science, etc).
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
SwiGLU FFN, immediate block-wise weight sharing, learnable meta/register
|
| 16 |
-
tokens, RoPE.
|
| 17 |
|
| 18 |
-
|
| 19 |
-
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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).
|