Tokenizers and Text Tensors #
Text and NLP helpers for TorchLean examples.
Language models may keep token ids as Nat tensors and gather embedding rows directly. Small
examples can instead use one-hot tensors of shape
(batchSize × sequenceLength × vocabularySize). Both representations
remain separate from floating-point model parameters at the API boundary.
This module provides:
- a tokenizer interface (with a byte-level tokenizer),
- helpers to turn token streams into one-hot tensors,
- “next-token prediction” sample builders used by GPT-style examples,
- display helpers for turning model logits back into readable token predictions.
Tokenizers #
Tokenizer interface (encode/decode).
- vocabularySize : ℕ
Vocabulary size (token ids are expected to be in
[0, vocabularySize)). Encode a string into a variable-length token buffer.
Decode a variable-length token buffer back into a string.
Instances For
Byte-level UTF-8 tokenizer: each byte is one token in $[0,256)$.
Decoding uses UTF-8 when possible and falls back to byte-wise display for generated streams that are not valid UTF-8.
Example:
-- 256 tokens, no vocabulary file, nothing to train: this is where every text example starts.
def tokenizer : text.Tokenizer := text.Tokenizer.byte
-- Encode then decode returns the original string whenever it was valid UTF-8.
def roundTrip (line : String) : String :=
tokenizer.decode (tokenizer.encode line)
Instances For
Build a character-level tokenizer from an explicit alphabet.
The resulting encode/decode pair has the same role as the stoi/itos tables used in
character-level GPT examples: encode maps characters to ids
0..alphabet.size-1, and decode maps ids back to characters.
The unknownTokenId argument proves that the alphabet is nonempty and identifies the token used for
a character outside the alphabet. Ids outside [0, alphabet.size) decode to unknownCharacter.
Repeated characters in the alphabet encode to their first index. A lookup table is shared across
calls to the returned encoder.
Example:
-- The `stoi` and `itos` tables of character-level GPT tutorials, with the nonempty-alphabet
-- requirement carried by the unknown-token index instead of a runtime assertion.
def alphabet : Array Char := #['a', 'b', 'c', ' ']
def tokenizer : text.Tokenizer :=
text.Tokenizer.fromAlphabet alphabet ⟨3, by decide⟩ (unknownCharacter := '?')
Instances For
Encode a string and pad or truncate it to exactly sequenceLength token ids.
Example:
-- Padded or truncated to the length the model expects, so the result carries a shape rather than
-- a length a caller has to check.
def tokens : Tensor Nat [16] :=
text.Tokenizer.byte.encodeFixed 16 "hello world"
Instances For
Encode exactly batchSize strings, padding or truncating each row to
sequenceLength token ids.
Example:
-- Two prompts become one `[2, 16]` batch, ready for a model with a batch axis in front.
def batch : Tensor Nat [2, 16] :=
text.Tokenizer.byte.encodeFixedBatch 16 ["hello", "world"]
Instances For
Byte-Corpus Windows #
Read one byte token from a raw corpus, returning paddingTokenId past the end.
This is byte-level rather than BPE-level: examples can train causal language models directly from a
text file without depending on an external tokenizer artifact. GPT-2 BPE support lives in
NN.API.Text.Bpe.
Lives in Internal on purpose: byteTokenWindow below is the only caller, and a padded
single-byte read is not something a user of text should have to reason about. (private is not an
option here. Every API module is inside @[expose] public section, so a private helper cannot be
named from an exposed body; a nested Internal namespace is how the rest of the codebase says
"plumbing".)
Instances For
Extract a fixed-length byte-token window from a raw corpus.
offset is measured in bytes, as required for byte-level causal language modeling. This avoids
hidden UTF-8 slicing assumptions.
Instances For
Corpus Helpers #
Read a UTF-8 text file with a caller-supplied preparation hint.
The examples pass their executable name and a concrete hint so failures point users to the exact download or conversion command for that dataset.
Instances For
Read a raw byte corpus and optionally enforce a minimum size.
allowSmallData is an explicit override for bounded local runs. Corpus-training commands can set
minimumBytes to the scale they expect and require users to acknowledge smaller local files.
Instances For
Parse a text-corpus flag set and return (text, remainingArgs).
Supported forms:
--data-file PATH- any named alias in
aliases, such as("--tiny-shakespeare", path) - no data flag, which uses
defaultPath
Instances For
Number of legal start positions for a (sequenceLength + 1) next-token window.
We return at least one start position so bounded corpora stay total; callers can still enforce a minimum corpus size before training.
Instances For
Deterministic sliding-window offset for a byte corpus.
Instances For
Deterministic minGPT-style random offsets for one training batch.
The result has one corpus start offset per batch row. We derive the
random key from (seed, step) and then draw row offsets by the row index, so the run is
reproducible without using ambient IO randomness. This is the text equivalent of a shuffled
EpochLoader epoch.
Instances For
Build token windows for one deterministic random text batch.
Each row gets sequenceLength + 1 ids so downstream causal-LM helpers can form both the input and
shifted target.
Byte, character, BPE, and synthetic tokenizers share the same tensor batching semantics.
Instances For
Choose training-window offsets, biased toward a prompt occurrence when the corpus contains it.
If the prompt is present in the corpus, a portion of the sampled windows covers nearby text. That keeps generation reports tied to text the model actually saw during training.