PowerMachine commited on
Commit
c82b8d4
·
verified ·
1 Parent(s): e96c019

Delete cnn_bigru/training/hypothesis_controller.py with huggingface_hub

Browse files
cnn_bigru/training/hypothesis_controller.py DELETED
@@ -1,276 +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
-
136
- def __init__(
137
- self,
138
- feat_dim: int,
139
- out_dim: int,
140
- config: HypothesisConfig,
141
- ):
142
- super().__init__()
143
- self.cfg = config
144
- self.feat_dim = feat_dim
145
- self.out_dim = out_dim
146
-
147
- # Camadas de hipótese paralelas
148
- self.hyp_layer = HypothesisLayer(
149
- in_dim=feat_dim,
150
- out_dim=out_dim,
151
- n_hypotheses=config.n_hypotheses,
152
- activation="gelu",
153
- )
154
-
155
- # Porta de ativação (gating) baseada em v
156
- # Se v alto, mantém identidade; se v baixo, ativa hipóteses
157
- self.gate = nn.Linear(feat_dim + 1, 1) # +1 para o valor v
158
-
159
- # Projeto para combinar com input original
160
- self.combine = nn.Linear(feat_dim + out_dim, out_dim)
161
-
162
- def forward(
163
- self,
164
- x: torch.Tensor,
165
- v: torch.Tensor,
166
- ) -> Dict[str, torch.Tensor]:
167
- """
168
- Args:
169
- x: [B, feat_dim]
170
- v: [B, 1] probabilidade do verificador (0 = erro, 1 = correto)
171
-
172
- Returns:
173
- dict com:
174
- output: [B, out_dim] features modificadas
175
- gate: [B, 1] valor da porta (0 = identity, 1 = hipóteses)
176
- weights: [B, N] pesos das hipóteses
177
- activated: bool se as hipóteses foram ativadas (em qualquer amostra)
178
- """
179
- # Porta: gate = Sigmoid(linear(concat(x, v)))
180
- # Como v baixo -> gate alto (ativa hipóteses), usamos (1 - v) na entrada
181
- gate_input = torch.cat((x, 1.0 - v), dim=-1) # [B, feat_dim + 1]
182
- gate = torch.sigmoid(self.gate(gate_input)) # [B, 1]
183
-
184
- # Hipóteses
185
- hyp_out, weights = self.hyp_layer(
186
- x, gumbel_temp=self.cfg.gumbel_temp, gumbel_hard=self.cfg.gumbel_hard
187
- )
188
-
189
- # Combina: out = combine(concat(x, hyp_out))
190
- combined = self.combine(torch.cat((x, hyp_out), dim=-1))
191
-
192
- # Aplica gate: se gate=0, mantém x (projetado); se gate=1, usa hipóteses
193
- # Projeção de x para out_dim
194
- x_proj = combined # placeholder; usamos combined como base
195
- output = gate * combined + (1 - gate) * x_proj
196
-
197
- # Verifica se alguma amostra foi punida
198
- activated = bool((v.squeeze(-1) < self.cfg.threshold_err).any().item())
199
-
200
- return {
201
- "output": output,
202
- "gate": gate,
203
- "weights": weights,
204
- "activated": activated,
205
- }
206
-
207
-
208
- class SynergySearcher:
209
- """Busca sinérgica: tenta N combinações de hiperparâmetros e escolhe a melhor.
210
-
211
- Dado um conjunto de "alavancas" (e.g. pesos das perdas, taxas de aprendizado),
212
- explora N configurações e seleciona aquela que produz menor perda em um
213
- mini-batch de validação.
214
-
215
- Não é diferenciável — é uma busca heurística no espaço de configurações.
216
- """
217
-
218
- def __init__(
219
- self,
220
- n_attempts: int = 4,
221
- seed: int = 42,
222
- ):
223
- self.n_attempts = n_attempts
224
- self.rng = random.Random(seed)
225
- self.history: List[Dict] = []
226
-
227
- def sample_config(self, base: Dict) -> Dict:
228
- """Amostra uma configuração perturbando a base."""
229
- cfg = dict(base)
230
- # Perturbação: multiplica pesos por fator aleatório próximo de 1
231
- for key in ["alpha", "beta", "gamma_loss", "delta", "lambda_penal", "mu_exp_penal"]:
232
- if key in cfg:
233
- factor = 1.0 + self.rng.uniform(-0.2, 0.2)
234
- cfg[key] = max(1e-6, cfg[key] * factor)
235
- return cfg
236
-
237
- def search(
238
- self,
239
- base_config: Dict,
240
- eval_fn,
241
- ) -> Dict:
242
- """Executa N tentativas e retorna a melhor configuração.
243
-
244
- Args:
245
- base_config: configuração base (dict de hiperparâmetros)
246
- eval_fn: função cfg -> loss (callable)
247
-
248
- Returns:
249
- melhor configuração encontrada
250
- """
251
- best_config = dict(base_config)
252
- best_loss = float("inf")
253
-
254
- for attempt in range(self.n_attempts):
255
- try:
256
- cfg = self.sample_config(base_config) if attempt > 0 else dict(base_config)
257
- loss = float(eval_fn(cfg))
258
- self.history.append({"attempt": attempt, "config": cfg, "loss": loss})
259
- logger.info("SynergySearch attempt %d: loss=%.4f", attempt, loss)
260
- if loss < best_loss:
261
- best_loss = loss
262
- best_config = cfg
263
- except Exception as e:
264
- logger.warning("SynergySearch attempt %d falhou: %s", attempt, e)
265
- continue
266
-
267
- logger.info("SynergySearch melhor loss=%.4f", best_loss)
268
- return best_config
269
-
270
-
271
- __all__ = [
272
- "HypothesisConfig",
273
- "HypothesisLayer",
274
- "HypothesisController",
275
- "SynergySearcher",
276
- ]