PowerMachine commited on
Commit
12fa917
·
verified ·
1 Parent(s): 87d5a70

Delete models/transformer_block.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. models/transformer_block.py +0 -401
models/transformer_block.py DELETED
@@ -1,401 +0,0 @@
1
- """
2
- Transformer Decoder Block — CausalSelfAttention + TransformerBlock.
3
-
4
- Implementa o bloco decodificador causal conforme dados.txt seções 6-7:
5
- - Multi-head self-attention com máscara triangular inferior
6
- - Scaled dot-product: (q @ k.T) / sqrt(d_head)
7
- - QKV projection unificada
8
- - Pre-LN: x = x + attn(ln1(x)); x = x + ffn(ln2(x))
9
- - FFN: Linear(d, 4d) → GELU → Linear(4d, d) → Dropout
10
- - Weight tying opcional entre embedding e LM head
11
-
12
- Inclui:
13
- - RoPE (Rotary Position Embeddings) opcional
14
- - Cache KV opcional para geração autoregressiva eficiente
15
- - Padding mask (ignora tokens [PAD])
16
- - Causal mask (não olha para o futuro)
17
-
18
- Autor: CNN-BiGRU Project
19
- """
20
- from __future__ import annotations
21
-
22
- import logging
23
- import math
24
- from dataclasses import dataclass, field
25
- from typing import Optional, Tuple
26
-
27
- import torch
28
- import torch.nn as nn
29
- import torch.nn.functional as F
30
-
31
- from .rope import RotaryPositionEmbedding
32
-
33
- logger = logging.getLogger(__name__)
34
-
35
-
36
- # ============================================================================
37
- # Configuração
38
- # ============================================================================
39
-
40
- @dataclass
41
- class TransformerBlockConfig:
42
- """Configuração de um bloco Transformer Decoder."""
43
- embed_dim: int = 256
44
- n_heads: int = 4
45
- ff_dim: Optional[int] = None # default: 4 * embed_dim
46
- dropout: float = 0.1
47
- use_rope: bool = True
48
- max_seq_len: int = 512
49
- rope_base: float = 10000.0
50
- # Limiar para attention dropout
51
- attn_dropout: float = 0.1
52
- # Bias nas projeções lineares
53
- qkv_bias: bool = False
54
- out_bias: bool = False
55
- ffn_bias: bool = False
56
-
57
-
58
- # ============================================================================
59
- # Causal Self-Attention
60
- # ============================================================================
61
-
62
- class CausalSelfAttention(nn.Module):
63
- """
64
- Mecanismo de Atenção Multi-Head com Máscara Causal (Triangular Inferior).
65
-
66
- Conforme dados.txt linhas 553-592.
67
-
68
- Fórmula matemática:
69
- Attention(Q, K, V) = softmax( (Q @ K^T) / sqrt(d_k) + mask ) @ V
70
-
71
- Com RoPE opcional, Q e K são rotacionados antes do produto escalar.
72
- """
73
-
74
- def __init__(self, config: TransformerBlockConfig):
75
- super().__init__()
76
- self.config = config
77
- self.embed_dim = config.embed_dim
78
- self.n_heads = config.n_heads
79
- if self.embed_dim % self.n_heads != 0:
80
- raise ValueError(
81
- f"embed_dim ({self.embed_dim}) deve ser divisível por "
82
- f"n_heads ({self.n_heads})"
83
- )
84
- self.head_dim = self.embed_dim // self.n_heads
85
-
86
- # Projeção QKV unificada (3 * embed_dim) — ganho de performance
87
- self.qkv_projection = nn.Linear(self.embed_dim, 3 * self.embed_dim, bias=config.qkv_bias)
88
- self.out_projection = nn.Linear(self.embed_dim, self.embed_dim, bias=config.out_bias)
89
-
90
- self.attn_dropout = nn.Dropout(config.attn_dropout)
91
- self.resid_dropout = nn.Dropout(config.dropout)
92
-
93
- # RoPE (opcional)
94
- self.use_rope = config.use_rope
95
- if self.use_rope:
96
- self.rope = RotaryPositionEmbedding(
97
- head_dim=self.head_dim,
98
- max_seq_len=config.max_seq_len,
99
- base=config.rope_base,
100
- )
101
- else:
102
- self.rope = None
103
-
104
- def forward(
105
- self,
106
- x: torch.Tensor,
107
- padding_mask: Optional[torch.Tensor] = None,
108
- kv_cache_k: Optional[torch.Tensor] = None,
109
- kv_cache_v: Optional[torch.Tensor] = None,
110
- position_offset: int = 0,
111
- ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
112
- """
113
- Args:
114
- x: [batch, seq_len, embed_dim]
115
- padding_mask: [batch, seq_len] — 1 para token real, 0 para PAD
116
- (se None, não aplica)
117
- kv_cache_k, kv_cache_v: cache KV de passos anteriores
118
- [batch, n_heads, cached_len, head_dim] ou None
119
- position_offset: offset posicional (para uso com cache KV)
120
-
121
- Returns:
122
- out: [batch, seq_len, embed_dim]
123
- new_k: [batch, n_heads, total_len, head_dim] — K atualizado para cache
124
- new_v: [batch, n_heads, total_len, head_dim] — V atualizado
125
- """
126
- B, T, C = x.size()
127
-
128
- # Projeção QKV unificada
129
- qkv = self.qkv_projection(x) # [B, T, 3*C]
130
- q, k, v = qkv.split(self.embed_dim, dim=2) # cada um: [B, T, C]
131
-
132
- # Reshape para multi-head: [B, n_heads, T, head_dim]
133
- q = q.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
134
- k = k.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
135
- v = v.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
136
-
137
- # Aplicar RoPE a Q e K (não a V)
138
- if self.use_rope and self.rope is not None:
139
- q = self.rope(q, offset=position_offset)
140
- k = self.rope(k, offset=position_offset)
141
-
142
- # Concatenar com cache KV (se fornecido)
143
- if kv_cache_k is not None and kv_cache_v is not None:
144
- k = torch.cat([kv_cache_k, k], dim=2) # [B, n_heads, cached+T, head_dim]
145
- v = torch.cat([kv_cache_v, v], dim=2)
146
- new_k, new_v = k, v
147
-
148
- total_len = k.size(2)
149
-
150
- # Atenção por produto escalar escalonado
151
- # scores: [B, n_heads, T, total_len]
152
- attn_scores = torch.matmul(q, k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim))
153
-
154
- # Máscara causal: o token na posição i só pode olhar para posições <= i
155
- # Em modo cache: pos i corresponde à posição absoluta (offset + i)
156
- # O token atual (offset+i) não pode olhar para futuros (offset+i+1, ...)
157
- # Como K contém [cached..., current_T], a máscara deve permitir que
158
- # cada q[j] (j=0..T-1) veja k[0..cached+j]
159
- if T > 0:
160
- # Índices absolutos de Q: [offset, offset+T-1]
161
- # Índices absolutos de K: [0, total_len-1]
162
- q_pos = torch.arange(position_offset, position_offset + T, device=x.device) # [T]
163
- k_pos = torch.arange(0, total_len, device=x.device) # [total_len]
164
- # permitido se k_pos <= q_pos
165
- causal_mask = k_pos.unsqueeze(0) <= q_pos.unsqueeze(1) # [T, total_len]
166
- # Aplicar: onde causal_mask==False → -inf
167
- attn_scores = attn_scores.masked_fill(
168
- ~causal_mask.unsqueeze(0).unsqueeze(0), # [1, 1, T, total_len]
169
- float('-inf')
170
- )
171
-
172
- # Máscara de padding (se fornecida)
173
- if padding_mask is not None:
174
- # padding_mask: [B, T_query] — mas a máscara deve afetar a chave (K)
175
- # Se um token K é PAD, sua atenção deve ser zero
176
- # Como o cache K pode conter tokens do passado que NÃO são PAD,
177
- # a máscara de padding deve cobrir todo o K (cached + atual)
178
- # Para simplicidade, assumimos que tokens cached não são PAD
179
- # e usamos padding_mask apenas para o K atual
180
- if padding_mask.dim() == 2:
181
- # [B, T] → [B, 1, 1, T]
182
- pad_mask_k = (padding_mask > 0).unsqueeze(1).unsqueeze(1)
183
- else:
184
- pad_mask_k = (padding_mask > 0).unsqueeze(1).unsqueeze(1)
185
- # Construir máscara completa sobre K
186
- if kv_cache_k is not None:
187
- cached_len = kv_cache_k.size(2)
188
- # Tokens cached: considerados válidos (não PAD)
189
- cached_part = torch.ones(
190
- B, 1, 1, cached_len, device=x.device, dtype=pad_mask_k.dtype
191
- )
192
- pad_mask_full = torch.cat([cached_part, pad_mask_k], dim=3)
193
- else:
194
- pad_mask_full = pad_mask_k
195
- attn_scores = attn_scores.masked_fill(~pad_mask_full, float('-inf'))
196
-
197
- # Softmax + dropout
198
- # Lidar com linhas totalmente -inf (todas as chaves são PAD)
199
- # Substituir -inf por 0 para evitar NaN
200
- all_neg = (attn_scores <= -1e9).all(dim=-1, keepdim=True)
201
- attn_scores = torch.where(all_neg, torch.zeros_like(attn_scores), attn_scores)
202
-
203
- attn_weights = F.softmax(attn_scores, dim=-1)
204
- attn_weights = self.attn_dropout(attn_weights)
205
-
206
- # Fusão dos contextos
207
- out = torch.matmul(attn_weights, v) # [B, n_heads, T, head_dim]
208
- out = out.transpose(1, 2).contiguous().view(B, T, C)
209
-
210
- out = self.resid_dropout(self.out_projection(out))
211
- return out, new_k, new_v
212
-
213
-
214
- # ============================================================================
215
- # Transformer Block
216
- # ============================================================================
217
-
218
- class TransformerBlock(nn.Module):
219
- """
220
- Bloco decodificador padrão com Pre-LN (conforme dados.txt linhas 594-612).
221
-
222
- Estrutura:
223
- x = x + attn(ln1(x))
224
- x = x + ffn(ln2(x))
225
- FFN: Linear(d, 4d) → GELU → Linear(4d, d) → Dropout
226
- """
227
-
228
- def __init__(self, config: TransformerBlockConfig):
229
- super().__init__()
230
- self.config = config
231
- self.ln1 = nn.LayerNorm(config.embed_dim)
232
- self.attn = CausalSelfAttention(config)
233
- self.ln2 = nn.LayerNorm(config.embed_dim)
234
- ff_dim = config.ff_dim or (4 * config.embed_dim)
235
- self.ffn = nn.Sequential(
236
- nn.Linear(config.embed_dim, ff_dim, bias=config.ffn_bias),
237
- nn.GELU(),
238
- nn.Linear(ff_dim, config.embed_dim, bias=config.ffn_bias),
239
- nn.Dropout(config.dropout),
240
- )
241
-
242
- def forward(
243
- self,
244
- x: torch.Tensor,
245
- padding_mask: Optional[torch.Tensor] = None,
246
- kv_cache_k: Optional[torch.Tensor] = None,
247
- kv_cache_v: Optional[torch.Tensor] = None,
248
- position_offset: int = 0,
249
- ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
250
- """
251
- Args:
252
- x: [batch, seq_len, embed_dim]
253
- padding_mask: [batch, seq_len] opcional
254
- kv_cache_k, kv_cache_v: cache KV opcional
255
- position_offset: offset posicional para RoPE
256
-
257
- Returns:
258
- out: [batch, seq_len, embed_dim]
259
- new_k: cache K atualizado
260
- new_v: cache V atualizado
261
- """
262
- # Pre-LN: normaliza antes de passar para a sub-camada
263
- normed = self.ln1(x)
264
- attn_out, new_k, new_v = self.attn(
265
- normed, padding_mask=padding_mask,
266
- kv_cache_k=kv_cache_k, kv_cache_v=kv_cache_v,
267
- position_offset=position_offset,
268
- )
269
- x = x + attn_out
270
-
271
- # FFN
272
- x = x + self.ffn(self.ln2(x))
273
- return x, new_k, new_v
274
-
275
-
276
- # ============================================================================
277
- # Stack de Transformer Blocks (Decoder completo)
278
- # ============================================================================
279
-
280
- class TransformerDecoderStack(nn.Module):
281
- """
282
- Empilhamento de N TransformerBlocks (decoder-only, estilo GPT).
283
-
284
- Conforme dados.txt linhas 614-648.
285
-
286
- Suporta weight tying (compartilhamento de pesos entre token_embedding e lm_head).
287
- """
288
-
289
- def __init__(
290
- self,
291
- vocab_size: int,
292
- embed_dim: int = 256,
293
- n_heads: int = 4,
294
- n_layers: int = 2,
295
- ff_dim: Optional[int] = None,
296
- dropout: float = 0.1,
297
- max_seq_len: int = 512,
298
- use_rope: bool = True,
299
- pad_id: int = 0,
300
- weight_tying: bool = True,
301
- ):
302
- super().__init__()
303
- self.vocab_size = vocab_size
304
- self.embed_dim = embed_dim
305
- self.max_seq_len = max_seq_len
306
- self.pad_id = pad_id
307
- self.weight_tying = weight_tying
308
-
309
- block_config = TransformerBlockConfig(
310
- embed_dim=embed_dim,
311
- n_heads=n_heads,
312
- ff_dim=ff_dim,
313
- dropout=dropout,
314
- use_rope=use_rope,
315
- max_seq_len=max_seq_len,
316
- )
317
-
318
- self.token_embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=pad_id)
319
- # Sem positional embedding separado quando RoPE está ativo
320
- if not use_rope:
321
- self.position_embedding = nn.Embedding(max_seq_len, embed_dim)
322
- else:
323
- self.position_embedding = None
324
-
325
- self.blocks = nn.ModuleList([
326
- TransformerBlock(block_config) for _ in range(n_layers)
327
- ])
328
- self.ln_f = nn.LayerNorm(embed_dim)
329
- self.lm_head = nn.Linear(embed_dim, vocab_size, bias=False)
330
-
331
- # Weight tying
332
- if weight_tying:
333
- self.lm_head.weight = self.token_embedding.weight
334
-
335
- def forward(
336
- self,
337
- idx: torch.Tensor,
338
- padding_mask: Optional[torch.Tensor] = None,
339
- kv_caches: Optional[list] = None,
340
- position_offset: int = 0,
341
- return_hidden: bool = False,
342
- ) -> torch.Tensor:
343
- """
344
- Args:
345
- idx: [batch, seq_len] — token IDs
346
- padding_mask: [batch, seq_len] — 1 para real, 0 para PAD
347
- kv_caches: lista de (k, v) por camada, ou None
348
- position_offset: offset posicional (para uso com cache)
349
- return_hidden: se True, retorna também o hidden state antes do lm_head
350
-
351
- Returns:
352
- logits: [batch, seq_len, vocab_size]
353
- hidden (opcional): [batch, seq_len, embed_dim]
354
- """
355
- B, T = idx.size()
356
- if T > self.max_seq_len:
357
- raise ValueError(
358
- f"Sequência de tamanho {T} excede o limite máximo configurado de "
359
- f"{self.max_seq_len}."
360
- )
361
-
362
- # Embedding
363
- x = self.token_embedding(idx)
364
- if not self.weight_tying and self.position_embedding is not None:
365
- positions = torch.arange(
366
- position_offset, position_offset + T,
367
- dtype=torch.long, device=idx.device
368
- ).unsqueeze(0)
369
- x = x + self.position_embedding(positions)
370
-
371
- # Passar pelos blocos
372
- new_caches = []
373
- for i, block in enumerate(self.blocks):
374
- cache_k = None
375
- cache_v = None
376
- if kv_caches is not None and i < len(kv_caches):
377
- cache_k, cache_v = kv_caches[i]
378
- x, new_k, new_v = block(
379
- x, padding_mask=padding_mask,
380
- kv_cache_k=cache_k, kv_cache_v=cache_v,
381
- position_offset=position_offset,
382
- )
383
- new_caches.append((new_k, new_v))
384
-
385
- # LayerNorm final
386
- x = self.ln_f(x)
387
-
388
- # LM head
389
- logits = self.lm_head(x)
390
-
391
- if return_hidden:
392
- return logits, x, new_caches
393
- return logits, new_caches
394
-
395
-
396
- __all__ = [
397
- "TransformerBlockConfig",
398
- "CausalSelfAttention",
399
- "TransformerBlock",
400
- "TransformerDecoderStack",
401
- ]