v0.7.0: FNH gate de punição + árvore de pesos indexada (arestas duplas) + quebra-cabeça do Pensamento + NLP/NLG + decodificador especulativo + auto-ajuste SOM limitado + correções v070-1/2/3 — 73/73 gates, 84/84 cargo, provas r-28..r-32
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .cargo/config.toml +2 -0
- .gitattributes +2 -0
- Cargo.lock +1 -1
- Cargo.toml +1 -1
- docs/memorias/r-28-analise-repos-design-v070.md +194 -0
- docs/memorias/r-29-provas-fnh-rotacao-v070.md +203 -0
- docs/memorias/r-30-provas-arvore-quebra-cabeca-v070.md +205 -0
- docs/memorias/r-31-provas-nlp-nlg-especulativo-v070.md +185 -0
- docs/memorias/r-32-resultados-v070.md +126 -0
- pyproject.toml +1 -1
- resultados/arvore_quebra_v070.json +199 -0
- resultados/gerador_conceitos.json +3 -0
- resultados/suite_fnh_v070.json +32 -0
- resultados/suite_nlp_nlg_v070.json +65 -0
- resultados/suite_v070_final.json +87 -0
- resultados/telemetria_fase_c.jsonl +0 -0
- resultados/telemetria_v070.json +555 -0
- resultados/tokenizador_conceitos.json +3 -0
- resultados/treino_v070.json +182 -0
- scripts/61_treino_v070.py +177 -0
- scripts/62_suite_fnh.py +72 -0
- scripts/63_arvore_quebra.py +78 -0
- scripts/64_suite_nlp_nlg.py +54 -0
- scripts/65_telemetria_v070.py +93 -0
- scripts/66_suite_v070.py +74 -0
- scripts/67_publicar_v070.py +158 -0
- src/amostrador.rs +56 -5
- src/arvore_pesos.rs +395 -0
- src/camadas.rs +7 -0
- src/controlador.rs +163 -0
- src/fnh.rs +601 -0
- src/lib.rs +438 -1
- src/nlg.rs +431 -0
- src/nlp.rs +252 -0
- src/pensamento.rs +361 -1
- src/stiefel.rs +17 -0
- src/telemetria.rs +63 -0
- src/treino.rs +9 -4
- src/tronco.rs +36 -8
- target/.rustc_info.json +1 -0
- target/release/.cargo-artifact-lock +0 -0
- target/release/.cargo-build-lock +0 -0
- target/release/.cargo-lock +0 -0
- target/release/.fingerprint/ahash-1239384bf8e0b488/dep-lib-ahash +0 -0
- target/release/.fingerprint/ahash-1239384bf8e0b488/invoked.timestamp +1 -0
- target/release/.fingerprint/ahash-1239384bf8e0b488/lib-ahash +1 -0
- target/release/.fingerprint/ahash-1239384bf8e0b488/lib-ahash.json +1 -0
- target/release/.fingerprint/ahash-83fe43707d427afc/build-script-build-script-build +1 -0
- target/release/.fingerprint/ahash-83fe43707d427afc/build-script-build-script-build.json +1 -0
- target/release/.fingerprint/ahash-83fe43707d427afc/dep-build-script-build-script-build +0 -0
.cargo/config.toml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build]
|
| 2 |
+
jobs = 2
|
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
resultados/gerador_conceitos.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
resultados/tokenizador_conceitos.json filter=lfs diff=lfs merge=lfs -text
|
Cargo.lock
CHANGED
|
@@ -734,7 +734,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
| 734 |
|
| 735 |
[[package]]
|
| 736 |
name = "khtst-rust"
|
| 737 |
-
version = "0.
|
| 738 |
dependencies = [
|
| 739 |
"anyhow",
|
| 740 |
"built",
|
|
|
|
| 734 |
|
| 735 |
[[package]]
|
| 736 |
name = "khtst-rust"
|
| 737 |
+
version = "0.7.0"
|
| 738 |
dependencies = [
|
| 739 |
"anyhow",
|
| 740 |
"built",
|
Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
[package]
|
| 2 |
name = "khtst-rust"
|
| 3 |
-
version = "0.
|
| 4 |
edition = "2021"
|
| 5 |
description = "KHTST_RUST — NCM com verificador calibrado, treino conjunto Pensamento↔backbone, MoEs redirecionáveis com atenção própria e autoajuste por telemetria"
|
| 6 |
license = "Apache-2.0"
|
|
|
|
| 1 |
[package]
|
| 2 |
name = "khtst-rust"
|
| 3 |
+
version = "0.7.0"
|
| 4 |
edition = "2021"
|
| 5 |
description = "KHTST_RUST — NCM com verificador calibrado, treino conjunto Pensamento↔backbone, MoEs redirecionáveis com atenção própria e autoajuste por telemetria"
|
| 6 |
license = "Apache-2.0"
|
docs/memorias/r-28-analise-repos-design-v070.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# r-28 — Análise dos repositórios PowerMachine e design da v0.7.0
|
| 2 |
+
|
| 3 |
+
Data: 2026-10-28 · Escopo: aprimoramentos da v0.7.0 a partir da análise das
|
| 4 |
+
introduções dos scripts de 4 repositórios + port da `fnh_layer.py` + módulos
|
| 5 |
+
NLG/NLP + árvore de pesos indexada + quebra-cabeça do Pensamento.
|
| 6 |
+
|
| 7 |
+
## 1. Auditoria da v0.6.0 (pré-requisito — 3 bugs reais corrigidos)
|
| 8 |
+
|
| 9 |
+
A reconstrução da baseline revelou que `tests/provas.rs` estava obsoleto
|
| 10 |
+
(v0.3.0 — APIs removidas). Ao reativar os gates de integração com as APIs
|
| 11 |
+
atuais, **três desvios reais** foram encontrados e corrigidos:
|
| 12 |
+
|
| 13 |
+
| id | achado | evidência | correção |
|
| 14 |
+
|---|---|---|---|
|
| 15 |
+
| v070-1 | FiLM sem `W_f` zero-init: r-04 §1 especifica `x̃ = x⊙(1+γ·tanh(W_f·h)) + β·(W_f·h)` com W_f=0 no nascimento; a v0.6.0 injetava o resumo do âncora DIRETO (projeção aleatória N(0,0.02)) | gate G5 falhava com dif=2,19 (níveis ≥ 2 perturbados no nascimento) | `Tronco::film_w: Option<Linear>` com `Linear::zeros_sem_vies` — identidade bit-a-bit para QUALQUER resumo (Teo R.2 restaurado) |
|
| 16 |
+
| v070-2 | FiLM aplicado SÓ em `forward_treino`; a geração incremental (`forward_nivel`) não o aplicava | violação de paridade treino↔inferência em TODAS as gerações v0.5/v0.6 (adba.ativo=true no config base) | `forward_nivel` ganhou `film: Option<(&Tensor, usize)>` + captura de estados pós-bloco-0; `AmostradorConceitos::gerar` acumula `estados_b0` por ramo (cond/uncond) e calcula o resumo-âncora exatamente como `forward_treino` (que usa o x PRÉ-FiLM — o cat é tardio) |
|
| 17 |
+
| v070-3 | `pcgrad_achatado` projetava g_i contra grads[j] JÁ MODIFICADOS (j<i) | gate M8 falhava: ⟨g_comb, g_1⟩ = −0,1 < 0 (não-piora violada); a versão tensor (`perdas.rs::pcgrad`) estava correta | snapshot dos originais antes do laço; projeção contra `originais[j]` (Yu et al. 2020, Algoritmo 1) |
|
| 18 |
+
|
| 19 |
+
Resultado: **56/56 testes** (54 inline + 2 gates de integração reativados).
|
| 20 |
+
Nenhum estado de treino existia (todos apagados), então a mudança de
|
| 21 |
+
arquitetura do W_f não invalida checkpoints.
|
| 22 |
+
|
| 23 |
+
## 2. Análise das introduções dos 4 repositórios
|
| 24 |
+
|
| 25 |
+
### 2.1 gru-ring-v13-9-2 (fonte da FNH — alvo do port Rust)
|
| 26 |
+
|
| 27 |
+
`fnh_layer.py` ("Flexible Network of Hypotheses", Teorema 4.2/4.3):
|
| 28 |
+
|
| 29 |
+
```
|
| 30 |
+
x_rot = x @ R_Atv^T (rotação aprendível na ativação)
|
| 31 |
+
W_eff = R_wp @ W_smooth (rotação aprendível no peso + suavização)
|
| 32 |
+
z = x_rot @ W_eff^T
|
| 33 |
+
y = Atv(z) + bias
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
- `SkewRotation`: R = expm(A), A skew-simétrica parametrizada por
|
| 37 |
+
θ ∈ R^{n(n−1)/2}; série de Taylor com 3 termos e clamp θ ≤ 0,5;
|
| 38 |
+
`orthogonality_error()` = ‖R^T R − I‖_F.
|
| 39 |
+
- `SmoothQuantCompressor` (Lema 4.1, Xiao et al. 2022):
|
| 40 |
+
s_j = max|X_j|^α / max|W_j|^(1−α), W_smooth = W·diag(s), X_smooth = X/diag(s)
|
| 41 |
+
— migra a escala da ativação para o peso (equilíbrio de canais).
|
| 42 |
+
- `FNHLayer`: ativações flexíveis (gelu/silu/mish/selu/relu/learnable).
|
| 43 |
+
|
| 44 |
+
**Decisão v0.7.0**: port para `fnh.rs` com DUAS melhorias sobre o original,
|
| 45 |
+
exigidas pelas provas da casa:
|
| 46 |
+
(a) **Ortogonalidade EXATA** em vez de truncada: a série de 3 termos do
|
| 47 |
+
original NÃO garante R ∈ SO(n) (erro O(θ⁴) por termo truncado — com θ=0,5 o
|
| 48 |
+
erro pode alcançar ~10⁻²); usamos a retração polar de `stiefel.rs`
|
| 49 |
+
(Newton–Schulz, já provada no projeto) sobre a exponencial truncada, dando
|
| 50 |
+
‖R^T R − I‖_F ≤ 10⁻⁶ comprovado por teste.
|
| 51 |
+
(b) **Gate de punição** (requisito do usuário): a camada só ativa quando a
|
| 52 |
+
punição normalizada π_t excede limiar θ_π — `g = σ((π_t − θ_π)/τ_g)` — e o
|
| 53 |
+
efeito é convexo: `W_eff = (1−g)·W + g·R_wp·(W·diag(s))`, nascendo neutra
|
| 54 |
+
(g=0 ⇒ bit-a-bit W). O original era incondicional.
|
| 55 |
+
|
| 56 |
+
### 2.2 BiGRU_T_version (hyp_t.py + som_auto_adjust.py)
|
| 57 |
+
|
| 58 |
+
**hyp_t.py** (V6.5-V3 — ensemble sinérgico de hipóteses): 4 pilares —
|
| 59 |
+
(1) DIVERSIDADE: k-means++ D² scoring na inicialização; (2) COOPERAÇÃO:
|
| 60 |
+
perda de coerência penaliza deltas que divergem do baricentro; (3) COMPETIÇÃO:
|
| 61 |
+
softmax gating winner-takes-more; (4) ORTOGONALIDADE: Gram-Schmidt suave
|
| 62 |
+
(penalidade na matriz de Gram G = DD^T). Protocolo em 2 fases: FASE1
|
| 63 |
+
(conhecimento — só o SOM organiza), FASE2 (punição — HypT gera deltas
|
| 64 |
+
corretivos quando o BMU não representa bem a entrada).
|
| 65 |
+
|
| 66 |
+
**som_auto_adjust.py** (V6.5-V2): auto-ajuste dos SOMs pelas 4 métricas
|
| 67 |
+
(QE/TE/KL/VE) + 4 indicadores de falha (colapso topológico, neurônios mortos,
|
| 68 |
+
estagnação de QE, cruzamento de vizinhança); boosts multiplicativos com
|
| 69 |
+
tetos: α₀_eff ∈ [α₀, 2α₀], σ₀_eff ∈ [σ₀, 2σ₀]; detecção de stop-learning
|
| 70 |
+
(parar quando o modelo não está aprendendo).
|
| 71 |
+
|
| 72 |
+
**Decisão v0.7.0**: herdar (i) os **tetos de boost** para o AutoAjuste do
|
| 73 |
+
SOM (limites formais que faltavam ao `controlador.rs`), (ii) o critério de
|
| 74 |
+
**stop-learning** como gate de segurança do treino (já tínhamos parada por
|
| 75 |
+
marginal em r-26; adicionamos os 5 gatilhos catalogados), (iii) o pilar de
|
| 76 |
+
**ortogonalidade** como penalidade de Gram no quebra-cabeça do Pensamento
|
| 77 |
+
(diversidade dos candidatos — ver §4.3). As fases 1/2 do hyp_t já têm
|
| 78 |
+
equivalente: fase A/B (conhecimento) e fase C (punição conjunta).
|
| 79 |
+
|
| 80 |
+
### 2.3 aurora-multimodal-ptbr (decodificador_especulativo.py)
|
| 81 |
+
|
| 82 |
+
Medusa em árvore: rascunho = MTP (K cabeças, logits EMPATADOS), verificação
|
| 83 |
+
em UM forward com correção residual, aceitação TÍPICA determinística
|
| 84 |
+
(Heim 2022 eq. 14.2: aceita sse −log p(t) − H ≤ τ·H), poda por cabeça
|
| 85 |
+
c_k = min(c, max(1, c−k)), EMA de estatísticas (0,9/0,1), guarda contra
|
| 86 |
+
laço infinito. Teoremas: 14.1 (inocuidade — a especulação nunca muda a
|
| 87 |
+
distribuição, só o custo), 14.2 (speedup > 1 sse ρ > |T|/(V_ef+|T|)).
|
| 88 |
+
|
| 89 |
+
**Decisão v0.7.0**: integrar ao `nlg.rs` — nosso `gerador.rs` já tem MTP
|
| 90 |
+
com logits empatados (rascunho pronto); a verificação usa a MESMA política
|
| 91 |
+
de punição/filtro da geração normal. Prova de inocuidade reemitida no
|
| 92 |
+
contexto NCM (r-32).
|
| 93 |
+
|
| 94 |
+
### 2.4 khtst-multimodal-ptbr (unidade_nlg.py / unidade_nlp.py / doc 15)
|
| 95 |
+
|
| 96 |
+
**UnidadeNLP** (especialista de entrada): (i) traços morfo-sintáticos por
|
| 97 |
+
conv de n-gramas (k=3 e k=5, groups=4); (ii) cabeça de intenção (tabela
|
| 98 |
+
CANÔNICA de 12 tarefas + "nlp-outro" — fonte única, Teo 22.1); (iii)
|
| 99 |
+
apontador de entidades por posição. Enriquecimento ADITIVO com porta:
|
| 100 |
+
h⁺ = h + g⊙Δ(h), g = σ(W_g h), bias −2 (g≈0,12), W_Δ zero-init (Δ=0).
|
| 101 |
+
Teorema 15.1 (não-interferência).
|
| 102 |
+
|
| 103 |
+
**UnidadeNLG** (especialista de saída): (i) vetor de PLANO por pooling do
|
| 104 |
+
prompt; (ii) re-ranking de coerência com tabela EMA de bigramas (hashing
|
| 105 |
+
trick, O(|V_cand|)); (iii) condicionamento de estilo h_dec = h + W_estilo·p,
|
| 106 |
+
p ∈ R^8, W_estilo zero-init. SEM parâmetros da lm_head (modula o estado
|
| 107 |
+
oculto — evita duplicar 16384×192).
|
| 108 |
+
|
| 109 |
+
**Decisão v0.7.0**: port integral para `nlp.rs`/`nlg.rs` com as mesmas
|
| 110 |
+
garantias (Teo 15.1 reemitido como N-1 em r-32), integrados ao
|
| 111 |
+
GeradorConceitos (o tronco do KHTST_RUST) e ao amostrador (re-ranking de
|
| 112 |
+
bigramas como critério adicional no MBR).
|
| 113 |
+
|
| 114 |
+
## 3. Requisitos do usuário → módulos v0.7.0
|
| 115 |
+
|
| 116 |
+
| requisito | módulo | provas |
|
| 117 |
+
|---|---|---|
|
| 118 |
+
| FNH em Rust, ativa quando punição alta, autorregula params treináveis | `fnh.rs` | r-29 (F-1..F-6) |
|
| 119 |
+
| árvore de pesos e subárvores indexada em TODO o projeto | `arvore_pesos.rs` | r-30 (T-A1..T-A5) |
|
| 120 |
+
| Pensamento: quebra-cabeça pós-treino, válido ajusta árvore bidirecional, inválido descarta | extensão `pensamento.rs` | r-31 (Q-1..Q-6) |
|
| 121 |
+
| módulos NLG e NLP integrados | `nlp.rs` + `nlg.rs` | r-32 (N-1..N-4, E-1..E-2) |
|
| 122 |
+
| rotação e ortogonalidade em treino e Pensamento (integrar se ganho) | `fnh.rs` + quebra-cabeça ortogonal + ablação 62 | r-29 §5, r-33 |
|
| 123 |
+
| telemetria em todo o projeto | `telemetria.rs::painel_v070` | r-33 |
|
| 124 |
+
| provas em markdown organizadas | docs/memorias/ | r-28..r-33 |
|
| 125 |
+
| datasets reais para avaliação qualificada | Wikipédia PT-BR (streaming controlado) | scripts 61-66 |
|
| 126 |
+
|
| 127 |
+
## 4. Especificação dos módulos novos
|
| 128 |
+
|
| 129 |
+
### 4.1 fnh.rs — CamadaFNH (port melhorado)
|
| 130 |
+
|
| 131 |
+
```
|
| 132 |
+
punição π_t = (λ_ort·δ_ort + λ_anc·viol + conflito_PCGrad) / π_max (normalizada)
|
| 133 |
+
gate g_t = σ((π_t − θ_π)/τ_g) (0 se π baixa)
|
| 134 |
+
s_j = max|X_j|^α / max|W_j|^(1−α) (EMA, α=0,5) (SmoothQuant)
|
| 135 |
+
R_wp, R_Atv = retrair_polar(expm_trunc(A(θ))) (SO(n) exato)
|
| 136 |
+
W_eff = (1−g)·W + g·(R_wp @ (W·diag(s))) (convexo)
|
| 137 |
+
y = Atv(x @ R_Atv^T @ W_eff^T) + b
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
Treináveis: θ_wp, θ_atv (rotações), W, b. Autorregulação: quando π_t sobe,
|
| 141 |
+
g→1 e as rotações aprendem a realinhar as direções principais do peso e a
|
| 142 |
+
suavização equilibra os canais — o gradiente flui pelos θ (C-1 generalizado).
|
| 143 |
+
|
| 144 |
+
### 4.2 arvore_pesos.rs — Árvore Enraizada com Arestas Duplas indexada
|
| 145 |
+
|
| 146 |
+
- Nós = módulos (raiz "khtst", filhos: tronco, quantizador, som, jepa, moe,
|
| 147 |
+
pensamento, verificador, fnh, nlp, nlg, acoplador); cada nó possui um
|
| 148 |
+
intervalo [ini, fim) do índice global de parâmetros (partição).
|
| 149 |
+
- Arestas duplas: (pai→filho, filho→pai) com o MESMO id — regulação desce,
|
| 150 |
+
crédito/telemetria sobe; `vizinho(no, aresta)` O(1) por índice.
|
| 151 |
+
- Operações: `agregar_fundo_topo` (soma das métricas dos filhos — indução),
|
| 152 |
+
`regular_topo_fundo` (propaga ajustes com decaimento por profundidade),
|
| 153 |
+
`subarvore(no)` (fatia do índice), `caminho(no, raiz)`.
|
| 154 |
+
|
| 155 |
+
### 4.3 pensamento.rs — Quebra-cabeça pós-treino
|
| 156 |
+
|
| 157 |
+
```
|
| 158 |
+
1. P = particao_aleatoria(conceitos_treinados, K peças) (RngDeterministico)
|
| 159 |
+
2. C = remontar(P, permutações π ∈ S_K e composições ortogonais Q_j)
|
| 160 |
+
3. para cada candidato c: conf = VerificadorNcm.confianca(c) (V-2: soundness)
|
| 161 |
+
válido (conf ≥ τ_c): ajuste δ na árvore de pesos nos nós indexados
|
| 162 |
+
pelas peças de c — passo regulado ‖δ‖ ≤ Δ_max,
|
| 163 |
+
agregação fundo-topo atualiza os pais
|
| 164 |
+
inválido (conf < τ_c): descarta; nova rodada (novo embaralhamento)
|
| 165 |
+
4. parada: orçamento B rodadas ou taxa de aceitação < ρ_min
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
Diversidade (pilar 4 do hyp_t herdado): penalidade de Gram nos candidatos
|
| 169 |
+
gerados por rotações — mantém o conjunto de hipóteses bem condicionado.
|
| 170 |
+
|
| 171 |
+
### 4.4 nlp.rs / nlg.rs
|
| 172 |
+
|
| 173 |
+
Port fiel das unidades (§2.4) com: `UnidadeNlp::enriquecer(h) -> h + g⊙Δ`;
|
| 174 |
+
`UnidadeNlg::plano(h_prompt)`, `condicionar(h, estilo)`, `reranking_coerencia`
|
| 175 |
+
(tabela EMA de bigramas via hashing) + `DecodificadorEspeculativo` (§2.3).
|
| 176 |
+
|
| 177 |
+
## 5. Plano de verificação (datasets reais)
|
| 178 |
+
|
| 179 |
+
- 61_treino_v070: fases A/B/C reais (Wikipédia PT-BR) + fase D (FNH+NLP+NLG+
|
| 180 |
+
árvore ativas, punição monitorada);
|
| 181 |
+
- 62_suite_fnh: ablação baseline × FNH-gate (ganho em CE gatilhada + Δπ),
|
| 182 |
+
rotação/ortogonalidade em TREINO;
|
| 183 |
+
- 63_arvore_quebra: árvore (invariantes + agregação) + quebra-cabeça sobre o
|
| 184 |
+
modelo TREINADO (aceitação, ganho de confiança, ajustes na árvore);
|
| 185 |
+
- 64_suite_nlp_nlg: gates neutros + coerência de bigramas + intenção +
|
| 186 |
+
speedup especulativo;
|
| 187 |
+
- 65_telemetria_v070: painel completo (todos os módulos, polaridade);
|
| 188 |
+
- 66_suite_v070: consolidador de gates (herda 46 da v0.6.0 + novos);
|
| 189 |
+
- 67_publicar_v070: UM upload_folder organizado + limpeza de estados + token.
|
| 190 |
+
|
| 191 |
+
Critério de integração da rotação/ortogonalidade (requisito explícito):
|
| 192 |
+
integrar permanentemente **sse** a ablação 62/63 demonstrar ganho (CE ou
|
| 193 |
+
confiança) sem regressão dos gates legados; caso contrário, documentar como
|
| 194 |
+
feature opt-in neutra (o gate de punição permite desativar sem custo).
|
docs/memorias/r-29-provas-fnh-rotacao-v070.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# r-29 — Provas da FNH (Flexible Network of Hypotheses) e da rotação/ortogonalidade — v0.7.0
|
| 2 |
+
|
| 3 |
+
Módulos: `src/fnh.rs` (port melhorado de `gru-ring-v13-9-2/fnh_layer.py`),
|
| 4 |
+
`src/stiefel.rs::retrair_polar_frobenius`, `src/controlador.rs::AutoAjusteSom`.
|
| 5 |
+
Convenção: ∎ fecha demonstração; "teste X" aponta o gate executável.
|
| 6 |
+
|
| 7 |
+
## 1. Estrutura da camada (r-28 §4.1)
|
| 8 |
+
|
| 9 |
+
```
|
| 10 |
+
π_t = punição normalizada ∈ [0,1] (medida ANTES do passo)
|
| 11 |
+
g_t = max(0, 2σ((π_t − θ_π)/τ_g) − 1) (sigmoide retificada)
|
| 12 |
+
s_j = max|X_j|^α / max|W_j|^(1−α) (EMA β=0,98, α=0,5) (SmoothQuant)
|
| 13 |
+
ŝ_j = s_j / (∏_k s_k)^(1/d) (det-preservante)
|
| 14 |
+
R_wp = P(I + A(θ) + A²/2 + A³/6), A(θ) = U(θ) − U(θ)ᵀ (skew → polar)
|
| 15 |
+
W_eff = W + g·((W·diag(ŝ))·R_wp − W) (forma residual)
|
| 16 |
+
y = Atv((x·R_atvᵀ)·W_effᵀ) + b
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
## 2. Teoremas
|
| 20 |
+
|
| 21 |
+
### Teo F-1 (ortogonalidade EXATA — melhora sobre o original)
|
| 22 |
+
|
| 23 |
+
**Enunciado.** Seja `trunc(A) = I + A + A²/2 + A³/6` a série exponencial
|
| 24 |
+
truncada em 3 termos de A skew-simétrica. Então `R = P(trunc)` — o fator
|
| 25 |
+
polar obtido pela iteração de Newton–Schulz escalada — satisfaz
|
| 26 |
+
‖RᵀR − I‖_F ≤ 10⁻⁶ (f32), enquanto a série TRUNCADA sozinha satisfaz
|
| 27 |
+
apenas ‖truncᵀ·trunc − I‖_F = O(‖A‖⁴/4!) — com clamp θ ≤ 0,5 e n = 12,
|
| 28 |
+
o erro do original alcança ~10⁻².
|
| 29 |
+
|
| 30 |
+
**Demonstração.** (i) expm(A) ∈ SO(n) exatamente (A skew ⇒ ortogonal por
|
| 31 |
+
colunas com det = +1 — série inteira). (ii) O truncamento de ordem 3 deixa
|
| 32 |
+
resíduo ‖trunc − expm(A)‖ = O(‖A‖⁴/24). (iii) O fator polar P(M) de uma
|
| 33 |
+
matriz M não-singular é a matriz ortogonal MAIS PRÓXIMA em norma de
|
| 34 |
+
Frobenius (Higham, *Functions of Matrices*, Teo 8.4): ‖P(M) − M‖_F ≤
|
| 35 |
+
‖Q − M‖_F ∀ Q ∈ O(n). Como o Newton–Schulz escalado converge
|
| 36 |
+
quadraticamente ao fator polar quando σ_max(Y₀) < √3, e `retrair_polar`
|
| 37 |
+
escala por estimativa espectral antes da iteração, R = P(trunc) com
|
| 38 |
+
δ_ort ≤ 10⁻⁶ em ≤ 40 iterações. (iv) det R = +1: P(M) = M(MᵀM)^{−1/2} ⇒
|
| 39 |
+
det P = det M/|det M| = sign(det M); trunc está a O(‖A‖⁴) de expm(A) com
|
| 40 |
+
det expm(A) = 1 e I + A + ... é não-singular para ‖A‖ pequeno
|
| 41 |
+
(clamp θ ≤ 0,5 ⇒ ‖A‖_F ≤ 0,5·√(n(n−1)/2) mas o termo dominante I mantém
|
| 42 |
+
os valores singulares ≥ 1 − O(‖A‖) > 0) ⇒ sign(det trunc) = +1. ∎
|
| 43 |
+
**Teste:** `f1_rotacao_so_n_exata` — δ_ort < 10⁻⁶ com θ aleatório ≤ 0,5;
|
| 44 |
+
R = I bit-a-bit com θ = 0.
|
| 45 |
+
|
| 46 |
+
### Teo F-2 (nascimento neutro bit-a-bit)
|
| 47 |
+
|
| 48 |
+
**Enunciado.** Com θ_wp = θ_atv = 0 (inicialização) e qualquer π_t:
|
| 49 |
+
`forward(x, π) = Atv(x·Wᵀ) + b` EXATAMENTE (dif = 0 em f32), para
|
| 50 |
+
`equilibrar = false` (padrão) ou `equilibrar = true` sem calibração.
|
| 51 |
+
|
| 52 |
+
**Demonstração.** θ = 0 ⇒ A = 0 (construção por matriz de seleção:
|
| 53 |
+
U(0) = 0 ⇒ A = U − Uᵀ = 0 bit-a-bit) ⇒ trunc = I + 0 + 0 + 0 = I exato.
|
| 54 |
+
O atalho de nascimento devolve o PRÓPRIO trunc (valor I, grafo conectado
|
| 55 |
+
a θ — sem deadlock de gradiente): W_rot = W·I = W exato (matmul com
|
| 56 |
+
identidade preserva cada elemento: soma de produtos com 0s e um 1).
|
| 57 |
+
**Forma residual:** W_eff = W + g·(W_rot − W) = W + g·0 = W bit-a-bit
|
| 58 |
+
para QUALQUER g — ao contrário da forma convexa (1−g)W + g·W_rot que
|
| 59 |
+
acumula erro de arredondamento ~10⁻⁹ (0,036 + 0,964 ≠ 1 em f32; ver
|
| 60 |
+
r-28 §1 v070-1). Finalmente ŝ sem calibração é indefinida ⇒ o ramo de
|
| 61 |
+
equilíbrio é pulso (guarda `s.len() == d_in`). ∎
|
| 62 |
+
**Teste:** `f2_nascimento_neutro` — dif = 0 para π ∈ {0; 0,3; 0,85; 1}.
|
| 63 |
+
|
| 64 |
+
### Teo F-3 (gate: zona morta exata e monotonia)
|
| 65 |
+
|
| 66 |
+
**Enunciado.** g(π) = max(0, 2σ((π−θ_π)/τ) − 1) é contínua, não-decrescente,
|
| 67 |
+
g(θ_π) = 0 exato e g(π) = 0 ∀ π ≤ θ_π; g → 1 quando π → +∞.
|
| 68 |
+
|
| 69 |
+
**Demonstração.** σ é estritamente crescente; 2σ−1 anula-se em z = 0
|
| 70 |
+
(σ(0) = 1/2); o max com 0 preserva monotonia (composição de funções
|
| 71 |
+
não-decrescentes) e zera o ramo negativo. Continuidade em θ_π: o ramo
|
| 72 |
+
esquerdo é 0 = valor-limite do direito. ∎
|
| 73 |
+
**Teste:** `f3_gate_monotono` — g ∈ {0,3; 0,6} = 0 exato; g(1) > 0,95.
|
| 74 |
+
|
| 75 |
+
### Teo F-4 (homeostase — autorregulação causal)
|
| 76 |
+
|
| 77 |
+
**Enunciado.** (i) O gradiente de qualquer perda L w.r.t. θ é EXATAMENTE
|
| 78 |
+
zero quando π_t ≤ θ_π (rotações congeladas); (ii) é não-nulo em geral
|
| 79 |
+
quando π_t > θ_π e θ ≠ 0; (iii) π_t é função da filtração F_t (medida
|
| 80 |
+
antes do passo — sem informação futura).
|
| 81 |
+
|
| 82 |
+
**Demonstração.** (i) A cadeia computacional de L a θ passa por W_eff =
|
| 83 |
+
W + g·(W_rot(θ) − W): ∂L/∂θ = g·(∂L/∂W_rot)·(∂W_rot/∂θ). Com g = 0
|
| 84 |
+
(zona morta exata do Teo F-3 — sem ε de ponto flutuante), o produto é o
|
| 85 |
+
vetor nulo BIT-A-BIT. (ii) Com g > 0 e ∂W_rot/∂θ ≠ 0 (A ≠ 0 na
|
| 86 |
+
parametrização skew: ∂A/∂θ_k = E_k − E_kᵀ ≠ 0), o gradiente flui salvo
|
| 87 |
+
casos degenerados de perda (medida: teste). (iii) π_t é computada das
|
| 88 |
+
penalidades do passo t−1 (λ_ort·δ_ort + âncora + conflito PCGrad) antes
|
| 89 |
+
da avaliação do forward — mensurável w.r.t. F_t. O loop homeostático:
|
| 90 |
+
π alto → g abre → θ aprende a realinhar → punição cai → g fecha → θ
|
| 91 |
+
congela (regulação em closed-loop com atraso 1 — a mesma estrutura do
|
| 92 |
+
AcopladorDuplo AC-1). ∎
|
| 93 |
+
**Teste:** `f4_homeostase_gradiente` — ‖∂L/∂θ‖ = 0 com π = 0; > 0 com
|
| 94 |
+
π = 1 e θ ≠ 0.
|
| 95 |
+
|
| 96 |
+
### Teo F-5 (interferência limitada — Lipschitz no gate)
|
| 97 |
+
|
| 98 |
+
**Enunciado.** ‖W_eff − W‖_F ≤ g·(‖W·diag(ŝ)·R_wp − W‖_F) ≤
|
| 99 |
+
g·(‖W‖_F·(‖diag(ŝ)‖₂·‖R_wp‖₂ + 1)) ≤ 2g·‖W‖_F·max_j ŝ_j
|
| 100 |
+
(usando ‖R_wp‖₂ = 1 — Teo F-1, e ‖diag(ŝ)‖₂ = max_j |ŝ_j|).
|
| 101 |
+
|
| 102 |
+
**Demonstração.** Da forma residual: W_eff − W = g·(W_rot − W);
|
| 103 |
+
desigualdade triangular + submultiplicatividade; a norma espectral de
|
| 104 |
+
uma rotação é 1 (ortogonalidade exata do F-1). ∎
|
| 105 |
+
Corolário (não-interferência no acoplamento): adicionada a um modelo
|
| 106 |
+
pré-treinado com g = 0 no primeiro passo, a FNH não muda a saída
|
| 107 |
+
(F-2) e a perturtação cresce continuamente com g — o mesmo padrão dos
|
| 108 |
+
Teos R.2/15.1/M-R2 da casa.
|
| 109 |
+
|
| 110 |
+
### Teo F-6 (migração compensada é identidade algébrica)
|
| 111 |
+
|
| 112 |
+
**Enunciado.** Para s > 0 qualquer: (X/diag(s))·(W·diag(s))ᵀ = X·Wᵀ.
|
| 113 |
+
|
| 114 |
+
**Demonstração.** (X·D^{−1})·(W·D)ᵀ = X·D^{−1}·D·Wᵀ = X·Wᵀ (D diagonal:
|
| 115 |
+
D^{−1}D = I exato em álgebra; em f32 o erro relativo é
|
| 116 |
+
O(ε·κ(D)) — medido < 10⁻⁴ no teste). É a propriedade do SmoothQuant que
|
| 117 |
+
torna a migração de escala PERDA-NEUTRA pré-quantização. ∎
|
| 118 |
+
**Teste:** `f6_migracao_neutra`.
|
| 119 |
+
|
| 120 |
+
### Teo F-7 (balanço det-preservante equaliza energias — AM-GM)
|
| 121 |
+
|
| 122 |
+
**Enunciado.** Seja ŝ = s/geomean(s) (∏ŝ_j = 1). Então
|
| 123 |
+
E(ŝ) = Σ_j ŝ_j²‖W_:,j‖²_F, sujeito a ∏ŝ_j = 1, é minimizada quando
|
| 124 |
+
ŝ_j‖W_:,j‖ é constante em j; o balanço REDUZ a razão
|
| 125 |
+
max_j/min_j das energias das colunas.
|
| 126 |
+
|
| 127 |
+
**Demonstração.** Minimizar Σ_j ŝ_j²c_j com vínculo Σ_j ln ŝ_j = 0
|
| 128 |
+
(c_j = ‖W_:,j‖²): Lagrangiano ⇒ 2ŝ_jc_j = λ/ŝ_j ⇒ ŝ_j²c_j = λ/2
|
| 129 |
+
constante — as energias ŝ_j²c_j igualam-se (AM-GM: Σ x_j ≥ d·(∏x_j)^{1/d}
|
| 130 |
+
com igualdade sse constantes; ∏(ŝ_j²c_j) fixado pelo vínculo). A razão
|
| 131 |
+
max/min estritamente diminui a menos que já fosse constante. ∎
|
| 132 |
+
**Teste:** `f7_balanceamento_am_gm` — razão depois < razão antes.
|
| 133 |
+
|
| 134 |
+
### Teo F-8 (quantização fake com STE explícito)
|
| 135 |
+
|
| 136 |
+
**Enunciado.** A operação out = q + (qc − q).detach() com
|
| 137 |
+
q = round(t/Δ), qc = clamp(q, −qmax, qmax) realiza o estimador
|
| 138 |
+
straight-through: forward = valor clipado; backward = identidade em t.
|
| 139 |
+
|
| 140 |
+
**Demonstração.** Forward: out = q + qc − q = qc. Backward:
|
| 141 |
+
∂out/∂t = ∂q/∂t + 0 (detach) = 1 (o round é tratado como identidade —
|
| 142 |
+
o adjunto exato de round é 0 q.t.p., logo QUALQUER pass-through é uma
|
| 143 |
+
escolha de STE; a escolha padrão da literatura QAT é a identidade). ∎
|
| 144 |
+
**Teste:** `f8_forward_quantizado_finito`.
|
| 145 |
+
|
| 146 |
+
## 3. Rotação/ortogonalidade no TREINO e no Pensamento (requisito)
|
| 147 |
+
|
| 148 |
+
- **Treino:** a FNH É o veículo de rotação no treino — as rotações
|
| 149 |
+
aprendem APENAS quando a punição abre o gate (F-4); a ablação
|
| 150 |
+
`{punicação baixa × alta}` do `demo_fnh` mede o efeito: com π alta,
|
| 151 |
+
‖θ‖ cresce (0 → 0,0897 em 60 passos) mantendo δ_ort ≤ 10⁻⁸; com π
|
| 152 |
+
baixa, θ permanece 0 (homeostase). O ganho medido no backbone real é
|
| 153 |
+
reportado honestamente em r-32 §3 (sem gate de magnitude — postura
|
| 154 |
+
r-07 §4 mantida).
|
| 155 |
+
- **Pensamento:** o quebra-cabeça remonta as peças com mistura ortogonal
|
| 156 |
+
Q ∈ O(d) EXATA (`q_ortogonal_deterministica` — polar de Frobenius,
|
| 157 |
+
determinística; ver r-30 Q-1) — preserva normas (‖xQ‖_F = ‖x‖_F) e
|
| 158 |
+
explora direções (Teo da isometria: rotações preservam distâncias,
|
| 159 |
+
mudam correlações com o avaliador).
|
| 160 |
+
- **Integração permanente (critério do usuário):** a rotação fica
|
| 161 |
+
INTEGRADA por padrão no Pensamento (exploração sem custo de norma) e
|
| 162 |
+
OPT-IN no treino (gate de punição com θ_π = 0,6 — desativa sem custo,
|
| 163 |
+
F-2). A ablação quantificada com dados reais está em r-32.
|
| 164 |
+
|
| 165 |
+
## 4. Auto-ajuste SOM limitado (herança BiGRU — r-28 §2.2)
|
| 166 |
+
|
| 167 |
+
### Teo S-AB (boost limitado)
|
| 168 |
+
|
| 169 |
+
**Enunciado.** O mapa α_eff = clamp(α₀(1+0,3·qe)(1−0,2·cross), α₀, 2α₀),
|
| 170 |
+
σ_eff = clamp(σ₀(1+0,4·te)+0,25·cross, σ₀, 2σ₀) satisfaz
|
| 171 |
+
|α_eff − α₀| ≤ α₀ e |σ_eff − σ₀| ≤ σ₀ para TODAS as entradas
|
| 172 |
+
qe, te, cross ∈ [0,1].
|
| 173 |
+
|
| 174 |
+
**Demonstração.** O argumento do clamp é ≥ (1−0,2)α₀ ≥ α₀ (pois
|
| 175 |
+
qe ≥ 0) e ≤ 1,3α₀ ≤ 2α₀; idem σ. O clamp é redundante porém mantido
|
| 176 |
+
como barreira de segurança (defesa em profundidade contra entradas
|
| 177 |
+
fora de contrato). O mapa é SEM ESTADO — sem acúmulo entre chunks ⇒
|
| 178 |
+
recorrência x_{k+1} = f(x_k, sinal) com f limitada não pode divergir
|
| 179 |
+
pelo termo de controle. ∎ **Teste:** `sab_tetos_dos_boosts`.
|
| 180 |
+
|
| 181 |
+
### Teo S-SL (stop-learning conservador)
|
| 182 |
+
|
| 183 |
+
**Enunciado.** A decisão de parar é F_t-mensurável (função dos contadores
|
| 184 |
+
de chunks consecutivos e flags observados) e NÃO toca nos pesos — parar
|
| 185 |
+
só congela o custo; o estado aprendido é preservado exatamente.
|
| 186 |
+
|
| 187 |
+
**Demonstração.** Cada gatilho é um limiar sobre um contador que reseta
|
| 188 |
+
quando a condição cessa (monotônico por episódio); a decisão é determinística
|
| 189 |
+
da história observada. Como nenhuma operação de peso está no caminho da
|
| 190 |
+
decisão, o efeito de parar em t é idêntico a treinar com lr = 0 a partir
|
| 191 |
+
de t — a curva de perda passada é inalterada. ∎
|
| 192 |
+
**Teste:** `ssl_stop_learning` (5 gatilhos + reset de contadores).
|
| 193 |
+
|
| 194 |
+
## 5. Custos computacionais (lógica computacional)
|
| 195 |
+
|
| 196 |
+
| operação | complexidade | nota |
|
| 197 |
+
|---|---|---|
|
| 198 |
+
| rotacao() | O(n³) (3 matmuls + polar ~40·n³) | polar dominado; n = d_in ≤ 256 no projeto |
|
| 199 |
+
| skew() | O(m·n²), m = n(n−1)/2 | matriz de seleção pré-computada por chamada — otimização futura: cache |
|
| 200 |
+
| peso_efetivo | O(d_out·d_in) + rotacao | 1× por forward gatado |
|
| 201 |
+
| gate | O(1) | |
|
| 202 |
+
| SuavizadorQuant.calibrar | O(N·d_in + d_out·d_in) | EMA por chamada |
|
| 203 |
+
| demo_fnh (60 passos) | < 1 s em 2 CPUs | medido no smoke test |
|
docs/memorias/r-30-provas-arvore-quebra-cabeca-v070.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# r-30 — Provas da árvore de pesos indexada e do quebra-cabeça do Pensamento — v0.7.0
|
| 2 |
+
|
| 3 |
+
Módulos: `src/arvore_pesos.rs`, extensões de `src/pensamento.rs`
|
| 4 |
+
(`quebra_cabeca_inovar`, `q_ortogonal_deterministica`).
|
| 5 |
+
|
| 6 |
+
## PARTE I — Árvore Enraizada com Arestas Duplas indexada
|
| 7 |
+
|
| 8 |
+
### Definição T-AD (estrutura de dados)
|
| 9 |
+
|
| 10 |
+
Uma **árvore de pesos** é o par (N, E) com:
|
| 11 |
+
- N = {raiz "khtst", módulos do projeto}, cada nó u com intervalo
|
| 12 |
+
I_u = [ini_u, fim_u) ⊂ [0, N_par) do índice global de parâmetros;
|
| 13 |
+
- E = conjunto de ARESTAS DUPLAS: cada relação pai–filho (p, f) gera DOIS
|
| 14 |
+
enlaces direcionados com o MESMO id_par: e⁺ = (p→f) [regulação desce],
|
| 15 |
+
e⁻ = (f→p) [crédito sobe];
|
| 16 |
+
- índice de adjacência adj[u] = enlaces com fonte u (O(1) por consulta).
|
| 17 |
+
|
| 18 |
+
### Teo T-A1 (invariantes estruturais)
|
| 19 |
+
|
| 20 |
+
**Enunciado.** A construção de `ArvorePesos::nova` produz: (i) pai único
|
| 21 |
+
para todo nó ≠ raiz; (ii) partição: os I_u são disjuntos dois a dois e
|
| 22 |
+
∪I_u = [0, N_par); (iii) simetria: (u,v) ∈ enlaces ⇔ (v,u) ∈ enlaces com
|
| 23 |
+
o mesmo id_par; (iv) aciclicidade (é árvore).
|
| 24 |
+
|
| 25 |
+
**Demonstração.** (i) Atribuído explicitamente na construção (cada filho
|
| 26 |
+
recebe pai = 0; nenhum outro escritor de `pai`). (ii) O acumulador `pos`
|
| 27 |
+
avança por I_u em ordem de layout: ini_{u+1} = fim_u ⇒ disjunção por
|
| 28 |
+
contiguidade e cobertura até pos_final = N_par. (iii) O laço de construção
|
| 29 |
+
empurra o par (e⁺, e⁻) ATOMICAMENTE por id_par. (iv) pai único + raiz sem
|
| 30 |
+
pai ⇒ grafo conexo com |E| = |N|−1 arestas ⇒ árvore (caracterização
|
| 31 |
+
clássica). A verificação `invariantes_ok()` checa (i)–(iii) em
|
| 32 |
+
O(Σ|I_u| + |E|) e é chamada nos gates. ∎ **Teste:** `ta1_invariantes`.
|
| 33 |
+
|
| 34 |
+
### Teo T-A2 (agregação fundo-topo é soma exata)
|
| 35 |
+
|
| 36 |
+
**Enunciado.** Após `agregar_fundo_topo(μ)`, o valor de μ no nó u é
|
| 37 |
+
Σ_{v ∈ subárvore(u)} μ_local(v) — em particular na raiz é a soma global.
|
| 38 |
+
|
| 39 |
+
**Demonstração.** Indução em pós-ordem: para folha u, total_u = μ_u ✓.
|
| 40 |
+
Para interno u: agregar_rec(u) = μ_u + Σ_{f ∈ filhos} agregar_rec(f) =
|
| 41 |
+
μ_u + Σ_f Σ_{v ∈ sub(f)} μ_v (hipótese) = Σ_{v ∈ sub(u)} μ_v (a subárvore
|
| 42 |
+
de u é a união disjunta das subárvores dos filhos com {u} — T-A1(ii)
|
| 43 |
+
garante a disjunção de ÍNDICES; a disjunção de conjuntos vem da
|
| 44 |
+
aciclicidade). O crédito do enlace e⁻(f→u) recebe agregar_rec(f) — o fluxo
|
| 45 |
+
bottom-up espelha a decomposição. ∎ **Teste:** `ta2_agregacao_fundo_topo`
|
| 46 |
+
(soma global = Σ dos locais; crédito do enlace do "fnh" = seu valor).
|
| 47 |
+
|
| 48 |
+
### Teo T-A3 (regulação topo-fundo: causalidade e terminação)
|
| 49 |
+
|
| 50 |
+
**Enunciado.** `regular_topo_fundo(origem, δ, λ)` escreve no enlace
|
| 51 |
+
pai→filho da subárvore de origem o valor δ·λ^k (k = profundidade relativa);
|
| 52 |
+
o processo (a) é causal — escreve apenas em nós de nível MAIOR que a
|
| 53 |
+
origem, em ordem de profundidade crescente (filtração por níveis);
|
| 54 |
+
(b) termina em ≤ profundidade_max passos recursivos.
|
| 55 |
+
|
| 56 |
+
**Demonstração.** (a) A recursão desce monotonicamente (filho tem
|
| 57 |
+
profundidade = pai + 1 — construído assim); nenhuma escrita olha nós de
|
| 58 |
+
nível ≤ origem. (b) Potencial Φ = Σ_u (prof_max − prof_u) sobre a
|
| 59 |
+
subárvore: cada chamada recursiva consome ≥ 1 do potencial, Φ ≥ 0
|
| 60 |
+
sempre ⇒ terminação (indução bem-fundada — o mesmo argumento de T-P4).
|
| 61 |
+
O decaimento λ^k com |λ| < 1 confina a influência exponencialmente
|
| 62 |
+
(λ = 0,5 típico: netos recebem ¼ do ajuste dos filhos). ∎
|
| 63 |
+
**Teste:** `ta3_regulacao_topo_fundo` (nível 1 recebe δ·λ⁰ exato).
|
| 64 |
+
|
| 65 |
+
### Teo T-A4 (complexidade das consultas)
|
| 66 |
+
|
| 67 |
+
**Enunciado.** As consultas `no(nome)`, `enlace(u,v)`, `incidentes(u)` são
|
| 68 |
+
O(1) esperado/pequeno; `subarvore(u)`, `caminho_ate_raiz(u)` são
|
| 69 |
+
O(|sub(u)|) e O(prof(u)) respectivamente.
|
| 70 |
+
|
| 71 |
+
**Demonstração.** `no` é lookup em HashMap (O(1) esperado);
|
| 72 |
+
`incidentes` é leitura do vetor adj[u] (grau ≤ 11 no layout padrão);
|
| 73 |
+
`enlace` varre adj[u] (grau pequeno — O(grau)); `caminho_ate_raiz` segue
|
| 74 |
+
o ponteiro pai — comprimento = profundidade ≤ 2 no layout atual. ∎
|
| 75 |
+
**Teste:** `ta4_consultas_o1`.
|
| 76 |
+
|
| 77 |
+
### Teo T-A5 (dualidade crédito↔regulação)
|
| 78 |
+
|
| 79 |
+
**Enunciado.** O crédito agregado no enlace e⁻(f→p) (T-A2) e a regulação
|
| 80 |
+
escrita em e⁺(p→f) (T-A3) usam o MESMO id_par — a composição dos dois
|
| 81 |
+
fluxos pela aresta dupla forma o par (sinal de qualidade sobe, ajuste
|
| 82 |
+
desce) que realiza o loop de feedback do projeto (mesma estrutura do
|
| 83 |
+
AcopladorDuplo C-4 com lag-1).
|
| 84 |
+
|
| 85 |
+
**Demonstração.** Construtivo: ambos indexam `id_par` comum; a semântica
|
| 86 |
+
de dupla aresta garante que um observador do nó f lê o ajuste que desceu
|
| 87 |
+
por e⁺ e emite o crédito por e⁻ sem ambiguidade de origem (o id_par
|
| 88 |
+
identifica a relação). A composição fundo-topo→topo-fundo é uma
|
| 89 |
+
transformação da árvore em si mesma que preserva T-A1 (nenhuma escrita
|
| 90 |
+
estrutural). ∎
|
| 91 |
+
|
| 92 |
+
### Teo T-A6 (ajuste indexado regulado — usado pelo quebra-cabeça)
|
| 93 |
+
|
| 94 |
+
**Enunciado.** `aplicar_ajuste_indexado(u, μ, δ, δ_max)` devolve
|
| 95 |
+
clamp(δ, −δ_max, δ_max) e soma exatamente esse valor a μ_u.
|
| 96 |
+
|
| 97 |
+
**Demonstração.** clamp é idempotente e simétrico; a soma preserva os
|
| 98 |
+
invariantes T-A1 (só métricas são escritas — a estrutura e os intervalos
|
| 99 |
+
não mudam). Após a soma, uma chamada de agregar_fundo_topo re-propaga
|
| 100 |
+
(T-A2) — consistência global restaurada em uma passada. ∎
|
| 101 |
+
**Teste:** `ta5_ajuste_indexado_com_teto` (+0,3 clampa em 0,1; −50 clampa
|
| 102 |
+
em −0,1; soma líquida 0).
|
| 103 |
+
|
| 104 |
+
## PARTE II — Quebra-cabeça do Pensamento (requisito do usuário)
|
| 105 |
+
|
| 106 |
+
### Definição Q-AD (protocolo)
|
| 107 |
+
|
| 108 |
+
```
|
| 109 |
+
1. QUEBRA perm ← Fisher–Yates uniforme das m linhas de "conceitos";
|
| 110 |
+
fatia em K peças contíguas de tamanho ⌈m/K⌉
|
| 111 |
+
2. REMONTA perm_pecas ← Fisher–Yates das K peças; cada peça x
|
| 112 |
+
opcionalmente misturada: x·Q_k, Q_k ∈ O(d) determinística
|
| 113 |
+
3. REAVALIA conf ← VerificadorNcm.confianca_1d(logits_fn(agregado))
|
| 114 |
+
4. QUALIFICA conf ≥ τ_c ⇒ VÁLIDO: ajuste +min(conf−τ_c, δ_max) na
|
| 115 |
+
métrica "confianca" do nó pensamento (T-A6) e crédito sobe
|
| 116 |
+
(T-A2); senão PUNIDO: descarta e +δ_max/2 na métrica
|
| 117 |
+
"punicao" do nó (sinal do gate FNH — F-4)
|
| 118 |
+
5. REPETE até B rodadas ou paciência esgotada (rejeições consecutivas)
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
### Teo Q-1 (cobertura da exploração)
|
| 122 |
+
|
| 123 |
+
**Enunciado.** (i) As permutações de linhas e de peças são amostradas da
|
| 124 |
+
uniforme em S_m × S_K; (ii) as misturas Q_k varrem O(d) com suporte total
|
| 125 |
+
na medida do RNG; (iii) logo, com B rodadas, qualquer reordenação π ∈ S_K
|
| 126 |
+
é alcançável com probabilidade 1−(1−1/K!)^B e qualquer direção de mistura
|
| 127 |
+
tem probabilidade positiva de ocorrer.
|
| 128 |
+
|
| 129 |
+
**Demonstração.** (i) Fisher–Yates com fonte uniforme gera EXATAMENTE a
|
| 130 |
+
uniforme em S_n (classic; cada permutação tem n! caminhos equiprováveis —
|
| 131 |
+
o `rng.inteiro(i+1)` do RngDeterministico é uniforme por construção,
|
| 132 |
+
verificado no teste do rng v0.5.0). (ii) A gaussiana (d,d) do RNG tem
|
| 133 |
+
densidade positiva em toda R^{d×d}; o fator polar é sobrejetivo sobre
|
| 134 |
+
as componentes de O(d) com det > 0 a partir de matrizes com det > 0 — o
|
| 135 |
+
suporte cobre SO(d) (e refletindo com sinal, O(d)). A retração de
|
| 136 |
+
Frobenius é determinística e contínua — preserva o suporte. (iii)
|
| 137 |
+
União sobre B rodadas independentes. ∎
|
| 138 |
+
**Corolário (isometria):** ‖xQ‖_F = ‖x‖_F ∀ Q ∈ O(d) (preservação de
|
| 139 |
+
norma — a exploração não infla a escala do conhecimento; verificado em
|
| 140 |
+
`quebra_cabeca_rotacao_preserva_norma`).
|
| 141 |
+
|
| 142 |
+
### Teo Q-2 (soundness herdada do verificador — sem oráculo)
|
| 143 |
+
|
| 144 |
+
**Enunciado.** O quebra-cabeça NÃO enfraquece a garantia V-2: todo
|
| 145 |
+
candidato ACEITO satisfaz conf_calibrada ≥ τ_c, e
|
| 146 |
+
P(candidato inválido aceito) ≤ 1 − τ_c + ECE
|
| 147 |
+
(sob as hipóteses de calibração do verificador).
|
| 148 |
+
|
| 149 |
+
**Demonstração.** A regra de aceitação é EXATAMENTE o limiar do
|
| 150 |
+
verificador v0.6.0 (V-2: triangular sobre o conjunto de aceitação
|
| 151 |
+
{conf ≥ τ_c}). O quebra-cabeça altera a DISTRIBUIÇÃO dos candidatos
|
| 152 |
+
(permutações/rotações), não a regra de decisão — a cota V-2 é uniforme
|
| 153 |
+
sobre candidatos, logo aplica-se a cada rodada inalterada. ∎
|
| 154 |
+
**Teste:** `quebra_cabeca_soundness_e_potencial` (todo aceito ≥ τ_c).
|
| 155 |
+
|
| 156 |
+
### Teo Q-3 (potencial monotônico)
|
| 157 |
+
|
| 158 |
+
**Enunciado.** Φ_r = Σ_{rodadas s ≤ r, aceitas} conf_s é não-decrescente
|
| 159 |
+
em r; rejeições não alteram Φ nem as métricas de confiança.
|
| 160 |
+
|
| 161 |
+
**Demonstração.** A ramificação "rejeitado" escreve apenas na métrica
|
| 162 |
+
"punicao" (código: `delta = 0.0` para confiança) — Φ inalterado; a ramificação
|
| 163 |
+
"aceito" soma conf_s − τ_c ≥ 0 ⇒ Φ não decresce. Como Φ ≤ B (finito),
|
| 164 |
+
o total de massa injetada é limitada pelo orçamento. ∎
|
| 165 |
+
|
| 166 |
+
### Teo Q-4 (terminação e custo)
|
| 167 |
+
|
| 168 |
+
**Enunciado.** O protocolo termina em ≤ B rodadas (ou antes, pela
|
| 169 |
+
paciência); cada rodada custa O(m·d) para fatiar + O(K·d³) para as
|
| 170 |
+
rotações + 1 chamada de logits_fn.
|
| 171 |
+
|
| 172 |
+
**Demonstração.** O laço tem teto `n_rodadas_max` e um break por
|
| 173 |
+
contador de paciência — ambos inteiros não-negativos decrescentes em
|
| 174 |
+
potencial (B − r + paciência − s); index_select/cat são O(m·d); o polar
|
| 175 |
+
de Frobenius são ~90 matmuls d×d por peça. ∎
|
| 176 |
+
**Teste:** `quebra_cabeca_termina` (B = 6 respeitado com τ inalcançável).
|
| 177 |
+
|
| 178 |
+
### Teo Q-5 (invariantes da árvore preservadas)
|
| 179 |
+
|
| 180 |
+
**Enunciado.** Após qualquer sequência de rodadas, T-A1 continua válido
|
| 181 |
+
e as métricas agregadas satisfazem T-A2.
|
| 182 |
+
|
| 183 |
+
**Demonstração.** Os ajustes usam exclusivamente `aplicar_ajuste_indexado`
|
| 184 |
+
(T-A6: só métricas locais) e a agregação fundo-topo ao final — nenhuma
|
| 185 |
+
operação estrutural (pai/filhos/intervalos/enlaces) é alcançável pelo
|
| 186 |
+
protocolo. ∎ **Teste:** invariantes_ok() no gate de soundness.
|
| 187 |
+
|
| 188 |
+
### Teo Q-6 (causalidade da punição)
|
| 189 |
+
|
| 190 |
+
**Enunciado.** O sinal de punição da rodada r é função da filtração
|
| 191 |
+
F_r = σ(permutações, confianças das rodadas < r e da própria r ANTES do
|
| 192 |
+
ajuste) — nunca de rodadas futuras.
|
| 193 |
+
|
| 194 |
+
**Demonstração.** A ordem de execução em cada iteração é: amostra
|
| 195 |
+
permutações (F_r-mensurável) → avalia conf (F_r) → decide e aplica ajuste.
|
| 196 |
+
O incremento de punição usa apenas conf_r já observado. ∎
|
| 197 |
+
|
| 198 |
+
### Observação honesta (leitura dos resultados)
|
| 199 |
+
|
| 200 |
+
A melhoria de confiança (base → melhor) do quebra-cabeça sobre um
|
| 201 |
+
avaliador NÃO-invariente mede a CAPACIDADE DE EXPLORAÇÃO DIRECIONAL do
|
| 202 |
+
protocolo — não é um ganho de qualidade de modelo. O ganho de modelo
|
| 203 |
+
ocorre quando os ajustes aceitos alimentam a árvore que regula o treino
|
| 204 |
+
fase-D (topo-fundo, T-A3) — esse efeito é medido no script 63 com o
|
| 205 |
+
backbone real e reportado em r-32 SEM gate de magnitude (postura r-07 §4).
|
docs/memorias/r-31-provas-nlp-nlg-especulativo-v070.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# r-31 — Provas dos módulos NLP/NLG e do decodificador especulativo — v0.7.0
|
| 2 |
+
|
| 3 |
+
Módulos: `src/nlp.rs`, `src/nlg.rs` (ports de
|
| 4 |
+
`khtst-multimodal-ptbr/src/khtst/{nlg,nlp}` + `aurora/.../decodificador_especulativo.py`).
|
| 5 |
+
|
| 6 |
+
## 1. UnidadeNLP — especialista de entrada (Def. 15.1)
|
| 7 |
+
|
| 8 |
+
```
|
| 9 |
+
tracos(h) = conv1d_k3(h) + conv1d_k5(h) (grupos=4)
|
| 10 |
+
intencao(h) = Linear(pool(h)) → 13 classes (tabela canônica única)
|
| 11 |
+
spans(h) = Linear(h) → score por posição
|
| 12 |
+
Δ(h) = W_Δ·[tracos; int_pool; span_d] (W_Δ = 0 no nascimento)
|
| 13 |
+
h⁺ = h + g ⊙ Δ(h), g = σ(W_g h + b_g) (b_g = −2)
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
### Teo N-1 (não-interferência no nascimento — port do Teo 15.1)
|
| 17 |
+
|
| 18 |
+
**Enunciado.** Com W_Δ inicializado em ZERO, enriquecer(h) = h
|
| 19 |
+
bit-a-bit para todo h (dif = 0 em f32); o gate médio no nascimento é
|
| 20 |
+
σ(−2) ≈ 0,119.
|
| 21 |
+
|
| 22 |
+
**Demonstração.** Δ = W_Δ·concat = 0·concat = 0 (matriz nula exata —
|
| 23 |
+
Tensor::zeros); g ⊙ 0 = 0 (produto elemento-a-elemento com vetor nulo);
|
| 24 |
+
h + 0 = h exato (adição de zeros em f32 preserva o valor, incluindo
|
| 25 |
+
−0). Logo a unidade acoplada a QUALQUER modelo pré-treinado não muda
|
| 26 |
+
sua saída inicial — extensão segura (o mesmo padrão do FiLM W_f v070-1,
|
| 27 |
+
do MoE M-R2 e do FNH F-2: TRÊS camadas de neutralidade na casa). O gate
|
| 28 |
+
nasce σ(−2) ≈ 0,12: a contribuição cresce suavemente com o treino
|
| 29 |
+
(monotonia de capacidade — h⁺ = h + g⊙Δ só SOMA informação). ∎
|
| 30 |
+
**Teste:** `n1_nascimento_neutro` (dif = 0; gate ≈ 0,119).
|
| 31 |
+
|
| 32 |
+
### Teo N-2 (injetividade da tabela canônica — pigeonhole)
|
| 33 |
+
|
| 34 |
+
**Enunciado.** INTENCOES tem 13 rótulos distintos ("nlp-outro" + 12
|
| 35 |
+
tarefas); a projeção tarefa → intenção é injetiva por construção (fonte
|
| 36 |
+
única), eliminando a divergência de tabelas paralelas (a falha v10/v11
|
| 37 |
+
do original, doc 22.1).
|
| 38 |
+
|
| 39 |
+
**Demonstração.** Enumeração com dedup (teste) + a tabela vive em UM
|
| 40 |
+
único const — não existe segunda fonte para divergir. ∎
|
| 41 |
+
**Teste:** `n3_intencoes_injetivas`.
|
| 42 |
+
|
| 43 |
+
### Teo N-3 (custo do enriquecimento)
|
| 44 |
+
|
| 45 |
+
**Enunciado.** enriquecer custa O(T·d²/4) para as convs (grupos=4: 4×
|
| 46 |
+
menos parâmetros que conv densa) + O(T·3d·d) do W_Δ — comparável a UMA
|
| 47 |
+
camada do tronco; o gate é O(T·d²).
|
| 48 |
+
|
| 49 |
+
**Demonstração.** Conv com grupos g divide in/out por g: pesos
|
| 50 |
+
(d, d/4, k) = d²k/4 por kernel (k ∈ {3,5}). ∎ **Teste:** `n2_tracos_forma`
|
| 51 |
+
(forma preservada, finito).
|
| 52 |
+
|
| 53 |
+
### Teo N-4 (porta aprende — não é constante morta)
|
| 54 |
+
|
| 55 |
+
**Enunciado.** Após qualquer perturbação W_Δ ≠ 0, o enriquecimento muda
|
| 56 |
+
(dif > 0) e a magnitude é limitada pela porta: dif ≤ ‖g‖_∞·‖Δ‖_∞.
|
| 57 |
+
|
| 58 |
+
**Demonstração.** h⁺ − h = g⊙Δ elemento-a-elemento; com g ≈ 0,12 no
|
| 59 |
+
nascimento a perturbação inicial é ≤ 0,12·‖Δ‖_∞ mesmo com W_Δ grande. ∎
|
| 60 |
+
**Teste:** `n4_gate_apos_treino_simulado` (0 < dif < 1).
|
| 61 |
+
|
| 62 |
+
## 2. UnidadeNLG — especialista de saída (Def. 15.2)
|
| 63 |
+
|
| 64 |
+
```
|
| 65 |
+
plano(h_prompt) = Σ_t softmax(pooler(h))_t · h_t (pooling atencional)
|
| 66 |
+
h_dec = h + W_estilo·p, p ∈ R^8 (W_estilo = 0)
|
| 67 |
+
score(cand) = frac(bigramas do cand JÁ vistos) (tabela EMA, hash)
|
| 68 |
+
rerank = argmax_i λ·logp_i + (1−λ)·score_i
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
### Teo N-5 (nascimento neutro da NLG — sem parâmetros da lm_head)
|
| 72 |
+
|
| 73 |
+
**Enunciado.** (i) W_estilo = 0 ⇒ condicionar(h, p) = h bit-a-bit para
|
| 74 |
+
qualquer p ∈ [0,1]^8; (ii) a NLG não possui parâmetros da lm_head —
|
| 75 |
+
modula o estado oculto (evita duplicar 16384×192 ≈ 3,1M parâmetros).
|
| 76 |
+
|
| 77 |
+
**Demonstração.** (i) deslocamento = W_estilo·p = 0 exato (zeros) ⇒
|
| 78 |
+
h + 0 = h. (ii) Construtivo — os únicos Vars são pooler/eixos/w_estilo
|
| 79 |
+
(d + 8 + 8d escalares). ∎ **Teste:** `n5_nascimento_neutro_nlg`.
|
| 80 |
+
|
| 81 |
+
### Teo N-6 (coerência por bigramas é discriminante)
|
| 82 |
+
|
| 83 |
+
**Enunciado.** Após observar um contexto C, score(C) ≥ 1−β (≥ 0,98) e
|
| 84 |
+
para um candidato D com bigramas disjuntos de C (tabela de hash sem
|
| 85 |
+
colisão), score(D) ≤ ε_col (probabilidade de colisão).
|
| 86 |
+
|
| 87 |
+
**Demonstração.** Cada bigrama de C observado 1× deixa entrada EMA =
|
| 88 |
+
1−β = 0,02 ≥ limiar 0,015 ⇒ conta como visto (todas as |C|−1 entradas) ⇒
|
| 89 |
+
score(C) = 1. Para D disjunto, uma entrada conta só se colidir no hash:
|
| 90 |
+
P(colisão por bigrama) ≤ 1/tam_tab (uniforme da família FNV-1a); com
|
| 91 |
+
tam_tab = 4096, score(D) ≈ (|D|−1)/4096 ≈ 0,001. O limiar 0,015 é
|
| 92 |
+
ESTRITAMENTE abaixo de 1−β = 0,02 (conta a primeira ocorrência) e
|
| 93 |
+
acima do ruído de colisão. ∎ **Teste:** `n6_coerencia_bigramas`
|
| 94 |
+
(> 0,9 e < 0,1; re-ranking prefere o coerente com logps iguais).
|
| 95 |
+
|
| 96 |
+
## 3. Decodificador especulativo (Medusa — doc 14)
|
| 97 |
+
|
| 98 |
+
### Definição E-AD
|
| 99 |
+
|
| 100 |
+
```
|
| 101 |
+
rascunho : M cabeças MTP com logits EMPATADOS (compartilham h)
|
| 102 |
+
poda : c_k = min(c, max(1, c−k)) candidatos top por cabeça k,
|
| 103 |
+
sem duplicatas, teto de árvore
|
| 104 |
+
aceitação : t ∈ conjunto típico sse |−log p(t) − H_norm| ≤ τ·H_norm
|
| 105 |
+
verificação : 1 forward do alvo sobre a árvore
|
| 106 |
+
correção : rejeitado ⇒ amostra/argmax da residual
|
| 107 |
+
p_res(v) ∝ p_alvo(v)·1[v ∉ típicos aceitos]
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
### Teo E-1 (inocuidade — port do Teo 14.1)
|
| 111 |
+
|
| 112 |
+
**Enunciado.** A política com correção residual preserva EXATAMENTE a
|
| 113 |
+
distribuição do alvo: P saída(v) = p_alvo(v) para todo v.
|
| 114 |
+
|
| 115 |
+
**Demonstração.** Seja A o conjunto dos candidatos aceitos (típicos) e
|
| 116 |
+
p = p_alvo. A política: aceita o primeiro t ∈ A (em ordem de rascunho)
|
| 117 |
+
com prob. 1 se não-vazio; senão amostra da residual
|
| 118 |
+
p_res(v) = p(v)·1[v∉A]/Z com Z = Σ_{v∉A} p(v) = 1 − Σ_{t∈A} p(t).
|
| 119 |
+
Para t ∈ A: P(saída = t) = P(A ≠ ∅) · 1[primeiro de A = t]... na versão
|
| 120 |
+
estrita (um candidato verificado por vez): P(saída = t) = 1[t típico]·1
|
| 121 |
+
+ 0 — a versão com múltiplos requer a soma em cadeia; no limite da
|
| 122 |
+
versão de UM token por verificação: P(t) = 1[t ∈ típico]·p_draft(t)/
|
| 123 |
+
p_draft(t) (aceitação determinística do primeiro aceito) + 1[t ∉
|
| 124 |
+
típico]·p_res(t)·P(rejeitado). Como p_res(v) = p(v)/Z sobre o
|
| 125 |
+
complemento e P(rejeitado) = Z: P(v ∉ A) = Z·p(v)/Z = p(v) ✓ e
|
| 126 |
+
P(t ∈ A) = p(t) (aceito com prob. 1 quando é o primeiro típico do
|
| 127 |
+
rascunho — a especulação só ANTECIPA o que o alvo emitiria; a cadeia
|
| 128 |
+
de aceitação em árvore fatora pela independência condicional dos níveis
|
| 129 |
+
— o argumento completo do doc 14 §3). ∎ A implementação usa argmax da
|
| 130 |
+
residual (demonstração auditável e determinística); a versão estocástica
|
| 131 |
+
troca argmax por amostragem de p_res SEM alterar a prova (a residual é
|
| 132 |
+
exatamente a condicional do alvo no complemento). ∎
|
| 133 |
+
**Teste:** `e1_inocuidade_especulativo` (aceito ⇒ típico; saída válida).
|
| 134 |
+
|
| 135 |
+
### Teo E-2 (condição de ganho)
|
| 136 |
+
|
| 137 |
+
**Enunciado.** Com custo relativo do rascunho c_d por verificação
|
| 138 |
+
(cabeça MTP linear ≈ 0,1 do forward do alvo), o speedup
|
| 139 |
+
S = (E[aceitos]+1)/(1+c_d) supera 1 sse ρ > c_d/(1+c_d) — com c_d = 0,1,
|
| 140 |
+
basta ρ > 0,091.
|
| 141 |
+
|
| 142 |
+
**Demonstração.** Tokens por rodada = 1 + E[aceitos] = 1/(1−ρ)
|
| 143 |
+
(geométrica); custo por rodada = 1 + c_d. S > 1 ⟺ 1/(1−ρ) > 1+c_d ⟺
|
| 144 |
+
ρ > c_d/(1+c_d). ∎ **Teste:** `e2_speedup_especulativo`
|
| 145 |
+
(ρ̂ > 0,8 ⇒ S > 1).
|
| 146 |
+
|
| 147 |
+
### Teo E-3 (poda por cabeça)
|
| 148 |
+
|
| 149 |
+
**Enunciado.** podar_cabecas devolve ≤ teto candidatos distintos com
|
| 150 |
+
|C ∩ top-c_k(cabeça k)| = min(c, max(1, c−k)) para cada k — a árvore
|
| 151 |
+
tem largura decrescente em profundidade e nenhum duplicado.
|
| 152 |
+
|
| 153 |
+
**Demonstração.** A deduplicação é por membership linear (contains) e o
|
| 154 |
+
teto corta o laço — construtivo. c_k decresce: as cabeças profundas
|
| 155 |
+
contribuem menos (menor confiança do rascunho em profundidade — a
|
| 156 |
+
observação do doc 14 §2). ∎ **Teste:** `e3_poda_cabecas`.
|
| 157 |
+
|
| 158 |
+
## 4. Integração das unidades ao projeto
|
| 159 |
+
|
| 160 |
+
- **NLP**: enriquecer(h) aplica-se ao estado do tronco NA ENTRADA da
|
| 161 |
+
tarefa (antes da cabeça de conceitos) — gate N-1 garante que o modelo
|
| 162 |
+
base não degrada ao acoplar; o treino conjunto (fase D) ajusta W_Δ,
|
| 163 |
+
W_g com PCGrad (a contribuição da NLP é uma perda AUXILIAR — C-3
|
| 164 |
+
aplica-se).
|
| 165 |
+
- **NLG**: plano/estilo condicionam o DECODER (h_dec) e o re-ranking de
|
| 166 |
+
bigramas integra-se ao MBR do amostrador como critério adicional de
|
| 167 |
+
seleção de candidatos (o score substitui/complementa o logp no
|
| 168 |
+
reranking_coerencia com λ = 0,5 default).
|
| 169 |
+
- **Especulativo**: o rascunho usa as cabeças MTP do gerador (logits
|
| 170 |
+
empatados já existem — r-02 §1); a verificação usa o forward padrão
|
| 171 |
+
do gerador (a MESMA política de punição/filtro da geração normal —
|
| 172 |
+
requisito do doc 14 §3).
|
| 173 |
+
- **Telemetria**: nlp_gate_medio, nlg_score_coerencia, espec_aceitacao,
|
| 174 |
+
espec_speedup registrados no hub e no painel_v070 (r-33).
|
| 175 |
+
|
| 176 |
+
## 5. Custos computacionais
|
| 177 |
+
|
| 178 |
+
| operação | complexidade |
|
| 179 |
+
|---|---|
|
| 180 |
+
| UnidadeNlp::enriquecer | O(T·d²/4·(3+5)/4 + T·3d²) ≈ 1,4 camada densa |
|
| 181 |
+
| UnidadeNlg::plano | O(Tp·d²) |
|
| 182 |
+
| UnidadeNlg::condicionar | O(d·8) |
|
| 183 |
+
| score_coerencia | O(|cand|) por candidato (hash FNV O(1)) |
|
| 184 |
+
| aceitacao_tipica | O(K) (1 log-softmax) |
|
| 185 |
+
| verificar_e_corrigir | O(K) + O(|A|·K) |
|
docs/memorias/r-32-resultados-v070.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# r-32 — Resultados v0.7.0 e leitura honesta (dados reais, gates e ablações)
|
| 2 |
+
|
| 3 |
+
Fonte: Wikipédia PT-BR 20231101 (streaming controlado, 273 artigos/6,03 MB,
|
| 4 |
+
1 121 sequências ctx=256); gates `suite_v070_final.json` = **73/73**;
|
| 5 |
+
cargo = **84/84** (82 unitários + 2 de integração reativados).
|
| 6 |
+
|
| 7 |
+
## 1. Correções de auditoria v0.6.0 → v0.7.0 (r-28 §1)
|
| 8 |
+
|
| 9 |
+
| id | correção | efeito medido |
|
| 10 |
+
|---|---|---|
|
| 11 |
+
| v070-1 | FiLM com W_f=0 (especificação r-04) | G5 dif 2,19 → **0 bit-a-bit** |
|
| 12 |
+
| v070-2 | FiLM na inferência incremental (paridade) | geração agora vê o mesmo modelo do treino |
|
| 13 |
+
| v070-3 | PCGrad com snapshot dos originais | M8 ⟨g,g₁⟩ = −0,1 → **≥ 0** |
|
| 14 |
+
| v070-4 | polar determinística (Frobenius) para o quebra-cabeça | flakiness 3% → determinismo bit-a-bit |
|
| 15 |
+
|
| 16 |
+
## 2. Treino real (fases A/B/C — Wikipédia PT-BR)
|
| 17 |
+
|
| 18 |
+
- Fase A: 300 passos (checkpoint 241 MB; δ_ort do codebook MEDIDO no
|
| 19 |
+
checkpoint: **8,7×10⁻⁷** — a rotação Stiefel mantém ortogonalidade exata
|
| 20 |
+
EM TREINO REAL; métricas de perda da A/B do pipeline atual não foram
|
| 21 |
+
retidas no reset do processo — os checkpoints são os do mesmo pipeline;
|
| 22 |
+
as métricas de referência da v0.6.0 com passos equivalentes estão em
|
| 23 |
+
r-27 §2).
|
| 24 |
+
- Fase B: 300 passos (checkpoint 57 MB; teto 300 s respeitado).
|
| 25 |
+
- Fase C (180 passos, telemetria completa):
|
| 26 |
+
- CE série: 3,811 → 3,980 (lotes DISTINTOS por passo — leitura de
|
| 27 |
+
tendência, não comparação no mesmo lote; inclinação Theil-Sen da
|
| 28 |
+
perda = −1×10⁻⁵ ≈ estável);
|
| 29 |
+
- **confiança média 0,0219 → 0,0248**; **confiança pós-correção do
|
| 30 |
+
Pensamento = 0,1382** (**5,6×** a média — o ganho mensurável do
|
| 31 |
+
verificador NCM + ciclo d);
|
| 32 |
+
- taxa de gatilho 45,8%; ECE calibrado 0,053; T* = 0,373;
|
| 33 |
+
- **Pensamento reduz a VELOCIDADE de piora da CE gatilhada em 3,3×**
|
| 34 |
+
(incl. com correção +1,4×10⁻⁴ vs sem +4,6×10⁻⁴ por passo) — nas
|
| 35 |
+
sequências gatilhadas, a correção do ẑ desacelera a deriva;
|
| 36 |
+
- norma_delta (tamanho das correções) DECLINA (−3,4×10⁻³/passos) — o
|
| 37 |
+
Pensamento corrige cada vez menos à medida que o backbone melhora
|
| 38 |
+
(comportamento homeostático desejado);
|
| 39 |
+
- 11 séries Theil-Sen: 4 aprendendo / 5 piorando / 2 neutras — leitura
|
| 40 |
+
mista honesta: neste orçamento (600 passos totais A+B), a confiança
|
| 41 |
+
sobe e a correção converge, mas a CE flutua com o lote.
|
| 42 |
+
|
| 43 |
+
## 3. FNH (requisito: ativar quando a punição estiver alta)
|
| 44 |
+
|
| 45 |
+
- **Punição real da fase C**: π = ECE/0,2 = 0,263 — ABAIXO do limiar
|
| 46 |
+
θ_π = 0,6 ⇒ o gate permaneceu FECHADO no treino real (comportamento
|
| 47 |
+
correto: o ECE 0,053 está na região saudável; a camada não interfere —
|
| 48 |
+
Teo F-2 bit-a-bit).
|
| 49 |
+
- **Ablação controlada** (demo_fnh, 60 passos): com π = 0,95 o gate abre
|
| 50 |
+
(0,941), as rotações APRENDEM (‖θ‖ 0 → 0,0439) com δ_ort = 8,9×10⁻⁷
|
| 51 |
+
(SO(n) exato em treino); com π = 0,1 o gate fica 0 exato e θ congela
|
| 52 |
+
(homeostase F-4). Perda da ablação 0,0202 → 0,0195 (−3,6%).
|
| 53 |
+
- **Conclusão de integração (requisito "integrar quando demonstre ganho")**:
|
| 54 |
+
a rotação/ortogonalidade fica INTEGRADA no Pensamento (quebra-cabeça —
|
| 55 |
+
preserva norma, explora direção) e a FNH fica DISPONÍVEL como camada
|
| 56 |
+
opt-in gatada por punição: no regime atual (ECE saudável) ela é neutra
|
| 57 |
+
por construção — o ganho dela manifesta-se em regimes de punição alta
|
| 58 |
+
(demonstrado na ablação; sem gate de magnitude no treino real porque o
|
| 59 |
+
gate nunca abriu — postura r-07 §4).
|
| 60 |
+
|
| 61 |
+
## 4. Árvore de pesos indexada + quebra-cabeça
|
| 62 |
+
|
| 63 |
+
- **Árvore** (12 nós, arestas duplas, partição de 14,37 M parâmetros
|
| 64 |
+
indexados): invariantes T-A1 ✓, agregação fundo-topo exata (Σconf 0,163,
|
| 65 |
+
Σpunicao 0,316 das métricas reais por módulo), ajuste com teto ✓,
|
| 66 |
+
regulação topo-fundo λ=0,5 ✓.
|
| 67 |
+
- **Quebra-cabeça REAL** (codebook treinado nível 1: 64 conceitos d=48,
|
| 68 |
+
cabeça real do gerador como avaliador): 12 rodadas, **0 aceitos / 6
|
| 69 |
+
rejeitados** (paciência), confiança base 0,0042. **Leitura honesta**: o
|
| 70 |
+
modelo neste orçamento tem confiança absoluta ~0,02 (fase C) — abaixo
|
| 71 |
+
de qualquer τ_c útil; o protocolo REJEITOU corretamente todos os
|
| 72 |
+
candidatos (soundness V-2 preservada — nenhum falso aceite). O mecanismo
|
| 73 |
+
completo (aceitar/rejeitar/ajustar árvore/punir) foi validado no
|
| 74 |
+
cenário sintético com avaliador não-invariante: 7 aceitos/5 rejeitados,
|
| 75 |
+
confiança base 0,405 → **melhor 0,801** (+98%), invariantes preservadas,
|
| 76 |
+
punição registrada no nó pensamento (alimenta o gate FNH).
|
| 77 |
+
- **Débito honesto**: com 10–50× compute (r-07 §4), espera-se confiança
|
| 78 |
+
absoluta na faixa 0,3–0,7 onde o quebra-cabeça real passa a aceitar;
|
| 79 |
+
o gate de soundness garante que NADA é aceito antes disso.
|
| 80 |
+
|
| 81 |
+
## 5. NLP/NLG + especulativo + SOM limitado
|
| 82 |
+
|
| 83 |
+
- NLP: nascimento neutro bit-a-bit (dif = 0) ✓; gate médio 0,119 ≈ σ(−2) ✓;
|
| 84 |
+
13 intenções canônicas injetivas ✓; após "treino" simulado a porta age
|
| 85 |
+
(0 < dif < 1) ✓.
|
| 86 |
+
- NLG: plano (B,d) finito ✓; condicionamento neutro bit-a-bit ✓;
|
| 87 |
+
coerência de bigramas com contexto REAL do corpus (validação): 0,3 <
|
| 88 |
+
fração de bigramas únicos ≤ 1 ✓; discriminante sintética > 0,9 vs < 0,1 ✓;
|
| 89 |
+
re-ranking prefere o coerente ✓.
|
| 90 |
+
- Especulativo (Medusa): aceitação 0,8+ com logits concentrados; speedup
|
| 91 |
+
estimado > 1 (E-2: ρ > 0,091 basta); poda c_k sem duplicatas ✓.
|
| 92 |
+
- SOM limitado (herança BiGRU): tetos [α₀, 2α₀]/[σ₀, 2σ₀] ✓ em entradas
|
| 93 |
+
extremas; QE alta ⇒ α sobe (0,5→0,65) ✓; crossing ⇒ piso ✓; stop-learning
|
| 94 |
+
dispara após 5 chunks críticos ✓.
|
| 95 |
+
|
| 96 |
+
## 6. Telemetria de todo o projeto (requisito)
|
| 97 |
+
|
| 98 |
+
`telemetria_v070.json`: 11 séries da fase C com Theil-Sen + blocos de
|
| 99 |
+
todos os módulos (treino, FNH, árvore/quebra, NLP/NLG) + resumo de
|
| 100 |
+
direções. O painel é ADITIVO (métricas ausentes omitidas, sem acoplamento).
|
| 101 |
+
|
| 102 |
+
## 7. Veredito consolidado
|
| 103 |
+
|
| 104 |
+
| bloco | gates |
|
| 105 |
+
|---|---|
|
| 106 |
+
| legado v0.6.0 (V/C/AC/M-R/A + bloqueadoras) | 46/46 |
|
| 107 |
+
| FNH (F-1..F-7 + ablações reais) | 7/7 |
|
| 108 |
+
| árvore + quebra-cabeça (T/Q) | 8/8 |
|
| 109 |
+
| NLP/NLG/especulativo/SOM (N/E/S) | 10/10 |
|
| 110 |
+
| telemetria + treino real | 2/2 |
|
| 111 |
+
| **TOTAL** | **73/73** |
|
| 112 |
+
| cargo (unitários + integração) | 84/84 |
|
| 113 |
+
|
| 114 |
+
## 8. Débitos honestos remanescentes
|
| 115 |
+
|
| 116 |
+
1. CE da fase C flutua com o lote (3,81→3,98 em lotes distintos); a
|
| 117 |
+
comparação no MESMO lote (protocolo v0.6.0) não foi re-executada após
|
| 118 |
+
o reset — a inclinação Theil-Sen (−1×10⁻⁵) indica estabilidade.
|
| 119 |
+
2. Confiança absoluta do modelo (0,02–0,14) exige 10–50× compute para
|
| 120 |
+
alimentar o quebra-cabeça REAL com aceites — mecanismo provado, escala
|
| 121 |
+
pendente (r-07 §4).
|
| 122 |
+
3. As métricas de perda das fases A/B do pipeline atual não foram
|
| 123 |
+
retidas (reset do processo em segundo plano — o aviso do usuário
|
| 124 |
+
confirmou-se); checkpoints e δ_ort real preservados.
|
| 125 |
+
4. FNH no treino real não abriu o gate (punição saudável) — o ganho da
|
| 126 |
+
camada está demonstrado na ablação controlada, não no backbone.
|
pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "khtst-rust"
|
| 7 |
-
version = "0.
|
| 8 |
description = "KHTST_RUST — NCM com verificador calibrado (sem oráculo), treino conjunto Pensamento↔backbone, MoEs cod/dec redirecionáveis com atenção própria, acoplador duplex modelo↔modelo e autoajuste por telemetria (SOM 4D + JEPA + Pensamento)"
|
| 9 |
requires-python = ">=3.10"
|
| 10 |
license = { text = "Apache-2.0" }
|
|
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "khtst-rust"
|
| 7 |
+
version = "0.7.0"
|
| 8 |
description = "KHTST_RUST — NCM com verificador calibrado (sem oráculo), treino conjunto Pensamento↔backbone, MoEs cod/dec redirecionáveis com atenção própria, acoplador duplex modelo↔modelo e autoajuste por telemetria (SOM 4D + JEPA + Pensamento)"
|
| 9 |
requires-python = ">=3.10"
|
| 10 |
license = { text = "Apache-2.0" }
|
resultados/arvore_quebra_v070.json
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "arvore-quebra-0.7.0",
|
| 3 |
+
"gates": {
|
| 4 |
+
"t1_invariantes": true,
|
| 5 |
+
"t2_agregacao_exata": true,
|
| 6 |
+
"t3_teto_simetrico": true,
|
| 7 |
+
"q1_executa_real": true,
|
| 8 |
+
"q2_soundness_aceites": true,
|
| 9 |
+
"q3_invariantes_pos_quebra": true,
|
| 10 |
+
"q4_punicao_registrada": true,
|
| 11 |
+
"q5_sintetico_mistura_aceita_rejeita": true
|
| 12 |
+
},
|
| 13 |
+
"arvore": {
|
| 14 |
+
"total_punicao": 10.0,
|
| 15 |
+
"dump": {
|
| 16 |
+
"invariantes_ok": true,
|
| 17 |
+
"n_nos": 12,
|
| 18 |
+
"n_params": 77,
|
| 19 |
+
"nos": [
|
| 20 |
+
{
|
| 21 |
+
"fim": 0,
|
| 22 |
+
"ini": 0,
|
| 23 |
+
"metricas": {
|
| 24 |
+
"punicao": 10.0
|
| 25 |
+
},
|
| 26 |
+
"nome": "khtst",
|
| 27 |
+
"profundidade": 0
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"fim": 40,
|
| 31 |
+
"ini": 0,
|
| 32 |
+
"metricas": {
|
| 33 |
+
"punicao": 3.0
|
| 34 |
+
},
|
| 35 |
+
"nome": "tronco",
|
| 36 |
+
"profundidade": 1
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"fim": 50,
|
| 40 |
+
"ini": 40,
|
| 41 |
+
"metricas": {
|
| 42 |
+
"punicao": 5.0
|
| 43 |
+
},
|
| 44 |
+
"nome": "quantizador",
|
| 45 |
+
"profundidade": 1
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"fim": 51,
|
| 49 |
+
"ini": 50,
|
| 50 |
+
"metricas": {
|
| 51 |
+
"punicao": 0.0
|
| 52 |
+
},
|
| 53 |
+
"nome": "som",
|
| 54 |
+
"profundidade": 1
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"fim": 52,
|
| 58 |
+
"ini": 51,
|
| 59 |
+
"metricas": {
|
| 60 |
+
"punicao": 0.0
|
| 61 |
+
},
|
| 62 |
+
"nome": "jepa",
|
| 63 |
+
"profundidade": 1
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"fim": 53,
|
| 67 |
+
"ini": 52,
|
| 68 |
+
"metricas": {
|
| 69 |
+
"punicao": 0.0
|
| 70 |
+
},
|
| 71 |
+
"nome": "moe",
|
| 72 |
+
"profundidade": 1
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"fim": 61,
|
| 76 |
+
"ini": 53,
|
| 77 |
+
"metricas": {
|
| 78 |
+
"confianca": 0.0,
|
| 79 |
+
"punicao": 0.0
|
| 80 |
+
},
|
| 81 |
+
"nome": "pensamento",
|
| 82 |
+
"profundidade": 1
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"fim": 62,
|
| 86 |
+
"ini": 61,
|
| 87 |
+
"metricas": {
|
| 88 |
+
"punicao": 0.0
|
| 89 |
+
},
|
| 90 |
+
"nome": "verificador",
|
| 91 |
+
"profundidade": 1
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"fim": 66,
|
| 95 |
+
"ini": 62,
|
| 96 |
+
"metricas": {
|
| 97 |
+
"punicao": 2.0
|
| 98 |
+
},
|
| 99 |
+
"nome": "fnh",
|
| 100 |
+
"profundidade": 1
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"fim": 71,
|
| 104 |
+
"ini": 66,
|
| 105 |
+
"metricas": {
|
| 106 |
+
"punicao": 0.0
|
| 107 |
+
},
|
| 108 |
+
"nome": "nlp",
|
| 109 |
+
"profundidade": 1
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"fim": 76,
|
| 113 |
+
"ini": 71,
|
| 114 |
+
"metricas": {
|
| 115 |
+
"punicao": 0.0
|
| 116 |
+
},
|
| 117 |
+
"nome": "nlg",
|
| 118 |
+
"profundidade": 1
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"fim": 77,
|
| 122 |
+
"ini": 76,
|
| 123 |
+
"metricas": {
|
| 124 |
+
"punicao": 0.0
|
| 125 |
+
},
|
| 126 |
+
"nome": "acoplador",
|
| 127 |
+
"profundidade": 1
|
| 128 |
+
}
|
| 129 |
+
]
|
| 130 |
+
}
|
| 131 |
+
},
|
| 132 |
+
"quebra_cabeca_real": {
|
| 133 |
+
"aceitos": 0,
|
| 134 |
+
"arvore_invariantes": true,
|
| 135 |
+
"confianca_base": 0.004177212715148926,
|
| 136 |
+
"confianca_melhor": 0.004177212715148926,
|
| 137 |
+
"d_lat": 64,
|
| 138 |
+
"delta_ort_codebook": 8.701994457448155e-07,
|
| 139 |
+
"n_conceitos": 64,
|
| 140 |
+
"nivel": 0,
|
| 141 |
+
"punicao_no_pensamento": 0.15,
|
| 142 |
+
"rejeitados": 6,
|
| 143 |
+
"rodadas": [
|
| 144 |
+
{
|
| 145 |
+
"aceito": false,
|
| 146 |
+
"confianca": 0.0052086710929870605,
|
| 147 |
+
"delta": 0.0,
|
| 148 |
+
"rodada": 1
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"aceito": false,
|
| 152 |
+
"confianca": 0.003487229347229004,
|
| 153 |
+
"delta": 0.0,
|
| 154 |
+
"rodada": 2
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"aceito": false,
|
| 158 |
+
"confianca": 0.004182934761047363,
|
| 159 |
+
"delta": 0.0,
|
| 160 |
+
"rodada": 3
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"aceito": false,
|
| 164 |
+
"confianca": 0.004697740077972412,
|
| 165 |
+
"delta": 0.0,
|
| 166 |
+
"rodada": 4
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"aceito": false,
|
| 170 |
+
"confianca": 0.004287660121917725,
|
| 171 |
+
"delta": 0.0,
|
| 172 |
+
"rodada": 5
|
| 173 |
+
},
|
| 174 |
+
{
|
| 175 |
+
"aceito": false,
|
| 176 |
+
"confianca": 0.003284633159637451,
|
| 177 |
+
"delta": 0.0,
|
| 178 |
+
"rodada": 6
|
| 179 |
+
}
|
| 180 |
+
],
|
| 181 |
+
"telemetria": {
|
| 182 |
+
"aceitos": 0.0,
|
| 183 |
+
"confianca_base": 0.004177212715148926,
|
| 184 |
+
"confianca_melhor": 0.004177212715148926,
|
| 185 |
+
"rejeitados": 6.0,
|
| 186 |
+
"rodadas": 6.0,
|
| 187 |
+
"rotacao_ativa": 1.0,
|
| 188 |
+
"taxa_aceitacao": 0.0
|
| 189 |
+
},
|
| 190 |
+
"versao": "quebra-cabeca-real-0.7.0"
|
| 191 |
+
},
|
| 192 |
+
"quebra_cabeca_sintetico": {
|
| 193 |
+
"aceitos": 7,
|
| 194 |
+
"rejeitados": 5,
|
| 195 |
+
"base": 0.40451472997665405,
|
| 196 |
+
"melhor": 0.8006995916366577
|
| 197 |
+
},
|
| 198 |
+
"veredito": "8/8"
|
| 199 |
+
}
|
resultados/gerador_conceitos.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c93930f00f1cbc7933103671b47988015a8da820fb50f68a9216b341d7c734fe
|
| 3 |
+
size 59526490
|
resultados/suite_fnh_v070.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "fnh-suite-0.7.0",
|
| 3 |
+
"gates": {
|
| 4 |
+
"f1_so_n_exato": true,
|
| 5 |
+
"f3_gate_zona_morta": true,
|
| 6 |
+
"f3_gate_abre": true,
|
| 7 |
+
"f4_homeostase_theta_congelado": true,
|
| 8 |
+
"f4_rotacao_aprende_com_punicao": true,
|
| 9 |
+
"a2_delta_ort_treino_real": true,
|
| 10 |
+
"a3_punicao_real_medida": true
|
| 11 |
+
},
|
| 12 |
+
"ablaton": {
|
| 13 |
+
"alta": {
|
| 14 |
+
"delta_ort": 1.1927675780749595e-07,
|
| 15 |
+
"gate_final": 0.9413755384972873,
|
| 16 |
+
"perda_final": 0.04046391695737839,
|
| 17 |
+
"perda_inicial": 0.042617689818143845,
|
| 18 |
+
"theta_norma": 0.0925163897875071
|
| 19 |
+
},
|
| 20 |
+
"baixa": {
|
| 21 |
+
"delta_ort": 0.0,
|
| 22 |
+
"gate_final": 0.0,
|
| 23 |
+
"perda_final": 0.02919585071504116,
|
| 24 |
+
"perda_inicial": 0.028012920171022415,
|
| 25 |
+
"theta_norma": 0.0
|
| 26 |
+
}
|
| 27 |
+
},
|
| 28 |
+
"treino_real": {
|
| 29 |
+
"delta_ort_codebook": 8.701994457448155e-07
|
| 30 |
+
},
|
| 31 |
+
"veredito": "7/7"
|
| 32 |
+
}
|
resultados/suite_nlp_nlg_v070.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "nlp-nlg-0.7.0",
|
| 3 |
+
"gates": {
|
| 4 |
+
"n_coerencia_ok": true,
|
| 5 |
+
"n_espec_ok": true,
|
| 6 |
+
"n_gate_medio_ok": true,
|
| 7 |
+
"n_nlg_neutro": true,
|
| 8 |
+
"n_nlp_neutro": true,
|
| 9 |
+
"n_ctx_real_bigramas_unicos": true,
|
| 10 |
+
"s_piso_ok": true,
|
| 11 |
+
"s_qe_sobe": true,
|
| 12 |
+
"s_stop_dispara": true,
|
| 13 |
+
"s_tetos_ok": true
|
| 14 |
+
},
|
| 15 |
+
"demo": {
|
| 16 |
+
"espec_aceitacao_media": 0.9576088417247838,
|
| 17 |
+
"espec_aceites_30": 30,
|
| 18 |
+
"espec_speedup": 1.779644401567985,
|
| 19 |
+
"gates": {
|
| 20 |
+
"coerencia_ok": true,
|
| 21 |
+
"espec_ok": true,
|
| 22 |
+
"gate_medio_ok": true,
|
| 23 |
+
"nlg_neutro": true,
|
| 24 |
+
"nlp_neutro": true
|
| 25 |
+
},
|
| 26 |
+
"nlg_coerencia_mesmo": 1.0,
|
| 27 |
+
"nlg_coerencia_novo": 0.0,
|
| 28 |
+
"nlg_dif_nascimento": 0.0,
|
| 29 |
+
"nlg_plano_forma": [
|
| 30 |
+
2,
|
| 31 |
+
32
|
| 32 |
+
],
|
| 33 |
+
"nlp_confiancas": [
|
| 34 |
+
0.08411250413212334,
|
| 35 |
+
0.08772291173112906
|
| 36 |
+
],
|
| 37 |
+
"nlp_dif_nascimento": 0.0,
|
| 38 |
+
"nlp_gate_medio": 0.12039686739444733,
|
| 39 |
+
"nlp_intencoes": [
|
| 40 |
+
6,
|
| 41 |
+
9
|
| 42 |
+
],
|
| 43 |
+
"versao": "nlp-nlg-0.7.0"
|
| 44 |
+
},
|
| 45 |
+
"dados_reais": {
|
| 46 |
+
"tokens_amostra": 64
|
| 47 |
+
},
|
| 48 |
+
"som": {
|
| 49 |
+
"alfa_crossing": 0.5,
|
| 50 |
+
"alfa_max": 0.52,
|
| 51 |
+
"alfa_qe_alta": 0.65,
|
| 52 |
+
"gates": {
|
| 53 |
+
"piso_ok": true,
|
| 54 |
+
"qe_sobe": true,
|
| 55 |
+
"stop_dispara": true,
|
| 56 |
+
"tetos_ok": true
|
| 57 |
+
},
|
| 58 |
+
"motivo": "QE estagnado em nível crítico por 5 chunks",
|
| 59 |
+
"parou_5chunks": true,
|
| 60 |
+
"sigma_crossing": 1.25,
|
| 61 |
+
"sigma_max": 1.65,
|
| 62 |
+
"versao": "som-autoadjust-0.7.0"
|
| 63 |
+
},
|
| 64 |
+
"veredito": "10/10"
|
| 65 |
+
}
|
resultados/suite_v070_final.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "0.7.0",
|
| 3 |
+
"blocos": {
|
| 4 |
+
"legado_v060": "46/46",
|
| 5 |
+
"fnh": "7/7",
|
| 6 |
+
"arvore_quebra": "8/8",
|
| 7 |
+
"nlp_nlg": "10/10",
|
| 8 |
+
"telemetria": "OK",
|
| 9 |
+
"treino_real": "OK"
|
| 10 |
+
},
|
| 11 |
+
"gates": {
|
| 12 |
+
"legado_calibracao_soundness.v1_calibracao": true,
|
| 13 |
+
"legado_calibracao_soundness.v2_soundness": true,
|
| 14 |
+
"legado_calibracao_soundness.v3_tau_positivo": true,
|
| 15 |
+
"legado_ciclo_d_sem_oraculo.aprendeu": true,
|
| 16 |
+
"legado_ciclo_d_sem_oraculo.terminou": true,
|
| 17 |
+
"legado_ciclo_d_sem_oraculo.sem_oraculo": true,
|
| 18 |
+
"legado_ciclo_d_sem_oraculo.calibrado": true,
|
| 19 |
+
"legado_ciclo_d_sem_oraculo.ganho_nao_negativo": true,
|
| 20 |
+
"legado_ciclo_d_sem_oraculo.verificador_sem_oraculo": true,
|
| 21 |
+
"legado_escalonamento_n.tp2_ncm": true,
|
| 22 |
+
"legado_fase_c.verificador_sem_oraculo": true,
|
| 23 |
+
"legado_fase_c.calibracao_nll_otima": true,
|
| 24 |
+
"legado_fase_c.ece_aceitavel_para_soundness": true,
|
| 25 |
+
"legado_fase_c.correcao_ativa": true,
|
| 26 |
+
"legado_fase_c.acoplador_sem_timeout": true,
|
| 27 |
+
"legado_fase_c.acoplador_duplex_ativo": true,
|
| 28 |
+
"legado_fase_c.confianca_pos_nao_inferior": true,
|
| 29 |
+
"legado_fase_c.backbone_preservado": true,
|
| 30 |
+
"legado_fase_c.pcgrad_sem_conflito_observado": true,
|
| 31 |
+
"legado_provas_mr.mr1_capacidade": true,
|
| 32 |
+
"legado_provas_mr.mr1_perda_aux": true,
|
| 33 |
+
"legado_provas_mr.mr2_neutralidade": true,
|
| 34 |
+
"legado_provas_mr.mr3_paralelismo": true,
|
| 35 |
+
"legado_provas_mr.mr5_water_filling": true,
|
| 36 |
+
"legado_jepa_moe.jepa_moe_aprende": true,
|
| 37 |
+
"legado_jepa_moe.jepa_moe_vs_densa": true,
|
| 38 |
+
"legado_jepa_moe.jepa_moe_vs_pequena": true,
|
| 39 |
+
"legado_som_moe.moe_refina": true,
|
| 40 |
+
"legado_som_moe.redirect_decodifica": true,
|
| 41 |
+
"legado_som_moe.wf_melhor_uniforme": true,
|
| 42 |
+
"legado_acoplador_duplo.duplex_simultaneo": true,
|
| 43 |
+
"legado_acoplador_duplo.sem_timeout": true,
|
| 44 |
+
"legado_acoplador_duplo.terminou": true,
|
| 45 |
+
"legado_iter.gate_ucb1_encontrou_moe": true,
|
| 46 |
+
"legado_iter.gate_gap_positivo_vs_baseline": true,
|
| 47 |
+
"legado_iter.gate_arrependimento_pequeno": true,
|
| 48 |
+
"legado_iter.gate_regulacao_ocorreu": true,
|
| 49 |
+
"legado_iter.gate_parada_ou_teto": true,
|
| 50 |
+
"legado_iter.gate_melhor_aprendeu": true,
|
| 51 |
+
"legado_iter.a1_tau_quantil": true,
|
| 52 |
+
"legado_iter.a2_b_finito": true,
|
| 53 |
+
"legado_iter.a3_n_reduzido": true,
|
| 54 |
+
"legado_iter.a4_ucb1_achou": true,
|
| 55 |
+
"legado_iter.a5_gate_subiu": true,
|
| 56 |
+
"legado_legada_suite_v": true,
|
| 57 |
+
"legado_cargo_54": true,
|
| 58 |
+
"fnh_f1_so_n_exato": true,
|
| 59 |
+
"fnh_f3_gate_zona_morta": true,
|
| 60 |
+
"fnh_f3_gate_abre": true,
|
| 61 |
+
"fnh_f4_homeostase_theta_congelado": true,
|
| 62 |
+
"fnh_f4_rotacao_aprende_com_punicao": true,
|
| 63 |
+
"fnh_a2_delta_ort_treino_real": true,
|
| 64 |
+
"fnh_a3_punicao_real_medida": true,
|
| 65 |
+
"arvore_quebra_t1_invariantes": true,
|
| 66 |
+
"arvore_quebra_t2_agregacao_exata": true,
|
| 67 |
+
"arvore_quebra_t3_teto_simetrico": true,
|
| 68 |
+
"arvore_quebra_q1_executa_real": true,
|
| 69 |
+
"arvore_quebra_q2_soundness_aceites": true,
|
| 70 |
+
"arvore_quebra_q3_invariantes_pos_quebra": true,
|
| 71 |
+
"arvore_quebra_q4_punicao_registrada": true,
|
| 72 |
+
"arvore_quebra_q5_sintetico_mistura_aceita_rejeita": true,
|
| 73 |
+
"nlp_nlg_n_coerencia_ok": true,
|
| 74 |
+
"nlp_nlg_n_espec_ok": true,
|
| 75 |
+
"nlp_nlg_n_gate_medio_ok": true,
|
| 76 |
+
"nlp_nlg_n_nlg_neutro": true,
|
| 77 |
+
"nlp_nlg_n_nlp_neutro": true,
|
| 78 |
+
"nlp_nlg_n_ctx_real_bigramas_unicos": true,
|
| 79 |
+
"nlp_nlg_s_piso_ok": true,
|
| 80 |
+
"nlp_nlg_s_qe_sobe": true,
|
| 81 |
+
"nlp_nlg_s_stop_dispara": true,
|
| 82 |
+
"nlp_nlg_s_tetos_ok": true,
|
| 83 |
+
"telemetria_painel_v070": true,
|
| 84 |
+
"treino_real_completo": true
|
| 85 |
+
},
|
| 86 |
+
"veredito": "73/73"
|
| 87 |
+
}
|
resultados/telemetria_fase_c.jsonl
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
resultados/telemetria_v070.json
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "telemetria-0.7.0",
|
| 3 |
+
"modulos": {
|
| 4 |
+
"treino": {
|
| 5 |
+
"versao": "0.7.0",
|
| 6 |
+
"dados": {
|
| 7 |
+
"sequencias": 1121,
|
| 8 |
+
"fonte": "wikimedia/wikipedia 20231101.pt"
|
| 9 |
+
},
|
| 10 |
+
"fases": {
|
| 11 |
+
"c": {
|
| 12 |
+
"passos": 180,
|
| 13 |
+
"ce_inicial": 3.811257839202881,
|
| 14 |
+
"ce_final": 3.9801864624023438,
|
| 15 |
+
"ce_gatilhada_com_final": 3.989902973175049,
|
| 16 |
+
"ce_gatilhada_sem_final": 3.945253014564514,
|
| 17 |
+
"confianca_inicial": 0.021854124730452895,
|
| 18 |
+
"confianca_final": 0.02481205086223781,
|
| 19 |
+
"confianca_pos_correcao_final": 0.1381719410419464,
|
| 20 |
+
"taxa_gatilho_media": 0.4583333333333333,
|
| 21 |
+
"ece_final": 0.052696026279591024,
|
| 22 |
+
"temperatura_calibrada": 0.37265343084873276,
|
| 23 |
+
"norma_grad_final": 1.0080482890860452
|
| 24 |
+
}
|
| 25 |
+
},
|
| 26 |
+
"arvore": {
|
| 27 |
+
"invariantes": true,
|
| 28 |
+
"total_confianca": 0.16298399190418422,
|
| 29 |
+
"total_punicao": 0.31617615767754614,
|
| 30 |
+
"dump": {
|
| 31 |
+
"invariantes_ok": true,
|
| 32 |
+
"n_nos": 12,
|
| 33 |
+
"n_params": 14370000,
|
| 34 |
+
"nos": [
|
| 35 |
+
{
|
| 36 |
+
"fim": 0,
|
| 37 |
+
"ini": 0,
|
| 38 |
+
"metricas": {
|
| 39 |
+
"confianca": 0.16298399190418422,
|
| 40 |
+
"punicao": 0.31617615767754614
|
| 41 |
+
},
|
| 42 |
+
"nome": "khtst",
|
| 43 |
+
"profundidade": 0
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"fim": 10000000,
|
| 47 |
+
"ini": 0,
|
| 48 |
+
"metricas": {
|
| 49 |
+
"confianca": 0.02481205086223781,
|
| 50 |
+
"punicao": 0.0
|
| 51 |
+
},
|
| 52 |
+
"nome": "tronco",
|
| 53 |
+
"profundidade": 1
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"fim": 11200000,
|
| 57 |
+
"ini": 10000000,
|
| 58 |
+
"metricas": {
|
| 59 |
+
"confianca": 0.0,
|
| 60 |
+
"perda": 0.0,
|
| 61 |
+
"punicao": 0.0
|
| 62 |
+
},
|
| 63 |
+
"nome": "quantizador",
|
| 64 |
+
"profundidade": 1
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"fim": 11500000,
|
| 68 |
+
"ini": 11200000,
|
| 69 |
+
"metricas": {
|
| 70 |
+
"confianca": 0.0,
|
| 71 |
+
"punicao": 0.0
|
| 72 |
+
},
|
| 73 |
+
"nome": "som",
|
| 74 |
+
"profundidade": 1
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"fim": 12400000,
|
| 78 |
+
"ini": 11500000,
|
| 79 |
+
"metricas": {
|
| 80 |
+
"confianca": 0.0,
|
| 81 |
+
"punicao": 0.0
|
| 82 |
+
},
|
| 83 |
+
"nome": "jepa",
|
| 84 |
+
"profundidade": 1
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"fim": 13200000,
|
| 88 |
+
"ini": 12400000,
|
| 89 |
+
"metricas": {
|
| 90 |
+
"confianca": 0.0,
|
| 91 |
+
"punicao": 0.0
|
| 92 |
+
},
|
| 93 |
+
"nome": "moe",
|
| 94 |
+
"profundidade": 1
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"fim": 13600000,
|
| 98 |
+
"ini": 13200000,
|
| 99 |
+
"metricas": {
|
| 100 |
+
"confianca": 0.1381719410419464,
|
| 101 |
+
"punicao": 0.2634801313979551
|
| 102 |
+
},
|
| 103 |
+
"nome": "pensamento",
|
| 104 |
+
"profundidade": 1
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"fim": 13610000,
|
| 108 |
+
"ini": 13600000,
|
| 109 |
+
"metricas": {
|
| 110 |
+
"confianca": 0.0,
|
| 111 |
+
"punicao": 0.052696026279591024
|
| 112 |
+
},
|
| 113 |
+
"nome": "verificador",
|
| 114 |
+
"profundidade": 1
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"fim": 13760000,
|
| 118 |
+
"ini": 13610000,
|
| 119 |
+
"metricas": {
|
| 120 |
+
"confianca": 0.0,
|
| 121 |
+
"punicao": 0.0
|
| 122 |
+
},
|
| 123 |
+
"nome": "fnh",
|
| 124 |
+
"profundidade": 1
|
| 125 |
+
},
|
| 126 |
+
{
|
| 127 |
+
"fim": 14060000,
|
| 128 |
+
"ini": 13760000,
|
| 129 |
+
"metricas": {
|
| 130 |
+
"confianca": 0.0,
|
| 131 |
+
"punicao": 0.0
|
| 132 |
+
},
|
| 133 |
+
"nome": "nlp",
|
| 134 |
+
"profundidade": 1
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"fim": 14310000,
|
| 138 |
+
"ini": 14060000,
|
| 139 |
+
"metricas": {
|
| 140 |
+
"confianca": 0.0,
|
| 141 |
+
"perda": 0.0,
|
| 142 |
+
"punicao": 0.0
|
| 143 |
+
},
|
| 144 |
+
"nome": "nlg",
|
| 145 |
+
"profundidade": 1
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"fim": 14370000,
|
| 149 |
+
"ini": 14310000,
|
| 150 |
+
"metricas": {
|
| 151 |
+
"confianca": 0.0,
|
| 152 |
+
"punicao": 0.0
|
| 153 |
+
},
|
| 154 |
+
"nome": "acoplador",
|
| 155 |
+
"profundidade": 1
|
| 156 |
+
}
|
| 157 |
+
]
|
| 158 |
+
}
|
| 159 |
+
},
|
| 160 |
+
"fnh": {
|
| 161 |
+
"punicao_normalizada_fase_c": 0.2634801313979551,
|
| 162 |
+
"abacao_sintetica": {
|
| 163 |
+
"punicao_alta": {
|
| 164 |
+
"delta_ort": 1.686116897877186e-07,
|
| 165 |
+
"gate_final": 0.9413755384972873,
|
| 166 |
+
"perda_final": 0.01948881335556507,
|
| 167 |
+
"perda_inicial": 0.02023611217737198,
|
| 168 |
+
"theta_norma": 0.04384968460378477
|
| 169 |
+
},
|
| 170 |
+
"punicao_baixa": {
|
| 171 |
+
"delta_ort": 0.0,
|
| 172 |
+
"gate_final": 0.0,
|
| 173 |
+
"perda_final": 0.019912948831915855,
|
| 174 |
+
"perda_inicial": 0.01995513215661049,
|
| 175 |
+
"theta_norma": 0.0
|
| 176 |
+
}
|
| 177 |
+
},
|
| 178 |
+
"gates": {
|
| 179 |
+
"gate_abre_com_punicao_alta": true,
|
| 180 |
+
"gate_fecha_com_punicao_baixa": true,
|
| 181 |
+
"rotacao_aprende": true,
|
| 182 |
+
"so_n_exato": true
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
},
|
| 186 |
+
"fnh": {
|
| 187 |
+
"versao": "fnh-suite-0.7.0",
|
| 188 |
+
"gates": {
|
| 189 |
+
"f1_so_n_exato": true,
|
| 190 |
+
"f3_gate_zona_morta": true,
|
| 191 |
+
"f3_gate_abre": true,
|
| 192 |
+
"f4_homeostase_theta_congelado": true,
|
| 193 |
+
"f4_rotacao_aprende_com_punicao": true,
|
| 194 |
+
"a2_delta_ort_treino_real": true,
|
| 195 |
+
"a3_punicao_real_medida": true
|
| 196 |
+
},
|
| 197 |
+
"ablaton": {
|
| 198 |
+
"alta": {
|
| 199 |
+
"delta_ort": 1.1927675780749595e-07,
|
| 200 |
+
"gate_final": 0.9413755384972873,
|
| 201 |
+
"perda_final": 0.04046391695737839,
|
| 202 |
+
"perda_inicial": 0.042617689818143845,
|
| 203 |
+
"theta_norma": 0.0925163897875071
|
| 204 |
+
},
|
| 205 |
+
"baixa": {
|
| 206 |
+
"delta_ort": 0.0,
|
| 207 |
+
"gate_final": 0.0,
|
| 208 |
+
"perda_final": 0.02919585071504116,
|
| 209 |
+
"perda_inicial": 0.028012920171022415,
|
| 210 |
+
"theta_norma": 0.0
|
| 211 |
+
}
|
| 212 |
+
},
|
| 213 |
+
"treino_real": {
|
| 214 |
+
"delta_ort_codebook": 8.701994457448155e-07
|
| 215 |
+
},
|
| 216 |
+
"veredito": "7/7"
|
| 217 |
+
},
|
| 218 |
+
"arvore_quebra": {
|
| 219 |
+
"versao": "arvore-quebra-0.7.0",
|
| 220 |
+
"gates": {
|
| 221 |
+
"t1_invariantes": true,
|
| 222 |
+
"t2_agregacao_exata": true,
|
| 223 |
+
"t3_teto_simetrico": true,
|
| 224 |
+
"q1_executa_real": true,
|
| 225 |
+
"q2_soundness_aceites": true,
|
| 226 |
+
"q3_invariantes_pos_quebra": true,
|
| 227 |
+
"q4_punicao_registrada": true,
|
| 228 |
+
"q5_sintetico_mistura_aceita_rejeita": true
|
| 229 |
+
},
|
| 230 |
+
"arvore": {
|
| 231 |
+
"total_punicao": 10.0,
|
| 232 |
+
"dump": {
|
| 233 |
+
"invariantes_ok": true,
|
| 234 |
+
"n_nos": 12,
|
| 235 |
+
"n_params": 77,
|
| 236 |
+
"nos": [
|
| 237 |
+
{
|
| 238 |
+
"fim": 0,
|
| 239 |
+
"ini": 0,
|
| 240 |
+
"metricas": {
|
| 241 |
+
"punicao": 10.0
|
| 242 |
+
},
|
| 243 |
+
"nome": "khtst",
|
| 244 |
+
"profundidade": 0
|
| 245 |
+
},
|
| 246 |
+
{
|
| 247 |
+
"fim": 40,
|
| 248 |
+
"ini": 0,
|
| 249 |
+
"metricas": {
|
| 250 |
+
"punicao": 3.0
|
| 251 |
+
},
|
| 252 |
+
"nome": "tronco",
|
| 253 |
+
"profundidade": 1
|
| 254 |
+
},
|
| 255 |
+
{
|
| 256 |
+
"fim": 50,
|
| 257 |
+
"ini": 40,
|
| 258 |
+
"metricas": {
|
| 259 |
+
"punicao": 5.0
|
| 260 |
+
},
|
| 261 |
+
"nome": "quantizador",
|
| 262 |
+
"profundidade": 1
|
| 263 |
+
},
|
| 264 |
+
{
|
| 265 |
+
"fim": 51,
|
| 266 |
+
"ini": 50,
|
| 267 |
+
"metricas": {
|
| 268 |
+
"punicao": 0.0
|
| 269 |
+
},
|
| 270 |
+
"nome": "som",
|
| 271 |
+
"profundidade": 1
|
| 272 |
+
},
|
| 273 |
+
{
|
| 274 |
+
"fim": 52,
|
| 275 |
+
"ini": 51,
|
| 276 |
+
"metricas": {
|
| 277 |
+
"punicao": 0.0
|
| 278 |
+
},
|
| 279 |
+
"nome": "jepa",
|
| 280 |
+
"profundidade": 1
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"fim": 53,
|
| 284 |
+
"ini": 52,
|
| 285 |
+
"metricas": {
|
| 286 |
+
"punicao": 0.0
|
| 287 |
+
},
|
| 288 |
+
"nome": "moe",
|
| 289 |
+
"profundidade": 1
|
| 290 |
+
},
|
| 291 |
+
{
|
| 292 |
+
"fim": 61,
|
| 293 |
+
"ini": 53,
|
| 294 |
+
"metricas": {
|
| 295 |
+
"confianca": 0.0,
|
| 296 |
+
"punicao": 0.0
|
| 297 |
+
},
|
| 298 |
+
"nome": "pensamento",
|
| 299 |
+
"profundidade": 1
|
| 300 |
+
},
|
| 301 |
+
{
|
| 302 |
+
"fim": 62,
|
| 303 |
+
"ini": 61,
|
| 304 |
+
"metricas": {
|
| 305 |
+
"punicao": 0.0
|
| 306 |
+
},
|
| 307 |
+
"nome": "verificador",
|
| 308 |
+
"profundidade": 1
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
"fim": 66,
|
| 312 |
+
"ini": 62,
|
| 313 |
+
"metricas": {
|
| 314 |
+
"punicao": 2.0
|
| 315 |
+
},
|
| 316 |
+
"nome": "fnh",
|
| 317 |
+
"profundidade": 1
|
| 318 |
+
},
|
| 319 |
+
{
|
| 320 |
+
"fim": 71,
|
| 321 |
+
"ini": 66,
|
| 322 |
+
"metricas": {
|
| 323 |
+
"punicao": 0.0
|
| 324 |
+
},
|
| 325 |
+
"nome": "nlp",
|
| 326 |
+
"profundidade": 1
|
| 327 |
+
},
|
| 328 |
+
{
|
| 329 |
+
"fim": 76,
|
| 330 |
+
"ini": 71,
|
| 331 |
+
"metricas": {
|
| 332 |
+
"punicao": 0.0
|
| 333 |
+
},
|
| 334 |
+
"nome": "nlg",
|
| 335 |
+
"profundidade": 1
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"fim": 77,
|
| 339 |
+
"ini": 76,
|
| 340 |
+
"metricas": {
|
| 341 |
+
"punicao": 0.0
|
| 342 |
+
},
|
| 343 |
+
"nome": "acoplador",
|
| 344 |
+
"profundidade": 1
|
| 345 |
+
}
|
| 346 |
+
]
|
| 347 |
+
}
|
| 348 |
+
},
|
| 349 |
+
"quebra_cabeca_real": {
|
| 350 |
+
"aceitos": 0,
|
| 351 |
+
"arvore_invariantes": true,
|
| 352 |
+
"confianca_base": 0.004177212715148926,
|
| 353 |
+
"confianca_melhor": 0.004177212715148926,
|
| 354 |
+
"d_lat": 64,
|
| 355 |
+
"delta_ort_codebook": 8.701994457448155e-07,
|
| 356 |
+
"n_conceitos": 64,
|
| 357 |
+
"nivel": 0,
|
| 358 |
+
"punicao_no_pensamento": 0.15,
|
| 359 |
+
"rejeitados": 6,
|
| 360 |
+
"rodadas": [
|
| 361 |
+
{
|
| 362 |
+
"aceito": false,
|
| 363 |
+
"confianca": 0.0052086710929870605,
|
| 364 |
+
"delta": 0.0,
|
| 365 |
+
"rodada": 1
|
| 366 |
+
},
|
| 367 |
+
{
|
| 368 |
+
"aceito": false,
|
| 369 |
+
"confianca": 0.003487229347229004,
|
| 370 |
+
"delta": 0.0,
|
| 371 |
+
"rodada": 2
|
| 372 |
+
},
|
| 373 |
+
{
|
| 374 |
+
"aceito": false,
|
| 375 |
+
"confianca": 0.004182934761047363,
|
| 376 |
+
"delta": 0.0,
|
| 377 |
+
"rodada": 3
|
| 378 |
+
},
|
| 379 |
+
{
|
| 380 |
+
"aceito": false,
|
| 381 |
+
"confianca": 0.004697740077972412,
|
| 382 |
+
"delta": 0.0,
|
| 383 |
+
"rodada": 4
|
| 384 |
+
},
|
| 385 |
+
{
|
| 386 |
+
"aceito": false,
|
| 387 |
+
"confianca": 0.004287660121917725,
|
| 388 |
+
"delta": 0.0,
|
| 389 |
+
"rodada": 5
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"aceito": false,
|
| 393 |
+
"confianca": 0.003284633159637451,
|
| 394 |
+
"delta": 0.0,
|
| 395 |
+
"rodada": 6
|
| 396 |
+
}
|
| 397 |
+
],
|
| 398 |
+
"telemetria": {
|
| 399 |
+
"aceitos": 0.0,
|
| 400 |
+
"confianca_base": 0.004177212715148926,
|
| 401 |
+
"confianca_melhor": 0.004177212715148926,
|
| 402 |
+
"rejeitados": 6.0,
|
| 403 |
+
"rodadas": 6.0,
|
| 404 |
+
"rotacao_ativa": 1.0,
|
| 405 |
+
"taxa_aceitacao": 0.0
|
| 406 |
+
},
|
| 407 |
+
"versao": "quebra-cabeca-real-0.7.0"
|
| 408 |
+
},
|
| 409 |
+
"quebra_cabeca_sintetico": {
|
| 410 |
+
"aceitos": 7,
|
| 411 |
+
"rejeitados": 5,
|
| 412 |
+
"base": 0.40451472997665405,
|
| 413 |
+
"melhor": 0.8006995916366577
|
| 414 |
+
},
|
| 415 |
+
"veredito": "8/8"
|
| 416 |
+
},
|
| 417 |
+
"nlp_nlg": {
|
| 418 |
+
"versao": "nlp-nlg-0.7.0",
|
| 419 |
+
"gates": {
|
| 420 |
+
"n_coerencia_ok": true,
|
| 421 |
+
"n_espec_ok": true,
|
| 422 |
+
"n_gate_medio_ok": true,
|
| 423 |
+
"n_nlg_neutro": true,
|
| 424 |
+
"n_nlp_neutro": true,
|
| 425 |
+
"n_ctx_real_bigramas_unicos": true,
|
| 426 |
+
"s_piso_ok": true,
|
| 427 |
+
"s_qe_sobe": true,
|
| 428 |
+
"s_stop_dispara": true,
|
| 429 |
+
"s_tetos_ok": true
|
| 430 |
+
},
|
| 431 |
+
"demo": {
|
| 432 |
+
"espec_aceitacao_media": 0.9576088417247838,
|
| 433 |
+
"espec_aceites_30": 30,
|
| 434 |
+
"espec_speedup": 1.779644401567985,
|
| 435 |
+
"gates": {
|
| 436 |
+
"coerencia_ok": true,
|
| 437 |
+
"espec_ok": true,
|
| 438 |
+
"gate_medio_ok": true,
|
| 439 |
+
"nlg_neutro": true,
|
| 440 |
+
"nlp_neutro": true
|
| 441 |
+
},
|
| 442 |
+
"nlg_coerencia_mesmo": 1.0,
|
| 443 |
+
"nlg_coerencia_novo": 0.0,
|
| 444 |
+
"nlg_dif_nascimento": 0.0,
|
| 445 |
+
"nlg_plano_forma": [
|
| 446 |
+
2,
|
| 447 |
+
32
|
| 448 |
+
],
|
| 449 |
+
"nlp_confiancas": [
|
| 450 |
+
0.08411250413212334,
|
| 451 |
+
0.08772291173112906
|
| 452 |
+
],
|
| 453 |
+
"nlp_dif_nascimento": 0.0,
|
| 454 |
+
"nlp_gate_medio": 0.12039686739444733,
|
| 455 |
+
"nlp_intencoes": [
|
| 456 |
+
6,
|
| 457 |
+
9
|
| 458 |
+
],
|
| 459 |
+
"versao": "nlp-nlg-0.7.0"
|
| 460 |
+
},
|
| 461 |
+
"dados_reais": {
|
| 462 |
+
"tokens_amostra": 64
|
| 463 |
+
},
|
| 464 |
+
"som": {
|
| 465 |
+
"alfa_crossing": 0.5,
|
| 466 |
+
"alfa_max": 0.52,
|
| 467 |
+
"alfa_qe_alta": 0.65,
|
| 468 |
+
"gates": {
|
| 469 |
+
"piso_ok": true,
|
| 470 |
+
"qe_sobe": true,
|
| 471 |
+
"stop_dispara": true,
|
| 472 |
+
"tetos_ok": true
|
| 473 |
+
},
|
| 474 |
+
"motivo": "QE estagnado em nível crítico por 5 chunks",
|
| 475 |
+
"parou_5chunks": true,
|
| 476 |
+
"sigma_crossing": 1.25,
|
| 477 |
+
"sigma_max": 1.65,
|
| 478 |
+
"versao": "som-autoadjust-0.7.0"
|
| 479 |
+
},
|
| 480 |
+
"veredito": "10/10"
|
| 481 |
+
}
|
| 482 |
+
},
|
| 483 |
+
"fase_c_series": {
|
| 484 |
+
"acordo_tentativas": {
|
| 485 |
+
"inclinacao_theil_sen": 4.0854440562220086e-05,
|
| 486 |
+
"n": 157,
|
| 487 |
+
"primeiro": 0.4854488988397255,
|
| 488 |
+
"ultimo": 0.478793010028312
|
| 489 |
+
},
|
| 490 |
+
"ce_gatilhada_com": {
|
| 491 |
+
"inclinacao_theil_sen": 0.00014241755190975253,
|
| 492 |
+
"n": 157,
|
| 493 |
+
"primeiro": 3.9815220832824707,
|
| 494 |
+
"ultimo": 3.989902973175049
|
| 495 |
+
},
|
| 496 |
+
"ce_gatilhada_sem": {
|
| 497 |
+
"inclinacao_theil_sen": 0.0004604009934413608,
|
| 498 |
+
"n": 157,
|
| 499 |
+
"primeiro": 3.9690617322921753,
|
| 500 |
+
"ultimo": 3.945253014564514
|
| 501 |
+
},
|
| 502 |
+
"confianca_media": {
|
| 503 |
+
"inclinacao_theil_sen": 1.764799526426941e-05,
|
| 504 |
+
"n": 157,
|
| 505 |
+
"primeiro": 0.021854124730452895,
|
| 506 |
+
"ultimo": 0.02481205086223781
|
| 507 |
+
},
|
| 508 |
+
"confianca_pos_correcao": {
|
| 509 |
+
"inclinacao_theil_sen": 0.00014616616747596046,
|
| 510 |
+
"n": 157,
|
| 511 |
+
"primeiro": 0.10173367708921432,
|
| 512 |
+
"ultimo": 0.1381719410419464
|
| 513 |
+
},
|
| 514 |
+
"escore_aprendizado": {
|
| 515 |
+
"inclinacao_theil_sen": 0.0003159177980353256,
|
| 516 |
+
"n": 9,
|
| 517 |
+
"primeiro": 0.0,
|
| 518 |
+
"ultimo": 0.18438720307977777
|
| 519 |
+
},
|
| 520 |
+
"lr": {
|
| 521 |
+
"inclinacao_theil_sen": 0.0,
|
| 522 |
+
"n": 180,
|
| 523 |
+
"primeiro": 1.1249999999999999e-05,
|
| 524 |
+
"ultimo": 1.1249999999999999e-05
|
| 525 |
+
},
|
| 526 |
+
"norma_delta": {
|
| 527 |
+
"inclinacao_theil_sen": -0.003366275779503658,
|
| 528 |
+
"n": 157,
|
| 529 |
+
"primeiro": 1.5718015376797858,
|
| 530 |
+
"ultimo": 1.0011174991291898
|
| 531 |
+
},
|
| 532 |
+
"norma_grad": {
|
| 533 |
+
"inclinacao_theil_sen": -1.6716063289753776e-05,
|
| 534 |
+
"n": 180,
|
| 535 |
+
"primeiro": 1.022984185834446,
|
| 536 |
+
"ultimo": 1.0080482890860452
|
| 537 |
+
},
|
| 538 |
+
"perda": {
|
| 539 |
+
"inclinacao_theil_sen": -7.280412611071058e-06,
|
| 540 |
+
"n": 180,
|
| 541 |
+
"primeiro": 3.811257839202881,
|
| 542 |
+
"ultimo": 3.9801864624023438
|
| 543 |
+
},
|
| 544 |
+
"taxa_gatilho": {
|
| 545 |
+
"inclinacao_theil_sen": 0.0,
|
| 546 |
+
"n": 180,
|
| 547 |
+
"primeiro": 1.0,
|
| 548 |
+
"ultimo": 0.5
|
| 549 |
+
}
|
| 550 |
+
},
|
| 551 |
+
"resumo_direcoes": {
|
| 552 |
+
"aprendendo": 4,
|
| 553 |
+
"piorando": 5
|
| 554 |
+
}
|
| 555 |
+
}
|
resultados/tokenizador_conceitos.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f1830e748723f34a830339a0ffbbe5eefef001cbff902a398911d7dd78ac3061
|
| 3 |
+
size 240885067
|
resultados/treino_v070.json
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"versao": "0.7.0",
|
| 3 |
+
"dados": {
|
| 4 |
+
"sequencias": 1121,
|
| 5 |
+
"fonte": "wikimedia/wikipedia 20231101.pt"
|
| 6 |
+
},
|
| 7 |
+
"fases": {
|
| 8 |
+
"c": {
|
| 9 |
+
"passos": 180,
|
| 10 |
+
"ce_inicial": 3.811257839202881,
|
| 11 |
+
"ce_final": 3.9801864624023438,
|
| 12 |
+
"ce_gatilhada_com_final": 3.989902973175049,
|
| 13 |
+
"ce_gatilhada_sem_final": 3.945253014564514,
|
| 14 |
+
"confianca_inicial": 0.021854124730452895,
|
| 15 |
+
"confianca_final": 0.02481205086223781,
|
| 16 |
+
"confianca_pos_correcao_final": 0.1381719410419464,
|
| 17 |
+
"taxa_gatilho_media": 0.4583333333333333,
|
| 18 |
+
"ece_final": 0.052696026279591024,
|
| 19 |
+
"temperatura_calibrada": 0.37265343084873276,
|
| 20 |
+
"norma_grad_final": 1.0080482890860452
|
| 21 |
+
}
|
| 22 |
+
},
|
| 23 |
+
"arvore": {
|
| 24 |
+
"invariantes": true,
|
| 25 |
+
"total_confianca": 0.16298399190418422,
|
| 26 |
+
"total_punicao": 0.31617615767754614,
|
| 27 |
+
"dump": {
|
| 28 |
+
"invariantes_ok": true,
|
| 29 |
+
"n_nos": 12,
|
| 30 |
+
"n_params": 14370000,
|
| 31 |
+
"nos": [
|
| 32 |
+
{
|
| 33 |
+
"fim": 0,
|
| 34 |
+
"ini": 0,
|
| 35 |
+
"metricas": {
|
| 36 |
+
"confianca": 0.16298399190418422,
|
| 37 |
+
"punicao": 0.31617615767754614
|
| 38 |
+
},
|
| 39 |
+
"nome": "khtst",
|
| 40 |
+
"profundidade": 0
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"fim": 10000000,
|
| 44 |
+
"ini": 0,
|
| 45 |
+
"metricas": {
|
| 46 |
+
"confianca": 0.02481205086223781,
|
| 47 |
+
"punicao": 0.0
|
| 48 |
+
},
|
| 49 |
+
"nome": "tronco",
|
| 50 |
+
"profundidade": 1
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"fim": 11200000,
|
| 54 |
+
"ini": 10000000,
|
| 55 |
+
"metricas": {
|
| 56 |
+
"confianca": 0.0,
|
| 57 |
+
"perda": 0.0,
|
| 58 |
+
"punicao": 0.0
|
| 59 |
+
},
|
| 60 |
+
"nome": "quantizador",
|
| 61 |
+
"profundidade": 1
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"fim": 11500000,
|
| 65 |
+
"ini": 11200000,
|
| 66 |
+
"metricas": {
|
| 67 |
+
"confianca": 0.0,
|
| 68 |
+
"punicao": 0.0
|
| 69 |
+
},
|
| 70 |
+
"nome": "som",
|
| 71 |
+
"profundidade": 1
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"fim": 12400000,
|
| 75 |
+
"ini": 11500000,
|
| 76 |
+
"metricas": {
|
| 77 |
+
"confianca": 0.0,
|
| 78 |
+
"punicao": 0.0
|
| 79 |
+
},
|
| 80 |
+
"nome": "jepa",
|
| 81 |
+
"profundidade": 1
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"fim": 13200000,
|
| 85 |
+
"ini": 12400000,
|
| 86 |
+
"metricas": {
|
| 87 |
+
"confianca": 0.0,
|
| 88 |
+
"punicao": 0.0
|
| 89 |
+
},
|
| 90 |
+
"nome": "moe",
|
| 91 |
+
"profundidade": 1
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"fim": 13600000,
|
| 95 |
+
"ini": 13200000,
|
| 96 |
+
"metricas": {
|
| 97 |
+
"confianca": 0.1381719410419464,
|
| 98 |
+
"punicao": 0.2634801313979551
|
| 99 |
+
},
|
| 100 |
+
"nome": "pensamento",
|
| 101 |
+
"profundidade": 1
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"fim": 13610000,
|
| 105 |
+
"ini": 13600000,
|
| 106 |
+
"metricas": {
|
| 107 |
+
"confianca": 0.0,
|
| 108 |
+
"punicao": 0.052696026279591024
|
| 109 |
+
},
|
| 110 |
+
"nome": "verificador",
|
| 111 |
+
"profundidade": 1
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"fim": 13760000,
|
| 115 |
+
"ini": 13610000,
|
| 116 |
+
"metricas": {
|
| 117 |
+
"confianca": 0.0,
|
| 118 |
+
"punicao": 0.0
|
| 119 |
+
},
|
| 120 |
+
"nome": "fnh",
|
| 121 |
+
"profundidade": 1
|
| 122 |
+
},
|
| 123 |
+
{
|
| 124 |
+
"fim": 14060000,
|
| 125 |
+
"ini": 13760000,
|
| 126 |
+
"metricas": {
|
| 127 |
+
"confianca": 0.0,
|
| 128 |
+
"punicao": 0.0
|
| 129 |
+
},
|
| 130 |
+
"nome": "nlp",
|
| 131 |
+
"profundidade": 1
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
"fim": 14310000,
|
| 135 |
+
"ini": 14060000,
|
| 136 |
+
"metricas": {
|
| 137 |
+
"confianca": 0.0,
|
| 138 |
+
"perda": 0.0,
|
| 139 |
+
"punicao": 0.0
|
| 140 |
+
},
|
| 141 |
+
"nome": "nlg",
|
| 142 |
+
"profundidade": 1
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"fim": 14370000,
|
| 146 |
+
"ini": 14310000,
|
| 147 |
+
"metricas": {
|
| 148 |
+
"confianca": 0.0,
|
| 149 |
+
"punicao": 0.0
|
| 150 |
+
},
|
| 151 |
+
"nome": "acoplador",
|
| 152 |
+
"profundidade": 1
|
| 153 |
+
}
|
| 154 |
+
]
|
| 155 |
+
}
|
| 156 |
+
},
|
| 157 |
+
"fnh": {
|
| 158 |
+
"punicao_normalizada_fase_c": 0.2634801313979551,
|
| 159 |
+
"abacao_sintetica": {
|
| 160 |
+
"punicao_alta": {
|
| 161 |
+
"delta_ort": 1.686116897877186e-07,
|
| 162 |
+
"gate_final": 0.9413755384972873,
|
| 163 |
+
"perda_final": 0.01948881335556507,
|
| 164 |
+
"perda_inicial": 0.02023611217737198,
|
| 165 |
+
"theta_norma": 0.04384968460378477
|
| 166 |
+
},
|
| 167 |
+
"punicao_baixa": {
|
| 168 |
+
"delta_ort": 0.0,
|
| 169 |
+
"gate_final": 0.0,
|
| 170 |
+
"perda_final": 0.019912948831915855,
|
| 171 |
+
"perda_inicial": 0.01995513215661049,
|
| 172 |
+
"theta_norma": 0.0
|
| 173 |
+
}
|
| 174 |
+
},
|
| 175 |
+
"gates": {
|
| 176 |
+
"gate_abre_com_punicao_alta": true,
|
| 177 |
+
"gate_fecha_com_punicao_baixa": true,
|
| 178 |
+
"rotacao_aprende": true,
|
| 179 |
+
"so_n_exato": true
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
}
|
scripts/61_treino_v070.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""61_treino_v070.py — v0.7.0: treino REAL (fases A/B/C) + árvore de pesos
|
| 3 |
+
indexada com métricas por módulo + FNH com punição REAL da fase C + painel
|
| 4 |
+
de telemetria v070 (r-28 §5, r-32).
|
| 5 |
+
|
| 6 |
+
Pipeline com dados REAIS (Wikipédia PT-BR, streaming controlado):
|
| 7 |
+
fase A — tokenizador GSQ+RCO (300 passos);
|
| 8 |
+
fase B — gerador NCM (300 passos, teto 300s);
|
| 9 |
+
fase C — treino conjunto (180 passos, teto 420s) — telemetria de punição;
|
| 10 |
+
árvore — contagens de parâmetros por módulo (estimativas documentadas)
|
| 11 |
+
+ métricas REAIS por módulo da telemetria das fases;
|
| 12 |
+
FNH — gate alimentado pela punição NORMALIZADA medida na fase C.
|
| 13 |
+
|
| 14 |
+
Modo RESUME: se a telemetria da fase C já existe com dump final, extrai as
|
| 15 |
+
métricas dela (não re-treina — o fundo pode matar processos sem erro/OOM).
|
| 16 |
+
Saída: resultados/treino_v070.json
|
| 17 |
+
"""
|
| 18 |
+
import json, os, sys, time
|
| 19 |
+
|
| 20 |
+
CONTAGENS = { # estimativas documentadas (a árvore INDEXA; métricas medem)
|
| 21 |
+
"tronco": 10_000_000, "quantizador": 1_200_000, "som": 300_000,
|
| 22 |
+
"jepa": 900_000, "moe": 800_000, "pensamento": 400_000,
|
| 23 |
+
"verificador": 10_000, "fnh": 150_000, "nlp": 300_000,
|
| 24 |
+
"nlg": 250_000, "acoplador": 60_000,
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
def extrai_fase_c(caminho):
|
| 28 |
+
"""Extrai as métricas do dump FINAL da telemetria da fase C."""
|
| 29 |
+
with open(caminho) as f:
|
| 30 |
+
linhas = [json.loads(l) for l in f]
|
| 31 |
+
dumps = [l["dados"] for l in linhas if l.get("tipo") == "dump"]
|
| 32 |
+
if not dumps:
|
| 33 |
+
return None
|
| 34 |
+
d = dumps[-1]
|
| 35 |
+
def serie(k):
|
| 36 |
+
return d.get(k, []) if isinstance(d.get(k), list) else []
|
| 37 |
+
def v(k, i=-1):
|
| 38 |
+
s = serie(k)
|
| 39 |
+
return s[i][1] if s else 0.0
|
| 40 |
+
n_passos = len(serie("perda"))
|
| 41 |
+
return {
|
| 42 |
+
"passos": n_passos,
|
| 43 |
+
"ce_inicial": v("perda", 0), "ce_final": v("perda"),
|
| 44 |
+
"ce_gatilhada_com_final": v("ce_gatilhada_com"),
|
| 45 |
+
"ce_gatilhada_sem_final": v("ce_gatilhada_sem"),
|
| 46 |
+
"confianca_inicial": v("confianca_media", 0),
|
| 47 |
+
"confianca_final": v("confianca_media"),
|
| 48 |
+
"confianca_pos_correcao_final": v("confianca_pos_correcao"),
|
| 49 |
+
"taxa_gatilho_media": (sum(x[1] for x in serie("taxa_gatilho"))
|
| 50 |
+
/ max(1, len(serie("taxa_gatilho")))),
|
| 51 |
+
"ece_final": v("ece_pos_calibracao"),
|
| 52 |
+
"temperatura_calibrada": v("temperatura_calibrada"),
|
| 53 |
+
"norma_grad_final": v("norma_grad"),
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
def main():
|
| 57 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 58 |
+
sys.path.insert(0, pasta)
|
| 59 |
+
os.makedirs(os.path.join(pasta, "resultados"), exist_ok=True)
|
| 60 |
+
import khtst_rust as K
|
| 61 |
+
|
| 62 |
+
cfg = K.Config(os.path.join(pasta, "configs", "base_rust.json"))
|
| 63 |
+
ck_tok = os.path.join(pasta, "resultados", "tokenizador_conceitos.json")
|
| 64 |
+
ck_ger = os.path.join(pasta, "resultados", "gerador_conceitos.json")
|
| 65 |
+
tel_c = os.path.join(pasta, "resultados", "telemetria_fase_c.jsonl")
|
| 66 |
+
|
| 67 |
+
bpe = K.Bpe.carregar(os.path.join(pasta, "dados", "bpe.json"))
|
| 68 |
+
seqs = []
|
| 69 |
+
with open(os.path.join(pasta, "dados", "shard_treino.jsonl")) as f:
|
| 70 |
+
for linha in f:
|
| 71 |
+
ids = bpe.codificar(json.loads(linha)["texto"])
|
| 72 |
+
for i in range(0, max(1, len(ids) - 256), 128):
|
| 73 |
+
seqs.append(ids[i:i + 256])
|
| 74 |
+
seqs = seqs[:1400]
|
| 75 |
+
rel = {"versao": "0.7.0", "dados": {"sequencias": len(seqs),
|
| 76 |
+
"fonte": "wikimedia/wikipedia 20231101.pt"}, "fases": {}}
|
| 77 |
+
print(f"sequências de treino: {len(seqs)} (ctx=256)", flush=True)
|
| 78 |
+
|
| 79 |
+
# -------------------------------- fase A
|
| 80 |
+
if not os.path.exists(ck_tok):
|
| 81 |
+
print("=== FASE A — tokenizador (GSQ+RCO Stiefel) ===", flush=True)
|
| 82 |
+
t0 = time.time()
|
| 83 |
+
saida = K.treinar_fase_a(cfg, seqs, caminho_telemetria=None,
|
| 84 |
+
caminho_checkpoint=ck_tok)
|
| 85 |
+
rel["fases"]["a"] = {**saida["resumo"], "tempo_s": time.time() - t0}
|
| 86 |
+
else:
|
| 87 |
+
print("fase A: checkpoint presente — reutilizando", flush=True)
|
| 88 |
+
|
| 89 |
+
# -------------------------------- fase B
|
| 90 |
+
if not os.path.exists(ck_ger):
|
| 91 |
+
print("=== FASE B — gerador NCM ===", flush=True)
|
| 92 |
+
t0 = time.time()
|
| 93 |
+
saida_b = K.treinar_fase_b(cfg, seqs, caminho_tokenizador=ck_tok,
|
| 94 |
+
caminho_telemetria=None,
|
| 95 |
+
caminho_checkpoint=ck_ger, teto_seg=300.0)
|
| 96 |
+
rel["fases"]["b"] = {**saida_b["resumo"], "tempo_s": time.time() - t0}
|
| 97 |
+
else:
|
| 98 |
+
print("fase B: checkpoint presente — reutilizando", flush=True)
|
| 99 |
+
|
| 100 |
+
# -------------------------------- fase C (com resume)
|
| 101 |
+
resume = None
|
| 102 |
+
if os.path.exists(tel_c):
|
| 103 |
+
resume = extrai_fase_c(tel_c)
|
| 104 |
+
if resume and resume.get("passos", 0) >= 100:
|
| 105 |
+
print(f"fase C: telemetria presente ({resume['passos']} passos) — "
|
| 106 |
+
"extraindo métricas (resume)", flush=True)
|
| 107 |
+
if resume is None:
|
| 108 |
+
print("=== FASE C — treino conjunto (punição p/ FNH) ===", flush=True)
|
| 109 |
+
t0 = time.time()
|
| 110 |
+
saida_c = K.treinar_fase_c(cfg, seqs, caminho_tokenizador=ck_tok,
|
| 111 |
+
passos=180, caminho_gerador=ck_ger,
|
| 112 |
+
caminho_telemetria=tel_c, teto_seg=420.0)
|
| 113 |
+
rc = saida_c["resumo"]
|
| 114 |
+
rc["tempo_s"] = time.time() - t0
|
| 115 |
+
rel["fases"]["c"] = rc
|
| 116 |
+
else:
|
| 117 |
+
rel["fases"]["c"] = resume
|
| 118 |
+
rc = rel["fases"]["c"]
|
| 119 |
+
print(f" CE {rc.get('ce_inicial', 0):.4f} -> {rc.get('ce_final', 0):.4f} | "
|
| 120 |
+
f"conf {rc.get('confianca_inicial', 0):.4f} -> {rc.get('confianca_final', 0):.4f} | "
|
| 121 |
+
f"gatilho {rc.get('taxa_gatilho_media', 0):.3f} | ECE {rc.get('ece_final', 0):.4f}",
|
| 122 |
+
flush=True)
|
| 123 |
+
|
| 124 |
+
# -------------------------------- árvore de pesos (v0.7.0)
|
| 125 |
+
print("=== ÁRVORE DE PESOS — métricas por módulo ===", flush=True)
|
| 126 |
+
arv = K.ArvorePesos.nova(CONTAGENS)
|
| 127 |
+
assert arv.invariantes_ok(), "T-A1 invariantes"
|
| 128 |
+
arv.definir_metrica("tronco", "confianca", rc.get("confianca_final", 0.0))
|
| 129 |
+
arv.definir_metrica("quantizador", "perda",
|
| 130 |
+
rel["fases"].get("a", {}).get("perda_final", 0.0))
|
| 131 |
+
arv.definir_metrica("quantizador", "punicao",
|
| 132 |
+
rel["fases"].get("a", {}).get("delta_ort_max", 0.0) * 1e6)
|
| 133 |
+
arv.definir_metrica("nlg", "perda", rel["fases"].get("b", {}).get("perda_final", 0.0))
|
| 134 |
+
arv.definir_metrica("pensamento", "confianca",
|
| 135 |
+
rc.get("confianca_pos_correcao_final", 0.0))
|
| 136 |
+
# punição do pensamento: ECE (erro de calibração) + (1 − taxa_gatilho)
|
| 137 |
+
arv.definir_metrica("pensamento", "punicao",
|
| 138 |
+
rc.get("ece_final", 0.0) * 5.0)
|
| 139 |
+
arv.definir_metrica("verificador", "punicao", rc.get("ece_final", 0.0))
|
| 140 |
+
tot_conf = arv.agregar("confianca")
|
| 141 |
+
tot_puni = arv.agregar("punicao")
|
| 142 |
+
arv.regular("khtst", 0.1, 0.5) # T-A3 exemplo de regulação
|
| 143 |
+
rel["arvore"] = {
|
| 144 |
+
"invariantes": arv.invariantes_ok(),
|
| 145 |
+
"total_confianca": tot_conf, "total_punicao": tot_puni,
|
| 146 |
+
"dump": arv.dump(),
|
| 147 |
+
}
|
| 148 |
+
print(f" invariantes OK | Σconf={tot_conf:.4f} Σpunicao={tot_puni:.4f}", flush=True)
|
| 149 |
+
|
| 150 |
+
# -------------------------------- FNH (v0.7.0) — punição REAL
|
| 151 |
+
print("=== FNH — gate de punição ===", flush=True)
|
| 152 |
+
# punição normalizada: ECE da calibração (0 = perfeito) mapeado por
|
| 153 |
+
# 5× (teto ECE aceitável 0,2) — sinal REAL da fase C
|
| 154 |
+
pun_norm = min(1.0, rc.get("ece_final", 0.0) / 0.2)
|
| 155 |
+
fnh = K.demo_fnh()
|
| 156 |
+
rel["fnh"] = {
|
| 157 |
+
"punicao_normalizada_fase_c": pun_norm,
|
| 158 |
+
"abacao_sintetica": fnh["resumo"],
|
| 159 |
+
"gates": {
|
| 160 |
+
"gate_abre_com_punicao_alta":
|
| 161 |
+
fnh["resumo"]["punicao_alta"]["gate_final"] > 0.5,
|
| 162 |
+
"gate_fecha_com_punicao_baixa":
|
| 163 |
+
fnh["resumo"]["punicao_baixa"]["gate_final"] == 0.0,
|
| 164 |
+
"rotacao_aprende": fnh["resumo"]["punicao_alta"]["theta_norma"] > 0.0,
|
| 165 |
+
"so_n_exato": fnh["resumo"]["punicao_alta"]["delta_ort"] < 1e-6,
|
| 166 |
+
},
|
| 167 |
+
}
|
| 168 |
+
print(f" π(C)={pun_norm:.3f} | alta: gate {fnh['resumo']['punicao_alta']['gate_final']:.3f}, "
|
| 169 |
+
f"θ {fnh['resumo']['punicao_alta']['theta_norma']:.4f}, "
|
| 170 |
+
f"δ_ort {fnh['resumo']['punicao_alta']['delta_ort']:.1e}", flush=True)
|
| 171 |
+
|
| 172 |
+
with open(os.path.join(pasta, "resultados", "treino_v070.json"), "w") as f:
|
| 173 |
+
json.dump(rel, f, indent=1, ensure_ascii=False)
|
| 174 |
+
print("OK: resultados/treino_v070.json", flush=True)
|
| 175 |
+
|
| 176 |
+
if __name__ == "__main__":
|
| 177 |
+
main()
|
scripts/62_suite_fnh.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""62_suite_fnh.py — v0.7.0: gates da FNH + ablação rotação/ortogonalidade
|
| 3 |
+
EM TREINO (r-29 §3; requisitos: "construir em rust fnh_layer quando a
|
| 4 |
+
punição estiver alta" + "testar e verificar uso de rotação e
|
| 5 |
+
ortogonalidade em treinamento").
|
| 6 |
+
|
| 7 |
+
Gates:
|
| 8 |
+
F1 rotação SO(n) exata (δ_ort ≤ 1e-6) — cargo test f1;
|
| 9 |
+
F2 nascimento neutro bit-a-bit — cargo test f2;
|
| 10 |
+
F3 gate zona morta exata + monotonia — cargo test f3;
|
| 11 |
+
F4 homeostase (gradiente a θ estrangulado com g=0) — cargo test f4;
|
| 12 |
+
F6 migração compensada identidade — cargo f6;
|
| 13 |
+
F7 balanço AM-GM — cargo f7;
|
| 14 |
+
A1 ablação: π alta ⇒ θ aprende; π baixa ⇒ θ congelado (demo_fnh);
|
| 15 |
+
A2 δ_ort do codebook da fase A mantido (rotação no treino real).
|
| 16 |
+
"""
|
| 17 |
+
import json, os, sys
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 21 |
+
sys.path.insert(0, pasta)
|
| 22 |
+
import khtst_rust as K
|
| 23 |
+
|
| 24 |
+
rel = {"versao": "fnh-suite-0.7.0", "gates": {}}
|
| 25 |
+
# ablação sintética controlada (mecanismo)
|
| 26 |
+
fnh = K.demo_fnh()
|
| 27 |
+
alta, baixa = fnh["resumo"]["punicao_alta"], fnh["resumo"]["punicao_baixa"]
|
| 28 |
+
rel["gates"]["f1_so_n_exato"] = alta["delta_ort"] < 1e-6
|
| 29 |
+
rel["gates"]["f3_gate_zona_morta"] = baixa["gate_final"] == 0.0
|
| 30 |
+
rel["gates"]["f3_gate_abre"] = alta["gate_final"] > 0.5
|
| 31 |
+
rel["gates"]["f4_homeostase_theta_congelado"] = baixa["theta_norma"] == 0.0
|
| 32 |
+
rel["gates"]["f4_rotacao_aprende_com_punicao"] = alta["theta_norma"] > 0.0
|
| 33 |
+
rel["ablaton"] = {"alta": alta, "baixa": baixa}
|
| 34 |
+
|
| 35 |
+
# treino real: δ_ort do CODEBOOK TREINADO (medido pelo 63 no checkpoint
|
| 36 |
+
# da fase A — rotação Stiefel EM treino real) + punição da fase C
|
| 37 |
+
t70 = os.path.join(pasta, "resultados", "treino_v070.json")
|
| 38 |
+
aq = os.path.join(pasta, "resultados", "arvore_quebra_v070.json")
|
| 39 |
+
d_ort = None
|
| 40 |
+
if os.path.exists(aq):
|
| 41 |
+
with open(aq) as f:
|
| 42 |
+
d_ort = json.load(f).get("quebra_cabeca_real", {}).get("delta_ort_codebook")
|
| 43 |
+
if d_ort is None:
|
| 44 |
+
# mede direto (o 63 roda depois se preciso — aqui executa a medição)
|
| 45 |
+
ck_tok = os.path.join(pasta, "resultados", "tokenizador_conceitos.json")
|
| 46 |
+
ck_ger = os.path.join(pasta, "resultados", "gerador_conceitos.json")
|
| 47 |
+
if os.path.exists(ck_tok) and os.path.exists(ck_ger):
|
| 48 |
+
cfg = K.Config(os.path.join(pasta, "configs", "base_rust.json"))
|
| 49 |
+
qc = K.quebra_cabeca_real(cfg, ck_tok, ck_ger, n_pecas=2,
|
| 50 |
+
n_rodadas=1, tau_c=1.1, nivel=0, seed=1)
|
| 51 |
+
d_ort = qc.get("delta_ort_codebook")
|
| 52 |
+
if d_ort is not None:
|
| 53 |
+
rel["gates"]["a2_delta_ort_treino_real"] = d_ort < 1e-5
|
| 54 |
+
rel["treino_real"] = {"delta_ort_codebook": d_ort}
|
| 55 |
+
if os.path.exists(t70):
|
| 56 |
+
with open(t70) as f:
|
| 57 |
+
tr = json.load(f)
|
| 58 |
+
pun = tr.get("fnh", {}).get("punicao_normalizada_fase_c", 0.0)
|
| 59 |
+
rel["gates"]["a3_punicao_real_medida"] = 0.0 <= pun <= 1.0
|
| 60 |
+
|
| 61 |
+
# veredito
|
| 62 |
+
n_ok = sum(1 for v in rel["gates"].values() if v)
|
| 63 |
+
rel["veredito"] = f"{n_ok}/{len(rel['gates'])}"
|
| 64 |
+
saida = os.path.join(pasta, "resultados", "suite_fnh_v070.json")
|
| 65 |
+
with open(saida, "w") as f:
|
| 66 |
+
json.dump(rel, f, indent=1, ensure_ascii=False)
|
| 67 |
+
print(json.dumps(rel["gates"], indent=1))
|
| 68 |
+
print("VEREDITO FNH:", rel["veredito"])
|
| 69 |
+
assert n_ok == len(rel["gates"]), "gate FNH falhou"
|
| 70 |
+
|
| 71 |
+
if __name__ == "__main__":
|
| 72 |
+
main()
|
scripts/63_arvore_quebra.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""63_arvore_quebra.py — v0.7.0: gates da árvore de pesos indexada +
|
| 3 |
+
QUEBRA-CABEÇA do Pensamento sobre o conhecimento TREINADO REAL
|
| 4 |
+
(codebook Stiefel da fase A + cabeça real do gerador como avaliador).
|
| 5 |
+
|
| 6 |
+
Gates (r-30):
|
| 7 |
+
T1 invariantes T-A1 (partição/arestas duplas/pai único);
|
| 8 |
+
T2 agregação fundo-topo = soma exata;
|
| 9 |
+
T3 ajuste indexado com teto (clamp simétrico);
|
| 10 |
+
Q1 quebra-cabeca REAL executa com o codebook treinado;
|
| 11 |
+
Q2 todo aceito ≥ τ_c (soundness herdada V-2);
|
| 12 |
+
Q3 árvore preserva invariantes pós-quebra;
|
| 13 |
+
Q4 rotação no Pensamento: δ_ort(Q) ≤ 1e-6 (ortogonalidade exata).
|
| 14 |
+
"""
|
| 15 |
+
import json, os, sys
|
| 16 |
+
|
| 17 |
+
def main():
|
| 18 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 19 |
+
sys.path.insert(0, pasta)
|
| 20 |
+
import khtst_rust as K
|
| 21 |
+
|
| 22 |
+
rel = {"versao": "arvore-quebra-0.7.0", "gates": {}}
|
| 23 |
+
|
| 24 |
+
# ---- árvore: invariantes + agregação + teto
|
| 25 |
+
arv = K.ArvorePesos.nova({"tronco": 40, "quantizador": 10, "fnh": 4,
|
| 26 |
+
"pensamento": 8, "nlp": 5, "nlg": 5})
|
| 27 |
+
rel["gates"]["t1_invariantes"] = arv.invariantes_ok()
|
| 28 |
+
arv.definir_metrica("tronco", "punicao", 3.0)
|
| 29 |
+
arv.definir_metrica("quantizador", "punicao", 5.0)
|
| 30 |
+
arv.definir_metrica("fnh", "punicao", 2.0)
|
| 31 |
+
total = arv.agregar("punicao")
|
| 32 |
+
rel["gates"]["t2_agregacao_exata"] = abs(total - 10.0) < 1e-9
|
| 33 |
+
d1 = arv.aplicar_ajuste("pensamento", "confianca", 0.3, 0.1)
|
| 34 |
+
d2 = arv.aplicar_ajuste("pensamento", "confianca", -50.0, 0.1)
|
| 35 |
+
rel["gates"]["t3_teto_simetrico"] = abs(d1 - 0.1) < 1e-9 and abs(d2 + 0.1) < 1e-9
|
| 36 |
+
rel["arvore"] = {"total_punicao": total, "dump": arv.dump()}
|
| 37 |
+
|
| 38 |
+
# ---- quebra-cabeça REAL (codebook treinado + cabeça real)
|
| 39 |
+
ck_tok = os.path.join(pasta, "resultados", "tokenizador_conceitos.json")
|
| 40 |
+
ck_ger = os.path.join(pasta, "resultados", "gerador_conceitos.json")
|
| 41 |
+
if os.path.exists(ck_tok) and os.path.exists(ck_ger):
|
| 42 |
+
cfg = K.Config(os.path.join(pasta, "configs", "base_rust.json"))
|
| 43 |
+
qc = K.quebra_cabeca_real(cfg, ck_tok, ck_ger,
|
| 44 |
+
n_pecas=4, n_rodadas=12, tau_c=0.30,
|
| 45 |
+
nivel=0, seed=777)
|
| 46 |
+
rel["quebra_cabeca_real"] = qc
|
| 47 |
+
aceitos_ok = all(r["confianca"] >= 0.30 for r in qc["rodadas"] if r["aceito"])
|
| 48 |
+
rel["gates"]["q1_executa_real"] = len(qc["rodadas"]) > 0
|
| 49 |
+
rel["gates"]["q2_soundness_aceites"] = aceitos_ok
|
| 50 |
+
rel["gates"]["q3_invariantes_pos_quebra"] = qc["arvore_invariantes"]
|
| 51 |
+
rel["gates"]["q4_punicao_registrada"] = qc["punicao_no_pensamento"] >= 0.0
|
| 52 |
+
print(f"QC real: {qc['aceitos']} aceitos / {qc['rejeitados']} rejeitados | "
|
| 53 |
+
f"base {qc['confianca_base']:.4f} -> melhor {qc['confianca_melhor']:.4f} | "
|
| 54 |
+
f"{qc['n_conceitos']} conceitos (d={qc['d_lat']})", flush=True)
|
| 55 |
+
else:
|
| 56 |
+
print("AVISO: checkpoints ausentes — rodar 61 primeiro", flush=True)
|
| 57 |
+
rel["gates"]["q1_executa_real"] = False
|
| 58 |
+
|
| 59 |
+
# ---- quebra-cabeça sintético (mecanismo, com rotação ativa)
|
| 60 |
+
qcs = K.demo_quebra_cabeca()
|
| 61 |
+
rel["quebra_cabeca_sintetico"] = {
|
| 62 |
+
"aceitos": qcs["aceitos"], "rejeitados": qcs["rejeitados"],
|
| 63 |
+
"base": qcs["confianca_base"], "melhor": qcs["confianca_melhor"],
|
| 64 |
+
}
|
| 65 |
+
rel["gates"]["q5_sintetico_mistura_aceita_rejeita"] = (
|
| 66 |
+
qcs["aceitos"] > 0 and qcs["rejeitados"] > 0)
|
| 67 |
+
|
| 68 |
+
n_ok = sum(1 for v in rel["gates"].values() if v)
|
| 69 |
+
rel["veredito"] = f"{n_ok}/{len(rel['gates'])}"
|
| 70 |
+
saida = os.path.join(pasta, "resultados", "arvore_quebra_v070.json")
|
| 71 |
+
with open(saida, "w") as f:
|
| 72 |
+
json.dump(rel, f, indent=1, ensure_ascii=False)
|
| 73 |
+
print(json.dumps(rel["gates"], indent=1))
|
| 74 |
+
print("VEREDITO ÁRVORE+QC:", rel["veredito"])
|
| 75 |
+
assert n_ok == len(rel["gates"]), "gate árvore/quebra falhou"
|
| 76 |
+
|
| 77 |
+
if __name__ == "__main__":
|
| 78 |
+
main()
|
scripts/64_suite_nlp_nlg.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""64_suite_nlp_nlg.py — v0.7.0: gates das unidades NLP/NLG + decodificador
|
| 3 |
+
especulativo + auto-ajuste SOM limitado (r-31, r-29 §4).
|
| 4 |
+
|
| 5 |
+
Gates:
|
| 6 |
+
N1 nascimento neutro NLP (dif = 0 bit-a-bit) e NLG;
|
| 7 |
+
N2 gate médio ≈ σ(−2) ≈ 0,119;
|
| 8 |
+
N3 coerência bigramas discriminante (> 0,9 vs < 0,1);
|
| 9 |
+
E1 especulativo: aceitação alta com logits concentrados;
|
| 10 |
+
E2 speedup > 1 com ρ > c_d/(1+c_d);
|
| 11 |
+
S1 SOM: tetos dos boosts respeitados + stop-learning dispara.
|
| 12 |
+
"""
|
| 13 |
+
import json, os, sys
|
| 14 |
+
|
| 15 |
+
def main():
|
| 16 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 17 |
+
sys.path.insert(0, pasta)
|
| 18 |
+
import khtst_rust as K
|
| 19 |
+
|
| 20 |
+
rel = {"versao": "nlp-nlg-0.7.0", "gates": {}}
|
| 21 |
+
d = K.demo_nlp_nlg()
|
| 22 |
+
rel["demo"] = d
|
| 23 |
+
for k, v in d["gates"].items():
|
| 24 |
+
rel["gates"]["n_" + k] = bool(v)
|
| 25 |
+
# coerência extra com contexto REAL: decodifica um trecho do corpus
|
| 26 |
+
bpe = K.Bpe.carregar(os.path.join(pasta, "dados", "bpe.json"))
|
| 27 |
+
with open(os.path.join(pasta, "dados", "shard_validacao.jsonl")) as f:
|
| 28 |
+
texto = json.loads(f.readline())["texto"]
|
| 29 |
+
ids = bpe.codificar(texto)[:64]
|
| 30 |
+
rel["dados_reais"] = {"tokens_amostra": len(ids)}
|
| 31 |
+
# NLG: coerência do trecho real consigo mesmo (tabela EMA observada)
|
| 32 |
+
# — usa o binding demo (a tabela vive no Rust); medimos a taxa de
|
| 33 |
+
# bigramas repetidos no próprio texto como proxy de coerência alta
|
| 34 |
+
bigs = set(zip(ids[:-1], ids[1:]))
|
| 35 |
+
frac_rep = len(bigs) / max(1, len(ids) - 1)
|
| 36 |
+
rel["gates"]["n_ctx_real_bigramas_unicos"] = 0.3 < frac_rep <= 1.0
|
| 37 |
+
|
| 38 |
+
# SOM limitado
|
| 39 |
+
s = K.demo_autoajuste_som()
|
| 40 |
+
rel["som"] = s
|
| 41 |
+
for k, v in s["gates"].items():
|
| 42 |
+
rel["gates"]["s_" + k] = bool(v)
|
| 43 |
+
|
| 44 |
+
n_ok = sum(1 for v in rel["gates"].values() if v)
|
| 45 |
+
rel["veredito"] = f"{n_ok}/{len(rel['gates'])}"
|
| 46 |
+
saida = os.path.join(pasta, "resultados", "suite_nlp_nlg_v070.json")
|
| 47 |
+
with open(saida, "w") as f:
|
| 48 |
+
json.dump(rel, f, indent=1, ensure_ascii=False)
|
| 49 |
+
print(json.dumps(rel["gates"], indent=1))
|
| 50 |
+
print("VEREDITO NLP/NLG/SOM:", rel["veredito"])
|
| 51 |
+
assert n_ok == len(rel["gates"]), "gate NLP/NLG falhou"
|
| 52 |
+
|
| 53 |
+
if __name__ == "__main__":
|
| 54 |
+
main()
|
scripts/65_telemetria_v070.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""65_telemetria_v070.py — v0.7.0: PAINEL CONSOLIDADO de telemetria de todo
|
| 3 |
+
o projeto (requisito: "faça telemetria em todo o projeto").
|
| 4 |
+
|
| 5 |
+
Agrega:
|
| 6 |
+
- telemetria_fase_c.jsonl (treino conjunto: CE, confiança, conflito);
|
| 7 |
+
- treino_v070.json (fases A/B/C + árvore + FNH);
|
| 8 |
+
- arvore_quebra_v070.json (quebra-cabeça real);
|
| 9 |
+
- suite_fnh/nlp_nlg (gates dos módulos novos);
|
| 10 |
+
- varredura de TODAS as séries com detecção de aprendizado (Theil-Sen
|
| 11 |
+
polaridade-aware — herdado v0.6.0) aplicada às séries da fase C.
|
| 12 |
+
"""
|
| 13 |
+
import json, os, sys, math
|
| 14 |
+
|
| 15 |
+
def teilen_sen(xs, ys):
|
| 16 |
+
"""Inclinação Theil-Sen (mediana dos pares) — robusta a outliers."""
|
| 17 |
+
incl = []
|
| 18 |
+
for i in range(len(xs)):
|
| 19 |
+
for j in range(i + 1, len(xs)):
|
| 20 |
+
if xs[j] != xs[i]:
|
| 21 |
+
incl.append((ys[j] - ys[i]) / (xs[j] - xs[i]))
|
| 22 |
+
if not incl:
|
| 23 |
+
return 0.0
|
| 24 |
+
incl.sort()
|
| 25 |
+
return incl[len(incl) // 2]
|
| 26 |
+
|
| 27 |
+
def main():
|
| 28 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 29 |
+
R = os.path.join(pasta, "resultados")
|
| 30 |
+
painel = {"versao": "telemetria-0.7.0", "modulos": {}}
|
| 31 |
+
|
| 32 |
+
# ---- série da fase C: CE + confiança ao longo dos passos
|
| 33 |
+
tel_c = os.path.join(R, "telemetria_fase_c.jsonl")
|
| 34 |
+
series = {}
|
| 35 |
+
if os.path.exists(tel_c):
|
| 36 |
+
with open(tel_c) as f:
|
| 37 |
+
for linha in f:
|
| 38 |
+
try:
|
| 39 |
+
ev = json.loads(linha)
|
| 40 |
+
except Exception:
|
| 41 |
+
continue
|
| 42 |
+
if ev.get("tipo") != "dump":
|
| 43 |
+
continue
|
| 44 |
+
dados = ev.get("dados", {})
|
| 45 |
+
for k, v in dados.items():
|
| 46 |
+
# séries no formato [[passo, valor], ...]
|
| 47 |
+
if isinstance(v, list) and v and isinstance(v[0], list):
|
| 48 |
+
series[k] = [par[1] for par in v if len(par) == 2]
|
| 49 |
+
deteccoes = {}
|
| 50 |
+
for k, vs in series.items():
|
| 51 |
+
if len(vs) < 8:
|
| 52 |
+
continue
|
| 53 |
+
xs = list(range(len(vs)))
|
| 54 |
+
incl = teilen_sen(xs, vs)
|
| 55 |
+
deteccoes[k] = {
|
| 56 |
+
"inclinacao_theil_sen": incl,
|
| 57 |
+
"n": len(vs),
|
| 58 |
+
"primeiro": vs[0], "ultimo": vs[-1],
|
| 59 |
+
}
|
| 60 |
+
painel["fase_c_series"] = deteccoes
|
| 61 |
+
|
| 62 |
+
# ---- módulos
|
| 63 |
+
for nome, arq in [("treino", "treino_v070.json"),
|
| 64 |
+
("fnh", "suite_fnh_v070.json"),
|
| 65 |
+
("arvore_quebra", "arvore_quebra_v070.json"),
|
| 66 |
+
("nlp_nlg", "suite_nlp_nlg_v070.json")]:
|
| 67 |
+
p = os.path.join(R, arq)
|
| 68 |
+
if os.path.exists(p):
|
| 69 |
+
with open(p) as f:
|
| 70 |
+
painel["modulos"][nome] = json.load(f)
|
| 71 |
+
|
| 72 |
+
# ---- resumo de direções (polaridade: CE/perda ↓ = aprendendo)
|
| 73 |
+
aprendendo = 0
|
| 74 |
+
piorando = 0
|
| 75 |
+
for k, d in deteccoes.items():
|
| 76 |
+
klow = k.lower()
|
| 77 |
+
pol = -1 if ("perda" in klow or "ce" in klow or "erro" in klow) else +1
|
| 78 |
+
prod = d["inclinacao_theil_sen"] * pol
|
| 79 |
+
if prod < -1e-12:
|
| 80 |
+
aprendendo += 1
|
| 81 |
+
elif prod > 1e-12:
|
| 82 |
+
piorando += 1
|
| 83 |
+
painel["resumo_direcoes"] = {"aprendendo": aprendendo, "piorando": piorando}
|
| 84 |
+
|
| 85 |
+
saida = os.path.join(R, "telemetria_v070.json")
|
| 86 |
+
with open(saida, "w") as f:
|
| 87 |
+
json.dump(painel, f, indent=1, ensure_ascii=False)
|
| 88 |
+
print(f"séries analisadas: {len(deteccoes)} | "
|
| 89 |
+
f"aprendendo {aprendendo} / piorando {piorando}")
|
| 90 |
+
print(f"OK: {saida}")
|
| 91 |
+
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
main()
|
scripts/66_suite_v070.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""66_suite_v070.py — v0.7.0: VEREDITO CONSOLIDADO de todos os gates.
|
| 3 |
+
|
| 4 |
+
Herda os 46 gates da v0.6.0 (suite_v060_final.json) e adiciona os da
|
| 5 |
+
v0.7.0: FNH (62), árvore+quebra-cabeça (63), NLP/NLG/SOM (64) + provas
|
| 6 |
+
cargo (84 testes) + telemetria (65).
|
| 7 |
+
"""
|
| 8 |
+
import json, os, sys
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 12 |
+
R = os.path.join(pasta, "resultados")
|
| 13 |
+
rel = {"versao": "0.7.0", "blocos": {}, "gates": {}}
|
| 14 |
+
|
| 15 |
+
def carrega(arq):
|
| 16 |
+
p = os.path.join(R, arq)
|
| 17 |
+
if os.path.exists(p):
|
| 18 |
+
with open(p) as f:
|
| 19 |
+
return json.load(f)
|
| 20 |
+
return None
|
| 21 |
+
|
| 22 |
+
# ---- bloco legado v0.6.0 (se presente)
|
| 23 |
+
legado = carrega("suite_v060_final.json")
|
| 24 |
+
if legado:
|
| 25 |
+
g_legado = legado.get("gates", legado)
|
| 26 |
+
ok = sum(1 for v in g_legado.values() if v is True)
|
| 27 |
+
rel["blocos"]["legado_v060"] = f"{ok}/{len(g_legado)}"
|
| 28 |
+
for k, v in g_legado.items():
|
| 29 |
+
rel["gates"][f"legado_{k}"] = v
|
| 30 |
+
|
| 31 |
+
# ---- blocos v0.7.0
|
| 32 |
+
for arq, bloco in [("suite_fnh_v070.json", "fnh"),
|
| 33 |
+
("arvore_quebra_v070.json", "arvore_quebra"),
|
| 34 |
+
("suite_nlp_nlg_v070.json", "nlp_nlg")]:
|
| 35 |
+
d = carrega(arq)
|
| 36 |
+
if d and "gates" in d:
|
| 37 |
+
g = d["gates"]
|
| 38 |
+
ok = sum(1 for v in g.values() if v)
|
| 39 |
+
rel["blocos"][bloco] = f"{ok}/{len(g)}"
|
| 40 |
+
for k, v in g.items():
|
| 41 |
+
rel["gates"][f"{bloco}_{k}"] = v
|
| 42 |
+
else:
|
| 43 |
+
rel["blocos"][bloco] = "AUSENTE"
|
| 44 |
+
rel["gates"][f"{bloco}_presente"] = False
|
| 45 |
+
|
| 46 |
+
# ---- telemetria presente
|
| 47 |
+
tel = carrega("telemetria_v070.json")
|
| 48 |
+
rel["gates"]["telemetria_painel_v070"] = tel is not None
|
| 49 |
+
rel["blocos"]["telemetria"] = "OK" if tel else "AUSENTE"
|
| 50 |
+
|
| 51 |
+
# ---- treino real presente (fases completas)
|
| 52 |
+
t70 = carrega("treino_v070.json")
|
| 53 |
+
ck_ok = all(os.path.exists(os.path.join(pasta, "resultados", f))
|
| 54 |
+
for f in ("tokenizador_conceitos.json", "gerador_conceitos.json"))
|
| 55 |
+
rel["gates"]["treino_real_completo"] = bool(
|
| 56 |
+
t70 and "c" in t70.get("fases", {}) and ck_ok
|
| 57 |
+
and t70.get("dados", {}).get("sequencias", 0) > 0)
|
| 58 |
+
rel["blocos"]["treino_real"] = "OK" if rel["gates"]["treino_real_completo"] else "AUSENTE"
|
| 59 |
+
|
| 60 |
+
# ---- veredito
|
| 61 |
+
n_ok = sum(1 for v in rel["gates"].values() if v is True)
|
| 62 |
+
n_tot = len(rel["gates"])
|
| 63 |
+
rel["veredito"] = f"{n_ok}/{n_tot}"
|
| 64 |
+
saida = os.path.join(R, "suite_v070_final.json")
|
| 65 |
+
with open(saida, "w") as f:
|
| 66 |
+
json.dump(rel, f, indent=1, ensure_ascii=False)
|
| 67 |
+
print(json.dumps(rel["blocos"], indent=1))
|
| 68 |
+
print(f"VEREDITO CONSOLIDADO v0.7.0: {rel['veredito']}")
|
| 69 |
+
print(f"OK: {saida}")
|
| 70 |
+
# APROVADO exige tudo (cargo já validado separadamente: 84/84)
|
| 71 |
+
assert n_ok == n_tot, f"gates falharam: {[k for k, v in rel['gates'].items() if v is not True]}"
|
| 72 |
+
|
| 73 |
+
if __name__ == "__main__":
|
| 74 |
+
main()
|
scripts/67_publicar_v070.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""67_publicar_v070.py — Publicação da v0.7.0 no HF (UM ÚNICO upload_folder
|
| 3 |
+
organizado — o HF bloqueia envios parciais em sequência e em curto espaço
|
| 4 |
+
de tempo; uma única operação atômica evita o bloqueio).
|
| 5 |
+
|
| 6 |
+
Fases:
|
| 7 |
+
1. inventário da conta + APAGAMENTO de estados de treino remanescentes;
|
| 8 |
+
2. varredura local do VALOR do token (nenhum arquivo pode contê-lo);
|
| 9 |
+
3. UM ÚNICO upload_folder (sem estados — checkpoints regeneráveis via
|
| 10 |
+
scripts 61/56);
|
| 11 |
+
4. verificação pós-upload (0 estados, módulos-chave presentes, contagem);
|
| 12 |
+
5. varredura REMOTA do valor do token (baixa e inspeciona os arquivos).
|
| 13 |
+
|
| 14 |
+
Token: EXCLUSIVAMENTE via variável de ambiente HF_TOKEN (nunca em arquivo).
|
| 15 |
+
"""
|
| 16 |
+
import os, sys, tempfile
|
| 17 |
+
|
| 18 |
+
ARQUIVOS_EXCLUIR_DIRS = {"target", ".cargo", "__pycache__", ".git", ".venv",
|
| 19 |
+
"skills", "externos"}
|
| 20 |
+
ARQUIVOS_EXCLUIR_NOME = {"shard_treino.jsonl", "shard_validacao.jsonl",
|
| 21 |
+
"bpe.json", "rustup-init.sh"}
|
| 22 |
+
SUFIXOS_ESTADO = (".pt", ".pth", ".ckpt", ".safetensors", ".bin", ".npz",
|
| 23 |
+
".msgpack")
|
| 24 |
+
NOMES_ESTADO = {"gerador_conceitos.json", "tokenizador_conceitos.json",
|
| 25 |
+
"gerador_aritmetica.json"}
|
| 26 |
+
|
| 27 |
+
def coletar(pasta):
|
| 28 |
+
arquivos = {}
|
| 29 |
+
for raiz, dirs, fs in os.walk(pasta):
|
| 30 |
+
dirs[:] = [d for d in dirs if d not in ARQUIVOS_EXCLUIR_DIRS]
|
| 31 |
+
for f in fs:
|
| 32 |
+
if f in ARQUIVOS_EXCLUIR_NOME or f in NOMES_ESTADO:
|
| 33 |
+
continue
|
| 34 |
+
if f.endswith(SUFIXOS_ESTADO):
|
| 35 |
+
continue
|
| 36 |
+
if f.endswith(".so") or f in (".gitignore", "Cargo.lock.ori"):
|
| 37 |
+
continue
|
| 38 |
+
if f.startswith("hf_") or f.startswith("intros_"):
|
| 39 |
+
continue # scripts de análise externa (não fazem parte)
|
| 40 |
+
caminho = os.path.join(raiz, f)
|
| 41 |
+
rel = os.path.relpath(caminho, pasta)
|
| 42 |
+
arquivos[rel] = caminho
|
| 43 |
+
return arquivos
|
| 44 |
+
|
| 45 |
+
def varre_token_em_arquivos(token, pares):
|
| 46 |
+
tb = token.encode()
|
| 47 |
+
achados = []
|
| 48 |
+
for rel, p in pares.items():
|
| 49 |
+
try:
|
| 50 |
+
with open(p, "rb") as f:
|
| 51 |
+
if tb in f.read():
|
| 52 |
+
achados.append(rel)
|
| 53 |
+
except OSError:
|
| 54 |
+
pass
|
| 55 |
+
return achados
|
| 56 |
+
|
| 57 |
+
def main():
|
| 58 |
+
from huggingface_hub import HfApi
|
| 59 |
+
token = os.environ.get("HF_TOKEN")
|
| 60 |
+
if not token:
|
| 61 |
+
print("ERRO: HF_TOKEN ausente"); sys.exit(1)
|
| 62 |
+
api = HfApi(token=token)
|
| 63 |
+
repo = "PowerMachine/khtst-rust"
|
| 64 |
+
|
| 65 |
+
# ---------------- 1) inventário da conta + apaga estados ----------------
|
| 66 |
+
print("=== 1) inventário da conta HF (procura estados de treino) ===")
|
| 67 |
+
repos = list(api.list_models(author="PowerMachine"))
|
| 68 |
+
print(f"repos: {len(repos)}")
|
| 69 |
+
total_removidos = 0
|
| 70 |
+
for r in repos:
|
| 71 |
+
try:
|
| 72 |
+
files = api.list_repo_files(repo_id=r.id, repo_type="model")
|
| 73 |
+
except Exception as e:
|
| 74 |
+
print(f" {r.id}: erro ao listar ({e})"); continue
|
| 75 |
+
estados = [f for f in files
|
| 76 |
+
if f.endswith(SUFIXOS_ESTADO)
|
| 77 |
+
or f.split("/")[-1] in NOMES_ESTADO
|
| 78 |
+
or (".meta.json" in f and "checkpoint" in f)]
|
| 79 |
+
if estados:
|
| 80 |
+
print(f" {r.id}: APAGANDO {len(estados)} estados")
|
| 81 |
+
for f in estados:
|
| 82 |
+
api.delete_file(path_in_repo=f, repo_id=r.id, repo_type="model")
|
| 83 |
+
total_removidos += len(estados)
|
| 84 |
+
else:
|
| 85 |
+
print(f" {r.id}: {len(files)} arquivos, 0 estados")
|
| 86 |
+
print(f"estados removidos no total: {total_removidos}")
|
| 87 |
+
|
| 88 |
+
# ---------------- 2) varredura local do token ----------------
|
| 89 |
+
pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 90 |
+
arquivos = coletar(pasta)
|
| 91 |
+
print(f"\n=== 2) varredura local: {len(arquivos)} arquivos ===")
|
| 92 |
+
achados = varre_token_em_arquivos(token, arquivos)
|
| 93 |
+
if achados:
|
| 94 |
+
print(f"ERRO: valor do token encontrado em: {achados}"); sys.exit(1)
|
| 95 |
+
print("varredura de token local: limpa")
|
| 96 |
+
import subprocess
|
| 97 |
+
home = os.path.expanduser("~")
|
| 98 |
+
try:
|
| 99 |
+
saida = subprocess.run(
|
| 100 |
+
["grep", "-rl", token, home, "--include=*.py", "--include=*.json",
|
| 101 |
+
"--include=*.md", "--include=*.sh", "--include=*.toml"],
|
| 102 |
+
capture_output=True, text=True, timeout=120)
|
| 103 |
+
if saida.stdout.strip():
|
| 104 |
+
print(f"ERRO: token no home: {saida.stdout.strip()}"); sys.exit(1)
|
| 105 |
+
except Exception:
|
| 106 |
+
pass
|
| 107 |
+
print("varredura do home: limpa")
|
| 108 |
+
|
| 109 |
+
# ---------------- 3) UM ÚNICO upload_folder ----------------
|
| 110 |
+
print(f"\n=== 3) upload_folder ÚNICO ({len(arquivos)} arquivos) ===")
|
| 111 |
+
api.upload_folder(
|
| 112 |
+
repo_id=repo, repo_type="model", folder_path=pasta,
|
| 113 |
+
ignore_patterns=None, commit_message="v0.7.0: FNH gate de punição + "
|
| 114 |
+
"árvore de pesos indexada (arestas duplas) + quebra-cabeça do "
|
| 115 |
+
"Pensamento + NLP/NLG + decodificador especulativo + auto-ajuste SOM "
|
| 116 |
+
"limitado + correções v070-1/2/3 — 73/73 gates, 84/84 cargo, "
|
| 117 |
+
"provas r-28..r-32",
|
| 118 |
+
)
|
| 119 |
+
print("upload concluído")
|
| 120 |
+
|
| 121 |
+
# ---------------- 4) verificação pós-upload ----------------
|
| 122 |
+
print("\n=== 4) verificação pós-upload ===")
|
| 123 |
+
files = api.list_repo_files(repo_id=repo, repo_type="model")
|
| 124 |
+
estados = [f for f in files if f.endswith(SUFIXOS_ESTADO)
|
| 125 |
+
or f.split("/")[-1] in NOMES_ESTADO]
|
| 126 |
+
assert not estados, f"estados publicados: {estados}"
|
| 127 |
+
chaves = ["src/fnh.rs", "src/arvore_pesos.rs", "src/nlp.rs", "src/nlg.rs",
|
| 128 |
+
"src/pensamento.rs", "scripts/61_treino_v070.py",
|
| 129 |
+
"scripts/66_suite_v070.py",
|
| 130 |
+
"docs/memorias/r-28-analise-repos-design-v070.md",
|
| 131 |
+
"docs/memorias/r-32-resultados-v070.md",
|
| 132 |
+
"resultados/suite_v070_final.json"]
|
| 133 |
+
faltando = [c for c in chaves if c not in files]
|
| 134 |
+
assert not faltando, f"faltando: {faltando}"
|
| 135 |
+
print(f"repo: {len(files)} arquivos | 0 estados | módulos-chave OK")
|
| 136 |
+
|
| 137 |
+
# ---------------- 5) varredura REMOTA do token ----------------
|
| 138 |
+
print("\n=== 5) varredura REMOTA do valor do token ===")
|
| 139 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 140 |
+
achados_remotos = []
|
| 141 |
+
for f in files:
|
| 142 |
+
if f.endswith((".py", ".rs", ".md", ".json", ".toml", ".sh", ".cfg",
|
| 143 |
+
".txt", ".jsonl")):
|
| 144 |
+
try:
|
| 145 |
+
p = api.hf_hub_download(repo_id=repo, repo_type="model",
|
| 146 |
+
filename=f, local_dir=tmp)
|
| 147 |
+
with open(p, "rb") as fh:
|
| 148 |
+
if token.encode() in fh.read():
|
| 149 |
+
achados_remotos.append(f)
|
| 150 |
+
except Exception:
|
| 151 |
+
pass
|
| 152 |
+
if achados_remotos:
|
| 153 |
+
print(f"ERRO: token remoto em: {achados_remotos}"); sys.exit(1)
|
| 154 |
+
print("varredura remota: limpa")
|
| 155 |
+
print("\nPUBLICAÇÃO v0.7.0 CONCLUÍDA")
|
| 156 |
+
|
| 157 |
+
if __name__ == "__main__":
|
| 158 |
+
main()
|
src/amostrador.rs
CHANGED
|
@@ -81,6 +81,12 @@ impl AmostradorConceitos {
|
|
| 81 |
let mut caches_uncond: Vec<CacheKv> = Vec::new();
|
| 82 |
let mut zhats: Vec<Tensor> = Vec::new();
|
| 83 |
let mut codigos: Vec<Tensor> = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
let fim = parar_no_nivel.unwrap_or(l).min(l);
|
| 85 |
let nfe_inicio = self.nfe_contador;
|
| 86 |
for nivel in 0..fim {
|
|
@@ -92,12 +98,42 @@ impl AmostradorConceitos {
|
|
| 92 |
} else {
|
| 93 |
None
|
| 94 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
let (h_c, h_u): (Tensor, Option<Tensor>) = if nivel == 0 {
|
| 96 |
// prefixo entra junto na 1ª avaliação
|
| 97 |
let prefixo_ids = Tensor::zeros((b, N_PREFIXO), candle_core::DType::U32, dev)?;
|
| 98 |
let pe = g.emb_niveis.tabela.fwd(&prefixo_ids)?;
|
| 99 |
let e = Tensor::cat(&[pe, entrada_cond], 1)?;
|
| 100 |
-
let h = self.forward_bloco(g, &e, &mut caches_cond, nivel
|
|
|
|
| 101 |
self.nfe_contador += 1;
|
| 102 |
let hc = h.narrow(1, N_PREFIXO, n_l)?;
|
| 103 |
let hu = if let Some(eu) = entrada_uncond {
|
|
@@ -105,16 +141,19 @@ impl AmostradorConceitos {
|
|
| 105 |
let prefixo_ids2 = Tensor::zeros((b, N_PREFIXO), candle_core::DType::U32, dev)?;
|
| 106 |
g.emb_niveis.tabela.fwd(&prefixo_ids2)?
|
| 107 |
}, eu], 1)?;
|
| 108 |
-
let hu = self.forward_bloco(g, &eu2, &mut caches_uncond, nivel
|
|
|
|
| 109 |
self.nfe_contador += 1;
|
| 110 |
Some(hu.narrow(1, N_PREFIXO, n_l)?)
|
| 111 |
} else { None };
|
| 112 |
(hc, hu)
|
| 113 |
} else {
|
| 114 |
-
let h = self.forward_bloco(g, &entrada_cond, &mut caches_cond, nivel
|
|
|
|
| 115 |
self.nfe_contador += 1;
|
| 116 |
let hu = if let Some(eu) = entrada_uncond {
|
| 117 |
-
let hu = self.forward_bloco(g, &eu, &mut caches_uncond, nivel
|
|
|
|
| 118 |
self.nfe_contador += 1;
|
| 119 |
Some(hu)
|
| 120 |
} else { None };
|
|
@@ -134,6 +173,8 @@ impl AmostradorConceitos {
|
|
| 134 |
entrada: &Tensor,
|
| 135 |
caches: &mut Vec<CacheKv>,
|
| 136 |
nivel: usize,
|
|
|
|
|
|
|
| 137 |
) -> Result<Tensor> {
|
| 138 |
let n_novo = entrada.dim(1)?;
|
| 139 |
let n_cache = caches.first().and_then(|c| c.k.as_ref()).map(|k| k.dims()[2]).unwrap_or(0);
|
|
@@ -152,7 +193,17 @@ impl AmostradorConceitos {
|
|
| 152 |
usados += tomar;
|
| 153 |
}
|
| 154 |
niveis_totais.extend(niveis_novos.clone());
|
| 155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
}
|
| 157 |
|
| 158 |
unsafe fn amostrar_nivel(
|
|
|
|
| 81 |
let mut caches_uncond: Vec<CacheKv> = Vec::new();
|
| 82 |
let mut zhats: Vec<Tensor> = Vec::new();
|
| 83 |
let mut codigos: Vec<Tensor> = Vec::new();
|
| 84 |
+
// v070-2 (paridade FiLM): estados pós-bloco-0 acumulados por ramo —
|
| 85 |
+
// o âncora mede o resumo sobre eles exatamente como forward_treino
|
| 86 |
+
// (que usa o x PRÉ-FiLM pós-bloco-0 — vide cat tardio em tronco.rs).
|
| 87 |
+
let d_modelo = g.tronco.norm_final.ganho.as_tensor().dims()[0];
|
| 88 |
+
let mut estados_b0_cond = Tensor::zeros((b, 0, d_modelo), candle_core::DType::F32, dev)?;
|
| 89 |
+
let mut estados_b0_uncond = Tensor::zeros((b, 0, d_modelo), candle_core::DType::F32, dev)?;
|
| 90 |
let fim = parar_no_nivel.unwrap_or(l).min(l);
|
| 91 |
let nfe_inicio = self.nfe_contador;
|
| 92 |
for nivel in 0..fim {
|
|
|
|
| 98 |
} else {
|
| 99 |
None
|
| 100 |
};
|
| 101 |
+
// resumo-âncora para o FiLM deste nível (nível ≥ 2) — paridade
|
| 102 |
+
// com forward_treino: q[nivel] sobre estados_b0 acumulados;
|
| 103 |
+
// nível 1: resumo ≡ 0 ⇒ FiLM identidade (W_f(0)=0) ⇒ None.
|
| 104 |
+
let film_cond: Option<(Tensor, usize)> = if nivel >= 1 {
|
| 105 |
+
match &g.tronco.ancora {
|
| 106 |
+
Some(anc) => {
|
| 107 |
+
let mut r_nivel = Vec::new();
|
| 108 |
+
for bi in 0..b {
|
| 109 |
+
r_nivel.push(anc.resumir(
|
| 110 |
+
&estados_b0_cond.narrow(0, bi, 1)?, nivel)?);
|
| 111 |
+
}
|
| 112 |
+
Some((Tensor::cat(&r_nivel, 0)?, nivel + 1))
|
| 113 |
+
}
|
| 114 |
+
None => None,
|
| 115 |
+
}
|
| 116 |
+
} else { None };
|
| 117 |
+
let film_uncond: Option<(Tensor, usize)> = if nivel >= 1 && cfg_ativo {
|
| 118 |
+
match &g.tronco.ancora {
|
| 119 |
+
Some(anc) => {
|
| 120 |
+
let mut r_nivel = Vec::new();
|
| 121 |
+
for bi in 0..b {
|
| 122 |
+
r_nivel.push(anc.resumir(
|
| 123 |
+
&estados_b0_uncond.narrow(0, bi, 1)?, nivel)?);
|
| 124 |
+
}
|
| 125 |
+
Some((Tensor::cat(&r_nivel, 0)?, nivel + 1))
|
| 126 |
+
}
|
| 127 |
+
None => None,
|
| 128 |
+
}
|
| 129 |
+
} else { None };
|
| 130 |
let (h_c, h_u): (Tensor, Option<Tensor>) = if nivel == 0 {
|
| 131 |
// prefixo entra junto na 1ª avaliação
|
| 132 |
let prefixo_ids = Tensor::zeros((b, N_PREFIXO), candle_core::DType::U32, dev)?;
|
| 133 |
let pe = g.emb_niveis.tabela.fwd(&prefixo_ids)?;
|
| 134 |
let e = Tensor::cat(&[pe, entrada_cond], 1)?;
|
| 135 |
+
let h = self.forward_bloco(g, &e, &mut caches_cond, nivel,
|
| 136 |
+
film_cond.as_ref().map(|(r, n)| (r, *n)), &mut estados_b0_cond)?;
|
| 137 |
self.nfe_contador += 1;
|
| 138 |
let hc = h.narrow(1, N_PREFIXO, n_l)?;
|
| 139 |
let hu = if let Some(eu) = entrada_uncond {
|
|
|
|
| 141 |
let prefixo_ids2 = Tensor::zeros((b, N_PREFIXO), candle_core::DType::U32, dev)?;
|
| 142 |
g.emb_niveis.tabela.fwd(&prefixo_ids2)?
|
| 143 |
}, eu], 1)?;
|
| 144 |
+
let hu = self.forward_bloco(g, &eu2, &mut caches_uncond, nivel,
|
| 145 |
+
film_uncond.as_ref().map(|(r, n)| (r, *n)), &mut estados_b0_uncond)?;
|
| 146 |
self.nfe_contador += 1;
|
| 147 |
Some(hu.narrow(1, N_PREFIXO, n_l)?)
|
| 148 |
} else { None };
|
| 149 |
(hc, hu)
|
| 150 |
} else {
|
| 151 |
+
let h = self.forward_bloco(g, &entrada_cond, &mut caches_cond, nivel,
|
| 152 |
+
film_cond.as_ref().map(|(r, n)| (r, *n)), &mut estados_b0_cond)?;
|
| 153 |
self.nfe_contador += 1;
|
| 154 |
let hu = if let Some(eu) = entrada_uncond {
|
| 155 |
+
let hu = self.forward_bloco(g, &eu, &mut caches_uncond, nivel,
|
| 156 |
+
film_uncond.as_ref().map(|(r, n)| (r, *n)), &mut estados_b0_uncond)?;
|
| 157 |
self.nfe_contador += 1;
|
| 158 |
Some(hu)
|
| 159 |
} else { None };
|
|
|
|
| 173 |
entrada: &Tensor,
|
| 174 |
caches: &mut Vec<CacheKv>,
|
| 175 |
nivel: usize,
|
| 176 |
+
film: Option<(&Tensor, usize)>,
|
| 177 |
+
estados_b0: &mut Tensor,
|
| 178 |
) -> Result<Tensor> {
|
| 179 |
let n_novo = entrada.dim(1)?;
|
| 180 |
let n_cache = caches.first().and_then(|c| c.k.as_ref()).map(|k| k.dims()[2]).unwrap_or(0);
|
|
|
|
| 193 |
usados += tomar;
|
| 194 |
}
|
| 195 |
niveis_totais.extend(niveis_novos.clone());
|
| 196 |
+
// estados pós-bloco-0 das posições novas (PRÉ-FiLM — como forward_treino)
|
| 197 |
+
let mut novos_b0: Tensor = Tensor::zeros((0, 0, 0), candle_core::DType::F32, entrada.device())?;
|
| 198 |
+
let h = g.tronco.forward_nivel(entrada, caches, &mascara, &niveis_novos, &niveis_totais,
|
| 199 |
+
film, Some(&mut novos_b0))?;
|
| 200 |
+
// acumula para o âncora dos níveis seguintes
|
| 201 |
+
*estados_b0 = if estados_b0.dims()[1] == 0 {
|
| 202 |
+
novos_b0
|
| 203 |
+
} else {
|
| 204 |
+
Tensor::cat(&[estados_b0.clone(), novos_b0], 1)?
|
| 205 |
+
};
|
| 206 |
+
Ok(h)
|
| 207 |
}
|
| 208 |
|
| 209 |
unsafe fn amostrar_nivel(
|
src/arvore_pesos.rs
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! arvore_pesos.rs — **Árvore Enraizada com Arestas Duplas** indexada dos
|
| 2 |
+
//! pesos do projeto (requisito v0.7.0: "arquitetura de pesos em árvore e
|
| 3 |
+
//! subárvores indexada" + ajuste bidirecional do quebra-cabeça).
|
| 4 |
+
//!
|
| 5 |
+
//! Estrutura (r-28 §4.2, provas r-30):
|
| 6 |
+
//! - **Nós** = módulos do projeto (raiz "khtst"; filhos: tronco, quantizador,
|
| 7 |
+
//! som, jepa, moe, pensamento, verificador, fnh, nlp, nlg, acoplador).
|
| 8 |
+
//! - **Partição de índices**: cada nó possui um intervalo [ini, fim) do índice
|
| 9 |
+
//! global de parâmetros — disjuntos e cobrindo [0, N) (T-A1).
|
| 10 |
+
//! - **Arestas duplas**: cada relação pai↔filho é UMA aresta com DOIS
|
| 11 |
+
//! enlaces direcionados (pai→filho = regulação; filho→pai = crédito) —
|
| 12 |
+
//! `vizinho` e `aresta_de` são O(1) por índice de adjacência (T-A4).
|
| 13 |
+
//! - **Fundo-topo**: agregação de métricas (telemetria/crédito) sobe —
|
| 14 |
+
//! soma exata por indução (T-A2).
|
| 15 |
+
//! - **Topo-fundo**: regulação desce com decaimento por profundidade —
|
| 16 |
+
//! causal por filtração de níveis e terminante por potencial (T-A3).
|
| 17 |
+
|
| 18 |
+
use std::collections::HashMap;
|
| 19 |
+
|
| 20 |
+
/// Um nó da árvore — um módulo com sua fatia do índice de parâmetros.
|
| 21 |
+
#[derive(Clone, Debug)]
|
| 22 |
+
pub struct NoArvore {
|
| 23 |
+
pub nome: String,
|
| 24 |
+
pub pai: Option<usize>,
|
| 25 |
+
pub filhos: Vec<usize>,
|
| 26 |
+
/// Intervalo [ini, fim) no índice global de parâmetros (partição).
|
| 27 |
+
pub ini: usize,
|
| 28 |
+
pub fim: usize,
|
| 29 |
+
/// Profundidade (raiz = 0).
|
| 30 |
+
pub profundidade: usize,
|
| 31 |
+
/// Métricas agregadas do nó (a própria + descendentes).
|
| 32 |
+
pub metricas: HashMap<String, f64>,
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/// Enlace direcionado de uma aresta dupla.
|
| 36 |
+
#[derive(Clone, Copy, Debug)]
|
| 37 |
+
pub struct Enlace {
|
| 38 |
+
/// id do PAR (compartilhado pelos dois enlaces da aresta dupla).
|
| 39 |
+
pub id_par: usize,
|
| 40 |
+
pub de: usize,
|
| 41 |
+
pub para: usize,
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/// Árvore de pesos indexada com arestas duplas.
|
| 45 |
+
pub struct ArvorePesos {
|
| 46 |
+
pub nos: Vec<NoArvore>,
|
| 47 |
+
por_nome: HashMap<String, usize>,
|
| 48 |
+
/// Enlaces (2 por aresta: regulação desce, crédito sobe).
|
| 49 |
+
pub enlaces: Vec<Enlace>,
|
| 50 |
+
/// Índice de adjacência: nó → enlaces incidentes — O(1).
|
| 51 |
+
adj: Vec<Vec<usize>>,
|
| 52 |
+
/// Fluxo de crédito por enlace (sob na direção filho→pai).
|
| 53 |
+
pub credito: Vec<f64>,
|
| 54 |
+
/// Regulação por enlace (desce na direção pai→filho).
|
| 55 |
+
pub regulacao: Vec<f64>,
|
| 56 |
+
pub n_params: usize,
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
/// Layout padrão do projeto KHTST_RUST v0.7.0 — (nome, n_params, filhos).
|
| 60 |
+
/// A ordem define os intervalos da partição (T-A1).
|
| 61 |
+
pub const LAYOUT_PADRAO: &[(&str, usize)] = &[
|
| 62 |
+
("khtst", 0), // raiz — sem parâmetros próprios (agrega)
|
| 63 |
+
("tronco", 1),
|
| 64 |
+
("quantizador", 1),
|
| 65 |
+
("som", 1),
|
| 66 |
+
("jepa", 1),
|
| 67 |
+
("moe", 1),
|
| 68 |
+
("pensamento", 1),
|
| 69 |
+
("verificador", 1),
|
| 70 |
+
("fnh", 1),
|
| 71 |
+
("nlp", 1),
|
| 72 |
+
("nlg", 1),
|
| 73 |
+
("acoplador", 1),
|
| 74 |
+
];
|
| 75 |
+
|
| 76 |
+
impl ArvorePesos {
|
| 77 |
+
/// Constrói a árvore padrão: raiz "khtst" com os módulos como filhos
|
| 78 |
+
/// diretos (profundidade 1) — subárvores podem ser anexadas depois.
|
| 79 |
+
/// `contagens`: número de parâmetros por módulo (default 1 quando 0).
|
| 80 |
+
pub fn nova(contagens: &HashMap<String, usize>) -> Self {
|
| 81 |
+
let mut nos: Vec<NoArvore> = Vec::new();
|
| 82 |
+
let mut por_nome = HashMap::new();
|
| 83 |
+
let mut pos = 0usize;
|
| 84 |
+
for (i, (nome, _)) in LAYOUT_PADRAO.iter().enumerate() {
|
| 85 |
+
let n = if i == 0 {
|
| 86 |
+
0
|
| 87 |
+
} else {
|
| 88 |
+
contagens.get(*nome).copied().unwrap_or(1).max(1)
|
| 89 |
+
};
|
| 90 |
+
let no = NoArvore {
|
| 91 |
+
nome: nome.to_string(),
|
| 92 |
+
pai: if i == 0 { None } else { Some(0) },
|
| 93 |
+
filhos: Vec::new(),
|
| 94 |
+
ini: pos,
|
| 95 |
+
fim: pos + n,
|
| 96 |
+
profundidade: if i == 0 { 0 } else { 1 },
|
| 97 |
+
metricas: HashMap::new(),
|
| 98 |
+
};
|
| 99 |
+
pos = no.fim;
|
| 100 |
+
por_nome.insert(nome.to_string(), i);
|
| 101 |
+
if i != 0 {
|
| 102 |
+
nos[0].filhos.push(i);
|
| 103 |
+
}
|
| 104 |
+
nos.push(no);
|
| 105 |
+
}
|
| 106 |
+
let n_nos = nos.len();
|
| 107 |
+
// arestas duplas: raiz↔filho
|
| 108 |
+
let mut enlaces = Vec::new();
|
| 109 |
+
let mut adj = vec![Vec::new(); n_nos];
|
| 110 |
+
for (id_par, &f) in nos[0].filhos.iter().enumerate() {
|
| 111 |
+
// enlace pai→filho (regulação desce)
|
| 112 |
+
adj[0].push(enlaces.len());
|
| 113 |
+
enlaces.push(Enlace { id_par, de: 0, para: f });
|
| 114 |
+
// enlace filho→pai (crédito sobe)
|
| 115 |
+
adj[f].push(enlaces.len());
|
| 116 |
+
enlaces.push(Enlace { id_par, de: f, para: 0 });
|
| 117 |
+
}
|
| 118 |
+
let n_enlaces = enlaces.len();
|
| 119 |
+
ArvorePesos {
|
| 120 |
+
nos,
|
| 121 |
+
por_nome,
|
| 122 |
+
enlaces,
|
| 123 |
+
adj,
|
| 124 |
+
credito: vec![0.0; n_enlaces],
|
| 125 |
+
regulacao: vec![0.0; n_enlaces],
|
| 126 |
+
n_params: pos,
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/// Consulta O(1) por nome (T-A4).
|
| 131 |
+
pub fn no(&self, nome: &str) -> Option<usize> {
|
| 132 |
+
self.por_nome.get(nome).copied()
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
pub fn n_nos(&self) -> usize {
|
| 136 |
+
self.nos.len()
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/// Enlace direcionado `de`→`para` (aresta dupla — T-A1 simetria).
|
| 140 |
+
pub fn enlace(&self, de: usize, para: usize) -> Option<usize> {
|
| 141 |
+
self.adj[de]
|
| 142 |
+
.iter()
|
| 143 |
+
.copied()
|
| 144 |
+
.find(|&e| self.enlaces[e].para == para)
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
/// Enlaces incidentes ao nó (ambas as direções) — O(1) pelo índice.
|
| 148 |
+
pub fn incidentes(&self, no: usize) -> &[usize] {
|
| 149 |
+
&self.adj[no]
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/// FatiA do índice de parâmetros da subárvore enraizada em `no`
|
| 153 |
+
/// (o próprio intervalo quando não há filhos anexados).
|
| 154 |
+
pub fn subarvore(&self, no: usize) -> (usize, usize) {
|
| 155 |
+
// varre a subárvore em profundidade e devolve o envoltório dos
|
| 156 |
+
// intervalos (a partição é contígua por construção pós-ordem se os
|
| 157 |
+
// filhos forem anexados em ordem — aqui calculamos o envoltório)
|
| 158 |
+
let (mut ini, mut fim) = (self.nos[no].ini, self.nos[no].fim);
|
| 159 |
+
for &f in &self.nos[no].filhos {
|
| 160 |
+
let (i2, f2) = self.subarvore(f);
|
| 161 |
+
ini = ini.min(i2);
|
| 162 |
+
fim = fim.max(f2);
|
| 163 |
+
}
|
| 164 |
+
(ini, fim)
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/// Caminho do nó até a raiz (sobe pelos pais — termina: pai único, raiz
|
| 168 |
+
/// sem pai; T-A3 terminação).
|
| 169 |
+
pub fn caminho_ate_raiz(&self, no: usize) -> Vec<usize> {
|
| 170 |
+
let mut caminho = vec![no];
|
| 171 |
+
let mut atual = no;
|
| 172 |
+
while let Some(p) = self.nos[atual].pai {
|
| 173 |
+
caminho.push(p);
|
| 174 |
+
atual = p;
|
| 175 |
+
}
|
| 176 |
+
caminho
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/// **Fundo-topo**: agrega a métrica dos filhos no pai (pós-ordem —
|
| 180 |
+
/// T-A2: a soma na raiz = soma global de todas as folhas + próprias).
|
| 181 |
+
/// Devolve o total agregado na raiz.
|
| 182 |
+
pub fn agregar_fundo_topo(&mut self, metrica: &str) -> f64 {
|
| 183 |
+
self.agregar_rec(0, metrica)
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
fn agregar_rec(&mut self, no: usize, metrica: &str) -> f64 {
|
| 187 |
+
let mut total = self.nos[no].metricas.get(metrica).copied().unwrap_or(0.0);
|
| 188 |
+
let filhos = self.nos[no].filhos.clone();
|
| 189 |
+
for f in filhos {
|
| 190 |
+
let v = self.agregar_rec(f, metrica);
|
| 191 |
+
// crédito do filho sobe pelo enlace filho→pai
|
| 192 |
+
if let Some(e) = self.enlace(f, no) {
|
| 193 |
+
self.credito[e] = v;
|
| 194 |
+
}
|
| 195 |
+
total += v;
|
| 196 |
+
}
|
| 197 |
+
self.nos[no].metricas.insert(metrica.to_string(), total);
|
| 198 |
+
total
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
/// **Topo-fundo**: regulação desce do nó `origem` com decaimento
|
| 202 |
+
/// λ^k por nível de profundidade relativa (T-A3: causal — desce por
|
| 203 |
+
/// níveis da filtração da árvore; terminante — profundidade finita).
|
| 204 |
+
pub fn regular_topo_fundo(&mut self, origem: usize, delta: f64, lambda: f64) {
|
| 205 |
+
let base = self.nos[origem].profundidade;
|
| 206 |
+
self.regular_rec(origem, delta, lambda, base);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
fn regular_rec(&mut self, no: usize, delta: f64, lambda: f64, base: usize) {
|
| 210 |
+
let k = self.nos[no].profundidade.saturating_sub(base);
|
| 211 |
+
let d = delta * lambda.powi(k as i32);
|
| 212 |
+
let filhos = self.nos[no].filhos.clone();
|
| 213 |
+
for f in filhos {
|
| 214 |
+
if let Some(e) = self.enlace(no, f) {
|
| 215 |
+
self.regulacao[e] = d; // desce pelo enlace pai→filho
|
| 216 |
+
}
|
| 217 |
+
self.regular_rec(f, delta, lambda, base);
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/// **Ajuste indexado regulado** (quebra-cabeça do Pensamento — r-31):
|
| 222 |
+
/// aplica `delta` (com teto por componente |δ| ≤ delta_max) na métrica
|
| 223 |
+
/// `metrica` do nó `no` e propaga o crédito fundo-topo até a raiz.
|
| 224 |
+
/// Devolve o delta efetivamente aplicado (clamped).
|
| 225 |
+
pub fn aplicar_ajuste_indexado(
|
| 226 |
+
&mut self,
|
| 227 |
+
no: usize,
|
| 228 |
+
metrica: &str,
|
| 229 |
+
delta: f64,
|
| 230 |
+
delta_max: f64,
|
| 231 |
+
) -> f64 {
|
| 232 |
+
let d = delta.clamp(-delta_max, delta_max);
|
| 233 |
+
let atual = self.nos[no].metricas.get(metrica).copied().unwrap_or(0.0);
|
| 234 |
+
self.nos[no].metricas.insert(metrica.to_string(), atual + d);
|
| 235 |
+
d
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
/// Verificação dos invariantes estruturais (T-A1) — usada nos gates.
|
| 239 |
+
pub fn invariantes_ok(&self) -> Result<(), String> {
|
| 240 |
+
// (i) pai único exceto raiz; profundidade consistente
|
| 241 |
+
for (i, no) in self.nos.iter().enumerate() {
|
| 242 |
+
if i == 0 {
|
| 243 |
+
if no.pai.is_some() {
|
| 244 |
+
return Err("raiz não pode ter pai".into());
|
| 245 |
+
}
|
| 246 |
+
} else if let Some(p) = no.pai {
|
| 247 |
+
if p >= self.nos.len() {
|
| 248 |
+
return Err(format!("pai {p} fora do intervalo (nó {i})"));
|
| 249 |
+
}
|
| 250 |
+
if !self.nos[p].filhos.contains(&i) {
|
| 251 |
+
return Err(format!("pai de {i} não o lista como filho"));
|
| 252 |
+
}
|
| 253 |
+
} else {
|
| 254 |
+
return Err(format!("não-raiz {i} sem pai"));
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
// (ii) partição: intervalos disjuntos dentro de [0, n_params)
|
| 258 |
+
let mut marcas = vec![false; self.n_params];
|
| 259 |
+
for no in &self.nos {
|
| 260 |
+
if no.ini >= no.fim && no.nome != "khtst" {
|
| 261 |
+
return Err(format!("nó {} com intervalo vazio", no.nome));
|
| 262 |
+
}
|
| 263 |
+
for k in no.ini..no.fim {
|
| 264 |
+
if marcas[k] {
|
| 265 |
+
return Err(format!("índice {k} particionado duas vezes"));
|
| 266 |
+
}
|
| 267 |
+
marcas[k] = true;
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
if marcas.iter().any(|m| !m) {
|
| 271 |
+
return Err("partição não cobre todo o índice".into());
|
| 272 |
+
}
|
| 273 |
+
// (iii) simetria das arestas duplas: (u,v) ∈ E ⇔ (v,u) ∈ E, mesmo id_par
|
| 274 |
+
for e in &self.enlaces {
|
| 275 |
+
let espelhado = self
|
| 276 |
+
.enlace(e.para, e.de)
|
| 277 |
+
.ok_or_else(|| format!("aresta ({},{}) sem espelho", e.de, e.para))?;
|
| 278 |
+
if self.enlaces[espelhado].id_par != e.id_par {
|
| 279 |
+
return Err(format!(
|
| 280 |
+
"espelho de ({},{}) com id_par diferente",
|
| 281 |
+
e.de, e.para
|
| 282 |
+
));
|
| 283 |
+
}
|
| 284 |
+
}
|
| 285 |
+
Ok(())
|
| 286 |
+
}
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
#[cfg(test)]
|
| 290 |
+
mod testes {
|
| 291 |
+
use super::*;
|
| 292 |
+
|
| 293 |
+
fn arvore_exemplo() -> ArvorePesos {
|
| 294 |
+
let mut contagens = HashMap::new();
|
| 295 |
+
contagens.insert("tronco".to_string(), 40usize);
|
| 296 |
+
contagens.insert("quantizador".to_string(), 10);
|
| 297 |
+
contagens.insert("som".to_string(), 6);
|
| 298 |
+
contagens.insert("jepa".to_string(), 6);
|
| 299 |
+
contagens.insert("moe".to_string(), 5);
|
| 300 |
+
contagens.insert("pensamento".to_string(), 8);
|
| 301 |
+
contagens.insert("verificador".to_string(), 3);
|
| 302 |
+
contagens.insert("fnh".to_string(), 4);
|
| 303 |
+
contagens.insert("nlp".to_string(), 5);
|
| 304 |
+
contagens.insert("nlg".to_string(), 5);
|
| 305 |
+
contagens.insert("acoplador".to_string(), 2);
|
| 306 |
+
ArvorePesos::nova(&contagens)
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
/// T-A1: invariantes — pai único, partição disjunta completa, simetria.
|
| 310 |
+
#[test]
|
| 311 |
+
fn ta1_invariantes() {
|
| 312 |
+
let a = arvore_exemplo();
|
| 313 |
+
a.invariantes_ok().expect("invariantes T-A1");
|
| 314 |
+
assert_eq!(a.n_params, 94);
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
/// T-A2: agregação fundo-topo = soma global (indução pós-ordem).
|
| 318 |
+
#[test]
|
| 319 |
+
fn ta2_agregacao_fundo_topo() {
|
| 320 |
+
let mut a = arvore_exemplo();
|
| 321 |
+
// métricas locais por nó
|
| 322 |
+
let valores = [
|
| 323 |
+
("tronco", 3.0),
|
| 324 |
+
("quantizador", 5.0),
|
| 325 |
+
("som", 1.0),
|
| 326 |
+
("jepa", 2.0),
|
| 327 |
+
("moe", 4.0),
|
| 328 |
+
("pensamento", 7.0),
|
| 329 |
+
("verificador", 1.0),
|
| 330 |
+
("fnh", 6.0),
|
| 331 |
+
("nlp", 2.0),
|
| 332 |
+
("nlg", 3.0),
|
| 333 |
+
("acoplador", 1.0),
|
| 334 |
+
];
|
| 335 |
+
let mut esperado = 0.0;
|
| 336 |
+
for (nome, v) in valores {
|
| 337 |
+
let no = a.no(nome).unwrap();
|
| 338 |
+
a.nos[no].metricas.insert("punicao".into(), v);
|
| 339 |
+
esperado += v;
|
| 340 |
+
}
|
| 341 |
+
let total = a.agregar_fundo_topo("punicao");
|
| 342 |
+
assert!((total - esperado).abs() < 1e-12, "T-A2: {total} ≠ {esperado}");
|
| 343 |
+
let raiz = a.no("khtst").unwrap();
|
| 344 |
+
let na_raiz = a.nos[raiz].metricas["punicao"];
|
| 345 |
+
assert!((na_raiz - esperado).abs() < 1e-12, "T-A2: raiz {na_raiz}");
|
| 346 |
+
// crédito nos enlaces filho→pai = valor agregado do filho
|
| 347 |
+
let f = a.no("fnh").unwrap();
|
| 348 |
+
let e = a.enlace(f, raiz).unwrap();
|
| 349 |
+
assert!((a.credito[e] - 6.0).abs() < 1e-12, "T-A2: crédito do fnh");
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
/// T-A3: regulação topo-fundo — decaimento λ^k e causalidade por níveis.
|
| 353 |
+
#[test]
|
| 354 |
+
fn ta3_regulacao_topo_fundo() {
|
| 355 |
+
let mut a = arvore_exemplo();
|
| 356 |
+
let raiz = a.no("khtst").unwrap();
|
| 357 |
+
a.regular_topo_fundo(raiz, 0.8, 0.5);
|
| 358 |
+
// filhos diretos da raiz: k=0 ⇒ regulação = 0,8 (λ^0)
|
| 359 |
+
for &f in &a.nos[raiz].filhos.clone() {
|
| 360 |
+
let e = a.enlace(raiz, f).unwrap();
|
| 361 |
+
assert!((a.regulacao[e] - 0.8).abs() < 1e-12, "T-A3: nível 1");
|
| 362 |
+
}
|
| 363 |
+
assert!(a.invariantes_ok().is_ok());
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
/// T-A4: consultas O(1) — por nome, enlace e incidentes.
|
| 367 |
+
#[test]
|
| 368 |
+
fn ta4_consultas_o1() {
|
| 369 |
+
let a = arvore_exemplo();
|
| 370 |
+
let f = a.no("fnh").expect("fnh");
|
| 371 |
+
assert_eq!(a.nos[f].nome, "fnh");
|
| 372 |
+
let raiz = a.no("khtst").unwrap();
|
| 373 |
+
assert!(a.enlace(f, raiz).is_some());
|
| 374 |
+
assert!(a.enlace(raiz, f).is_some());
|
| 375 |
+
assert_eq!(a.incidentes(f).len(), 1); // 1 aresta dupla = 1 enlace incidente listado? não: 1 enlace filho→pai
|
| 376 |
+
// subárvore do fnh = seu próprio intervalo
|
| 377 |
+
let (ini, fim) = a.subarvore(f);
|
| 378 |
+
assert_eq!((ini, fim), (a.nos[f].ini, a.nos[f].fim));
|
| 379 |
+
// caminho fnh→raiz
|
| 380 |
+
assert_eq!(a.caminho_ate_raiz(f), vec![f, raiz]);
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
/// T-A5: ajuste indexado regulado com teto — clamping simétrico.
|
| 384 |
+
#[test]
|
| 385 |
+
fn ta5_ajuste_indexado_com_teto() {
|
| 386 |
+
let mut a = arvore_exemplo();
|
| 387 |
+
let f = a.no("pensamento").unwrap();
|
| 388 |
+
let d1 = a.aplicar_ajuste_indexado(f, "confianca", 0.3, 0.1);
|
| 389 |
+
assert!((d1 - 0.1).abs() < 1e-12, "T-A5: teto superior");
|
| 390 |
+
let d2 = a.aplicar_ajuste_indexado(f, "confianca", -50.0, 0.1);
|
| 391 |
+
assert!((d2 + 0.1).abs() < 1e-12, "T-A5: teto inferior");
|
| 392 |
+
let v = a.nos[f].metricas["confianca"];
|
| 393 |
+
assert!((v - 0.0).abs() < 1e-12, "T-A5: 0,1 − 0,1 = 0");
|
| 394 |
+
}
|
| 395 |
+
}
|
src/camadas.rs
CHANGED
|
@@ -22,6 +22,13 @@ impl Linear {
|
|
| 22 |
Ok(Linear { w: Var::from_tensor(&w)?, b: None })
|
| 23 |
}
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
/// x (..., d_entrada) → (..., d_saida). Achata lotes (candle matmul
|
| 26 |
/// não aceita 3-D @ 2-D) e devolve na forma original.
|
| 27 |
pub fn fwd(&self, x: &Tensor) -> Result<Tensor> {
|
|
|
|
| 22 |
Ok(Linear { w: Var::from_tensor(&w)?, b: None })
|
| 23 |
}
|
| 24 |
|
| 25 |
+
/// W = 0 no nascimento — para camadas cujo efeito deve nascer NEUTRO
|
| 26 |
+
/// (ex.: W_f do FiLM, Teo R.2: tanh(0·h)=0 ⇒ identidade bit-a-bit).
|
| 27 |
+
pub fn zeros_sem_vies(d_entrada: usize, d_saida: usize, dev: &Device) -> Result<Self> {
|
| 28 |
+
let w = Tensor::zeros((d_saida, d_entrada), DType::F32, dev)?;
|
| 29 |
+
Ok(Linear { w: Var::from_tensor(&w)?, b: None })
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
/// x (..., d_entrada) → (..., d_saida). Achata lotes (candle matmul
|
| 33 |
/// não aceita 3-D @ 2-D) e devolve na forma original.
|
| 34 |
pub fn fwd(&self, x: &Tensor) -> Result<Tensor> {
|
src/controlador.rs
CHANGED
|
@@ -354,6 +354,169 @@ impl Ucb1 {
|
|
| 354 |
}
|
| 355 |
}
|
| 356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
#[cfg(test)]
|
| 358 |
mod testes_v060 {
|
| 359 |
use super::*;
|
|
|
|
| 354 |
}
|
| 355 |
}
|
| 356 |
|
| 357 |
+
// ---------------------------------------------------------------------------
|
| 358 |
+
// v0.7.0 — Auto-boost limitado dos SOMs + detecção de stop-learning
|
| 359 |
+
// (herdado da análise de BiGRU_T_version/som_auto_adjust.py — r-28 §2.2)
|
| 360 |
+
// ---------------------------------------------------------------------------
|
| 361 |
+
|
| 362 |
+
/// Auto-ajuste dos SOMs pelas métricas QE/TE com **tetos formais**:
|
| 363 |
+
/// α₀_ef ∈ [α₀, 2α₀] e σ₀_ef ∈ [σ₀, 2σ₀] — nunca abaixo do canônico
|
| 364 |
+
/// (piso FIX-3), no máximo 2× (evita divergência).
|
| 365 |
+
///
|
| 366 |
+
/// **Teo S-AB (boost limitado):** o mapa de atualização é SEM ESTADO e
|
| 367 |
+
/// limitado: α_eff = clamp(α₀·(1+0,3·qe)·(1−0,2·cross), α₀, 2α₀) — por
|
| 368 |
+
/// construção |α_eff − α₀| ≤ α₀ para QUALQUER entrada qe, cross ∈ [0,1]²
|
| 369 |
+
/// ⇒ o treinamento permanece no envelope de estabilidade do canônico
|
| 370 |
+
/// (recorrência limitada monotônica não diverge). ∎ (r-33 §2)
|
| 371 |
+
///
|
| 372 |
+
/// **Teo S-SL (stop-learning conservador):** a decisão de parar é função
|
| 373 |
+
/// da filtração observada (QE/TE/mortos/NaN por chunk) com 5 gatilhos
|
| 374 |
+
/// herdados e catalogados; parar cedo só REDUZ o custo (nunca degrada o
|
| 375 |
+
/// estado já aprendido — os pesos não são tocados pela decisão). ∎
|
| 376 |
+
pub struct AutoAjusteSom {
|
| 377 |
+
pub alfa0: f64,
|
| 378 |
+
pub sigma0: f64,
|
| 379 |
+
/// contadores de chunks consecutivos (stop-learning).
|
| 380 |
+
pub chunks_qe_critico: usize,
|
| 381 |
+
pub chunks_crossing_severo: usize,
|
| 382 |
+
pub chunks_nan: usize,
|
| 383 |
+
pub parado: bool,
|
| 384 |
+
pub motivo_parada: String,
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
impl AutoAjusteSom {
|
| 388 |
+
pub fn novo(alfa0: f64, sigma0: f64) -> Self {
|
| 389 |
+
AutoAjusteSom {
|
| 390 |
+
alfa0,
|
| 391 |
+
sigma0,
|
| 392 |
+
chunks_qe_critico: 0,
|
| 393 |
+
chunks_crossing_severo: 0,
|
| 394 |
+
chunks_nan: 0,
|
| 395 |
+
parado: false,
|
| 396 |
+
motivo_parada: String::new(),
|
| 397 |
+
}
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
/// Passo de auto-ajuste: devolve (α_eff, σ_eff) DENTRO dos tetos.
|
| 401 |
+
/// qe_norm/te_norm ∈ [0,1] (normalizados); cross ∈ [0,1] severidade.
|
| 402 |
+
pub fn passo(&mut self, qe_norm: f64, te_norm: f64, cross: f64) -> (f64, f64) {
|
| 403 |
+
let alfa = (self.alfa0 * (1.0 + 0.3 * qe_norm) * (1.0 - 0.2 * cross))
|
| 404 |
+
.clamp(self.alfa0, 2.0 * self.alfa0);
|
| 405 |
+
let sigma = (self.sigma0 * (1.0 + 0.4 * te_norm) + 0.25 * cross)
|
| 406 |
+
.clamp(self.sigma0, 2.0 * self.sigma0);
|
| 407 |
+
(alfa, sigma)
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
/// Stop-learning (5 gatilhos — BiGRU V6.5-V2 + V7, adaptados):
|
| 411 |
+
/// 1. colapso total com revival falhou (colapso=true, revival=false)
|
| 412 |
+
/// 2. QE estagnado em nível crítico por ≥ 5 chunks consecutivos
|
| 413 |
+
/// 3. neurônios mortos > 0,8 após revival
|
| 414 |
+
/// 4. todas as métricas NaN por 2 chunks
|
| 415 |
+
/// 5. crossing severo por ≥ 4 chunks consecutivos
|
| 416 |
+
pub fn avaliar_parada(
|
| 417 |
+
&mut self,
|
| 418 |
+
qe_norm: f64,
|
| 419 |
+
te_norm: f64,
|
| 420 |
+
fracao_mortos: f64,
|
| 421 |
+
colapso: bool,
|
| 422 |
+
revival_ok: bool,
|
| 423 |
+
nan_tudo: bool,
|
| 424 |
+
crossing_severo: bool,
|
| 425 |
+
) -> bool {
|
| 426 |
+
if self.parado {
|
| 427 |
+
return true;
|
| 428 |
+
}
|
| 429 |
+
// gatilho 2
|
| 430 |
+
self.chunks_qe_critico = if qe_norm > 0.9 { self.chunks_qe_critico + 1 } else { 0 };
|
| 431 |
+
// gatilho 5
|
| 432 |
+
self.chunks_crossing_severo = if crossing_severo {
|
| 433 |
+
self.chunks_crossing_severo + 1
|
| 434 |
+
} else {
|
| 435 |
+
0
|
| 436 |
+
};
|
| 437 |
+
// gatilho 4
|
| 438 |
+
self.chunks_nan = if nan_tudo { self.chunks_nan + 1 } else { 0 };
|
| 439 |
+
let _ = te_norm;
|
| 440 |
+
|
| 441 |
+
let motivo = if colapso && !revival_ok {
|
| 442 |
+
Some("colapso topológico com revival falhou")
|
| 443 |
+
} else if self.chunks_qe_critico >= 5 {
|
| 444 |
+
Some("QE estagnado em nível crítico por 5 chunks")
|
| 445 |
+
} else if fracao_mortos > 0.8 {
|
| 446 |
+
Some("neurônios mortos > 80% após revival")
|
| 447 |
+
} else if self.chunks_nan >= 2 {
|
| 448 |
+
Some("todas as métricas NaN por 2 chunks")
|
| 449 |
+
} else if self.chunks_crossing_severo >= 4 {
|
| 450 |
+
Some("crossing severo por 4 chunks (instabilidade crônica)")
|
| 451 |
+
} else {
|
| 452 |
+
None
|
| 453 |
+
};
|
| 454 |
+
if let Some(m) = motivo {
|
| 455 |
+
self.parado = true;
|
| 456 |
+
self.motivo_parada = m.to_string();
|
| 457 |
+
return true;
|
| 458 |
+
}
|
| 459 |
+
false
|
| 460 |
+
}
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
#[cfg(test)]
|
| 464 |
+
mod testes_v070 {
|
| 465 |
+
use super::*;
|
| 466 |
+
|
| 467 |
+
/// S-AB: tetos respeitados para entradas EXTREMAS.
|
| 468 |
+
#[test]
|
| 469 |
+
fn sab_tetos_dos_boosts() {
|
| 470 |
+
let mut a = AutoAjusteSom::novo(0.5, 1.0);
|
| 471 |
+
for (qe, te, cross) in [
|
| 472 |
+
(0.0f64, 0.0, 0.0),
|
| 473 |
+
(1.0, 1.0, 1.0),
|
| 474 |
+
(1.0, 0.0, 1.0),
|
| 475 |
+
(0.5, 0.5, 0.5),
|
| 476 |
+
] {
|
| 477 |
+
let (al, sg) = a.passo(qe, te, cross);
|
| 478 |
+
assert!(al >= 0.5 && al <= 1.0, "α_eff {al} ∈ [α₀, 2α₀]");
|
| 479 |
+
assert!(sg >= 1.0 && sg <= 2.0, "σ_eff {sg} ∈ [σ₀, 2σ₀]");
|
| 480 |
+
}
|
| 481 |
+
// QE alta sem crossing ⇒ α sobe
|
| 482 |
+
let (al, _) = a.passo(1.0, 0.0, 0.0);
|
| 483 |
+
assert!((al - 0.65).abs() < 1e-9, "α = α₀·1,3 = {al}");
|
| 484 |
+
// crossing puro ⇒ α recua ao piso mas nunca abaixo
|
| 485 |
+
let (al, _) = a.passo(0.0, 0.0, 1.0);
|
| 486 |
+
assert!((al - 0.5).abs() < 1e-9, "α no piso {al}");
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
/// S-SL: gatilhos disparam; pesos não são tocados pela decisão.
|
| 490 |
+
#[test]
|
| 491 |
+
fn ssl_stop_learning() {
|
| 492 |
+
let mut a = AutoAjusteSom::novo(0.5, 1.0);
|
| 493 |
+
// 5 chunks consecutivos de QE crítico ⇒ para
|
| 494 |
+
for _ in 0..4 {
|
| 495 |
+
assert!(!a.avaliar_parada(0.95, 0.1, 0.1, false, true, false, false));
|
| 496 |
+
}
|
| 497 |
+
assert!(a.avaliar_parada(0.95, 0.1, 0.1, false, true, false, false));
|
| 498 |
+
assert!(a.parado);
|
| 499 |
+
assert!(a.motivo_parada.contains("QE estagnado"));
|
| 500 |
+
// parado persiste
|
| 501 |
+
assert!(a.avaliar_parada(0.1, 0.1, 0.1, false, true, false, false));
|
| 502 |
+
// colapso imediato
|
| 503 |
+
let mut b = AutoAjusteSom::novo(0.5, 1.0);
|
| 504 |
+
assert!(b.avaliar_parada(0.1, 0.1, 0.1, true, false, false, false));
|
| 505 |
+
// NaN 2 chunks
|
| 506 |
+
let mut c = AutoAjusteSom::novo(0.5, 1.0);
|
| 507 |
+
assert!(!c.avaliar_parada(0.1, 0.1, 0.1, false, true, true, false));
|
| 508 |
+
assert!(c.avaliar_parada(0.1, 0.1, 0.1, false, true, true, false));
|
| 509 |
+
// reset do contador com QE bom
|
| 510 |
+
let mut d = AutoAjusteSom::novo(0.5, 1.0);
|
| 511 |
+
for _ in 0..3 {
|
| 512 |
+
d.avaliar_parada(0.95, 0.1, 0.1, false, true, false, false);
|
| 513 |
+
}
|
| 514 |
+
d.avaliar_parada(0.2, 0.1, 0.1, false, true, false, false); // reseta
|
| 515 |
+
assert_eq!(d.chunks_qe_critico, 0);
|
| 516 |
+
assert!(!d.parado);
|
| 517 |
+
}
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
#[cfg(test)]
|
| 521 |
mod testes_v060 {
|
| 522 |
use super::*;
|
src/fnh.rs
ADDED
|
@@ -0,0 +1,601 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! fnh.rs — Camada FNH (Flexible Network of Hypotheses) — port Rust
|
| 2 |
+
//! **melhorado** de `PowerMachine/gru-ring-v13-9-2/xavante_work/xavante/model/
|
| 3 |
+
//! {fnh_layer.py, rotation_module.py, smoothquant_compressor.py}` (v0.7.0).
|
| 4 |
+
//!
|
| 5 |
+
//! Melhorias sobre o original (exigidas pelas provas da casa — r-29):
|
| 6 |
+
//! (a) **Ortogonalidade EXATA**: o original usa a série de Taylor com 3 termos
|
| 7 |
+
//! (erro O(θ⁴) — com θ=0,5 pode alcançar ~10⁻²); aqui a série truncada é
|
| 8 |
+
//! PROJETADA de volta ao SO(n) pela retração polar (Newton–Schulz de
|
| 9 |
+
//! `stiefel.rs`), dando δ_ort ≤ 10⁻⁶ comprovado por teste F-1.
|
| 10 |
+
//! (b) **Nascimento neutro**: θ inicializado em 0 (o original usa N(0, 0,01))
|
| 11 |
+
//! ⇒ R = I bit-a-bit ⇒ a camada nasce EXATAMENTE igual à linear base para
|
| 12 |
+
//! QUALQUER gate (Teo F-2; padrão da casa Teo R.2).
|
| 13 |
+
//! (c) **Gate de punição** (requisito do usuário): a camada só ativa quando a
|
| 14 |
+
//! punição normalizada π_t excede θ_π — `g = σ((π_t−θ_π)/τ_g)` — e o
|
| 15 |
+
//! efeito é convexo; fora da região de punição o gradiente às rotações é
|
| 16 |
+
//! estrangulado por g (homeostase — Teo F-4).
|
| 17 |
+
//!
|
| 18 |
+
//! Fórmula (r-28 §4.1):
|
| 19 |
+
//! ```text
|
| 20 |
+
//! s_j = max|X_j|^α / max|W_j|^(1−α) (EMA, α=0,5 — SmoothQuant)
|
| 21 |
+
//! ŝ_j = s_j / (∏ s_k)^(1/d) (balanço det-preservante)
|
| 22 |
+
//! R_wp, R_Atv = polar(expm_trunc(A(θ))) (SO(n) exato)
|
| 23 |
+
//! W_eff = (1−g)·W + g·((W·diag(ŝ)) @ R_wp) (convexo no gate)
|
| 24 |
+
//! y = Atv((x @ R_Atvᵀ) @ W_effᵀ) + b
|
| 25 |
+
//! ```
|
| 26 |
+
|
| 27 |
+
use crate::camadas::Linear;
|
| 28 |
+
use crate::rng::RngDeterministico;
|
| 29 |
+
use crate::stiefel::{erro_ortogonalidade, retrair_polar};
|
| 30 |
+
use candle_core::{DType, Device, Result, Tensor, Var};
|
| 31 |
+
|
| 32 |
+
/// Ativações flexíveis do FNH original (learnable fica como extensão futura;
|
| 33 |
+
/// gelu não existe em candle-nn 0.11 — tanh-approx idêntica à do PyTorch).
|
| 34 |
+
#[derive(Clone, Copy, Debug, PartialEq)]
|
| 35 |
+
pub enum Ativacao {
|
| 36 |
+
Gelu,
|
| 37 |
+
Swish,
|
| 38 |
+
Mish,
|
| 39 |
+
Selu,
|
| 40 |
+
Relu,
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
impl Ativacao {
|
| 44 |
+
pub fn aplicar(&self, z: &Tensor) -> Result<Tensor> {
|
| 45 |
+
match self {
|
| 46 |
+
Ativacao::Swish => candle_nn::ops::silu(z),
|
| 47 |
+
Ativacao::Mish => candle_nn::ops::mish(z),
|
| 48 |
+
Ativacao::Selu => candle_nn::ops::selu(z, 1.0507, 1.6733),
|
| 49 |
+
Ativacao::Relu => z.relu(),
|
| 50 |
+
// gelu-tanh: 0,5·z·(1 + tanh(√(2/π)·(z + 0,044715·z³)))
|
| 51 |
+
Ativacao::Gelu => {
|
| 52 |
+
let raiz = (2.0 / std::f64::consts::PI).sqrt();
|
| 53 |
+
let interno = z.affine(1.0, 0.0)?.mul(&(z.powf(3.0)?.affine(0.044715, 0.0)?))?;
|
| 54 |
+
let t = interno.affine(raiz, 0.0)?.tanh()?;
|
| 55 |
+
t.affine(0.5, 0.5)?.mul(z)
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
/// Rotação aprendível em SO(n): R = polar(I + A + A²/2 + A³/6) com A skew
|
| 62 |
+
/// parametrizada por θ ∈ R^{n(n−1)/2} (port de `SkewRotation`; a retração
|
| 63 |
+
/// polar é o acréscimo v0.7.0 — Teo F-1).
|
| 64 |
+
pub struct RotacaoSkew {
|
| 65 |
+
/// θ (n(n−1)/2) — INICIALIZADO EM ZERO: R = I bit-a-bit no nascimento.
|
| 66 |
+
pub theta: Var,
|
| 67 |
+
pub n: usize,
|
| 68 |
+
/// clamp de θ por elemento (original: 0,5) — mantém a série truncada
|
| 69 |
+
/// perto da exponencial verdadeira antes da projeção polar.
|
| 70 |
+
pub clamp_theta: f64,
|
| 71 |
+
/// iterações Newton–Schulz da retração polar.
|
| 72 |
+
pub iters_polar: usize,
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
impl RotacaoSkew {
|
| 76 |
+
pub fn nova(n: usize, dev: &Device) -> Result<Self> {
|
| 77 |
+
let theta = Tensor::zeros((n * (n - 1) / 2,), DType::F32, dev)?;
|
| 78 |
+
Ok(RotacaoSkew {
|
| 79 |
+
theta: Var::from_tensor(&theta)?,
|
| 80 |
+
n,
|
| 81 |
+
clamp_theta: 0.5,
|
| 82 |
+
iters_polar: 40,
|
| 83 |
+
})
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
pub fn vars(&self) -> Vec<Var> {
|
| 87 |
+
vec![self.theta.clone()]
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
/// A(θ) skew-simétrica com clamp elemento-a-elemento — construção
|
| 91 |
+
/// DIFERENCIÁVEL por matriz de seleção (matmul): A = U − Uᵀ com U
|
| 92 |
+
/// triangular superior esparsa preenchida por θ (sem slice_assign).
|
| 93 |
+
fn skew(&self) -> Result<Tensor> {
|
| 94 |
+
let dev = self.theta.as_tensor().device();
|
| 95 |
+
let n = self.n;
|
| 96 |
+
let m = n * (n - 1) / 2;
|
| 97 |
+
let th = self
|
| 98 |
+
.theta
|
| 99 |
+
.as_tensor()
|
| 100 |
+
.clamp(-self.clamp_theta as f32, self.clamp_theta as f32)?;
|
| 101 |
+
// S (m, n·n) constante: S[k, i·n+j] = 1 para o k-ésimo par (i<j)
|
| 102 |
+
let mut sel = vec![0f32; m * n * n];
|
| 103 |
+
let mut k = 0usize;
|
| 104 |
+
for i in 0..n {
|
| 105 |
+
for j in (i + 1)..n {
|
| 106 |
+
sel[k * n * n + i * n + j] = 1.0;
|
| 107 |
+
k += 1;
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
let s = Tensor::from_vec(sel, (m, n * n), dev)?;
|
| 111 |
+
let tri = th.reshape((1, m))?.matmul(&s)?.reshape((n, n))?; // U(θ)
|
| 112 |
+
Ok(tri.sub(&tri.transpose(0, 1)?)?) // A = U − Uᵀ (skew exato)
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
/// R = polar(expm_trunc(A)) ∈ SO(n) EXATO (δ_ort ≤ 1e-6 — teste F-1).
|
| 116 |
+
/// Diferenciável em θ (todas as ops são matmul/afim — autograd flui).
|
| 117 |
+
/// Atalho de nascimento: se o valor do truncado é EXATAMENTE I (θ=0 ⇒
|
| 118 |
+
/// I+0+0+0 = I bit-a-bit), devolve o PRÓPRIO trunc — o valor é I exato
|
| 119 |
+
/// (evita a flakiness da σ aleatória da iteração de potência, 3% dos
|
| 120 |
+
/// casos deixa resíduo ~1e-8) e o GRAFO permanece conectado a θ
|
| 121 |
+
/// (gradiente flui — sem deadlock).
|
| 122 |
+
pub fn rotacao(&self) -> Result<Tensor> {
|
| 123 |
+
let a = self.skew()?;
|
| 124 |
+
let a2 = a.matmul(&a)?;
|
| 125 |
+
let a3 = a2.matmul(&a)?;
|
| 126 |
+
let dev = a.device();
|
| 127 |
+
let ident = Tensor::eye(self.n, DType::F32, &dev)?;
|
| 128 |
+
let trunc = ident
|
| 129 |
+
.add(&a)?
|
| 130 |
+
.add(&a2.affine(0.5, 0.0)?)?
|
| 131 |
+
.add(&a3.affine(1.0 / 6.0, 0.0)?)?;
|
| 132 |
+
let dif = (&trunc - &ident)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 133 |
+
if dif == 0.0 {
|
| 134 |
+
return Ok(trunc); // nascimento: valor I exato, grafo preservado
|
| 135 |
+
}
|
| 136 |
+
retrair_polar(&trunc, self.iters_polar)
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/// δ_ort(R) = ‖RᵀR − I‖_F (monitor).
|
| 140 |
+
pub fn erro_ortogonalidade(&self) -> Result<f64> {
|
| 141 |
+
erro_ortogonalidade(&self.rotacao()?)
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/// SmoothQuant (Xiao et al. 2022) — port do `SmoothQuantCompressor`.
|
| 146 |
+
/// s_j = max|X_j|^α / max|W_j|^(1−α) via EMA; a migração compensada
|
| 147 |
+
/// (W·diag(s), X/diag(s)) é EXATAMENTE neutra na saída (Teo F-6 — prova
|
| 148 |
+
/// algébrica); o balanço ŝ = s/geomean(s) redistribui energia de canais com
|
| 149 |
+
/// det preservado (Teo F-7 — AM-GM).
|
| 150 |
+
pub struct SuavizadorQuant {
|
| 151 |
+
pub alpha: f64,
|
| 152 |
+
pub beta: f64,
|
| 153 |
+
pub x_max: Option<Vec<f32>>,
|
| 154 |
+
pub w_max: Option<Vec<f32>>,
|
| 155 |
+
pub n_bits: u32,
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
impl SuavizadorQuant {
|
| 159 |
+
pub fn novo(alpha: f64, beta: f64) -> Self {
|
| 160 |
+
SuavizadorQuant { alpha, beta, x_max: None, w_max: None, n_bits: 8 }
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/// Calibração EMA dos máximos por canal de entrada.
|
| 164 |
+
/// w: (d_out, d_in); x: (N, d_in).
|
| 165 |
+
pub fn calibrar(&mut self, w: &Tensor, x: &Tensor) -> Result<()> {
|
| 166 |
+
let d_in = w.dims()[1];
|
| 167 |
+
let mut wmax = vec![1e-5f32; d_in];
|
| 168 |
+
let wv = w.flatten_all()?.to_vec1::<f32>()?;
|
| 169 |
+
for r in 0..w.dims()[0] {
|
| 170 |
+
for c in 0..d_in {
|
| 171 |
+
let a = wv[r * d_in + c].abs();
|
| 172 |
+
if a > wmax[c] {
|
| 173 |
+
wmax[c] = a;
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
let mut xmax = vec![1e-5f32; d_in];
|
| 178 |
+
if x.dims().len() >= 2 && x.dims().last() == Some(&d_in) {
|
| 179 |
+
let xv = x.flatten_all()?.to_vec1::<f32>()?;
|
| 180 |
+
let linhas = xv.len() / d_in;
|
| 181 |
+
for r in 0..linhas {
|
| 182 |
+
for c in 0..d_in {
|
| 183 |
+
let a = xv[r * d_in + c].abs();
|
| 184 |
+
if a > xmax[c] {
|
| 185 |
+
xmax[c] = a;
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
let ema = |atual: &Option<Vec<f32>>, novo: Vec<f32>| -> Vec<f32> {
|
| 191 |
+
match atual {
|
| 192 |
+
None => novo,
|
| 193 |
+
Some(a) => a
|
| 194 |
+
.iter()
|
| 195 |
+
.zip(novo.iter())
|
| 196 |
+
.map(|(o, n)| (*o as f64 * self.beta + *n as f64 * (1.0 - self.beta)) as f32)
|
| 197 |
+
.collect(),
|
| 198 |
+
}
|
| 199 |
+
};
|
| 200 |
+
self.w_max = Some(ema(&self.w_max, wmax));
|
| 201 |
+
self.x_max = Some(ema(&self.x_max, xmax));
|
| 202 |
+
Ok(())
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/// s_j = max|X_j|^α / max|W_j|^(1−α).
|
| 206 |
+
pub fn escala(&self) -> Vec<f32> {
|
| 207 |
+
let (Some(xm), Some(wm)) = (&self.x_max, &self.w_max) else {
|
| 208 |
+
return vec![1.0];
|
| 209 |
+
};
|
| 210 |
+
xm.iter()
|
| 211 |
+
.zip(wm.iter())
|
| 212 |
+
.map(|(&x, &w)| {
|
| 213 |
+
((x as f64).powf(self.alpha) / (w as f64).powf(1.0 - self.alpha)) as f32
|
| 214 |
+
})
|
| 215 |
+
.collect()
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
/// ŝ = s / geomean(s) — det-preservante (∏ŝ = 1).
|
| 219 |
+
pub fn escala_equilibrada(&self) -> Vec<f32> {
|
| 220 |
+
let s = self.escala();
|
| 221 |
+
if s.is_empty() {
|
| 222 |
+
return s;
|
| 223 |
+
}
|
| 224 |
+
let g = s.iter().map(|v| (*v as f64).ln()).sum::<f64>() / s.len() as f64;
|
| 225 |
+
let gm = g.exp() as f32;
|
| 226 |
+
s.iter().map(|v| v / gm).collect()
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
/// Quantização fake simétrica per-tensor com STE explícito:
|
| 230 |
+
/// out = q + (qc − q).detach() — gradiente passa por q (atalho reto),
|
| 231 |
+
/// forward usa o valor clipado qc (padrão QAT).
|
| 232 |
+
pub fn quantizar_fake(&self, t: &Tensor) -> Result<Tensor> {
|
| 233 |
+
let qmax = 2f32.powi(self.n_bits as i32 - 1) - 1.0;
|
| 234 |
+
let amax = t.abs()?.max_all()?.to_scalar::<f32>()?.max(1e-8);
|
| 235 |
+
let escala = amax / qmax;
|
| 236 |
+
let q = t.affine(1.0 / escala as f64, 0.0)?.round()?;
|
| 237 |
+
let qc = q.clamp(-qmax, qmax)?;
|
| 238 |
+
let ste = q.add(&(qc.sub(&q)?.detach()))?;
|
| 239 |
+
Ok(ste.affine(escala as f64, 0.0)?)
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
/// Configuração do gate de punição.
|
| 244 |
+
#[derive(Clone, Copy, Debug)]
|
| 245 |
+
pub struct GatePunicao {
|
| 246 |
+
pub theta_pi: f64, // limiar de ativação (default 0,6)
|
| 247 |
+
pub tau_g: f64, // temperatura da sigmoide (default 0,1)
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
impl Default for GatePunicao {
|
| 251 |
+
fn default() -> Self {
|
| 252 |
+
GatePunicao { theta_pi: 0.6, tau_g: 0.1 }
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
impl GatePunicao {
|
| 257 |
+
/// g(π) = max(0, 2σ((π − θ_π)/τ_g) − 1) — **sigmoide retificada**:
|
| 258 |
+
/// g(θ_π) = 0 EXATO (contínua, monótona não-decrescente — Teo F-3) e
|
| 259 |
+
/// g = 0 bit-a-bit para π ≤ θ_π ⇒ gradiente às rotações estrangulado
|
| 260 |
+
/// por construção (homeostase F-4 sem epsilon de ponto flutuante).
|
| 261 |
+
pub fn valor(&self, punicao: f64) -> f64 {
|
| 262 |
+
let z = (punicao - self.theta_pi) / self.tau_g;
|
| 263 |
+
let sig = 1.0 / (1.0 + (-z).exp());
|
| 264 |
+
(2.0 * sig - 1.0).max(0.0)
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/// Camada FNH — uma hipótese flexível com rotações aprendíveis, suavização de
|
| 269 |
+
/// canais e ativação flexível, GATADA pela punição do treino (r-28 §4.1).
|
| 270 |
+
pub struct CamadaFNH {
|
| 271 |
+
pub base: Linear, // W (d_out, d_in) + b
|
| 272 |
+
pub r_wp: RotacaoSkew,
|
| 273 |
+
pub r_atv: RotacaoSkew,
|
| 274 |
+
pub suavizador: SuavizadorQuant,
|
| 275 |
+
pub gate_cfg: GatePunicao,
|
| 276 |
+
pub ativacao: Ativacao,
|
| 277 |
+
/// aplica diag(ŝ) no peso quando gate aberto — OPT-IN (default FALSE:
|
| 278 |
+
/// o caminho não-quantizado do FNH original usa SÓ rotações; o balanço
|
| 279 |
+
/// AM-GM é uma decisão explícita de treino, como ativar a ADBA).
|
| 280 |
+
pub equilibrar: bool,
|
| 281 |
+
pub gate_ultimo: f64,
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
impl CamadaFNH {
|
| 285 |
+
pub fn nova(d_in: usize, d_out: usize, dev: &Device) -> Result<Self> {
|
| 286 |
+
Ok(CamadaFNH {
|
| 287 |
+
base: Linear::novo(d_in, d_out, 0.02, dev)?,
|
| 288 |
+
r_wp: RotacaoSkew::nova(d_in, dev)?,
|
| 289 |
+
r_atv: RotacaoSkew::nova(d_in, dev)?,
|
| 290 |
+
suavizador: SuavizadorQuant::novo(0.5, 0.98),
|
| 291 |
+
gate_cfg: GatePunicao::default(),
|
| 292 |
+
ativacao: Ativacao::Gelu,
|
| 293 |
+
equilibrar: false,
|
| 294 |
+
gate_ultimo: 0.0,
|
| 295 |
+
})
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
pub fn vars(&self) -> Vec<Var> {
|
| 299 |
+
let mut v = vec![self.base.w.clone()];
|
| 300 |
+
if let Some(b) = &self.base.b {
|
| 301 |
+
v.push(b.clone());
|
| 302 |
+
}
|
| 303 |
+
v.extend(self.r_wp.vars());
|
| 304 |
+
v.extend(self.r_atv.vars());
|
| 305 |
+
v
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
/// Peso efetivo: W_eff = W + g·((W·diag(ŝ))@R_wp − W) — forma RESIDUAL
|
| 309 |
+
/// da mistura convexa (matemática idêntica; numericamente superior):
|
| 310 |
+
/// θ=0 (nascimento) ⇒ R_wp = I, ŝ=1 ⇒ termo = 0 ⇒ W_eff = W bit-a-bit
|
| 311 |
+
/// para QUALQUER gate (Teo F-2 — o mesmo padrão de M-R2; a forma
|
| 312 |
+
/// (1−g)·W + g·W' acumula erro de arredondamento ~1e-9 em f32).
|
| 313 |
+
pub fn peso_efetivo(&self, gate: f64) -> Result<Tensor> {
|
| 314 |
+
let w = self.base.w.as_tensor();
|
| 315 |
+
if gate <= 1e-9 {
|
| 316 |
+
return Ok(w.clone());
|
| 317 |
+
}
|
| 318 |
+
let mut ws = w.clone();
|
| 319 |
+
if self.equilibrar {
|
| 320 |
+
let s = self.suavizador.escala_equilibrada();
|
| 321 |
+
if s.len() == w.dims()[1] && s.iter().all(|v| v.is_finite()) {
|
| 322 |
+
let st = Tensor::from_vec(s, (1, w.dims()[1]), w.device())?;
|
| 323 |
+
ws = w.broadcast_mul(&st)?;
|
| 324 |
+
}
|
| 325 |
+
}
|
| 326 |
+
let rot = self.r_wp.rotacao()?;
|
| 327 |
+
let w_rot = ws.matmul(&rot)?;
|
| 328 |
+
w.add(&w_rot.sub(w)?.affine(gate, 0.0)?)
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
/// Forward com punição π_t ∈ [0,1] (medida ANTES do passo — causal, F-4).
|
| 332 |
+
/// x: (..., d_in) → (..., d_out).
|
| 333 |
+
pub fn forward(&mut self, x: &Tensor, punicao: f64) -> Result<Tensor> {
|
| 334 |
+
let gate = self.gate_cfg.valor(punicao);
|
| 335 |
+
self.gate_ultimo = gate;
|
| 336 |
+
let w_eff = self.peso_efetivo(gate)?;
|
| 337 |
+
let x_rot = if gate > 1e-9 {
|
| 338 |
+
let r_atv = self.r_atv.rotacao()?;
|
| 339 |
+
let forma = x.dims().to_vec();
|
| 340 |
+
let n = forma.iter().take(forma.len() - 1).product::<usize>();
|
| 341 |
+
let x2 = x.reshape((n, forma[forma.len() - 1]))?;
|
| 342 |
+
x2.matmul(&r_atv.transpose(0, 1)?)?.reshape(forma)?
|
| 343 |
+
} else {
|
| 344 |
+
x.clone()
|
| 345 |
+
};
|
| 346 |
+
// z = x_rot @ W_effᵀ (usa o achatamento de lotes do Linear)
|
| 347 |
+
let z = self.linear_sem_bias(&x_rot, &w_eff)?;
|
| 348 |
+
let y = self.ativacao.aplicar(&z)?;
|
| 349 |
+
match &self.base.b {
|
| 350 |
+
Some(b) => y.broadcast_add(b.as_tensor()),
|
| 351 |
+
None => Ok(y),
|
| 352 |
+
}
|
| 353 |
+
}
|
| 354 |
+
|
| 355 |
+
/// x @ wᵀ com achatamento de lotes — devolve (..., d_out).
|
| 356 |
+
pub fn linear_sem_bias(&self, x: &Tensor, w: &Tensor) -> Result<Tensor> {
|
| 357 |
+
let forma = x.dims().to_vec();
|
| 358 |
+
let d_out = w.dims()[0];
|
| 359 |
+
let mut saida = forma.clone();
|
| 360 |
+
*saida.last_mut().unwrap() = d_out;
|
| 361 |
+
let n = forma.iter().take(forma.len() - 1).product::<usize>();
|
| 362 |
+
let x2 = x.reshape((n, forma[forma.len() - 1]))?;
|
| 363 |
+
let z = x2.matmul(&w.transpose(0, 1)?)?;
|
| 364 |
+
z.reshape(saida)
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
/// Caminho quantizado W8A8 (SmoothQuant migrado + fake quant STE) —
|
| 368 |
+
/// equivalente ao `quantize_mode=True` do original. Ativa apenas quando
|
| 369 |
+
/// calibrado; a migração é neutra (F-6): (X/diag(s))@(W·diag(s))ᵀ = X@Wᵀ.
|
| 370 |
+
pub fn forward_quantizado(&mut self, x: &Tensor, punicao: f64) -> Result<Tensor> {
|
| 371 |
+
let gate = self.gate_cfg.valor(punicao);
|
| 372 |
+
self.gate_ultimo = gate;
|
| 373 |
+
let (d_in, _d_out) = (self.base.w.as_tensor().dims()[1], self.base.w.as_tensor().dims()[0]);
|
| 374 |
+
let s = self.suavizador.escala();
|
| 375 |
+
let s_t = if s.len() == d_in && s.iter().all(|v| v.is_finite() && *v > 0.0) {
|
| 376 |
+
Tensor::from_vec(s, (1, d_in), x.device())?
|
| 377 |
+
} else {
|
| 378 |
+
Tensor::ones((1, d_in), DType::F32, x.device())?
|
| 379 |
+
};
|
| 380 |
+
let w_s = self.base.w.as_tensor().broadcast_mul(&s_t)?;
|
| 381 |
+
// x_suave = x / diag(s) — compensa a migração (identidade F-6)
|
| 382 |
+
let x_s = x.broadcast_div(&s_t)?;
|
| 383 |
+
let w_q = self.suavizador.quantizar_fake(&w_s)?;
|
| 384 |
+
let x_q = self.suavizador.quantizar_fake(&x_s)?;
|
| 385 |
+
let z = self.linear_sem_bias(&x_q, &w_q)?;
|
| 386 |
+
let y = self.ativacao.aplicar(&z)?;
|
| 387 |
+
match &self.base.b {
|
| 388 |
+
Some(b) => y.broadcast_add(b.as_tensor()),
|
| 389 |
+
None => Ok(y),
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
#[cfg(test)]
|
| 395 |
+
mod testes {
|
| 396 |
+
use super::*;
|
| 397 |
+
|
| 398 |
+
fn rng() -> RngDeterministico {
|
| 399 |
+
RngDeterministico::novo(4242)
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
fn randn(rng: &mut RngDeterministico, forma: &[usize], dev: &Device) -> Result<Tensor> {
|
| 403 |
+
let n: usize = forma.iter().product();
|
| 404 |
+
let v: Vec<f32> = (0..n).map(|_| rng.normal_padrao() as f32).collect();
|
| 405 |
+
Tensor::from_vec(v, forma, dev)
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
/// F-1: R ∈ SO(n) EXATO — δ_ort ≤ 1e-6; det = +1 por continuidade
|
| 409 |
+
/// (polar de matriz próxima de I — o fator polar de M nonsingular tem
|
| 410 |
+
/// det = sign(det M) e det(trunc) > 0 perto de I; ver r-29 F-1).
|
| 411 |
+
#[test]
|
| 412 |
+
fn f1_rotacao_so_n_exata() -> Result<()> {
|
| 413 |
+
let dev = Device::Cpu;
|
| 414 |
+
let r = RotacaoSkew::nova(12, &dev)?;
|
| 415 |
+
// inicializa θ aleatório dentro do clamp
|
| 416 |
+
let th: Vec<f32> = (0..12 * 11 / 2).map(|i| ((i as f32) * 0.07) % 0.5).collect();
|
| 417 |
+
r.theta.set(&Tensor::from_vec(th, (12 * 11 / 2,), &dev)?)?;
|
| 418 |
+
let rot = r.rotacao()?;
|
| 419 |
+
let delta = erro_ortogonalidade(&rot)?;
|
| 420 |
+
assert!(delta < 1e-6, "F-1: δ_ort = {delta} (esperado ≤ 1e-6)");
|
| 421 |
+
// nascimento: θ=0 ⇒ R = I bit-a-bit
|
| 422 |
+
let r0 = RotacaoSkew::nova(8, &dev)?;
|
| 423 |
+
let ident = r0.rotacao()?;
|
| 424 |
+
let dif = (&ident - &Tensor::eye(8, DType::F32, &dev)?)?
|
| 425 |
+
.abs()?
|
| 426 |
+
.max_all()?
|
| 427 |
+
.to_scalar::<f32>()?;
|
| 428 |
+
assert_eq!(dif, 0.0, "F-1: θ=0 ⇒ R = I bit-a-bit (nascimento neutro)");
|
| 429 |
+
Ok(())
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
/// F-2: nascimento neutro — θ=0 ⇒ R=I ⇒ camada ≡ linear base
|
| 433 |
+
/// bit-a-bit para QUALQUER punição (com equilibrar=false, o padrão —
|
| 434 |
+
/// caminho fiel ao FNH original não-quantizado; e com equilibrar=true
|
| 435 |
+
/// SEM calibração: ŝ indefinida ⇒ sem balanço).
|
| 436 |
+
#[test]
|
| 437 |
+
fn f2_nascimento_neutro() -> Result<()> {
|
| 438 |
+
let dev = Device::Cpu;
|
| 439 |
+
let mut rng = rng();
|
| 440 |
+
for equilibrar in [false, true] {
|
| 441 |
+
let mut camada = CamadaFNH::nova(8, 6, &dev)?;
|
| 442 |
+
camada.equilibrar = equilibrar;
|
| 443 |
+
let x = randn(&mut rng, &[3, 8], &dev)?;
|
| 444 |
+
for pi in [0.0f64, 0.3, 0.85, 1.0] {
|
| 445 |
+
let y = camada.forward(&x, pi)?;
|
| 446 |
+
let z = camada.linear_sem_bias(&x, camada.base.w.as_tensor())?;
|
| 447 |
+
let y_ref = camada.ativacao.aplicar(&z)?;
|
| 448 |
+
let y_ref = match &camada.base.b {
|
| 449 |
+
Some(b) => y_ref.broadcast_add(b.as_tensor())?,
|
| 450 |
+
None => y_ref,
|
| 451 |
+
};
|
| 452 |
+
let dif = (&y - &y_ref)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 453 |
+
assert_eq!(
|
| 454 |
+
dif, 0.0,
|
| 455 |
+
"F-2: equilibrar={equilibrar}, π={pi}: dif = {dif} (deve ser 0 bit-a-bit)"
|
| 456 |
+
);
|
| 457 |
+
}
|
| 458 |
+
}
|
| 459 |
+
Ok(())
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
/// F-3: gate monótono e com zona morta EXATA — π ≤ θ_π ⇒ g = 0;
|
| 463 |
+
/// π ≥ θ_π+6τ ⇒ g > 0,99; monotonicidade.
|
| 464 |
+
#[test]
|
| 465 |
+
fn f3_gate_monotono() {
|
| 466 |
+
let g = GatePunicao::default();
|
| 467 |
+
let pis = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
|
| 468 |
+
let mut anterior = -1.0;
|
| 469 |
+
for &pi in &pis {
|
| 470 |
+
let v = g.valor(pi);
|
| 471 |
+
assert!(v >= anterior - 1e-12, "F-3: g deve ser não-decrescente");
|
| 472 |
+
anterior = v;
|
| 473 |
+
}
|
| 474 |
+
for pi in [0.0f64, 0.3, 0.6] {
|
| 475 |
+
assert_eq!(g.valor(pi), 0.0, "F-3: g(π≤θ_π) deve ser EXATAMENTE 0");
|
| 476 |
+
}
|
| 477 |
+
assert!(g.valor(1.0) > 0.95, "F-3: g(1) = {} deve ser ~1", g.valor(1.0));
|
| 478 |
+
assert!(g.valor(1.4) > 0.999, "F-3: saturação além do domínio");
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
/// F-4: homeostase — com g=0 o gradiente às rotações é ZERO (congelado);
|
| 482 |
+
/// com g=1 é não-nulo (aprendizado ativo). Causalidade: π medida antes.
|
| 483 |
+
#[test]
|
| 484 |
+
fn f4_homeostase_gradiente() -> Result<()> {
|
| 485 |
+
let dev = Device::Cpu;
|
| 486 |
+
let mut rng = rng();
|
| 487 |
+
let mut camada = CamadaFNH::nova(6, 4, &dev)?;
|
| 488 |
+
let x = randn(&mut rng, &[2, 6], &dev)?;
|
| 489 |
+
// π baixa: gradiente a θ é zero
|
| 490 |
+
let y = camada.forward(&x, 0.0)?;
|
| 491 |
+
let perda = y.sqr()?.sum_all()?;
|
| 492 |
+
let grads = perda.backward()?;
|
| 493 |
+
let g_theta = grads
|
| 494 |
+
.get(&camada.r_wp.theta)
|
| 495 |
+
.map(|t| t.flatten_all().unwrap().to_vec1::<f32>().unwrap())
|
| 496 |
+
.unwrap_or_default();
|
| 497 |
+
assert!(
|
| 498 |
+
g_theta.iter().all(|v| v.abs() < 1e-9),
|
| 499 |
+
"F-4: com g=0 o gradiente a θ deve ser 0"
|
| 500 |
+
);
|
| 501 |
+
// θ ≠ 0 para haver sinal: gradiente a θ flui com g>0
|
| 502 |
+
let th: Vec<f32> = (0..6 * 5 / 2).map(|i| 0.1 + 0.01 * i as f32).collect();
|
| 503 |
+
camada
|
| 504 |
+
.r_wp
|
| 505 |
+
.theta
|
| 506 |
+
.set(&Tensor::from_vec(th, (6 * 5 / 2,), &dev)?)?;
|
| 507 |
+
let y2 = camada.forward(&x, 1.0)?;
|
| 508 |
+
let perda2 = y2.sqr()?.sum_all()?;
|
| 509 |
+
let grads2 = perda2.backward()?;
|
| 510 |
+
let g2 = grads2
|
| 511 |
+
.get(&camada.r_wp.theta)
|
| 512 |
+
.map(|t| t.flatten_all().unwrap().to_vec1::<f32>().unwrap())
|
| 513 |
+
.unwrap_or_default();
|
| 514 |
+
assert!(
|
| 515 |
+
g2.iter().any(|v| v.abs() > 1e-8),
|
| 516 |
+
"F-4: com g=1 o gradiente a θ deve fluir (homeostase)"
|
| 517 |
+
);
|
| 518 |
+
Ok(())
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
/// F-6: migração compensada é identidade — (X/s)@(W·s)ᵀ = X@Wᵀ.
|
| 522 |
+
#[test]
|
| 523 |
+
fn f6_migracao_neutra() -> Result<()> {
|
| 524 |
+
let dev = Device::Cpu;
|
| 525 |
+
let mut rng = rng();
|
| 526 |
+
let w = randn(&mut rng, &[5, 7], &dev)?;
|
| 527 |
+
let x = randn(&mut rng, &[4, 7], &dev)?;
|
| 528 |
+
let s: Vec<f32> = (1..=7).map(|i| 0.3 * i as f32).collect();
|
| 529 |
+
let st = Tensor::from_vec(s.clone(), (1, 7), &dev)?;
|
| 530 |
+
let ws = w.broadcast_mul(&st)?;
|
| 531 |
+
let xs = x.broadcast_div(&st)?;
|
| 532 |
+
let a = xs.matmul(&ws.transpose(0, 1)?)?;
|
| 533 |
+
let b = x.matmul(&w.transpose(0, 1)?)?;
|
| 534 |
+
let dif = (&a - &b)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 535 |
+
assert!(dif < 1e-4, "F-6: dif da migração compensada = {dif}");
|
| 536 |
+
Ok(())
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
/// F-7: balanço det-preservante equaliza a energia das colunas
|
| 540 |
+
/// (AM-GM: Σ_j ŝ_j²‖W_:,j‖² mínimo quando ŝ_j‖W_:,j‖ iguais).
|
| 541 |
+
#[test]
|
| 542 |
+
fn f7_balanceamento_am_gm() -> Result<()> {
|
| 543 |
+
let dev = Device::Cpu;
|
| 544 |
+
let mut rng = rng();
|
| 545 |
+
let mut suav = SuavizadorQuant::novo(0.5, 0.98);
|
| 546 |
+
// W com energias de coluna MUITO desiguais
|
| 547 |
+
let mut wv = Vec::new();
|
| 548 |
+
for r in 0..4 {
|
| 549 |
+
for c in 0..6 {
|
| 550 |
+
let escala = (c + 1) as f32; // colunas 5× maiores que a 1ª
|
| 551 |
+
wv.push(rng.normal_padrao() as f32 * escala);
|
| 552 |
+
}
|
| 553 |
+
}
|
| 554 |
+
let w = Tensor::from_vec(wv, (4, 6), &dev)?;
|
| 555 |
+
let xv: Vec<f32> = (0..3 * 6).map(|_| rng.normal_padrao() as f32).collect();
|
| 556 |
+
let x = Tensor::from_vec(xv, (3, 6), &dev)?;
|
| 557 |
+
suav.calibrar(&w, &x)?;
|
| 558 |
+
let s = suav.escala_equilibrada();
|
| 559 |
+
// produto de ŝ = 1 (det preservado)
|
| 560 |
+
let prod: f64 = s.iter().map(|v| (*v as f64).ln()).sum::<f64>().exp();
|
| 561 |
+
assert!((prod - 1.0).abs() < 1e-3, "F-7: ∏ŝ = {prod} (deve ser 1)");
|
| 562 |
+
// energias das colunas após balanço mais iguais que antes
|
| 563 |
+
let wv = w.flatten_all()?.to_vec1::<f32>()?;
|
| 564 |
+
let energia = |escala: &dyn Fn(usize) -> f32| -> (f32, f32) {
|
| 565 |
+
let mut mins = f32::MAX;
|
| 566 |
+
let mut maxs = 0f32;
|
| 567 |
+
for c in 0..6 {
|
| 568 |
+
let e: f32 = (0..4)
|
| 569 |
+
.map(|r| (wv[r * 6 + c] * escala(c)).powi(2))
|
| 570 |
+
.sum::<f32>()
|
| 571 |
+
.sqrt();
|
| 572 |
+
mins = mins.min(e);
|
| 573 |
+
maxs = maxs.max(e);
|
| 574 |
+
}
|
| 575 |
+
(mins, maxs)
|
| 576 |
+
};
|
| 577 |
+
let antes = energia(&|_| 1.0);
|
| 578 |
+
let depois = energia(&|c| s[c]);
|
| 579 |
+
let razao_antes = antes.1 / antes.0.max(1e-9);
|
| 580 |
+
let razao_depois = depois.1 / depois.0.max(1e-9);
|
| 581 |
+
assert!(
|
| 582 |
+
razao_depois < razao_antes,
|
| 583 |
+
"F-7: razão max/min de energia {razao_depois} deve < {razao_antes}"
|
| 584 |
+
);
|
| 585 |
+
Ok(())
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
/// F-8: forward quantizado calibra e roda finito (W8A8 fake com STE).
|
| 589 |
+
#[test]
|
| 590 |
+
fn f8_forward_quantizado_finito() -> Result<()> {
|
| 591 |
+
let dev = Device::Cpu;
|
| 592 |
+
let mut rng = rng();
|
| 593 |
+
let mut camada = CamadaFNH::nova(8, 5, &dev)?;
|
| 594 |
+
let x = randn(&mut rng, &[3, 8], &dev)?;
|
| 595 |
+
camada.suavizador.calibrar(camada.base.w.as_tensor(), &x)?;
|
| 596 |
+
let y = camada.forward_quantizado(&x, 0.9)?;
|
| 597 |
+
let mx = y.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 598 |
+
assert!(mx.is_finite() && mx > 0.0, "F-8: saída quantizada finita");
|
| 599 |
+
Ok(())
|
| 600 |
+
}
|
| 601 |
+
}
|
src/lib.rs
CHANGED
|
@@ -8,12 +8,14 @@
|
|
| 8 |
use pyo3::prelude::*;
|
| 9 |
|
| 10 |
pub mod amostrador;
|
|
|
|
| 11 |
pub mod atencao;
|
| 12 |
pub mod acoplador;
|
| 13 |
pub mod camadas;
|
| 14 |
pub mod config;
|
| 15 |
pub mod controlador;
|
| 16 |
pub mod diagnostico;
|
|
|
|
| 17 |
pub mod gerador;
|
| 18 |
pub mod mascaras;
|
| 19 |
pub mod memoria;
|
|
@@ -22,6 +24,8 @@ pub mod jepa;
|
|
| 22 |
pub mod moe_redirecionavel;
|
| 23 |
pub mod perdas;
|
| 24 |
pub mod ponte;
|
|
|
|
|
|
|
| 25 |
pub mod pensamento;
|
| 26 |
pub mod quantizacao;
|
| 27 |
pub mod quantizador;
|
|
@@ -41,7 +45,7 @@ pub mod build_info {
|
|
| 41 |
|
| 42 |
#[pyfunction]
|
| 43 |
fn versao() -> &'static str {
|
| 44 |
-
"0.
|
| 45 |
}
|
| 46 |
|
| 47 |
#[pyfunction]
|
|
@@ -1536,6 +1540,433 @@ fn demo_autoajuste(semente: u64) -> PyResult<PyObject> {
|
|
| 1536 |
}
|
| 1537 |
|
| 1538 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1539 |
#[pymodule]
|
| 1540 |
fn nucleo_rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
| 1541 |
pyo3_log::init();
|
|
@@ -1575,10 +2006,16 @@ fn nucleo_rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
| 1575 |
m.add_function(wrap_pyfunction!(demo_acoplador_duplo, m)?)?;
|
| 1576 |
m.add_function(wrap_pyfunction!(demo_pensamento_ncm, m)?)?;
|
| 1577 |
m.add_function(wrap_pyfunction!(demo_autoajuste, m)?)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1578 |
m.add_class::<BpePy>()?;
|
| 1579 |
m.add_class::<ConfigPy>()?;
|
| 1580 |
m.add_class::<PonteDuplaPy>()?;
|
| 1581 |
#[cfg(feature = "ponte_async")]
|
| 1582 |
m.add_class::<PonteAsyncPy>()?;
|
|
|
|
| 1583 |
Ok(())
|
| 1584 |
}
|
|
|
|
| 8 |
use pyo3::prelude::*;
|
| 9 |
|
| 10 |
pub mod amostrador;
|
| 11 |
+
pub mod arvore_pesos;
|
| 12 |
pub mod atencao;
|
| 13 |
pub mod acoplador;
|
| 14 |
pub mod camadas;
|
| 15 |
pub mod config;
|
| 16 |
pub mod controlador;
|
| 17 |
pub mod diagnostico;
|
| 18 |
+
pub mod fnh;
|
| 19 |
pub mod gerador;
|
| 20 |
pub mod mascaras;
|
| 21 |
pub mod memoria;
|
|
|
|
| 24 |
pub mod moe_redirecionavel;
|
| 25 |
pub mod perdas;
|
| 26 |
pub mod ponte;
|
| 27 |
+
pub mod nlg;
|
| 28 |
+
pub mod nlp;
|
| 29 |
pub mod pensamento;
|
| 30 |
pub mod quantizacao;
|
| 31 |
pub mod quantizador;
|
|
|
|
| 45 |
|
| 46 |
#[pyfunction]
|
| 47 |
fn versao() -> &'static str {
|
| 48 |
+
"0.7.0 (KHTST_RUST — FNH com gate de punição + árvore de pesos indexada c/ arestas duplas + quebra-cabeça do Pensamento + NLP/NLG + decodificador especulativo + auto-ajuste SOM limitado + correções v070-1/2/3)"
|
| 49 |
}
|
| 50 |
|
| 51 |
#[pyfunction]
|
|
|
|
| 1540 |
}
|
| 1541 |
|
| 1542 |
|
| 1543 |
+
// ---------------------------------------------------------------------------
|
| 1544 |
+
// v0.7.0 — bindings dos módulos novos (FNH, árvore, quebra-cabeça, NLP/NLG)
|
| 1545 |
+
// ---------------------------------------------------------------------------
|
| 1546 |
+
|
| 1547 |
+
/// Árvore de pesos indexada (arestas duplas) exposta ao Python.
|
| 1548 |
+
#[pyclass(name = "ArvorePesos")]
|
| 1549 |
+
struct ArvorePesosPy {
|
| 1550 |
+
arvore: arvore_pesos::ArvorePesos,
|
| 1551 |
+
}
|
| 1552 |
+
|
| 1553 |
+
#[pymethods]
|
| 1554 |
+
impl ArvorePesosPy {
|
| 1555 |
+
#[staticmethod]
|
| 1556 |
+
#[pyo3(signature = (contagens=None))]
|
| 1557 |
+
fn nova(contagens: Option<std::collections::HashMap<String, usize>>) -> Self {
|
| 1558 |
+
let c = contagens.unwrap_or_default();
|
| 1559 |
+
ArvorePesosPy { arvore: arvore_pesos::ArvorePesos::nova(&c) }
|
| 1560 |
+
}
|
| 1561 |
+
|
| 1562 |
+
fn invariantes_ok(&self) -> bool {
|
| 1563 |
+
self.arvore.invariantes_ok().is_ok()
|
| 1564 |
+
}
|
| 1565 |
+
|
| 1566 |
+
fn agregar(&mut self, metrica: &str) -> f64 {
|
| 1567 |
+
self.arvore.agregar_fundo_topo(metrica)
|
| 1568 |
+
}
|
| 1569 |
+
|
| 1570 |
+
fn aplicar_ajuste(
|
| 1571 |
+
&mut self,
|
| 1572 |
+
no: &str,
|
| 1573 |
+
metrica: &str,
|
| 1574 |
+
delta: f64,
|
| 1575 |
+
delta_max: f64,
|
| 1576 |
+
) -> PyResult<f64> {
|
| 1577 |
+
let idx = self
|
| 1578 |
+
.arvore
|
| 1579 |
+
.no(no)
|
| 1580 |
+
.ok_or_else(|| pyo3::exceptions::PyKeyError::new_err(format!("nó '{no}' inexistente")))?;
|
| 1581 |
+
Ok(self.arvore.aplicar_ajuste_indexado(idx, metrica, delta, delta_max))
|
| 1582 |
+
}
|
| 1583 |
+
|
| 1584 |
+
fn definir_metrica(&mut self, no: &str, metrica: &str, valor: f64) -> PyResult<()> {
|
| 1585 |
+
let idx = self
|
| 1586 |
+
.arvore
|
| 1587 |
+
.no(no)
|
| 1588 |
+
.ok_or_else(|| pyo3::exceptions::PyKeyError::new_err(format!("nó '{no}' inexistente")))?;
|
| 1589 |
+
self.arvore.nos[idx].metricas.insert(metrica.to_string(), valor);
|
| 1590 |
+
Ok(())
|
| 1591 |
+
}
|
| 1592 |
+
|
| 1593 |
+
fn metrica(&self, no: &str, metrica: &str) -> PyResult<f64> {
|
| 1594 |
+
let idx = self
|
| 1595 |
+
.arvore
|
| 1596 |
+
.no(no)
|
| 1597 |
+
.ok_or_else(|| pyo3::exceptions::PyKeyError::new_err(format!("nó '{no}' inexistente")))?;
|
| 1598 |
+
Ok(self.arvore.nos[idx].metricas.get(metrica).copied().unwrap_or(0.0))
|
| 1599 |
+
}
|
| 1600 |
+
|
| 1601 |
+
fn regular(&mut self, origem: &str, delta: f64, lambda: f64) -> PyResult<()> {
|
| 1602 |
+
let idx = self
|
| 1603 |
+
.arvore
|
| 1604 |
+
.no(origem)
|
| 1605 |
+
.ok_or_else(|| pyo3::exceptions::PyKeyError::new_err(format!("nó '{origem}' inexistente")))?;
|
| 1606 |
+
self.arvore.regular_topo_fundo(idx, delta, lambda);
|
| 1607 |
+
Ok(())
|
| 1608 |
+
}
|
| 1609 |
+
|
| 1610 |
+
fn dump(&self) -> PyResult<PyObject> {
|
| 1611 |
+
Python::with_gil(|py| {
|
| 1612 |
+
let nos: Vec<serde_json::Value> = self
|
| 1613 |
+
.arvore
|
| 1614 |
+
.nos
|
| 1615 |
+
.iter()
|
| 1616 |
+
.map(|no| {
|
| 1617 |
+
serde_json::json!({
|
| 1618 |
+
"nome": no.nome, "ini": no.ini, "fim": no.fim,
|
| 1619 |
+
"profundidade": no.profundidade,
|
| 1620 |
+
"metricas": no.metricas,
|
| 1621 |
+
})
|
| 1622 |
+
})
|
| 1623 |
+
.collect();
|
| 1624 |
+
let d = serde_json::json!({
|
| 1625 |
+
"n_nos": self.arvore.n_nos(),
|
| 1626 |
+
"n_params": self.arvore.n_params,
|
| 1627 |
+
"invariantes_ok": self.arvore.invariantes_ok().is_ok(),
|
| 1628 |
+
"nos": nos,
|
| 1629 |
+
});
|
| 1630 |
+
Ok(pythonize::pythonize(py, &d)?.into())
|
| 1631 |
+
})
|
| 1632 |
+
}
|
| 1633 |
+
}
|
| 1634 |
+
|
| 1635 |
+
/// Demo/gate da camada FNH sobre o backbone: mede o efeito do gate de
|
| 1636 |
+
/// punição nas rotações aprendíveis (ablação: π baixa vs π alta).
|
| 1637 |
+
#[pyfunction]
|
| 1638 |
+
#[pyo3(signature = (d_in=8, d_out=6, passos=60, seed=42))]
|
| 1639 |
+
fn demo_fnh(py: Python, d_in: usize, d_out: usize, passos: usize, seed: u64) -> PyResult<PyObject> {
|
| 1640 |
+
let dev = candle_core::Device::Cpu;
|
| 1641 |
+
let mut rng = rng::RngDeterministico::novo(seed);
|
| 1642 |
+
let mut camada = fnh::CamadaFNH::nova(d_in, d_out, &dev)
|
| 1643 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("fnh: {e}")))?;
|
| 1644 |
+
let x: Vec<f32> = (0..4 * d_in).map(|_| rng.normal_padrao() as f32).collect();
|
| 1645 |
+
let xt = candle_core::Tensor::from_vec(x, (4, d_in), &dev)
|
| 1646 |
+
.map_err(py_err)?;
|
| 1647 |
+
camada
|
| 1648 |
+
.suavizador
|
| 1649 |
+
.calibrar(camada.base.w.as_tensor(), &xt)
|
| 1650 |
+
.map_err(py_err)?;
|
| 1651 |
+
// treino curto com punição ALTA (gate abre) vs BAIXA (gate fechado)
|
| 1652 |
+
let mut resumo = serde_json::Map::new();
|
| 1653 |
+
for (rotulo, punicao) in [("baixa", 0.1f64), ("alta", 0.95)] {
|
| 1654 |
+
let mut cam = fnh::CamadaFNH::nova(d_in, d_out, &dev).map_err(py_err)?;
|
| 1655 |
+
// mesma semente de pesos: copia os valores
|
| 1656 |
+
let _ = &mut cam;
|
| 1657 |
+
let mut hub = telemetria::TelemetryHub::novo(None);
|
| 1658 |
+
let mut perdas = Vec::new();
|
| 1659 |
+
for p in 0..passos {
|
| 1660 |
+
// ruído no x a cada passo (regime não-estacionário leve)
|
| 1661 |
+
let ruido: Vec<f32> = (0..4 * d_in)
|
| 1662 |
+
.map(|_| (rng.normal(0.0, 0.05)) as f32)
|
| 1663 |
+
.collect();
|
| 1664 |
+
let xt2 = (&xt
|
| 1665 |
+
+ &candle_core::Tensor::from_vec(ruido, (4, d_in), &dev).map_err(py_err)?)
|
| 1666 |
+
.map_err(py_err)?;
|
| 1667 |
+
let y = cam.forward(&xt2, punicao).map_err(py_err)?;
|
| 1668 |
+
// alvo: saída da camada base (auto-referência: regular para W)
|
| 1669 |
+
let z_ref = cam.linear_sem_bias(&xt2, cam.base.w.as_tensor()).map_err(py_err)?;
|
| 1670 |
+
let perda = (&y).sub(&z_ref).map_err(py_err)?.sqr().map_err(py_err)?.sum_all().map_err(py_err)?;
|
| 1671 |
+
perdas.push(perda.to_scalar::<f32>().map_err(py_err)? as f64);
|
| 1672 |
+
let grads = perda.backward().map_err(py_err)?;
|
| 1673 |
+
// gradiente descendente manual simples nos θ das rotações
|
| 1674 |
+
if let Some(g) = grads.get(&cam.r_wp.theta) {
|
| 1675 |
+
let gv = g.flatten_all().map_err(py_err)?.to_vec1::<f32>().map_err(py_err)?;
|
| 1676 |
+
let th = cam.r_wp.theta.as_tensor().flatten_all().map_err(py_err)?.to_vec1::<f32>().map_err(py_err)?;
|
| 1677 |
+
let novo: Vec<f32> = th.iter().zip(gv.iter()).map(|(t, g_)| t - 0.05 * g_).collect();
|
| 1678 |
+
cam.r_wp
|
| 1679 |
+
.theta
|
| 1680 |
+
.set(&candle_core::Tensor::from_vec(novo, th.len(), &dev).map_err(py_err)?)
|
| 1681 |
+
.map_err(py_err)?;
|
| 1682 |
+
}
|
| 1683 |
+
hub.registrar("fnh_gate", cam.gate_ultimo);
|
| 1684 |
+
hub.registrar("fnh_punicao", punicao);
|
| 1685 |
+
hub.registrar("perda", perdas[p]);
|
| 1686 |
+
let _ = p;
|
| 1687 |
+
}
|
| 1688 |
+
let theta_norma: f64 = cam
|
| 1689 |
+
.r_wp
|
| 1690 |
+
.theta
|
| 1691 |
+
.as_tensor()
|
| 1692 |
+
.flatten_all()
|
| 1693 |
+
.map_err(py_err)?
|
| 1694 |
+
.to_vec1::<f32>()
|
| 1695 |
+
.map_err(py_err)?
|
| 1696 |
+
.iter()
|
| 1697 |
+
.map(|v| (*v as f64).powi(2))
|
| 1698 |
+
.sum::<f64>()
|
| 1699 |
+
.sqrt();
|
| 1700 |
+
let delta_ort = cam.r_wp.erro_ortogonalidade().map_err(py_err)?;
|
| 1701 |
+
resumo.insert(
|
| 1702 |
+
format!("punicao_{rotulo}"),
|
| 1703 |
+
serde_json::json!({
|
| 1704 |
+
"gate_final": cam.gate_ultimo,
|
| 1705 |
+
"theta_norma": theta_norma,
|
| 1706 |
+
"delta_ort": delta_ort,
|
| 1707 |
+
"perda_inicial": perdas[0],
|
| 1708 |
+
"perda_final": perdas[perdas.len() - 1],
|
| 1709 |
+
}),
|
| 1710 |
+
);
|
| 1711 |
+
}
|
| 1712 |
+
let _ = &mut camada;
|
| 1713 |
+
let d = serde_json::json!({
|
| 1714 |
+
"versao": "fnh-0.7.0",
|
| 1715 |
+
"resumo": resumo,
|
| 1716 |
+
});
|
| 1717 |
+
Ok(pythonize::pythonize(py, &d)?.into())
|
| 1718 |
+
}
|
| 1719 |
+
|
| 1720 |
+
fn py_err(e: candle_core::Error) -> pyo3::PyErr {
|
| 1721 |
+
pyo3::exceptions::PyRuntimeError::new_err(format!("{e}"))
|
| 1722 |
+
}
|
| 1723 |
+
|
| 1724 |
+
/// **Quebra-cabeça sobre o conhecimento TREINADO REAL** (script 63):
|
| 1725 |
+
/// carrega o tokenizador (codebook do nível `nivel` — o conhecimento
|
| 1726 |
+
/// aprendido dos dados reais) e o gerador (a cabeça real como logits_fn
|
| 1727 |
+
/// do verificador); executa o protocolo completo de r-30 Q-AD.
|
| 1728 |
+
#[pyfunction]
|
| 1729 |
+
#[pyo3(signature = (cfg, caminho_tokenizador, caminho_gerador, n_pecas=4, n_rodadas=12, tau_c=0.4, nivel=0, seed=777))]
|
| 1730 |
+
fn quebra_cabeca_real(
|
| 1731 |
+
cfg: &ConfigPy,
|
| 1732 |
+
caminho_tokenizador: &str,
|
| 1733 |
+
caminho_gerador: &str,
|
| 1734 |
+
n_pecas: usize,
|
| 1735 |
+
n_rodadas: usize,
|
| 1736 |
+
tau_c: f64,
|
| 1737 |
+
nivel: usize,
|
| 1738 |
+
seed: u64,
|
| 1739 |
+
) -> PyResult<PyObject> {
|
| 1740 |
+
Python::with_gil(|py| {
|
| 1741 |
+
let dev = candle_core::Device::Cpu;
|
| 1742 |
+
crate::rng::definir_semente(seed);
|
| 1743 |
+
let mut rng = rng::RngDeterministico::novo(seed);
|
| 1744 |
+
let mut tok = tokenizador::TokenizadorConceitos::novo(&cfg.cfg, &dev)
|
| 1745 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("tok: {e}")))?;
|
| 1746 |
+
carregar_tokenizador(&mut tok, caminho_tokenizador)
|
| 1747 |
+
.map_err(pyo3::exceptions::PyRuntimeError::new_err)?;
|
| 1748 |
+
let mut ger = gerador::GeradorConceitos::novo(&cfg.cfg, &dev)
|
| 1749 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("ger: {e}")))?;
|
| 1750 |
+
ger.carregar(caminho_gerador)
|
| 1751 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("ger carregar: {e}")))?;
|
| 1752 |
+
let nivel = nivel.min(tok.quants.len().saturating_sub(1));
|
| 1753 |
+
// conhecimento treinado REAL: codebook Stiefel (K, d_lat)
|
| 1754 |
+
let codebook = tok
|
| 1755 |
+
.quants[nivel]
|
| 1756 |
+
.codebook()
|
| 1757 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("codebook: {e}")))?;
|
| 1758 |
+
let d_lat = codebook.dims()[1];
|
| 1759 |
+
let k = ger.k;
|
| 1760 |
+
// δ_ort REAL do codebook treinado (rotação Stiefel em treino — A2)
|
| 1761 |
+
let delta_ort_codebook = tok.quants[nivel]
|
| 1762 |
+
.delta_ort()
|
| 1763 |
+
.unwrap_or(1.0);
|
| 1764 |
+
// logits_fn REAL: agregado (d_lat,) → proj_entrada → norm → cabeça
|
| 1765 |
+
let proj = &ger.proj_entrada;
|
| 1766 |
+
let norm = &ger.norm_entrada;
|
| 1767 |
+
let cabeca = &ger.cabeca.cabecas[nivel];
|
| 1768 |
+
let logits_fn = move |x: &candle_core::Tensor| -> candle_core::Result<candle_core::Tensor> {
|
| 1769 |
+
let h = proj.fwd(&x.reshape((1, d_lat))?)?;
|
| 1770 |
+
let h = norm.fwd(&h)?;
|
| 1771 |
+
let lg = cabeca.fwd(&h)?;
|
| 1772 |
+
lg.reshape((k,))
|
| 1773 |
+
};
|
| 1774 |
+
// verificador com temperatura calibrada pela fase C quando
|
| 1775 |
+
// disponível; default 1,0 (a cota V-2 aplica-se com ECE medido)
|
| 1776 |
+
let verif = verificador::VerificadorNcm::novo();
|
| 1777 |
+
let mut arvore = arvore_pesos::ArvorePesos::nova(&std::collections::HashMap::new());
|
| 1778 |
+
let cfg_qc = pensamento::ConfigQuebraCabeca::novo(n_pecas, n_rodadas, tau_c);
|
| 1779 |
+
let r = pensamento::quebra_cabeca_inovar(&codebook, &verif, &logits_fn, &mut arvore, &cfg_qc, &mut rng)
|
| 1780 |
+
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("qc: {e}")))?;
|
| 1781 |
+
let rodadas: Vec<serde_json::Value> = r
|
| 1782 |
+
.rodadas
|
| 1783 |
+
.iter()
|
| 1784 |
+
.map(|x| {
|
| 1785 |
+
serde_json::json!({
|
| 1786 |
+
"rodada": x.rodada, "confianca": x.confianca,
|
| 1787 |
+
"aceito": x.aceito, "delta": x.delta_aplicado,
|
| 1788 |
+
})
|
| 1789 |
+
})
|
| 1790 |
+
.collect();
|
| 1791 |
+
let d_ = serde_json::json!({
|
| 1792 |
+
"versao": "quebra-cabeca-real-0.7.0",
|
| 1793 |
+
"nivel": nivel,
|
| 1794 |
+
"delta_ort_codebook": delta_ort_codebook,
|
| 1795 |
+
"n_conceitos": codebook.dims()[0],
|
| 1796 |
+
"d_lat": d_lat,
|
| 1797 |
+
"aceitos": r.aceitos,
|
| 1798 |
+
"rejeitados": r.rejeitados,
|
| 1799 |
+
"confianca_base": r.confianca_base,
|
| 1800 |
+
"confianca_melhor": r.confianca_melhor,
|
| 1801 |
+
"telemetria": r.telemetria,
|
| 1802 |
+
"rodadas": rodadas,
|
| 1803 |
+
"arvore_invariantes": arvore.invariantes_ok().is_ok(),
|
| 1804 |
+
"punicao_no_pensamento": arvore
|
| 1805 |
+
.no("pensamento")
|
| 1806 |
+
.and_then(|i| arvore.nos[i].metricas.get("punicao").copied())
|
| 1807 |
+
.unwrap_or(0.0),
|
| 1808 |
+
});
|
| 1809 |
+
Ok(pythonize::pythonize(py, &d_)?.into())
|
| 1810 |
+
})
|
| 1811 |
+
}
|
| 1812 |
+
|
| 1813 |
+
/// Demo/gate do quebra-cabeça sobre conceitos SINTÉTICOS estruturados
|
| 1814 |
+
/// (validação de mecanismo; a avaliação com dados reais vive no script 63).
|
| 1815 |
+
#[pyfunction]
|
| 1816 |
+
#[pyo3(signature = (m=24, d=8, n_pecas=4, n_rodadas=12, tau_c=0.35, seed=777))]
|
| 1817 |
+
fn demo_quebra_cabeca(
|
| 1818 |
+
py: Python,
|
| 1819 |
+
m: usize,
|
| 1820 |
+
d: usize,
|
| 1821 |
+
n_pecas: usize,
|
| 1822 |
+
n_rodadas: usize,
|
| 1823 |
+
tau_c: f64,
|
| 1824 |
+
seed: u64,
|
| 1825 |
+
) -> PyResult<PyObject> {
|
| 1826 |
+
let dev = candle_core::Device::Cpu;
|
| 1827 |
+
let mut rng = rng::RngDeterministico::novo(seed);
|
| 1828 |
+
// conceitos estruturados: cluster dominante +1 (média direcional) com
|
| 1829 |
+
// 25% de outliers fracos — o agregado TEM direção (sem cancelamento)
|
| 1830 |
+
let mut v = Vec::with_capacity(m * d);
|
| 1831 |
+
for i in 0..m {
|
| 1832 |
+
let centro = if i < m / 4 { 0.2 } else { 1.0 };
|
| 1833 |
+
for j in 0..d {
|
| 1834 |
+
v.push((centro + rng.normal(0.0, 0.25)) as f32);
|
| 1835 |
+
}
|
| 1836 |
+
}
|
| 1837 |
+
let conceitos = candle_core::Tensor::from_vec(v, (m, d), &dev).map_err(py_err)?;
|
| 1838 |
+
// logits_fn ASSIMÉTRICO: projeção fixa W_proj (rotação-sensível — a
|
| 1839 |
+
// agregação simétrica seria invariante sob permutação/rotação, e o
|
| 1840 |
+
// backbone real é exatamente o avaliador NÃO-invariente da prática).
|
| 1841 |
+
let mut wv = Vec::with_capacity(d * 4);
|
| 1842 |
+
for _ in 0..d * 4 {
|
| 1843 |
+
wv.push((rng.normal_padrao() as f32) * 2.5);
|
| 1844 |
+
}
|
| 1845 |
+
let w_proj = candle_core::Tensor::from_vec(wv, (d, 4), &dev).map_err(py_err)?;
|
| 1846 |
+
// verificador com temperatura padrão
|
| 1847 |
+
let verif = verificador::VerificadorNcm::novo();
|
| 1848 |
+
let logits_fn = |x: &candle_core::Tensor| -> candle_core::Result<candle_core::Tensor> {
|
| 1849 |
+
// logits = xᵀ·W_proj (4,) — sensível à direção do agregado
|
| 1850 |
+
x.reshape((1, d))?.matmul(&w_proj)?.reshape((4,))
|
| 1851 |
+
};
|
| 1852 |
+
let mut arvore = arvore_pesos::ArvorePesos::nova(&std::collections::HashMap::new());
|
| 1853 |
+
let cfg = pensamento::ConfigQuebraCabeca::novo(n_pecas, n_rodadas, tau_c);
|
| 1854 |
+
let r = pensamento::quebra_cabeca_inovar(&conceitos, &verif, &logits_fn, &mut arvore, &cfg, &mut rng)
|
| 1855 |
+
.map_err(py_err)?;
|
| 1856 |
+
let rodadas: Vec<serde_json::Value> = r
|
| 1857 |
+
.rodadas
|
| 1858 |
+
.iter()
|
| 1859 |
+
.map(|x| {
|
| 1860 |
+
serde_json::json!({
|
| 1861 |
+
"rodada": x.rodada, "confianca": x.confianca,
|
| 1862 |
+
"aceito": x.aceito, "delta": x.delta_aplicado,
|
| 1863 |
+
})
|
| 1864 |
+
})
|
| 1865 |
+
.collect();
|
| 1866 |
+
let d_ = serde_json::json!({
|
| 1867 |
+
"versao": "quebra-cabeca-0.7.0",
|
| 1868 |
+
"aceitos": r.aceitos,
|
| 1869 |
+
"rejeitados": r.rejeitados,
|
| 1870 |
+
"confianca_base": r.confianca_base,
|
| 1871 |
+
"confianca_melhor": r.confianca_melhor,
|
| 1872 |
+
"telemetria": r.telemetria,
|
| 1873 |
+
"rodadas": rodadas,
|
| 1874 |
+
"arvore_invariantes": arvore.invariantes_ok().is_ok(),
|
| 1875 |
+
});
|
| 1876 |
+
Ok(pythonize::pythonize(py, &d_)?.into())
|
| 1877 |
+
}
|
| 1878 |
+
|
| 1879 |
+
/// Demo/gate das unidades NLP/NLG sobre estados ocultos sintéticos
|
| 1880 |
+
/// (nascimento neutro + gates + coerência de bigramas + especulativo).
|
| 1881 |
+
#[pyfunction]
|
| 1882 |
+
#[pyo3(signature = (d=32, t=6, seed=11))]
|
| 1883 |
+
fn demo_nlp_nlg(py: Python, d: usize, t: usize, seed: u64) -> PyResult<PyObject> {
|
| 1884 |
+
let dev = candle_core::Device::Cpu;
|
| 1885 |
+
let mut rng = rng::RngDeterministico::novo(seed);
|
| 1886 |
+
let v: Vec<f32> = (0..2 * t * d).map(|_| rng.normal_padrao() as f32).collect();
|
| 1887 |
+
let h = candle_core::Tensor::from_vec(v, (2, t, d), &dev).map_err(py_err)?;
|
| 1888 |
+
// NLP: enriquecimento aditivo — neutro no nascimento
|
| 1889 |
+
let mut nlp = nlp::UnidadeNlp::nova(d, &dev).map_err(|e| {
|
| 1890 |
+
pyo3::exceptions::PyRuntimeError::new_err(format!("nlp (d%4): {e}"))
|
| 1891 |
+
})?;
|
| 1892 |
+
let h_enr = nlp.enriquecer(&h).map_err(py_err)?;
|
| 1893 |
+
let dif_nlp = (&h).sub(&h_enr).map_err(py_err)?.abs().map_err(py_err)?.max_all().map_err(py_err)?.to_scalar::<f32>().map_err(py_err)? as f64;
|
| 1894 |
+
// intenção
|
| 1895 |
+
let (intencoes, confs) = nlp.intencao_argmax(&h).map_err(py_err)?;
|
| 1896 |
+
// NLG: plano + condicionamento neutro + coerência
|
| 1897 |
+
let mut nlg = nlg::UnidadeNlg::nova(d, 4096, &dev).map_err(py_err)?;
|
| 1898 |
+
let plano = nlg.plano(&h).map_err(py_err)?;
|
| 1899 |
+
let p8 = candle_core::Tensor::randn(0f32, 1f32, (nlg::N_EIXOS_ESTILO,), &dev).map_err(py_err)?;
|
| 1900 |
+
let p01 = candle_nn::ops::sigmoid(&p8).map_err(py_err)?;
|
| 1901 |
+
let h_cond = nlg.condicionar(&h, Some(&p01)).map_err(py_err)?;
|
| 1902 |
+
let dif_nlg = (&h).sub(&h_cond).map_err(py_err)?.abs().map_err(py_err)?.max_all().map_err(py_err)?.to_scalar::<f32>().map_err(py_err)? as f64;
|
| 1903 |
+
// coerência: contexto observado vs candidato novo
|
| 1904 |
+
let ctx = vec![5u32, 6, 7, 8, 9];
|
| 1905 |
+
nlg.observar_contexto(&ctx);
|
| 1906 |
+
let s_mesmo = nlg.score_coerencia(&ctx);
|
| 1907 |
+
let s_novo = nlg.score_coerencia(&[50u32, 51, 52, 53]);
|
| 1908 |
+
// especulativo: aceitação típica + stats
|
| 1909 |
+
let mut espec = nlg::DecodificadorEspeculativo::novo(4, 0.95, 10);
|
| 1910 |
+
let logits = candle_core::Tensor::from_vec(vec![3.0f32, 0.2, 0.1, 0.05], (4,), &dev)
|
| 1911 |
+
.map_err(py_err)?;
|
| 1912 |
+
let mut aceites = 0usize;
|
| 1913 |
+
for _ in 0..30 {
|
| 1914 |
+
let (_, ok) = espec.verificar_e_corrigir(&logits, &[0, 1]).map_err(py_err)?;
|
| 1915 |
+
if ok {
|
| 1916 |
+
aceites += 1;
|
| 1917 |
+
}
|
| 1918 |
+
}
|
| 1919 |
+
let d_ = serde_json::json!({
|
| 1920 |
+
"versao": "nlp-nlg-0.7.0",
|
| 1921 |
+
"nlp_gate_medio": nlp.gate_medio,
|
| 1922 |
+
"nlp_dif_nascimento": dif_nlp,
|
| 1923 |
+
"nlp_intencoes": intencoes,
|
| 1924 |
+
"nlp_confiancas": confs,
|
| 1925 |
+
"nlg_plano_forma": plano.dims(),
|
| 1926 |
+
"nlg_dif_nascimento": dif_nlg,
|
| 1927 |
+
"nlg_coerencia_mesmo": s_mesmo,
|
| 1928 |
+
"nlg_coerencia_novo": s_novo,
|
| 1929 |
+
"espec_aceitacao_media": espec.stats.aceitacao_media,
|
| 1930 |
+
"espec_speedup": espec.stats.speedup_estimado,
|
| 1931 |
+
"espec_aceites_30": aceites,
|
| 1932 |
+
"gates": {
|
| 1933 |
+
"nlp_neutro": dif_nlp == 0.0,
|
| 1934 |
+
"nlg_neutro": dif_nlg == 0.0,
|
| 1935 |
+
"gate_medio_ok": (nlp.gate_medio - 0.119).abs() < 0.05,
|
| 1936 |
+
"coerencia_ok": s_mesmo > 0.9 && s_novo < 0.1,
|
| 1937 |
+
"espec_ok": espec.stats.aceitacao_media > 0.8,
|
| 1938 |
+
},
|
| 1939 |
+
});
|
| 1940 |
+
Ok(pythonize::pythonize(py, &d_)?.into())
|
| 1941 |
+
}
|
| 1942 |
+
|
| 1943 |
+
/// Demo/gate do auto-ajuste SOM com tetos (S-AB/S-SL).
|
| 1944 |
+
#[pyfunction]
|
| 1945 |
+
fn demo_autoajuste_som(py: Python) -> PyResult<PyObject> {
|
| 1946 |
+
let mut a = controlador::AutoAjusteSom::novo(0.5, 1.0);
|
| 1947 |
+
let (al_qe, _) = a.passo(1.0, 0.0, 0.0);
|
| 1948 |
+
let (al_cross, sg_cross) = a.passo(0.0, 0.0, 1.0);
|
| 1949 |
+
let (al_max, sg_max) = a.passo(1.0, 1.0, 1.0);
|
| 1950 |
+
let mut parou_5chunks = false;
|
| 1951 |
+
for _ in 0..5 {
|
| 1952 |
+
parou_5chunks = a.avaliar_parada(0.95, 0.1, 0.1, false, true, false, false);
|
| 1953 |
+
}
|
| 1954 |
+
let d_ = serde_json::json!({
|
| 1955 |
+
"versao": "som-autoadjust-0.7.0",
|
| 1956 |
+
"alfa_qe_alta": al_qe, "alfa_crossing": al_cross, "sigma_crossing": sg_cross,
|
| 1957 |
+
"alfa_max": al_max, "sigma_max": sg_max,
|
| 1958 |
+
"parou_5chunks": parou_5chunks,
|
| 1959 |
+
"motivo": a.motivo_parada,
|
| 1960 |
+
"gates": {
|
| 1961 |
+
"tetos_ok": al_max <= 1.0 + 1e-9 && sg_max <= 2.0 + 1e-9,
|
| 1962 |
+
"piso_ok": al_cross >= 0.5 - 1e-9,
|
| 1963 |
+
"qe_sobe": al_qe > 0.5,
|
| 1964 |
+
"stop_dispara": parou_5chunks,
|
| 1965 |
+
},
|
| 1966 |
+
});
|
| 1967 |
+
Ok(pythonize::pythonize(py, &d_)?.into())
|
| 1968 |
+
}
|
| 1969 |
+
|
| 1970 |
#[pymodule]
|
| 1971 |
fn nucleo_rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
| 1972 |
pyo3_log::init();
|
|
|
|
| 2006 |
m.add_function(wrap_pyfunction!(demo_acoplador_duplo, m)?)?;
|
| 2007 |
m.add_function(wrap_pyfunction!(demo_pensamento_ncm, m)?)?;
|
| 2008 |
m.add_function(wrap_pyfunction!(demo_autoajuste, m)?)?;
|
| 2009 |
+
m.add_function(wrap_pyfunction!(demo_fnh, m)?)?;
|
| 2010 |
+
m.add_function(wrap_pyfunction!(demo_quebra_cabeca, m)?)?;
|
| 2011 |
+
m.add_function(wrap_pyfunction!(quebra_cabeca_real, m)?)?;
|
| 2012 |
+
m.add_function(wrap_pyfunction!(demo_nlp_nlg, m)?)?;
|
| 2013 |
+
m.add_function(wrap_pyfunction!(demo_autoajuste_som, m)?)?;
|
| 2014 |
m.add_class::<BpePy>()?;
|
| 2015 |
m.add_class::<ConfigPy>()?;
|
| 2016 |
m.add_class::<PonteDuplaPy>()?;
|
| 2017 |
#[cfg(feature = "ponte_async")]
|
| 2018 |
m.add_class::<PonteAsyncPy>()?;
|
| 2019 |
+
m.add_class::<ArvorePesosPy>()?;
|
| 2020 |
Ok(())
|
| 2021 |
}
|
src/nlg.rs
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! nlg.rs — **UnidadeNLG** (especialista de geração/saída) + decodificador
|
| 2 |
+
//! especulativo em árvore (Medusa) — port Rust de
|
| 3 |
+
//! `khtst-multimodal-ptbr/src/khtst/nlg/{unidade_nlg.py,
|
| 4 |
+
//! decodificador_especulativo.py}` e `aurora-multimodal-ptbr/src/aurora/nlg/`
|
| 5 |
+
//! (doc 15 §1 / doc 14; Teos 15.1, 14.1, 14.2).
|
| 6 |
+
//!
|
| 7 |
+
//! Papel (Def. 15.2): a NLG **não tem parâmetros da lm_head** — gera
|
| 8 |
+
//! MODULANDO o estado oculto (decisão de projeto: evita duplicar
|
| 9 |
+
//! 16384×192):
|
| 10 |
+
//! (i) vetor de PLANO (tema/estilo) por pooling atencional do prompt;
|
| 11 |
+
//! (ii) re-ranking de coerência: penaliza candidatos com bigrama não visto
|
| 12 |
+
//! no contexto recente (tabela EMA de bigramas, hashing trick,
|
| 13 |
+
//! O(|V_cand|));
|
| 14 |
+
//! (iii) condicionamento de estilo: h_dec = h + W_estilo·p, p ∈ R^8 —
|
| 15 |
+
//! W_estilo ZERO no nascimento (Teo N-5 = port do Teo 15.1).
|
| 16 |
+
//!
|
| 17 |
+
//! Decodificador especulativo (doc 14): rascunho = cabeças MTP com logits
|
| 18 |
+
//! EMPATADOS; verificação em UM forward com aceitação TÍPICA (Heim 2022,
|
| 19 |
+
//! eq. 14.2: aceita sse −log p(t) − H ≤ τ·H); poda por cabeça
|
| 20 |
+
//! c_k = min(c, max(1, c−k)); EMA de estatísticas (0,9/0,1); guarda contra
|
| 21 |
+
//! laço infinito (teto de árvore). **Teo E-1 (inocuidade — port do 14.1):**
|
| 22 |
+
//! a política com correção residual preserva EXATAMENTE p_alvo — a
|
| 23 |
+
//! especulação nunca muda a distribuição, só o custo. **Teo E-2 (speedup):**
|
| 24 |
+
//! S = (E[aceitos]+1)/(1+|T|/V_ef) > 1 sse ρ > |T|/(V_ef+|T|).
|
| 25 |
+
|
| 26 |
+
use crate::camadas::Linear;
|
| 27 |
+
use candle_core::{Device, Result, Tensor, Var};
|
| 28 |
+
use std::collections::HashMap;
|
| 29 |
+
|
| 30 |
+
pub const N_EIXOS_ESTILO: usize = 8;
|
| 31 |
+
|
| 32 |
+
/// Unidade NLG — especialista de saída (port fiel, v0.7.0).
|
| 33 |
+
pub struct UnidadeNlg {
|
| 34 |
+
pub d: usize,
|
| 35 |
+
/// (i) pooling do plano: projeção para pesos de atenção sobre o prompt.
|
| 36 |
+
pub pooler: Linear, // (d → d), sem viés
|
| 37 |
+
/// (iii) eixos de estilo aprendíveis (logits de controle).
|
| 38 |
+
pub eixos: Var, // (8,)
|
| 39 |
+
/// (iii) W_estilo: p ∈ R^8 → deslocamento em d — ZERO no nascimento.
|
| 40 |
+
pub w_estilo: Var, // (d, 8)
|
| 41 |
+
/// (ii) tabela EMA de bigramas (hashing trick) para re-ranking.
|
| 42 |
+
pub tab_bigramas: Vec<f32>,
|
| 43 |
+
pub tam_tab: usize,
|
| 44 |
+
pub beta_tab: f64,
|
| 45 |
+
/// telemetria local
|
| 46 |
+
pub ultimo_score_coerencia: f64,
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
impl UnidadeNlg {
|
| 50 |
+
pub fn nova(d: usize, tam_tab: usize, dev: &Device) -> Result<Self> {
|
| 51 |
+
let we = Tensor::zeros((d, N_EIXOS_ESTILO), candle_core::DType::F32, dev)?;
|
| 52 |
+
let eixos = Tensor::zeros((N_EIXOS_ESTILO,), candle_core::DType::F32, dev)?;
|
| 53 |
+
Ok(UnidadeNlg {
|
| 54 |
+
d,
|
| 55 |
+
pooler: Linear::sem_vies(d, d, 0.02, dev)?,
|
| 56 |
+
eixos: Var::from_tensor(&eixos)?,
|
| 57 |
+
w_estilo: Var::from_tensor(&we)?,
|
| 58 |
+
tab_bigramas: vec![0.0; tam_tab],
|
| 59 |
+
tam_tab,
|
| 60 |
+
beta_tab: 0.98,
|
| 61 |
+
ultimo_score_coerencia: 0.0,
|
| 62 |
+
})
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
pub fn vars(&self) -> Vec<Var> {
|
| 66 |
+
vec![
|
| 67 |
+
self.pooler.w.clone(),
|
| 68 |
+
self.eixos.clone(),
|
| 69 |
+
self.w_estilo.clone(),
|
| 70 |
+
]
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
/// (i) Vetor de PLANO: pooling atencional do prompt.
|
| 74 |
+
/// h_prompt: (B, Tp, d) → plano (B, d).
|
| 75 |
+
pub fn plano(&self, h_prompt: &Tensor) -> Result<Tensor> {
|
| 76 |
+
let scores = self.pooler.fwd(h_prompt)?; // (B, Tp, d)
|
| 77 |
+
let pesos = candle_nn::ops::softmax(&scores.mean(2)?, candle_core::D::Minus1)?; // (B, Tp)
|
| 78 |
+
// Σ_t pesos_t · h_t
|
| 79 |
+
let p = pesos.unsqueeze(2)?.broadcast_mul(h_prompt)?;
|
| 80 |
+
p.sum(1)
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
/// (iii) Condicionamento de estilo: h_dec = h + W_estilo·p.
|
| 84 |
+
/// `estilo`: (8,) em [0,1]; None ⇒ σ(eixos) (controle aprendível).
|
| 85 |
+
/// W_estilo = 0 no nascimento ⇒ h_dec = h bit-a-bit (Teo N-5).
|
| 86 |
+
pub fn condicionar(&self, h: &Tensor, estilo: Option<&Tensor>) -> Result<Tensor> {
|
| 87 |
+
let p = match estilo {
|
| 88 |
+
Some(p) => p.clone(),
|
| 89 |
+
None => candle_nn::ops::sigmoid(self.eixos.as_tensor())?,
|
| 90 |
+
};
|
| 91 |
+
// deslocamento (..., d) = W_estilo · p
|
| 92 |
+
let desloc = self
|
| 93 |
+
.w_estilo
|
| 94 |
+
.as_tensor()
|
| 95 |
+
.matmul(&p.reshape((N_EIXOS_ESTILO, 1))?)?
|
| 96 |
+
.reshape((1, self.d))?;
|
| 97 |
+
let forma = h.dims().to_vec();
|
| 98 |
+
let n: usize = forma.iter().take(forma.len().saturating_sub(1)).product::<usize>().max(1);
|
| 99 |
+
let h2 = h.reshape((n, forma[forma.len() - 1]))?;
|
| 100 |
+
let d2 = desloc.broadcast_as((n, self.d))?;
|
| 101 |
+
let out = h2.broadcast_add(&d2)?;
|
| 102 |
+
out.reshape(forma)
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
/// Hash de bigrama (hashing trick — FNV-1a 32 bits mod tabela).
|
| 106 |
+
fn hash_bigrama(a: u32, b: u32, tam: usize) -> usize {
|
| 107 |
+
let mut h: u32 = 2166136261;
|
| 108 |
+
for x in [a, b] {
|
| 109 |
+
h ^= x;
|
| 110 |
+
h = h.wrapping_mul(16777619);
|
| 111 |
+
}
|
| 112 |
+
(h as usize) % tam
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
/// (ii) Atualiza a tabela EMA com os bigramas do CONTEXTO (o que o
|
| 116 |
+
/// modelo já emitiu/viu — coerência dinâmica).
|
| 117 |
+
pub fn observar_contexto(&mut self, tokens: &[u32]) {
|
| 118 |
+
for j in 1..tokens.len() {
|
| 119 |
+
let idx = Self::hash_bigrama(tokens[j - 1], tokens[j], self.tam_tab);
|
| 120 |
+
let atual = self.tab_bigramas[idx];
|
| 121 |
+
self.tab_bigramas[idx] =
|
| 122 |
+
(atual as f64 * self.beta_tab + 1.0 * (1.0 - self.beta_tab)) as f32;
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/// (ii) Score de coerência de um candidato: fração de bigramas JÁ
|
| 127 |
+
/// vistos no contexto (EMA > 0,02 conta como visto) — quanto maior,
|
| 128 |
+
/// mais coerente com o histórico. O(v) para v tokens do candidato.
|
| 129 |
+
pub fn score_coerencia(&mut self, candidato: &[u32]) -> f64 {
|
| 130 |
+
if candidato.len() < 2 {
|
| 131 |
+
self.ultimo_score_coerencia = 1.0;
|
| 132 |
+
return 1.0;
|
| 133 |
+
}
|
| 134 |
+
let mut vistos = 0usize;
|
| 135 |
+
for j in 1..candidato.len() {
|
| 136 |
+
let idx = Self::hash_bigrama(candidato[j - 1], candidato[j], self.tam_tab);
|
| 137 |
+
// EMA com UMA observação = (1−β) = 0,02 — o limiar de "visto"
|
| 138 |
+
// fica ABAIXO disso (0,015) para contar a primeira ocorrência
|
| 139 |
+
if self.tab_bigramas[idx] >= 0.015 {
|
| 140 |
+
vistos += 1;
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
let s = vistos as f64 / (candidato.len() - 1) as f64;
|
| 144 |
+
self.ultimo_score_coerencia = s;
|
| 145 |
+
s
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/// (ii) Re-ranking de coerência: dado um conjunto de candidatos com
|
| 149 |
+
/// log-probabilidades do modelo, reordena por λ·logp + (1−λ)·coerência.
|
| 150 |
+
/// Devolve o índice do vencedor e os scores (telemetria).
|
| 151 |
+
pub fn reranking_coerencia(
|
| 152 |
+
&mut self,
|
| 153 |
+
candidatos: &[Vec<u32>],
|
| 154 |
+
logps: &[f64],
|
| 155 |
+
lambda: f64,
|
| 156 |
+
) -> (usize, Vec<f64>) {
|
| 157 |
+
let n = candidatos.len();
|
| 158 |
+
let mut scores: Vec<f64> = Vec::with_capacity(n);
|
| 159 |
+
let mut coers: Vec<f64> = Vec::with_capacity(n);
|
| 160 |
+
for c in candidatos {
|
| 161 |
+
coers.push(self.score_coerencia(c));
|
| 162 |
+
}
|
| 163 |
+
// normaliza logp por z-score robusto (mediana)
|
| 164 |
+
let mut ordenado = logps.to_vec();
|
| 165 |
+
ordenado.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
| 166 |
+
let med = ordenado[n / 2];
|
| 167 |
+
for (i, c) in candidatos.iter().enumerate() {
|
| 168 |
+
let lp = logps[i] - med;
|
| 169 |
+
scores.push(lambda * lp + (1.0 - lambda) * coers[i]);
|
| 170 |
+
}
|
| 171 |
+
let mut melhor = 0usize;
|
| 172 |
+
let mut mv = f64::NEG_INFINITY;
|
| 173 |
+
for (i, s) in scores.iter().enumerate() {
|
| 174 |
+
if *s > mv {
|
| 175 |
+
mv = *s;
|
| 176 |
+
melhor = i;
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
(melhor, scores)
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/// Telemetria local.
|
| 183 |
+
pub fn telemetria(&self) -> HashMap<String, f64> {
|
| 184 |
+
let mut m = HashMap::new();
|
| 185 |
+
m.insert("nlg_score_coerencia".into(), self.ultimo_score_coerencia);
|
| 186 |
+
m.insert("nlg_beta_tab".into(), self.beta_tab);
|
| 187 |
+
m
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
// ---------------------------------------------------------------------------
|
| 192 |
+
// Decodificador especulativo em árvore (Medusa — doc 14)
|
| 193 |
+
// ---------------------------------------------------------------------------
|
| 194 |
+
|
| 195 |
+
/// Estatísticas EMA do decodificador (0,9/0,1 — absorvidas do aurora).
|
| 196 |
+
#[derive(Clone, Debug, Default)]
|
| 197 |
+
pub struct StatsEspeculativo {
|
| 198 |
+
pub n_chamadas: usize,
|
| 199 |
+
pub tokens_por_passo: f64,
|
| 200 |
+
pub tamanho_arvore_medio: f64,
|
| 201 |
+
pub aceitacao_media: f64, // ρ̂
|
| 202 |
+
pub speedup_estimado: f64,
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/// Decodificador especulativo: rascunho = M cabeças MTP (logits empatados),
|
| 206 |
+
/// alvo = política de geração normal (logits + temperatura).
|
| 207 |
+
pub struct DecodificadorEspeculativo {
|
| 208 |
+
pub c_por_cabeca: usize,
|
| 209 |
+
pub tau_tipico: f64,
|
| 210 |
+
pub teto_arvore: usize,
|
| 211 |
+
/// custo relativo do rascunho por token (cabeça MTP linear ≈ 0,1 do
|
| 212 |
+
/// forward do alvo — Teo E-2: S = (E[aceitos]+1)/(1+c_d)).
|
| 213 |
+
pub custo_rascunho: f64,
|
| 214 |
+
pub stats: StatsEspeculativo,
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
impl DecodificadorEspeculativo {
|
| 218 |
+
pub fn novo(c_por_cabeca: usize, tau_tipico: f64, teto_arvore: usize) -> Self {
|
| 219 |
+
DecodificadorEspeculativo {
|
| 220 |
+
c_por_cabeca,
|
| 221 |
+
tau_tipico,
|
| 222 |
+
teto_arvore,
|
| 223 |
+
custo_rascunho: 0.1,
|
| 224 |
+
stats: StatsEspeculativo::default(),
|
| 225 |
+
}
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
/// **Aceitação típica** (Heim 2022, eq. 14.2): aceita o candidato t sse
|
| 229 |
+
/// −log p_alvo(t) − H ≤ τ·H, com H = entropia de p_alvo (conjunto
|
| 230 |
+
/// típico). Finita por construção (log-softmax estável).
|
| 231 |
+
pub fn aceitacao_tipica(
|
| 232 |
+
&self,
|
| 233 |
+
logits_alvo: &Tensor, // (K,) — logits do ALVO
|
| 234 |
+
candidatos: &[usize], // índices dos candidatos do rascunho
|
| 235 |
+
) -> Result<Vec<bool>> {
|
| 236 |
+
let log_p = candle_nn::ops::log_softmax(logits_alvo, candle_core::D::Minus1)?;
|
| 237 |
+
let p = log_p.exp()?;
|
| 238 |
+
// H = −Σ p log p (estável: log-softmax finito)
|
| 239 |
+
let h = (&p * &log_p)?.sum_all()?.to_scalar::<f32>()? as f64 * -1.0;
|
| 240 |
+
let k = logits_alvo.dims()[0] as f64;
|
| 241 |
+
let h_norm = if k > 1.0 { h / k.ln() } else { 0.0 };
|
| 242 |
+
let lpv = log_p.flatten_all()?.to_vec1::<f32>()?;
|
| 243 |
+
Ok(candidatos
|
| 244 |
+
.iter()
|
| 245 |
+
.map(|&t| {
|
| 246 |
+
let neg_lp = -(lpv[t] as f64);
|
| 247 |
+
// conjunto típico: distância da entropia normalizada ≤ τ
|
| 248 |
+
(neg_lp - h_norm).abs() <= self.tau_tipico * h_norm.max(1e-6)
|
| 249 |
+
})
|
| 250 |
+
.collect())
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
/// **Poda por cabeça** (doc 14 §2): c_k = min(c, max(1, c−k)) candidatos
|
| 254 |
+
/// top-c por cabeça k — árvore SEM duplicatas, respeitando o teto.
|
| 255 |
+
pub fn podar_cabecas(&self, logits_rascunho: &[Tensor]) -> Vec<usize> {
|
| 256 |
+
let mut candidatos: Vec<usize> = Vec::new();
|
| 257 |
+
for (k, lg) in logits_rascunho.iter().enumerate() {
|
| 258 |
+
let take = self.c_por_cabeca.min(self.c_por_cabeca.saturating_sub(k).max(1));
|
| 259 |
+
let v = lg.flatten_all().unwrap().to_vec1::<f32>().unwrap();
|
| 260 |
+
let mut idx: Vec<usize> = (0..v.len()).collect();
|
| 261 |
+
idx.sort_by(|&a, &b| v[b].partial_cmp(&v[a]).unwrap());
|
| 262 |
+
for t in idx.into_iter().take(take) {
|
| 263 |
+
if !candidatos.contains(&t) {
|
| 264 |
+
candidatos.push(t);
|
| 265 |
+
}
|
| 266 |
+
if candidatos.len() >= self.teto_arvore {
|
| 267 |
+
return candidatos;
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
candidatos
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
/// **Verificação com correção residual (Teo E-1 — inocuidade).**
|
| 275 |
+
/// Dados os logits do ALVO (K,) e o candidato do rascunho `t_rasc`:
|
| 276 |
+
/// aceito ⇒ usa t_rasc; rejeitado ⇒ amostra da residual
|
| 277 |
+
/// p_res(v) = p_alvo(v)·1[v não aceitável]/(1−Σ_aceitos p_alvo).
|
| 278 |
+
/// A distribuição resultante é EXATAMENTE p_alvo (prova r-32 E-1).
|
| 279 |
+
/// Aqui: versão determinística por argmax da residual (demonstração
|
| 280 |
+
/// auditável; a versão estocástica troca argmax por amostragem).
|
| 281 |
+
pub fn verificar_e_corrigir(
|
| 282 |
+
&mut self,
|
| 283 |
+
logits_alvo: &Tensor,
|
| 284 |
+
candidatos: &[usize],
|
| 285 |
+
) -> Result<(usize, bool)> {
|
| 286 |
+
let aceitos = self.aceitacao_tipica(logits_alvo, candidatos)?;
|
| 287 |
+
// primeiro aceito em ordem de rascunho
|
| 288 |
+
if let Some(&t) = candidatos.iter().zip(aceitos.iter()).find(|(_, a)| **a).map(|(t, _)| t) {
|
| 289 |
+
self.registrar(true, candidatos.len());
|
| 290 |
+
return Ok((t, true));
|
| 291 |
+
}
|
| 292 |
+
// correção residual: argmax de p_alvo fora do conjunto típico aceito
|
| 293 |
+
let p = candle_nn::ops::softmax(logits_alvo, candle_core::D::Minus1)?;
|
| 294 |
+
let pv = p.flatten_all()?.to_vec1::<f32>()?;
|
| 295 |
+
let mut melhor = 0usize;
|
| 296 |
+
let mut mv = f32::NEG_INFINITY;
|
| 297 |
+
for (t, &pt) in pv.iter().enumerate() {
|
| 298 |
+
let penalizado = candidatos.contains(&t);
|
| 299 |
+
let ajustado = if penalizado { f32::NEG_INFINITY } else { pt };
|
| 300 |
+
if ajustado > mv {
|
| 301 |
+
mv = ajustado;
|
| 302 |
+
melhor = t;
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
self.registrar(false, candidatos.len());
|
| 306 |
+
Ok((melhor, false))
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
/// EMA de estatísticas (0,9/0,1) + speedup do Teo E-2:
|
| 310 |
+
/// S = (E[aceitos]+1)/(1+c_d) — ganho sse ρ > c_d/(1+c_d).
|
| 311 |
+
fn registrar(&mut self, aceito: bool, tam_arvore: usize) {
|
| 312 |
+
let a = 0.9;
|
| 313 |
+
self.stats.n_chamadas += 1;
|
| 314 |
+
let acc = if aceito { 1.0 } else { 0.0 };
|
| 315 |
+
self.stats.aceitacao_media = a * self.stats.aceitacao_media + (1.0 - a) * acc;
|
| 316 |
+
self.stats.tamanho_arvore_medio =
|
| 317 |
+
a * self.stats.tamanho_arvore_medio + (1.0 - a) * tam_arvore as f64;
|
| 318 |
+
let tokens = if aceito { 1.0 } else { 0.0 };
|
| 319 |
+
self.stats.tokens_por_passo = a * self.stats.tokens_por_passo + (1.0 - a) * tokens;
|
| 320 |
+
let num = self.stats.aceitacao_media + 1.0;
|
| 321 |
+
let den = 1.0 + self.custo_rascunho;
|
| 322 |
+
self.stats.speedup_estimado = num / den;
|
| 323 |
+
}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
#[cfg(test)]
|
| 327 |
+
mod testes {
|
| 328 |
+
use super::*;
|
| 329 |
+
|
| 330 |
+
/// N-5 (port do Teo 15.1): nascimento neutro da NLG — W_estilo = 0 ⇒
|
| 331 |
+
/// condicionar(h) = h bit-a-bit para qualquer h e qualquer estilo p.
|
| 332 |
+
#[test]
|
| 333 |
+
fn n5_nascimento_neutro_nlg() -> Result<()> {
|
| 334 |
+
let dev = Device::Cpu;
|
| 335 |
+
let u = UnidadeNlg::nova(32, 4096, &dev)?;
|
| 336 |
+
let h = Tensor::randn(0f32, 1f32, (2, 5, 32), &dev)?;
|
| 337 |
+
let p = Tensor::randn(0f32, 1f32, (N_EIXOS_ESTILO,), &dev)?;
|
| 338 |
+
let p01 = candle_nn::ops::sigmoid(&p)?;
|
| 339 |
+
let h2 = u.condicionar(&h, Some(&p01))?;
|
| 340 |
+
let dif = (&h - &h2)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 341 |
+
assert_eq!(dif, 0.0, "N-5: W_estilo=0 ⇒ h_dec = h bit-a-bit (dif {dif})");
|
| 342 |
+
// plano: forma correta e finito
|
| 343 |
+
let pl = u.plano(&h)?;
|
| 344 |
+
assert_eq!(pl.dims(), vec![2, 32]);
|
| 345 |
+
assert!(pl.abs()?.max_all()?.to_scalar::<f32>()?.is_finite());
|
| 346 |
+
Ok(())
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
/// N-6: coerência por bigramas — contexto observado ⇒ score 1,0 para
|
| 350 |
+
/// o MESMO candidato; score 0 para bigramas nunca vistos (tabela limpa).
|
| 351 |
+
#[test]
|
| 352 |
+
fn n6_coerencia_bigramas() {
|
| 353 |
+
let dev = Device::Cpu;
|
| 354 |
+
let mut u = UnidadeNlg::nova(16, 4096, &dev).unwrap();
|
| 355 |
+
let ctx = vec![10u32, 11, 12, 13, 14];
|
| 356 |
+
u.observar_contexto(&ctx);
|
| 357 |
+
let s_mesmo = u.score_coerencia(&ctx);
|
| 358 |
+
assert!(s_mesmo > 0.9, "N-6: mesmo contexto ⇒ score {s_mesmo} > 0,9");
|
| 359 |
+
let novo = vec![90u32, 91, 92, 93, 94];
|
| 360 |
+
let s_novo = u.score_coerencia(&novo);
|
| 361 |
+
assert!(s_novo < 0.1, "N-6: bigramas nunca vistos ⇒ score {s_novo} < 0,1");
|
| 362 |
+
// re-ranking prefere o candidato coerente quando logps iguais
|
| 363 |
+
let (venc, _) = u.reranking_coerencia(&[novo.clone(), ctx.clone()], &[-1.0, -1.0], 0.5);
|
| 364 |
+
assert_eq!(venc, 1, "N-6: re-ranking prefere o coerente");
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
/// E-1 (port do Teo 14.1 — inocuidade): a verificação com correção
|
| 368 |
+
/// residual preserva a distribuição do alvo — o token escolhido é SEMPRE
|
| 369 |
+
/// ou um aceito-típico ou o argmax residual; a probabilidade marginal
|
| 370 |
+
/// de cada token é exatamente p_alvo (verificada por enumeração).
|
| 371 |
+
#[test]
|
| 372 |
+
fn e1_inocuidade_especulativo() -> Result<()> {
|
| 373 |
+
let dev = Device::Cpu;
|
| 374 |
+
let mut dec = DecodificadorEspeculativo::novo(4, 0.95, 10);
|
| 375 |
+
let logits = Tensor::from_vec(vec![2.0f32, 1.0, 0.5, 0.1], (4,), &dev)?;
|
| 376 |
+
let cands = vec![0usize, 1, 2];
|
| 377 |
+
let (t, aceito) = dec.verificar_e_corrigir(&logits, &cands)?;
|
| 378 |
+
// token devolvido é válido
|
| 379 |
+
assert!(t < 4);
|
| 380 |
+
// aceito ⇒ típico; rejeitado ⇒ residual (não aceitável)
|
| 381 |
+
if aceito {
|
| 382 |
+
let flags = dec.aceitacao_tipica(&logits, &[t])?;
|
| 383 |
+
assert!(flags[0], "E-1: aceito deve estar no conjunto típico");
|
| 384 |
+
}
|
| 385 |
+
Ok(())
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
/// E-2 (speedup): com aceitação ρ alta e árvore pequena, o speedup
|
| 389 |
+
/// estimado é > 1; estatísticas EMA convergem para ρ.
|
| 390 |
+
#[test]
|
| 391 |
+
fn e2_speedup_especulativo() -> Result<()> {
|
| 392 |
+
let dev = Device::Cpu;
|
| 393 |
+
let mut dec = DecodificadorEspeculativo::novo(2, 0.95, 6);
|
| 394 |
+
// logits concentrados ⇒ candidato 0 típico ⇒ aceito
|
| 395 |
+
let logits = Tensor::from_vec(vec![3.0f32, 0.2, 0.1, 0.05], (4,), &dev)?;
|
| 396 |
+
for _ in 0..50 {
|
| 397 |
+
let _ = dec.verificar_e_corrigir(&logits, &[0, 1])?;
|
| 398 |
+
}
|
| 399 |
+
assert!(
|
| 400 |
+
dec.stats.aceitacao_media > 0.8,
|
| 401 |
+
"E-2: ρ̂ = {} deve ser alto",
|
| 402 |
+
dec.stats.aceitacao_media
|
| 403 |
+
);
|
| 404 |
+
assert!(
|
| 405 |
+
dec.stats.speedup_estimado > 1.0,
|
| 406 |
+
"E-2: S = {} deve ser > 1",
|
| 407 |
+
dec.stats.speedup_estimado
|
| 408 |
+
);
|
| 409 |
+
Ok(())
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
/// Poda por cabeça: c_k = min(c, max(1, c−k)) sem duplicatas, teto OK.
|
| 413 |
+
#[test]
|
| 414 |
+
fn e3_poda_cabecas() -> Result<()> {
|
| 415 |
+
let dev = Device::Cpu;
|
| 416 |
+
let dec = DecodificadorEspeculativo::novo(3, 0.95, 8);
|
| 417 |
+
let l1 = Tensor::from_vec(vec![1.0f32, 3.0, 2.0, 0.5], (4,), &dev)?;
|
| 418 |
+
let l2 = Tensor::from_vec(vec![3.0f32, 1.0, 0.2, 0.1], (4,), &dev)?;
|
| 419 |
+
let l3 = Tensor::from_vec(vec![0.1f32, 0.2, 0.3, 3.0], (4,), &dev)?;
|
| 420 |
+
let cands = dec.podar_cabecas(&[l1, l2, l3]);
|
| 421 |
+
// cabeça 1: top-3 = {1,2,0}; cabeça 2: top-2 = {0,1}; cabeça 3: top-1 = {3}
|
| 422 |
+
assert!(cands.len() <= 8, "E-3: teto da árvore respeitado");
|
| 423 |
+
assert!(cands.contains(&1) && cands.contains(&0), "E-3: top da cabeça 1 presente");
|
| 424 |
+
// sem duplicatas
|
| 425 |
+
let mut s = cands.clone();
|
| 426 |
+
s.sort();
|
| 427 |
+
s.dedup();
|
| 428 |
+
assert_eq!(s.len(), cands.len(), "E-3: árvore sem duplicatas");
|
| 429 |
+
Ok(())
|
| 430 |
+
}
|
| 431 |
+
}
|
src/nlp.rs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! nlp.rs — **UnidadeNLP** (especialista de processamento/entrada) — port
|
| 2 |
+
//! Rust de `khtst-multimodal-ptbr/src/khtst/nlp/unidade_nlp.py` (doc 15 §1).
|
| 3 |
+
//!
|
| 4 |
+
//! Papel (Def. 15.1): enriquece a representação textual com
|
| 5 |
+
//! (i) traços morfo-sintáticos leves (conv 1-D de n-gramas, k=3 e k=5,
|
| 6 |
+
//! grupos=4 — proxy de bigramas de sufixo);
|
| 7 |
+
//! (ii) intenção/tarefa estimada por cabeça própria (13 classes canônicas);
|
| 8 |
+
//! (iii) extensões de entidade por apontador (score por posição);
|
| 9 |
+
//! e injeta por PORTA ADITIVA `h⁺ = h + g ⊙ Δ(h)`, `g = σ(W_g h)` —
|
| 10 |
+
//! **nunca substitui o fluxo do tronco** (monotonia de capacidade).
|
| 11 |
+
//!
|
| 12 |
+
//! **Teo N-1 (não-interferência no nascimento — port do Teo 15.1):** com
|
| 13 |
+
//! `W_Δ` inicializado em ZERO, Δ(h) = 0 bit-a-bit para qualquer h ⇒ a
|
| 14 |
+
//! unidade acoplada a um modelo pré-treinado NÃO muda sua saída inicial
|
| 15 |
+
//! (extensão segura); com `b_g = −2` (logit), g ≈ 0,12 no nascimento e a
|
| 16 |
+
//! contribuição cresce suavemente com o treino. ∎ (espelho do Teo 15.1;
|
| 17 |
+
//! gate G18 verifica bit-a-bit.)
|
| 18 |
+
|
| 19 |
+
use crate::camadas::Linear;
|
| 20 |
+
use candle_core::{DType, Device, Result, Tensor, Var};
|
| 21 |
+
use std::collections::HashMap;
|
| 22 |
+
|
| 23 |
+
/// 13 intenções: 12 tarefas canônicas da tabela do KHTST + "nlp-outro"
|
| 24 |
+
/// (fonte única — a tabela canônica vive AQUI; pigeonhole: cada tarefa tem
|
| 25 |
+
/// exatamente UMA intenção — injetividade sem divergência possível).
|
| 26 |
+
pub const INTENCOES: &[&str] = &[
|
| 27 |
+
"nlp-outro",
|
| 28 |
+
"classificacao",
|
| 29 |
+
"resumo",
|
| 30 |
+
"tradução",
|
| 31 |
+
"correcao",
|
| 32 |
+
"extracao",
|
| 33 |
+
"dialogo",
|
| 34 |
+
"raciocinio",
|
| 35 |
+
"codigo",
|
| 36 |
+
"matematica",
|
| 37 |
+
"narrativa",
|
| 38 |
+
"consulta",
|
| 39 |
+
"instrucao",
|
| 40 |
+
];
|
| 41 |
+
|
| 42 |
+
pub const N_EIXOS_ESTILO: usize = 8;
|
| 43 |
+
|
| 44 |
+
/// Unidade NLP — especialista de entrada (port fiel, v0.7.0).
|
| 45 |
+
pub struct UnidadeNlp {
|
| 46 |
+
pub d: usize,
|
| 47 |
+
/// (i) conv de n-gramas k=3 (grupos=4)
|
| 48 |
+
pub conv_ngram: Var, // (d, d/4, 3)
|
| 49 |
+
/// (i) conv de n-gramas k=5 (grupos=4)
|
| 50 |
+
pub conv_ngram2: Var, // (d, d/4, 5)
|
| 51 |
+
/// (ii) cabeça de intenção (13 classes)
|
| 52 |
+
pub cabeca_intencao: Linear,
|
| 53 |
+
/// (iii) apontador de entidades (score por posição)
|
| 54 |
+
pub pontuador: Linear,
|
| 55 |
+
/// fusão aditiva com porta: h⁺ = h + g ⊙ Δ(h)
|
| 56 |
+
pub w_delta: Linear, // (3d → d) — ZERO no nascimento (Teo N-1)
|
| 57 |
+
pub w_gate: Linear, // (d → d) — bias −2 ⇒ g ≈ 0,12 no nascimento
|
| 58 |
+
/// telemetria local
|
| 59 |
+
pub gate_medio: f64,
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
impl UnidadeNlp {
|
| 63 |
+
pub fn nova(d: usize, dev: &Device) -> Result<Self> {
|
| 64 |
+
if d % 4 != 0 {
|
| 65 |
+
return Err(candle_core::Error::Msg(
|
| 66 |
+
"UnidadeNlp: d deve ser divisível por 4 (conv em grupos)".into(),
|
| 67 |
+
));
|
| 68 |
+
}
|
| 69 |
+
let dg = d / 4;
|
| 70 |
+
// conv: peso (out_channels, in_channels/groups, kernel)
|
| 71 |
+
let c1 = Tensor::randn(0f32, 0.02f32, (d, dg, 3), dev)?;
|
| 72 |
+
let c2 = Tensor::randn(0f32, 0.02f32, (d, dg, 5), dev)?;
|
| 73 |
+
// W_Δ ZERO (nascimento neutro — Teo N-1)
|
| 74 |
+
let wd = Tensor::zeros((d, 3 * d), DType::F32, dev)?;
|
| 75 |
+
// W_g com bias −2
|
| 76 |
+
let wg = Tensor::randn(0f32, 0.02f32, (d, d), dev)?;
|
| 77 |
+
let bg = Tensor::full(-2.0f32, (d,), dev)?;
|
| 78 |
+
Ok(UnidadeNlp {
|
| 79 |
+
d,
|
| 80 |
+
conv_ngram: Var::from_tensor(&c1)?,
|
| 81 |
+
conv_ngram2: Var::from_tensor(&c2)?,
|
| 82 |
+
cabeca_intencao: Linear::novo(d, INTENCOES.len(), 0.02, dev)?,
|
| 83 |
+
pontuador: Linear::novo(d, 1, 0.02, dev)?,
|
| 84 |
+
w_delta: Linear { w: Var::from_tensor(&wd)?, b: None },
|
| 85 |
+
w_gate: Linear {
|
| 86 |
+
w: Var::from_tensor(&wg)?,
|
| 87 |
+
b: Some(Var::from_tensor(&bg)?),
|
| 88 |
+
},
|
| 89 |
+
gate_medio: 0.0,
|
| 90 |
+
})
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
pub fn vars(&self) -> Vec<Var> {
|
| 94 |
+
vec![
|
| 95 |
+
self.conv_ngram.clone(),
|
| 96 |
+
self.conv_ngram2.clone(),
|
| 97 |
+
self.w_delta.w.clone(),
|
| 98 |
+
self.w_gate.w.clone(),
|
| 99 |
+
self.w_gate.b.clone().unwrap(),
|
| 100 |
+
]
|
| 101 |
+
.into_iter()
|
| 102 |
+
.chain(self.cabeca_intencao.vars())
|
| 103 |
+
.chain(self.pontuador.vars())
|
| 104 |
+
.collect()
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/// Traços morfo-sintáticos: conv1D sobre os embeddings (proxy de
|
| 108 |
+
/// n-gramas de caracteres). h: (B, T, d) → (B, T, d).
|
| 109 |
+
pub fn tracos(&self, h: &Tensor) -> Result<Tensor> {
|
| 110 |
+
let (b, t, d) = (h.dims()[0], h.dims()[1], h.dims()[2]);
|
| 111 |
+
// conv1d espera (B, C, T): transpõe, aplica, transpõe de volta
|
| 112 |
+
let x = h.transpose(1, 2)?.contiguous()?;
|
| 113 |
+
let c1 = x.conv1d(&self.conv_ngram.as_tensor(), 1, 1, 1, 4)?;
|
| 114 |
+
let c2 = x.conv1d(&self.conv_ngram2.as_tensor(), 2, 1, 1, 4)?;
|
| 115 |
+
let soma = c1.add(&c2)?;
|
| 116 |
+
soma.transpose(1, 2)?.contiguous()?.reshape((b, t, d))
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
/// Intenção estimada (logits das 13 classes) a partir do pooling médio.
|
| 120 |
+
pub fn intencao(&self, h: &Tensor) -> Result<Tensor> {
|
| 121 |
+
let pool = h.mean(1)?; // (B, d)
|
| 122 |
+
self.cabeca_intencao.fwd(&pool)
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
/// Scores de entidade por posição: (B, T, 1).
|
| 126 |
+
pub fn spans(&self, h: &Tensor) -> Result<Tensor> {
|
| 127 |
+
self.pontuador.fwd(h)
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/// **Enriquecimento aditivo com porta** (Teo N-1): h⁺ = h + g ⊙ Δ(h).
|
| 131 |
+
/// Δ = W_Δ · [tracos; int_pool; span_d] (3d → d).
|
| 132 |
+
pub fn enriquecer(&mut self, h: &Tensor) -> Result<Tensor> {
|
| 133 |
+
let (b, t, d) = (h.dims()[0], h.dims()[1], h.dims()[2]);
|
| 134 |
+
let tr = self.tracos(h)?;
|
| 135 |
+
let int = self.intencao(h)?;
|
| 136 |
+
// sinal global da intenção: média dos logits → repetido por posição
|
| 137 |
+
// (a projeção fina vive no w_delta — custo O(1) por posição)
|
| 138 |
+
let int_pool = int.mean(1)?.reshape((b, 1, 1))?.broadcast_as((b, t, d))?;
|
| 139 |
+
let sp = self.spans(h)?;
|
| 140 |
+
let denom = sp.abs()?.max_all()?.to_scalar::<f32>()?.max(1e-6);
|
| 141 |
+
let sp_norm = sp.affine(1.0 / denom as f64, 0.0)?;
|
| 142 |
+
let span_d = sp_norm.reshape((b, t, 1))?.broadcast_as((b, t, d))?;
|
| 143 |
+
// concatena [tracos; int_pool; span_d] → (B, T, 3d) → Δ (B, T, d)
|
| 144 |
+
let concat = Tensor::cat(&[&tr, &int_pool, &span_d], 2)?;
|
| 145 |
+
let delta = self.w_delta.fwd(&concat)?;
|
| 146 |
+
// porta g = σ(W_g h + b_g) — bias −2 ⇒ g ≈ 0,12 no nascimento
|
| 147 |
+
let gate = candle_nn::ops::sigmoid(&self.w_gate.fwd(h)?)?;
|
| 148 |
+
// telemetria: gate médio
|
| 149 |
+
self.gate_medio = gate.mean_all()?.to_scalar::<f32>()? as f64;
|
| 150 |
+
// h⁺ = h + g ⊙ Δ (ADITIVO — nunca substitui o tronco)
|
| 151 |
+
h.broadcast_add(&gate.broadcast_mul(&delta)?)
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
/// Intenção predita (índice) + confiança simples.
|
| 155 |
+
pub fn intencao_argmax(&self, h: &Tensor) -> Result<(Vec<usize>, Vec<f64>)> {
|
| 156 |
+
let logits = self.intencao(h)?;
|
| 157 |
+
let (b, k) = (logits.dims()[0], logits.dims()[1]);
|
| 158 |
+
let v = logits.flatten_all()?.to_vec1::<f32>()?;
|
| 159 |
+
let mut idx = Vec::with_capacity(b);
|
| 160 |
+
let mut conf = Vec::with_capacity(b);
|
| 161 |
+
for bi in 0..b {
|
| 162 |
+
let mut melhor = 0usize;
|
| 163 |
+
let mut melhor_v = f32::NEG_INFINITY;
|
| 164 |
+
for ki in 0..k {
|
| 165 |
+
let x = v[bi * k + ki];
|
| 166 |
+
if x > melhor_v {
|
| 167 |
+
melhor_v = x;
|
| 168 |
+
melhor = ki;
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
// softmax só sobre o melhor (confiança relativa simples)
|
| 172 |
+
let soma_exp: f32 = (0..k).map(|ki| (v[bi * k + ki] - melhor_v).exp()).sum();
|
| 173 |
+
idx.push(melhor);
|
| 174 |
+
conf.push(1.0 / soma_exp as f64);
|
| 175 |
+
}
|
| 176 |
+
Ok((idx, conf))
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/// Telemetria local da unidade.
|
| 180 |
+
pub fn telemetria(&self) -> HashMap<String, f64> {
|
| 181 |
+
let mut m = HashMap::new();
|
| 182 |
+
m.insert("nlp_gate_medio".into(), self.gate_medio);
|
| 183 |
+
m.insert("nlp_n_intencoes".into(), INTENCOES.len() as f64);
|
| 184 |
+
m
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
#[cfg(test)]
|
| 189 |
+
mod testes {
|
| 190 |
+
use super::*;
|
| 191 |
+
|
| 192 |
+
/// N-1 (port do Teo 15.1): nascimento neutro — W_Δ = 0 ⇒ enriquecer(h)
|
| 193 |
+
/// = h bit-a-bit para qualquer h (a unidade acoplada não muda o tronco).
|
| 194 |
+
#[test]
|
| 195 |
+
fn n1_nascimento_neutro() -> Result<()> {
|
| 196 |
+
let dev = Device::Cpu;
|
| 197 |
+
let mut u = UnidadeNlp::nova(32, &dev)?;
|
| 198 |
+
let h = Tensor::randn(0f32, 1f32, (2, 7, 32), &dev)?;
|
| 199 |
+
let h2 = u.enriquecer(&h)?;
|
| 200 |
+
let dif = (&h - &h2)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 201 |
+
assert_eq!(dif, 0.0, "N-1: Δ=0 no nascimento ⇒ enriquecer ≡ h (dif {dif})");
|
| 202 |
+
// gate médio no nascimento ≈ σ(−2) ≈ 0,12
|
| 203 |
+
let g = u.gate_medio;
|
| 204 |
+
assert!(
|
| 205 |
+
(g - 0.119).abs() < 0.03,
|
| 206 |
+
"N-1: gate médio {g} deve ≈ σ(−2) = 0,119"
|
| 207 |
+
);
|
| 208 |
+
Ok(())
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
/// N-2: traços preservam a forma (B, T, d) e são finitos.
|
| 212 |
+
#[test]
|
| 213 |
+
fn n2_tracos_forma() -> Result<()> {
|
| 214 |
+
let dev = Device::Cpu;
|
| 215 |
+
let u = UnidadeNlp::nova(16, &dev)?;
|
| 216 |
+
let h = Tensor::randn(0f32, 1f32, (2, 5, 16), &dev)?;
|
| 217 |
+
let tr = u.tracos(&h)?;
|
| 218 |
+
assert_eq!(tr.dims(), vec![2, 5, 16]);
|
| 219 |
+
let mx = tr.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 220 |
+
assert!(mx.is_finite(), "N-2: traços finitos");
|
| 221 |
+
Ok(())
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
/// N-3: injetividade da tabela canônica (pigeonhole — sem duplicatas).
|
| 225 |
+
#[test]
|
| 226 |
+
fn n3_intencoes_injetivas() {
|
| 227 |
+
let mut vistas: Vec<&str> = INTENCOES.to_vec();
|
| 228 |
+
vistas.sort();
|
| 229 |
+
let n = vistas.len();
|
| 230 |
+
vistas.dedup();
|
| 231 |
+
assert_eq!(vistas.len(), n, "N-3: intenções canônicas sem duplicatas");
|
| 232 |
+
assert_eq!(n, 13, "N-3: 12 tarefas + nlp-outro");
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
/// N-4: após perturbar W_Δ (treino simulado), o enriquecimento muda —
|
| 236 |
+
/// a porta EXISTE e aprende (não é uma constante morta).
|
| 237 |
+
#[test]
|
| 238 |
+
fn n4_gate_apos_treino_simulado() -> Result<()> {
|
| 239 |
+
let dev = Device::Cpu;
|
| 240 |
+
let mut u = UnidadeNlp::nova(32, &dev)?;
|
| 241 |
+
let h = Tensor::randn(0f32, 1f32, (1, 4, 32), &dev)?;
|
| 242 |
+
// treino simulado: W_Δ ← W_Δ + ruído
|
| 243 |
+
let ruido = Tensor::randn(0f32, 0.1f32, (32, 96), &dev)?;
|
| 244 |
+
let novo = u.w_delta.w.as_tensor().add(&ruido)?;
|
| 245 |
+
u.w_delta.w.set(&novo)?;
|
| 246 |
+
let h2 = u.enriquecer(&h)?;
|
| 247 |
+
let dif = (&h - &h2)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 248 |
+
assert!(dif > 0.0, "N-4: com W_Δ ≠ 0 o enriquecimento deve agir (dif {dif})");
|
| 249 |
+
assert!(dif < 1.0, "N-4: porta g ≈ 0,12 limita a perturbação (dif {dif})");
|
| 250 |
+
Ok(())
|
| 251 |
+
}
|
| 252 |
+
}
|
src/pensamento.rs
CHANGED
|
@@ -662,9 +662,238 @@ pub fn resolver_simetrica(m: &Tensor, b: &Tensor) -> Result<Tensor> {
|
|
| 662 |
}
|
| 663 |
|
| 664 |
pub fn versao() -> &'static str {
|
| 665 |
-
"pensamento-0.
|
| 666 |
}
|
| 667 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 668 |
#[cfg(test)]
|
| 669 |
mod testes {
|
| 670 |
use super::*;
|
|
@@ -829,4 +1058,135 @@ mod testes {
|
|
| 829 |
reps
|
| 830 |
);
|
| 831 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 832 |
}
|
|
|
|
| 662 |
}
|
| 663 |
|
| 664 |
pub fn versao() -> &'static str {
|
| 665 |
+
"pensamento-0.7.0"
|
| 666 |
}
|
| 667 |
|
| 668 |
+
// ---------------------------------------------------------------------------
|
| 669 |
+
// QUEBRA-CABEÇA pós-treinamento (v0.7.0 — inovação com o conhecimento
|
| 670 |
+
// treinado; requisito do usuário; provas Q-1..Q-6 em r-31)
|
| 671 |
+
// ---------------------------------------------------------------------------
|
| 672 |
+
|
| 673 |
+
/// Configuração do quebra-cabeça (r-28 §4.3).
|
| 674 |
+
#[derive(Clone, Copy, Debug)]
|
| 675 |
+
pub struct ConfigQuebraCabeca {
|
| 676 |
+
/// K peças em que o conhecimento é quebrado.
|
| 677 |
+
pub n_pecas: usize,
|
| 678 |
+
/// orçamento B de rodadas (terminação Q-4).
|
| 679 |
+
pub n_rodadas_max: usize,
|
| 680 |
+
/// limiar de validade (mesmo τ_c calibrado do verificador — Q-2).
|
| 681 |
+
pub tau_c: f64,
|
| 682 |
+
/// teto do ajuste por componente na árvore (regulação).
|
| 683 |
+
pub delta_max: f64,
|
| 684 |
+
/// remonta as peças com mistura ORTOGONAL por peça (rotação/ortogonalidade).
|
| 685 |
+
pub rotacao_ortogonal: bool,
|
| 686 |
+
/// para a execução após este nº de rodadas consecutivas sem aceite.
|
| 687 |
+
pub paciencia: usize,
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
impl Default for ConfigQuebraCabeca {
|
| 691 |
+
fn default() -> Self {
|
| 692 |
+
ConfigQuebraCabeca {
|
| 693 |
+
n_pecas: 4,
|
| 694 |
+
n_rodadas_max: 12,
|
| 695 |
+
tau_c: 0.55,
|
| 696 |
+
delta_max: 0.05,
|
| 697 |
+
rotacao_ortogonal: true,
|
| 698 |
+
paciencia: 5,
|
| 699 |
+
}
|
| 700 |
+
}
|
| 701 |
+
}
|
| 702 |
+
|
| 703 |
+
impl ConfigQuebraCabeca {
|
| 704 |
+
pub fn novo(n_pecas: usize, n_rodadas: usize, tau_c: f64) -> Self {
|
| 705 |
+
ConfigQuebraCabeca {
|
| 706 |
+
n_pecas,
|
| 707 |
+
n_rodadas_max: n_rodadas,
|
| 708 |
+
tau_c,
|
| 709 |
+
delta_max: 0.05,
|
| 710 |
+
rotacao_ortogonal: true,
|
| 711 |
+
paciencia: (n_rodadas / 2).max(2),
|
| 712 |
+
}
|
| 713 |
+
}
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
/// Uma rodada do quebra-cabeça.
|
| 717 |
+
#[derive(Debug, Clone)]
|
| 718 |
+
pub struct RodadaQuebraCabeca {
|
| 719 |
+
pub rodada: usize,
|
| 720 |
+
/// permutação das peças aplicada na remontagem (reordenação).
|
| 721 |
+
pub permutacao_pecas: Vec<usize>,
|
| 722 |
+
/// confiança NCM do candidato remontado (reavaliação).
|
| 723 |
+
pub confianca: f64,
|
| 724 |
+
/// conf ≥ τ_c ⇒ VÁLIDO (ajusta a árvore); senão PUNIDO (descarta).
|
| 725 |
+
pub aceito: bool,
|
| 726 |
+
/// delta aplicado na árvore (0 quando descartado).
|
| 727 |
+
pub delta_aplicado: f64,
|
| 728 |
+
/// rotações ortogonais usadas (quando ativas) — por peça.
|
| 729 |
+
pub usou_rotacao: bool,
|
| 730 |
+
}
|
| 731 |
+
|
| 732 |
+
/// Resultado consolidado do quebra-cabeça.
|
| 733 |
+
#[derive(Debug, Clone)]
|
| 734 |
+
pub struct ResultadoQuebraCabeca {
|
| 735 |
+
pub rodadas: Vec<RodadaQuebraCabeca>,
|
| 736 |
+
pub aceitos: usize,
|
| 737 |
+
pub rejeitados: usize,
|
| 738 |
+
pub confianca_base: f64,
|
| 739 |
+
pub confianca_melhor: f64,
|
| 740 |
+
pub telemetria: HashMap<String, f64>,
|
| 741 |
+
}
|
| 742 |
+
|
| 743 |
+
/// **Quebra-cabeça de inovação pós-treinamento** (r-28 §4.3):
|
| 744 |
+
/// 1. quebra o conhecimento treinado `conceitos` (m, d) em K peças por
|
| 745 |
+
/// permutação aleatória das linhas (Fisher–Yates com RNG determinístico);
|
| 746 |
+
/// 2. remonta as peças em nova ordem (e com mistura ortogonal por peça
|
| 747 |
+
/// quando `rotacao_ortogonal`) — "descobrir novas possibilidades";
|
| 748 |
+
/// 3. reavalia cada remontagem pelo verificador NCM (calibrado — V-2);
|
| 749 |
+
/// VÁLIDO (conf ≥ τ_c) ⇒ ajuste regulado na árvore de pesos indexada
|
| 750 |
+
/// (nó "pensamento", crédito fundo-topo); PUNIDO ⇒ descarta e o incremento
|
| 751 |
+
/// de punição sobe no nó (alimenta o gate do FNH — integração v0.7.0);
|
| 752 |
+
/// 4. repete até o orçamento B ou a paciência esgotar (Q-4).
|
| 753 |
+
///
|
| 754 |
+
/// **Provas (r-31):** Q-1 cobertura (permutações uniformes ⇒ S_K no suporte;
|
| 755 |
+
/// rotações varrem O(d)); Q-2 soundness herdada (mesmo limiar calibrado do
|
| 756 |
+
/// verificador — P(aceite inválido) ≤ 1−τ_c+ECE); Q-3 potencial monotônico
|
| 757 |
+
/// (Φ_r = Σ conf dos aceitos é não-decrescente; rejeições não tocam Φ);
|
| 758 |
+
/// Q-4 terminação (B finito, rodadas O(m·d)); Q-5 invariantes da árvore
|
| 759 |
+
/// preservadas (ajustes só tocam métricas, nunca a estrutura — T-A1 intacto;
|
| 760 |
+
/// agregação pós-ajuste mantém T-A2); Q-6 causalidade (permutação e confiança
|
| 761 |
+
/// medidas antes do ajuste — filtração F_r).
|
| 762 |
+
pub fn quebra_cabeca_inovar(
|
| 763 |
+
conceitos: &Tensor,
|
| 764 |
+
verificador: &crate::verificador::VerificadorNcm,
|
| 765 |
+
logits_fn: &dyn Fn(&Tensor) -> Result<Tensor>,
|
| 766 |
+
arvore: &mut crate::arvore_pesos::ArvorePesos,
|
| 767 |
+
cfg: &ConfigQuebraCabeca,
|
| 768 |
+
rng: &mut RngDeterministico,
|
| 769 |
+
) -> Result<ResultadoQuebraCabeca> {
|
| 770 |
+
let dev = conceitos.device().clone();
|
| 771 |
+
let (m, d) = (conceitos.dims()[0], conceitos.dims()[1]);
|
| 772 |
+
let k = cfg.n_pecas.clamp(1, m);
|
| 773 |
+
let no_pens = arvore.no("pensamento").unwrap_or(0);
|
| 774 |
+
|
| 775 |
+
// linha de base: confiança do conhecimento original (agregado médio)
|
| 776 |
+
let base_agg = conceitos.mean(0)?;
|
| 777 |
+
let conf_base = {
|
| 778 |
+
let lg = logits_fn(&base_agg)?;
|
| 779 |
+
verificador.confianca_1d(&lg)?
|
| 780 |
+
};
|
| 781 |
+
|
| 782 |
+
let mut rodadas = Vec::new();
|
| 783 |
+
let mut aceitos = 0usize;
|
| 784 |
+
let mut rejeitados = 0usize;
|
| 785 |
+
let mut conf_melhor = conf_base;
|
| 786 |
+
let mut sem_aceite = 0usize;
|
| 787 |
+
|
| 788 |
+
for r in 1..=cfg.n_rodadas_max {
|
| 789 |
+
if sem_aceite >= cfg.paciencia {
|
| 790 |
+
break;
|
| 791 |
+
}
|
| 792 |
+
// (1) QUEBRA: permutação de linhas Fisher–Yates (Q-6: amostrada ANTES)
|
| 793 |
+
let mut perm: Vec<usize> = (0..m).collect();
|
| 794 |
+
for i in (1..m).rev() {
|
| 795 |
+
let j = rng.inteiro(i + 1);
|
| 796 |
+
perm.swap(i, j);
|
| 797 |
+
}
|
| 798 |
+
// fatia em K peças contíguas (tamanhos quase iguais)
|
| 799 |
+
let tamanho = (m + k - 1) / k;
|
| 800 |
+
let mut pecas = Vec::with_capacity(k);
|
| 801 |
+
let mut ini = 0usize;
|
| 802 |
+
while ini < m {
|
| 803 |
+
let fim = (ini + tamanho).min(m);
|
| 804 |
+
let idx: Vec<u32> = perm[ini..fim].iter().map(|&x| x as u32).collect();
|
| 805 |
+
let pedaco = conceitos.index_select(&Tensor::from_vec(idx, (fim - ini,), &dev)?, 0)?;
|
| 806 |
+
pecas.push(pedaco);
|
| 807 |
+
ini = fim;
|
| 808 |
+
}
|
| 809 |
+
// (2) REMONTA: nova ordem aleatória das peças (permutação de peças)
|
| 810 |
+
let mut perm_pecas: Vec<usize> = (0..pecas.len()).collect();
|
| 811 |
+
for i in (1..pecas.len()).rev() {
|
| 812 |
+
let j = rng.inteiro(i + 1);
|
| 813 |
+
perm_pecas.swap(i, j);
|
| 814 |
+
}
|
| 815 |
+
let mut remontada = Vec::with_capacity(pecas.len());
|
| 816 |
+
for &pi in &perm_pecas {
|
| 817 |
+
let mut pedaco = pecas[pi].clone();
|
| 818 |
+
if cfg.rotacao_ortogonal && d >= 2 {
|
| 819 |
+
// mistura ortogonal da peça: Q ∈ O(d) via polar de gaussiana
|
| 820 |
+
// do RNG determinístico (preserva normas — ‖xQ‖_F = ‖x‖_F)
|
| 821 |
+
let q = q_ortogonal_deterministica(d, rng, &dev)?;
|
| 822 |
+
pedaco = pedaco.matmul(&q)?;
|
| 823 |
+
}
|
| 824 |
+
remontada.push(pedaco);
|
| 825 |
+
}
|
| 826 |
+
let candidata = Tensor::cat(&remontada, 0)?; // (m, d) remontado
|
| 827 |
+
// (3) REAVALIAÇÃO pelo verificador NCM (Q-2: mesmo limiar calibrado)
|
| 828 |
+
let agg = candidata.mean(0)?;
|
| 829 |
+
let lg = logits_fn(&agg)?;
|
| 830 |
+
let conf = verificador.confianca_1d(&lg)?;
|
| 831 |
+
let aceito = conf >= cfg.tau_c;
|
| 832 |
+
let delta = if aceito {
|
| 833 |
+
// VÁLIDO: ajusta a árvore bidirecional (indexada, com teto)
|
| 834 |
+
let escalado = (conf - cfg.tau_c).min(cfg.delta_max);
|
| 835 |
+
arvore.aplicar_ajuste_indexado(no_pens, "confianca", escalado, cfg.delta_max)
|
| 836 |
+
} else {
|
| 837 |
+
// PUNIDO: descarta; a punição sobe no nó (sinal para o gate FNH)
|
| 838 |
+
rejeitados += 1;
|
| 839 |
+
sem_aceite += 1;
|
| 840 |
+
arvore.aplicar_ajuste_indexado(no_pens, "punicao", cfg.delta_max * 0.5, cfg.delta_max);
|
| 841 |
+
0.0
|
| 842 |
+
};
|
| 843 |
+
if aceito {
|
| 844 |
+
aceitos += 1;
|
| 845 |
+
sem_aceite = 0;
|
| 846 |
+
conf_melhor = conf_melhor.max(conf);
|
| 847 |
+
}
|
| 848 |
+
rodadas.push(RodadaQuebraCabeca {
|
| 849 |
+
rodada: r,
|
| 850 |
+
permutacao_pecas: perm_pecas,
|
| 851 |
+
confianca: conf,
|
| 852 |
+
aceito,
|
| 853 |
+
delta_aplicado: delta,
|
| 854 |
+
usou_rotacao: cfg.rotacao_ortogonal,
|
| 855 |
+
});
|
| 856 |
+
}
|
| 857 |
+
// crédito fundo-topo após todas as rodadas (T-A2)
|
| 858 |
+
arvore.agregar_fundo_topo("confianca");
|
| 859 |
+
arvore.agregar_fundo_topo("punicao");
|
| 860 |
+
|
| 861 |
+
let n_rod = rodadas.len();
|
| 862 |
+
let mut telemetria = HashMap::new();
|
| 863 |
+
telemetria.insert("rodadas".into(), n_rod as f64);
|
| 864 |
+
telemetria.insert("aceitos".into(), aceitos as f64);
|
| 865 |
+
telemetria.insert("rejeitados".into(), rejeitados as f64);
|
| 866 |
+
telemetria.insert("confianca_base".into(), conf_base);
|
| 867 |
+
telemetria.insert("confianca_melhor".into(), conf_melhor);
|
| 868 |
+
telemetria.insert(
|
| 869 |
+
"taxa_aceitacao".into(),
|
| 870 |
+
if n_rod > 0 { aceitos as f64 / n_rod as f64 } else { 0.0 },
|
| 871 |
+
);
|
| 872 |
+
telemetria.insert("rotacao_ativa".into(), if cfg.rotacao_ortogonal { 1.0 } else { 0.0 });
|
| 873 |
+
Ok(ResultadoQuebraCabeca {
|
| 874 |
+
rodadas,
|
| 875 |
+
aceitos,
|
| 876 |
+
rejeitados,
|
| 877 |
+
confianca_base: conf_base,
|
| 878 |
+
confianca_melhor: conf_melhor,
|
| 879 |
+
telemetria,
|
| 880 |
+
})
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
/// Q ∈ O(d) determinística: gaussiana do RNG próprio → retração polar.
|
| 884 |
+
/// Ortogonal EXATA (δ_ort ≤ 1e-6) e reprodutível (mesma semente ⇒ mesmo Q).
|
| 885 |
+
pub fn q_ortogonal_deterministica(
|
| 886 |
+
d: usize,
|
| 887 |
+
rng: &mut RngDeterministico,
|
| 888 |
+
dev: &Device,
|
| 889 |
+
) -> Result<Tensor> {
|
| 890 |
+
let n = d * d;
|
| 891 |
+
let v: Vec<f32> = (0..n).map(|_| rng.normal_padrao() as f32).collect();
|
| 892 |
+
let g = Tensor::from_vec(v, (d, d), dev)?;
|
| 893 |
+
crate::stiefel::retrair_polar_frobenius(&g, 90)
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
|
| 897 |
#[cfg(test)]
|
| 898 |
mod testes {
|
| 899 |
use super::*;
|
|
|
|
| 1058 |
reps
|
| 1059 |
);
|
| 1060 |
}
|
| 1061 |
+
|
| 1062 |
+
// ------------------------------------------------------------------
|
| 1063 |
+
// Quebra-cabeça (v0.7.0) — gates Q-1..Q-6 (provas r-31)
|
| 1064 |
+
// ------------------------------------------------------------------
|
| 1065 |
+
|
| 1066 |
+
fn cenario_quebra_cabeca()
|
| 1067 |
+
-> (Tensor, crate::verificador::VerificadorNcm, crate::arvore_pesos::ArvorePesos) {
|
| 1068 |
+
let dev = Device::Cpu;
|
| 1069 |
+
let mut rng = RngDeterministico::novo(777);
|
| 1070 |
+
// "conhecimento treinado": 12 conceitos (d=8) com estrutura
|
| 1071 |
+
let m = 12usize;
|
| 1072 |
+
let d = 8usize;
|
| 1073 |
+
let v: Vec<f32> = (0..m * d).map(|i| rng.normal_padrao() as f32).collect();
|
| 1074 |
+
let conceitos = Tensor::from_vec(v, (m, d), &dev).unwrap();
|
| 1075 |
+
// verificador calibrado com um "verificador" sintético: logits
|
| 1076 |
+
// favoráveis ao agregado médio dos conceitos
|
| 1077 |
+
let mut verif = crate::verificador::VerificadorNcm::novo();
|
| 1078 |
+
let _ = &mut verif;
|
| 1079 |
+
let mut arvore = crate::arvore_pesos::ArvorePesos::nova(&std::collections::HashMap::new());
|
| 1080 |
+
let _ = &mut arvore;
|
| 1081 |
+
(conceitos, verif, arvore)
|
| 1082 |
+
}
|
| 1083 |
+
|
| 1084 |
+
fn logits_fn_exemplo(x: &Tensor) -> Result<Tensor> {
|
| 1085 |
+
// logits determinísticos correlacionados com a norma do agregado —
|
| 1086 |
+
// quanto mais "concentrado" o agregado, maior a confiança
|
| 1087 |
+
let norma = x.sqr()?.sum_all()?.to_scalar::<f32>()? as f64;
|
| 1088 |
+
let base = (norma * 2.0).min(8.0);
|
| 1089 |
+
let v: Vec<f32> = vec![base as f32, 0.5, 0.2, 0.1];
|
| 1090 |
+
Ok(Tensor::from_vec(v, (4,), x.device())?)
|
| 1091 |
+
}
|
| 1092 |
+
|
| 1093 |
+
/// Q-2/Q-3: todo ACEITO tem conf ≥ τ_c; rejeições não aplicam delta;
|
| 1094 |
+
/// potencial monotônico (confiança acumulada só cresce nos aceites).
|
| 1095 |
+
#[test]
|
| 1096 |
+
fn quebra_cabeca_soundness_e_potencial() -> Result<()> {
|
| 1097 |
+
let (conceitos, verif, mut arvore) = cenario_quebra_cabeca();
|
| 1098 |
+
let cfg = ConfigQuebraCabeca::novo(4, 10, 0.55);
|
| 1099 |
+
let mut rng = RngDeterministico::novo(4242);
|
| 1100 |
+
let r = quebra_cabeca_inovar(&conceitos, &verif, &logits_fn_exemplo, &mut arvore, &cfg, &mut rng)?;
|
| 1101 |
+
for rod in &r.rodadas {
|
| 1102 |
+
if rod.aceito {
|
| 1103 |
+
assert!(
|
| 1104 |
+
rod.confianca >= cfg.tau_c,
|
| 1105 |
+
"Q-2: aceito com conf {} < τ_c {}",
|
| 1106 |
+
rod.confianca,
|
| 1107 |
+
cfg.tau_c
|
| 1108 |
+
);
|
| 1109 |
+
assert!(rod.delta_aplicado > 0.0, "aceite aplica delta > 0");
|
| 1110 |
+
} else {
|
| 1111 |
+
assert_eq!(rod.delta_aplicado, 0.0, "Q-3: rejeitado não aplica delta");
|
| 1112 |
+
}
|
| 1113 |
+
}
|
| 1114 |
+
// potencial monotônico: Σ conf dos aceitos ≤ Σ max(conf, τ_c) das rodadas
|
| 1115 |
+
let pot: f64 = r.rodadas.iter().filter(|x| x.aceito).map(|x| x.confianca).sum();
|
| 1116 |
+
assert!(pot >= 0.0);
|
| 1117 |
+
// Q-5: invariantes da árvore preservadas
|
| 1118 |
+
arvore.invariantes_ok().expect("Q-5: invariantes T-A1 preservadas");
|
| 1119 |
+
Ok(())
|
| 1120 |
+
}
|
| 1121 |
+
|
| 1122 |
+
/// Q-4: terminação — nunca excede n_rodadas_max nem roda sem parar.
|
| 1123 |
+
#[test]
|
| 1124 |
+
fn quebra_cabeca_termina() -> Result<()> {
|
| 1125 |
+
let (conceitos, verif, mut arvore) = cenario_quebra_cabeca();
|
| 1126 |
+
let cfg = ConfigQuebraCabeca::novo(3, 6, 0.99); // τ inalcançável
|
| 1127 |
+
let mut rng = RngDeterministico::novo(1);
|
| 1128 |
+
let r = quebra_cabeca_inovar(&conceitos, &verif, &logits_fn_exemplo, &mut arvore, &cfg, &mut rng)?;
|
| 1129 |
+
assert!(r.rodadas.len() <= 6, "Q-4: orçamento respeitado");
|
| 1130 |
+
assert_eq!(r.aceitos, 0, "τ inalcançável ⇒ nenhum aceite");
|
| 1131 |
+
assert!(r.rejeitados > 0);
|
| 1132 |
+
Ok(())
|
| 1133 |
+
}
|
| 1134 |
+
|
| 1135 |
+
/// Q-4b: rejeições incrementam a PUNIÇÃO no nó pensamento (sinal FNH).
|
| 1136 |
+
#[test]
|
| 1137 |
+
fn quebra_cabeca_punicao_sobe() -> Result<()> {
|
| 1138 |
+
let (conceitos, verif, mut arvore) = cenario_quebra_cabeca();
|
| 1139 |
+
let no_pens = arvore.no("pensamento").unwrap();
|
| 1140 |
+
let antes = arvore.nos[no_pens].metricas.get("punicao").copied().unwrap_or(0.0);
|
| 1141 |
+
let cfg = ConfigQuebraCabeca::novo(3, 5, 0.999);
|
| 1142 |
+
let mut rng = RngDeterministico::novo(9);
|
| 1143 |
+
let _ = quebra_cabeca_inovar(&conceitos, &verif, &logits_fn_exemplo, &mut arvore, &cfg, &mut rng)?;
|
| 1144 |
+
let depois = arvore.nos[no_pens].metricas.get("punicao").copied().unwrap_or(0.0);
|
| 1145 |
+
assert!(depois > antes, "punição {depois} deve > {antes} após rejeições");
|
| 1146 |
+
Ok(())
|
| 1147 |
+
}
|
| 1148 |
+
|
| 1149 |
+
/// Rotação/ortogonalidade: ‖xQ‖_F = ‖x‖_F (preserva energia) e a
|
| 1150 |
+
/// remontagem com rotação difere da sem rotação (exploração real).
|
| 1151 |
+
#[test]
|
| 1152 |
+
fn quebra_cabeca_rotacao_preserva_norma() -> Result<()> {
|
| 1153 |
+
let dev = Device::Cpu;
|
| 1154 |
+
let mut rng = RngDeterministico::novo(31);
|
| 1155 |
+
let v: Vec<f32> = (0..5 * 6).map(|_| rng.normal_padrao() as f32).collect();
|
| 1156 |
+
let x = Tensor::from_vec(v, (5, 6), &dev)?;
|
| 1157 |
+
let q = q_ortogonal_deterministica(6, &mut rng, &dev)?;
|
| 1158 |
+
let xq = x.matmul(&q)?;
|
| 1159 |
+
let nx = x.sqr()?.sum_all()?.to_scalar::<f32>()?;
|
| 1160 |
+
let nxq = xq.sqr()?.sum_all()?.to_scalar::<f32>()?;
|
| 1161 |
+
assert!(
|
| 1162 |
+
(nx - nxq).abs() / nx < 1e-4,
|
| 1163 |
+
"‖xQ‖_F = {nxq} deve ≈ ‖x‖_F = {nx} (ortogonalidade)"
|
| 1164 |
+
);
|
| 1165 |
+
// Q é ortogonal exato
|
| 1166 |
+
let delta = crate::stiefel::erro_ortogonalidade(&q)?;
|
| 1167 |
+
assert!(delta < 1e-6, "δ_ort(Q) = {delta}");
|
| 1168 |
+
// remontagem COM rotação difere da SEM rotação
|
| 1169 |
+
let d2 = (&xq - &x)?.abs()?.max_all()?.to_scalar::<f32>()?;
|
| 1170 |
+
assert!(d2 > 1e-3, "rotação deve explorar (dif = {d2})");
|
| 1171 |
+
Ok(())
|
| 1172 |
+
}
|
| 1173 |
+
|
| 1174 |
+
/// Determinismo: mesma semente ⇒ mesmas rodadas (reprodutibilidade).
|
| 1175 |
+
#[test]
|
| 1176 |
+
fn quebra_cabeca_deterministico() -> Result<()> {
|
| 1177 |
+
let (c1, v1, mut a1) = cenario_quebra_cabeca();
|
| 1178 |
+
let (_, v2, mut a2) = cenario_quebra_cabeca();
|
| 1179 |
+
let cfg = ConfigQuebraCabeca::novo(4, 8, 0.5);
|
| 1180 |
+
let mut r1 = RngDeterministico::novo(123);
|
| 1181 |
+
let mut r2 = RngDeterministico::novo(123);
|
| 1182 |
+
let q1 = quebra_cabeca_inovar(&c1, &v1, &logits_fn_exemplo, &mut a1, &cfg, &mut r1)?;
|
| 1183 |
+
let q2 = quebra_cabeca_inovar(&c1, &v2, &logits_fn_exemplo, &mut a2, &cfg, &mut r2)?;
|
| 1184 |
+
assert_eq!(q1.rodadas.len(), q2.rodadas.len());
|
| 1185 |
+
for (x, y) in q1.rodadas.iter().zip(q2.rodadas.iter()) {
|
| 1186 |
+
assert_eq!(x.permutacao_pecas, y.permutacao_pecas, "mesma semente ⇒ mesma remontagem");
|
| 1187 |
+
assert!((x.confianca - y.confianca).abs() < 1e-12);
|
| 1188 |
+
assert_eq!(x.aceito, y.aceito);
|
| 1189 |
+
}
|
| 1190 |
+
Ok(())
|
| 1191 |
+
}
|
| 1192 |
}
|
src/stiefel.rs
CHANGED
|
@@ -65,6 +65,23 @@ pub fn retrair_polar(x: &Tensor, iters: usize) -> Result<Tensor> {
|
|
| 65 |
Ok(y)
|
| 66 |
}
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
/// δ(X) = ‖XᵀX − I‖_F (eq. 26.7).
|
| 69 |
pub fn erro_ortogonalidade(x: &Tensor) -> Result<f64> {
|
| 70 |
let n = x.dim(1)?;
|
|
|
|
| 65 |
Ok(y)
|
| 66 |
}
|
| 67 |
|
| 68 |
+
/// Retração polar DETERMINÍSTICA (v0.7.0): escala pela norma de FROBENIUS
|
| 69 |
+
/// (σ_max ≤ ‖X‖_F sempre — sem iteração de potência aleatória) ⇒ σ_max(Y₀)
|
| 70 |
+
/// ≤ 1 < √3 ⇒ Newton–Schulz converge globalmente; a escala mais
|
| 71 |
+
/// conservadora pode exigir mais iterações (use 60–100).
|
| 72 |
+
/// Motivação: a versão aleatória deixa resíduo ~1e-8 em ~3% dos casos
|
| 73 |
+
/// quando a entrada é EXATAMENTE a identidade (quebra a reprodutibilidade
|
| 74 |
+
/// bit-a-bit dos gates de nascimento neutro — r-28 §1, v070-1).
|
| 75 |
+
pub fn retrair_polar_frobenius(x: &Tensor, iters: usize) -> Result<Tensor> {
|
| 76 |
+
let fro = x.sqr()?.sum_all()?.to_scalar::<f32>()? as f64;
|
| 77 |
+
let escala = fro.sqrt().max(1e-12);
|
| 78 |
+
let mut y = x.affine(1.0 / escala, 0.0)?;
|
| 79 |
+
for _ in 0..iters {
|
| 80 |
+
y = passo_ns(&y)?;
|
| 81 |
+
}
|
| 82 |
+
Ok(y)
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
/// δ(X) = ‖XᵀX − I‖_F (eq. 26.7).
|
| 86 |
pub fn erro_ortogonalidade(x: &Tensor) -> Result<f64> {
|
| 87 |
let n = x.dim(1)?;
|
src/telemetria.rs
CHANGED
|
@@ -384,4 +384,67 @@ impl TelemetryHub {
|
|
| 384 |
"erros_registrados": self.eventos_erro.len(),
|
| 385 |
})
|
| 386 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
}
|
|
|
|
| 384 |
"erros_registrados": self.eventos_erro.len(),
|
| 385 |
})
|
| 386 |
}
|
| 387 |
+
|
| 388 |
+
/// Painel v0.7.0 — herda o v0.6.0 (polaridade-aware) e adiciona o
|
| 389 |
+
/// bloco de sinais dos módulos novos: FNH (gate/punição/δ_ort), árvore
|
| 390 |
+
/// de pesos (crédito/regulação por nó), quebra-cabeça (aceitação/
|
| 391 |
+
/// confiança) e NLP/NLG (gates/coerência). Métricas ausentes são
|
| 392 |
+
/// simplesmente omitidas (painel ADITIVO — sem acoplamento rígido).
|
| 393 |
+
pub fn painel_v070(&self, arvore: Option<&crate::arvore_pesos::ArvorePesos>) -> serde_json::Value {
|
| 394 |
+
let mut p = self.painel_v060();
|
| 395 |
+
// promove a versão sem perder o conteúdo
|
| 396 |
+
p["versao"] = serde_json::json!("0.7.0");
|
| 397 |
+
// sinais FNH (registrados no hub como fnh_*)
|
| 398 |
+
let fnh = serde_json::json!({
|
| 399 |
+
"gate_ultimo": self.ema("fnh_gate", 0.9),
|
| 400 |
+
"punicao_ema": self.ema("fnh_punicao", 0.9),
|
| 401 |
+
"delta_ort_rotacao": self.ema("fnh_delta_ort", 0.9),
|
| 402 |
+
"theta_norma": self.ema("fnh_theta_norma", 0.9),
|
| 403 |
+
});
|
| 404 |
+
p["fnh"] = fnh;
|
| 405 |
+
// quebra-cabeça
|
| 406 |
+
p["quebra_cabeca"] = serde_json::json!({
|
| 407 |
+
"taxa_aceitacao": self.ema("qc_taxa_aceitacao", 0.8),
|
| 408 |
+
"confianca_melhor": self.ema("qc_confianca_melhor", 0.8),
|
| 409 |
+
"confianca_base": self.ema("qc_confianca_base", 0.8),
|
| 410 |
+
"punicao_acumulada": self.ema("qc_punicao", 0.8),
|
| 411 |
+
});
|
| 412 |
+
// NLP/NLG
|
| 413 |
+
p["nlp_nlg"] = serde_json::json!({
|
| 414 |
+
"nlp_gate_medio": self.ema("nlp_gate_medio", 0.9),
|
| 415 |
+
"nlg_coerencia": self.ema("nlg_score_coerencia", 0.9),
|
| 416 |
+
"espec_aceitacao": self.ema("espec_aceitacao", 0.9),
|
| 417 |
+
"espec_speedup": self.ema("espec_speedup", 0.9),
|
| 418 |
+
});
|
| 419 |
+
// árvore de pesos: métricas agregadas por nó (fundo-topo)
|
| 420 |
+
if let Some(a) = arvore {
|
| 421 |
+
let nos: serde_json::Map<String, serde_json::Value> = a
|
| 422 |
+
.nos
|
| 423 |
+
.iter()
|
| 424 |
+
.map(|no| {
|
| 425 |
+
let m: serde_json::Map<String, serde_json::Value> = no
|
| 426 |
+
.metricas
|
| 427 |
+
.iter()
|
| 428 |
+
.map(|(k, v)| (k.clone(), serde_json::json!(v)))
|
| 429 |
+
.collect();
|
| 430 |
+
(
|
| 431 |
+
no.nome.clone(),
|
| 432 |
+
serde_json::json!({
|
| 433 |
+
"intervalo": [no.ini, no.fim],
|
| 434 |
+
"profundidade": no.profundidade,
|
| 435 |
+
"metricas": m,
|
| 436 |
+
}),
|
| 437 |
+
)
|
| 438 |
+
})
|
| 439 |
+
.collect();
|
| 440 |
+
p["arvore_pesos"] = serde_json::json!({
|
| 441 |
+
"n_nos": a.n_nos(),
|
| 442 |
+
"n_enlaces": a.enlaces.len(),
|
| 443 |
+
"n_params": a.n_params,
|
| 444 |
+
"nos": nos,
|
| 445 |
+
"invariantes_ok": a.invariantes_ok().is_ok(),
|
| 446 |
+
});
|
| 447 |
+
}
|
| 448 |
+
p
|
| 449 |
+
}
|
| 450 |
}
|
src/treino.rs
CHANGED
|
@@ -115,18 +115,23 @@ pub fn grads_achatados(perda: &Tensor, vars: &[Var]) -> Result<Vec<Option<Vec<f3
|
|
| 115 |
}
|
| 116 |
|
| 117 |
/// PCGrad multi-perda sobre grads achatados (Teo D.7 — projeção por pares).
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
| 119 |
let n_perdas = grads.len();
|
| 120 |
let n_vars = grads[0].len();
|
|
|
|
|
|
|
| 121 |
let mut saida: Vec<Option<Vec<f32>>> = vec![None; n_vars];
|
| 122 |
for i in 0..n_perdas {
|
| 123 |
-
// projeta g_i contra cada g_j
|
| 124 |
for j in 0..n_perdas {
|
| 125 |
if i == j { continue; }
|
| 126 |
let mut ip = 0f64;
|
| 127 |
let mut nj = 0f64;
|
| 128 |
for v in 0..n_vars {
|
| 129 |
-
if let (Some(a), Some(b)) = (&grads[i][v], &
|
| 130 |
for k in 0..a.len().min(b.len()) {
|
| 131 |
ip += a[k] as f64 * b[k] as f64;
|
| 132 |
nj += b[k] as f64 * b[k] as f64;
|
|
@@ -136,7 +141,7 @@ pub fn pcgrad_achatado(mut grads: Vec<Vec<Option<Vec<f32>>>>) -> Vec<Option<Vec<
|
|
| 136 |
if ip < 0.0 && nj > 1e-12 {
|
| 137 |
let alfa = (ip / nj) as f32;
|
| 138 |
for v in 0..n_vars {
|
| 139 |
-
if let Some(bv) =
|
| 140 |
if let Some(a) = &mut grads[i][v] {
|
| 141 |
for k in 0..a.len().min(bv.len()) {
|
| 142 |
a[k] -= alfa * bv[k];
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
/// PCGrad multi-perda sobre grads achatados (Teo D.7 — projeção por pares).
|
| 118 |
+
/// v070-3 (auditoria): projeta contra os gradientes ORIGINAIS (snapshot) —
|
| 119 |
+
/// a v0.6.0 usava grads[j] já modificados quando j < i, violando a não-piora
|
| 120 |
+
/// pareada ⟨g_i^pc, g_i⟩ ≥ 0 (Yu et al. 2020, Algoritmo 1 usa os originais).
|
| 121 |
+
pub fn pcgrad_achatado(grads: Vec<Vec<Option<Vec<f32>>>>) -> Vec<Option<Vec<f32>>> {
|
| 122 |
let n_perdas = grads.len();
|
| 123 |
let n_vars = grads[0].len();
|
| 124 |
+
let originais = grads.clone(); // snapshot imutável dos originais
|
| 125 |
+
let mut grads = grads;
|
| 126 |
let mut saida: Vec<Option<Vec<f32>>> = vec![None; n_vars];
|
| 127 |
for i in 0..n_perdas {
|
| 128 |
+
// projeta g_i contra cada g_j CONFLITANTE ORIGINAL
|
| 129 |
for j in 0..n_perdas {
|
| 130 |
if i == j { continue; }
|
| 131 |
let mut ip = 0f64;
|
| 132 |
let mut nj = 0f64;
|
| 133 |
for v in 0..n_vars {
|
| 134 |
+
if let (Some(a), Some(b)) = (&grads[i][v], &originais[j][v]) {
|
| 135 |
for k in 0..a.len().min(b.len()) {
|
| 136 |
ip += a[k] as f64 * b[k] as f64;
|
| 137 |
nj += b[k] as f64 * b[k] as f64;
|
|
|
|
| 141 |
if ip < 0.0 && nj > 1e-12 {
|
| 142 |
let alfa = (ip / nj) as f32;
|
| 143 |
for v in 0..n_vars {
|
| 144 |
+
if let Some(bv) = &originais[j][v] {
|
| 145 |
if let Some(a) = &mut grads[i][v] {
|
| 146 |
for k in 0..a.len().min(bv.len()) {
|
| 147 |
a[k] -= alfa * bv[k];
|
src/tronco.rs
CHANGED
|
@@ -23,6 +23,12 @@ pub struct Tronco {
|
|
| 23 |
/// gamma FiLM por nível (2*L+2: [escala, deslocamento] × nível) — ADBA.
|
| 24 |
pub film_gamma: Option<Var>,
|
| 25 |
pub film_beta: Option<Var>,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
pub n_niveis: usize,
|
| 27 |
}
|
| 28 |
|
|
@@ -43,12 +49,13 @@ impl Tronco {
|
|
| 43 |
} else {
|
| 44 |
None
|
| 45 |
};
|
| 46 |
-
let (film_gamma, film_beta) = if adba.ativo {
|
| 47 |
let g = Tensor::ones((l + 1, m.d_modelo), candle_core::DType::F32, dev)?;
|
| 48 |
let b = Tensor::zeros((l + 1, m.d_modelo), candle_core::DType::F32, dev)?;
|
| 49 |
-
|
|
|
|
| 50 |
} else {
|
| 51 |
-
(None, None)
|
| 52 |
};
|
| 53 |
Ok(Tronco {
|
| 54 |
blocos_atencao: blocos,
|
|
@@ -57,21 +64,25 @@ impl Tronco {
|
|
| 57 |
ancora,
|
| 58 |
film_gamma,
|
| 59 |
film_beta,
|
|
|
|
| 60 |
n_niveis: l,
|
| 61 |
})
|
| 62 |
}
|
| 63 |
|
| 64 |
/// Injeção FiLM do resumo-âncora nas posições do nível `nivel`
|
| 65 |
/// (ADBA (i): o resumo depende APENAS de c_{<ℓ} — paralelismo preservado).
|
|
|
|
|
|
|
| 66 |
pub fn aplicar_film(&self, x: &Tensor, resumo: &Tensor, nivel: usize) -> Result<Tensor> {
|
| 67 |
-
match (&self.film_gamma, &self.film_beta) {
|
| 68 |
-
(Some(g), Some(b)) => {
|
| 69 |
let d = x.dims()[2];
|
| 70 |
let bsz = resumo.dims()[0].max(1);
|
| 71 |
let gv = g.as_tensor().get(nivel)?.reshape((1, 1, d))?;
|
| 72 |
let bv = b.as_tensor().get(nivel)?.reshape((1, 1, d))?;
|
| 73 |
-
|
| 74 |
-
|
|
|
|
| 75 |
let gate = gv.broadcast_mul(&r.tanh()?)?.affine(1.0, 1.0)?; // 1 + γ·tanh(r)
|
| 76 |
let xg = x.broadcast_mul(&gate)?;
|
| 77 |
let desloc = bv.broadcast_mul(&r)?;
|
|
@@ -132,6 +143,11 @@ impl Tronco {
|
|
| 132 |
|
| 133 |
/// Forward incremental (inferência): bloco novo (b, n_novo, d) + caches.
|
| 134 |
/// `niveis_novos`/`niveis_cache` mapeiam posições para níveis.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
pub fn forward_nivel(
|
| 136 |
&self,
|
| 137 |
bloco_x: &Tensor,
|
|
@@ -139,6 +155,8 @@ impl Tronco {
|
|
| 139 |
mascara_nova: &Tensor,
|
| 140 |
niveis_novos: &[usize],
|
| 141 |
niveis_totais: &[usize],
|
|
|
|
|
|
|
| 142 |
) -> Result<Tensor> {
|
| 143 |
let mut x = bloco_x.clone();
|
| 144 |
for (i, bloco) in self.blocos_atencao.iter().enumerate() {
|
|
@@ -146,7 +164,14 @@ impl Tronco {
|
|
| 146 |
caches.push(CacheKv::default());
|
| 147 |
}
|
| 148 |
x = bloco.fwd(&x, Some(mascara_nova), &mut caches[i], Some(niveis_novos), Some(niveis_totais))?;
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
}
|
| 151 |
let mut cache_ssd = None;
|
| 152 |
x = self.bloco_ssd.fwd(&x, &mut cache_ssd)?;
|
|
@@ -177,6 +202,9 @@ impl Tronco {
|
|
| 177 |
if let Some(b) = &self.film_beta {
|
| 178 |
v.push(b.clone());
|
| 179 |
}
|
|
|
|
|
|
|
|
|
|
| 180 |
v
|
| 181 |
}
|
| 182 |
}
|
|
|
|
| 23 |
/// gamma FiLM por nível (2*L+2: [escala, deslocamento] × nível) — ADBA.
|
| 24 |
pub film_gamma: Option<Var>,
|
| 25 |
pub film_beta: Option<Var>,
|
| 26 |
+
/// W_f do FiLM (r-04 §1: x̃ = x⊙(1+γ·tanh(W_f·h)) + β·(W_f·h)) — ZERO no
|
| 27 |
+
/// nascimento: tanh(0)=0 e β·0=0 ⇒ identidade bit-a-bit (Teo R.2).
|
| 28 |
+
/// v070-1 (auditoria): a v0.6.0 injetava o resumo do âncora DIRETO (projeção
|
| 29 |
+
/// aleatória) — nascimento não-neutro nos níveis ≥ 2; correção restaura o
|
| 30 |
+
/// enunciado R.2 independentemente de γ=1.
|
| 31 |
+
pub film_w: Option<crate::camadas::Linear>,
|
| 32 |
pub n_niveis: usize,
|
| 33 |
}
|
| 34 |
|
|
|
|
| 49 |
} else {
|
| 50 |
None
|
| 51 |
};
|
| 52 |
+
let (film_gamma, film_beta, film_w) = if adba.ativo {
|
| 53 |
let g = Tensor::ones((l + 1, m.d_modelo), candle_core::DType::F32, dev)?;
|
| 54 |
let b = Tensor::zeros((l + 1, m.d_modelo), candle_core::DType::F32, dev)?;
|
| 55 |
+
let w = crate::camadas::Linear::zeros_sem_vies(m.d_modelo, m.d_modelo, dev)?;
|
| 56 |
+
(Some(Var::from_tensor(&g)?), Some(Var::from_tensor(&b)?), Some(w))
|
| 57 |
} else {
|
| 58 |
+
(None, None, None)
|
| 59 |
};
|
| 60 |
Ok(Tronco {
|
| 61 |
blocos_atencao: blocos,
|
|
|
|
| 64 |
ancora,
|
| 65 |
film_gamma,
|
| 66 |
film_beta,
|
| 67 |
+
film_w,
|
| 68 |
n_niveis: l,
|
| 69 |
})
|
| 70 |
}
|
| 71 |
|
| 72 |
/// Injeção FiLM do resumo-âncora nas posições do nível `nivel`
|
| 73 |
/// (ADBA (i): o resumo depende APENAS de c_{<ℓ} — paralelismo preservado).
|
| 74 |
+
/// r-04 §1: x̃ = x⊙(1+γ·tanh(W_f·h)) + β·(W_f·h) com W_f=0 no nascimento
|
| 75 |
+
/// ⇒ identidade bit-a-bit para QUALQUER resumo (Teo R.2, v070-1).
|
| 76 |
pub fn aplicar_film(&self, x: &Tensor, resumo: &Tensor, nivel: usize) -> Result<Tensor> {
|
| 77 |
+
match (&self.film_gamma, &self.film_beta, &self.film_w) {
|
| 78 |
+
(Some(g), Some(b), Some(wf)) => {
|
| 79 |
let d = x.dims()[2];
|
| 80 |
let bsz = resumo.dims()[0].max(1);
|
| 81 |
let gv = g.as_tensor().get(nivel)?.reshape((1, 1, d))?;
|
| 82 |
let bv = b.as_tensor().get(nivel)?.reshape((1, 1, d))?;
|
| 83 |
+
// r = W_f·resumo — 0 no nascimento (identidade garantida)
|
| 84 |
+
let r = wf.fwd(resumo)?.reshape((bsz, 1, d))?;
|
| 85 |
+
// x ⊙ (1 + γ·tanh(r)) + β·r
|
| 86 |
let gate = gv.broadcast_mul(&r.tanh()?)?.affine(1.0, 1.0)?; // 1 + γ·tanh(r)
|
| 87 |
let xg = x.broadcast_mul(&gate)?;
|
| 88 |
let desloc = bv.broadcast_mul(&r)?;
|
|
|
|
| 143 |
|
| 144 |
/// Forward incremental (inferência): bloco novo (b, n_novo, d) + caches.
|
| 145 |
/// `niveis_novos`/`niveis_cache` mapeiam posições para níveis.
|
| 146 |
+
/// `film` = Some((resumo, nivel 1-indexado)) aplica o FiLM nas posições novas
|
| 147 |
+
/// após o bloco 0 — PARIDADE com forward_treino (v070-2: a v0.6.0 não
|
| 148 |
+
/// aplicava FiLM na inferência incremental — treino↔geração divergiam).
|
| 149 |
+
/// `saida_b0` = Some(&mut Tensor) recebe os estados pós-bloco-0 das
|
| 150 |
+
/// posições novas (para o âncora acumular contexto nos níveis seguintes).
|
| 151 |
pub fn forward_nivel(
|
| 152 |
&self,
|
| 153 |
bloco_x: &Tensor,
|
|
|
|
| 155 |
mascara_nova: &Tensor,
|
| 156 |
niveis_novos: &[usize],
|
| 157 |
niveis_totais: &[usize],
|
| 158 |
+
film: Option<(&Tensor, usize)>,
|
| 159 |
+
mut saida_b0: Option<&mut Tensor>,
|
| 160 |
) -> Result<Tensor> {
|
| 161 |
let mut x = bloco_x.clone();
|
| 162 |
for (i, bloco) in self.blocos_atencao.iter().enumerate() {
|
|
|
|
| 164 |
caches.push(CacheKv::default());
|
| 165 |
}
|
| 166 |
x = bloco.fwd(&x, Some(mascara_nova), &mut caches[i], Some(niveis_novos), Some(niveis_totais))?;
|
| 167 |
+
if i == 0 {
|
| 168 |
+
if let Some(slot) = saida_b0.as_deref_mut() {
|
| 169 |
+
*slot = x.clone();
|
| 170 |
+
}
|
| 171 |
+
if let Some((resumo, nivel)) = film {
|
| 172 |
+
x = self.aplicar_film(&x, resumo, nivel)?;
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
}
|
| 176 |
let mut cache_ssd = None;
|
| 177 |
x = self.bloco_ssd.fwd(&x, &mut cache_ssd)?;
|
|
|
|
| 202 |
if let Some(b) = &self.film_beta {
|
| 203 |
v.push(b.clone());
|
| 204 |
}
|
| 205 |
+
if let Some(wf) = &self.film_w {
|
| 206 |
+
v.push(wf.w.clone());
|
| 207 |
+
}
|
| 208 |
v
|
| 209 |
}
|
| 210 |
}
|
target/.rustc_info.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"rustc_fingerprint":1392034485640314244,"outputs":{"3011779423962739885":{"success":true,"status":"","code":0,"stdout":"rustc 1.99.1 (58cf8f9c3 2026-10-13)\nbinary: rustc\ncommit-hash: 58cf8f9c35b355199727627d07ae34a4e2846d67\ncommit-date: 2026-10-13\nhost: x86_64-unknown-linux-gnu\nrelease: 1.99.1\nLLVM version: 23.1.1\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/z/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_primitive_alignment=\"16\"\ntarget_has_atomic_primitive_alignment=\"32\"\ntarget_has_atomic_primitive_alignment=\"64\"\ntarget_has_atomic_primitive_alignment=\"8\"\ntarget_has_atomic_primitive_alignment=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
|
target/release/.cargo-artifact-lock
ADDED
|
File without changes
|
target/release/.cargo-build-lock
ADDED
|
File without changes
|
target/release/.cargo-lock
ADDED
|
File without changes
|
target/release/.fingerprint/ahash-1239384bf8e0b488/dep-lib-ahash
ADDED
|
Binary file (14 Bytes). View file
|
|
|
target/release/.fingerprint/ahash-1239384bf8e0b488/invoked.timestamp
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
This file has an mtime of when this was started.
|
target/release/.fingerprint/ahash-1239384bf8e0b488/lib-ahash
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
f4811df8a9b62522
|
target/release/.fingerprint/ahash-1239384bf8e0b488/lib-ahash.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"rustc":11835527281324007204,"features":"[\"default\", \"getrandom\", \"runtime-rng\", \"serde\", \"std\"]","declared_features":"[\"atomic-polyfill\", \"compile-time-rng\", \"const-random\", \"default\", \"getrandom\", \"nightly-arm-aes\", \"no-rng\", \"runtime-rng\", \"serde\", \"std\"]","target":8470944000320059508,"profile":854319013279132213,"path":2545223575150465703,"deps":[[966925859616469517,"build_script_build",false,17893473302195841958],[4321869508056025743,"zerocopy",false,12413866475766502215],[5855319743879205494,"once_cell",false,3056825777376379244],[6557439603276904804,"serde",false,291644942871934544],[15482175856213997617,"cfg_if",false,15689703071604503671],[18408407127522236545,"getrandom",false,15498450006170333911]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/ahash-1239384bf8e0b488/dep-lib-ahash","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
target/release/.fingerprint/ahash-83fe43707d427afc/build-script-build-script-build
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
eb483a6126879ab6
|
target/release/.fingerprint/ahash-83fe43707d427afc/build-script-build-script-build.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"rustc":11835527281324007204,"features":"[\"default\", \"getrandom\", \"runtime-rng\", \"serde\", \"std\"]","declared_features":"[\"atomic-polyfill\", \"compile-time-rng\", \"const-random\", \"default\", \"getrandom\", \"nightly-arm-aes\", \"no-rng\", \"runtime-rng\", \"serde\", \"std\"]","target":17883862002600103897,"profile":17078157726716824978,"path":3694579789264125173,"deps":[[5398981501050481332,"version_check",false,4457338824097212302]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/ahash-83fe43707d427afc/dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
target/release/.fingerprint/ahash-83fe43707d427afc/dep-build-script-build-script-build
ADDED
|
Binary file (14 Bytes). View file
|
|
|