File size: 6,701 Bytes
a2d6a0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# Mixed Selectivity: FFN Compartido + Modulación Contextual (FiLM)

> Última actualización: Abril 2026  
> Autor: Lucas (concepto) + implementación en PamparV3

---

## Resumen

PamparV3 reemplaza las **4 copias independientes de StreamFFN** (una por territorio) con **1 FFN compartido** + **4 ContextModulators** por nivel. El mismo bloque de pesos se lee de 4 formas distintas según un vector contextual de 63 dimensiones.

**Resultado:** 62.6M params (antes ~105M) — **40% de reducción** sin perder capacidad expresiva.

---

## Motivación

### Neurociencia: "Mixed Selectivity"

Una neurona cortical no responde a un solo estímulo. Rigotti et al. (2013) demostraron que las neuronas exhiben **selectividad mixta**: la misma neurona que responde a "ubicación" también codifica "tiempo" y "contexto de tarea". Esta propiedad es _necesaria_ para computación cognitiva compleja.

### La conexión con PamparV3

PamparV3 ya tiene un sistema de routing (Tálamo) que genera:

- `zona_acts [B, L, 52]` — activación de 52 zonas de Brodmann (tipo de token)
- `terr_acts [B, L, 4]` — pesos de los 4 territorios (sintaxis, semántica, lógico, estructural)

La idea de Lucas: _"Si ya sabemos QUÉ tipo de token es y QUÉ territorio domina... ¿por qué no usar esa info para LEER el mismo FFN de forma diferente en vez de tener 4 copias?"_

---

## Diseño técnico

### Vector contextual (63 dimensiones)

```

ctx = [zona_acts(52), terr_acts(4), depth(1), conf(1), n_levels(1), stream_one_hot(4)]

       ─────────── ──────────── ──────── ─────── ──────────── ────────────────

       Tipo token   Dominio     Nivel    Confianza  Meta         Identidad

```

| Indicador   | Dims | Fuente                      | Interpretación                     |
| ----------- | ---- | --------------------------- | ---------------------------------- |
| `zona_acts` | 52   | TálamoInicial               | keyword, variable, string, etc.    |
| `terr_acts` | 4    | TálamoInicial               | peso por territorio                |
| `depth`     | 1    | `nivel_idx / n_levels`      | 0.0=superficial, 1.0=profundo      |
| `conf`      | 1    | `exit_head` (con `no_grad`) | 0-1, ¿el modelo ya entendió?       |
| `n_levels`  | 1    | `config.n_niveles / 10`     | normalización del modelo           |
| `stream_oh` | 4    | one-hot del stream actual   | identidad del stream que se modula |

### ContextModulator (FiLM)

```python

class ContextModulator(nn.Module):

    CONTEXT_DIM = 63



    def __init__(self, dim: int, bottleneck: int = 128):

        self.proj = nn.Sequential(

            nn.Linear(63, bottleneck),    # comprimir

            nn.SiLU(),

            nn.Linear(bottleneck, dim*2), # generar gamma + beta

        )

        # La última capa inicia en zeros → gamma≈0, beta≈0 → identidad



    def forward(self, ffn_out, zona_acts, terr_acts, stream_idx, nivel_idx, n_levels, conf):

        ctx = self._build_context(zona_acts, terr_acts, stream_idx, nivel_idx, n_levels, conf)

        gamma, beta = self.proj(ctx).chunk(2, dim=-1)

        return (1 + gamma) * ffn_out + beta

```

La fórmula FiLM `(1 + γ) · x + β`:

- **γ (gamma)** escala cada dimensión — amplifica features relevantes, suprime irrelevantes
- **β (beta)** desplaza — inyecta información contextual que el FFN base no tiene
- Al iniciar con γ=0, β=0 → pasa el FFN sin modificar → entrenamiento estable

### Flujo en NivelProfundo

```

1. Combinar: x_combined = Σ streams[t] × terr_acts[:,:,t]

2. Atención: x_attn = BloqueAttn(x_combined)

3. Re-route: zona_acts actualizado = TálamoNivel(x_attn)

          → conf_value = exit_head(x_combined + x_attn)  [no_grad]

4. FFN:     h_base = ffn_shared(norm(stream + x_attn))     ← 1 sola FFN

5. Modular: h_mod = modulator_t(h_base, ctx)               ← 4 modulators

6. Weight:  h = h_mod × terr_acts[:,:,t]                   ← territorial gating

7. Lateral: fibras blancas entre streams

8. Exit?:   si conf > 0.90 → salir temprano

```

---

## Conteo de parámetros

| Componente                | Legacy (4 FFN)    | Mixed Selectivity |
| ------------------------- | ----------------- | ----------------- |
| Embeddings (tok_emb/head) | 30.7M             | 30.7M             |

| Atención GQA ×5           | 5.1M              | 5.1M              |

| **StreamFFN**             | **4× ×5 = 65.5M** | **1× ×5 = 16.4M** |

| **ContextModulators**     | —                 | **4× ×5 = 3.4M**  |

| LateralGates ×5           | 3.3M              | 3.3M              |

| Tálamo + routing + norms  | ~6M               | ~6M               |

| **TOTAL**                 | **~105M**         | **~62.6M**        |



**Ahorro neto: 42.4M params (40%)**



---



## Configuración



En `ConfigV3`:



```python

use_mixed_selectivity: bool = True   # True = compartido + modulators

modulator_bottleneck: int = 128      # tamaño intermedio del modulator
```



`use_mixed_selectivity=False` restaura el comportamiento original con 4 FFNs independientes. Los checkpoints del modo legacy **no son compatibles** con el modo mixed (keys diferentes en state_dict).



---



## Archivos modificados



| Archivo                             | Cambio                                        |

| ----------------------------------- | --------------------------------------------- |

| `pampar/coder/v3/bloques.py`        | +ContextModulator, NivelProfundo init/forward |

| `pampar/coder/v3/config.py`         | +use_mixed_selectivity, +modulator_bottleneck |

| `pampar/coder/v3/modelo.py`         | checkpointing con zona_acts, docstring        |

| `scripts/test_mixed_selectivity.py` | Test de compilación + forward pass            |



---



## Posibilidades futuras



1. **Más profundidad:** Con 42M ahorrados, subir de 5 a 8+ niveles manteniendo ~105M.

2. **Más streams:** De 4 a 6-8 especialidades. Costo marginal: solo modulators extra (~170K c/u).

3. **Dimensión mayor:** Subir dim de 640 a ~830 para vectores más expresivos.

4. **Cross-level modulators:** Compartir el FFN entre NIVELES también (no solo streams).

5. **Adaptive bottleneck:** El tamaño del modulator podría crecer con la profundidad.



---



## Referencias



- Rigotti, M. et al. (2013). _The importance of mixed selectivity in complex cognitive tasks._ Nature.

- Perez, E. et al. (2018). _FiLM: Visual Reasoning with a General Conditioning Layer._ AAAI.

- Anthropic (2022). _Superposition in Neural Networks._