Objetos) 25 26 # Se usa una sola matriz grande por eficiencia de hardware ( fused projection ) self . W_qkv = nn . Linear ( d_model , d_model * 3 , bias = False ) 27 28 29 # Proyecci ó n de salida self . W_o = nn . Linear ( d_model , d_model , bias = False ) 30 31 32 self . dropout = nn . Dropout ( dropout ) self . scale = math . sqrt ( self . head_dim ) 33 34 35 36 37 38 39 40 41 42 43 def forward ( self , x : Tensor , mask : Tensor = None ) -> Tensor : """ Args : x : Tensor de entrada de forma ( batch_size , seq_len , d_model ) mask : Tensor opcional de forma ( batch_size , 1 , 1 , seq_len ) o ( batch_size , seq_len , seq_len ) para enmascarar posiciones futuras ( causal ) o tokens de padding . Returns : Tensor de salida de forma ( batch_size , seq_len , d_model ) """ batch_size , seq_len , _ = x . size () 44 45 46 47 # 1. Proyecci ó n lineal y divisi ó n en cabezas # qkv shape : ( batch_size , seq_len , 3 * d_model ) qkv = self . W_qkv ( x ) 48 49 50 51 # Dividir la ú ltima dimensi ó n en 3 (Q , K , V ) y luego en ( num_heads , head_dim ) # reshape a : ( batch_size , seq_len , 3 , num_heads , head_dim ) qkv = qkv . reshape ( batch_size , seq_len , 3 , self . num_heads , self . head_dim ) 52 53 54 # Permutar a : (3 , batch_size , num_heads , seq_len , head_dim ) qkv = qkv . permute (2 , 0 , 3 , 1 , 4) 55 56 57 # Desempaquetar Q , K , V q , k , v = qkv [0] , qkv [1] , qkv [2] seq_len , head_dim ) # Cada uno : ( batch_size , num_heads , 58 59 60 61 # 2. Scaled Dot - Product Attention # q @ k . transpose ( -2 , -1) -> ( batch_size , num_heads , seq_len , seq_len ) attn_scores = torch . matmul (q , k . transpose ( -2 , -1) ) / self . scale 62 63 64 65 66 67 # 3. Aplicar m á scara ( si existe ) if mask is not None : # La m á scara debe ser broadcasteable a ( batch_size , num_heads , seq_len , seq_len ) # Los valores enmascarados se establecen a -1 e9 ( aproximaci ó n de inf para estabilidad num é rica ) attn_scores = attn_scores . masked_fill ( mask == 0 , -1 e9 ) 68 69 70 71 # 4. Softmax y Dropout attn_weights = torch . softmax ( attn_scores , dim = -1) attn_weights = self . dropout ( attn_weights ) 72 73 74 75 # 5. Multiplicar por V # attn_weights @ v -> ( batch_size , num_heads , seq_len , head_dim ) context = torch . matmul ( attn_weights , v ) 76 77 78 # 6. Concatenar cabezas y proyectar de vuelta a d_model # Transponer y aplanar : ( batch_size , seq_len , num_heads , head_dim ) -> ( Abraham Zamudio 19