1bitLabs's picture
Upload README.md with huggingface_hub
cc4f22f verified
|
Raw
History Blame Contribute Delete
10.3 kB
---
license: apache-2.0
pipeline_tag: text-generation
library_name: pytorch
tags:
- ternary
- bitnet
- 1.58-bit
- code
- base-model
- experimental
language:
- en
---
# 1bitLabs Ultron 0.3B — Base (Experimental Research Artifact)
**Experimental native-ternary dense base model, ~305M parameters.** Every weight
is -1, 0, or +1 from step one — not post-hoc quantized. Code-heavy pretraining
corpus, 30B tokens, 2048 context.
**Not an instruct or chat model.** The base continues text; it does not follow
requests. A Python-coding instruct variant is included and follows short
specifications, but produces frequently-incorrect algorithms.
**Not compatible with stock bitnet.cpp or llama.cpp.** The architecture uses a
two-tensor squared-ReLU FFN with no sub-norms, which does not match any BITNET
graph in llama.cpp. Reference runtime is PyTorch, included.
Intended for fine-tuning, ternary scaling study, and comparison. Not for
out-of-box assistant use.
Trained by one person on rented compute for about $235. Full recipe, costs, and
failure log below.
---
## Files
| File | What it is |
|---|---|
| `ultron_r008_305m.pt` | Base model, step 228882 |
| `ultron_r008_305m_instruct.pt` | Python instruct variant |
| `ultron.py` | Self-contained inference: model, KV cache, CLI |
Tokenizer, config, and the special-token table are bundled inside each
checkpoint. There are no other files to download.
```bash
pip install torch sentencepiece
python ultron.py -p "def fibonacci(n):"
python ultron.py --ckpt ultron_r008_305m_instruct.pt \
-p "Implement a function \`reverse_string(s: str) -> str\` that returns the reverse of the input string. Raise TypeError if the input is not a string."
```
---
## Architecture
| | |
|---|---|
| Parameters | 305.45M |
| Layers | 26 |
| Hidden | 1024 |
| Heads | 16 query / 4 KV (GQA) |
| FFN | squared-ReLU, d_ff 3840 — **two tensors, no gate** |
| Norm | RMSNorm, pre-norm — **no sub-norms** |
| RoPE base | 10000.0 |
| Context | 2048 |
| Vocab | 32000 (SentencePiece) |
| Embeddings | tied |
| Quantization | absmean ternary, per-output-channel scale, 8-bit activations |
### Why this is not a drop-in BitNet GGUF
Ternary weights are the quantization scheme. The architecture is a separate
question, and this one differs from Microsoft's BitNet b1.58.
All three BITNET architectures in llama.cpp — `BITNET`, `BITNET_25`,
`BITNET_B158` — require `FFN_GATE`, `ATTN_SUB_NORM`, and `FFN_SUB_NORM`. Ultron
has a two-tensor squared-ReLU FFN and no sub-norms, so it has none of them.
Conversion would require a patched llama.cpp.
**Native ternary != drop-in b1.58 GGUF.** Use the PyTorch path.
---
## Training
30B tokens on a single A100-SXM4-40GB, ~14.3 days, **~$235** rented.
| Domain | Share | Source |
|---|---|---|
| Code | 65% | The Stack (10 languages) |
| General | 20% | FineWeb-Edu |
| Math | 10% | open-web-math, finemath |
| Science | 5% | peS2o |
228,882 steps at 131,072 tokens/step. MFU 14.3%. Two OOM restarts, both
recovered from checkpoints.
**Final val_loss 2.5064**, inside a pre-registered band of 2.35-2.55 with a
primary prediction of 2.42, registered before any training data existed.
### What val_loss 2.5064 does and does not mean
The validation set is **100% peS2o science** while training was 65% code. This
is an out-of-domain number and there is no in-domain generalization measurement.
That is a design mistake — a stratified holdout should have been cut at build
time and cannot be recovered retroactively.
More importantly: on peS2o-style prompts the model reproduces academic register
with real fidelity — section numbering, statistical boilerplate, citation
conventions — and says nothing of substance. Cross-entropy rewards exactly that.
**2.5064 is a real, honestly obtained, pre-registered number that is compatible
with output that is fluent, well-formatted, and empty.**
---
## Behaviour
Measured on 30 frozen probes, greedy and sampled, 256 tokens each.
### Form >> content
The base has learned the *shape* of Python — imports, class structure, dunder
conventions, docstring formats, the `__main__` guard, where a file ends — much
more strongly than what code computes.
Greedy, `def fibonacci(n):` after a partial loop body:
```python
a, b = b, a + b
return a
```
Correct. Then it writes `fibonacci_2`, `fibonacci_3`, `fibonacci_4` with
identical bodies until the token cap.
Sampled (temp 0.8, top-k 40), same prompt:
```python
if a < b:
a = b
else:
b = a
return a
def main():
fibonacci(10)
if __name__ == "__main__":
main()
```
Wrong algorithm. Well-formed file. Terminates.
**Neither decoding mode is "the truth."** Greedy is deterministic and sometimes
exactly right on short completions, but locks into verbatim repetition. Sampling
produces better document structure and terminates more often, at some cost to
correctness. Both are shown deliberately.
### Termination
The model emits `<|endoftext|>` (token 4) at document boundaries. Teacher-forced
at 20 real corpus boundaries: **median rank 2**, top-1 in 6/20, max p 0.88, from
a 32,000-token vocabulary.
In practice: **3/30 sampled probes terminated, all three code completions. 0/24
on prose, math, or science.** Greedy: 0/30 — under argmax a rank-2 token never
wins.
Always set a length cap.
### Where the base fails
- **Synthesis from specification.** All 7 docstring-to-function probes wrong:
`s.lower() == s.lower()` for palindrome, `[x for x in a if x not in b]`
labelled as a merge, `arr.index(target)` labelled binary search, infinite
recursion in flatten.
- **Math.** Gives the probability of *not* rolling a six as 1/3. "Proves" a
summation identity by listing n = 1 through 29.
- **Prose.** Tautological loops. "The first mistake is to make a mistake when
cooking with cast iron."
- **Non-Python code.** JavaScript degenerates. Go invents `wg.Done()` as a
channel. SQL emits eight identical LEFT JOINs.
---
## The instruct variant
SFT on 3,111 execution-verified Python coding tasks. 276 steps, 37 minutes,
val_loss 0.2739.
**No chat template.** Bare instruction in, code out.
Prompt:
> Implement a function `reverse_string(s: str) -> str` that returns the reverse
> of the input string. Raise TypeError if the input is not a string.
Output:
```python
def reverse_string(s: str) -> str:
if not isinstance(s, str):
raise TypeError("Input must be a string")
return s.replace(" ", "").lower()
```
Correct signature. Correct type guard. Correct error with a sensible message.
**Wrong body.** Terminates cleanly.
This is representative: the right *shape* of answer, frequently the wrong
algorithm.
### Prompt detail matters
Training prompts averaged ~194 tokens — backtick-quoted signatures, type
annotations, explicit error conditions, edge cases. Terse prompts do markedly
worse.
"Write a function that reverses a string." produces:
```python
def reverse(self):
return self.replace(...)
```
Write specifications, not requests.
### Python only
The SFT set was 3,111 Python rows. Off-distribution requests fail badly.
Asked for an HTML page directly, the model emitted
`import { LitElement, html } from 'lit';` eleven times and ran to the cap
without terminating.
Asked for a *Python function that returns HTML*, it produced a well-formed
function that terminated — and read "escape HTML special characters" as "strip
all non-alphanumerics."
---
## Limitations
- Frequently wrong algorithms. Structure is not correctness.
- Python only in the instruct variant.
- No math.
- Degenerates into repetition off-distribution, and does not terminate there.
- 2048 context.
- No multi-turn, no tool use, no chat template.
- **No safety tuning of any kind.**
- Not stock bitnet.cpp / llama.cpp compatible.
- No in-domain benchmark number. Golden-set evaluation is in progress; the
harness that produced earlier numbers did not pass a stop token, so those
numbers are uninterpretable and are withheld.
**Do not use this for anything that matters.** It is a research artifact and a
fine-tuning starting point.
---
## What broke, and what we fixed
Published because the lab publishes failures.
**The trainer used the wrong EOS token.** `sft_train.py` resolved end-of-text via
`sp.eos_id()`, which returns 2 (`</s>`). The corpus uses token 4
(`<|endoftext|>`). Every SFT and DPO run for roughly a month terminated responses
with a token the base had never seen at a document boundary across 30B tokens,
while suppressing the one it had. The wrong value printed in every training log
and nobody caught it, because 2 *is* the correct SentencePiece EOS — just not for
this corpus. Fixed by reading `eot_id` from the corpus manifest and asserting the
piece.
**A stale directory produced a false diagnosis.** A `runs/R008/` directory held a
*dense, non-ternary* 16k-vocab baseline stopped at 1% of training. Checking token
id 4 against that model's tokenizer produced the conclusion that the corpus
separator was an apostrophe and that the model had no learnable stop token. That
conclusion propagated across work sessions into a documented lesson and became
the load-bearing premise of a recovery plan that included a corpus rebuild. It
was false. Token 4 is a properly reserved `<|endoftext|>` written at document
boundaries throughout the corpus; the apostrophe is 27530. The error was caught
the first time anyone generated from the model and read the raw output.
**The evaluation harness never stopped generation.** It passed no stop token and
the reference `generate()` had no stop logic, so every evaluation ran to the
length cap regardless of what the model emitted. Earlier benchmark numbers are
therefore uninterpretable and are not published.
If you fine-tune this model: the EOT is **token 4**. Do not use `sp.eos_id()`.
---
## Citation
```bibtex
@misc{ultron-r008-2026,
title = {Ultron 0.3B: an experimental native ternary language model},
author = {1bitLabs},
year = {2026},
note = {305M parameters, 30B tokens, ~\$235}
}
```
## License
Apache 2.0 — weights and code.
Training data derives from The Stack, FineWeb-Edu, open-web-math, finemath, and
peS2o. Their respective licenses apply to their content.