Text Generation
Transformers
Safetensors
thox_microprocessor
thox
tiny-language-model
edge-ai
custom-code
custom_code
Instructions to use Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories
- SGLang
How to use Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories 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 "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories" \ --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": "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories", "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 "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories" \ --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": "Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories with Docker Model Runner:
docker model run hf.co/Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories
Download modeling_thox_microprocessor.py from Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories: direct link, hf CLI and curl.
- Browser
- Download file 15.3 kB
-
https://huggingface.co/Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories/resolve/main/modeling_thox_microprocessor.py
- Command line
-
hf download hf://Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories/modeling_thox_microprocessor.py
-
curl -L -o modeling_thox_microprocessor.py https://huggingface.co/Thox-ai/ThoxMicroprocessor-8M-Base-TinyStories/resolve/main/modeling_thox_microprocessor.py
15.3 kB
| """Hugging Face remote-code implementation of THOX Microprocessor. | |
| This file is uploaded verbatim to the model repository, so it must be | |
| self-contained: it may not import the ``thox_microprocessor`` package. That | |
| duplication is deliberate but dangerous — v0.1 shipped a copy whose submodules | |
| were named ``n1``/``post``/``n2`` while the training package used | |
| ``attn_norm``/``post_attn_norm``/``mixer_norm``, so a published checkpoint would | |
| have loaded with three randomly-initialised norms per layer and no error. | |
| ``tests/test_hub_parity.py`` now asserts strict state-dict compatibility and | |
| logit equality between the two implementations. | |
| Scope note: this reference path recomputes the full prefix for each decoding | |
| step rather than maintaining a KV cache. It is portable across transformers | |
| versions, which matters more for remote code than decode throughput; the | |
| ``thox_microprocessor`` package implements cached decoding for training and | |
| deployment. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| from typing import List, Optional, Tuple, Union | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import PreTrainedModel | |
| from transformers.modeling_outputs import CausalLMOutputWithPast | |
| try: # transformers >= 4.50 stopped mixing GenerationMixin into PreTrainedModel | |
| from transformers import GenerationMixin | |
| except ImportError: # pragma: no cover - older transformers | |
| from transformers.generation import GenerationMixin | |
| from .configuration_thox_microprocessor import ThoxMicroprocessorConfig | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-6) -> None: | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dim)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| variance = x.float().pow(2).mean(dim=-1, keepdim=True) | |
| return x * torch.rsqrt(variance + self.eps).to(dtype=x.dtype) * self.weight | |
| class QKRMSNorm(RMSNorm): | |
| """Per-head Q/K RMS normalization applied before RoPE.""" | |
| def _rope_frequencies(seq_len: int, head_dim: int, theta: float, device) -> Tuple[torch.Tensor, torch.Tensor]: | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device, dtype=torch.float32) / head_dim)) | |
| positions = torch.arange(seq_len, device=device, dtype=torch.float32) | |
| angles = torch.outer(positions, inv_freq) | |
| return angles.cos(), angles.sin() | |
| def _apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor: | |
| even = x[..., 0::2] | |
| odd = x[..., 1::2] | |
| cos = cos[None, None, :, :].to(dtype=x.dtype) | |
| sin = sin[None, None, :, :].to(dtype=x.dtype) | |
| return torch.stack((even * cos - odd * sin, even * sin + odd * cos), dim=-1).flatten(-2) | |
| def _fwht_block(x: torch.Tensor) -> torch.Tensor: | |
| n = x.shape[-1] | |
| if n <= 0 or n & (n - 1): | |
| raise ValueError(f"FWHT input width must be a power of two, got {n}") | |
| y = x | |
| h = 1 | |
| while h < n: | |
| shape = y.shape | |
| y = y.reshape(*shape[:-1], -1, 2, h) | |
| a = y[..., 0, :] | |
| b = y[..., 1, :] | |
| y = torch.cat((a + b, a - b), dim=-1).reshape(*shape) | |
| h *= 2 | |
| return y * (1.0 / math.sqrt(n)) | |
| def _stride_shuffle(x: torch.Tensor, block_size: int) -> torch.Tensor: | |
| nblocks = x.shape[-1] // block_size | |
| return x.reshape(*x.shape[:-1], block_size, nblocks).transpose(-1, -2).reshape(x.shape) | |
| def _stride_unshuffle(x: torch.Tensor, block_size: int) -> torch.Tensor: | |
| nblocks = x.shape[-1] // block_size | |
| return x.reshape(*x.shape[:-1], nblocks, block_size).transpose(-1, -2).reshape(x.shape) | |
| class BlockHadamardMixer(nn.Module): | |
| def __init__(self, dim: int, block_size: int = 64, center: bool = True, shuffle: bool = False) -> None: | |
| super().__init__() | |
| if dim % block_size != 0: | |
| raise ValueError(f"dim ({dim}) must be divisible by block_size ({block_size})") | |
| self.dim = dim | |
| self.block_size = block_size | |
| self.num_blocks = dim // block_size | |
| self.center = center | |
| self.shuffle = shuffle and self.num_blocks > 1 | |
| self.d1 = nn.Parameter(torch.ones(dim)) | |
| self.d2 = nn.Parameter(torch.ones(dim)) | |
| self.d3 = nn.Parameter(torch.full((dim,), 0.02)) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| shape = x.shape | |
| y = x * self.d1 | |
| if self.shuffle: | |
| y = _stride_shuffle(y, self.block_size) | |
| y = y.reshape(*shape[:-1], self.num_blocks, self.block_size) | |
| y = _fwht_block(y) | |
| y = F.silu(y * self.d2.reshape(self.num_blocks, self.block_size)) | |
| if self.center: | |
| y = y - y.mean(dim=-1, keepdim=True) | |
| y = _fwht_block(y).reshape(shape) | |
| if self.shuffle: | |
| y = _stride_unshuffle(y, self.block_size) | |
| return y * self.d3 | |
| class GroupedQueryAttention(nn.Module): | |
| def __init__(self, config: ThoxMicroprocessorConfig) -> None: | |
| super().__init__() | |
| self.config = config | |
| self.head_dim = config.d_model // config.num_heads | |
| kv_dim = config.num_kv_heads * self.head_dim | |
| self.q_proj = nn.Linear(config.d_model, config.d_model, bias=False) | |
| self.k_proj = nn.Linear(config.d_model, kv_dim, bias=False) | |
| self.v_proj = nn.Linear(config.d_model, kv_dim, bias=False) | |
| self.out_proj = nn.Linear(config.d_model, config.d_model, bias=False) | |
| self.head_gate = nn.Linear(config.d_model, config.num_heads, bias=False) | |
| self.q_norm = QKRMSNorm(self.head_dim, config.rms_eps) if config.use_qk_norm else nn.Identity() | |
| self.k_norm = QKRMSNorm(self.head_dim, config.rms_eps) if config.use_qk_norm else nn.Identity() | |
| def forward( | |
| self, | |
| x: torch.Tensor, | |
| cos: torch.Tensor, | |
| sin: torch.Tensor, | |
| attn_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| cfg = self.config | |
| batch, seq_len, _ = x.shape | |
| q = self.q_proj(x).view(batch, seq_len, cfg.num_heads, self.head_dim).transpose(1, 2) | |
| k = self.k_proj(x).view(batch, seq_len, cfg.num_kv_heads, self.head_dim).transpose(1, 2) | |
| v = self.v_proj(x).view(batch, seq_len, cfg.num_kv_heads, self.head_dim).transpose(1, 2) | |
| q = _apply_rope(self.q_norm(q), cos, sin) | |
| k = _apply_rope(self.k_norm(k), cos, sin) | |
| repeats = cfg.num_heads // cfg.num_kv_heads | |
| if repeats > 1: | |
| k = k.repeat_interleave(repeats, dim=1) | |
| v = v.repeat_interleave(repeats, dim=1) | |
| scores = torch.matmul(q, k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim)) | |
| if attn_bias is not None: | |
| scores = scores + attn_bias | |
| probs = F.softmax(scores.float(), dim=-1).to(dtype=q.dtype) | |
| probs = F.dropout(probs, p=cfg.attention_dropout, training=self.training) | |
| context = torch.matmul(probs, v) | |
| gate = torch.sigmoid(self.head_gate(x)).transpose(1, 2).unsqueeze(-1) | |
| context = (context * gate).transpose(1, 2).contiguous().view(batch, seq_len, cfg.d_model) | |
| return self.out_proj(context) | |
| class ThoxMicroBlock(nn.Module): | |
| def __init__(self, config: ThoxMicroprocessorConfig, layer_index: int = 0) -> None: | |
| super().__init__() | |
| self.config = config | |
| self.attn_norm = RMSNorm(config.d_model, config.rms_eps) | |
| self.attn = GroupedQueryAttention(config) | |
| self.post_attn_norm = ( | |
| RMSNorm(config.d_model, config.rms_eps) if config.use_sandwich_norm else nn.Identity() | |
| ) | |
| if config.use_hadamard_mixer: | |
| self.mixer_norm = RMSNorm(config.d_model, config.rms_eps) | |
| self.mixer = BlockHadamardMixer( | |
| config.d_model, | |
| config.hadamard_block_size, | |
| center=config.mixer_center, | |
| shuffle=config.mixer_block_shuffle and (layer_index % 2 == 1), | |
| ) | |
| else: | |
| self.mixer_norm = None | |
| self.mixer = None | |
| if config.use_residual_gate: | |
| self.attn_gate = nn.Parameter(torch.tensor(0.0)) | |
| self.mixer_gate = nn.Parameter(torch.tensor(0.0)) | |
| else: | |
| self.register_parameter("attn_gate", None) | |
| self.register_parameter("mixer_gate", None) | |
| def forward( | |
| self, | |
| x: torch.Tensor, | |
| cos: torch.Tensor, | |
| sin: torch.Tensor, | |
| attn_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| update = self.post_attn_norm(self.attn(self.attn_norm(x), cos, sin, attn_bias)) | |
| if self.attn_gate is not None: | |
| update = torch.sigmoid(self.attn_gate) * update | |
| x = x + update | |
| if self.mixer is not None: | |
| update = self.mixer(self.mixer_norm(x)) | |
| if self.mixer_gate is not None: | |
| update = torch.sigmoid(self.mixer_gate) * update | |
| x = x + update | |
| return x | |
| class ThoxMicroprocessorPreTrainedModel(PreTrainedModel): | |
| config_class = ThoxMicroprocessorConfig | |
| base_model_prefix = "thox_microprocessor" | |
| supports_gradient_checkpointing = False | |
| _supports_cache_class = False | |
| _supports_sdpa = False | |
| _supports_flash_attn_2 = False | |
| # With tied embeddings there is no second tensor to tie, so nothing is | |
| # shared and this stays empty. An empty dict is iterated identically by the | |
| # transformers 4.x (list) and 5.x (mapping) code paths. | |
| _tied_weights_keys: dict = {} | |
| def _init_weights(self, module: nn.Module) -> None: | |
| std = self.config.initializer_range | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, mean=0.0, std=std) | |
| if isinstance(module, nn.Linear) and module.bias is not None: | |
| nn.init.zeros_(module.bias) | |
| elif isinstance(module, RMSNorm): | |
| nn.init.ones_(module.weight) | |
| elif isinstance(module, BlockHadamardMixer): | |
| nn.init.ones_(module.d1) | |
| nn.init.ones_(module.d2) | |
| nn.init.constant_(module.d3, 0.02) | |
| class ThoxMicroprocessorForCausalLM(ThoxMicroprocessorPreTrainedModel, GenerationMixin): | |
| def __init__(self, config: ThoxMicroprocessorConfig) -> None: | |
| super().__init__(config) | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.d_model) | |
| self.embed_dropout = nn.Dropout(config.embedding_dropout) | |
| self.layers = nn.ModuleList( | |
| [ThoxMicroBlock(config, layer_index=i) for i in range(config.num_layers)] | |
| ) | |
| self.final_norm = RMSNorm(config.d_model, config.rms_eps) | |
| self.lm_head = ( | |
| None | |
| if config.tie_word_embeddings | |
| else nn.Linear(config.d_model, config.vocab_size, bias=False) | |
| ) | |
| self.post_init() | |
| # Depth-scaled residual init, applied after post_init so it is not | |
| # overwritten. This must match the training package exactly. | |
| residual_std = config.initializer_range / math.sqrt(2 * config.num_layers) | |
| for layer in self.layers: | |
| nn.init.normal_(layer.attn.out_proj.weight, mean=0.0, std=residual_std) | |
| def get_input_embeddings(self) -> nn.Module: | |
| return self.embed_tokens | |
| def set_input_embeddings(self, value: nn.Module) -> None: | |
| self.embed_tokens = value | |
| def get_output_embeddings(self): | |
| return self.lm_head | |
| def set_output_embeddings(self, value) -> None: | |
| self.lm_head = value | |
| def _output_weight(self) -> torch.Tensor: | |
| return self.embed_tokens.weight if self.lm_head is None else self.lm_head.weight | |
| def _attention_bias(self, batch, seq_len, attention_mask, dtype, device) -> torch.Tensor: | |
| neg = torch.finfo(dtype).min | |
| causal = torch.ones(seq_len, seq_len, dtype=torch.bool, device=device).tril() | |
| bias = torch.where( | |
| causal, | |
| torch.zeros((), dtype=dtype, device=device), | |
| torch.full((), neg, dtype=dtype, device=device), | |
| ).expand(batch, 1, seq_len, seq_len) | |
| if attention_mask is not None: | |
| if attention_mask.ndim != 2 or attention_mask.shape != (batch, seq_len): | |
| raise ValueError( | |
| f"attention_mask must have shape [{batch}, {seq_len}], " | |
| f"got {tuple(attention_mask.shape)}" | |
| ) | |
| pad = torch.where( | |
| attention_mask.to(torch.bool), | |
| torch.zeros((), dtype=dtype, device=device), | |
| torch.full((), neg, dtype=dtype, device=device), | |
| ) | |
| bias = bias + pad[:, None, None, :] | |
| return bias | |
| def forward( | |
| self, | |
| input_ids: Optional[torch.LongTensor] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| inputs_embeds: Optional[torch.FloatTensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| past_key_values: Optional[List[torch.FloatTensor]] = None, | |
| use_cache: Optional[bool] = None, | |
| return_dict: Optional[bool] = None, | |
| **kwargs, | |
| ) -> Union[Tuple, CausalLMOutputWithPast]: | |
| if (input_ids is None) == (inputs_embeds is None): | |
| raise ValueError("provide exactly one of input_ids or inputs_embeds") | |
| return_dict = True if return_dict is None else return_dict | |
| if inputs_embeds is None: | |
| if input_ids.ndim != 2: | |
| raise ValueError( | |
| f"input_ids must have shape [batch, seq_len], got {tuple(input_ids.shape)}" | |
| ) | |
| inputs_embeds = self.embed_tokens(input_ids) | |
| batch, seq_len, _ = inputs_embeds.shape | |
| if seq_len > self.config.max_seq_len: | |
| raise ValueError( | |
| f"sequence length {seq_len} exceeds max_seq_len={self.config.max_seq_len}" | |
| ) | |
| x = self.embed_dropout(inputs_embeds * math.sqrt(self.config.d_model)) | |
| device = x.device | |
| cos, sin = _rope_frequencies(seq_len, self.config.head_dim, self.config.rope_theta, device) | |
| attn_bias = self._attention_bias(batch, seq_len, attention_mask, x.dtype, device) | |
| for layer in self.layers: | |
| x = layer(x, cos, sin, attn_bias) | |
| x = self.final_norm(x) | |
| logits = F.linear(x, self._output_weight()) | |
| loss = None | |
| if labels is not None: | |
| if labels.shape[:2] != (batch, seq_len): | |
| raise ValueError("labels must have shape [batch, seq_len]") | |
| loss = F.cross_entropy( | |
| logits[:, :-1].contiguous().view(-1, self.config.vocab_size).float(), | |
| labels[:, 1:].contiguous().view(-1), | |
| ignore_index=-100, | |
| ) | |
| if not return_dict: | |
| return (loss, logits) if loss is not None else (logits,) | |
| return CausalLMOutputWithPast(loss=loss, logits=logits, past_key_values=None) | |
| def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **kwargs): | |
| # No KV cache: always re-encode the (truncated) prefix. | |
| window = self.config.max_seq_len | |
| input_ids = input_ids[:, -window:] | |
| if attention_mask is not None: | |
| attention_mask = attention_mask[:, -window:] | |
| return {"input_ids": input_ids, "attention_mask": attention_mask, "use_cache": False} | |
| __all__ = [ | |
| "ThoxMicroprocessorConfig", | |
| "ThoxMicroprocessorPreTrainedModel", | |
| "ThoxMicroprocessorForCausalLM", | |
| ] | |