PowerMachine commited on
Commit
681d907
·
verified ·
1 Parent(s): 1b7cb43

Upload cnn_bigru/losses/losses.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. cnn_bigru/losses/losses.py +207 -0
cnn_bigru/losses/losses.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """losses.py — Sistema de múltiplas perdas para o CNN-BiGRU cooperativo.
2
+
3
+ Implementa as perdas descritas na seção 8 de dados.txt:
4
+
5
+ L_G = CrossEntropy(logits_prova, prova_alvo)
6
+ L_penal = λ * Σ (1 - v_t) (penalidade linear)
7
+ L_exp_penal = μ * Σ exp(γ * (1 - v_t)) (penalidade exponencial)
8
+ L_V = BCE(v_t, rotulo_real) (perda do verificador)
9
+ L_AH = Σ (1 - τ_t) (perda anti-alucinação)
10
+ L_reg = L2 + estimativa de curvatura (regularização)
11
+ L_total = α * L_G_total + β * L_V + γ_loss * L_AH + δ * L_reg
12
+
13
+ onde:
14
+ L_G_total = L_G + λ_penal * L_penal + μ_exp * L_exp_penal
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ import math
20
+ from dataclasses import dataclass, field
21
+ from typing import Dict, List, Optional
22
+
23
+ import torch
24
+ import torch.nn as nn
25
+ import torch.nn.functional as F
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ @dataclass
31
+ class LossConfig:
32
+ """Configuração das perdas e pesos."""
33
+ # Pesos das perdas (ALPHA, BETA, GAMMA, DELTA do pseudocódigo)
34
+ alpha: float = 1.0 # peso de L_G_total
35
+ beta: float = 0.5 # peso de L_V
36
+ gamma_loss: float = 0.3 # peso de L_AH
37
+ delta: float = 0.01 # peso de L_reg
38
+
39
+ # Penalidades
40
+ lambda_penal: float = 0.1 # LAMBDA_PENAL
41
+ mu_exp_penal: float = 0.05 # MU_EXP_PENAL
42
+ gamma_exp: float = 1.0 # GAMMA_EXP
43
+ threshold_err: float = 0.5 # THRESHOLD_ERR para penalidade exponencial
44
+
45
+ # Regularização
46
+ l2_reg: float = 1e-5
47
+
48
+ # Curvatura
49
+ use_curvature: bool = True
50
+ curvature_eps: float = 1e-3
51
+
52
+
53
+ class MultiLoss(nn.Module):
54
+ """Computa todas as perdas do sistema cooperativo.
55
+
56
+ Uso:
57
+ loss_fn = MultiLoss(cfg)
58
+ result = loss_fn(logits, targets, v_list, tau_list, generator, verifier)
59
+ loss = result['total']
60
+ loss.backward()
61
+ """
62
+
63
+ def __init__(self, config: LossConfig, pad_idx: int = 1):
64
+ super().__init__()
65
+ self.cfg = config
66
+ self.pad_idx = pad_idx
67
+ self.ce = nn.CrossEntropyLoss(ignore_index=pad_idx, reduction='mean')
68
+ self.bce = nn.BCELoss(reduction='mean')
69
+
70
+ def generation_loss(
71
+ self,
72
+ logits: torch.Tensor, # [B, T, V]
73
+ targets: torch.Tensor, # [B, T]
74
+ ) -> torch.Tensor:
75
+ """L_G: CrossEntropy ignorando padding."""
76
+ B, T, V = logits.shape
77
+ return self.ce(logits.reshape(B * T, V), targets.reshape(B * T))
78
+
79
+ def penalty_loss(
80
+ self,
81
+ v_list: List[torch.Tensor], # lista de [B, 1] probabilidades do verificador
82
+ ) -> Dict[str, torch.Tensor]:
83
+ """Calcula L_penal (linear) e L_exp_penal (exponencial)."""
84
+ if not v_list:
85
+ zero = torch.tensor(0.0)
86
+ return {"linear": zero, "exp": zero}
87
+
88
+ # Stack: [T, B, 1] -> [T, B]
89
+ v_stack = torch.stack([v.squeeze(-1) if v.dim() == 2 else v for v in v_list], dim=0)
90
+ # Penalidade linear: Σ (1 - v)
91
+ l_penal = (1.0 - v_stack).sum() / max(v_stack.numel(), 1)
92
+
93
+ # Penalidade exponencial: Σ exp(γ * (1 - v)) onde v < threshold
94
+ mask = v_stack < self.cfg.threshold_err
95
+ if mask.any():
96
+ # exp(γ * (1 - v)) para os graves
97
+ exp_vals = torch.exp(self.cfg.gamma_exp * (1.0 - v_stack[mask]))
98
+ l_exp = exp_vals.sum() / max(v_stack.numel(), 1)
99
+ else:
100
+ l_exp = torch.tensor(0.0, device=v_stack.device)
101
+
102
+ return {"linear": l_penal, "exp": l_exp}
103
+
104
+ def verifier_loss(
105
+ self,
106
+ v_list: List[torch.Tensor],
107
+ labels: List[torch.Tensor], # lista de [B, 1] com 0/1
108
+ ) -> torch.Tensor:
109
+ """L_V: BCE entre v_t e rotulo_real."""
110
+ if not v_list:
111
+ return torch.tensor(0.0)
112
+ total = torch.tensor(0.0)
113
+ for v, label in zip(v_list, labels):
114
+ v_flat = (v.squeeze(-1) if v.dim() == 2 else v).clamp(1e-7, 1 - 1e-7)
115
+ label_flat = label.squeeze(-1) if label.dim() == 2 else label
116
+ total = total + self.bce(v_flat, label_flat.float())
117
+ return total / max(len(v_list), 1)
118
+
119
+ def anti_hallucination_loss(
120
+ self,
121
+ tau_list: List[torch.Tensor], # lista de [B, 1] valores de verdade suave
122
+ ) -> torch.Tensor:
123
+ """L_AH = Σ (1 - τ_t)."""
124
+ if not tau_list:
125
+ return torch.tensor(0.0)
126
+ total = torch.tensor(0.0)
127
+ for tau in tau_list:
128
+ tau_flat = tau.squeeze(-1) if tau.dim() == 2 else tau
129
+ total = total + (1.0 - tau_flat).mean()
130
+ return total / max(len(tau_list), 1)
131
+
132
+ def regularization_loss(
133
+ self,
134
+ models: List[nn.Module],
135
+ ) -> torch.Tensor:
136
+ """L_reg = L2 + estimativa de curvatura (aproximada via norma de pesos)."""
137
+ l2 = torch.tensor(0.0)
138
+ for model in models:
139
+ for p in model.parameters():
140
+ if p.requires_grad:
141
+ l2 = l2 + p.pow(2).sum()
142
+ l2 = self.cfg.l2_reg * l2
143
+
144
+ # Estimativa de curvatura aproximada: variância das normas dos gradientes
145
+ # (calculada externamente e passada via attr, aqui só placeholder)
146
+ curv = getattr(self, '_curvature_estimate', torch.tensor(0.0))
147
+ return l2 + (curv if isinstance(curv, torch.Tensor) else torch.tensor(float(curv)))
148
+
149
+ def set_curvature_estimate(self, value: torch.Tensor) -> None:
150
+ """Define a estimativa de curvatura (calculada externamente)."""
151
+ self._curvature_estimate = value
152
+
153
+ def forward(
154
+ self,
155
+ logits: torch.Tensor,
156
+ targets: torch.Tensor,
157
+ v_list: List[torch.Tensor],
158
+ tau_list: List[torch.Tensor],
159
+ verifier_labels: Optional[List[torch.Tensor]] = None,
160
+ models_for_reg: Optional[List[nn.Module]] = None,
161
+ ) -> Dict[str, torch.Tensor]:
162
+ """Computa todas as perdas e combina."""
163
+ # L_G
164
+ l_g = self.generation_loss(logits, targets)
165
+
166
+ # Penalidades
167
+ pens = self.penalty_loss(v_list)
168
+ l_g_total = l_g + self.cfg.lambda_penal * pens["linear"] + self.cfg.mu_exp_penal * pens["exp"]
169
+
170
+ # L_V
171
+ if verifier_labels is not None:
172
+ l_v = self.verifier_loss(v_list, verifier_labels)
173
+ else:
174
+ # Sem rótulos reais: assume que v deveria ser 1 (passo correto)
175
+ labels_ones = [torch.ones_like(v) for v in v_list]
176
+ l_v = self.verifier_loss(v_list, labels_ones)
177
+
178
+ # L_AH
179
+ l_ah = self.anti_hallucination_loss(tau_list)
180
+
181
+ # L_reg
182
+ if models_for_reg:
183
+ l_reg = self.regularization_loss(models_for_reg)
184
+ else:
185
+ l_reg = torch.tensor(0.0)
186
+
187
+ # Combinação
188
+ total = (
189
+ self.cfg.alpha * l_g_total
190
+ + self.cfg.beta * l_v
191
+ + self.cfg.gamma_loss * l_ah
192
+ + self.cfg.delta * l_reg
193
+ )
194
+
195
+ return {
196
+ "total": total,
197
+ "l_g": l_g,
198
+ "l_g_total": l_g_total,
199
+ "l_penal_linear": pens["linear"],
200
+ "l_penal_exp": pens["exp"],
201
+ "l_v": l_v,
202
+ "l_ah": l_ah,
203
+ "l_reg": l_reg,
204
+ }
205
+
206
+
207
+ __all__ = ["LossConfig", "MultiLoss"]