PowerMachine commited on
Commit
f3b2bf2
·
verified ·
1 Parent(s): babd829

Delete models/medusa_heads.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. models/medusa_heads.py +0 -409
models/medusa_heads.py DELETED
@@ -1,409 +0,0 @@
1
- """medusa_heads.py — Multi-Token Prediction (MTP) com Medusa Heads.
2
-
3
- Implementa Medusa (Cai et al., 2024): múltiplas cabeças de previsão que
4
- predizem tokens em posições futuras (t+1, t+2, ..., t+K), permitindo
5
- geração paralela de K tokens por forward pass.
6
-
7
- ==============================================================================
8
- ANÁLISE MATEMÁTICA E LÓGICA
9
- ==============================================================================
10
-
11
- Seja h_t ∈ R^d o hidden state produzido pelo modelo (por exemplo, saída do
12
- TransformerDecoderStack no tempo t). Em vez de prever apenas y_{t+1},
13
- Medusa adiciona K cabeças extras:
14
-
15
- y_{t+k} = softmax(W_k h_t + b_k) para k = 1, ..., K
16
-
17
- A cabeça principal (k=1) é o LM head original; as cabeças k=2..K são
18
- "cabeças Medusa" adicionais treinadas com a loss:
19
-
20
- L_Medusa = sum_{k=1}^{K} lambda_k * CE(head_k(h_t), y_{t+k})
21
-
22
- onde lambda_k é um peso decrescente (tipicamente lambda_k = 1/k ou
23
- lambda_k = decay^k), pois previsões mais distantes são mais difíceis.
24
-
25
- ÁRVORE DE DECODIFICAÇÃO:
26
- Em inferência, cada cabeça Medusa gera top-B candidatos, formando uma
27
- árvore de filhos. A árvore é avaliada em paralelo (single forward) e
28
- os candidatos são aceitos se coincidirem com a previsão do modelo
29
- na posição correspondente (verificação via attention mask da árvore).
30
-
31
- Speedup teórico: até K× (em prática 2-3× devido à taxa de aceitação <1).
32
-
33
- INTEGRAÇÃO COM CNN-BiGRU:
34
- - As cabeças Medusa são aplicadas sobre o hidden state final do
35
- TransformerDecoderStack (após self-attention + FFN).
36
- - O hidden state h_t já contém contexto cooperativo (streams A+B fundidos).
37
- - Em treinamento, adicionamos L_Medusa à loss total (com peso mu_medusa).
38
- - Em inferência, ativamos tree-decoding quando use_medusa=True.
39
-
40
- VANTAGENS:
41
- 1. Acelera geração em ~2-3× sem perda de qualidade.
42
- 2. Não requer mudanças no backbone — apenas cabeças extras.
43
- 3. Treinável com poucas amostras (as cabeças convergem rápido).
44
-
45
- LIMITAÇÕES:
46
- 1. Aumenta o uso de memória (K * vocab_size params extras).
47
- 2. Taxa de aceitação depende da tarefa — previsões muito long-range
48
- são menos precisas.
49
-
50
- Autor: CNN-BiGRU Project
51
- """
52
- from __future__ import annotations
53
-
54
- import logging
55
- import math
56
- from dataclasses import dataclass, field
57
- from typing import Dict, List, Optional, Tuple
58
-
59
- import torch
60
- import torch.nn as nn
61
- import torch.nn.functional as F
62
-
63
- logger = logging.getLogger(__name__)
64
-
65
-
66
- # ============================================================================
67
- # Configuração
68
- # ============================================================================
69
-
70
- @dataclass
71
- class MedusaConfig:
72
- """Configuração das cabeças Medusa."""
73
- vocab_size: int = 32000
74
- embed_dim: int = 256
75
- n_heads: int = 4 # K cabeças Medusa (além da principal)
76
- head_hidden_mult: int = 2 # multiplicador da camada oculta de cada cabeça
77
- dropout: float = 0.1
78
- # Peso de cada cabeça na loss (lambda_k). Se None, usa 1/(k+1).
79
- head_weights: Optional[List[float]] = None
80
- # Top-B candidatos por cabeça para tree decoding
81
- top_b_candidates: int = 4
82
- # Se True, compartilha a primeira camada das cabeças (reduz params)
83
- share_first_layer: bool = False
84
- # Initialize biases to favor first token (BOS) — estabilidade
85
- init_bias_to_bos: bool = False
86
- bos_id: int = 0
87
-
88
-
89
- # ============================================================================
90
- # Cabeça Medusa individual
91
- # ============================================================================
92
-
93
- class MedusaHead(nn.Module):
94
- """Uma única cabeça Medusa: prediz y_{t+k} dado h_t.
95
-
96
- Estrutura: Linear(d, d*mult) -> SiLU -> Dropout -> Linear(d*mult, V)
97
- """
98
-
99
- def __init__(
100
- self,
101
- embed_dim: int,
102
- vocab_size: int,
103
- hidden_mult: int = 2,
104
- dropout: float = 0.1,
105
- ):
106
- super().__init__()
107
- hidden = embed_dim * hidden_mult
108
- self.fc1 = nn.Linear(embed_dim, hidden)
109
- self.fc2 = nn.Linear(hidden, vocab_size)
110
- self.dropout = nn.Dropout(dropout)
111
- # Inicialização
112
- for layer in (self.fc1, self.fc2):
113
- nn.init.xavier_uniform_(layer.weight)
114
- nn.init.zeros_(layer.bias)
115
-
116
- def forward(self, h: torch.Tensor) -> torch.Tensor:
117
- """
118
- Args:
119
- h: [B, T, D] ou [B, D] hidden states
120
- Returns:
121
- logits: [B, T, V] ou [B, V]
122
- """
123
- x = self.dropout(F.silu(self.fc1(h)))
124
- return self.fc2(x)
125
-
126
-
127
- # ============================================================================
128
- # MTP com Medusa Heads
129
- # ============================================================================
130
-
131
- class MedusaMTP(nn.Module):
132
- """Multi-Token Prediction com Medusa Heads.
133
-
134
- Args:
135
- config: configuração das cabeças
136
- """
137
-
138
- def __init__(self, config: MedusaConfig):
139
- super().__init__()
140
- self.config = config
141
- self.K = config.n_heads
142
- self.vocab_size = config.vocab_size
143
- self.embed_dim = config.embed_dim
144
-
145
- # Cabeças Medusa (k=1..K)
146
- # k=1 é a cabeça "principal extra" (além do lm_head do modelo)
147
- # k=2..K são cabeças adicionais
148
- if config.share_first_layer:
149
- # Compartilha fc1 entre todas as cabeças
150
- shared_hidden = config.embed_dim * config.head_hidden_mult
151
- self.shared_fc1 = nn.Linear(config.embed_dim, shared_hidden)
152
- nn.init.xavier_uniform_(self.shared_fc1.weight)
153
- nn.init.zeros_(self.shared_fc1.bias)
154
- self.heads = nn.ModuleList([
155
- nn.Linear(shared_hidden, config.vocab_size)
156
- for _ in range(config.n_heads)
157
- ])
158
- for h in self.heads:
159
- nn.init.xavier_uniform_(h.weight)
160
- nn.init.zeros_(h.bias)
161
- self._shared = True
162
- else:
163
- self.heads = nn.ModuleList([
164
- MedusaHead(
165
- embed_dim=config.embed_dim,
166
- vocab_size=config.vocab_size,
167
- hidden_mult=config.head_hidden_mult,
168
- dropout=config.dropout,
169
- )
170
- for _ in range(config.n_heads)
171
- ])
172
- self._shared = False
173
-
174
- # Pesos lambda_k (decrescentes)
175
- if config.head_weights is not None:
176
- assert len(config.head_weights) == config.n_heads, \
177
- "head_weights deve ter n_heads elementos"
178
- self.register_buffer(
179
- "head_weights",
180
- torch.tensor(config.head_weights, dtype=torch.float32),
181
- )
182
- else:
183
- # default: 1/(k+1) para k=1..K
184
- weights = [1.0 / (k + 1) for k in range(1, config.n_heads + 1)]
185
- self.register_buffer(
186
- "head_weights",
187
- torch.tensor(weights, dtype=torch.float32),
188
- )
189
-
190
- # Inicialização bias para BOS (opcional)
191
- if config.init_bias_to_bos:
192
- self._init_biases_to_bos()
193
-
194
- def _init_biases_to_bos(self):
195
- """Inicializa biases das cabeças para favorecer BOS no início."""
196
- bos_id = self.config.bos_id
197
- with torch.no_grad():
198
- if self._shared:
199
- for h in self.heads:
200
- h.bias.zero_()
201
- h.bias[bos_id] = 1.0
202
- else:
203
- for h in self.heads:
204
- h.fc2.bias.zero_()
205
- h.fc2.bias[bos_id] = 1.0
206
-
207
- def forward(self, h: torch.Tensor) -> List[torch.Tensor]:
208
- """Computa logits de todas as cabeças.
209
-
210
- Args:
211
- h: [B, T, D] hidden states do backbone (saída do TransformerDecoder)
212
-
213
- Returns:
214
- list de K tensores, cada um [B, T, V] (logits da cabeça k)
215
- heads[k-1] prediz y_{t+k} dado h_t
216
- """
217
- if h.dim() == 2:
218
- h = h.unsqueeze(1) # [B, D] -> [B, 1, D]
219
- if h.size(-1) != self.embed_dim:
220
- raise ValueError(
221
- f"hidden dim mismatch: h={h.size(-1)}, config={self.embed_dim}"
222
- )
223
-
224
- all_logits = []
225
- if self._shared:
226
- shared = F.silu(self.shared_fc1(h))
227
- for head in self.heads:
228
- all_logits.append(head(shared))
229
- else:
230
- for head in self.heads:
231
- all_logits.append(head(h))
232
- return all_logits
233
-
234
- def compute_loss(
235
- self,
236
- h: torch.Tensor,
237
- target_ids: torch.Tensor,
238
- ignore_index: int = -100,
239
- ) -> Tuple[torch.Tensor, Dict[str, float]]:
240
- """Computa a loss Medusa total.
241
-
242
- Args:
243
- h: [B, T, D] hidden states do backbone
244
- target_ids: [B, T] tokens alvo (y_{t+1}, y_{t+2}, ...)
245
- A cabeça k deve prever target_ids[:, t+k-1] dado h_t.
246
- ignore_index: índice a ignorar na CE (default: -100, ou pad_id)
247
-
248
- Returns:
249
- loss: escalar (weighted sum of head losses)
250
- stats: dict com loss por cabeça
251
- """
252
- if h.dim() == 2:
253
- h = h.unsqueeze(1)
254
- B, T, D = h.shape
255
- if target_ids.dim() != 2:
256
- raise ValueError(f"target_ids deve ser [B, T]; recebido {target_ids.shape}")
257
-
258
- all_logits = self.forward(h) # list de K tensores [B, T, V]
259
-
260
- total_loss = torch.zeros(1, device=h.device, dtype=h.dtype)
261
- stats = {}
262
-
263
- for k in range(self.K):
264
- # Cabeça k prediz y_{t+k+1} dado h_t (k=0 -> y_{t+1}, k=1 -> y_{t+2}, ...)
265
- shift = k + 1
266
- if shift >= T:
267
- # Não há targets suficientes para esta cabeça
268
- stats[f"head_{k+1}_loss"] = float("nan")
269
- continue
270
- # logits_k: [B, T, V] -> pegamos[:B, :T-shift, :] para alinhar com target[:, shift:]
271
- logits_k = all_logits[k][:, :T - shift, :].contiguous() # [B, T-shift, V]
272
- target_k = target_ids[:, shift:T].contiguous() # [B, T-shift]
273
-
274
- loss_k = F.cross_entropy(
275
- logits_k.view(-1, self.vocab_size),
276
- target_k.view(-1),
277
- ignore_index=ignore_index,
278
- reduction="mean",
279
- )
280
- weight_k = self.head_weights[k].to(h.device)
281
- total_loss = total_loss + weight_k * loss_k
282
- stats[f"head_{k+1}_loss"] = float(loss_k.detach())
283
-
284
- return total_loss.squeeze(), stats
285
-
286
- def predict_top_b(
287
- self,
288
- h: torch.Tensor,
289
- top_b: Optional[int] = None,
290
- ) -> List[Tuple[torch.Tensor, torch.Tensor]]:
291
- """Para cada cabeça, retorna top-B candidatos e suas probabilidades.
292
-
293
- Usado em tree decoding (inferência).
294
-
295
- Args:
296
- h: [B, T, D] hidden states (ou [B, D] para último token)
297
- top_b: número de candidatos (default: config.top_b_candidates)
298
-
299
- Returns:
300
- list de K tuplas (tokens, probs) onde:
301
- tokens: [B, top_b] — IDs dos top-B tokens
302
- probs: [B, top_b] — probabilidades (após softmax)
303
- """
304
- if h.dim() == 2:
305
- h = h.unsqueeze(1)
306
- # Usar apenas o último token para previsão
307
- h_last = h[:, -1:, :] # [B, 1, D]
308
- all_logits = self.forward(h_last) # list de K [B, 1, V]
309
- b = top_b or self.config.top_b_candidates
310
-
311
- results = []
312
- for k, logits_k in enumerate(all_logits):
313
- logits_k = logits_k[:, -1, :] # [B, V]
314
- probs = F.softmax(logits_k, dim=-1)
315
- top_probs, top_ids = torch.topk(probs, k=b, dim=-1)
316
- results.append((top_ids, top_probs))
317
- return results
318
-
319
-
320
- # ============================================================================
321
- # Tree Decoding (esqueleto — implementação simplificada)
322
- # ============================================================================
323
-
324
- def medusa_tree_decode(
325
- medusa: MedusaMTP,
326
- base_logits: torch.Tensor,
327
- h_last: torch.Tensor,
328
- accept_threshold: float = 0.0,
329
- ) -> Dict[str, torch.Tensor]:
330
- """Decodificação por árvore Medusa (simplificada).
331
-
332
- Em uma implementação completa, isto construiria uma árvore de candidatos
333
- e faria um forward pass único para verificar aceitação. Aqui retornamos
334
- apenas os candidatos de cada cabeça para uso posterior.
335
-
336
- Args:
337
- medusa: módulo MedusaMTP
338
- base_logits: [B, V] logits da cabeça principal (já computados)
339
- h_last: [B, D] hidden state do último token
340
- accept_threshold: threshold de probabilidade para aceitar candidato
341
-
342
- Returns:
343
- dict com:
344
- base_token: [B] argmax da cabeça principal
345
- base_prob: [B] prob do argmax
346
- medusa_candidates: list de K tuplas (tokens, probs)
347
- accepted: list de K tensores booleanos [B] indicando aceitação
348
- """
349
- base_probs = F.softmax(base_logits, dim=-1)
350
- base_token = base_probs.argmax(dim=-1)
351
- base_prob = base_probs.max(dim=-1).values
352
-
353
- candidates = medusa.predict_top_b(h_last)
354
-
355
- accepted = []
356
- for k, (tokens, probs) in enumerate(candidates):
357
- # Aceita o primeiro candidato se prob >= threshold
358
- first_prob = probs[:, 0]
359
- accept = first_prob >= accept_threshold
360
- accepted.append(accept)
361
-
362
- return {
363
- "base_token": base_token,
364
- "base_prob": base_prob,
365
- "medusa_candidates": candidates,
366
- "accepted": accepted,
367
- }
368
-
369
-
370
- # ============================================================================
371
- # Self-test
372
- # ============================================================================
373
-
374
- def _self_test():
375
- torch.manual_seed(42)
376
- config = MedusaConfig(
377
- vocab_size=100,
378
- embed_dim=32,
379
- n_heads=4,
380
- head_hidden_mult=2,
381
- dropout=0.1,
382
- )
383
- medusa = MedusaMTP(config)
384
- h = torch.randn(2, 8, 32)
385
- target = torch.randint(0, 100, (2, 8))
386
- loss, stats = medusa.compute_loss(h, target)
387
- print(f"Loss: {loss.item():.4f}")
388
- print(f"Stats: {stats}")
389
-
390
- # Test top-B
391
- cands = medusa.predict_top_b(h)
392
- print(f"K={len(cands)}, top_b shape: {cands[0][0].shape}")
393
-
394
- # Test tree decode
395
- base_logits = torch.randn(2, 100)
396
- result = medusa_tree_decode(medusa, base_logits, h[:, -1, :])
397
- print(f"Base token: {result['base_token']}")
398
-
399
-
400
- if __name__ == "__main__":
401
- _self_test()
402
-
403
-
404
- __all__ = [
405
- "MedusaConfig",
406
- "MedusaHead",
407
- "MedusaMTP",
408
- "medusa_tree_decode",
409
- ]