ELBennett commited on
Commit
40de0ff
·
verified ·
1 Parent(s): 202afd9

Add Weevil Transformers model, tokenizer and weights

Browse files
README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ pipeline_tag: text-generation
4
+ tags:
5
+ - weevil
6
+ - gdn2
7
+ - custom_code
8
+ ---
9
+
10
+ # Weevil-10M
11
+
12
+ Pretrained causal language model. 9,894,464 parameters, eight parallel GDN2/SwiGLU blocks,
13
+ and a shared 4,096-token BPE tokenizer. Uses the shared Weevil Transformers implementation.
14
+
15
+ ```python
16
+ from transformers import AutoModelForCausalLM, AutoTokenizer
17
+
18
+ repo = "ELBennett/Weevil-10M"
19
+ tokenizer = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
20
+ model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True)
21
+ inputs = tokenizer("The purpose of this article is", return_tensors="pt")
22
+ output = model.generate(**inputs, max_new_tokens=64, do_sample=True,
23
+ temperature=0.8, top_k=40)
24
+ print(tokenizer.decode(output[0], skip_special_tokens=True))
25
+ ```
26
+
27
+ Requires PyTorch and Transformers (`pip install torch 'transformers>=4.57,<6'`).
28
+ Weights are safetensors; generation uses fixed-size recurrent/convolution caches.
29
+ Source checkpoint: `step-00038147`. This is a continuation model, with no chat template.
config.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "WeevilForCausalLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_weevil.WeevilConfig",
7
+ "AutoModelForCausalLM": "modeling_weevil.WeevilForCausalLM"
8
+ },
9
+ "bos_token_id": 0,
10
+ "conv_kernel": 4,
11
+ "dtype": "float32",
12
+ "embedding_dim": 128,
13
+ "eos_token_id": 0,
14
+ "hidden_size": 256,
15
+ "intermediate_size": 960,
16
+ "model_type": "weevil",
17
+ "norm_eps": 1e-06,
18
+ "num_heads": 8,
19
+ "num_hidden_layers": 8,
20
+ "pad_token_id": 1,
21
+ "qkv_rank": null,
22
+ "tie_word_embeddings": false,
23
+ "transformers_version": "5.20.2",
24
+ "use_cache": true,
25
+ "vocab_size": 4096
26
+ }
configuration_weevil.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Hugging Face configuration shared by every Weevil checkpoint."""
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ class WeevilConfig(PretrainedConfig):
7
+ model_type = "weevil"
8
+
9
+ def __init__(
10
+ self, vocab_size=4096, hidden_size=256, embedding_dim=128,
11
+ intermediate_size=960, num_hidden_layers=8, num_heads=8,
12
+ qkv_rank=None, conv_kernel=4, norm_eps=1e-6, use_cache=True,
13
+ **kwargs,
14
+ ):
15
+ kwargs.setdefault("bos_token_id", 0)
16
+ kwargs.setdefault("eos_token_id", 0)
17
+ kwargs.setdefault("pad_token_id", 1)
18
+ kwargs.setdefault("tie_word_embeddings", False)
19
+ super().__init__(**kwargs)
20
+ self.vocab_size = vocab_size
21
+ self.hidden_size = hidden_size
22
+ self.embedding_dim = embedding_dim
23
+ self.intermediate_size = intermediate_size
24
+ self.num_hidden_layers = num_hidden_layers
25
+ self.num_heads = num_heads
26
+ self.qkv_rank = qkv_rank
27
+ self.conv_kernel = conv_kernel
28
+ self.norm_eps = norm_eps
29
+ self.use_cache = use_cache
30
+ if min(vocab_size, hidden_size, embedding_dim, intermediate_size,
31
+ num_hidden_layers, num_heads, conv_kernel) < 1 or hidden_size < num_heads:
32
+ raise ValueError("Weevil dimensions must be positive; heads cannot exceed hidden size")
33
+ if qkv_rank is not None and not 1 <= qkv_rank <= hidden_size:
34
+ raise ValueError("qkv_rank must be between 1 and hidden_size")
35
+
36
+
37
+ WeevilConfig.register_for_auto_class()
export_metadata.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "variant": "Weevil-10M",
3
+ "source_checkpoint": "reports/tst-s6-8gpu-10b/step-00038147",
4
+ "source_model_sha256": "e30b2a0277d008bab462bd7ae9aa5b0a8022b7ce1cb5b3b6687a50dde6d4820f",
5
+ "source_step": 38147,
6
+ "parameters": 9894464,
7
+ "model_spec": null,
8
+ "weights_dtype": "float32"
9
+ }
generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 0,
4
+ "eos_token_id": 0,
5
+ "output_attentions": false,
6
+ "output_hidden_states": false,
7
+ "pad_token_id": 1,
8
+ "transformers_version": "5.20.2",
9
+ "use_cache": true
10
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c0147378710e100fa483cacb0a4a2a1b1a55773b09493881259cfa399d44e95
3
+ size 39595600
modeling_weevil.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Portable PyTorch Weevil, with fp32 recurrence and fixed-size generation caches.
2
+
3
+ This reference implementation needs only PyTorch and Transformers, including for
4
+ the latent-QKV variant. Both embedding factors are tied to the vocabulary readout.
5
+ """
6
+
7
+ from typing import ClassVar
8
+
9
+ import torch
10
+ from torch import nn
11
+ from torch.nn import functional as F
12
+ from transformers import GenerationMixin, PreTrainedModel
13
+ from transformers.cache_utils import Cache
14
+ from transformers.modeling_outputs import CausalLMOutputWithPast
15
+
16
+ from .configuration_weevil import WeevilConfig
17
+
18
+
19
+ class WeevilCache(Cache):
20
+ def __init__(self, states=None, seen_tokens=0):
21
+ super().__init__(layers=[])
22
+ self.states = states or []
23
+ self.seen_tokens = seen_tokens
24
+
25
+ def get_seq_length(self, layer_idx=0):
26
+ return self.seen_tokens
27
+
28
+ def reorder_cache(self, beam_idx):
29
+ self.states = [tuple(x.index_select(0, beam_idx.to(x.device)) for x in state)
30
+ for state in self.states]
31
+
32
+ def batch_repeat_interleave(self, repeats):
33
+ self.states = [tuple(x.repeat_interleave(repeats, dim=0) for x in state)
34
+ for state in self.states]
35
+
36
+
37
+ class ZCRMSNorm(nn.Module):
38
+ def __init__(self, width, eps):
39
+ super().__init__()
40
+ self.scale = nn.Parameter(torch.zeros(width))
41
+ self.eps = eps
42
+
43
+ def forward(self, x):
44
+ x = x.float()
45
+ return (1 + self.scale.float()) * x * torch.rsqrt(x.square().mean(-1, keepdim=True) + self.eps)
46
+
47
+
48
+ class TiedEmbedding(nn.Module):
49
+ def __init__(self, c):
50
+ super().__init__()
51
+ self.embed = nn.Embedding(c.vocab_size, c.embedding_dim)
52
+ self.proj = nn.Linear(c.embedding_dim, c.hidden_size, bias=False)
53
+
54
+ def forward(self, ids):
55
+ return self.proj(self.embed(ids))
56
+
57
+ def token_logits(self, x):
58
+ x = F.linear(x.to(self.proj.weight.dtype), self.proj.weight.T)
59
+ return F.linear(x, self.embed.weight).float()
60
+
61
+
62
+ class CausalConv(nn.Module):
63
+ def __init__(self, channels, kernel):
64
+ super().__init__()
65
+ self.weight = nn.Parameter(torch.empty(channels, kernel))
66
+ nn.init.normal_(self.weight, std=0.02)
67
+
68
+ def step(self, x, history, valid):
69
+ taps = torch.cat((history, x.float().unsqueeze(1)), dim=1)
70
+ output = (taps * self.weight.float().T).sum(1)
71
+ history = torch.where(valid[:, None, None], taps[:, 1:], history)
72
+ return F.silu(output.to(x.dtype)), history
73
+
74
+
75
+ class GDN2Mixer(nn.Module):
76
+ def __init__(self, c):
77
+ super().__init__()
78
+ self.config = c
79
+ hidden, head = c.hidden_size, c.hidden_size // c.num_heads
80
+ width = c.num_heads * head
81
+
82
+ def linear(i, o, bias=False):
83
+ return nn.Linear(i, o, bias=bias)
84
+
85
+ if c.qkv_rank is None:
86
+ self.q_proj, self.k_proj, self.v_proj = [linear(hidden, width) for _ in range(3)]
87
+ else:
88
+ self.qkv_a = linear(hidden, c.qkv_rank)
89
+ self.q_b, self.k_b, self.v_b = [linear(c.qkv_rank, width) for _ in range(3)]
90
+ self.q_conv, self.k_conv, self.v_conv = [CausalConv(width, c.conv_kernel) for _ in range(3)]
91
+ self.f_in, self.f_out = linear(hidden, head), linear(head, width)
92
+ self.b_proj, self.w_proj = linear(hidden, width), linear(hidden, width)
93
+ self.g_in, self.g_out = linear(hidden, head), linear(head, width, bias=True)
94
+ self.o_proj = linear(width, hidden)
95
+ self.o_norm = nn.Parameter(torch.ones(head))
96
+ self.A_log = nn.Parameter(torch.zeros(c.num_heads))
97
+ self.dt_bias = nn.Parameter(torch.zeros(width))
98
+
99
+ def forward(self, x, valid, state=None):
100
+ c = self.config
101
+ batch, length, _ = x.shape
102
+ head = c.hidden_size // c.num_heads
103
+ width = c.num_heads * head
104
+ if state is None:
105
+ recurrent = torch.zeros(batch, c.num_heads, head, head, device=x.device)
106
+ histories = [torch.zeros(batch, c.conv_kernel - 1, width, device=x.device) for _ in range(3)]
107
+ else:
108
+ recurrent, *histories = state
109
+ if c.qkv_rank is None:
110
+ projections = [self.q_proj(x), self.k_proj(x), self.v_proj(x)]
111
+ else:
112
+ latent = self.qkv_a(x)
113
+ projections = [self.q_b(latent), self.k_b(latent), self.v_b(latent)]
114
+ shape = (batch, length, c.num_heads, head)
115
+ decay = -self.A_log.float().exp()[None, None, :, None] * F.softplus(
116
+ self.f_out(self.f_in(x)).float() + self.dt_bias.float()).reshape(shape)
117
+ erase = self.b_proj(x).sigmoid().float().reshape(shape)
118
+ write = self.w_proj(x).sigmoid().float().reshape(shape)
119
+ outputs = []
120
+ for t in range(length):
121
+ active = valid[:, t]
122
+ qkv = []
123
+ for i, conv in enumerate((self.q_conv, self.k_conv, self.v_conv)):
124
+ value, histories[i] = conv.step(projections[i][:, t], histories[i], active)
125
+ qkv.append(value.float().reshape(batch, c.num_heads, head))
126
+ q, k, v = qkv
127
+ q = q * torch.rsqrt(q.square().sum(-1, keepdim=True) + 1e-6) * head ** -0.5
128
+ k = k * torch.rsqrt(k.square().sum(-1, keepdim=True) + 1e-6)
129
+ decayed = recurrent * decay[:, t].exp().unsqueeze(-1)
130
+ erased = ((erase[:, t] * k).unsqueeze(-1) * decayed).sum(-2)
131
+ updated = decayed + k.unsqueeze(-1) * (write[:, t] * v - erased).unsqueeze(-2)
132
+ recurrent = torch.where(active[:, None, None, None], updated, recurrent)
133
+ outputs.append((q.unsqueeze(-1) * recurrent).sum(-2))
134
+ output = torch.stack(outputs, dim=1)
135
+ output = output * torch.rsqrt(output.square().mean(-1, keepdim=True) + c.norm_eps)
136
+ gate = self.g_out(self.g_in(x)).float().reshape(shape).sigmoid()
137
+ output = output * self.o_norm.float() * gate
138
+ output = self.o_proj(output.flatten(-2).to(x.dtype))
139
+ return output.masked_fill(~valid[..., None], 0), (recurrent, *histories)
140
+
141
+
142
+ class SwiGLU(nn.Module):
143
+ def __init__(self, c):
144
+ super().__init__()
145
+ self.gate_proj = nn.Linear(c.hidden_size, c.intermediate_size, bias=False)
146
+ self.up_proj = nn.Linear(c.hidden_size, c.intermediate_size, bias=False)
147
+ self.down_proj = nn.Linear(c.intermediate_size, c.hidden_size, bias=False)
148
+
149
+ def forward(self, x):
150
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
151
+
152
+
153
+ class WeevilLayer(nn.Module):
154
+ def __init__(self, c):
155
+ super().__init__()
156
+ self.norm = ZCRMSNorm(c.hidden_size, c.norm_eps)
157
+ self.mixer = GDN2Mixer(c)
158
+ self.mlp = SwiGLU(c)
159
+
160
+ def forward(self, x, valid, state):
161
+ z = self.norm(x).to(self.mixer.o_proj.weight.dtype)
162
+ mixed, state = self.mixer(z, valid, state)
163
+ x = x.float() + mixed.float() + self.mlp(z).float()
164
+ return x.masked_fill(~valid[..., None], 0), state
165
+
166
+
167
+ class WeevilForCausalLM(PreTrainedModel, GenerationMixin):
168
+ config_class = WeevilConfig
169
+ base_model_prefix = "weevil"
170
+ main_input_name = "input_ids"
171
+ _no_split_modules: ClassVar[list[str]] = ["WeevilLayer"]
172
+
173
+ def __init__(self, config):
174
+ super().__init__(config)
175
+ self.embedding = TiedEmbedding(config)
176
+ self.decoder = nn.ModuleDict({"layers": nn.ModuleList([
177
+ WeevilLayer(config) for _ in range(config.num_hidden_layers)
178
+ ])})
179
+ self.final_norm = ZCRMSNorm(config.hidden_size, config.norm_eps)
180
+ self.post_init()
181
+
182
+ @classmethod
183
+ def _supports_default_dynamic_cache(cls):
184
+ return False
185
+
186
+ def get_input_embeddings(self):
187
+ return self.embedding.embed
188
+
189
+ def set_input_embeddings(self, value):
190
+ self.embedding.embed = value
191
+
192
+ def prepare_inputs_for_generation(self, input_ids, past_key_values=None,
193
+ attention_mask=None, use_cache=True, **kwargs):
194
+ if past_key_values is not None and past_key_values.get_seq_length():
195
+ input_ids = input_ids[:, past_key_values.get_seq_length():]
196
+ return {"input_ids": input_ids, "attention_mask": attention_mask,
197
+ "past_key_values": past_key_values, "use_cache": use_cache}
198
+
199
+ def forward(self, input_ids=None, attention_mask=None, past_key_values=None,
200
+ inputs_embeds=None, labels=None, use_cache=None, return_dict=None,
201
+ token_type_ids=None, **kwargs):
202
+ if (input_ids is None) == (inputs_embeds is None):
203
+ raise ValueError("Provide exactly one of input_ids or inputs_embeds")
204
+ x = self.embedding(input_ids) if inputs_embeds is None else inputs_embeds
205
+ batch, length = x.shape[:2]
206
+ valid = (torch.ones(batch, length, dtype=torch.bool, device=x.device)
207
+ if attention_mask is None else attention_mask[:, -length:].to(device=x.device, dtype=torch.bool))
208
+ x = x.float().masked_fill(~valid[..., None], 0)
209
+ use_cache = self.config.use_cache if use_cache is None else use_cache
210
+ if past_key_values is not None and not isinstance(past_key_values, WeevilCache):
211
+ raise ValueError("Weevil requires a WeevilCache, not an attention KV cache")
212
+ states = []
213
+ for i, layer in enumerate(self.decoder["layers"]):
214
+ state = past_key_values.states[i] if past_key_values is not None and past_key_values.states else None
215
+ x, state = layer(x, valid, state)
216
+ states.append(state)
217
+ logits = self.embedding.token_logits(self.final_norm(x))
218
+ logits[..., self.config.pad_token_id] = -1e9
219
+ logits = logits.masked_fill(~valid[..., None], 0)
220
+ loss = None
221
+ if labels is not None:
222
+ targets = labels[:, 1:].to(logits.device).clone()
223
+ targets.masked_fill_(~valid[:, 1:], -100)
224
+ loss = F.cross_entropy(logits[:, :-1].reshape(-1, self.config.vocab_size),
225
+ targets.reshape(-1), ignore_index=-100)
226
+ seen = 0 if past_key_values is None else past_key_values.get_seq_length()
227
+ cache = WeevilCache(states, seen + length) if use_cache else None
228
+ if return_dict is False:
229
+ result = (logits, cache) if use_cache else (logits,)
230
+ return (loss, *result) if loss is not None else result
231
+ return CausalLMOutputWithPast(loss=loss, logits=logits, past_key_values=cache)
232
+
233
+
234
+ WeevilForCausalLM.register_for_auto_class("AutoModelForCausalLM")
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "<|endoftext|>",
4
+ "clean_up_tokenization_spaces": false,
5
+ "eos_token": "<|endoftext|>",
6
+ "mask_token": "<|mask|>",
7
+ "model_input_names": [
8
+ "input_ids",
9
+ "attention_mask"
10
+ ],
11
+ "model_max_length": 32768,
12
+ "pad_token": "<|padding|>",
13
+ "padding_side": "left",
14
+ "tokenizer_class": "PreTrainedTokenizerFast"
15
+ }