Banaxi-Tech commited on
Commit
a0f30ef
·
verified ·
1 Parent(s): 0ae2d39

Publish BananaMind 2.1 Unified transformers wrapper

Browse files
configuration_bananamind21unified.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class BananaMind21UnifiedConfig(PretrainedConfig):
5
+ """BananaMind 2.1 Unified: three towers, a relay middle, one token out.
6
+
7
+ A and C are the outer towers and each owns an output head. B is the relay:
8
+ no head, no solo loss, and the only path between A and C.
9
+ """
10
+
11
+ model_type = "bananamind21_unified"
12
+
13
+ def __init__(
14
+ self,
15
+ vocab_size=8192,
16
+ head_dim=64,
17
+ max_position_embeddings=4096,
18
+ rope_theta=100000.0,
19
+ rms_norm_eps=1e-6,
20
+ embed_width=384,
21
+ hidden_size_a=256,
22
+ num_hidden_layers_a=14,
23
+ num_attention_heads_a=4,
24
+ num_key_value_heads_a=1,
25
+ intermediate_size_a=704,
26
+ hidden_size_b=320,
27
+ num_hidden_layers_b=5,
28
+ num_attention_heads_b=5,
29
+ num_key_value_heads_b=1,
30
+ intermediate_size_b=960,
31
+ hidden_size_c=384,
32
+ num_hidden_layers_c=6,
33
+ num_attention_heads_c=6,
34
+ num_key_value_heads_c=2,
35
+ intermediate_size_c=1024,
36
+ a_read=(5, 9, 12),
37
+ a_land=(7, 11, 14),
38
+ c_read=(2, 4, 5),
39
+ c_land=(3, 5, 6),
40
+ b_land=(1, 3, 5),
41
+ b_read=(2, 4, 5),
42
+ gate_init=0.01,
43
+ cut_bridges=False,
44
+ tie_word_embeddings=False,
45
+ **kwargs,
46
+ ):
47
+ self.vocab_size = vocab_size
48
+ self.head_dim = head_dim
49
+ self.max_position_embeddings = max_position_embeddings
50
+ self.rope_theta = rope_theta
51
+ self.rms_norm_eps = rms_norm_eps
52
+ self.embed_width = embed_width
53
+
54
+ self.hidden_size_a = hidden_size_a
55
+ self.num_hidden_layers_a = num_hidden_layers_a
56
+ self.num_attention_heads_a = num_attention_heads_a
57
+ self.num_key_value_heads_a = num_key_value_heads_a
58
+ self.intermediate_size_a = intermediate_size_a
59
+
60
+ self.hidden_size_b = hidden_size_b
61
+ self.num_hidden_layers_b = num_hidden_layers_b
62
+ self.num_attention_heads_b = num_attention_heads_b
63
+ self.num_key_value_heads_b = num_key_value_heads_b
64
+ self.intermediate_size_b = intermediate_size_b
65
+
66
+ self.hidden_size_c = hidden_size_c
67
+ self.num_hidden_layers_c = num_hidden_layers_c
68
+ self.num_attention_heads_c = num_attention_heads_c
69
+ self.num_key_value_heads_c = num_key_value_heads_c
70
+ self.intermediate_size_c = intermediate_size_c
71
+
72
+ self.a_read = list(a_read)
73
+ self.a_land = list(a_land)
74
+ self.c_read = list(c_read)
75
+ self.c_land = list(c_land)
76
+ self.b_land = list(b_land)
77
+ self.b_read = list(b_read)
78
+ self.gate_init = gate_init
79
+
80
+ # set true to run the three towers with every bridge removed: A and C
81
+ # become ordinary standalone transformers and B stops mattering
82
+ self.cut_bridges = cut_bridges
83
+
84
+ # `hidden_size` is what generic HF tooling looks for
85
+ self.hidden_size = hidden_size_c
86
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
modeling_bananamind21unified.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """BananaMind 2.1 Unified as an HF causal LM.
2
+
3
+ IMPORTANT - what `.logits` contains
4
+ -----------------------------------
5
+ Towers A and C are mixed in *probability* space, so the natural output of this
6
+ model is a normalised log-probability vector, not a logit vector:
7
+
8
+ log p = logaddexp(log a + log_softmax(logits_A),
9
+ log(1-a) + log_softmax(logits_C))
10
+
11
+ `.logits` carries that log-probability vector directly. This is safe for every
12
+ standard consumer, because `log_softmax` is the identity on an already
13
+ normalised log-probability vector (its logsumexp is 0), and `softmax(log p) = p`.
14
+ So loglikelihood scoring, `generate()`, and temperature-1 sampling all behave
15
+ correctly. What is *not* meaningful is treating these numbers as unnormalised
16
+ scores with an arbitrary additive offset - they are already calibrated.
17
+
18
+ Tower B is the relay. It has no output head and never appears in the mixture;
19
+ it exists only to carry signal between A and C, which have no other path to
20
+ each other. Set `config.cut_bridges = True` to sever every bridge, which turns
21
+ A and C into two ordinary standalone transformers.
22
+
23
+ Parameter names match the training module exactly, so a checkpoint transfers
24
+ without any key rewriting.
25
+ """
26
+ import math
27
+ from typing import Optional
28
+
29
+ import torch
30
+ import torch.nn as nn
31
+ import torch.nn.functional as F
32
+ from transformers import PreTrainedModel
33
+ from transformers.generation import GenerationMixin
34
+ from transformers.modeling_outputs import CausalLMOutputWithPast
35
+
36
+ from .configuration_bananamind21unified import BananaMind21UnifiedConfig
37
+
38
+
39
+ class RMSNorm(nn.Module):
40
+ def __init__(self, dim, eps=1e-6):
41
+ super().__init__()
42
+ self.eps = eps
43
+ self.weight = nn.Parameter(torch.ones(dim))
44
+
45
+ def forward(self, x):
46
+ x_float = x.float()
47
+ rms = torch.rsqrt(x_float.pow(2).mean(-1, keepdim=True) + self.eps)
48
+ return (x_float * rms * self.weight.float()).type_as(x)
49
+
50
+
51
+ def precompute_freqs_cis(head_dim, seq_len, theta=100000.0):
52
+ inv = 1.0 / (theta ** (torch.arange(0, head_dim, 2, dtype=torch.float32) / head_dim))
53
+ t = torch.arange(seq_len, dtype=torch.float32)
54
+ return torch.polar(torch.ones_like(torch.outer(t, inv)), torch.outer(t, inv))
55
+
56
+
57
+ def apply_rotary_emb(q, k, freqs_cis):
58
+ q_complex = torch.view_as_complex(q.float().reshape(*q.shape[:-1], -1, 2))
59
+ k_complex = torch.view_as_complex(k.float().reshape(*k.shape[:-1], -1, 2))
60
+ freqs_cis = freqs_cis.unsqueeze(0).unsqueeze(0)
61
+ q_out = torch.view_as_real(q_complex * freqs_cis).flatten(-2)
62
+ k_out = torch.view_as_real(k_complex * freqs_cis).flatten(-2)
63
+ return q_out.type_as(q), k_out.type_as(k)
64
+
65
+
66
+ class _Spec:
67
+ def __init__(self, hidden_size, num_hidden_layers, num_attention_heads, num_key_value_heads, intermediate_size):
68
+ self.hidden_size = hidden_size
69
+ self.num_hidden_layers = num_hidden_layers
70
+ self.num_attention_heads = num_attention_heads
71
+ self.num_key_value_heads = num_key_value_heads
72
+ self.intermediate_size = intermediate_size
73
+
74
+
75
+ class TowerAttention(nn.Module):
76
+ def __init__(self, spec, head_dim, rms_norm_eps):
77
+ super().__init__()
78
+ self.n_head = spec.num_attention_heads
79
+ self.n_kv_heads = spec.num_key_value_heads
80
+ self.head_dim = head_dim
81
+ self.n_rep = self.n_head // self.n_kv_heads
82
+ self.q_proj = nn.Linear(spec.hidden_size, self.n_head * head_dim, bias=False)
83
+ self.k_proj = nn.Linear(spec.hidden_size, self.n_kv_heads * head_dim, bias=False)
84
+ self.v_proj = nn.Linear(spec.hidden_size, self.n_kv_heads * head_dim, bias=False)
85
+ self.o_proj = nn.Linear(self.n_head * head_dim, spec.hidden_size, bias=False)
86
+ self.q_norm = RMSNorm(head_dim, eps=rms_norm_eps)
87
+ self.k_norm = RMSNorm(head_dim, eps=rms_norm_eps)
88
+
89
+ def forward(self, x, freqs_cis, attention_mask=None):
90
+ bsz, seq_len, _ = x.size()
91
+ q = self.q_proj(x).view(bsz, seq_len, self.n_head, self.head_dim).transpose(1, 2)
92
+ k = self.k_proj(x).view(bsz, seq_len, self.n_kv_heads, self.head_dim).transpose(1, 2)
93
+ v = self.v_proj(x).view(bsz, seq_len, self.n_kv_heads, self.head_dim).transpose(1, 2)
94
+ q = self.q_norm(q)
95
+ k = self.k_norm(k)
96
+ q, k = apply_rotary_emb(q, k, freqs_cis)
97
+ k = k.unsqueeze(2).expand(bsz, self.n_kv_heads, self.n_rep, seq_len, self.head_dim)
98
+ k = k.reshape(bsz, self.n_head, seq_len, self.head_dim).contiguous()
99
+ v = v.unsqueeze(2).expand(bsz, self.n_kv_heads, self.n_rep, seq_len, self.head_dim)
100
+ v = v.reshape(bsz, self.n_head, seq_len, self.head_dim).contiguous()
101
+
102
+ attn_mask = None
103
+ is_causal = True
104
+ if attention_mask is not None:
105
+ key_pad = attention_mask.to(torch.bool)[:, None, None, :]
106
+ causal = torch.ones(seq_len, seq_len, dtype=torch.bool, device=x.device).tril()
107
+ attn_mask = key_pad & causal[None, None, :, :]
108
+ is_causal = False
109
+
110
+ y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, is_causal=is_causal)
111
+ y = y.transpose(1, 2).contiguous().view(bsz, seq_len, self.n_head * self.head_dim)
112
+ return self.o_proj(y)
113
+
114
+
115
+ class TowerSwiGLUMLP(nn.Module):
116
+ def __init__(self, spec):
117
+ super().__init__()
118
+ self.w_gate = nn.Linear(spec.hidden_size, spec.intermediate_size, bias=False)
119
+ self.w_up = nn.Linear(spec.hidden_size, spec.intermediate_size, bias=False)
120
+ self.w_down = nn.Linear(spec.intermediate_size, spec.hidden_size, bias=False)
121
+
122
+ def forward(self, x):
123
+ return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
124
+
125
+
126
+ class TowerBlock(nn.Module):
127
+ def __init__(self, spec, head_dim, rms_norm_eps):
128
+ super().__init__()
129
+ self.ln_1 = RMSNorm(spec.hidden_size, eps=rms_norm_eps)
130
+ self.attn = TowerAttention(spec, head_dim, rms_norm_eps)
131
+ self.ln_2 = RMSNorm(spec.hidden_size, eps=rms_norm_eps)
132
+ self.mlp = TowerSwiGLUMLP(spec)
133
+
134
+ def forward(self, x, freqs_cis, attention_mask=None):
135
+ x = x + self.attn(self.ln_1(x), freqs_cis, attention_mask=attention_mask)
136
+ x = x + self.mlp(self.ln_2(x))
137
+ return x
138
+
139
+
140
+ class Edge(nn.Module):
141
+ def __init__(self, dim_in, dim_out, gate_init=0.01):
142
+ super().__init__()
143
+ self.w = nn.Linear(dim_in, dim_out, bias=False)
144
+ self.g = nn.Parameter(torch.full((dim_out,), float(gate_init)))
145
+
146
+ def forward(self, x):
147
+ return self.g * self.w(x)
148
+
149
+
150
+ class BananaMind21UnifiedPreTrainedModel(PreTrainedModel):
151
+ config_class = BananaMind21UnifiedConfig
152
+ base_model_prefix = "model"
153
+ supports_gradient_checkpointing = False
154
+
155
+ def _init_weights(self, module):
156
+ if isinstance(module, nn.Linear):
157
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
158
+ if module.bias is not None:
159
+ torch.nn.init.zeros_(module.bias)
160
+ elif isinstance(module, nn.Embedding):
161
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
162
+
163
+
164
+ class BananaMind21UnifiedForCausalLM(BananaMind21UnifiedPreTrainedModel, GenerationMixin):
165
+ def __init__(self, config):
166
+ super().__init__(config)
167
+ self.config = config
168
+ a = _Spec(config.hidden_size_a, config.num_hidden_layers_a, config.num_attention_heads_a,
169
+ config.num_key_value_heads_a, config.intermediate_size_a)
170
+ b = _Spec(config.hidden_size_b, config.num_hidden_layers_b, config.num_attention_heads_b,
171
+ config.num_key_value_heads_b, config.intermediate_size_b)
172
+ c = _Spec(config.hidden_size_c, config.num_hidden_layers_c, config.num_attention_heads_c,
173
+ config.num_key_value_heads_c, config.intermediate_size_c)
174
+
175
+ self.wte = nn.Embedding(config.vocab_size, config.embed_width)
176
+ self.in_proj_a = nn.Linear(config.embed_width, a.hidden_size, bias=False)
177
+ self.in_proj_b = nn.Linear(config.embed_width, b.hidden_size, bias=False)
178
+
179
+ self.blocks_a = nn.ModuleList([TowerBlock(a, config.head_dim, config.rms_norm_eps)
180
+ for _ in range(a.num_hidden_layers)])
181
+ self.blocks_b = nn.ModuleList([TowerBlock(b, config.head_dim, config.rms_norm_eps)
182
+ for _ in range(b.num_hidden_layers)])
183
+ self.blocks_c = nn.ModuleList([TowerBlock(c, config.head_dim, config.rms_norm_eps)
184
+ for _ in range(c.num_hidden_layers)])
185
+
186
+ n = len(config.a_read)
187
+ gi = config.gate_init
188
+ self.edges_a2b = nn.ModuleList([Edge(a.hidden_size, b.hidden_size, gi) for _ in range(n)])
189
+ self.edges_c2b = nn.ModuleList([Edge(c.hidden_size, b.hidden_size, gi) for _ in range(n)])
190
+ self.edges_b2a = nn.ModuleList([Edge(b.hidden_size, a.hidden_size, gi) for _ in range(n)])
191
+ self.edges_b2c = nn.ModuleList([Edge(b.hidden_size, c.hidden_size, gi) for _ in range(n)])
192
+
193
+ self.ln_f_a = RMSNorm(a.hidden_size, eps=config.rms_norm_eps)
194
+ self.ln_f_c = RMSNorm(c.hidden_size, eps=config.rms_norm_eps)
195
+ self.lm_head_a = nn.Linear(a.hidden_size, config.vocab_size, bias=False)
196
+ self.lm_head_c = nn.Linear(c.hidden_size, config.vocab_size, bias=False)
197
+ self.mix_head = nn.Linear(a.hidden_size + c.hidden_size, 1, bias=True)
198
+
199
+ self._embd_scale = math.sqrt(config.embed_width)
200
+ self._freqs_cis_cache = None
201
+ self.post_init()
202
+
203
+ def get_input_embeddings(self):
204
+ return self.wte
205
+
206
+ def set_input_embeddings(self, value):
207
+ self.wte = value
208
+
209
+ def get_output_embeddings(self):
210
+ return self.lm_head_c
211
+
212
+ def prepare_inputs_for_generation(self, input_ids, past_key_values=None, attention_mask=None, **kwargs):
213
+ return {"input_ids": input_ids, "attention_mask": attention_mask, "use_cache": False}
214
+
215
+ def _get_freqs_cis(self, seq_len, device):
216
+ cache = self._freqs_cis_cache
217
+ if cache is None or cache.device != device or cache.size(0) < seq_len:
218
+ cache = precompute_freqs_cis(
219
+ self.config.head_dim, seq_len, self.config.rope_theta
220
+ ).to(device)
221
+ self._freqs_cis_cache = cache
222
+ return cache[:seq_len]
223
+
224
+ @staticmethod
225
+ def _advance(blocks, x, cursor, target, freqs_cis, pending, attention_mask):
226
+ while cursor < target:
227
+ arriving = pending.pop(cursor + 1, None)
228
+ if arriving is not None:
229
+ x = x + arriving
230
+ x = blocks[cursor](x, freqs_cis, attention_mask=attention_mask)
231
+ cursor += 1
232
+ return x, cursor
233
+
234
+ def hidden_states(self, input_ids, attention_mask=None):
235
+ cfg = self.config
236
+ _, seq_len = input_ids.size()
237
+ freqs_cis = self._get_freqs_cis(seq_len, input_ids.device)
238
+
239
+ embedded = self.wte(input_ids) * self._embd_scale
240
+ x_a = self.in_proj_a(embedded)
241
+ x_b = self.in_proj_b(embedded)
242
+ x_c = embedded
243
+
244
+ pend_a, pend_b, pend_c = {}, {}, {}
245
+ cur_a = cur_b = cur_c = 0
246
+ cut = cfg.cut_bridges
247
+
248
+ for k in range(len(cfg.a_read)):
249
+ x_a, cur_a = self._advance(self.blocks_a, x_a, cur_a, cfg.a_read[k], freqs_cis, pend_a, attention_mask)
250
+ x_c, cur_c = self._advance(self.blocks_c, x_c, cur_c, cfg.c_read[k], freqs_cis, pend_c, attention_mask)
251
+ if not cut:
252
+ pend_b[cfg.b_land[k]] = self.edges_a2b[k](x_a) + self.edges_c2b[k](x_c)
253
+ x_b, cur_b = self._advance(self.blocks_b, x_b, cur_b, cfg.b_read[k], freqs_cis, pend_b, attention_mask)
254
+ if not cut:
255
+ pend_a[cfg.a_land[k]] = self.edges_b2a[k](x_b)
256
+ pend_c[cfg.c_land[k]] = self.edges_b2c[k](x_b)
257
+
258
+ x_a, _ = self._advance(self.blocks_a, x_a, cur_a, cfg.num_hidden_layers_a, freqs_cis, pend_a, attention_mask)
259
+ x_c, _ = self._advance(self.blocks_c, x_c, cur_c, cfg.num_hidden_layers_c, freqs_cis, pend_c, attention_mask)
260
+ x_b, _ = self._advance(self.blocks_b, x_b, cur_b, cfg.num_hidden_layers_b, freqs_cis, pend_b, attention_mask)
261
+
262
+ h_a = self.ln_f_a(x_a)
263
+ h_c = self.ln_f_c(x_c)
264
+ mix_logit = self.mix_head(torch.cat([h_a, h_c], dim=-1)).squeeze(-1)
265
+ return h_a, h_c, mix_logit
266
+
267
+ def forward(
268
+ self,
269
+ input_ids,
270
+ attention_mask=None,
271
+ labels=None,
272
+ past_key_values: Optional[object] = None,
273
+ use_cache=False,
274
+ **kwargs,
275
+ ):
276
+ h_a, h_c, mix_logit = self.hidden_states(input_ids, attention_mask=attention_mask)
277
+
278
+ log_p_a = F.log_softmax(self.lm_head_a(h_a).float(), dim=-1)
279
+ log_p_c = F.log_softmax(self.lm_head_c(h_c).float(), dim=-1)
280
+ log_alpha = F.logsigmoid(mix_logit).unsqueeze(-1)
281
+ log_one_minus = F.logsigmoid(-mix_logit).unsqueeze(-1)
282
+ log_p = torch.logaddexp(log_alpha + log_p_a, log_one_minus + log_p_c)
283
+
284
+ loss = None
285
+ if labels is not None:
286
+ loss = F.nll_loss(
287
+ log_p[..., :-1, :].reshape(-1, log_p.size(-1)),
288
+ labels[..., 1:].reshape(-1),
289
+ )
290
+ return CausalLMOutputWithPast(loss=loss, logits=log_p, past_key_values=None)