anandselvadurai-techus commited on
Commit
ff59975
·
verified ·
1 Parent(s): 933698f

Upload 10 files

Browse files
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tech.us
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,3 +1,145 @@
1
  ---
 
 
2
  license: mit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ language:
3
+ - en
4
  license: mit
5
+ library_name: transformers
6
+ pipeline_tag: text-generation
7
+ tags:
8
+ - text-generation
9
+ - causal-lm
10
+ - custom-architecture
11
+ - custom_code
12
+ - slm
13
+ - small-language-model
14
+ datasets:
15
+ - HuggingFaceFW/fineweb-edu
16
+ - HuggingFaceTB/cosmopedia
17
+ - agentlans/high-quality-english-sentences
18
+ - nampdn-ai/tiny-strange-textbooks
19
+ - armanc/ScienceQA
20
+ - nvidia/OpenMathInstruct-2
21
+ - microsoft/orca-math-word-problems-200k
22
  ---
23
+
24
+ # Tokle-3M
25
+
26
+ ## Model Summary
27
+
28
+ Tokle-3M is a decoder-only language model with 2.91M parameters. It was first trained on 12B tokens with SPAB (Static Pairwise Attention Bias), a frozen table of 8.39M token-pair association scores built from Pointwise Mutual Information (PMI) over the training corpus, giving 11.3M parameters in total during this stage. During training, for every query-key pair, SPAB hashed the two token IDs into the table, retrieved their PMI value, scaled it by a learned per-head factor, and added it to the attention logits before softmax.
29
+
30
+ After this stage, the SPAB table was removed and the model was trained for an additional 0.5B tokens to distill the knowledge in the SPAB matrix into its own layers. As a result, Tokle-3M runs entirely on its 2.91M parameters at inference, with no SPAB table required.
31
+
32
+ ## Model Architecture
33
+
34
+ | Parameter | Value |
35
+ |---|---|
36
+ | Architecture | Decoder-only transformer (RMSNorm, RoPE, GQA, SwiGLU) |
37
+ | Layers | 9 |
38
+ | Hidden size (d_model) | 144 |
39
+ | Attention heads | 3 |
40
+ | KV heads (GQA) | 1 (multi-query attention) |
41
+ | Head dim | 48 |
42
+ | FFN intermediate size | 432 |
43
+ | Max sequence length | 512 |
44
+ | Tie word embeddings | Yes |
45
+ | Precision | FP32 weights |
46
+ | Parameters | 2.91M |
47
+
48
+ ## How to use
49
+
50
+ ```python
51
+ import torch
52
+ from transformers import AutoModelForCausalLM, AutoTokenizer
53
+
54
+ model_id = "techdotus/Tokle-3M"
55
+ tok = AutoTokenizer.from_pretrained(model_id)
56
+ model = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True).eval()
57
+
58
+ ids = tok("The climate change", return_tensors="pt")
59
+ with torch.no_grad():
60
+ out = model.generate(**ids, max_new_tokens=32, do_sample=False,
61
+ repetition_penalty=1.3) # greedy
62
+ print(tok.decode(out[0], skip_special_tokens=True))
63
+ ```
64
+
65
+ ## Benchmark Results
66
+
67
+ All scores are 0-shot acc_norm, using the Open SLM Leaderboard methodology.
68
+
69
+ | HellaSwag | ARC-Easy | ARC-Challenge | PIQA | ArithMark-3 |
70
+ |---|---|---|---|---|
71
+ | 27.20% | 34.89% | 23.98% | 55.01% | 41.00% |
72
+
73
+ ### Ablation: SPAB vs. Distilled
74
+
75
+ Stage 1 model (SPAB active) vs. the released Tokle-3M (SPAB removed and distilled).
76
+
77
+ | Model | Params | Int Index | HellaSwag | ARC-Easy | ARC-Chal | PIQA | ArithMark-3 |
78
+ |---|---|---|---|---|---|---|---|
79
+ | Tokle-SPAB-11.3M (with SPAB) | 11.3M (2.91M trainable + 8.39M frozen) | 9.16 | 27.22% | 34.68% | 24.49% | 54.95% | 41.70% |
80
+ | Tokle-3M (SPAB distilled) | 2.91M | 8.96 | 27.20% | 34.89% | 23.98% | 55.01% | 41.00% |
81
+
82
+ ## Comparison Results
83
+
84
+ All scores are 0-shot acc_norm, using the Open SLM Leaderboard methodology. Scores for the other models are from the Open SLM Leaderboard. Bold marks the best result in each column.
85
+
86
+ | Model | Params | Int Index | HellaSwag | ARC-Easy | ARC-Chal | PIQA | ArithMark-3 |
87
+ |---|---|---|---|---|---|---|---|
88
+ | Tokle-3M (Tech.us) | 2.91M | **8.96** | 27.20% | **34.89%** | **23.98%** | 55.01% | **41.00%** |
89
+ | Ember-2 (SurjoLabs) | 2.96M×2 | 7.21 | 27.28% | 33.42% | 22.01% | 55.11% | 35.90% |
90
+ | BananaMind-2-Micro (BananaMind) | 2.9M | 6.01 | **28.27%** | 33.12% | 21.93% | 53.21% | 34.00% |
91
+ | GPT-S-1.4M (Axiomic Labs) | 1.4M | 5.40 | 26.89% | 31.57% | 21.93% | **55.17%** | 30.20% |
92
+
93
+ ## Training Details
94
+
95
+ Tokle-3M was trained in two stages on the same data mixture.
96
+
97
+ | Stage | Tokens | SPAB | Parameters |
98
+ |---|---|---|---|
99
+ | 1. Pretraining | 12B | Active (frozen PMI table) | 11.3M (2.91M trainable + 8.39M frozen) |
100
+ | 2. Distillation | 0.5B | Removed | 2.91M |
101
+
102
+ Stage 2 lets the trained weights absorb the prior the SPAB table had been providing, so the released model is self-contained rather than losing that knowledge when the table is removed.
103
+
104
+ ## Training Data
105
+
106
+ We trained on a curated mixture with a strict cleaning pipeline that also removed topics not useful for a model of this size.
107
+
108
+ | Source | Percentage |
109
+ |---|---|
110
+ | FineWeb-Edu | 43.1% |
111
+ | Cosmopedia | 24.3% |
112
+ | OpenMathInstruct-2 | 13.5% |
113
+ | Tiny Strange Textbooks | 9.0% |
114
+ | MegaScience (medicine & biology, custom curated) | 5.0% |
115
+ | High-Quality English Sentences | 3.0% |
116
+ | ScienceQA | 1.2% |
117
+ | Orca-Math Word Problems 200k | 0.9% |
118
+ | **Total** | **100%** |
119
+
120
+ - **Tokenizer:** all data was tokenized with the model's 5,048-token BPE tokenizer, and 1% was held out for validation.
121
+ - **Blending:** sources were blended per dataset using the weights above.
122
+
123
+ ## Limitations
124
+
125
+ - **Tiny model:** with 2.91M parameters and 144-dim hidden states, generations are often repetitive, incoherent or factually wrong. The model is a research artifact for studying small-scale LMs, not an assistant.
126
+ - **Short context:** 512 tokens maximum. RoPE tables are not built beyond that length.
127
+ - **English only:** trained on English web, educational, synthetic and math text.
128
+ - **Not instruction-tuned or safety-aligned:** it may reproduce biases present in web data.
129
+
130
+ ## License
131
+
132
+ Model weights and code: MIT.
133
+
134
+ ## Citation
135
+
136
+ ```bibtex
137
+ @misc{tokle2026,
138
+ title = {{Tokle-3M}: Pointwise Mutual Information as a Removable
139
+ Inductive Bias for Self-Attention},
140
+ author = {{Tech.us Team}},
141
+ year = {2026},
142
+ publisher = {Hugging Face},
143
+ howpublished = {\url{https://huggingface.co/techdotus/Tokle-3M}}
144
+ }
145
+ ```
config.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "TokleForCausalLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_tokle.TokleConfig",
7
+ "AutoModel": "modeling_tokle.TokleModel",
8
+ "AutoModelForCausalLM": "modeling_tokle.TokleForCausalLM"
9
+ },
10
+ "bos_token_id": 0,
11
+ "d_model": 144,
12
+ "dtype": "float32",
13
+ "eos_token_id": 1,
14
+ "ffn_hidden": 432,
15
+ "head_dim": 48,
16
+ "mask_padded_vocab_logits": true,
17
+ "max_seq_len": 512,
18
+ "model_type": "tokle",
19
+ "n_head": 3,
20
+ "n_kv_head": 1,
21
+ "n_layer": 9,
22
+ "norm_eps": 1e-05,
23
+ "pad_token_id": 2,
24
+ "real_vocab_size": 5048,
25
+ "rope_theta": 10000.0,
26
+ "transformers_version": "4.57.6",
27
+ "use_cache": false,
28
+ "vocab_size": 5056
29
+ }
configuration_tokle.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tokle config. attribute_map aliases the short field names (n_layer, d_model,
2
+ ...) to the ones Transformers and lm-eval look for.
3
+ """
4
+ from transformers.configuration_utils import PretrainedConfig
5
+
6
+
7
+ class TokleConfig(PretrainedConfig):
8
+ """Plain decoder-only LM: RMSNorm, RoPE, grouped-query attention with
9
+ QK-norm, SwiGLU, tied embeddings. No auxiliary tables or buffers.
10
+
11
+ Two fields are easy to trip over. vocab_size (5056) is the padded embedding
12
+ matrix; real_vocab_size (5048) is what the tokenizer can actually emit, and
13
+ the rows between them are unused filler. use_cache is False because the
14
+ model recomputes the prefix rather than carrying a KV cache.
15
+ """
16
+
17
+ model_type = "tokle"
18
+ keys_to_ignore_at_inference = ["past_key_values"]
19
+
20
+ attribute_map = {
21
+ "num_hidden_layers": "n_layer",
22
+ "hidden_size": "d_model",
23
+ "num_attention_heads": "n_head",
24
+ "num_key_value_heads": "n_kv_head",
25
+ "intermediate_size": "ffn_hidden",
26
+ "max_position_embeddings": "max_seq_len",
27
+ "rms_norm_eps": "norm_eps",
28
+ }
29
+
30
+ def __init__(
31
+ self,
32
+ vocab_size=5056,
33
+ real_vocab_size=5048,
34
+ max_seq_len=512,
35
+ n_layer=9,
36
+ d_model=144,
37
+ n_head=3,
38
+ n_kv_head=1,
39
+ head_dim=48,
40
+ ffn_hidden=432,
41
+ rope_theta=10000.0,
42
+ norm_eps=1e-5,
43
+ mask_padded_vocab_logits=True,
44
+ tie_word_embeddings=True,
45
+ bos_token_id=0,
46
+ eos_token_id=1,
47
+ pad_token_id=2,
48
+ use_cache=False,
49
+ **kwargs,
50
+ ):
51
+ self.vocab_size = vocab_size
52
+ self.real_vocab_size = real_vocab_size
53
+ self.max_seq_len = max_seq_len
54
+ self.n_layer = n_layer
55
+ self.d_model = d_model
56
+ self.n_head = n_head
57
+ self.n_kv_head = n_kv_head
58
+ self.head_dim = head_dim
59
+ self.ffn_hidden = ffn_hidden
60
+ self.rope_theta = rope_theta
61
+ self.norm_eps = norm_eps
62
+ self.mask_padded_vocab_logits = mask_padded_vocab_logits
63
+ self.use_cache = use_cache
64
+ super().__init__(
65
+ tie_word_embeddings=tie_word_embeddings,
66
+ bos_token_id=bos_token_id,
67
+ eos_token_id=eos_token_id,
68
+ pad_token_id=pad_token_id,
69
+ **kwargs,
70
+ )
generation_config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 0,
3
+ "do_sample": true,
4
+ "eos_token_id": 1,
5
+ "max_new_tokens": 64,
6
+ "pad_token_id": 2,
7
+ "temperature": 0.8,
8
+ "top_p": 0.95,
9
+ "transformers_version": "4.57.6",
10
+ "use_cache": false
11
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:79a942536fbe699865c582b5a03f83f7e1a72dd13c71966593882162c1401f17
3
+ size 11646512
modeling_tokle.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tokle: decoder-only transformer with RMSNorm, RoPE, GQA and SwiGLU.
2
+
3
+ Module and parameter names follow the usual Transformers layout.
4
+
5
+ Works on Transformers 4.x and 5.x; the v5 differences are marked inline.
6
+ """
7
+ import math
8
+ from typing import Optional, Tuple, Union
9
+
10
+ import torch
11
+ import torch.nn as nn
12
+ import torch.nn.functional as F
13
+ from transformers import __version__ as _transformers_version
14
+ from transformers.generation import GenerationMixin
15
+ from transformers.modeling_outputs import BaseModelOutput, CausalLMOutput
16
+ from transformers.modeling_utils import PreTrainedModel
17
+
18
+ try: # as a Transformers dynamic module (trust_remote_code)
19
+ from .configuration_tokle import TokleConfig
20
+ except ImportError: # running the files straight out of a checkout
21
+ from configuration_tokle import TokleConfig
22
+
23
+
24
+ # ----------------------------------------------------------------------------- norms and rope
25
+ class TokleRMSNorm(nn.Module):
26
+ def __init__(self, d, eps=1e-5):
27
+ super().__init__()
28
+ self.eps = eps
29
+ self.weight = nn.Parameter(torch.ones(d))
30
+
31
+ def forward(self, x):
32
+ # Always reduce in fp32: in fp16 the mean of squares underflows on the
33
+ # long tail and the norm comes back slightly wrong.
34
+ dt = x.dtype
35
+ x = x.float()
36
+ x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
37
+ return (x * self.weight.float()).to(dt)
38
+
39
+ def extra_repr(self):
40
+ return f"{tuple(self.weight.shape)}, eps={self.eps}"
41
+
42
+
43
+ def build_rope(head_dim, max_seq_len, theta, device=None):
44
+ inv = 1.0 / (theta ** (torch.arange(0, head_dim, 2, dtype=torch.float32,
45
+ device=device) / head_dim))
46
+ t = torch.arange(max_seq_len, dtype=torch.float32, device=device)
47
+ f = torch.outer(t, inv)
48
+ return torch.cos(f), torch.sin(f)
49
+
50
+
51
+ def apply_rope(x, cos, sin):
52
+ """Half-split rotation (first half against second), not the interleaved
53
+ variant. The two are not interchangeable -- swapping them degrades output
54
+ without raising anything."""
55
+ b, h, t, d = x.shape
56
+ x1, x2 = x.float().chunk(2, dim=-1)
57
+ c = cos[:t].view(1, 1, t, d // 2).to(x1.device)
58
+ s = sin[:t].view(1, 1, t, d // 2).to(x1.device)
59
+ return torch.cat([x1 * c - x2 * s, x1 * s + x2 * c], dim=-1).to(x.dtype)
60
+
61
+
62
+ # ----------------------------------------------------------------------------- blocks
63
+ class TokleAttention(nn.Module):
64
+ def __init__(self, config: TokleConfig, layer_idx: int):
65
+ super().__init__()
66
+ self.layer_idx = layer_idx
67
+ self.n_head = config.n_head
68
+ self.n_kv_head = config.n_kv_head
69
+ self.head_dim = config.head_dim
70
+ self.rep = config.n_head // config.n_kv_head
71
+ self.q_proj = nn.Linear(config.d_model, config.n_head * config.head_dim, bias=False)
72
+ self.k_proj = nn.Linear(config.d_model, config.n_kv_head * config.head_dim, bias=False)
73
+ self.v_proj = nn.Linear(config.d_model, config.n_kv_head * config.head_dim, bias=False)
74
+ self.o_proj = nn.Linear(config.n_head * config.head_dim, config.d_model, bias=False)
75
+ self.q_norm = TokleRMSNorm(config.head_dim, config.norm_eps)
76
+ self.k_norm = TokleRMSNorm(config.head_dim, config.norm_eps)
77
+
78
+ def forward(self, x, cos, sin, attn_mask):
79
+ b, t, _ = x.shape
80
+ q = self.q_proj(x).view(b, t, self.n_head, self.head_dim).transpose(1, 2)
81
+ k = self.k_proj(x).view(b, t, self.n_kv_head, self.head_dim).transpose(1, 2)
82
+ v = self.v_proj(x).view(b, t, self.n_kv_head, self.head_dim).transpose(1, 2)
83
+ # QK-norm is applied before rope.
84
+ q = apply_rope(self.q_norm(q), cos, sin)
85
+ k = apply_rope(self.k_norm(k), cos, sin)
86
+ k = k.repeat_interleave(self.rep, dim=1)
87
+ v = v.repeat_interleave(self.rep, dim=1)
88
+ if attn_mask is None:
89
+ # Nothing padded: let SDPA build the causal mask itself, which is
90
+ # the fast kernel path and what an unpadded forward always hits.
91
+ o = F.scaled_dot_product_attention(q, k, v, is_causal=True)
92
+ else:
93
+ o = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask.to(q.dtype))
94
+ return self.o_proj(o.transpose(1, 2).reshape(b, t, -1))
95
+
96
+
97
+ class TokleMLP(nn.Module):
98
+ def __init__(self, config: TokleConfig):
99
+ super().__init__()
100
+ self.gate_proj = nn.Linear(config.d_model, config.ffn_hidden, bias=False)
101
+ self.up_proj = nn.Linear(config.d_model, config.ffn_hidden, bias=False)
102
+ self.down_proj = nn.Linear(config.ffn_hidden, config.d_model, bias=False)
103
+
104
+ def forward(self, x):
105
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
106
+
107
+
108
+ class TokleDecoderLayer(nn.Module):
109
+ def __init__(self, config: TokleConfig, layer_idx: int):
110
+ super().__init__()
111
+ self.input_layernorm = TokleRMSNorm(config.d_model, config.norm_eps)
112
+ self.self_attn = TokleAttention(config, layer_idx)
113
+ self.post_attention_layernorm = TokleRMSNorm(config.d_model, config.norm_eps)
114
+ self.mlp = TokleMLP(config)
115
+
116
+ def forward(self, x, cos, sin, attn_mask):
117
+ x = x + self.self_attn(self.input_layernorm(x), cos, sin, attn_mask)
118
+ return x + self.mlp(self.post_attention_layernorm(x))
119
+
120
+
121
+ # ----------------------------------------------------------------------------- model
122
+ class ToklePreTrainedModel(PreTrainedModel):
123
+ config_class = TokleConfig
124
+ base_model_prefix = "model"
125
+ supports_gradient_checkpointing = True
126
+ _no_split_modules = ["TokleDecoderLayer"]
127
+ _supports_sdpa = True
128
+
129
+ def _init_weights(self, module):
130
+ if isinstance(module, nn.Linear):
131
+ nn.init.normal_(module.weight, 0.0, 0.02)
132
+ if module.bias is not None:
133
+ nn.init.zeros_(module.bias)
134
+ elif isinstance(module, nn.Embedding):
135
+ nn.init.normal_(module.weight, 0.0, 0.02)
136
+ elif isinstance(module, TokleRMSNorm):
137
+ nn.init.ones_(module.weight)
138
+ # Residual-output projections get scaled down by depth so the residual
139
+ # stream does not blow up at init (GPT-2 trick).
140
+ if isinstance(module, (TokleAttention, TokleMLP)):
141
+ out = module.o_proj if isinstance(module, TokleAttention) else module.down_proj
142
+ nn.init.normal_(out.weight, 0.0, 0.02 / math.sqrt(2 * self.config.n_layer))
143
+
144
+
145
+ class TokleModel(ToklePreTrainedModel):
146
+ def __init__(self, config: TokleConfig):
147
+ super().__init__(config)
148
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.d_model)
149
+ self.layers = nn.ModuleList(
150
+ [TokleDecoderLayer(config, i) for i in range(config.n_layer)])
151
+ self.norm = TokleRMSNorm(config.d_model, config.norm_eps)
152
+ # A plain dict, not a buffer. Transformers 5 builds models on the meta
153
+ # device and fills only tensors the weights file contains, so a
154
+ # non-persistent buffer would come back as uninitialised memory.
155
+ # Keyed by device, so .to()/.cuda() need not move anything.
156
+ self._rope_cache = {}
157
+ self.gradient_checkpointing = False
158
+ self.post_init()
159
+
160
+ def get_input_embeddings(self):
161
+ return self.embed_tokens
162
+
163
+ def set_input_embeddings(self, value):
164
+ self.embed_tokens = value
165
+
166
+ def rope(self, device):
167
+ key = str(device)
168
+ if key not in self._rope_cache:
169
+ self._rope_cache[key] = build_rope(
170
+ self.config.head_dim, self.config.max_seq_len,
171
+ self.config.rope_theta, device=device)
172
+ return self._rope_cache[key]
173
+
174
+ def _causal_pad_mask(self, attention_mask, t, dtype, device):
175
+ """Additive (b, 1, 1, t) padding mask folded together with the causal
176
+ mask, or None when nothing is padded so the caller can take the
177
+ is_causal fast path."""
178
+ if attention_mask is None:
179
+ return None
180
+ attention_mask = attention_mask.to(torch.bool)
181
+ if bool(attention_mask.all()):
182
+ return None
183
+ m = torch.zeros(attention_mask.shape, dtype=dtype, device=device)
184
+ m = m.masked_fill(~attention_mask, torch.finfo(dtype).min)[:, None, None, :]
185
+ # Under causal attention with right padding, position 0 always sees
186
+ # itself, so no row ends up fully masked (which would give NaNs).
187
+ causal = torch.ones(t, t, dtype=torch.bool, device=device).triu(1)
188
+ return m.masked_fill(causal, torch.finfo(dtype).min)
189
+
190
+ def forward(
191
+ self,
192
+ input_ids: Optional[torch.LongTensor] = None,
193
+ attention_mask: Optional[torch.Tensor] = None,
194
+ inputs_embeds: Optional[torch.FloatTensor] = None,
195
+ output_hidden_states: Optional[bool] = None,
196
+ return_dict: Optional[bool] = None,
197
+ **kwargs,
198
+ ) -> Union[Tuple, BaseModelOutput]:
199
+ # getattr, not config.use_return_dict: that property is deprecated in
200
+ # Transformers 5 and warns on every call.
201
+ return_dict = (return_dict if return_dict is not None
202
+ else getattr(self.config, "return_dict", True))
203
+ output_hidden_states = (output_hidden_states if output_hidden_states is not None
204
+ else self.config.output_hidden_states)
205
+ if (input_ids is None) == (inputs_embeds is None):
206
+ raise ValueError("Pass exactly one of input_ids or inputs_embeds.")
207
+
208
+ x = self.embed_tokens(input_ids) if inputs_embeds is None else inputs_embeds
209
+ t = x.shape[1]
210
+ if t > self.config.max_seq_len:
211
+ raise ValueError(
212
+ f"Sequence length {t} exceeds max_seq_len {self.config.max_seq_len}.")
213
+
214
+ cos, sin = self.rope(x.device)
215
+ mask = self._causal_pad_mask(attention_mask, t, x.dtype, x.device)
216
+
217
+ hidden_states = () if output_hidden_states else None
218
+ for layer in self.layers:
219
+ if output_hidden_states:
220
+ hidden_states += (x,)
221
+ if self.gradient_checkpointing and self.training:
222
+ x = self._gradient_checkpointing_func(layer.__call__, x, cos, sin, mask)
223
+ else:
224
+ x = layer(x, cos, sin, mask)
225
+ x = self.norm(x)
226
+ if output_hidden_states:
227
+ hidden_states += (x,)
228
+
229
+ if not return_dict:
230
+ return tuple(v for v in (x, hidden_states) if v is not None)
231
+ return BaseModelOutput(last_hidden_state=x, hidden_states=hidden_states)
232
+
233
+
234
+ # Transformers 5 turned _tied_weights_keys from a list of names into a
235
+ # {tied: source} mapping and calls .keys() on it, so the old form raises.
236
+ _TRANSFORMERS_V5 = int(_transformers_version.split(".")[0]) >= 5
237
+
238
+
239
+ class TokleForCausalLM(ToklePreTrainedModel, GenerationMixin):
240
+ _tied_weights_keys = ({"lm_head.weight": "model.embed_tokens.weight"}
241
+ if _TRANSFORMERS_V5 else ["lm_head.weight"])
242
+
243
+ def __init__(self, config: TokleConfig):
244
+ super().__init__(config)
245
+ self.model = TokleModel(config)
246
+ self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False)
247
+ self.post_init()
248
+
249
+ def get_input_embeddings(self):
250
+ return self.model.embed_tokens
251
+
252
+ def set_input_embeddings(self, value):
253
+ self.model.embed_tokens = value
254
+
255
+ def get_output_embeddings(self):
256
+ return self.lm_head
257
+
258
+ def set_output_embeddings(self, new_embeddings):
259
+ self.lm_head = new_embeddings
260
+
261
+ def get_decoder(self):
262
+ return self.model
263
+
264
+ def forward(
265
+ self,
266
+ input_ids: Optional[torch.LongTensor] = None,
267
+ attention_mask: Optional[torch.Tensor] = None,
268
+ inputs_embeds: Optional[torch.FloatTensor] = None,
269
+ labels: Optional[torch.LongTensor] = None,
270
+ output_hidden_states: Optional[bool] = None,
271
+ return_dict: Optional[bool] = None,
272
+ **kwargs,
273
+ ) -> Union[Tuple, CausalLMOutput]:
274
+ return_dict = (return_dict if return_dict is not None
275
+ else getattr(self.config, "return_dict", True))
276
+ out = self.model(
277
+ input_ids=input_ids,
278
+ attention_mask=attention_mask,
279
+ inputs_embeds=inputs_embeds,
280
+ output_hidden_states=output_hidden_states,
281
+ return_dict=True,
282
+ )
283
+ logits = self.lm_head(out.last_hidden_state)
284
+ # Rows past real_vocab_size only pad the matrix to a multiple of 64 and
285
+ # carry no meaning, so keep them out of sampling and out of any
286
+ # log-likelihood an evaluation harness sums.
287
+ rv = self.config.real_vocab_size
288
+ if self.config.mask_padded_vocab_logits and rv is not None and rv < self.config.vocab_size:
289
+ logits = logits.clone()
290
+ logits[..., rv:] = float("-inf")
291
+
292
+ loss = None
293
+ if labels is not None:
294
+ loss = self.loss_function(logits=logits, labels=labels,
295
+ vocab_size=self.config.vocab_size, **kwargs)
296
+
297
+ if not return_dict:
298
+ return (loss, logits) if loss is not None else (logits,)
299
+ return CausalLMOutput(loss=loss, logits=logits, hidden_states=out.hidden_states)
special_tokens_map.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<s>",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "</s>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "pad_token": {
17
+ "content": "<pad>",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ }
23
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
The diff for this file is too large to render. See raw diff