Instructions to use episod/tt-tnt with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use episod/tt-tnt with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="episod/tt-tnt")# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("episod/tt-tnt") model = AutoModelForCausalLM.from_pretrained("episod/tt-tnt", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use episod/tt-tnt with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "episod/tt-tnt" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "episod/tt-tnt", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/episod/tt-tnt
- SGLang
How to use episod/tt-tnt 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 "episod/tt-tnt" \ --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": "episod/tt-tnt", "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 "episod/tt-tnt" \ --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": "episod/tt-tnt", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use episod/tt-tnt with Docker Model Runner:
docker model run hf.co/episod/tt-tnt
TT-TNT (tt-tnt)
A ~22M-parameter Llama-3-style language model trained from random initialization on
Tenstorrent Blackhole hardware with ttml (tt-train), then converted to Hugging Face
format and numerically verified.
This is a demonstration of a pipeline, not a capable model. Please read the limitations before using it for anything.
What it demonstrates
That a model can be designed, trained, packaged, and served entirely on Tenstorrent tooling β TT-native from the first line of code rather than ported afterwards. The full build, including every dead end, is documented at tsingletaryTT/tt-tnt.
Model details
| Property | Value |
|---|---|
| Architecture | Llama-3 style β RoPE (ΞΈ=500000), RMSNorm, SwiGLU, grouped-query attention |
| Parameters | 22,025,088 |
| Hidden size | 384 |
| Layers | 6 |
| Attention heads / KV groups | 6 / 3 |
| Context length | 2048 |
| Vocabulary | 32,000 (byte-level BPE, trained for this model) |
| Weights dtype | bfloat16 |
| Training hardware | One Tenstorrent Blackhole chip (mesh_shape [1, 1]) |
Trained on a single Blackhole chip. The host is a TT-QuietBox 2 β four Blackhole chips on two dual-chip p300 cards β but training used one of them; the other three were idle.
The architecture is vendored in this project as
train/configs/model/tt-tnt-384.yaml,
a verbatim copy of tt-train's own nanollama3.yaml.
Training
| Corpus | Nine-source, licence-audited blend β TinyStories, Simple English Wikipedia, and seven curated Project Gutenberg slices (see docs/corpus_blend.md). This is the first tt-tnt checkpoint trained on a blend that carries document separators (</s>) β earlier revisions contained none at all, so no previous checkpoint had ever seen an end-of-document token and none could stop generating. Measured on the token arrays directly: the pre-fix array holds zero id-2 tokens across the whole training split; this one averages one per ~478 tokens (one per ~210 in the short-document sources, one per ~80,000 in the book-length ones). The blend recipe β source registry, fetch/prepare/measure/blend scripts, and the provenance manifest β is published separately as episod/tt-tnt-corpus |
| Tokens seen | 352,714,752 β the full training split, one epoch |
| Steps | 10,764 at batch 16, sequence length 2048 (32,768 tokens/step, unchanged from the previous run's 64Γ512) |
| Wall clock | ~91 minutes on a single Blackhole p300c |
| Final train loss | 3.25 |
| Final validation loss | 2.9937 β the end-of-run figure from train/run.py's evaluate(). The periodic curve's last entry (artifacts/checkpoints-tt-tnt-v3/val_losses.jsonl, step 10,764) reads 2.939; the two differ only by which held-out windows each sampled. Do not read this as an improvement on the previous checkpoint's 4.2203 β see below |
| Optimizer | AdamW, constant lr 3e-4, weight decay 0.01, stochastic_rounding: true |
Limitations β please read these
The headline validation loss is not comparable to the previous checkpoint's. 2.9937 against
4.2203 looks like a large gain, and most of it is not one. The previous checkpoint's validation
split was the tail 10% of a token stream whose sources are concatenated in sorted-name order,
so it landed entirely inside wikipedia_simple β the most out-of-domain source in the
blend, and by per-source measurement the second-hardest (held-out loss 4.28, against
TinyStories' 1.83; see
docs/measurements/per-source-loss-tt-tnt-v1.md).
That number measured domain transfer, not learning. This run's split is stratified by
source β a proportional tail from each of the nine β so it is a fair sample of the training
mixture, and a much easier one, because 31% of the mixture is TinyStories. A meaningful share
of the drop from 4.22 to 2.99 is the yardstick changing, not the model improving. The two
numbers should not be subtracted.
It has seen its training corpus once, not memorized it. At batch 16, sequence length 2048,
10,764 steps is one epoch over the blend's 352.7M-token training split β the same 32,768
tokens per step as the previous run, at four times the context. The validation curve
(artifacts/checkpoints-tt-tnt-v3/val_losses.jsonl) falls from 5.084 at step 500 to ~3.28 by
step 4,000, and then keeps drifting down slowly and noisily β the last ~2,300 steps oscillate
between 2.87 and 3.10 against ~3.12 around step 7,000β8,000. That is a real if modest continued
decline, unlike the previous run, which was flat for its final stretch. Read plainly: this one
had not clearly stopped improving when it ended, but the per-step gain over the last third is
small enough that more steps at these settings would not transform it.
It can now stop, which no previous checkpoint could. Every earlier tt-tnt checkpoint was
trained on a corpus containing zero </s> tokens while its config.json nonetheless
declared eos_token_id: 2 β so generation could never terminate naturally and always ran to
whatever token limit the caller set. This is the first checkpoint trained on a corpus that
marks document boundaries, and it does terminate: on the frozen 15-prompt evaluation set at
128 max new tokens, 5/15 completions end on </s> under greedy decoding (median 48 tokens)
and 11/30 under sampling at temperature 0.8 / top_p 0.95 (median 91). The comparable
numbers for the previous checkpoint are 0/15 and 0/30 β not "rarely", but never, by
construction. This matters most for chained generation, where you want a passage to end rather
than be cut off mid-sentence at the limit.
It is a partial fix, not a solved problem: two thirds of completions still run to the limit, and the model is much readier to stop on short-document material (TinyStories-like prompts) than on the book-length sources, which is what the corpus taught it β separator density in the blend ranges from one per ~210 tokens in the short sources to one per ~80,000 in the books.
The corpus is a nine-source, licence-audited blend, and the model's behavior is a mix to
match. Read against the frozen evaluation set
(docs/measurements/samples-tt-tnt-v3.md,
greedy decoding, 15 prompts): the model sometimes engages with a prompt's own material β sticks,
roses, a procession, bees β where a TinyStories-only baseline would default to a generic moral.
But TinyStories still dominates, and under greedy decoding several prompts still degenerate
into hard repetition loops ("the rose is a rose, and the rose is a rose, and..."; "the bees were
busy, and the bees were busy"). The oblique, observational voice this blend targets β closer to
Fabre's insect notebooks than to a children's story β is not present in this checkpoint.
Under sampling the output is markedly better behaved
(docs/measurements/samples-tt-tnt-v3-t0.8.md),
which is the honest way to read the greedy loops: greedy decoding on a 22M-parameter model
manufactures repetition attractors that sampling largely avoids. Treat the promising examples
as evidence of what the blend can nudge toward, not as evidence the voice has arrived.
It is a base completion model. No instruction tuning, no chat template. Give it the opening of a simple story; do not ask it questions.
Its context is 2048 tokens (256 β 512 β 2048 across the three checkpoints β see Lineage).
Note that tokenizer_config.json carries the conventional model_max_length sentinel
(~1e18). Do not derive a serving length from it β use max_position_embeddings. Serving this
model with a 4k context will silently degrade output; serving it at 512 silently discards three
quarters of the context it was trained to use.
Whether it actually uses all 2048 tokens is a separate question from whether it was trained
at 2048. A position-wise loss probe on the previous checkpoint found per-token loss flat
from position ~64 onward β that model extracted nothing from distant context, because the
corpus had no document boundaries and so distant context genuinely was unpredictable. Fixing
the separators is what makes a longer window worth having, and is why this run raised it. The
equivalent probe for this checkpoint
(docs/measurements/context-use-tt-tnt-v3.md)
shows loss still falling well past where the old one went flat β 4.23 at positions [0,32),
3.28 at [32,64), 2.94 at [64,128), 2.85 at [256,512) β but the improvement past ~256 tokens is
about 0.02 nats per bucket against a standard error of ~0.08, i.e. directionally right and
inside the noise. The honest claim is that the long window is no longer actively useless, not
that all 2048 tokens are earning their keep.
Unlike the original checkpoint, this run's RMSNorm layers did learn. The very first tt-tnt
checkpoint (see Lineage) trained with stochastic_rounding disabled, which silently froze all
13 RMSNorm gammas at bfloat16's rounding fixed-point of 1.0 β the gradients were real, but every
update rounded back to 1.0 and was discarded. This run set stochastic_rounding: true, and it
worked: read directly from the published weights, all 13 gammas have moved off 1.0 and none is
degenerate (per-tensor means 0.874β1.720, per-tensor standard deviation 0.034β0.207, values
spanning roughly 0.691β2.359 across the set; zero tensors have standard deviation 0). The model
trained with its normalization layers genuinely live.
Verification
The Hugging Face conversion is not merely assumed correct. It is checked against an independently derived pure-NumPy reimplementation of ttml's forward pass β written from ttml's C++ source rather than from the converter, so the two paths reach logits by different routes. They agree to a maximum absolute logit difference of ~6e-6 (correlation β 1 β 1e-13).
This mattered: an earlier conversion loaded cleanly, tied its weights correctly, showed sensible
next-token entropy, and generated fluent prose β while computing the wrong function, because of
a RoPE row-layout mismatch worth 1.3 nats. Only numerical comparison caught it. That story, and
the techniques that catch this class of bug, are written up in
docs/model-development-troubleshooting.md.
Usage
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
tok = AutoTokenizer.from_pretrained("episod/tt-tnt")
model = AutoModelForCausalLM.from_pretrained("episod/tt-tnt").eval()
ids = tok("Once upon a time, there was a little", return_tensors="pt").input_ids
with torch.no_grad():
out = model.generate(ids, max_new_tokens=60, do_sample=True, temperature=0.8, top_p=0.95)
print(tok.decode(out[0], skip_special_tokens=True))
Runs on CPU; no Tenstorrent hardware required for inference.
Sample output
Greedy decoding, 60 new tokens, from the frozen evaluation set
(docs/measurements/samples-tt-tnt-v3.md):
Once upon a time, there was a little girl named Lily. She loved to play outside in the park. One day, she saw a big, shiny rock on the ground. She picked it up and showed it to her mom. "Look, Mommy! I found a shiny rock!" she said. Her mom smiled and said, "That
And one that stops on its own rather than being cut off at the limit β the new behavior described in Limitations:
The ants had learned that being eaten was a way of helping others. The moral of the story is that it's important to be kind to others and to help others.
Both are near the top of the frozen set of 15, not typical of it β see Limitations above and
the linked file for the honest range, including the TinyStories collapses to "a little girl
named Lily" and several hard repetition loops. Sampled output at temperature 0.8 is in
docs/measurements/samples-tt-tnt-v3-t0.8.md
and is the more representative read of the model's range.
Licensing and provenance
The model weights and this project's code are Apache-2.0.
The training corpus is not. This checkpoint was trained on the nine-source blend described
above. Two of those sources are share-alike: tinystories
(roneneldan/TinyStories, 31% of the
blend, CDLA-Sharing-1.0) and wikipedia_simple
(wikimedia/wikipedia, 15% of the
blend, CC-BY-SA-3.0). Full per-source licence, attribution, and the pinned dataset revisions
are recorded in
docs/corpus_licensing.md,
which is generated from this project's source registry (train/corpus.py) rather than
hand-written, specifically so this card cannot drift out of sync with it the way hand-written
licensing prose has before. That document's "unsettled Data Derivative" language for
share-alike sources applies to this checkpoint exactly as written there, for both of the
sources named above; this card does not restate it.
The corpus itself is not redistributed here or anywhere else β this repository only ships the
recipe to reconstruct it byte-identically: source registry, pinned revisions, and
fetch/prepare/measure/blend scripts, published as
episod/tt-tnt-corpus on the Hub.
Architectural credit. The component choices β RoPE, RMSNorm, SwiGLU, GQA, subword BPE β
follow Mini-LLM by Ashx098, which the originating lesson
arc credits. That repository declares no license, so it grants no rights; this is a credit, not
a license inheritance. The components come from published papers, and this implementation
derives from tt-train's nanollama3 config and the ttml library, not from Mini-LLM's source.
Lineage
This model was originally published under the name tt-nanollama3, and it is worth being
plain about what changed. It started as a hand-rolled nanollama3-like model β a Llama-3
architecture trained from random initialization with tt-train's ttml trainer, on TinyStories.
The architecture and trainer have not changed: the config above is a verbatim copy of
tt-train's own nanollama3.yaml, and every checkpoint (including the one this card describes)
is trained through ttml against it. What changed is the corpus and tokenizer this project
now owns β a nine-source, licence-audited blend and a BPE tokenizer trained on that blend,
rather than a single downloaded corpus and an inherited vocabulary β and that is what earned
the new name, tt-tnt.
Three checkpoints have now been published under this repo id, and it is worth being plain about which is which, because they differ only in weights and one config field:
| corpus | context | document separators | |
|---|---|---|---|
| the original (TinyStories-only) | TinyStories alone | 256 | none |
| the first blend-trained checkpoint | nine-source blend | 512 | none |
| this one | nine-source blend, separator-carrying revision | 2048 | yes |
The middle checkpoint was the first trained on the blend; this one is the first trained on a
revision of that blend which marks where one document ends and the next begins, and the first
whose declared eos_token_id: 2 corresponds to a token the training data actually contained.
That, rather than the context length, is the substantive difference.
Origin
Built out of the "Build an LLM from Scratch" lesson arc in tt-vscode-toolkit, which builds a Llama-3-style model TT-native from the first line of code. This model takes that arc past where the lessons stop β real training, checkpointing, conversion, and numerical verification.
- Downloads last month
- -