PowerMachine commited on
Commit
3fbe9e3
·
verified ·
1 Parent(s): a5981f0

Delete training/hypothesis_controller.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. training/hypothesis_controller.py +0 -290
training/hypothesis_controller.py DELETED
@@ -1,290 +0,0 @@
1
- """hypothesis_controller.py — Mecanismo de hipóteses para autoaprendizado.
2
-
3
- Implementa o "acionamento de hipóteses quando ocorre punições no treinamento"
4
- descrito pelo usuário. Quando o verificador pune um passo (v < threshold),
5
- o sistema ativa camadas de hipótese que tentam N combinações alternativas
6
- de sinergias entre as redes, buscando a configuração de menor perda.
7
-
8
- Matemática:
9
- Quando v_t < THRESHOLD_ERR:
10
- hipótese_i = f_alternativa_i(input, params_perturbados_i)
11
- loss_i = compute_loss(hipótese_i)
12
- melhor = argmin(loss_i)
13
- params <- params + lr * grad(melhor)
14
-
15
- As "camadas de hipótese" são camadas lineares paralelas (hipóteses)
16
- ativadas condicionalmente. A ativação é diferenciável via Gumbel-Softmax.
17
- """
18
- from __future__ import annotations
19
-
20
- import logging
21
- import math
22
- import random
23
- from dataclasses import dataclass
24
- from typing import Dict, List, Optional, Tuple
25
-
26
- import torch
27
- import torch.nn as nn
28
- import torch.nn.functional as F
29
-
30
- logger = logging.getLogger(__name__)
31
-
32
-
33
- @dataclass
34
- class HypothesisConfig:
35
- """Configuração do mecanismo de hipóteses."""
36
- n_hypotheses: int = 4 # N tentativas de acerto
37
- threshold_err: float = 0.5 # abaixo deste valor, ativa hipóteses
38
- gumbel_temp: float = 1.0 # temperatura do Gumbel-Softmax
39
- gumbel_hard: bool = False # se True, usa argmax (não diferenciável)
40
- perturbation_std: float = 0.05 # desvio padrão da perturbação
41
-
42
-
43
- class HypothesisLayer(nn.Module):
44
- """Camada de hipótese: N transformações lineares paralelas.
45
-
46
- Para cada amostra, calcula N hipóteses e seleciona a melhor via
47
- Gumbel-Softmax (diferenciável) ou argmax (não diferenciável).
48
-
49
- A "seleção" é feita baseada em um score externo (e.g. negativo da perda).
50
- """
51
-
52
- def __init__(
53
- self,
54
- in_dim: int,
55
- out_dim: int,
56
- n_hypotheses: int = 4,
57
- activation: str = "relu",
58
- ):
59
- super().__init__()
60
- self.n_hypotheses = n_hypotheses
61
- self.in_dim = in_dim
62
- self.out_dim = out_dim
63
-
64
- # N transformações lineares paralelas
65
- self.hypotheses = nn.ModuleList([
66
- nn.Linear(in_dim, out_dim) for _ in range(n_hypotheses)
67
- ])
68
- # Inicialização diferente para cada hipótese (diversidade)
69
- for i, h in enumerate(self.hypotheses):
70
- nn.init.xavier_uniform_(h.weight, gain=0.5 + 0.2 * i)
71
- nn.init.zeros_(h.bias)
72
-
73
- # Scoreador: aprende a pontuar cada hipótese dado o input
74
- self.scorer = nn.Linear(in_dim, n_hypotheses)
75
-
76
- self.activation = activation
77
-
78
- def _activate(self, x: torch.Tensor) -> torch.Tensor:
79
- if self.activation == "relu":
80
- return F.relu(x)
81
- elif self.activation == "gelu":
82
- return F.gelu(x)
83
- elif self.activation == "tanh":
84
- return torch.tanh(x)
85
- return x
86
-
87
- def forward(
88
- self,
89
- x: torch.Tensor,
90
- gumbel_temp: float = 1.0,
91
- gumbel_hard: bool = False,
92
- ) -> Tuple[torch.Tensor, torch.Tensor]:
93
- """
94
- Args:
95
- x: [B, in_dim]
96
- gumbel_temp: temperatura do Gumbel-Softmax
97
- gumbel_hard: se True, usa argmax
98
-
99
- Returns:
100
- output: [B, out_dim]
101
- weights: [B, n_hypotheses]
102
- """
103
- B = x.size(0)
104
-
105
- # Calcula todas as N hipóteses: [B, N, out_dim]
106
- hyps = torch.stack([self._activate(h(x)) for h in self.hypotheses], dim=1)
107
-
108
- # Scoreia cada hipótese
109
- scores = self.scorer(x) # [B, N]
110
-
111
- # Gumbel-Softmax para seleção diferenciável
112
- if gumbel_hard:
113
- # Hard: argmax one-hot
114
- weights = F.one_hot(scores.argmax(dim=-1), num_classes=self.n_hypotheses).float()
115
- else:
116
- weights = F.gumbel_softmax(scores, tau=gumbel_temp, hard=False)
117
-
118
- # Combina: weighted sum
119
- output = (hyps * weights.unsqueeze(-1)).sum(dim=1) # [B, out_dim]
120
-
121
- return output, weights
122
-
123
-
124
- class HypothesisController(nn.Module):
125
- """Controlador de hipóteses: ativa camadas quando há punição.
126
-
127
- Recebe:
128
- - features do passo atual
129
- - valor v do verificador (probabilidade de correção)
130
- - loss atual
131
-
132
- Se v < threshold, ativa as hipóteses e tenta N combinações.
133
- Caso contrário, passa direto (identity).
134
-
135
- CORREÇÃO: O bug original era `x_proj = combined` que tornava
136
- `output = gate * combined + (1 - gate) * combined = combined` (sempre).
137
- Agora `x_proj` é uma projeção LINEAR REAL do input x (não o combined),
138
- permitindo que o gate efetivamente escolha entre:
139
- - gate=1: usar saída das hipóteses (combined)
140
- - gate=0: usar projeção do input original (x_proj)
141
- """
142
-
143
- def __init__(
144
- self,
145
- feat_dim: int,
146
- out_dim: int,
147
- config: HypothesisConfig,
148
- ):
149
- super().__init__()
150
- self.cfg = config
151
- self.feat_dim = feat_dim
152
- self.out_dim = out_dim
153
-
154
- # Camadas de hipótese paralelas
155
- self.hyp_layer = HypothesisLayer(
156
- in_dim=feat_dim,
157
- out_dim=out_dim,
158
- n_hypotheses=config.n_hypotheses,
159
- activation="gelu",
160
- )
161
-
162
- # Porta de ativação (gating) baseada em v
163
- # Se v alto, mantém identidade; se v baixo, ativa hipóteses
164
- self.gate = nn.Linear(feat_dim + 1, 1) # +1 para o valor v
165
-
166
- # Projeto para combinar hipótese + input
167
- self.combine = nn.Linear(feat_dim + out_dim, out_dim)
168
-
169
- # NOVO: projeção separada do input x para out_dim
170
- # Esta é a "via identity" — usada quando gate=0 (v alto, sem punição)
171
- self.x_proj = nn.Linear(feat_dim, out_dim)
172
- nn.init.xavier_uniform_(self.x_proj.weight, gain=0.5)
173
- nn.init.zeros_(self.x_proj.bias)
174
-
175
- def forward(
176
- self,
177
- x: torch.Tensor,
178
- v: torch.Tensor,
179
- ) -> Dict[str, torch.Tensor]:
180
- """
181
- Args:
182
- x: [B, feat_dim]
183
- v: [B, 1] probabilidade do verificador (0 = erro, 1 = correto)
184
-
185
- Returns:
186
- dict com:
187
- output: [B, out_dim] features modificadas
188
- gate: [B, 1] valor da porta (0 = identity, 1 = hipóteses)
189
- weights: [B, N] pesos das hipóteses
190
- activated: bool se as hipóteses foram ativadas (em qualquer amostra)
191
- """
192
- # Porta: gate = Sigmoid(linear(concat(x, 1-v)))
193
- # v baixo → 1-v alto → gate alto (ativa hipóteses)
194
- gate_input = torch.cat((x, 1.0 - v), dim=-1) # [B, feat_dim + 1]
195
- gate = torch.sigmoid(self.gate(gate_input)) # [B, 1]
196
-
197
- # Hipóteses (N tentativas paralelas)
198
- hyp_out, weights = self.hyp_layer(
199
- x, gumbel_temp=self.cfg.gumbel_temp, gumbel_hard=self.cfg.gumbel_hard
200
- )
201
-
202
- # Via das hipóteses: combina input original com saída das hipóteses
203
- combined = self.combine(torch.cat((x, hyp_out), dim=-1)) # [B, out_dim]
204
-
205
- # Via identidade: projeção direta do input (sem usar hipóteses)
206
- x_projected = self.x_proj(x) # [B, out_dim]
207
-
208
- # Interpolação: gate=1 → hipóteses; gate=0 → identidade
209
- output = gate * combined + (1.0 - gate) * x_projected
210
-
211
- # Verifica se alguma amostra foi punida
212
- activated = bool((v.squeeze(-1) < self.cfg.threshold_err).any().item())
213
-
214
- return {
215
- "output": output,
216
- "gate": gate,
217
- "weights": weights,
218
- "activated": activated,
219
- }
220
-
221
-
222
- class SynergySearcher:
223
- """Busca sinérgica: tenta N combinações de hiperparâmetros e escolhe a melhor.
224
-
225
- Dado um conjunto de "alavancas" (e.g. pesos das perdas, taxas de aprendizado),
226
- explora N configurações e seleciona aquela que produz menor perda em um
227
- mini-batch de validação.
228
-
229
- Não é diferenciável — é uma busca heurística no espaço de configurações.
230
- """
231
-
232
- def __init__(
233
- self,
234
- n_attempts: int = 4,
235
- seed: int = 42,
236
- ):
237
- self.n_attempts = n_attempts
238
- self.rng = random.Random(seed)
239
- self.history: List[Dict] = []
240
-
241
- def sample_config(self, base: Dict) -> Dict:
242
- """Amostra uma configuração perturbando a base."""
243
- cfg = dict(base)
244
- # Perturbação: multiplica pesos por fator aleatório próximo de 1
245
- for key in ["alpha", "beta", "gamma_loss", "delta", "lambda_penal", "mu_exp_penal"]:
246
- if key in cfg:
247
- factor = 1.0 + self.rng.uniform(-0.2, 0.2)
248
- cfg[key] = max(1e-6, cfg[key] * factor)
249
- return cfg
250
-
251
- def search(
252
- self,
253
- base_config: Dict,
254
- eval_fn,
255
- ) -> Dict:
256
- """Executa N tentativas e retorna a melhor configuração.
257
-
258
- Args:
259
- base_config: configuração base (dict de hiperparâmetros)
260
- eval_fn: função cfg -> loss (callable)
261
-
262
- Returns:
263
- melhor configuração encontrada
264
- """
265
- best_config = dict(base_config)
266
- best_loss = float("inf")
267
-
268
- for attempt in range(self.n_attempts):
269
- try:
270
- cfg = self.sample_config(base_config) if attempt > 0 else dict(base_config)
271
- loss = float(eval_fn(cfg))
272
- self.history.append({"attempt": attempt, "config": cfg, "loss": loss})
273
- logger.info("SynergySearch attempt %d: loss=%.4f", attempt, loss)
274
- if loss < best_loss:
275
- best_loss = loss
276
- best_config = cfg
277
- except Exception as e:
278
- logger.warning("SynergySearch attempt %d falhou: %s", attempt, e)
279
- continue
280
-
281
- logger.info("SynergySearch melhor loss=%.4f", best_loss)
282
- return best_config
283
-
284
-
285
- __all__ = [
286
- "HypothesisConfig",
287
- "HypothesisLayer",
288
- "HypothesisController",
289
- "SynergySearcher",
290
- ]