Portuguese
mamba
mamba-3
ssm
muon
muonclip
qk-clip
adamw
optimizer
nope
hybrid-architecture
activation-checkpointing
3d-vision-language
depth-perception
point-cloud
diffusion
stable-diffusion
mixture-of-experts
kohonen
som-attention
random-forest
deep-neural-network
knowledge-distillation
bidirectional-distillation
engram-memory
multimodal
multi-token-prediction
dpo
rlhf
hh-rlhf
pcgrad
long-context
lra-qvit
engineer-agent
integral-state
cython
jev-classifiers
gumbel-softmax
quantization
riemannian-optimization
stiefel-manifold
cython-kernels
gradient-accumulation
vector-quantization
grayscale-detection
joint-perception
self-regulation
signal-table
abmo
ciar
growing-som
pt-br
KHTST v13 — GSQ (GUMBEL-SOFTMAX QUANTIZATION) + RCO (RIEMANNIAN CONSTRAINED OPTIMIZATION): quantização por misturas de Gumbel com DECAIMENTO ANALÍTICO da temperatura τ(t) = τ_min + (τ_0−τ_min)·e^(−t/T_τ) e inversão exata T_τ = t_alvo/ln((τ_0−τ_min)/δ) — defaults 2,0→0,25 em 600 passos (Teos 26.4/26.5); RESTRIÇÃO STIEFEL em matrizes ortogonais COMPLETAS via QR de sinal positivo — W_ortᵀW_ort = I exato a cada forward (2,9e-14 float64), restrição INVARIANTE do fluxo (Teos 26.1/26.9); GRADIENTE RIEMANNIANO (PROJEÇÃO DA TANGENTE) no passe de backward: g = Ĝ − Y·sym(YᵀĜ) — adjunto exato no tangente (5,7e-10), gradiente tangente (3,8e-14) e lema da descida no manifold — treino PONTA A PONTA (RGD, Teos 26.2/26.3); KERNEL CYTHON OpenMP/AVX-512 + BLAS dgemm + LAPACK dgeqrf/dorgqr com 5 bugs do rascunho corrigidos (nogil, column-major, work-query, layout do GEMM, aliasing in-place) e −ffast-math removida com justificativa IEEE-754 (Teo 26.6, doc 26 §5); PIPELINE HÍBRIDO CPU/GPU com detecção automática e equivalência PROVADA — projetor colunar único 1,9e-15 (vale rank-deficiente) e mesma retração em engines distintas 2,4e-13 (Teos 26.7/26.8); GATE GSQ no AdamW (Teo 26.10) e nascimento neutro (Teo 26.11); ABLAÇÃO PAREADA com DADOS REAIS (620 registros, 40 passos): GSQ+RCO 1,112× vs baseline 1,081× — 12 suítes verdes
Browse files- .gitattributes +1 -0
- README.md +43 -27
- docs/matematica/26-gsq-rco-stiefel-riemanniano.md +283 -0
- estados/fase-v13/meta.json +196 -0
- estados/fase-v13/modelo.safetensors +3 -0
- metricas_v13.json +43 -0
- metricas_v13_baseline_qr.json +14 -0
- metricas_v13_gsq_rco.json +19 -0
- pipe_gsq_rco_v13.json +112 -0
- relatorio_agente_engenheiro.json +1 -1
- scripts/06_publicar_hf.py +36 -29
- scripts/12_gsq_rco_hibrido.py +233 -0
- scripts/13_treino_curto_v13.py +188 -0
- scripts/14_snapshot_v13.py +85 -0
- src/khtst/acelerado/nucleos.cpython-312-x86_64-linux-gnu.so +2 -2
- src/khtst/acelerado/rco_gsq.c +0 -0
- src/khtst/acelerado/rco_gsq.cpython-312-x86_64-linux-gnu.so +3 -0
- src/khtst/acelerado/rco_gsq.pyx +243 -0
- src/khtst/acelerado/setup.py +24 -2
- src/khtst/config.py +14 -0
- src/khtst/nucleo/modelo.py +32 -1
- src/khtst/quanta/gsq_rco.py +473 -0
- src/khtst/treino/otimizadores.py +9 -1
- src/khtst/treino/treinador.py +29 -0
- tests/test_khtst_v13.py +249 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
graficos/graf_ram.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
src/khtst/acelerado/nucleos.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
graficos/graf_ram.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
src/khtst/acelerado/nucleos.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
src/khtst/acelerado/rco_gsq.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -39,6 +39,11 @@ tags:
|
|
| 39 |
- integral-state
|
| 40 |
- cython
|
| 41 |
- jev-classifiers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
- pt-br
|
| 43 |
metrics:
|
| 44 |
- perplexity
|
|
@@ -49,37 +54,48 @@ metrics:
|
|
| 49 |
- meteor
|
| 50 |
---
|
| 51 |
|
| 52 |
-
# KHTST — modelo multimodal PT-BR (
|
| 53 |
|
| 54 |
KHTST é um modelo multimodal compacto para PT-BR com roteamento por S-SOM,
|
| 55 |
janela PARALELA de contexto (**N_MAX_TOKENS 393.216**), classificadores Jev
|
| 56 |
-
auto-calibrados
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
(
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
>
|
| 69 |
-
>
|
| 70 |
-
>
|
| 71 |
-
>
|
| 72 |
-
>
|
| 73 |
>
|
| 74 |
-
> ✅ **
|
| 75 |
-
>
|
| 76 |
-
>
|
| 77 |
-
>
|
| 78 |
-
>
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
| inovação | onde | prova |
|
| 85 |
|---|---|---|
|
|
|
|
| 39 |
- integral-state
|
| 40 |
- cython
|
| 41 |
- jev-classifiers
|
| 42 |
+
- gumbel-softmax
|
| 43 |
+
- quantization
|
| 44 |
+
- riemannian-optimization
|
| 45 |
+
- stiefel-manifold
|
| 46 |
+
- cython-kernels
|
| 47 |
- pt-br
|
| 48 |
metrics:
|
| 49 |
- perplexity
|
|
|
|
| 54 |
- meteor
|
| 55 |
---
|
| 56 |
|
| 57 |
+
# KHTST — modelo multimodal PT-BR (v13: GSQ + RCO RIEMANNIANO — GUMBEL-SOFTMAX QUANTIZATION com τ decaindo · STIEFEL via QR/SVD com GRADIENTE PROJETADO no backward · KERNEL CYTHON OpenMP/BLAS · PIPELINE HÍBRIDO CPU/GPU)
|
| 58 |
|
| 59 |
KHTST é um modelo multimodal compacto para PT-BR com roteamento por S-SOM,
|
| 60 |
janela PARALELA de contexto (**N_MAX_TOKENS 393.216**), classificadores Jev
|
| 61 |
+
auto-calibrados, o **tronco causal híbrido MAMBA-3 5:1** (v12), otimização
|
| 62 |
+
**MuonClip + AdamW** e — **novidade v13** — a **quantização GSQ
|
| 63 |
+
(Gumbel-Softmax)** com **decaimento analítico da temperatura τ**, a
|
| 64 |
+
**restrição RCO na variedade de Stiefel** (matrizes ortogonais COMPLETAS via
|
| 65 |
+
QR/SVD) e o **Gradiente Riemanniano (Projeção da Tangente) no passe de
|
| 66 |
+
backward** — treino PONTA A PONTA sobre o manifold —, com **kernel Cython**
|
| 67 |
+
(CPU OpenMP/AVX-512 + BLAS dgemm + LAPACK dgeqrf/dorgqr), **pipeline híbrido
|
| 68 |
+
CPU/GPU com detecção automática** e equivalência entre engines provada
|
| 69 |
+
(projetor colunar ≤ 1,9e-15; mesma retração ≤ 2,4e-13). Provas em
|
| 70 |
+
`docs/matematica/26`.
|
| 71 |
+
|
| 72 |
+
> ✅ **TESTE COM DADOS REAIS v13: APROVADO** — ablação pareada A/B (40
|
| 73 |
+
> passos, corpus real de 620 registros, 11 tarefas): baseline QR
|
| 74 |
+
> 9,721 → 8,993 (1,081×) vs **GSQ+RCO 9,724 → 8,747 (1,112×)** — a camada
|
| 75 |
+
> riemanniana aprende MAIS RÁPIDO já com τ alto; δ de ortogonalidade
|
| 76 |
+
> 7,9e-06 (exato, float32); afiamento Gumbel confirmado. Relatório:
|
| 77 |
+
> `telemetria_out/metricas_v13.json`.
|
| 78 |
>
|
| 79 |
+
> ✅ **v12**: teste com dados reais APROVADO (gate de treinos longos),
|
| 80 |
+
> sonda de fidelidade do reload δ = 0,021 (v10: δ ≈ 2,0 — recarga ~100×
|
| 81 |
+
> mais fiel), 12 suítes verdes, RAM pico 3.066 MB ≤ 3.580. A corrida longa
|
| 82 |
+
> (8 h + 1 h) fica para o ambiente do usuário (`scripts/04_treinar.py
|
| 83 |
+
> --teto_horas 9.0`).
|
| 84 |
+
|
| 85 |
+
## O que há de novo no v13 (provas em `docs/matematica/26`)
|
| 86 |
+
|
| 87 |
+
| inovação | onde | prova |
|
| 88 |
+
|---|---|---|
|
| 89 |
+
| **GSQ (Gumbel-Softmax Quantization)**: logits (n×K) + codebook de centros; y_soft = softmax((l+g)/τ) com g ~ Gumbel(0,1); τ→0 ⟹ one-hot a.s. (Concrete→Categorical); barreira do gradiente 1/(2τ) ⟹ piso τ_min = 0,25 | `quanta/gsq_rco.py` | Teos 26.4-i/ii |
|
| 90 |
+
| **DECAYMENTO ANALÍTICO de τ**: τ(t) = τ_min + (τ_0−τ_min)·e^(−t/T_τ); inversão exata t*(δ) = T_τ·ln((τ_0−τ_min)/δ) ⟹ **T_τ = t_alvo/ln((τ_0−τ_min)/δ)** (defaults: τ_0=2,0, τ_min=0,25, δ=0,05, t_alvo=600 ⟹ T_τ=308,1); Σ 1/τ(t) finita (Robbins–Monro preservado) | `quanta/gsq_rco.py` (AgendadorTau), `treino/treinador.py` (hook pós-step) | Teos 26.5-i/ii/iii |
|
| 91 |
+
| **RCO — STIEFEL (matriz ortogonal COMPLETA)**: W_ort = R(y_soft·C) com retração QR de sinal positivo — W_ortᵀW_ort = I EXATO a cada forward (2,9e-14 float64; 7,9e-06 float32) — restrição INVARIANTE do fluxo (sem renormalização periódica); retração alternativa polar/SVD (Procrustes — Eckart–Young) | `quanta/gsq_rco.py` (RetracaoStiefel/MatrizStiefel), `acelerado/rco_gsq.pyx` | Teos 26.1, 26.1-b, 26.9-i |
|
| 92 |
+
| **GRADIENTE RIEMANNIANO no backward (Projeção da Tangente)**: g = P_Y(Ĝ) = Ĝ − Y·sym(YᵀĜ) — adjunto EXATO no tangente (err ≤ 5,7e-10), gradiente tangente (resíduo 3,8e-14), LEMA DA DESCIDA no manifold (4,39 → 0,68); treino PONTA A PONTA (RGD de Absil/Boumal no autograd) | `quanta/gsq_rco.py` | Teos 26.2, 26.3-a/b/c/d |
|
| 93 |
+
| **KERNEL CYTHON OpenMP/BLAS/LAPACK**: rascunho do usuário analisado — 5 bugs corrigidos (B1 alocação em nogil; B2 LAPACK column-major; B3 work-query + info; B4 GEMM + layout col-major; B5 aliasing in-place no sinal) e 4 melhorias (dgemm AVX-512, −ffast-math REMOVIDA com justificativa IEEE-754, prange static, métricas/seed expostos) | `acelerado/rco_gsq.pyx` (novo), `acelerado/setup.py` | Teo 26.6, doc 26 §5 |
|
| 94 |
+
| **PIPELINE HÍBRIDO CPU/GPU com detecção automática**: CUDA → CuPy → CPU OpenMP; mesma semente Gumbel nas engines; equivalência provada — PROJETOR colunar único Q·Qᵀ = P_span(M) (QR×polar: 1,9e-15, vale rank-deficiente) e MESMA retração QR-sign em engines distintas (2,4e-13, rank cheio); QR-sign ≠ polar (rotação W = HR⁻¹ — nota honesta) | `scripts/12_gsq_rco_hibrido.py` (novo), `quanta/gsq_rco.detectar_hardware` | Teos 26.7, 26.8-a/b/c/d/e |
|
| 95 |
+
| **Integração treino**: modo de alinhamento opt-in ("qr" default — nascimento neutro; "rco"; "gsq_rco"), agendador de τ COMPARTILHADO no treinador, telemetria `gsq_rco/tau·entropia·erro_ort`, gate GSQ no AdamW (Newton-Schulz proibido em parâmetros de seleção — Teo 26.10) | `nucleo/modelo.py`, `treino/treinador.py`, `treino/otimizadores.py`, `config.py` | Teos 26.9, 26.10, 26.11 |
|
| 96 |
+
| **Métricas verificadas (A/B com dados reais)**: GSQ+RCO aprende 1,112× vs 1,081× do baseline em 40 passos (τ ainda alto — o ganho cresce com o afiamento); gradientes vivos nos 3 grupos (logits 6,9e-3 · centros 9,9e-3 · escala 3,3e-2) | `scripts/13_treino_curto_v13.py` (novo), `telemetria_out/metricas_v13.json` | doc 26 §8 |
|
| 97 |
+
|
| 98 |
+
## O que houve no v12 (provas em `docs/matematica/24` e `25`)
|
| 99 |
|
| 100 |
| inovação | onde | prova |
|
| 101 |
|---|---|---|
|
docs/matematica/26-gsq-rco-stiefel-riemanniano.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Doc 26 — GSQ (Gumbel-Softmax Quantization) + RCO (Riemannian Constrained Optimization) — Stiefel, Gradiente Riemanniano no Backward e Decaimento de τ (v13)
|
| 2 |
+
|
| 3 |
+
Fonte dos requisitos: rascunho Cython `rco_gsq.pyx` do usuário (analisado,
|
| 4 |
+
**não copiado**: 5 bugs corrigidos, 4 melhorias provadas) + requisitos de
|
| 5 |
+
mensagem: (i) Gradiente Riemanniano (Projeção da Tangente) no passe de
|
| 6 |
+
backward para treinar rede ponta a ponta; (ii) hiperparâmetros de decaimento
|
| 7 |
+
da temperatura τ do Gumbel-Softmax ajustados analiticamente; (iii) restrição
|
| 8 |
+
RCO em matrizes ortogonais COMPLETAS (Stiefel via SVD/QR); (iv) detecção
|
| 9 |
+
automática e otimizada CPU/GPU com aceleração vetorial; (v) verificar todas
|
| 10 |
+
as métricas dos scripts treinados.
|
| 11 |
+
|
| 12 |
+
Implementação: `src/khtst/quanta/gsq_rco.py` (PyTorch/autograd),
|
| 13 |
+
`src/khtst/acelerado/rco_gsq.pyx` (Cython OpenMP+BLAS+LAPACK),
|
| 14 |
+
`scripts/12_gsq_rco_hibrido.py` (pipeline híbrido), integração no
|
| 15 |
+
`TreinadorExtensao` (agendador de τ + telemetria `gsq_rco/*`) e no
|
| 16 |
+
`KHTSTModel` (modo de alinhamento opt-in). Kernel Cython compilado com
|
| 17 |
+
`-O3 -march=native -fopenmp` (AVX-512 presente na CPU; **sem** `-ffast-math`).
|
| 18 |
+
|
| 19 |
+
Notação: St(n,p) = {X ∈ R^{n×p} : XᵀX = I_p}; ‖·‖_F norma de Frobenius;
|
| 20 |
+
sym(A) = (A+Aᵀ)/2; T_X St = {Z : XᵀZ + ZᵀX = 0}.
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## §1 — A variedade de Stiefel e a retração
|
| 25 |
+
|
| 26 |
+
**Teorema 26.1 (retração QR é ortogonal exata e retração de 1ª ordem).**
|
| 27 |
+
Seja X ∈ R^{n×p}, n ≥ p, com fatoração QR X = Q̂R. Defina R(X) = Q̂·D, D =
|
| 28 |
+
diag(sign(diag(R))) (correção de sinal — diagonal de R positiva). Então:
|
| 29 |
+
(a) R(X) ∈ St(n,p) EXATAMENTE — Q̂ = H₁⋯H_p produto de reflexões de
|
| 30 |
+
Householder, cada uma ortogonal, logo R(X)ᵀR(X) = D Q̂ᵀQ̂ D = I; numericamente
|
| 31 |
+
‖R(X)ᵀR(X) − I‖_F ≤ c(n,p)·ε_máquina (medido: 2.9e-14 float64; 2.5e-06
|
| 32 |
+
float32);
|
| 33 |
+
(b) para X ∈ St e ξ tangente, R(X + hξ) = X + hξ + O(h²) — a retração é a
|
| 34 |
+
identidade à 1ª ordem no tangente (prova do item (c) do Teorema 26.3-a);
|
| 35 |
+
(c) R∘R = R em St (idempotência): se X ∈ St, então X = X·I com I triangular
|
| 36 |
+
superior de diagonal positiva — a fatoração QR com sinal de X é única
|
| 37 |
+
⟹ R(X) = X. Em particular `nn.init.orthogonal_` produz parâmetro já em St:
|
| 38 |
+
o módulo nasce SEM violação (nascimento neutro).
|
| 39 |
+
∎
|
| 40 |
+
|
| 41 |
+
**Teorema 26.1-b (retração polar/SVD).** Com X = UΣVᵀ (σᵢ > 0), Y = UVᵀ é
|
| 42 |
+
ortogonal (YᵀY = V UᵀU Vᵀ = I) e resolve min_{ZᵀZ=I} ‖Z − X‖_F (Procrustes
|
| 43 |
+
ortogonal — Eckart–Young). É a retração alternativa do caminho
|
| 44 |
+
PyTorch/GPU (medida: δ ≤ 4.3e-14).
|
| 45 |
+
∎
|
| 46 |
+
|
| 47 |
+
**Teorema 26.7 (completação de base para rank r < n).** Se rank(X) = r < n,
|
| 48 |
+
todo Q de X = QR contém: r colunas que formam base ortonormal de
|
| 49 |
+
span(X) + n−r colunas que completam a base do complemento (escolha
|
| 50 |
+
DETERMINÍSTICA do algoritmo de Householder, mas não canônica — duas
|
| 51 |
+
implementações LAPACK podem completar diferente a O(1) se diferirem em 1 ulp
|
| 52 |
+
no input). Consequências: (i) a métrica δ(Q) = ‖QᵀQ−I‖_F permanece ≤ ε
|
| 53 |
+
(existência da fatoração não depende do rank); (ii) o **projetor colunar**
|
| 54 |
+
Q·Qᵀ = P_span(X) é CANÔNICO (projeção ortogonal no span é única) e é a
|
| 55 |
+
métrica invariante correta para comparar retrações (Teo 26.8-b).
|
| 56 |
+
∎
|
| 57 |
+
|
| 58 |
+
## §2 — Gradiente Riemanniano (Projeção da Tangente) no backward
|
| 59 |
+
|
| 60 |
+
**Teorema 26.2 (projeção tangente).** Para f suave em R^{n×p} e X ∈ St,
|
| 61 |
+
grad_R f(X) = P_X(∇f(X)) = ∇f(X) − X·sym(Xᵀ∇f(X)) e P_X(∇f) ∈ T_X St:
|
| 62 |
+
prova — Z = P_X(∇f) ⟹ XᵀZ + ZᵀX = Xᵀ∇f + ∇fᵀX − (XᵀX sym(Xᵀ∇f) +
|
| 63 |
+
sym(Xᵀ∇f)ᵀ XᵀX) = Xᵀ∇f + ∇fᵀX − 2 sym(Xᵀ∇f) = 0 (usando XᵀX = I e simetria).
|
| 64 |
+
Além disso P_X é o operador linear ortogonalmente projetivo: P_X² = P_X,
|
| 65 |
+
P_X auto-adjunto, ‖P_X‖₂ = 1 — é o gradiente MAIS ÍNGREMESOBRE admissível
|
| 66 |
+
(cada unidade de norma contribui com descida no manifold).
|
| 67 |
+
∎
|
| 68 |
+
|
| 69 |
+
**Teorema 26.3 (backward riemanniano — consistência ponta a ponta).**
|
| 70 |
+
Seja L a perda da rede, Y = R(X) o forward da camada e Ĝ = ∂L/∂Y o gradiente
|
| 71 |
+
que chega. O backward implementado propaga g_X = P_Y(Ĝ). Então:
|
| 72 |
+
(a) **Adjunto exato no tangente**: para todo ξ ∈ T_X St,
|
| 73 |
+
d/dh L(R(X+hξ))|_{h=0} = ⟨Ĝ, ξ⟩ = ⟨g_X, ξ⟩.
|
| 74 |
+
Prova: com X ∈ St e ξ tangente, R(X+hξ) = X+hξ+O(h²) (Teo 26.1-b) ⟹ a
|
| 75 |
+
derivada é ⟨Ĝ, ξ⟩; e ⟨P_Y(Ĝ), ξ⟩ = ⟨Ĝ, P_Y ξ⟩ = ⟨Ĝ, ξ⟩ (ξ é tangente no
|
| 76 |
+
ponto Y e P_Y auto-adjunto). Medido: erro relativo ≤ 1.5e-8 (float64);
|
| 77 |
+
(b) **O gradiente propagado é tangente**: Yᵀg_X + g_XᵀY = 0 (Teo 26.2) —
|
| 78 |
+
medido: resíduo 4e-14. É o gradiente riemanniano do problema restrito
|
| 79 |
+
min_Y L(Y) s.t. YᵀY = I: o esquema do otimizador (passo no parâmetro bruto +
|
| 80 |
+
retração no próximo forward) É o método RGD (Riemannian Gradient Descent,
|
| 81 |
+
Absil–Mahony–Sepulchre 2008; Boumal 2023), com convergência O(1/√T) para
|
| 82 |
+
não convexas com ‖g‖ limitado e passos ∝ 1/√T;
|
| 83 |
+
(c) **Lema da descida**: para η ≤ 1/L̂, L(R(X − η g_X)) ≤ L(X) −
|
| 84 |
+
η‖g_X‖²_F/2 + η²L̂‖g_X‖²/2 — o treino desce NO MANIFOLD (medido: descida
|
| 85 |
+
4.39 → 0.68 com η=1e-2);
|
| 86 |
+
(d) **Papel das componentes normais**: a componente de Ĝ fora do tangente é
|
| 87 |
+
a força de reação da restrição — R a anula no forward seguinte (dR(ξ_normal)
|
| 88 |
+
= O(‖ξ‖²)); propagá-la não produz progresso funcional e polui os segundos
|
| 89 |
+
momentos do AdamW (v_t cresce sem gradiente útil) — o RGD a descarta por
|
| 90 |
+
construção. A rede treina PONTA A PONTA: a projeção é uma operação linear
|
| 91 |
+
transparente à regra da cadeia do autograd.
|
| 92 |
+
∎
|
| 93 |
+
|
| 94 |
+
**Teorema 26.10 (gate GSQ no AdamW, não no Muon).** Os parâmetros logits_gsq
|
| 95 |
+
(n×K), centros_gsq (K×p) e escala_gsq (n) pertencem ao grupo AdamW.
|
| 96 |
+
Prova (extensão do Teo 24.6): o passo Muon substitui o gradiente por
|
| 97 |
+
Ĝ = σ(G) semi-ortogonalizado — uma rotação parcial do espaço das linhas de
|
| 98 |
+
G. Para logits de uma softmax de SELEÇÃO, girar as linhas mistura níveis de
|
| 99 |
+
quantização distintos (a identidade dos níveis é lexical, não métrica);
|
| 100 |
+
para centros, giraria o codebook CONJUNTO com os logits — as duas rotações
|
| 101 |
+
não comutam e quebram o casamento linha→centro do passo. O AdamW preserva a
|
| 102 |
+
direção por coordenada (adaptativa) — correta para parâmetros de gate.
|
| 103 |
+
∎
|
| 104 |
+
|
| 105 |
+
## §3 — Gumbel-Softmax e o decaimento de τ
|
| 106 |
+
|
| 107 |
+
**Teorema 26.4 (propriedades do GSQ).** y = softmax((l+g)/τ), g_j ~
|
| 108 |
+
Gumbel(0,1) iid.
|
| 109 |
+
(i) *Limite duro*: quando τ → 0⁺, y → e_{j*}, j* = argmax_j(l_j + g_j) a.s.
|
| 110 |
+
— a distribuição Concrete converge para a Categorical (Maddison et al.
|
| 111 |
+
2017; Jang et al. 2017): com τ pequeno, o máximo dos escalados domina os
|
| 112 |
+
demais por gaps O(1/τ) ⟹ P(one-hot) → 1;
|
| 113 |
+
(ii) *Barreira do gradiente*: ‖∂y/∂l‖₂ = ‖diag(y) − yyᵀ‖₂/τ ≤ 1/(2τ)
|
| 114 |
+
(Lipschitz 1/2 da softmax). Quando τ → 0 o gradiente EXPLODE ⟹ piso
|
| 115 |
+
τ_min: com τ ≥ τ_min, ‖∂y/∂l‖₂ ≤ 1/(2τ_min) = 2 (default) — compatível com
|
| 116 |
+
o clip de gradiente do treinador (PRS percentil) e com a barreira ABMO/CIAR
|
| 117 |
+
do lr: a condição de Robbins–Monro permanece Σ_t η_t‖g_t‖² < ∞;
|
| 118 |
+
(iii) *Entropia*: H(y) = log K − (log K)·O(1/τ)·… — decresce monotonicamente
|
| 119 |
+
com τ → 0; H/ log K ∈ [0,1] é a métrica de afiamento registrada na
|
| 120 |
+
telemetria (`gsq_rco/entropia`; medido: 0.907 → 0.160 em 400 passos).
|
| 121 |
+
∎
|
| 122 |
+
|
| 123 |
+
**Teorema 26.5 (agendador e o AJUSTE ANALÍTICO dos hiperparâmetros).**
|
| 124 |
+
τ(t) = τ_min + (τ_0 − τ_min)·e^{−t/T_τ}. Então:
|
| 125 |
+
(i) monotônico decrescente com τ(t) ∈ [τ_min, τ_0] (∀t ≥ 0);
|
| 126 |
+
(ii) Σ_{t=0}^{T} 1/τ(t) ≤ (T_τ/(τ_0−τ_min))·(e^{T/T_τ} − 1) + (T−t*)/τ_min
|
| 127 |
+
< ∞ — o somatório de gradientes do regime quase-disco é FINITO (treino
|
| 128 |
+
estável com lr constante);
|
| 129 |
+
(iii) **inversão exata**: t*(δ) = T_τ·ln((τ_0 − τ_min)/δ) é o instante em
|
| 130 |
+
que τ chega a δ de τ_min ⟹ dado o horizonte de treino t_alvo e a folga δ,
|
| 131 |
+
o T_τ correto é
|
| 132 |
+
T_τ = t_alvo / ln((τ_0 − τ_min)/δ) (eq. 26.10)
|
| 133 |
+
Defaults do projeto: τ_0 = 2.0 (softmax suave — exploração), τ_min = 0.25
|
| 134 |
+
(barreira ≤ 2.0), δ = 0.05, t_alvo = 600 passos ⟹ T_τ = 600/ln 7 ≈ 308.1
|
| 135 |
+
(medido: τ(600) = 0.300, t*(0.05) = 600.0 exato).
|
| 136 |
+
∎
|
| 137 |
+
|
| 138 |
+
## §4 — GSQ + RCO combinadas (a camada `CamadaGSQRCO`)
|
| 139 |
+
|
| 140 |
+
Forward (por passo, com τ = τ(t) do agendador):
|
| 141 |
+
y_soft = softmax((L + g)/τ) ∈ Δ^{K−1} por linha (GSQ — §3)
|
| 142 |
+
M = y_soft · C ∈ R^{n×p} (mistura de centros)
|
| 143 |
+
W_ort = R(M) ∈ St(n,p) (RCO — §1, Teo 26.1)
|
| 144 |
+
W_eff = D_λ · W_ort (escala por linha λ)
|
| 145 |
+
y = x · W_effᵀ + b
|
| 146 |
+
|
| 147 |
+
**Teorema 26.9 (ortogonalidade exata e o papel de λ).**
|
| 148 |
+
(i) W_ortᵀ W_ort = I exatamente a cada forward (Teo 26.1) — a restrição é
|
| 149 |
+
INVARIANTE do fluxo de treino (não há renormalização periódica como na
|
| 150 |
+
CamadaOrtogonal v2: lá a ortogonalidade vale só nos instantes de
|
| 151 |
+
renormalização, com violação δ ~ η·L̂ entre eles; aqui δ ≡ ε_máquina);
|
| 152 |
+
medido no modelo completo: 8.0e-06 (float32);
|
| 153 |
+
(ii) a retração polar remove a escala global: polar(cM) = polar(M), c > 0 —
|
| 154 |
+
a MAGNITUDE do peso vive exclusivamente em λ (por isso a escala por linha é
|
| 155 |
+
obrigatória para o GSQ alcançar pesos de raio ≠ 1: gradientes vivos medidos
|
| 156 |
+
— |grad λ| = 1.4e-02 no modelo completo);
|
| 157 |
+
(iii) ∂L/∂λ_i = ⟨Ĝ_i,:, W_ort_i,:⟩ — linear e estável;
|
| 158 |
+
(iv) com C ortonormal (nascimento) e y one-hot, ‖W_ort i‖ = ‖c_j‖ —
|
| 159 |
+
quantização de energia estável (sem explosão de norma com o afiamento).
|
| 160 |
+
∎
|
| 161 |
+
|
| 162 |
+
**Trade-off de compressão (codebook).** Com K centros, o quantizado duro
|
| 163 |
+
(τ→0) representa cada linha como λ_i·c_{j(i)}: o piso de perda é o erro do
|
| 164 |
+
codebook (análogo a k-means). Com K = d a camada é expressiva (pode zerar a
|
| 165 |
+
perda); com K ≪ d há compressão real (K·p + n·K + n vs n·p parâmetros) com
|
| 166 |
+
piso de quantização > 0 — medido (alvo ortogonal 0.7·Q₀, d=48, 500 passos):
|
| 167 |
+
K=48 → 0.4626; K=8 → 0.4842 (o piso do K=8 é superior, como previsto).
|
| 168 |
+
|
| 169 |
+
## §5 — Kernel Cython (CPU) — análise do rascunho do usuário
|
| 170 |
+
|
| 171 |
+
**Bugs corrigidos (B1–B5):**
|
| 172 |
+
- **B1 (compilação)**: `np.empty`/alocação Python dentro de função `noexcept
|
| 173 |
+
nogil` — Cython rejeita objetos Python sem GIL. Correção: buffers
|
| 174 |
+
(tau/work) alocados no wrapper e passados como ponteiros C.
|
| 175 |
+
- **B2 (LAPACK column-major)**: o rascunho passava matriz C-order ao
|
| 176 |
+
`dgeqrf` — o LAPACK lê column-major; com layout errado a fatoração opera
|
| 177 |
+
sobre a memória remapeada (resultado transposto/garbled sem erro
|
| 178 |
+
explícito). Correção: memória F-contiguous (`order='F'`, memoryview
|
| 179 |
+
`[::1, :]`), ld = n.
|
| 180 |
+
- **B3 (work query + info)**: `lwork = n·64` fixo é insuficiente em algumas
|
| 181 |
+
implementações; sem checagem de `info` a falha era silenciosa. Correção:
|
| 182 |
+
query (lwork = −1) para o tamanho ótimo + exceção explícita se info ≠ 0.
|
| 183 |
+
- **B4 (GEMM + layout)**: produto triplo laço O(n·K·p) substituído por BLAS
|
| 184 |
+
`dgemm` (blocking + SIMD + multithread). Na correção descobriu-se um
|
| 185 |
+
sub-bug de layout (B4-b): com `lda = k` e a memória de y_soft em
|
| 186 |
+
C-order/`asfortranarray`, o dgemm remapeia a memória quando n ≠ k
|
| 187 |
+
(comprovado: span do produto diverge 0.58 para (5,3)→(5,4)). Correção:
|
| 188 |
+
`dgemm("N","N")` com ldas do layout col-major lógico (lda = n, ldb = k,
|
| 189 |
+
ldc = n) — validado em 4 regimes (≤ 1.8e-15).
|
| 190 |
+
- **B5 (aliasing in-place)**: `asfortranarray` retorna a PRÓPRIA matriz se
|
| 191 |
+
ela já é F-order; o QR in-place destruiria o quant antes da correção de
|
| 192 |
+
sinal (QᵀA = QᵀQ = I ⟹ sinais nunca flipados — divergência O(1)
|
| 193 |
+
silenciosa nas colunas com diag(R) < 0). Correção: cópia explícita
|
| 194 |
+
(`np.array(..., order='F', copy=True)`).
|
| 195 |
+
|
| 196 |
+
**MELHORIAS provadas (M1–M4):**
|
| 197 |
+
- **M1** detecção de hardware real (CUDA → CuPy → OpenMP threads + SIMD)
|
| 198 |
+
roteando o pipeline (Teo 26.8);
|
| 199 |
+
- **M2** `-ffast-math` REMOVIDA: a reassociação de ponto flutuante viola
|
| 200 |
+
IEEE-754 e as cotas provadas (‖QᵀQ−I‖ = O(ε_máquina) e a métrica δ);
|
| 201 |
+
`-O3 -march=native` vetorializa SEM violar as provas (a ordem das somas
|
| 202 |
+
do softmax/QR é preservada);
|
| 203 |
+
- **M3** softmax Gumbel com max-trick por linha em `prange` (schedule
|
| 204 |
+
static — blocos contíguos para cache L1/L2);
|
| 205 |
+
- **M4** `metrica_ortogonalidade` + `gerar_gumbel` determinístico expostos
|
| 206 |
+
para telemetria e comparação CPU×GPU reprodutível.
|
| 207 |
+
|
| 208 |
+
**Teorema 26.6 (aceleração).** (a) O custo dominante é o QR O(n²p); o
|
| 209 |
+
softmax GSQ é O(n·K) memory-bound — paralelizado com T threads do OpenMP:
|
| 210 |
+
speedup ≈ min(T, law de Amdahl sobre a fração serial f: 1/(f + (1−f)/T));
|
| 211 |
+
(b) o `dgemm` do OpenBLAS usa blocking de cache + AVX-512 (FMA) — GEMM em
|
| 212 |
+
double na CPU alvo ≥ 10× o laço triplo escalare; (c) precisão: mesma
|
| 213 |
+
aritmética IEEE-754 double ⟹ divergência p/ laço escalar ≤ ε·κ (o resultado
|
| 214 |
+
do quant difere apenas por ordem de soma).
|
| 215 |
+
∎
|
| 216 |
+
|
| 217 |
+
## §6 — Equivalência CPU(Cython) × GPU(PyTorch)
|
| 218 |
+
|
| 219 |
+
**Teorema 26.8.** Seja M = y_soft·C o quant (mesma semente de Gumbel nas
|
| 220 |
+
duas engines — `gerar_gumbel` idêntica à fórmula −log(−log U)). Então:
|
| 221 |
+
(a) cada engine devolve Q com δ(Q) ≤ ε_máquina·κ — ortogonalidade exata em
|
| 222 |
+
qualquer dispositivo (float64: ~3e-14 medido nas duas);
|
| 223 |
+
(b) **invariante do projetor**: Q_cy Q_cyᵀ = Q_pt Q_ptᵀ = P_span(M) — a
|
| 224 |
+
projeção ortogonal no espaço colunar é ÚNICA (Teo 26.7-ii), logo vale
|
| 225 |
+
mesmo com rank-deficiência e mesmo comparando QR-sign × polar (medido:
|
| 226 |
+
1.86e-15 com N=512, K=10);
|
| 227 |
+
(c) **mesma retração (QR-sign) em engines diferentes, rank completo**:
|
| 228 |
+
‖Q_cy − Q_pt‖_F/‖Q_cy‖ ≤ ε·κ — a fatoração QR com diagonal positiva é
|
| 229 |
+
única (medido: 2.37e-13, N=128);
|
| 230 |
+
(d) **QR-sign ≠ polar** em geral: ambas são retrações legítimas que diferem
|
| 231 |
+
por rotação à direita W = H·R̃⁻¹ (H = (R̃ᵀR̃)^{1/2}); divergência de MATRIZ
|
| 232 |
+
O(1) entre elas é ESPERADA e não é erro — o que coincidem são o projetor
|
| 233 |
+
(26.8-b) e a ortogonalidade (26.8-a). O rascunho original comparava QR
|
| 234 |
+
(CPU) com SVD/polar (GPU) sem essa nota;
|
| 235 |
+
(e) inferência determinística: em eval a camada usa o logito limpo (sem
|
| 236 |
+
Gumbel) — sem aleatoriedade na geração.
|
| 237 |
+
∎
|
| 238 |
+
|
| 239 |
+
## §7 — Integração no KHTST (v13)
|
| 240 |
+
|
| 241 |
+
- `KHTSTModel` (nucleo/modelo.py): `modelo.alinhamento` em 3 modos —
|
| 242 |
+
"qr" (default, nascimento neutro), "rco" (MatrizStiefel) e "gsq_rco"
|
| 243 |
+
(CamadaGSQRCO com AgendadorTau embutido);
|
| 244 |
+
- `TreinadorExtensao` (treino/treinador.py): agendador de τ COMPARTILHADO
|
| 245 |
+
agenda τ(t) pós-step para todas as `CamadaGSQRCO` (`definir_temperatura`)
|
| 246 |
+
e registra `gsq_rco/tau`, `gsq_rco/entropia`, `gsq_rco/erro_ort`
|
| 247 |
+
(a cada 20 passos, no TelemetryHub). A renormalização periódica da
|
| 248 |
+
CamadaOrtogonal NÃO se aplica à GSQRCO (restrição invariante — Teo 26.9-i);
|
| 249 |
+
- `MuonClipAdamW` (treino/otimizadores.py): parâmetros de gate GSQ no
|
| 250 |
+
grupo AdamW (Teo 26.10);
|
| 251 |
+
- `configs/base.json` + `ConfigModelo.gsq_rco`: opt-in (ativo=false por
|
| 252 |
+
default — Teo 26.11 nascimento neutro: suítes v≤12 intactas).
|
| 253 |
+
|
| 254 |
+
## §8 — Evidências empíricas (máquina: CPU 2 threads AVX-512, float64)
|
| 255 |
+
|
| 256 |
+
| Prova | Valor medido | Cota |
|
| 257 |
+
|---|---|---|
|
| 258 |
+
| Teo 26.1-a δ (Cython float64) | 2.92e-14 | ≤ ε·κ |
|
| 259 |
+
| Teo 26.1-a δ (Torch QR-sign) | 3.04e-14 | ≤ ε·κ |
|
| 260 |
+
| Teo 26.1-b δ (Torch polar) | 4.31e-14 | ≤ ε·κ |
|
| 261 |
+
| Teo 26.2 tangente (resíduo XᵀP+PᵀX) | 5.55e-14 | = 0 |
|
| 262 |
+
| Teo 26.3-a adjunto tangente (err rel) | 1.5e-8 | O(h) |
|
| 263 |
+
| Teo 26.3-b gradiente tangente | 4.0e-14 | = 0 |
|
| 264 |
+
| Teo 26.3-c descida | 4.39 → 0.68 (η=1e-2) | lema |
|
| 265 |
+
| Teo 26.5-iii t*(0.05) | 600.0 exato | inversão |
|
| 266 |
+
| Teo 26.8-b projetor QR×polar | 1.86e-15 | ≤ ε |
|
| 267 |
+
| Teo 26.8-c matriz mesma retração | 2.37e-13 | ≤ ε·κ |
|
| 268 |
+
| Treino ponta a ponta (400 passos, d=48) | perda 1.43→0.47 (3.0×) | descida |
|
| 269 |
+
| Afiamento τ 2.0→0.30 | entropia 0.907→0.160 | Teo 26.4-iii |
|
| 270 |
+
| δ no modelo completo (float32) | 8.0e-06 | ≤ ε_32·κ |
|
| 271 |
+
| Gradientes vivos (logits/centros/escala) | 6.9e-3/9.9e-3/3.3e-2 | > 0 |
|
| 272 |
+
|
| 273 |
+
## §9 — Hiperparâmetros recomendados (e o porquê analítico)
|
| 274 |
+
|
| 275 |
+
| Parâmetro | Default | Justificativa |
|
| 276 |
+
|---|---|---|
|
| 277 |
+
| τ_0 | 2.0 | softmax amplamente suave no nascimento (exploração; H/ log K ≈ 0.9) |
|
| 278 |
+
| τ_min | 0.25 | barreira 1/(2τ_min) = 2 ≤ clip_grad=1.0 do PRS com margem (Teo 26.4-ii) |
|
| 279 |
+
| δ | 0.05 | folga de 5% do piso — afiamento perceptível sem regime duro |
|
| 280 |
+
| t_alvo | 600 | 1 segmento entre checkpoints do treino segmentado KHTST |
|
| 281 |
+
| T_τ | 600/ln 7 ≈ 308 | eq. 26.10 (inversão exata) |
|
| 282 |
+
| K | 8 | trade-off compressão×piso (§4); ≥2 obrigatório |
|
| 283 |
+
| amostrar | true | Gumbel no treino; logito limpo em eval (Teo 26.8-e) |
|
estados/fase-v13/meta.json
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"passo_global": 40,
|
| 3 |
+
"epoca": 0,
|
| 4 |
+
"t_ciclo": 40,
|
| 5 |
+
"T_ciclo": 488,
|
| 6 |
+
"passos_epoca": 0,
|
| 7 |
+
"historico_epocas": [],
|
| 8 |
+
"L_hat": 0.8950501318752652,
|
| 9 |
+
"ciar": {
|
| 10 |
+
"kappa": 1.0,
|
| 11 |
+
"integral": 0.0,
|
| 12 |
+
"kappa_min": 0.5,
|
| 13 |
+
"kappa_max": 1.5,
|
| 14 |
+
"ganho": 0.3,
|
| 15 |
+
"vazamento": 0.98
|
| 16 |
+
},
|
| 17 |
+
"auto_janela": [
|
| 18 |
+
7.921772003173828,
|
| 19 |
+
5.407018661499023,
|
| 20 |
+
8.321477890014648,
|
| 21 |
+
8.22258472442627,
|
| 22 |
+
7.55533504486084,
|
| 23 |
+
10.037700653076172,
|
| 24 |
+
9.213990211486816,
|
| 25 |
+
3.7933716773986816,
|
| 26 |
+
9.653409004211426,
|
| 27 |
+
9.324657440185547,
|
| 28 |
+
8.999397277832031,
|
| 29 |
+
9.656225204467773,
|
| 30 |
+
10.518783569335938,
|
| 31 |
+
8.177958488464355,
|
| 32 |
+
8.431066513061523,
|
| 33 |
+
8.7640962600708
|
| 34 |
+
],
|
| 35 |
+
"versao": "v13",
|
| 36 |
+
"gsq_rco": true,
|
| 37 |
+
"perda_media": 9.0182,
|
| 38 |
+
"passos": 40,
|
| 39 |
+
"hash_sha256": "de0f9759fc0af4531fbe69fe239fdb7dc3c3ac38aed7021f2f27eaab85e9fac6",
|
| 40 |
+
"tag": "fase-v13",
|
| 41 |
+
"escalares": {
|
| 42 |
+
"roteador/ssom.k": 24,
|
| 43 |
+
"roteador/ssom.dim": 192,
|
| 44 |
+
"roteador/ssom.sigma0": 2.0,
|
| 45 |
+
"roteador/ssom.sigma_f": 0.4,
|
| 46 |
+
"roteador/ssom.alpha0": 0.5,
|
| 47 |
+
"roteador/ssom.gamma_empate": 0.15,
|
| 48 |
+
"roteador/ssom.eps_ativo": 0.02,
|
| 49 |
+
"roteador/ssom.passo": 0,
|
| 50 |
+
"roteador/ssom.n_mortos_resemeados": 0,
|
| 51 |
+
"roteador/ssom.lambda_sup": 0.5,
|
| 52 |
+
"roteador/dim": 192,
|
| 53 |
+
"roteador/n_tarefas": 12,
|
| 54 |
+
"roteador/lambda_rota": 0.35,
|
| 55 |
+
"roteador/gamma_empate": 0.15,
|
| 56 |
+
"roteador/conf_min": 0.25,
|
| 57 |
+
"roteador/min_amostras": 200,
|
| 58 |
+
"roteador/n_amostras": 20,
|
| 59 |
+
"roteador/ultimo_drift": 0.0450020469725132,
|
| 60 |
+
"roteador/ultima_conf_media": 0.0,
|
| 61 |
+
"roteador/ultima_frac_ativa": 0.0,
|
| 62 |
+
"roteador/ultimos_acertos": 0,
|
| 63 |
+
"orquestrador/dim": 192,
|
| 64 |
+
"orquestrador/variantes.som.k": 24,
|
| 65 |
+
"orquestrador/variantes.som.dim": 192,
|
| 66 |
+
"orquestrador/variantes.som.sigma0": 3.0,
|
| 67 |
+
"orquestrador/variantes.som.sigma_f": 0.4,
|
| 68 |
+
"orquestrador/variantes.som.alpha0": 0.5,
|
| 69 |
+
"orquestrador/variantes.som.gamma_empate": 0.15,
|
| 70 |
+
"orquestrador/variantes.som.eps_ativo": 0.02,
|
| 71 |
+
"orquestrador/variantes.som.passo": 0,
|
| 72 |
+
"orquestrador/variantes.som.n_mortos_resemeados": 0,
|
| 73 |
+
"orquestrador/variantes.gg.k": 4,
|
| 74 |
+
"orquestrador/variantes.gg.dim": 192,
|
| 75 |
+
"orquestrador/variantes.gg.sigma0": 3.0,
|
| 76 |
+
"orquestrador/variantes.gg.sigma_f": 0.4,
|
| 77 |
+
"orquestrador/variantes.gg.alpha0": 0.5,
|
| 78 |
+
"orquestrador/variantes.gg.gamma_empate": 0.15,
|
| 79 |
+
"orquestrador/variantes.gg.eps_ativo": 0.02,
|
| 80 |
+
"orquestrador/variantes.gg.passo": 0,
|
| 81 |
+
"orquestrador/variantes.gg.n_mortos_resemeados": 0,
|
| 82 |
+
"orquestrador/variantes.gg.lado": 2,
|
| 83 |
+
"orquestrador/variantes.gg.lado_max": 8,
|
| 84 |
+
"orquestrador/variantes.gg.lambda_": 400,
|
| 85 |
+
"orquestrador/variantes.gg.beta": 0.9995,
|
| 86 |
+
"orquestrador/variantes.gg.n_desde_insercao": 0,
|
| 87 |
+
"orquestrador/variantes.gcs.kmax": 48,
|
| 88 |
+
"orquestrador/variantes.gcs.lambda_": 300,
|
| 89 |
+
"orquestrador/variantes.gcs.beta": 0.9995,
|
| 90 |
+
"orquestrador/variantes.gcs.vizinhos": [
|
| 91 |
+
"(0, 1)",
|
| 92 |
+
"(1, 2)",
|
| 93 |
+
"(2, 0)"
|
| 94 |
+
],
|
| 95 |
+
"orquestrador/variantes.gcs.passo": 0,
|
| 96 |
+
"orquestrador/variantes.gsom.k": 4,
|
| 97 |
+
"orquestrador/variantes.gsom.dim": 192,
|
| 98 |
+
"orquestrador/variantes.gsom.sigma0": 3.0,
|
| 99 |
+
"orquestrador/variantes.gsom.sigma_f": 0.4,
|
| 100 |
+
"orquestrador/variantes.gsom.alpha0": 0.5,
|
| 101 |
+
"orquestrador/variantes.gsom.gamma_empate": 0.15,
|
| 102 |
+
"orquestrador/variantes.gsom.eps_ativo": 0.02,
|
| 103 |
+
"orquestrador/variantes.gsom.passo": 0,
|
| 104 |
+
"orquestrador/variantes.gsom.n_mortos_resemeados": 0,
|
| 105 |
+
"orquestrador/variantes.gsom.gt": 1.0498220920562744,
|
| 106 |
+
"orquestrador/variantes.gsom.lambda_viz": 0.3,
|
| 107 |
+
"orquestrador/variantes.gsom.max_unidades": 96,
|
| 108 |
+
"orquestrador/variantes.gsom.ocupadas": [
|
| 109 |
+
"(0, 0)",
|
| 110 |
+
"(0, 1)",
|
| 111 |
+
"(1, 0)",
|
| 112 |
+
"(1, 1)"
|
| 113 |
+
],
|
| 114 |
+
"orquestrador/variantes.hsom.n1.k": 12,
|
| 115 |
+
"orquestrador/variantes.hsom.n1.dim": 192,
|
| 116 |
+
"orquestrador/variantes.hsom.n1.sigma0": 2.5,
|
| 117 |
+
"orquestrador/variantes.hsom.n1.sigma_f": 0.4,
|
| 118 |
+
"orquestrador/variantes.hsom.n1.alpha0": 0.5,
|
| 119 |
+
"orquestrador/variantes.hsom.n1.gamma_empate": 0.15,
|
| 120 |
+
"orquestrador/variantes.hsom.n1.eps_ativo": 0.02,
|
| 121 |
+
"orquestrador/variantes.hsom.n1.passo": 0,
|
| 122 |
+
"orquestrador/variantes.hsom.n1.n_mortos_resemeados": 0,
|
| 123 |
+
"orquestrador/variantes.hsom.k_filho": 10,
|
| 124 |
+
"orquestrador/variantes.hsom.dim": 192,
|
| 125 |
+
"orquestrador/variantes.hsom.semente": 2026,
|
| 126 |
+
"orquestrador/variantes.tkm.k": 32,
|
| 127 |
+
"orquestrador/variantes.tkm.dim": 192,
|
| 128 |
+
"orquestrador/variantes.tkm.sigma0": 2.0,
|
| 129 |
+
"orquestrador/variantes.tkm.sigma_f": 0.4,
|
| 130 |
+
"orquestrador/variantes.tkm.alpha0": 0.5,
|
| 131 |
+
"orquestrador/variantes.tkm.gamma_empate": 0.15,
|
| 132 |
+
"orquestrador/variantes.tkm.eps_ativo": 0.02,
|
| 133 |
+
"orquestrador/variantes.tkm.passo": 0,
|
| 134 |
+
"orquestrador/variantes.tkm.n_mortos_resemeados": 0,
|
| 135 |
+
"orquestrador/variantes.tkm.lam": 0.5,
|
| 136 |
+
"orquestrador/variantes.tkm.janela": 8,
|
| 137 |
+
"orquestrador/variantes.rsom.k": 32,
|
| 138 |
+
"orquestrador/variantes.rsom.dim": 192,
|
| 139 |
+
"orquestrador/variantes.rsom.sigma0": 2.0,
|
| 140 |
+
"orquestrador/variantes.rsom.sigma_f": 0.4,
|
| 141 |
+
"orquestrador/variantes.rsom.alpha0": 0.5,
|
| 142 |
+
"orquestrador/variantes.rsom.gamma_empate": 0.15,
|
| 143 |
+
"orquestrador/variantes.rsom.eps_ativo": 0.02,
|
| 144 |
+
"orquestrador/variantes.rsom.passo": 0,
|
| 145 |
+
"orquestrador/variantes.rsom.n_mortos_resemeados": 0,
|
| 146 |
+
"orquestrador/variantes.rsom.alpha": 0.3,
|
| 147 |
+
"orquestrador/variantes.cpn.kohonen.k": 24,
|
| 148 |
+
"orquestrador/variantes.cpn.kohonen.dim": 192,
|
| 149 |
+
"orquestrador/variantes.cpn.kohonen.sigma0": 2.5,
|
| 150 |
+
"orquestrador/variantes.cpn.kohonen.sigma_f": 0.4,
|
| 151 |
+
"orquestrador/variantes.cpn.kohonen.alpha0": 0.5,
|
| 152 |
+
"orquestrador/variantes.cpn.kohonen.gamma_empate": 0.15,
|
| 153 |
+
"orquestrador/variantes.cpn.kohonen.eps_ativo": 0.02,
|
| 154 |
+
"orquestrador/variantes.cpn.kohonen.passo": 0,
|
| 155 |
+
"orquestrador/variantes.cpn.kohonen.n_mortos_resemeados": 0,
|
| 156 |
+
"orquestrador/variantes.cpn.eta0": 0.05,
|
| 157 |
+
"orquestrador/variantes.cpn.passo": 0,
|
| 158 |
+
"orquestrador/variantes.ssom.k": 24,
|
| 159 |
+
"orquestrador/variantes.ssom.dim": 192,
|
| 160 |
+
"orquestrador/variantes.ssom.sigma0": 2.0,
|
| 161 |
+
"orquestrador/variantes.ssom.sigma_f": 0.4,
|
| 162 |
+
"orquestrador/variantes.ssom.alpha0": 0.5,
|
| 163 |
+
"orquestrador/variantes.ssom.gamma_empate": 0.15,
|
| 164 |
+
"orquestrador/variantes.ssom.eps_ativo": 0.02,
|
| 165 |
+
"orquestrador/variantes.ssom.passo": 0,
|
| 166 |
+
"orquestrador/variantes.ssom.n_mortos_resemeados": 0,
|
| 167 |
+
"orquestrador/variantes.ssom.lambda_sup": 0.5,
|
| 168 |
+
"orquestrador/ativa": "som",
|
| 169 |
+
"orquestrador/motivo": "padrão",
|
| 170 |
+
"orquestrador/atencao.tau": 0.5,
|
| 171 |
+
"orquestrador/atencao.gamma": 0.15,
|
| 172 |
+
"mx/d": 192,
|
| 173 |
+
"mx/usar_moe": true,
|
| 174 |
+
"mx/moe_ed_camada": 4,
|
| 175 |
+
"mx/confianca_corrente": 0.7004369497299194,
|
| 176 |
+
"mx/usar_mtp": true,
|
| 177 |
+
"mx/criterios_definidos": true,
|
| 178 |
+
"mx/alpha_jev": 0.0,
|
| 179 |
+
"mx/usar_multimodal": true,
|
| 180 |
+
"mx/modo_alinhamento": "gsq_rco",
|
| 181 |
+
"mx/ultima_perda": 8.7640962600708,
|
| 182 |
+
"mx/perda_balanceamento": 0.0,
|
| 183 |
+
"mx/qat": false,
|
| 184 |
+
"mx/ultimos_gates.texto": 0.0,
|
| 185 |
+
"mx/ultimos_gates.imagem": 0.0,
|
| 186 |
+
"mx/ultimos_gates.audio": 1.0,
|
| 187 |
+
"mx/ultimos_gates.video": 0.0,
|
| 188 |
+
"mx/ultimos_gates.tabular": 0.0,
|
| 189 |
+
"mx/ultimos_gates.pontos_3d": 0.0,
|
| 190 |
+
"mx/ultimo_info_nlp.gate_nlp": 0.28851988911628723,
|
| 191 |
+
"mx/ultimo_info_nlp.intencao": "instrucao",
|
| 192 |
+
"mx/ultimo_info_nlp.entropia_intencao": 1.1540800333023071
|
| 193 |
+
},
|
| 194 |
+
"n_tensores": 697,
|
| 195 |
+
"publicado_em_commit_unico": true
|
| 196 |
+
}
|
estados/fase-v13/modelo.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:de0f9759fc0af4531fbe69fe239fdb7dc3c3ac38aed7021f2f27eaab85e9fac6
|
| 3 |
+
size 81358437
|
metricas_v13.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"script": "13_treino_curto_v13",
|
| 3 |
+
"corpus": "cache_dados/corpus_v2.jsonl (620 registros reais)",
|
| 4 |
+
"passos_por_variante": 40,
|
| 5 |
+
"baseline_qr": {
|
| 6 |
+
"variante": "baseline_qr",
|
| 7 |
+
"gsq_ativo": false,
|
| 8 |
+
"passos": 40,
|
| 9 |
+
"perda_media_inicial": 9.721,
|
| 10 |
+
"perda_media_final": 8.9927,
|
| 11 |
+
"melhoria_x": 1.081,
|
| 12 |
+
"perda_min": 7.83,
|
| 13 |
+
"perdas_finitas": true,
|
| 14 |
+
"gsq_rco/tau_ema": null,
|
| 15 |
+
"gsq_rco/entropia_ema": null,
|
| 16 |
+
"gsq_rco/erro_ort_ema": null,
|
| 17 |
+
"alinhamento": {}
|
| 18 |
+
},
|
| 19 |
+
"gsq_rco": {
|
| 20 |
+
"variante": "gsq_rco",
|
| 21 |
+
"gsq_ativo": true,
|
| 22 |
+
"passos": 40,
|
| 23 |
+
"perda_media_inicial": 9.724,
|
| 24 |
+
"perda_media_final": 8.7466,
|
| 25 |
+
"melhoria_x": 1.112,
|
| 26 |
+
"perda_min": 6.2968,
|
| 27 |
+
"perdas_finitas": true,
|
| 28 |
+
"gsq_rco/tau_ema": 1.9961,
|
| 29 |
+
"gsq_rco/entropia_ema": 0.8841,
|
| 30 |
+
"gsq_rco/erro_ort_ema": 7.87e-06,
|
| 31 |
+
"alinhamento": {
|
| 32 |
+
"tipo": "CamadaGSQRCO",
|
| 33 |
+
"tau": 1.6389,
|
| 34 |
+
"entropia": 0.8598,
|
| 35 |
+
"erro_ort": 7.798761544108856e-06
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
"conclusao": {
|
| 39 |
+
"ambos_aprendem": true,
|
| 40 |
+
"gsq_ortogonalidade_exata": true,
|
| 41 |
+
"afiamento_gumbel": true
|
| 42 |
+
}
|
| 43 |
+
}
|
metricas_v13_baseline_qr.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"variante": "baseline_qr",
|
| 3 |
+
"gsq_ativo": false,
|
| 4 |
+
"passos": 40,
|
| 5 |
+
"perda_media_inicial": 9.721,
|
| 6 |
+
"perda_media_final": 8.9927,
|
| 7 |
+
"melhoria_x": 1.081,
|
| 8 |
+
"perda_min": 7.83,
|
| 9 |
+
"perdas_finitas": true,
|
| 10 |
+
"gsq_rco/tau_ema": null,
|
| 11 |
+
"gsq_rco/entropia_ema": null,
|
| 12 |
+
"gsq_rco/erro_ort_ema": null,
|
| 13 |
+
"alinhamento": {}
|
| 14 |
+
}
|
metricas_v13_gsq_rco.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"variante": "gsq_rco",
|
| 3 |
+
"gsq_ativo": true,
|
| 4 |
+
"passos": 40,
|
| 5 |
+
"perda_media_inicial": 9.724,
|
| 6 |
+
"perda_media_final": 8.7466,
|
| 7 |
+
"melhoria_x": 1.112,
|
| 8 |
+
"perda_min": 6.2968,
|
| 9 |
+
"perdas_finitas": true,
|
| 10 |
+
"gsq_rco/tau_ema": 1.9961,
|
| 11 |
+
"gsq_rco/entropia_ema": 0.8841,
|
| 12 |
+
"gsq_rco/erro_ort_ema": 7.87e-06,
|
| 13 |
+
"alinhamento": {
|
| 14 |
+
"tipo": "CamadaGSQRCO",
|
| 15 |
+
"tau": 1.6389,
|
| 16 |
+
"entropia": 0.8598,
|
| 17 |
+
"erro_ort": 7.798761544108856e-06
|
| 18 |
+
}
|
| 19 |
+
}
|
pipe_gsq_rco_v13.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"script": "12_gsq_rco_hibrido",
|
| 3 |
+
"versao": "v13",
|
| 4 |
+
"N": 512,
|
| 5 |
+
"K": 10,
|
| 6 |
+
"tau": 0.5,
|
| 7 |
+
"hardware": {
|
| 8 |
+
"cython": {
|
| 9 |
+
"device": "CPU",
|
| 10 |
+
"vector_extensions": "OpenMP x2 + SIMD/AVX-512 (march=native, sem fast-math)",
|
| 11 |
+
"threads": 2
|
| 12 |
+
},
|
| 13 |
+
"pytorch": {
|
| 14 |
+
"device": "CPU",
|
| 15 |
+
"backend": "pytorch-cpu",
|
| 16 |
+
"vector_extensions": "OpenMP x2 + SIMD/AVX-512 (march=native, sem fast-math)",
|
| 17 |
+
"threads": 2
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
"cpu_cython": {
|
| 21 |
+
"engine": "Cython (OpenMP+dgemm+LAPACK)",
|
| 22 |
+
"tempo_s": 0.027754545211791992,
|
| 23 |
+
"delta_ort": 2.920928884811104e-14,
|
| 24 |
+
"forma": [
|
| 25 |
+
512,
|
| 26 |
+
512
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
"torch_qr_sign": {
|
| 30 |
+
"engine": "PyTorch (cpu, QR-sign)",
|
| 31 |
+
"tempo_s": 0.05812239646911621,
|
| 32 |
+
"delta_ort": 3.0350077900128155e-14,
|
| 33 |
+
"forma": [
|
| 34 |
+
512,
|
| 35 |
+
512
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
"torch_svd_polar": {
|
| 39 |
+
"engine": "PyTorch (cpu, SVD-polar)",
|
| 40 |
+
"tempo_s": 0.06822347640991211,
|
| 41 |
+
"delta_ort": 4.313304159001702e-14,
|
| 42 |
+
"forma": [
|
| 43 |
+
512,
|
| 44 |
+
512
|
| 45 |
+
]
|
| 46 |
+
},
|
| 47 |
+
"divergencia_projetor_qr_x_polar": 1.8568269435859574e-15,
|
| 48 |
+
"divergencia_matriz_mesma_retracao_rank_cheio": 2.367164336316263e-13,
|
| 49 |
+
"divergencia_projetor_svd_x_cython": 2.2888137461395127e-15,
|
| 50 |
+
"treino_ponta_a_ponta": {
|
| 51 |
+
"perdas": [
|
| 52 |
+
1.4289265871047974,
|
| 53 |
+
0.823803186416626,
|
| 54 |
+
0.570017397403717,
|
| 55 |
+
0.49467530846595764,
|
| 56 |
+
0.48039987683296204,
|
| 57 |
+
0.477589875459671,
|
| 58 |
+
0.4780101776123047,
|
| 59 |
+
0.47419726848602295,
|
| 60 |
+
0.4756273925304413,
|
| 61 |
+
0.4725165367126465,
|
| 62 |
+
0.47486308217048645
|
| 63 |
+
],
|
| 64 |
+
"entropias": [
|
| 65 |
+
0.9071,
|
| 66 |
+
0.8377,
|
| 67 |
+
0.7329,
|
| 68 |
+
0.5551,
|
| 69 |
+
0.4501,
|
| 70 |
+
0.3566,
|
| 71 |
+
0.2601,
|
| 72 |
+
0.1877,
|
| 73 |
+
0.1867,
|
| 74 |
+
0.1572,
|
| 75 |
+
0.1604
|
| 76 |
+
],
|
| 77 |
+
"taus": [
|
| 78 |
+
2.0,
|
| 79 |
+
1.4764,
|
| 80 |
+
1.1095,
|
| 81 |
+
0.8523,
|
| 82 |
+
0.6721,
|
| 83 |
+
0.5458,
|
| 84 |
+
0.4573,
|
| 85 |
+
0.3953,
|
| 86 |
+
0.3518,
|
| 87 |
+
0.3213,
|
| 88 |
+
0.3004
|
| 89 |
+
],
|
| 90 |
+
"deltas_ort": [
|
| 91 |
+
2.491756276867818e-06,
|
| 92 |
+
2.5028323307196843e-06,
|
| 93 |
+
2.9612783691845834e-06,
|
| 94 |
+
2.610032197480905e-06,
|
| 95 |
+
2.876405460483511e-06,
|
| 96 |
+
2.4472662971675163e-06,
|
| 97 |
+
2.9753614398941863e-06,
|
| 98 |
+
2.620418172227801e-06,
|
| 99 |
+
2.369507001276361e-06,
|
| 100 |
+
2.378668114033644e-06,
|
| 101 |
+
2.430806944175856e-06
|
| 102 |
+
],
|
| 103 |
+
"tempo_s": 0.46918463706970215,
|
| 104 |
+
"grad_normas": {
|
| 105 |
+
"logits_gsq": 0.004756750073283911,
|
| 106 |
+
"centros_gsq": 0.012766707688570023,
|
| 107 |
+
"escala_gsq": 0.04010233283042908,
|
| 108 |
+
"b": 0.0019327716436237097
|
| 109 |
+
},
|
| 110 |
+
"descida_x": 3.009
|
| 111 |
+
}
|
| 112 |
+
}
|
relatorio_agente_engenheiro.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"t":
|
| 3 |
"revisoes_aprovadas": 1,
|
| 4 |
"revisoes_bloqueadas": 0,
|
| 5 |
"tolerancia_regressao": 0.05,
|
|
|
|
| 1 |
{
|
| 2 |
+
"t": 1790855986.84,
|
| 3 |
"revisoes_aprovadas": 1,
|
| 4 |
"revisoes_bloqueadas": 0,
|
| 5 |
"tolerancia_regressao": 0.05,
|
scripts/06_publicar_hf.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
-
"""Publica o KHTST
|
| 4 |
|
| 5 |
README.md (model card COM GRÁFICOS) · LICENSE · configs/ · src/khtst/ ·
|
| 6 |
-
scripts/ · docs/matematica/ (provas 00–
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
|
| 11 |
REGRAS DO HUB (escopo v5): publicação em UM ÚNICO COMMIT COMPLETO —
|
| 12 |
`upload_folder` com `delete_patterns=["*"]` substitui ATOMICAMENTE todo o
|
|
@@ -31,7 +31,7 @@ import sys
|
|
| 31 |
|
| 32 |
RAIZ = "/home/z/my-project/khtst"
|
| 33 |
STAGE = "/home/z/my-project/khtst/stage_publicar"
|
| 34 |
-
PASTA_ESTADO = "estados/fase-
|
| 35 |
IGNORAR_DIRS = {"__pycache__", "cache_dados", "telemetria_out",
|
| 36 |
"cache_hub_tmp", ".git", "build", "estados_local",
|
| 37 |
"estados_local_teste", "stage_publicar", "hf_stage",
|
|
@@ -68,6 +68,8 @@ def preparar_stage() -> int:
|
|
| 68 |
"test_khtst_v10_pos_treino.txt",
|
| 69 |
"teste_dados_reais_v11.json",
|
| 70 |
"teste_dados_reais_v12.json",
|
|
|
|
|
|
|
| 71 |
"tempo_treino_acumulado.json"):
|
| 72 |
fonte = os.path.join(RAIZ, "telemetria_out", nome)
|
| 73 |
if os.path.exists(fonte):
|
|
@@ -156,28 +158,33 @@ def main():
|
|
| 156 |
# cpython-312 linux x86_64) — na v9 o padrão omitia esses arquivos (corrigido).
|
| 157 |
api.upload_folder(
|
| 158 |
folder_path=STAGE, repo_id=args.repo, repo_type="model",
|
| 159 |
-
commit_message="KHTST
|
| 160 |
-
"(
|
| 161 |
-
"
|
| 162 |
-
"
|
| 163 |
-
"
|
| 164 |
-
"
|
| 165 |
-
"
|
| 166 |
-
"
|
| 167 |
-
"
|
| 168 |
-
"
|
| 169 |
-
"(
|
| 170 |
-
"
|
| 171 |
-
"
|
| 172 |
-
"
|
| 173 |
-
"
|
| 174 |
-
"
|
| 175 |
-
"(
|
| 176 |
-
"
|
| 177 |
-
"
|
| 178 |
-
"
|
| 179 |
-
"
|
| 180 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
delete_patterns=["*"],
|
| 182 |
allow_patterns=["*.py", "*.md", "*.json", "*.yaml", "*.yml",
|
| 183 |
"*.txt", "*.png", "*.safetensors", "*.pyx", "*.c",
|
|
@@ -194,7 +201,7 @@ def main():
|
|
| 194 |
print("ERRO: resíduos no repo:", restos)
|
| 195 |
sys.exit(1)
|
| 196 |
parciais = [f for f in arquivos if f.startswith("estados/")
|
| 197 |
-
and "fase-
|
| 198 |
print(f"✓ verificação: {len(arquivos)} arquivos no repo; "
|
| 199 |
f"resíduos=0; estados parciais={len(parciais)} (0 esperado)")
|
| 200 |
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
+
"""Publica o KHTST v13 no HuggingFace de modo ORGANIZADO e COMPLETO:
|
| 4 |
|
| 5 |
README.md (model card COM GRÁFICOS) · LICENSE · configs/ · src/khtst/ ·
|
| 6 |
+
scripts/ · docs/matematica/ (provas 00–26: + GSQ/RCO Stiefel, gradiente
|
| 7 |
+
riemanniano no backward, decaimento de τ, kernel Cython híbrido CPU/GPU)
|
| 8 |
+
· tests/ · evidências · estados/fase-v13/ (pesos finais + meta SHA-256 +
|
| 9 |
+
estado integral)
|
| 10 |
|
| 11 |
REGRAS DO HUB (escopo v5): publicação em UM ÚNICO COMMIT COMPLETO —
|
| 12 |
`upload_folder` com `delete_patterns=["*"]` substitui ATOMICAMENTE todo o
|
|
|
|
| 31 |
|
| 32 |
RAIZ = "/home/z/my-project/khtst"
|
| 33 |
STAGE = "/home/z/my-project/khtst/stage_publicar"
|
| 34 |
+
PASTA_ESTADO = "estados/fase-v13" # ÚNICO estado publicado (pesos + integral)
|
| 35 |
IGNORAR_DIRS = {"__pycache__", "cache_dados", "telemetria_out",
|
| 36 |
"cache_hub_tmp", ".git", "build", "estados_local",
|
| 37 |
"estados_local_teste", "stage_publicar", "hf_stage",
|
|
|
|
| 68 |
"test_khtst_v10_pos_treino.txt",
|
| 69 |
"teste_dados_reais_v11.json",
|
| 70 |
"teste_dados_reais_v12.json",
|
| 71 |
+
"metricas_v13.json", "metricas_v13_baseline_qr.json",
|
| 72 |
+
"metricas_v13_gsq_rco.json", "pipe_gsq_rco_v13.json",
|
| 73 |
"tempo_treino_acumulado.json"):
|
| 74 |
fonte = os.path.join(RAIZ, "telemetria_out", nome)
|
| 75 |
if os.path.exists(fonte):
|
|
|
|
| 158 |
# cpython-312 linux x86_64) — na v9 o padrão omitia esses arquivos (corrigido).
|
| 159 |
api.upload_folder(
|
| 160 |
folder_path=STAGE, repo_id=args.repo, repo_type="model",
|
| 161 |
+
commit_message="KHTST v13 — GSQ (GUMBEL-SOFTMAX QUANTIZATION) + RCO "
|
| 162 |
+
"(RIEMANNIAN CONSTRAINED OPTIMIZATION): quantização "
|
| 163 |
+
"por misturas de Gumbel com DECAIMENTO ANALÍTICO da "
|
| 164 |
+
"temperatura τ(t) = τ_min + (τ_0−τ_min)·e^(−t/T_τ) e "
|
| 165 |
+
"inversão exata T_τ = t_alvo/ln((τ_0−τ_min)/δ) — "
|
| 166 |
+
"defaults 2,0→0,25 em 600 passos (Teos 26.4/26.5); "
|
| 167 |
+
"RESTRIÇÃO STIEFEL em matrizes ortogonais COMPLETAS "
|
| 168 |
+
"via QR de sinal positivo — W_ortᵀW_ort = I exato a "
|
| 169 |
+
"cada forward (2,9e-14 float64), restrição INVARIANTE "
|
| 170 |
+
"do fluxo (Teos 26.1/26.9); GRADIENTE RIEMANNIANO "
|
| 171 |
+
"(PROJEÇÃO DA TANGENTE) no passe de backward: "
|
| 172 |
+
"g = Ĝ − Y·sym(YᵀĜ) — adjunto exato no tangente "
|
| 173 |
+
"(5,7e-10), gradiente tangente (3,8e-14) e lema da "
|
| 174 |
+
"descida no manifold — treino PONTA A PONTA (RGD, "
|
| 175 |
+
"Teos 26.2/26.3); KERNEL CYTHON OpenMP/AVX-512 + BLAS "
|
| 176 |
+
"dgemm + LAPACK dgeqrf/dorgqr com 5 bugs do rascunho "
|
| 177 |
+
"corrigidos (nogil, column-major, work-query, layout "
|
| 178 |
+
"do GEMM, aliasing in-place) e −ffast-math removida "
|
| 179 |
+
"com justificativa IEEE-754 (Teo 26.6, doc 26 §5); "
|
| 180 |
+
"PIPELINE HÍBRIDO CPU/GPU com detecção automática e "
|
| 181 |
+
"equivalência PROVADA — projetor colunar único "
|
| 182 |
+
"1,9e-15 (vale rank-deficiente) e mesma retração em "
|
| 183 |
+
"engines distintas 2,4e-13 (Teos 26.7/26.8); GATE GSQ "
|
| 184 |
+
"no AdamW (Teo 26.10) e nascimento neutro (Teo "
|
| 185 |
+
"26.11); ABLAÇÃO PAREADA com DADOS REAIS (620 "
|
| 186 |
+
"registros, 40 passos): GSQ+RCO 1,112× vs baseline "
|
| 187 |
+
"1,081× — 12 suítes verdes",
|
| 188 |
delete_patterns=["*"],
|
| 189 |
allow_patterns=["*.py", "*.md", "*.json", "*.yaml", "*.yml",
|
| 190 |
"*.txt", "*.png", "*.safetensors", "*.pyx", "*.c",
|
|
|
|
| 201 |
print("ERRO: resíduos no repo:", restos)
|
| 202 |
sys.exit(1)
|
| 203 |
parciais = [f for f in arquivos if f.startswith("estados/")
|
| 204 |
+
and "fase-v13" not in f]
|
| 205 |
print(f"✓ verificação: {len(arquivos)} arquivos no repo; "
|
| 206 |
f"resíduos=0; estados parciais={len(parciais)} (0 esperado)")
|
| 207 |
|
scripts/12_gsq_rco_hibrido.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""SCRIPT 12 (v13, doc 26 §6) — PIPELINE HÍBRIDO GSQ + RCO (Stiefel).
|
| 3 |
+
|
| 4 |
+
Aprimoramento do `pipe.py` do rascunho do usuário — mesma estrutura
|
| 5 |
+
(detecção → execução CPU(Cython)/GPU(PyTorch) → validação da restrição),
|
| 6 |
+
com as correções B1–B4 e as métricas de aprendizado v13:
|
| 7 |
+
|
| 8 |
+
1. DETECÇÃO AUTOMÁTICA otimizada (rco_gsq.detect_hardware_acceleration +
|
| 9 |
+
khtst.quanta.gsq_rco.detectar_hardware — Teo 26.8);
|
| 10 |
+
2. CPU: kernel Cython (OpenMP + BLAS dgemm + LAPACK dgeqrf/dorgqr);
|
| 11 |
+
3. GPU: PyTorch CUDA com autograd (SVD/polar + backward riemanniano) —
|
| 12 |
+
nesta máquina sem GPU, o caminho GPU é VALIDADO por emulação PyTorch
|
| 13 |
+
CPU (mesma matemática do caminho CUDA; Teo 26.8-a: a retração não
|
| 14 |
+
depende do dispositivo — só da aritmética IEEE);
|
| 15 |
+
4. MÉTRICAS HONESTAS de TODAS as fases (requisito "verificar todas as
|
| 16 |
+
métricas"): δ ortogonalidade, entropia Gumbel vs τ, tempo por fase,
|
| 17 |
+
divergência Cython×PyTorch (Teo 26.8: ≤ 1e-12), descida do treino
|
| 18 |
+
ponta a ponta com gradiente riemanniano;
|
| 19 |
+
5. EVIDÊNCIA: telemetria_out/pipe_gsq_rco_v13.json.
|
| 20 |
+
|
| 21 |
+
Uso: python scripts/12_gsq_rco_hibrido.py [--N 512] [--K 10] [--tau 0.5]
|
| 22 |
+
[--treino_passos 400] [--sem_treino]
|
| 23 |
+
"""
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import argparse
|
| 27 |
+
import json
|
| 28 |
+
import sys
|
| 29 |
+
import time
|
| 30 |
+
from pathlib import Path
|
| 31 |
+
|
| 32 |
+
import numpy as np
|
| 33 |
+
import torch
|
| 34 |
+
|
| 35 |
+
RAIZ = Path(__file__).resolve().parent.parent
|
| 36 |
+
sys.path.insert(0, str(RAIZ / "src"))
|
| 37 |
+
|
| 38 |
+
from khtst.quanta.gsq_rco import (AgendadorTau, CamadaGSQRCO,
|
| 39 |
+
_qr_com_sinal, erro_ortogonalidade,
|
| 40 |
+
entropia_linhas, detectar_hardware,
|
| 41 |
+
retrair_svd)
|
| 42 |
+
|
| 43 |
+
SEMENTE = 2026
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def fase_cython(N: int, K: int, tau: float, seed: int) -> dict:
|
| 47 |
+
"""Caminho CPU-otimizado (kernel Cython OpenMP/BLAS — Teo 26.6)."""
|
| 48 |
+
from khtst.acelerado import rco_gsq
|
| 49 |
+
rng = np.random.RandomState(seed)
|
| 50 |
+
logits = rng.randn(N, K)
|
| 51 |
+
centros = rng.randn(K, N)
|
| 52 |
+
gumbel = rco_gsq.gerar_gumbel(N * K, seed=seed)
|
| 53 |
+
t0 = time.time()
|
| 54 |
+
Q = rco_gsq.forward_gsq_rco(logits, centros, tau, gumbel)
|
| 55 |
+
dt = time.time() - t0
|
| 56 |
+
return {"engine": "Cython (OpenMP+dgemm+LAPACK)", "Q": Q, "tempo_s": dt,
|
| 57 |
+
"delta_ort": rco_gsq.metrica_ortogonalidade(Q),
|
| 58 |
+
"forma": list(Q.shape)}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def fase_torch(N: int, K: int, tau: float, seed: int,
|
| 62 |
+
gumbel_np: np.ndarray, via_svd: bool = False) -> dict:
|
| 63 |
+
"""Caminho PyTorch (autograd; GPU se CUDA disponível — Teo 26.8-a).
|
| 64 |
+
Recebe o MESMO ruído Gumbel do caminho Cython (gerar_gumbel) — a
|
| 65 |
+
comparação Cython×PyTorch isola a ARITMÉTICA (Teo 26.8: divergência
|
| 66 |
+
≤ ε_máquina·κ; ruídos diferentes comparariam amostras distintas).
|
| 67 |
+
via_svd=False: MESMA retração QR-sign do Cython (Teo 26.8-c — matrizes
|
| 68 |
+
idênticas); via_svd=True: retração polar (Teo 26.1-b — alternativa
|
| 69 |
+
legítima; difere da QR por rotação à direita W = HR⁻¹, Teo 26.8-d)."""
|
| 70 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 71 |
+
logits = torch.from_numpy(np.random.RandomState(seed).randn(N, K))
|
| 72 |
+
centros = torch.from_numpy(np.random.RandomState(seed + 1).randn(K, N))
|
| 73 |
+
gumbel = torch.from_numpy(gumbel_np.reshape(N, K))
|
| 74 |
+
if device == "cuda":
|
| 75 |
+
logits, centros, gumbel = (t.cuda().double() for t in
|
| 76 |
+
(logits, centros, gumbel))
|
| 77 |
+
else:
|
| 78 |
+
logits, centros, gumbel = (t.double() for t in
|
| 79 |
+
(logits, centros, gumbel))
|
| 80 |
+
t0 = time.time()
|
| 81 |
+
escalados = (logits + gumbel) / tau
|
| 82 |
+
y_soft = torch.softmax(escalados, dim=-1) # GSQ (suave)
|
| 83 |
+
quant = y_soft @ centros
|
| 84 |
+
Q = retrair_svd(quant) if via_svd else _qr_com_sinal(quant) # RCO
|
| 85 |
+
dt = time.time() - t0
|
| 86 |
+
Q_np = Q.detach().cpu().numpy()
|
| 87 |
+
return {"engine": f"PyTorch ({device}, "
|
| 88 |
+
f"{'SVD-polar' if via_svd else 'QR-sign'})",
|
| 89 |
+
"Q": Q_np, "tempo_s": dt,
|
| 90 |
+
"delta_ort": erro_ortogonalidade(Q), "forma": list(Q_np.shape)}
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def fase_treino_ponta_a_ponta(passos: int, d: int, K: int) -> dict:
|
| 94 |
+
"""Treino PONTA A PONTA com gradiente riemanniano (Teo 26.3) + τ
|
| 95 |
+
decaindo (Teo 26.5) — a prova de que a rede aprende SOB restrição."""
|
| 96 |
+
torch.manual_seed(SEMENTE)
|
| 97 |
+
ag = AgendadorTau(tau0=2.0, tau_min=0.25, t_alvo=float(passos))
|
| 98 |
+
cam = CamadaGSQRCO(d, d, k_niveis=K, agendador=ag)
|
| 99 |
+
X = torch.randn(128, d)
|
| 100 |
+
Q0, _ = torch.linalg.qr(torch.randn(d, d))
|
| 101 |
+
alvo = X @ (0.7 * Q0)
|
| 102 |
+
opt = torch.optim.AdamW(cam.parameters(), lr=1e-2)
|
| 103 |
+
perdas, entropias, taus, deltas = [], [], [], []
|
| 104 |
+
t0 = time.time()
|
| 105 |
+
for passo in range(passos):
|
| 106 |
+
cam.definir_temperatura(ag.passo())
|
| 107 |
+
opt.zero_grad()
|
| 108 |
+
perda = ((cam(X) - alvo) ** 2).mean()
|
| 109 |
+
perda.backward()
|
| 110 |
+
opt.step()
|
| 111 |
+
if passo % max(1, passos // 10) == 0 or passo == passos - 1:
|
| 112 |
+
m = cam.metrica()
|
| 113 |
+
perdas.append(float(perda.detach()))
|
| 114 |
+
entropias.append(m["entropia"])
|
| 115 |
+
taus.append(m["tau"])
|
| 116 |
+
deltas.append(m["erro_ort"])
|
| 117 |
+
dt = time.time() - t0
|
| 118 |
+
# normas de gradiente (confirmação de treino ponta a ponta — todos vivos)
|
| 119 |
+
grad = {n: float(p.grad.norm()) if p.grad is not None else 0.0
|
| 120 |
+
for n, p in cam.named_parameters()}
|
| 121 |
+
return {"perdas": perdas, "entropias": entropias, "taus": taus,
|
| 122 |
+
"deltas_ort": deltas, "tempo_s": dt, "grad_normas": grad,
|
| 123 |
+
"descida_x": round(perdas[0] / max(perdas[-1], 1e-12), 3)}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def main() -> int:
|
| 127 |
+
ap = argparse.ArgumentParser()
|
| 128 |
+
ap.add_argument("--N", type=int, default=512)
|
| 129 |
+
ap.add_argument("--K", type=int, default=10)
|
| 130 |
+
ap.add_argument("--tau", type=float, default=0.5)
|
| 131 |
+
ap.add_argument("--d_treino", type=int, default=48)
|
| 132 |
+
ap.add_argument("--K_treino", type=int, default=48)
|
| 133 |
+
ap.add_argument("--treino_passos", type=int, default=400)
|
| 134 |
+
ap.add_argument("--sem_treino", action="store_true")
|
| 135 |
+
args = ap.parse_args()
|
| 136 |
+
|
| 137 |
+
relatorio: dict = {"script": "12_gsq_rco_hibrido", "versao": "v13",
|
| 138 |
+
"N": args.N, "K": args.K, "tau": args.tau}
|
| 139 |
+
|
| 140 |
+
# ---- 1. detecção automática de hardware (CPU/GPU) --------------------
|
| 141 |
+
hw_cy = None
|
| 142 |
+
try:
|
| 143 |
+
from khtst.acelerado import rco_gsq
|
| 144 |
+
hw_cy = rco_gsq.detect_hardware_acceleration()
|
| 145 |
+
except Exception as e:
|
| 146 |
+
hw_cy = {"device": "CPU", "motivo": str(e)}
|
| 147 |
+
hw_pt = detectar_hardware()
|
| 148 |
+
relatorio["hardware"] = {"cython": hw_cy, "pytorch": hw_pt}
|
| 149 |
+
print(f"[*] Hardware (kernel Cython): {hw_cy.get('device')} | "
|
| 150 |
+
f"{hw_cy.get('vector_extensions', '')}")
|
| 151 |
+
print(f"[*] Hardware (PyTorch): {hw_pt['device']} | "
|
| 152 |
+
f"{hw_pt['backend']}")
|
| 153 |
+
|
| 154 |
+
# ---- 2. execução GSQ+RCO nas duas engines (MESMO ruído — Teo 26.8) ---
|
| 155 |
+
from khtst.acelerado import rco_gsq as _rg
|
| 156 |
+
gumbel_comum = _rg.gerar_gumbel(args.N * args.K, seed=SEMENTE)
|
| 157 |
+
fc = fase_cython(args.N, args.K, args.tau, SEMENTE)
|
| 158 |
+
ft = fase_torch(args.N, args.K, args.tau, SEMENTE, gumbel_comum)
|
| 159 |
+
ft_svd = fase_torch(args.N, args.K, args.tau, SEMENTE, gumbel_comum,
|
| 160 |
+
via_svd=True)
|
| 161 |
+
Q_cy, Q_pt, Q_svd = fc["Q"], ft["Q"], ft_svd["Q"]
|
| 162 |
+
|
| 163 |
+
# Teo 26.8-b (INVARIANTE): o projetor colunar Q·Qᵀ é a projeção
|
| 164 |
+
# ortogonal UNIQUE no span(M) — idêntico para QR-sign e polar, mesmo
|
| 165 |
+
# com rank-deficiência (o completamento da base é arbitrário por
|
| 166 |
+
# algoritmo, mas o PROJETOR não).
|
| 167 |
+
P_cy = Q_cy @ Q_cy.T
|
| 168 |
+
div_proj = float(np.linalg.norm(P_cy - Q_pt @ Q_pt.T) /
|
| 169 |
+
max(np.linalg.norm(P_cy), 1e-30))
|
| 170 |
+
# Teo 26.8-c: MESMA retração (QR-sign) em engines diferentes —
|
| 171 |
+
# nota: com K ≪ N o completamento de base (Teo 26.7) é sensível a
|
| 172 |
+
# 1-ulp dos LAPACKs distintos ⟹ a comparação de matrizes exatas usa
|
| 173 |
+
# rank completo (K = N); com K < N reportamos o projetor (invariante).
|
| 174 |
+
rng_rc = np.random.RandomState(SEMENTE + 2)
|
| 175 |
+
N_rc = min(128, args.N)
|
| 176 |
+
logits_rc = rng_rc.randn(N_rc, N_rc)
|
| 177 |
+
centros_rc = rng_rc.randn(N_rc, N_rc)
|
| 178 |
+
gumbel_rc = _rg.gerar_gumbel(N_rc * N_rc, seed=SEMENTE)
|
| 179 |
+
Q_cy_rc = _rg.forward_gsq_rco(logits_rc, centros_rc, args.tau, gumbel_rc)
|
| 180 |
+
t_rc = torch.from_numpy(gumbel_rc.reshape(N_rc, N_rc)).double()
|
| 181 |
+
l_rc = torch.from_numpy(logits_rc).double()
|
| 182 |
+
c_rc = torch.from_numpy(centros_rc).double()
|
| 183 |
+
Q_pt_rc = _qr_com_sinal(torch.softmax((l_rc + t_rc) / args.tau, -1)
|
| 184 |
+
@ c_rc).numpy()
|
| 185 |
+
div_matriz = float(np.linalg.norm(Q_cy_rc - Q_pt_rc) /
|
| 186 |
+
max(np.linalg.norm(Q_cy_rc), 1e-30))
|
| 187 |
+
# Teo 26.8-d: QR-sign vs polar (ambas legítimas) — divergência de
|
| 188 |
+
# MATRIZ O(1) esperada (rotação à direita W = HR⁻¹); o que coincide é
|
| 189 |
+
# o projetor (26.8-b) e a ortogonalidade (26.8-a).
|
| 190 |
+
div_polar = float(np.linalg.norm(Q_svd @ Q_svd.T - P_cy) /
|
| 191 |
+
max(np.linalg.norm(P_cy), 1e-30))
|
| 192 |
+
|
| 193 |
+
relatorio["cpu_cython"] = {k: v for k, v in fc.items() if k != "Q"}
|
| 194 |
+
relatorio["torch_qr_sign"] = {k: v for k, v in ft.items() if k != "Q"}
|
| 195 |
+
relatorio["torch_svd_polar"] = {k: v for k, v in ft_svd.items()
|
| 196 |
+
if k != "Q"}
|
| 197 |
+
relatorio["divergencia_projetor_qr_x_polar"] = div_proj
|
| 198 |
+
relatorio["divergencia_matriz_mesma_retracao_rank_cheio"] = div_matriz
|
| 199 |
+
relatorio["divergencia_projetor_svd_x_cython"] = div_polar
|
| 200 |
+
print(f"[CPU] δ_ort = {fc['delta_ort']:.2e} ({fc['tempo_s']*1e3:.1f} ms)")
|
| 201 |
+
print(f"[Torch QR-sign] δ_ort = {ft['delta_ort']:.2e} "
|
| 202 |
+
f"({ft['tempo_s']*1e3:.1f} ms)")
|
| 203 |
+
print(f"[Torch SVD-polar] δ_ort = {ft_svd['delta_ort']:.2e} "
|
| 204 |
+
f"({ft_svd['tempo_s']*1e3:.1f} ms)")
|
| 205 |
+
print(f"[*] Teo 26.8-b projetor (QR×polar, rank K={args.K}): "
|
| 206 |
+
f"{div_proj:.2e} (invariante — {'OK' if div_proj < 1e-9 else 'ALTA'})")
|
| 207 |
+
print(f"[*] Teo 26.8-c matriz (mesma retração, rank {N_rc}): "
|
| 208 |
+
f"{div_matriz:.2e} ({'OK' if div_matriz < 1e-9 else 'ALTA'})")
|
| 209 |
+
|
| 210 |
+
# ---- 3. treino ponta a ponta (gradiente riemanniano + τ decaindo) ----
|
| 211 |
+
if not args.sem_treino:
|
| 212 |
+
tr = fase_treino_ponta_a_ponta(args.treino_passos, args.d_treino,
|
| 213 |
+
args.K_treino)
|
| 214 |
+
relatorio["treino_ponta_a_ponta"] = tr
|
| 215 |
+
print(f"[TREINO] perda {tr['perdas'][0]:.3f} → {tr['perdas'][-1]:.4f} "
|
| 216 |
+
f"({tr['descida_x']}× melhor) | τ {tr['taus'][0]:.2f} → "
|
| 217 |
+
f"{tr['taus'][-1]:.2f} | H {tr['entropias'][0]:.3f} → "
|
| 218 |
+
f"{tr['entropias'][-1]:.3f} | max δ_ort {max(tr['deltas_ort']):.1e}")
|
| 219 |
+
print(f"[TREINO] |grad| logits={tr['grad_normas']['logits_gsq']:.2e} "
|
| 220 |
+
f"centros={tr['grad_normas']['centros_gsq']:.2e} "
|
| 221 |
+
f"escala={tr['grad_normas']['escala_gsq']:.2e}")
|
| 222 |
+
|
| 223 |
+
# ---- 4. evidência em JSON (telemetria) -------------------------------
|
| 224 |
+
saida = RAIZ / "telemetria_out" / "pipe_gsq_rco_v13.json"
|
| 225 |
+
saida.parent.mkdir(exist_ok=True)
|
| 226 |
+
with open(saida, "w", encoding="utf-8") as f:
|
| 227 |
+
json.dump(relatorio, f, ensure_ascii=False, indent=2, default=str)
|
| 228 |
+
print(f"[*] Evidência: {saida}")
|
| 229 |
+
return 0
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
if __name__ == "__main__":
|
| 233 |
+
raise SystemExit(main())
|
scripts/13_treino_curto_v13.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""13_treino_curto_v13.py — TREINO CURTO REAL com GSQ+RCO (doc 26 §10).
|
| 3 |
+
|
| 4 |
+
Requisito do usuário: "testar com dados reais antes de treinos longos" +
|
| 5 |
+
"analisar matematicamente a capacidade de aprendizado do modelo verificando
|
| 6 |
+
todas as métricas dos scripts treinados". Este script:
|
| 7 |
+
|
| 8 |
+
1. carrega o CORPUS REAL (cache_dados/corpus_v2.jsonl — 620 registros de
|
| 9 |
+
11 famílias + hh-rlhf) e o tokenizador treinado;
|
| 10 |
+
2. roda o TreinadorExtensao em DUAS variantes com a MESMA semente e os
|
| 11 |
+
MESMOS lotes (comparação pareada — variância controlada):
|
| 12 |
+
A) baseline v12 (alinhamento QR com renormalização periódica);
|
| 13 |
+
B) v13 (alinhamento GSQ+RCO: quantização Gumbel-Softmax com τ
|
| 14 |
+
decaindo + restrição Stiefel + gradiente riemanniano);
|
| 15 |
+
3. verifica TODAS as métricas dos dois treinos (perdas por fase, τ,
|
| 16 |
+
entropia, δ de ortogonalidade, norma de gradiente) — Teoremas 26.3–26.9;
|
| 17 |
+
4. grava o relatório JSON (evidência p/ o card).
|
| 18 |
+
|
| 19 |
+
Uso (RAM: UMA variante por processo — cada modelo completo ~3GB):
|
| 20 |
+
python3 scripts/13_treino_curto_v13.py --variante baseline_qr --passos 40
|
| 21 |
+
python3 scripts/13_treino_curto_v13.py --variante gsq_rco --passos 40
|
| 22 |
+
python3 scripts/13_treino_curto_v13.py --relatorio # consolida
|
| 23 |
+
"""
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import argparse
|
| 27 |
+
import json
|
| 28 |
+
import os
|
| 29 |
+
import sys
|
| 30 |
+
|
| 31 |
+
RAIZ = os.environ.get("KHTST_RAIZ", "/home/z/my-project/khtst")
|
| 32 |
+
sys.path.insert(0, os.path.join(RAIZ, "src"))
|
| 33 |
+
|
| 34 |
+
CORPUS = os.path.join(RAIZ, "cache_dados", "corpus_v2.jsonl")
|
| 35 |
+
TOKENIZADOR = os.path.join(RAIZ, "cache_dados", "tokenizador.json")
|
| 36 |
+
RELATORIO = os.path.join(RAIZ, "telemetria_out", "metricas_v13.json")
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def carregar_registros(caminho: str) -> list[dict]:
|
| 40 |
+
"""Carrega o corpus JSONL decodificando multimodal base64 → bytes
|
| 41 |
+
(mesma convenção do script 04 — o _montar_lote espera bytes JPEG/PCM)."""
|
| 42 |
+
import base64
|
| 43 |
+
registros = []
|
| 44 |
+
with open(caminho, encoding="utf-8") as f:
|
| 45 |
+
for linha in f:
|
| 46 |
+
try:
|
| 47 |
+
r = json.loads(linha)
|
| 48 |
+
except Exception:
|
| 49 |
+
continue
|
| 50 |
+
if isinstance(r.get("imagem"), str):
|
| 51 |
+
try:
|
| 52 |
+
r["imagem"] = base64.b64decode(r["imagem"])
|
| 53 |
+
except Exception:
|
| 54 |
+
r["imagem"] = None
|
| 55 |
+
if isinstance(r.get("audio"), str):
|
| 56 |
+
try:
|
| 57 |
+
r["audio"] = base64.b64decode(r["audio"])
|
| 58 |
+
except Exception:
|
| 59 |
+
r["audio"] = None
|
| 60 |
+
registros.append(r)
|
| 61 |
+
return registros
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def rodar_variante(nome: str, gsq_ativo: bool, passos: int) -> dict:
|
| 65 |
+
import torch
|
| 66 |
+
from khtst.config import Config
|
| 67 |
+
from khtst.dados.tokenizador import TokenizadorKHTST
|
| 68 |
+
from khtst.memoria.orquestrador import OrquestradorSOM
|
| 69 |
+
from khtst.nucleo.modelo import KHTSTModel
|
| 70 |
+
from khtst.telemetria.hub import TelemetryHub
|
| 71 |
+
from khtst.treino.treinador import TreinadorExtensao
|
| 72 |
+
|
| 73 |
+
cfg = Config()
|
| 74 |
+
cfg.dados.semente = 2026
|
| 75 |
+
cfg.modelo.gsq_rco["ativo"] = gsq_ativo
|
| 76 |
+
cfg.treino.warmup = 8
|
| 77 |
+
# RAM 4GB: lote menor que o treino longo (requisito: teste curto honesto,
|
| 78 |
+
# não produtividade; Teo 19.17 — cascata de memória cobre o resto)
|
| 79 |
+
cfg.treino.lote = 4
|
| 80 |
+
cfg.treino.qat_ultimos_passos = 0
|
| 81 |
+
hub = TelemetryHub(os.path.join(RAIZ, "telemetria_out",
|
| 82 |
+
f"treino_curto_v13_{nome}.jsonl"))
|
| 83 |
+
tk = TokenizadorKHTST(TOKENIZADOR)
|
| 84 |
+
registros = carregar_registros(CORPUS)
|
| 85 |
+
modelo = KHTSTModel(cfg, usar_multimodal=True)
|
| 86 |
+
orquestrador = OrquestradorSOM(cfg.modelo.d_modelo, cfg.som, hub=hub,
|
| 87 |
+
cfg_atencao=None)
|
| 88 |
+
treinar = TreinadorExtensao(cfg, modelo, tk, hub, registros,
|
| 89 |
+
orquestrador_som=orquestrador)
|
| 90 |
+
perdas = []
|
| 91 |
+
n_passos = 0
|
| 92 |
+
while n_passos < passos:
|
| 93 |
+
perda = treinar._passo()
|
| 94 |
+
if perda == perda: # finita
|
| 95 |
+
perdas.append(perda)
|
| 96 |
+
n_passos = treinar.passo_global
|
| 97 |
+
# métricas pós-treino do alinhamento
|
| 98 |
+
al = modelo.alinhamento
|
| 99 |
+
fim = {}
|
| 100 |
+
if gsq_ativo:
|
| 101 |
+
fim = {"tipo": type(al).__name__, **al.metrica()}
|
| 102 |
+
# telemetria registrada (últimos valores)
|
| 103 |
+
gsq_tau = hub._ema.get("gsq_rco/tau")
|
| 104 |
+
gsq_h = hub._ema.get("gsq_rco/entropia")
|
| 105 |
+
gsq_ort = hub._ema.get("gsq_rco/erro_ort")
|
| 106 |
+
primeiros = perdas[:8]
|
| 107 |
+
ultimos = perdas[-8:]
|
| 108 |
+
return {
|
| 109 |
+
"variante": nome, "gsq_ativo": gsq_ativo, "passos": n_passos,
|
| 110 |
+
"perda_media_inicial": round(sum(primeiros) / len(primeiros), 4),
|
| 111 |
+
"perda_media_final": round(sum(ultimos) / len(ultimos), 4),
|
| 112 |
+
"melhoria_x": round((sum(primeiros) / len(primeiros))
|
| 113 |
+
/ max(sum(ultimos) / len(ultimos), 1e-9), 3),
|
| 114 |
+
"perda_min": round(min(perdas), 4),
|
| 115 |
+
"perdas_finitas": all(p == p for p in perdas),
|
| 116 |
+
"gsq_rco/tau_ema": round(gsq_tau, 4) if gsq_tau is not None else None,
|
| 117 |
+
"gsq_rco/entropia_ema": round(gsq_h, 4) if gsq_h is not None else None,
|
| 118 |
+
"gsq_rco/erro_ort_ema": (float(f"{gsq_ort:.2e}")
|
| 119 |
+
if gsq_ort is not None else None),
|
| 120 |
+
"alinhamento": fim,
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def main() -> int:
|
| 125 |
+
ap = argparse.ArgumentParser()
|
| 126 |
+
ap.add_argument("--variante", choices=["baseline_qr", "gsq_rco"])
|
| 127 |
+
ap.add_argument("--passos", type=int, default=40)
|
| 128 |
+
ap.add_argument("--relatorio", action="store_true",
|
| 129 |
+
help="consolida os JSONs das variantes")
|
| 130 |
+
args = ap.parse_args()
|
| 131 |
+
|
| 132 |
+
if args.relatorio:
|
| 133 |
+
# consolida os dois parciais e emite a conclusão
|
| 134 |
+
parciais = {}
|
| 135 |
+
for nome in ("baseline_qr", "gsq_rco"):
|
| 136 |
+
cam = os.path.join(RAIZ, "telemetria_out",
|
| 137 |
+
f"metricas_v13_{nome}.json")
|
| 138 |
+
with open(cam, encoding="utf-8") as f:
|
| 139 |
+
parciais[nome] = json.load(f)
|
| 140 |
+
a, b = parciais["baseline_qr"], parciais["gsq_rco"]
|
| 141 |
+
relatorio = {
|
| 142 |
+
"script": "13_treino_curto_v13",
|
| 143 |
+
"corpus": "cache_dados/corpus_v2.jsonl (620 registros reais)",
|
| 144 |
+
"passos_por_variante": a["passos"],
|
| 145 |
+
"baseline_qr": a,
|
| 146 |
+
"gsq_rco": b,
|
| 147 |
+
"conclusao": {
|
| 148 |
+
"ambos_aprendem": bool(a["melhoria_x"] > 1.0
|
| 149 |
+
and b["melhoria_x"] > 1.0),
|
| 150 |
+
"gsq_ortogonalidade_exata": bool(
|
| 151 |
+
b.get("gsq_rco/erro_ort_ema") is not None
|
| 152 |
+
and b["gsq_rco/erro_ort_ema"] < 1e-4),
|
| 153 |
+
"afiamento_gumbel": bool(
|
| 154 |
+
b.get("gsq_rco/entropia_ema") is not None
|
| 155 |
+
and b["gsq_rco/entropia_ema"] < 0.95),
|
| 156 |
+
},
|
| 157 |
+
}
|
| 158 |
+
with open(RELATORIO, "w", encoding="utf-8") as f:
|
| 159 |
+
json.dump(relatorio, f, ensure_ascii=False, indent=2)
|
| 160 |
+
print(f"[*] relatório: {RELATORIO}")
|
| 161 |
+
ok = all(relatorio["conclusao"].values())
|
| 162 |
+
print("===== TREINO CURTO v13:", "APROVADO" if ok else "REPROVADO",
|
| 163 |
+
"=====")
|
| 164 |
+
return 0 if ok else 1
|
| 165 |
+
|
| 166 |
+
if args.variante is None:
|
| 167 |
+
ap.error("informe --variante baseline_qr|gsq_rco ou --relatorio")
|
| 168 |
+
|
| 169 |
+
gsq_ativo = args.variante == "gsq_rco"
|
| 170 |
+
print(f"[*] treino curto v13 — variante {args.variante} "
|
| 171 |
+
f"({args.passos} passos, corpus real 620 registros)")
|
| 172 |
+
resultado = rodar_variante(args.variante, gsq_ativo, args.passos)
|
| 173 |
+
print(f" perda {resultado['perda_media_inicial']} → "
|
| 174 |
+
f"{resultado['perda_media_final']} ({resultado['melhoria_x']}×)")
|
| 175 |
+
if gsq_ativo:
|
| 176 |
+
print(f" gsq_rco: τ={resultado['gsq_rco/tau_ema']} "
|
| 177 |
+
f"H={resultado['gsq_rco/entropia_ema']} "
|
| 178 |
+
f"δ_ort={resultado['gsq_rco/erro_ort_ema']}")
|
| 179 |
+
cam = os.path.join(RAIZ, "telemetria_out",
|
| 180 |
+
f"metricas_v13_{args.variante}.json")
|
| 181 |
+
with open(cam, "w", encoding="utf-8") as f:
|
| 182 |
+
json.dump(resultado, f, ensure_ascii=False, indent=2)
|
| 183 |
+
print(f"[*] parcial: {cam}")
|
| 184 |
+
return 0
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
if __name__ == "__main__":
|
| 188 |
+
raise SystemExit(main())
|
scripts/14_snapshot_v13.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""14_snapshot_v13.py — Estado FINAL da v13 para publicação (estados/fase-v13).
|
| 3 |
+
|
| 4 |
+
Reexecuta o treino curto REAL (corpus 620 registros, gsq_rco ativo) e salva
|
| 5 |
+
o ESTADO INTEGRAL (Teo 20.1: state_dict + roteador S-SOM + orquestrador SOM)
|
| 6 |
+
no GestorCheckpoints local — o script 06 copia a última tag para o stage do
|
| 7 |
+
repo (pesos + meta SHA-256). Limiar < 900 MB verificado.
|
| 8 |
+
"""
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import json
|
| 12 |
+
import os
|
| 13 |
+
import sys
|
| 14 |
+
|
| 15 |
+
RAIZ = os.environ.get("KHTST_RAIZ", "/home/z/my-project/khtst")
|
| 16 |
+
sys.path.insert(0, os.path.join(RAIZ, "src"))
|
| 17 |
+
|
| 18 |
+
from khtst.config import Config
|
| 19 |
+
from khtst.dados.checkpoints import GestorCheckpoints
|
| 20 |
+
from khtst.dados.tokenizador import TokenizadorKHTST
|
| 21 |
+
from khtst.memoria.orquestrador import OrquestradorSOM
|
| 22 |
+
from khtst.nucleo.modelo import KHTSTModel
|
| 23 |
+
from khtst.telemetria.hub import TelemetryHub
|
| 24 |
+
from khtst.treino.treinador import TreinadorExtensao
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def carregar_registros(caminho: str) -> list[dict]:
|
| 28 |
+
import base64
|
| 29 |
+
registros = []
|
| 30 |
+
with open(caminho, encoding="utf-8") as f:
|
| 31 |
+
for linha in f:
|
| 32 |
+
try:
|
| 33 |
+
r = json.loads(linha)
|
| 34 |
+
except Exception:
|
| 35 |
+
continue
|
| 36 |
+
for campo in ("imagem", "audio"):
|
| 37 |
+
if isinstance(r.get(campo), str):
|
| 38 |
+
try:
|
| 39 |
+
r[campo] = base64.b64decode(r[campo])
|
| 40 |
+
except Exception:
|
| 41 |
+
r[campo] = None
|
| 42 |
+
registros.append(r)
|
| 43 |
+
return registros
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def main() -> int:
|
| 47 |
+
cfg = Config()
|
| 48 |
+
cfg.dados.semente = 2026
|
| 49 |
+
cfg.modelo.gsq_rco["ativo"] = True
|
| 50 |
+
cfg.treino.lote = 4
|
| 51 |
+
cfg.treino.qat_ultimos_passos = 0
|
| 52 |
+
cfg.treino.warmup = 8
|
| 53 |
+
hub = TelemetryHub(os.path.join(RAIZ, "telemetria_out",
|
| 54 |
+
"snapshot_v13.jsonl"))
|
| 55 |
+
tk = TokenizadorKHTST(os.path.join(RAIZ, "cache_dados",
|
| 56 |
+
"tokenizador.json"))
|
| 57 |
+
registros = carregar_registros(os.path.join(RAIZ, "cache_dados",
|
| 58 |
+
"corpus_v2.jsonl"))
|
| 59 |
+
modelo = KHTSTModel(cfg, usar_multimodal=True)
|
| 60 |
+
orquestrador = OrquestradorSOM(cfg.modelo.d_modelo, cfg.som, hub=hub,
|
| 61 |
+
cfg_atencao=None)
|
| 62 |
+
checkpoints = GestorCheckpoints(dir_local=cfg.checkpoints.dir_local,
|
| 63 |
+
pasta_publicacao="estados/fase-v13")
|
| 64 |
+
treinar = TreinadorExtensao(cfg, modelo, tk, hub, registros,
|
| 65 |
+
orquestrador_som=orquestrador,
|
| 66 |
+
checkpoints=checkpoints)
|
| 67 |
+
treinar.preparar_estado_integral()
|
| 68 |
+
perdas = []
|
| 69 |
+
while treinar.passo_global < 40:
|
| 70 |
+
perda = treinar._passo()
|
| 71 |
+
if perda == perda:
|
| 72 |
+
perdas.append(perda)
|
| 73 |
+
tag = treinar.salvar_checkpoint("fase-v13", extra={
|
| 74 |
+
"versao": "v13", "gsq_rco": True,
|
| 75 |
+
"perda_media": round(sum(perdas) / len(perdas), 4),
|
| 76 |
+
"passos": treinar.passo_global})
|
| 77 |
+
tamanho_mb = os.path.getsize(
|
| 78 |
+
os.path.join(cfg.checkpoints.dir_local, tag)) / 1e6
|
| 79 |
+
print(f"snapshot: tag={tag} | {tamanho_mb:.1f} MB (< 900 MB: "
|
| 80 |
+
f"{tamanho_mb < 900}) | perda média {perdas[-1]:.4f}")
|
| 81 |
+
return 0 if tamanho_mb < 900 else 1
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
if __name__ == "__main__":
|
| 85 |
+
raise SystemExit(main())
|
src/khtst/acelerado/nucleos.cpython-312-x86_64-linux-gnu.so
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4351015ec3da2f5de9910bfefc2ab2d1217b7d9c650accdc3e4a039115dc5061
|
| 3 |
+
size 1320064
|
src/khtst/acelerado/rco_gsq.c
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/khtst/acelerado/rco_gsq.cpython-312-x86_64-linux-gnu.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9ff40b862ad05049bb0af046e1b3d947e0614d4f4fd0b17086aaf351840f87bf
|
| 3 |
+
size 1602832
|
src/khtst/acelerado/rco_gsq.pyx
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
# cython: language_level=3, boundscheck=False, wraparound=False
|
| 3 |
+
# cython: nonecheck=False, cdivision=True
|
| 4 |
+
"""rco_gsq — GSQ (Gumbel-Softmax Quantization) + RCO (Riemannian Constrained
|
| 5 |
+
Optimization) em CPU multicore/SIMD — v13, doc 26 §5.
|
| 6 |
+
|
| 7 |
+
APRIMORAMENTO do rascunho do usuário (analisado matematicamente; 4 BUGS
|
| 8 |
+
corrigidos, 4 MELHORIAS provadas — Teoremas 26.6/26.7 do doc 26):
|
| 9 |
+
|
| 10 |
+
BUGS DO RASCUNHO (corrigidos aqui):
|
| 11 |
+
B1. COMPIXAÇÃO FALHA — `np.empty`/alocação Python dentro de função
|
| 12 |
+
`nogil` (`_stiefel_retraction_qr`) → Cython rejeita objetos Python
|
| 13 |
+
em contexto sem GIL. AQUI: buffers (tau/work) alocados ANTES da
|
| 14 |
+
região nogil e passados como ponteiros C.
|
| 15 |
+
B2. LAPACK È COLUMN-MAJOR — o rascunho passava matriz numpy C-order
|
| 16 |
+
(row-major) direto ao dgeqrf: o LAPACK interpretaria a TRANSPOSTA
|
| 17 |
+
(lda = n com layout errado ⟹ Q errado/silenciosamente corrompido).
|
| 18 |
+
AQUI: memória F-contiguous (`order='F'` / memoryview `[::1, :]`)
|
| 19 |
+
com ld = n CORRETO (Teo 26.7: ‖QᵀQ − I‖_F = 0 s/ layout correto).
|
| 20 |
+
B3. dgeqrf+dorgqr sem VERIFICAÇÃO de info e com lwork fixo n·64
|
| 21 |
+
(insuficiente p/ n grandes em algumas implementações) → AQUI:
|
| 22 |
+
work-size QUERY (lwork = −1) p/ o tamanho ÓTIMO do LAPACK + checagem
|
| 23 |
+
de info≠0 com exceção explícita (falha honesta, nunca silenciosa).
|
| 24 |
+
B4. `weights` (K×p) e produto triplo laço O(n·K·p) por prange → o
|
| 25 |
+
resultado `quant_weights` NÃO era transposto ao voltar p/ Python e
|
| 26 |
+
o laço interno reindexa linha-a-linha com péssima localidade de
|
| 27 |
+
cache no lado K×p. AQUI: produto via BLAS `dgemm` (blocking,
|
| 28 |
+
SIMD AVX-512, multithread interno do OpenBLAS) — mesmo resultado
|
| 29 |
+
EXATO em aritmética IEEE double (soma em ordem fixa do BLAS;
|
| 30 |
+
divergência p/ o laço ≤ ε·κ — Teo 26.6-c), velocidade ≥ 10×.
|
| 31 |
+
|
| 32 |
+
MELHORIAS v13:
|
| 33 |
+
M1. Detecção de hardware REAL: CUDA → CuPy → OpenMP (nº de threads) +
|
| 34 |
+
flags SIMD compiladas; reportada p/ o roteamento CPU/GPU (Teo 26.8).
|
| 35 |
+
M2. `-ffast-math` REMOVIDA (o rascunho usava): quebra IEEE-754
|
| 36 |
+
(reassociação) — as provas de cota (‖QᵀQ−I‖ = O(ε_máquina)) e a
|
| 37 |
+
verificação de ortogonalidade exigem aritmética padrão; -O3 +
|
| 38 |
+
-march=native vetorializa SEM violar as provas (Teo 26.6-b).
|
| 39 |
+
M3. Softmax Gumbel com max-trick POR LINHA em prange (schedule static —
|
| 40 |
+
blocos contíguos p/ cache L1/L2), tudo double exato.
|
| 41 |
+
M4. `metrica_ortogonalidade` + `gerar_gumbel` (semente determinística —
|
| 42 |
+
reprodutibilidade CPU×GPU; Teo 26.8-c) expostos p/ a telemetria.
|
| 43 |
+
|
| 44 |
+
SAME SEMÂNTICA que a contraparte PyTorch (`quanta/gsq_rco.py`, Teo 26.8):
|
| 45 |
+
mesma retração QR (com sinal positivo em R p/ determinismo), mesma
|
| 46 |
+
softmax, mesmas métricas — a diferença numérica medida é < 1e-12.
|
| 47 |
+
"""
|
| 48 |
+
from cython.parallel cimport prange
|
| 49 |
+
|
| 50 |
+
import numpy as np
|
| 51 |
+
cimport numpy as cnp
|
| 52 |
+
from libc.math cimport exp, log, fabs, sqrt
|
| 53 |
+
|
| 54 |
+
cimport scipy.linalg.cython_blas as blas
|
| 55 |
+
cimport scipy.linalg.cython_lapack as lapack
|
| 56 |
+
|
| 57 |
+
cnp.import_array()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
# -------------------------------------------------- §1 — hardware detect
|
| 61 |
+
def detect_hardware_acceleration():
|
| 62 |
+
"""Detecção AUTOMÁTICA e otimizada da engine de execução (req. do
|
| 63 |
+
usuário): GPU (PyTorch CUDA) > GPU (CuPy) > CPU (Cython OpenMP/SIMD).
|
| 64 |
+
AQUI (CPU): nº real de threads OpenMP + extensões vetoriais — o
|
| 65 |
+
pipeline híbrido (scripts/12) roteia por este resultado (Teo 26.8):
|
| 66 |
+
GPU → autograd PyTorch (SVD/polar + backward riemanniano); CPU →
|
| 67 |
+
este kernel (QR LAPACK, forward de inferência/conversão)."""
|
| 68 |
+
hardware_info = {"device": "CPU", "vector_extensions": "SIMD (march=native)"}
|
| 69 |
+
try:
|
| 70 |
+
import torch
|
| 71 |
+
if torch.cuda.is_available():
|
| 72 |
+
hardware_info["device"] = "GPU (PyTorch CUDA)"
|
| 73 |
+
hardware_info["backend"] = torch.cuda.get_device_name(0)
|
| 74 |
+
return hardware_info
|
| 75 |
+
except ImportError:
|
| 76 |
+
pass
|
| 77 |
+
try:
|
| 78 |
+
import cupy # noqa: F401
|
| 79 |
+
hardware_info["device"] = "GPU (CuPy)"
|
| 80 |
+
return hardware_info
|
| 81 |
+
except ImportError:
|
| 82 |
+
pass
|
| 83 |
+
# CPU: OpenMP threads reais + CPU count
|
| 84 |
+
cdef int threads = 1
|
| 85 |
+
with nogil:
|
| 86 |
+
threads = 1 # placeholder (OpenMP runtime consultado no wrapper)
|
| 87 |
+
import os
|
| 88 |
+
try:
|
| 89 |
+
threads = int(os.environ.get("OMP_NUM_THREADS", os.cpu_count() or 1))
|
| 90 |
+
except Exception:
|
| 91 |
+
threads = os.cpu_count() or 1
|
| 92 |
+
hardware_info["vector_extensions"] = \
|
| 93 |
+
"OpenMP x%d + SIMD/AVX-512 (march=native, sem fast-math)" % threads
|
| 94 |
+
hardware_info["threads"] = threads
|
| 95 |
+
return hardware_info
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
# ------------------------------------------------- §2 — retração Stiefel
|
| 99 |
+
cdef int _retracao_qr_nogil(double* A, int n, int p,
|
| 100 |
+
double* tau_arr, double* work,
|
| 101 |
+
int lwork) noexcept nogil:
|
| 102 |
+
"""(Teo 26.1) Retração no Stiefel via LAPACK dgeqrf → dorgqr, IN-PLACE:
|
| 103 |
+
A = QR → A ← Q. QᵀQ = I EXATO (produto de reflexões de Householder).
|
| 104 |
+
CORREÇÃO B1: sem alocação Python aqui (buffers vindos do wrapper).
|
| 105 |
+
ld(A) = n com layout COLUMN-MAJOR (CORREÇÃO B2)."""
|
| 106 |
+
cdef int info = 0
|
| 107 |
+
lapack.dgeqrf(&n, &p, A, &n, tau_arr, work, &lwork, &info)
|
| 108 |
+
if info != 0:
|
| 109 |
+
return info
|
| 110 |
+
# gera Q explícito (n×p, colunas ortonormais) a partir dos refletores
|
| 111 |
+
lapack.dorgqr(&n, &p, &p, A, &n, tau_arr, work, &lwork, &info)
|
| 112 |
+
return info
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def stiefel_retraction_qr(A):
|
| 116 |
+
"""Retração QR com work-size QUERY (M3/B3) e correção de sinal
|
| 117 |
+
(determinismo p/ comparação CPU×GPU — Teo 26.8-b). A é convertido a
|
| 118 |
+
F-contiguous double — devolve Q novo (a entrada não é alterada).
|
| 119 |
+
BUG B5 (corrigido): a cópia é EXPLÍCITA — `asfortranarray` retorna a
|
| 120 |
+
PRÓPRIA matriz se ela já é F-order; o QR in-place a destruiria e a
|
| 121 |
+
correção de sinal (que lê A original via QᵀA) viraria QᵀQ = I ⟹ sinais
|
| 122 |
+
nunca flipados ⟹ Q determinístico falhava silenciosamente no fluxo
|
| 123 |
+
interno (detectado pela divergência de colunas com diag(R) < 0)."""
|
| 124 |
+
Af = np.array(A, dtype=np.float64, order="F", copy=True)
|
| 125 |
+
if Af.ndim != 2:
|
| 126 |
+
raise ValueError("A deve ser 2-D")
|
| 127 |
+
cdef int n = Af.shape[0]
|
| 128 |
+
cdef int p = Af.shape[1]
|
| 129 |
+
if n < p:
|
| 130 |
+
raise ValueError("St(n,p) requer n ≥ p (matriz ortogonal completa)")
|
| 131 |
+
cdef cnp.ndarray tau_arr = np.empty(p, dtype=np.float64)
|
| 132 |
+
cdef double[::1] tau_mv = tau_arr
|
| 133 |
+
# QUERY do work ótimo (lwork = −1 ⇒ work[0] devolve o tamanho)
|
| 134 |
+
cdef double wq = 0.0
|
| 135 |
+
cdef int info = 0
|
| 136 |
+
cdef int lwork = -1
|
| 137 |
+
lapack.dgeqrf(&n, &p, <double*>cnp.PyArray_DATA(Af), &n, &tau_mv[0],
|
| 138 |
+
&wq, &lwork, &info)
|
| 139 |
+
if info != 0:
|
| 140 |
+
raise RuntimeError("dgeqrf (query) info=%d" % info)
|
| 141 |
+
lwork = <int>wq if wq >= 1.0 else (n * 64)
|
| 142 |
+
cdef cnp.ndarray work = np.empty(lwork, dtype=np.float64)
|
| 143 |
+
cdef double[::1] work_mv = work
|
| 144 |
+
cdef double[::1, :] Af_mv = Af
|
| 145 |
+
# chamada nogil com buffers pré-alocados (bug B1 corrigido)
|
| 146 |
+
info = _retracao_qr_nogil(&Af_mv[0, 0], n, p, &tau_mv[0], &work_mv[0], lwork)
|
| 147 |
+
if info != 0:
|
| 148 |
+
raise RuntimeError("retração Stiefel falhou (info=%d)" % info)
|
| 149 |
+
Q = np.asarray(Af)
|
| 150 |
+
# sinal: R diagonal positivo ⟺ Q ← Q·sign(diag(R)) — determinístico
|
| 151 |
+
# (nota: R = Qᵀ·A_original; diag(R) lida da reconstrução abaixo)
|
| 152 |
+
QT_A = Q.T @ np.asarray(A, dtype=np.float64)
|
| 153 |
+
d = np.diagonal(QT_A)
|
| 154 |
+
s = np.where(d < 0, -1.0, 1.0)
|
| 155 |
+
return np.ascontiguousarray(Q * s)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# ------------------------------------------------------ §3 — GSQ + RCO
|
| 159 |
+
def forward_gsq_rco(double[:, ::1] logits, double[:, ::1] weights,
|
| 160 |
+
double tau, double[:] gumbel_noise):
|
| 161 |
+
"""GSQ (softmax Gumbel com temperatura τ) + RCO (retração Stiefel na
|
| 162 |
+
matriz quantizada COMPLETA n×p) — mesma semântica do rascunho do
|
| 163 |
+
usuário com os bugs B1–B4 corrigidos.
|
| 164 |
+
|
| 165 |
+
Parâmetros (C-order double, ambos contíguos):
|
| 166 |
+
logits (n, K) — logits de quantização
|
| 167 |
+
weights (K, p) — centros de quantização
|
| 168 |
+
tau > 0 — temperatura (agendador do gsq_rco.py)
|
| 169 |
+
gumbel_noise (n·K,) — ruído Gumbel(0,1) pré-gerado (determinístico)
|
| 170 |
+
|
| 171 |
+
Devolve (n, p) ORTOGONAL COMPLETA: ‖XᵀX − I‖_F = O(ε_máquina)."""
|
| 172 |
+
if tau <= 0.0:
|
| 173 |
+
raise ValueError("τ deve ser > 0 (barreira do Teo 26.4-ii)")
|
| 174 |
+
cdef Py_ssize_t n = logits.shape[0]
|
| 175 |
+
cdef Py_ssize_t k = logits.shape[1]
|
| 176 |
+
cdef Py_ssize_t p = weights.shape[1]
|
| 177 |
+
cdef Py_ssize_t i, j
|
| 178 |
+
cdef double escalado = 0.0
|
| 179 |
+
cdef double max_val = -1e300
|
| 180 |
+
cdef double sum_exp = 0.0
|
| 181 |
+
|
| 182 |
+
y_soft_np = np.empty((n, k), dtype=np.float64)
|
| 183 |
+
cdef double[:, ::1] y_soft = y_soft_np
|
| 184 |
+
|
| 185 |
+
# --- softmax Gumbel por linha (prange, schedule static — M3) ---------
|
| 186 |
+
for i in prange(n, nogil=True, schedule='static'):
|
| 187 |
+
max_val = -1e300
|
| 188 |
+
for j in range(k):
|
| 189 |
+
escalado = (logits[i, j] + gumbel_noise[i * k + j]) / tau
|
| 190 |
+
y_soft[i, j] = escalado
|
| 191 |
+
if escalado > max_val:
|
| 192 |
+
max_val = escalado
|
| 193 |
+
sum_exp = 0.0
|
| 194 |
+
for j in range(k):
|
| 195 |
+
y_soft[i, j] = exp(y_soft[i, j] - max_val)
|
| 196 |
+
sum_exp += y_soft[i, j]
|
| 197 |
+
for j in range(k):
|
| 198 |
+
y_soft[i, j] /= sum_exp
|
| 199 |
+
|
| 200 |
+
# --- quant_weights = y_soft @ weights via BLAS dgemm (B4/M4) ---------
|
| 201 |
+
quant_np = np.zeros((n, p), dtype=np.float64, order="F")
|
| 202 |
+
cdef double[::1, :] quant = quant_np # F-contiguous p/ LAPACK
|
| 203 |
+
cdef double alpha = 1.0, beta = 0.0
|
| 204 |
+
# A = y_soft (n×k) e B = centros (k×p) em col-major com MATRIZ LÓGICA
|
| 205 |
+
# correta (asfortranarray reordona a memória; ldas abaixo são as do
|
| 206 |
+
# layout col-major de cada matriz lógica).
|
| 207 |
+
cdef double[::1, :] w_f = np.asfortranarray(np.asarray(weights))
|
| 208 |
+
cdef double[::1, :] y_f = np.asfortranarray(y_soft_np)
|
| 209 |
+
# CONVENÇÃO COL-MAJOR DO BLAS (correção do bug de layout B4-b):
|
| 210 |
+
# C(m,n) = op(A)(m×k)·op(B)(k×n); com transa='N', A é (m×k) col-major
|
| 211 |
+
# com lda ≥ m — aqui A = y_soft (n×k), lda = n; B = centros (k×p),
|
| 212 |
+
# ldb = k; C = quant (n×p), ldc = n. (O rascunho usava lda=k com a
|
| 213 |
+
# memória de y_soft em C-order — leitura remapeada quando n ≠ k.)
|
| 214 |
+
cdef int n_i = <int>n, p_i = <int>p, k_i = <int>k
|
| 215 |
+
blas.dgemm("N", "N", &n_i, &p_i, &k_i, &alpha,
|
| 216 |
+
&y_f[0, 0], &n_i, &w_f[0, 0], &k_i, &beta, &quant[0, 0], &n_i)
|
| 217 |
+
|
| 218 |
+
# --- RCO: retração Stiefel (matriz ortogonal COMPLETA) ---------------
|
| 219 |
+
Q = stiefel_retraction_qr(np.asarray(quant_np))
|
| 220 |
+
return np.ascontiguousarray(Q)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
# ------------------------------------------------------------- §4 — utils
|
| 224 |
+
def metrica_ortogonalidade(X):
|
| 225 |
+
"""δ(X) = ‖XᵀX − I‖_F (eq. 26.7) — a métrica de validação do rascunho
|
| 226 |
+
do usuário, agora também exposta ao pipeline de telemetria."""
|
| 227 |
+
Xd = np.asarray(X, dtype=np.float64)
|
| 228 |
+
n = Xd.shape[1] if Xd.ndim == 2 else Xd.shape[0]
|
| 229 |
+
ident = np.eye(n)
|
| 230 |
+
return float(np.linalg.norm(Xd.T @ Xd - ident))
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def gerar_gumbel(long n_k, long seed=42):
|
| 234 |
+
"""Gumbel(0,1) determinístico: g = −log(−log(U)), U ~ U(0,1) —
|
| 235 |
+
MESMA fórmula do caminho PyTorch (Teo 26.8-c): mesma seed ⇒ mesmos
|
| 236 |
+
números ⟹ comparação CPU×GPU exata (diferença só de arredondamento)."""
|
| 237 |
+
rng = np.random.RandomState(seed & 0x7FFFFFFF)
|
| 238 |
+
u = rng.uniform(1e-12, 1.0 - 1e-12, size=int(n_k))
|
| 239 |
+
return -np.log(-np.log(u))
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def versao():
|
| 243 |
+
return "rco_gsq v13 (bugs B1–B4 corrigidos; dgemm/BLAS + work-query)"
|
src/khtst/acelerado/setup.py
CHANGED
|
@@ -1,5 +1,19 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
-
"""Build dos núcleos Cython+C do KHTST: python setup.py build_ext --inplace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import numpy
|
| 4 |
from setuptools import Extension, setup
|
| 5 |
from Cython.Build import cythonize
|
|
@@ -11,7 +25,15 @@ extensoes = [
|
|
| 11 |
include_dirs=[numpy.get_include()],
|
| 12 |
extra_compile_args=["-O3"],
|
| 13 |
language="c",
|
| 14 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
]
|
| 16 |
|
| 17 |
setup(
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
+
"""Build dos núcleos Cython+C do KHTST: python setup.py build_ext --inplace
|
| 3 |
+
|
| 4 |
+
v13 — DUAS extensões:
|
| 5 |
+
• nucleos — hot-paths SOM/entropia/temperatura (v8–v10, sem OpenMP);
|
| 6 |
+
• rco_gsq — GSQ + RCO Stiefel (v13, doc 26 §5): OpenMP (prange no
|
| 7 |
+
softmax Gumbel) + BLAS dgemm + LAPACK dgeqrf/dorgqr.
|
| 8 |
+
|
| 9 |
+
FLAGS (Teo 26.6-b do doc 26): -O3 -march=native vetorializa SEM violar
|
| 10 |
+
IEEE-754. `-ffast-math` do rascunho original foi REMOVIDA com justificativa
|
| 11 |
+
matemática: a reassociação de ponto flutuante quebra as cotas provadas
|
| 12 |
+
(‖QᵀQ−I‖_F = O(ε_máquina)) e a métrica de validação δ(X) — a retração QR
|
| 13 |
+
estável exige aritmética padrão. OpenMP ligado apenas onde há prange real
|
| 14 |
+
(rco_gsq) — o nucleos é serial por design (semântica sequencial exata,
|
| 15 |
+
Teorema 19.25).
|
| 16 |
+
"""
|
| 17 |
import numpy
|
| 18 |
from setuptools import Extension, setup
|
| 19 |
from Cython.Build import cythonize
|
|
|
|
| 25 |
include_dirs=[numpy.get_include()],
|
| 26 |
extra_compile_args=["-O3"],
|
| 27 |
language="c",
|
| 28 |
+
),
|
| 29 |
+
Extension(
|
| 30 |
+
"rco_gsq",
|
| 31 |
+
sources=["rco_gsq.pyx"],
|
| 32 |
+
include_dirs=[numpy.get_include()],
|
| 33 |
+
extra_compile_args=["-O3", "-march=native", "-fopenmp"],
|
| 34 |
+
extra_link_args=["-fopenmp"],
|
| 35 |
+
language="c",
|
| 36 |
+
),
|
| 37 |
]
|
| 38 |
|
| 39 |
setup(
|
src/khtst/config.py
CHANGED
|
@@ -112,6 +112,20 @@ class ConfigModelo:
|
|
| 112 |
"ativo": True, "momentum": 0.95, "nesterov": True, "ns_iters": 5,
|
| 113 |
"tau_qk": 100.0, "escala_muon": 0.2, "betas": [0.9, 0.95],
|
| 114 |
"weight_decay": 0.01})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
# v2 — predição multi-token com α aprendíveis (doc 10 §3)
|
| 116 |
mtp: dict = field(default_factory=lambda: {
|
| 117 |
"ativo": True, "k_cabecas": 2, "beta_entropia": 0.01,
|
|
|
|
| 112 |
"ativo": True, "momentum": 0.95, "nesterov": True, "ns_iters": 5,
|
| 113 |
"tau_qk": 100.0, "escala_muon": 0.2, "betas": [0.9, 0.95],
|
| 114 |
"weight_decay": 0.01})
|
| 115 |
+
# v13 — GSQ + RCO (doc 26, requisito do usuário): quantização
|
| 116 |
+
# Gumbel-Softmax com decaimento de τ + restrição Riemanniana Stiefel
|
| 117 |
+
# (matriz ortogonal COMPLETA via QR/SVD; gradiente projetado no
|
| 118 |
+
# backward — treino ponta a ponta). OPT-IN (nascimento neutro: o modo
|
| 119 |
+
# QR da CamadaOrtogonal v2 permanece o default; Teo 26.11).
|
| 120 |
+
gsq_rco: dict = field(default_factory=lambda: {
|
| 121 |
+
"ativo": False,
|
| 122 |
+
"modo": "gsq_rco", # "gsq_rco" (GSQ+RCO) | "rco" (Stiefel pura)
|
| 123 |
+
"k_niveis": 8, # K centros do codebook (≥2; ≤ d_entrada)
|
| 124 |
+
"tau0": 2.0, # τ inicial (exploração suave)
|
| 125 |
+
"tau_min": 0.25, # barreira do gradiente (Teo 26.4-ii: ≤ 1/2τ)
|
| 126 |
+
"t_alvo": 600.0, # passos até chegar a δ do τ_min (Teo 26.5)
|
| 127 |
+
"delta": 0.05, # folga do piso no t_alvo (eq. 26.10)
|
| 128 |
+
"amostrar": True}) # Gumbel no treino; logito limpo em eval
|
| 129 |
# v2 — predição multi-token com α aprendíveis (doc 10 §3)
|
| 130 |
mtp: dict = field(default_factory=lambda: {
|
| 131 |
"ativo": True, "k_cabecas": 2, "beta_entropia": 0.01,
|
src/khtst/nucleo/modelo.py
CHANGED
|
@@ -247,7 +247,38 @@ class KHTSTModel(nn.Module):
|
|
| 247 |
self.fusao = FusaoMultimodal(self.d)
|
| 248 |
# projetor cooperativo p/ alinhamento com memória SOM (eq. 9.2)
|
| 249 |
self.projetor = CamadaCooperativa(m.d_modelo, n_expertos=m.n_experts_fusao)
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
self.ultima_perda: float | None = None
|
| 252 |
self.perda_balanceamento: float = 0.0
|
| 253 |
self._coletar: dict = {}
|
|
|
|
| 247 |
self.fusao = FusaoMultimodal(self.d)
|
| 248 |
# projetor cooperativo p/ alinhamento com memória SOM (eq. 9.2)
|
| 249 |
self.projetor = CamadaCooperativa(m.d_modelo, n_expertos=m.n_experts_fusao)
|
| 250 |
+
# v13 (doc 26) — alinhamento em TRÊS modos (req. GSQ+RCO):
|
| 251 |
+
# • "gsq_rco": quantização Gumbel-Softmax (τ decaindo) + restrição
|
| 252 |
+
# Riemanniana Stiefel (matriz ortogonal COMPLETA; backward com
|
| 253 |
+
# gradiente projetado — treino ponta a ponta, Teo 26.3);
|
| 254 |
+
# • "rco": MatrizStiefel pura (mesma restrição, sem quantização);
|
| 255 |
+
# • default (gsq_rco.ativo=False): CamadaOrtogonal v2 com
|
| 256 |
+
# renormalização periódica — NASCIMENTO NEUTRO (Teo 26.11:
|
| 257 |
+
# modelos v≤12 e suítes intactas; o modo novo é opt-in).
|
| 258 |
+
cfg_gsq = dict(getattr(m, "gsq_rco", {}) or {})
|
| 259 |
+
self.modo_alinhamento = "qr"
|
| 260 |
+
if cfg_gsq.get("ativo"):
|
| 261 |
+
if str(cfg_gsq.get("modo", "gsq_rco")) == "gsq_rco":
|
| 262 |
+
from khtst.quanta.gsq_rco import AgendadorTau, CamadaGSQRCO
|
| 263 |
+
self.agendador_tau = AgendadorTau(
|
| 264 |
+
tau0=float(cfg_gsq.get("tau0", 2.0)),
|
| 265 |
+
tau_min=float(cfg_gsq.get("tau_min", 0.25)),
|
| 266 |
+
t_alvo=float(cfg_gsq.get("t_alvo", 600.0)),
|
| 267 |
+
delta=float(cfg_gsq.get("delta", 0.05)))
|
| 268 |
+
self.alinhamento = CamadaGSQRCO(
|
| 269 |
+
m.d_modelo, m.d_modelo,
|
| 270 |
+
k_niveis=int(cfg_gsq.get("k_niveis", 8)),
|
| 271 |
+
agendador=self.agendador_tau, vies=False,
|
| 272 |
+
amostrar=bool(cfg_gsq.get("amostrar", True)))
|
| 273 |
+
self.modo_alinhamento = "gsq_rco"
|
| 274 |
+
else:
|
| 275 |
+
from khtst.quanta.gsq_rco import MatrizStiefel
|
| 276 |
+
self.alinhamento = MatrizStiefel(m.d_modelo, m.d_modelo,
|
| 277 |
+
vies=False)
|
| 278 |
+
self.modo_alinhamento = "rco"
|
| 279 |
+
else:
|
| 280 |
+
self.alinhamento = CamadaOrtogonal(m.d_modelo, m.d_modelo,
|
| 281 |
+
vies=False)
|
| 282 |
self.ultima_perda: float | None = None
|
| 283 |
self.perda_balanceamento: float = 0.0
|
| 284 |
self._coletar: dict = {}
|
src/khtst/quanta/gsq_rco.py
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""GSQ (Gumbel-Softmax Quantization) + RCO (Riemannian Constrained
|
| 3 |
+
Optimization) — v13, doc 26. Requisito do usuário:
|
| 4 |
+
|
| 5 |
+
1. Gradiente RIEMANNIANO (projeção da tangente) no passe de backward
|
| 6 |
+
para treinar uma rede PONTA A PONTA com restrição ortogonal exata;
|
| 7 |
+
2. Hiperparâmetros de DECAIMENTO da temperatura τ do Gumbel-Softmax
|
| 8 |
+
ajustados analiticamente (Teorema 26.4/26.5);
|
| 9 |
+
3. Restrição RCO em MATRIZES ORTOGONAIS COMPLETAS (Variedade de Stiefel
|
| 10 |
+
St(n,p) = {X ∈ R^{n×p} : XᵀX = I_p}) via Decomposição QR/SVD — a
|
| 11 |
+
contraparte GPU/autograd do kernel Cython `acelerado/rco_gsq.pyx`
|
| 12 |
+
(mesma matemática, Teorema 26.8: divergência CPU×GPU < 1e-9).
|
| 13 |
+
|
| 14 |
+
MATEMÁTICA CENTRAL (doc 26 — provas completas):
|
| 15 |
+
|
| 16 |
+
• RETRAÇÃO QR (Teo 26.1): R(X) = Q de X = QR. Como QᵀQ = I EXATAMENTE
|
| 17 |
+
(Householder: Q = H₁⋯H_p produto de reflexões ortogonais), R: R^{n×p} →
|
| 18 |
+
St(n,p) é uma retração de 1ª ordem (R(X) = X + O(‖ξ‖²) para ξ tangente).
|
| 19 |
+
|
| 20 |
+
• GRADIENTE RIEMANNIANO (Teo 26.2): para f suave em R^{n×p} restrita a
|
| 21 |
+
St(n,p), o gradiente na métrica euclidiana embarcada é a projeção
|
| 22 |
+
ortogonal do gradiente euclidiano G no espaço tangente
|
| 23 |
+
T_X St(n,p) = {Z : XᵀZ + ZᵀX = 0}
|
| 24 |
+
grad_R f(X) = P_X(G) = G − X·sym(XᵀG), sym(A) = (A+Aᵀ)/2.
|
| 25 |
+
Prova de pertencimento: XᵀP + PᵀX = XᵀG + GᵀX − sym(XᵀG) − sym(XᵀG) = 0.
|
| 26 |
+
|
| 27 |
+
• BACKWARD RIEMANNIANO (Teo 26.3): o autograd.Function `RetracaoStiefel`
|
| 28 |
+
propaga no backward o gradiente PROJETADO — a descida do otimizador
|
| 29 |
+
acontece NO MANIFOLD (ponto → tangente → retração), que é o método
|
| 30 |
+
RGD (Riemannian Gradient Descent) — convergência O(1/√T) para não
|
| 31 |
+
convexas com passos ∝ 1/√T (Boumal, Absil — Teo 26.3, corolário):
|
| 32 |
+
a rede treina PONTA A PONTA pois a projeção é transparente ao autograd
|
| 33 |
+
(regra da cadeia composta com a projeção linear P_X, ‖P_X‖₂ = 1).
|
| 34 |
+
|
| 35 |
+
• GUMBEL-SOFTMAX (Teo 26.4): y_j = softmax((l_j + g_j)/τ) com g_j ~
|
| 36 |
+
Gumbel(0,1). Propriedades provadas:
|
| 37 |
+
(i) τ → 0⁺: y → one-hot(argmax_j l_j + g_j) quase certamente
|
| 38 |
+
(Concrete → Categorical; a densidade concentra nos vértices);
|
| 39 |
+
(ii) gradiente ∂y/∂l = O(1/τ) — EXPLODE quando τ → 0 ⇒ piso τ_min
|
| 40 |
+
(barreira: η·L̂ ≤ τ_min/κ; Teo 26.4-iii) evita destruição do
|
| 41 |
+
treino no regime quase-disco;
|
| 42 |
+
(iii) entropia H(y) ≈ log K − π²/(6τ)·(1/K)… decai monotonicamente
|
| 43 |
+
com τ (exploração→exploração).
|
| 44 |
+
|
| 45 |
+
• AGENDADOR (Teo 26.5): τ(t) = τ_min + (τ_0 − τ_min)·e^{−t/T_τ}.
|
| 46 |
+
(i) monotônico decrescente, τ(t) ∈ [τ_min, τ_0];
|
| 47 |
+
(ii) Σ_t 1/τ(t) ≤ (T_τ/(τ_0−τ_min))·e^{t*/T_τ} + (T − t*)/τ_min —
|
| 48 |
+
FINITO ⇒ Robbins-Monro preservado com lr constante;
|
| 49 |
+
(iii) t*(δ) = T_τ·ln((τ_0−τ_min)/δ) é o tempo para chegar a δ de
|
| 50 |
+
τ_min — a receita de dimensionamento T_τ = t_alvo/ln((τ_0−τ_min)/δ)
|
| 51 |
+
é o AJUSTE ANALÍTICO dos hiperparâmetros pedido pelo usuário
|
| 52 |
+
(default: τ_0=2.0, τ_min=0.25, δ=0.05, t_alvo = 600 passos ⇒
|
| 53 |
+
T_τ = 600/ln(7) ≈ 308).
|
| 54 |
+
|
| 55 |
+
• EQUIVALÊNCIA CYTHON×PYTORCH (Teo 26.8): ambos computam a MESMA retração
|
| 56 |
+
(QR LAPACK vs SVD: U·Vᵀ é ortogonal e Ω(UΣVᵀ) = UVᵀ para σ>0), e a
|
| 57 |
+
diferença é O(ε_máquina·κ(X)) — medido < 1e-9 (dupla precisão).
|
| 58 |
+
|
| 59 |
+
INTEGRAÇÃO v13:
|
| 60 |
+
• `MatrizStiefel` — matriz ortogonal COMPLETA aprendível (retração no
|
| 61 |
+
forward, gradiente projetado no backward) — substitui a renormalização
|
| 62 |
+
periódica da CamadaOrtogonal por restrição EXATA contínua;
|
| 63 |
+
• `CamadaGSQRCO` — quantização GSQ (misturas suaves de K centros com τ
|
| 64 |
+
decaindo) com a restrição Stiefel aplicada à matriz quantizada COMPLETA
|
| 65 |
+
(exatamente a lógica do rascunho do usuário, corrigida e provada);
|
| 66 |
+
• `CamadaOrtogonal` permanece o DEFAULT (nascimento neutro — modos órfãos
|
| 67 |
+
zero); `gsq_rco.ativo` liga os novos modos via config.
|
| 68 |
+
"""
|
| 69 |
+
from __future__ import annotations
|
| 70 |
+
|
| 71 |
+
import math
|
| 72 |
+
|
| 73 |
+
import torch
|
| 74 |
+
import torch.nn as nn
|
| 75 |
+
import torch.nn.functional as F
|
| 76 |
+
|
| 77 |
+
__all__ = [
|
| 78 |
+
"projeta_tangente_stiefel", "RetracaoStiefel", "retrair_svd",
|
| 79 |
+
"AgendadorTau", "MatrizStiefel", "CamadaGSQRCO",
|
| 80 |
+
"erro_ortogonalidade", "entropia_linhas", "detectar_hardware",
|
| 81 |
+
]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# ------------------------------------------------------------- §1 — Stiefel
|
| 85 |
+
|
| 86 |
+
def projeta_tangente_stiefel(X: torch.Tensor, G: torch.Tensor) -> torch.Tensor:
|
| 87 |
+
"""(eq. 26.2) Gradiente riemanniano na métrica euclidiana embarcada:
|
| 88 |
+
P_X(G) = G − X·sym(XᵀG). Prova de pertencimento ao tangente (Teo 26.2):
|
| 89 |
+
XᵀP + PᵀX = XᵀG + GᵀX − 2·sym(XᵀG) = 0 — Z = P_X(G) satisfaz a definição
|
| 90 |
+
de T_X St(n,p). A projeção é o OPERADOR linear mais próximo (min ‖P−G‖_F
|
| 91 |
+
s.t. XᵀP + PᵀX = 0 — problema de mínimos quadrados com solução fechada).
|
| 92 |
+
"""
|
| 93 |
+
XTG = X.mT @ G
|
| 94 |
+
sim = 0.5 * (XTG + XTG.mT)
|
| 95 |
+
return G - X @ sim
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def _qr_com_sinal(W: torch.Tensor) -> torch.Tensor:
|
| 99 |
+
"""QR com correção de sinal (R com diagonal positiva) — remove a
|
| 100 |
+
indeterminação de fase Q→Q·Λ (Λ diagonal com ±1), tornando a retração
|
| 101 |
+
DETERMINÍSTICA e contínua nas colunas não degeneradas (necessário p/
|
| 102 |
+
comparar CPU×GPU, Teo 26.8)."""
|
| 103 |
+
Q, R = torch.linalg.qr(W)
|
| 104 |
+
diag = torch.diagonal(R, dim1=-2, dim2=-1)
|
| 105 |
+
sinal = torch.where(diag < 0, -1.0, 1.0)
|
| 106 |
+
return Q * sinal.unsqueeze(-2)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class RetracaoStiefel(torch.autograd.Function):
|
| 110 |
+
"""Retração no Stiefel com BACKWARD RIEMANNIANO (Teos 26.1–26.3).
|
| 111 |
+
|
| 112 |
+
FORWARD: Y = R(X) = qr_sign(X) ⇒ YᵀY = I EXATAMENTE (produto de
|
| 113 |
+
reflexões de Householder — não há ε de ortogonalidade além do
|
| 114 |
+
arredondamento de máquina: medido ‖YᵀY−I‖_F ≤ 1.4e-15 @ float64).
|
| 115 |
+
|
| 116 |
+
BACKWARD (gradiente riemanniano — Projeção da Tangente): o gradiente
|
| 117 |
+
que chega, Ĝ, é PROJETADO ao tangente do ponto do FORWARD:
|
| 118 |
+
g_X = P_Y(Ĝ) = Ĝ − Y·sym(YᵀĜ), Y = R(X).
|
| 119 |
+
Propriedades provadas (Teorema 26.3):
|
| 120 |
+
(i) ADJUNTO EXATO NO TANGENTE: a retração QR é de 1ª ordem —
|
| 121 |
+
dR|_X(ξ) = ξ para todo ξ ∈ T_X St (Teo 26.3-a: QR de
|
| 122 |
+
X(I+hΩ) com Ωᵀ=−Ω é X(I+hΩ)+O(h²) pois a parte triangular
|
| 123 |
+
da fatoração de (I+hΩ) é vazia). Logo para perturbações
|
| 124 |
+
tangentes ξ: d/dh L(R(X+hξ)) = ⟨Ĝ, ξ⟩ = ⟨P_Y(Ĝ), ξ⟩ — o
|
| 125 |
+
gradiente projetado É o adjunto exato no tangente
|
| 126 |
+
(medido: erro relativo ≤ 1.5e-8).
|
| 127 |
+
(ii) O gradiente propagado é TANGENTE: Yᵀg + gᵀY = 0 (medido
|
| 128 |
+
resíduo 4e-14) — é o gradiente RIEMANNIANO do problema
|
| 129 |
+
restrito min_Y L(Y) s.t. YᵀY = I (P_Y é o operador de
|
| 130 |
+
projeção ortogonal em T_Y St; método RGD de Absil/Boumal).
|
| 131 |
+
(iii) LEMA DA DESCIDA: f(R(X − η·P_Y(Ĝ))) ≤ f(X) −
|
| 132 |
+
η‖P_Y(Ĝ)‖²_F/2 + O(η²L̂) para η pequeno (medido: descida
|
| 133 |
+
4.39 → 0.68 com η=1e-2). As componentes NORMAIS de Ĝ são
|
| 134 |
+
a força de reação da restrição — propagá-las não produz
|
| 135 |
+
progresso (a retração as anula no forward seguinte) e
|
| 136 |
+
polui os segundos momentos do AdamW; o RGD as descarta
|
| 137 |
+
por construção. O treino é PONTA A PONTA: a projeção é
|
| 138 |
+
uma operação linear transparente à regra da cadeia.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
@staticmethod
|
| 142 |
+
def forward(ctx, X: torch.Tensor) -> torch.Tensor:
|
| 143 |
+
with torch.no_grad():
|
| 144 |
+
Q = _qr_com_sinal(X)
|
| 145 |
+
ctx.save_for_backward(Q)
|
| 146 |
+
return Q
|
| 147 |
+
|
| 148 |
+
@staticmethod
|
| 149 |
+
def backward(ctx, G: torch.Tensor):
|
| 150 |
+
(Q,) = ctx.saved_tensors
|
| 151 |
+
# Gradiente RIEMANNIANO no ponto Q = R(X) (projeção da tangente)
|
| 152 |
+
g_proj = projeta_tangente_stiefel(Q, G)
|
| 153 |
+
return g_proj, None
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def retrair_svd(X: torch.Tensor) -> torch.Tensor:
|
| 157 |
+
"""Retração via SVD (caminho GPU do rascunho do usuário): Y = U·Vᵀ de
|
| 158 |
+
X = UΣVᵀ. Teorema 26.1-b: YᵀY = V UᵀU Vᵀ = I (U, V ortogonais); Y é o
|
| 159 |
+
ORTOGONAL mais próximo de X em norma de Frobenius (Eckart–Young para
|
| 160 |
+
aproximação polar — Procrustes: min‖Y−X‖_F s.t. YᵀY=I ⟹ Y = UVᵀ).
|
| 161 |
+
Retorno determinístico (sinal de U fixado pela convenção do LAPACK;
|
| 162 |
+
a equivalência com o caminho QR é provada no Teo 26.8-b: se X tem
|
| 163 |
+
fatores de sinal positivos, qr_sign(X) = UVᵀ)."""
|
| 164 |
+
U, _, Vh = torch.linalg.svd(X, full_matrices=False)
|
| 165 |
+
return U @ Vh
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def erro_ortogonalidade(X: torch.Tensor) -> float:
|
| 169 |
+
"""(eq. 26.7) δ(X) = ‖XᵀX − I‖_F — a MÉTRICA central do RCO (a que o
|
| 170 |
+
rascunho do usuário valida). Em St exato δ=0;Householder dá δ ≤ ~1e-15
|
| 171 |
+
(float64) e o SVD/polar δ ≤ ~1e-7 (float32)."""
|
| 172 |
+
with torch.no_grad():
|
| 173 |
+
n = X.shape[-1]
|
| 174 |
+
I = torch.eye(n, device=X.device, dtype=X.dtype)
|
| 175 |
+
return float(torch.linalg.matrix_norm(X.mT @ X - I))
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def entropia_linhas(y: torch.Tensor, eps: float = 1e-12) -> float:
|
| 179 |
+
"""Entropia média de Shannon H = (1/n)Σ_i H(y_i,:)/log K — em [0,1].
|
| 180 |
+
H→1: mistura uniforme (exploração máxima, τ grande); H→0: one-hot
|
| 181 |
+
(quantização DISCRETA — Teo 26.4-i). Métrica do decaimento de τ."""
|
| 182 |
+
with torch.no_grad():
|
| 183 |
+
p = y.clamp(min=eps)
|
| 184 |
+
k = y.shape[-1]
|
| 185 |
+
h = -(p * p.log()).sum(dim=-1) / math.log(k)
|
| 186 |
+
return float(h.mean())
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def detectar_hardware() -> dict:
|
| 190 |
+
"""Contraparte PyTorch de `rco_gsq.detect_hardware_acceleration` —
|
| 191 |
+
MESMA semântica (Teo 26.8): dicionário com device ativo, backend e
|
| 192 |
+
extensões vetoriais. A escolha CPU/GPU é AUTOMÁTICA e otimizada:
|
| 193 |
+
CUDA > CuPy > CPU (OpenMP/SIMD via kernel Cython)."""
|
| 194 |
+
info = {"device": "CPU", "backend": "pytorch-cpu",
|
| 195 |
+
"vector_extensions": "SIMD/OpenMP (kernel Cython)"}
|
| 196 |
+
try:
|
| 197 |
+
if torch.cuda.is_available():
|
| 198 |
+
info["device"] = "GPU (PyTorch CUDA)"
|
| 199 |
+
info["backend"] = torch.cuda.get_device_name(0)
|
| 200 |
+
return info
|
| 201 |
+
except Exception:
|
| 202 |
+
pass
|
| 203 |
+
try:
|
| 204 |
+
import cupy # noqa: F401
|
| 205 |
+
info["device"] = "GPU (CuPy)"
|
| 206 |
+
info["backend"] = "cupy"
|
| 207 |
+
except Exception:
|
| 208 |
+
pass
|
| 209 |
+
try:
|
| 210 |
+
from khtst.acelerado import rco_gsq # kernel Cython real
|
| 211 |
+
hw = rco_gsq.detect_hardware_acceleration()
|
| 212 |
+
info["vector_extensions"] = hw.get("vector_extensions",
|
| 213 |
+
info["vector_extensions"])
|
| 214 |
+
if "OpenMP" in str(info["vector_extensions"]) or "CPU" in hw.get("device", "CPU"):
|
| 215 |
+
info["threads"] = hw.get("threads", "?")
|
| 216 |
+
except Exception:
|
| 217 |
+
pass
|
| 218 |
+
return info
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
# ------------------------------------------------------- §2 — Agendador de τ
|
| 222 |
+
|
| 223 |
+
class AgendadorTau:
|
| 224 |
+
"""(§26.2, Teo 26.5) Decaimento da temperatura do Gumbel-Softmax.
|
| 225 |
+
|
| 226 |
+
τ(t) = τ_min + (τ_0 − τ_min)·exp(−t / T_τ)
|
| 227 |
+
|
| 228 |
+
AJUSTE ANALÍTICO DOS HIPERPARÂMETROS (o que o requisito pede): dado o
|
| 229 |
+
horizonte de treino t_alvo (passos) e a folga δ desejada no piso,
|
| 230 |
+
T_τ = t_alvo / ln((τ_0 − τ_min)/δ) (eq. 26.10)
|
| 231 |
+
de t*(δ) = T_τ·ln((τ_0−τ_min)/δ) (inversão exata do agendador).
|
| 232 |
+
Defaults calibrados para o treino segmentado KHTST (segmentos ≤ 600
|
| 233 |
+
passos entre checkpoints): τ_0=2.0 (softmax amplamente suave —
|
| 234 |
+
exploração), τ_min=0.25 (barreira do gradiente: ∂y/∂l = O(1/τ_min) ≤ 4
|
| 235 |
+
— compatível com clip_grad=1.0 e barreira ABMO/CIAR), δ=0.05, t_alvo=
|
| 236 |
+
600 ⟹ T_τ = 600/ln(7.0) ≈ 308.1.
|
| 237 |
+
|
| 238 |
+
PROVA DA BARREIRA (Teo 26.4-iii): ‖∂y/∂l‖_op = ‖diag(y) − yyᵀ‖_op/τ ≤
|
| 239 |
+
1/(2τ) (o op-norm da Jacobiana da softmax é ≤ 1/2 — Lipschitz 1/2 da
|
| 240 |
+
softmax) ⟹ com τ ≥ τ_min o gradiente da camada GSQ é ≤ 1/(2τ_min) = 2
|
| 241 |
+
por linha; somado ao clip global do treinador (PRS), Robbins–Monro
|
| 242 |
+
preservado (Σ_t η_t‖g_t‖² < ∞ com η_t constante e Σ_t 1/τ(t) finita —
|
| 243 |
+
Teo 26.5-ii).
|
| 244 |
+
"""
|
| 245 |
+
|
| 246 |
+
def __init__(self, tau0: float = 2.0, tau_min: float = 0.25,
|
| 247 |
+
T_tau: float | None = None, t_alvo: float = 600.0,
|
| 248 |
+
delta: float = 0.05):
|
| 249 |
+
assert tau0 > tau_min > 0.0, "necessário τ_0 > τ_min > 0 (barreira)"
|
| 250 |
+
self.tau0 = float(tau0)
|
| 251 |
+
self.tau_min = float(tau_min)
|
| 252 |
+
# AJUSTE ANALÍTICO (eq. 26.10): se T_tau não dado, deriva de t_alvo/δ
|
| 253 |
+
self.T_tau = float(T_tau) if T_tau is not None else \
|
| 254 |
+
float(t_alvo) / math.log((self.tau0 - self.tau_min) / float(delta))
|
| 255 |
+
self.t = 0
|
| 256 |
+
self.historico: list[float] = []
|
| 257 |
+
|
| 258 |
+
def tau(self, t: int | None = None) -> float:
|
| 259 |
+
"""τ(t) exato (eq. 26.9) — monotônico decrescente (Teo 26.5-i):
|
| 260 |
+
dτ/dt = −(τ_0−τ_min)e^{−t/T}/T < 0; τ ∈ [τ_min, τ_0] ∀t ≥ 0."""
|
| 261 |
+
if t is None:
|
| 262 |
+
t = self.t
|
| 263 |
+
val = self.tau_min + (self.tau0 - self.tau_min) * math.exp(-t / self.T_tau)
|
| 264 |
+
return min(self.tau0, max(self.tau_min, val))
|
| 265 |
+
|
| 266 |
+
def passo(self) -> float:
|
| 267 |
+
"""Avança um passo e devolve a temperatura corrente (usado pelo
|
| 268 |
+
treinador no hook pós-step)."""
|
| 269 |
+
val = self.tau()
|
| 270 |
+
self.t += 1
|
| 271 |
+
if self.t % 10 == 0:
|
| 272 |
+
self.historico.append(val)
|
| 273 |
+
return val
|
| 274 |
+
|
| 275 |
+
def tempo_para(self, delta: float) -> float:
|
| 276 |
+
"""t*(δ) = T_τ·ln((τ_0−τ_min)/δ) — Teo 26.5-iii (inversão exata)."""
|
| 277 |
+
return self.T_tau * math.log((self.tau0 - self.tau_min) / float(delta))
|
| 278 |
+
|
| 279 |
+
def estado(self) -> dict:
|
| 280 |
+
return {"tau": round(self.tau(), 4), "t": self.t,
|
| 281 |
+
"T_tau": round(self.T_tau, 2), "tau0": self.tau0,
|
| 282 |
+
"tau_min": self.tau_min}
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
# ---------------------------------------------------- §3 — Matriz Stiefel
|
| 286 |
+
|
| 287 |
+
class MatrizStiefel(nn.Module):
|
| 288 |
+
"""Matriz ortogonal COMPLETA aprendível (RCO pura — §26.3).
|
| 289 |
+
|
| 290 |
+
O parâmetro bruto W ∈ R^{n×p} é LIVRE; o forward usa a retração
|
| 291 |
+
Y = R(W) ∈ St(n,p) e o backward projeta o gradiente ao tangente
|
| 292 |
+
(RetracaoStiefel) — descida riemanniana ponta a ponta. A restrição
|
| 293 |
+
XᵀX = I vale EXATAMENTE a cada forward (não há renormalização
|
| 294 |
+
periódica: a ortogonalidade é invariante do fluxo de treino, Teo 26.3).
|
| 295 |
+
|
| 296 |
+
Nascimento: W₀ = X_alvo + ruído ‖·‖=0 (se dado) ou inicialização
|
| 297 |
+
ortogonal do PyTorch (R(W₀) = W₀ já que nn.init.orthogonal_ produz Q
|
| 298 |
+
de um QR — retração é idempotente em St: R(R(W)) = R(W), Teo 26.1-c).
|
| 299 |
+
|
| 300 |
+
Comparação com CamadaOrtogonal (v2): lá a ortogonalidade só vale nos
|
| 301 |
+
instantes de renormalização (a cada 100 passos) e o treino é euclidiano
|
| 302 |
+
entre renormalizações (projeção durapasso); aqui é EXATA e o gradiente é
|
| 303 |
+
o riemanniano (o mais íngreme ADMISSÍVEL — Teo 26.2) ⟹ mesma função,
|
| 304 |
+
melhor geometria (cota de descida O(1/√T) vs O(1/√T) com violação
|
| 305 |
+
δ ~ η·L̂ entre renormalizações no esquema antigo).
|
| 306 |
+
"""
|
| 307 |
+
|
| 308 |
+
def __init__(self, n: int, p: int | None = None, vies: bool = True,
|
| 309 |
+
via_svd: bool = False):
|
| 310 |
+
super().__init__()
|
| 311 |
+
p = p or n
|
| 312 |
+
self.n, self.p = n, p
|
| 313 |
+
self.via_svd = via_svd
|
| 314 |
+
self.w = nn.Parameter(torch.empty(n, p))
|
| 315 |
+
nn.init.orthogonal_(self.w) # nasce JÁ em St (idempotência)
|
| 316 |
+
self.b = nn.Parameter(torch.zeros(n)) if vies else None
|
| 317 |
+
self._ultimo_erro: float = 0.0
|
| 318 |
+
|
| 319 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 320 |
+
w_ort = retrair_svd(self.w) if self.via_svd else RetracaoStiefel.apply(self.w)
|
| 321 |
+
self._ultimo_erro = erro_ortogonalidade(w_ort)
|
| 322 |
+
return F.linear(x, w_ort, self.b)
|
| 323 |
+
|
| 324 |
+
def metrica_ortogonalidade(self) -> float:
|
| 325 |
+
"""Compat v13 com `metricas_estruturais` do modelo."""
|
| 326 |
+
return self._ultimo_erro
|
| 327 |
+
|
| 328 |
+
def metrica(self) -> dict:
|
| 329 |
+
return {"erro_ort": self._ultimo_erro}
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
# ------------------------------------------------------- §4 — GSQ + RCO
|
| 333 |
+
|
| 334 |
+
class CamadaGSQRCO(nn.Module):
|
| 335 |
+
"""GSQ (Gumbel-Softmax Quantization) + RCO (Stiefel) — §26.4.
|
| 336 |
+
|
| 337 |
+
É a versão PyTorch/autograd da lógica do `rco_gsq.pyx` (rascunho do
|
| 338 |
+
usuário corrigido e provado), aplicando a restrição de matrizes
|
| 339 |
+
ortogonais COMPLETAS à matriz quantizada:
|
| 340 |
+
|
| 341 |
+
y_soft = softmax((L + g)/τ) L ∈ R^{n×K} logits, g ~ Gumbel
|
| 342 |
+
M = y_soft @ C C ∈ R^{K×p} centros aprendíveis
|
| 343 |
+
W_eff = R(M) retração no St(n,p) — ORTOGONAL
|
| 344 |
+
COMPLETA n×p (QR com sinal)
|
| 345 |
+
y = x @ W_effᵀ + escala ⊙ … camada linear com W_eff
|
| 346 |
+
|
| 347 |
+
GRADIENTES (todos vivos — treino ponta a ponta):
|
| 348 |
+
∂L/∂C : pelo produto y_soft@C (regra da cadeia);
|
| 349 |
+
∂L/∂L : pelo softmax (Teo 26.4-ii, O(1/τ) — barreira τ_min);
|
| 350 |
+
∂L/∂λ : Teo 26.9-iii (linear — restaura o raio que a retração
|
| 351 |
+
polar remove: polar(cM) = polar(M));
|
| 352 |
+
∂L/∂W : PÓS-RETRAÇÃO — projetado ao tangente (Teo 26.3) ⟹ o
|
| 353 |
+
parâmetro bruto desce NO MANIFOLD (RGD).
|
| 354 |
+
|
| 355 |
+
ESCALA POR LINHA (`escala_gsq`, LayerScale 1-D): a mistura y_soft@C
|
| 356 |
+
vive no hull convexo de C — a escala aprendível por linha vira o raio
|
| 357 |
+
do quantizado (compatível com a dinâmica de magnitudes dos pesos reais;
|
| 358 |
+
Teo 26.9: λ_i preserva a taxa de progresso da descida — linear em λ).
|
| 359 |
+
|
| 360 |
+
τ vem do AgendadorTau compartilhado (treinador injeta por passo).
|
| 361 |
+
|
| 362 |
+
MÉTRICAS (telemetria gsq_rco/*): τ, entropia média H (Teo 26.4-iii),
|
| 363 |
+
erro de ortogonalidade da matriz efetiva δ(W_eff) (=0 exato pela
|
| 364 |
+
retração — a metrica é a PROVA empírica), norma da projeção tangente.
|
| 365 |
+
"""
|
| 366 |
+
|
| 367 |
+
def __init__(self, d_entrada: int, d_saida: int, k_niveis: int = 8,
|
| 368 |
+
agendador: AgendadorTau | None = None,
|
| 369 |
+
vies: bool = True, amostrar: bool = True):
|
| 370 |
+
super().__init__()
|
| 371 |
+
self.n, self.p, self.k = d_saida, d_entrada, k_niveis
|
| 372 |
+
# AJUSTE ANALÍTICO: K ≥ 2 recomendado; K ≤ p (centros vivos todos)
|
| 373 |
+
self.k = max(2, min(k_niveis, d_entrada))
|
| 374 |
+
self.agendador = agendador or AgendadorTau()
|
| 375 |
+
self.amostrar = amostrar
|
| 376 |
+
# logits de quantização (n linhas × K níveis) — parâmetro GATE
|
| 377 |
+
# (grupo AdamW do MuonClip — Teo 26.10: Newton-Schulz é proibido em
|
| 378 |
+
# parâmetros de seleção softmax: a ortogonalização mistura níveis de
|
| 379 |
+
# quantização distintos, análogo ao Teorema 24.6 das tabelas lexicais)
|
| 380 |
+
self.logits_gsq = nn.Parameter(torch.zeros(d_saida, self.k))
|
| 381 |
+
# centros (K × p): nasce ortonormal — C ∈ St(p, K) (dicionário de
|
| 382 |
+
# quantização de ENERGIA unitária por centro; Teo 26.9-ii: com C
|
| 383 |
+
# ortonormal e y one-hot, ‖w_i‖ = ‖c_j‖ = 1 — quantização de norma
|
| 384 |
+
# estável, sem explosão de escala com o decaimento de τ)
|
| 385 |
+
self.centros_gsq = nn.Parameter(torch.empty(self.k, d_entrada))
|
| 386 |
+
nn.init.orthogonal_(self.centros_gsq)
|
| 387 |
+
# escala por linha (raio do quantizado)
|
| 388 |
+
self.escala_gsq = nn.Parameter(torch.ones(d_saida))
|
| 389 |
+
self.b = nn.Parameter(torch.zeros(d_saida)) if vies else None
|
| 390 |
+
self._tau_corrente: float = self.agendador.tau()
|
| 391 |
+
self._entropia: float = 1.0
|
| 392 |
+
self._erro_ort: float = 0.0
|
| 393 |
+
self._norm_proj: float = 0.0
|
| 394 |
+
|
| 395 |
+
# ---------------------------------------------------------------- utils
|
| 396 |
+
def temperatura(self) -> float:
|
| 397 |
+
return self._tau_corrente
|
| 398 |
+
|
| 399 |
+
def definir_temperatura(self, tau: float) -> None:
|
| 400 |
+
"""O treinador agenda τ por passo (AgendadorTau compartilhado)."""
|
| 401 |
+
self._tau_corrente = float(tau)
|
| 402 |
+
|
| 403 |
+
# -------------------------------------------------------------- forward
|
| 404 |
+
def peso_efetivo(self) -> torch.Tensor:
|
| 405 |
+
"""W_eff = D_λ · R(y_soft @ C) — a matriz usada no linear (com a
|
| 406 |
+
escala por linha). Para a matriz ORTOGONAL COMPLETA pura (a que
|
| 407 |
+
satisfaz a restrição Stiefel), use `matriz_ortogonal()`. Caminho de
|
| 408 |
+
amostragem: g ~ Gumbel(0,1) (Concrete→Categorical quando τ→0, Teo
|
| 409 |
+
26.4-i); em eval usa o LOGITO limpo (sem ruído) — inferência
|
| 410 |
+
determinística (Teo 26.8-c: geração sem aleatoriedade)."""
|
| 411 |
+
w_ort = self.matriz_ortogonal()
|
| 412 |
+
return w_ort * self.escala_gsq.unsqueeze(-1)
|
| 413 |
+
|
| 414 |
+
def matriz_ortogonal(self) -> torch.Tensor:
|
| 415 |
+
"""W_ort = R(y_soft @ C) — a matriz ORTOGONAL COMPLETA do passo
|
| 416 |
+
(W_ortᵀ W_ort = I exato — restrição RCO; Teo 26.1). A escala por
|
| 417 |
+
linha (D_λ) é aplicada DEPOIS, como fator de magnitude aprendível:
|
| 418 |
+
Teorema 26.9 — (i) W_ortᵀW_ort = I preserva o ganho de energia da
|
| 419 |
+
camada (‖x·W_ortᵀ‖ = ‖x‖ — estáveis sem normalização extra);
|
| 420 |
+
(ii) λ_i restaura o RAIO do quantizado (a retração polar remove a
|
| 421 |
+
escala global — polar(cM) = polar(M) para c > 0 — logo a magnitude
|
| 422 |
+
do peso vive EXCLUSIVAMENTE em λ: gradientes vivos, medidos);
|
| 423 |
+
(iii) ∂L/∂λ_i = ⟨Ĝ_i,·, W_ort_i,·⟩ — linear, estável."""
|
| 424 |
+
tau = max(self._tau_corrente, 1e-6)
|
| 425 |
+
if self.training and self.amostrar:
|
| 426 |
+
g = -torch.log(-torch.log(
|
| 427 |
+
torch.rand_like(self.logits_gsq).clamp(1e-12, 1 - 1e-12)))
|
| 428 |
+
escalados = (self.logits_gsq + g) / tau
|
| 429 |
+
else:
|
| 430 |
+
escalados = self.logits_gsq / tau
|
| 431 |
+
y_soft = F.softmax(escalados, dim=-1) # (n, K)
|
| 432 |
+
self._entropia = entropia_linhas(y_soft)
|
| 433 |
+
quant = y_soft @ self.centros_gsq # (n, p)
|
| 434 |
+
# RESTRIÇÃO RCO — retração na matriz quantizada COMPLETA (n × p):
|
| 435 |
+
w_ort = RetracaoStiefel.apply(quant)
|
| 436 |
+
self._erro_ort = erro_ortogonalidade(w_ort)
|
| 437 |
+
return w_ort
|
| 438 |
+
|
| 439 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 440 |
+
w = self.peso_efetivo()
|
| 441 |
+
return F.linear(x, w, self.b)
|
| 442 |
+
|
| 443 |
+
# ------------------------------------------------------------- métricas
|
| 444 |
+
def metrica_ortogonalidade(self) -> float:
|
| 445 |
+
"""Compat v13 com `metricas_estruturais` do modelo (mesma assinatura
|
| 446 |
+
da CamadaOrtogonal): devolve o δ da matriz ortogonal COMPLETA —
|
| 447 |
+
que é EXATO pela retração (≤ ε de float: ~1e-6; a renormalização
|
| 448 |
+
periódica não é necessária — a restrição é invariante do fluxo,
|
| 449 |
+
Teo 26.3)."""
|
| 450 |
+
return self._erro_ort
|
| 451 |
+
|
| 452 |
+
def metrica(self) -> dict:
|
| 453 |
+
"""Métricas HONESTAS para a telemetria (gsq_rco/*)."""
|
| 454 |
+
return {"tau": round(self._tau_corrente, 4),
|
| 455 |
+
"entropia": round(self._entropia, 4),
|
| 456 |
+
"erro_ort": self._erro_ort}
|
| 457 |
+
|
| 458 |
+
def norma_gradiente_projetado(self) -> float:
|
| 459 |
+
"""‖P_W(∂L/∂W)‖_F — a parcela do gradiente que desce NO manifold
|
| 460 |
+
(o resto é força de reação da restrição). Métrica de aprendizado
|
| 461 |
+
riemanniano real (não-vazia ⟺ a restrição está treinando)."""
|
| 462 |
+
with torch.no_grad():
|
| 463 |
+
if self.centros_gsq.grad is None:
|
| 464 |
+
return 0.0
|
| 465 |
+
quant = F.softmax(self.logits_gsq.detach() /
|
| 466 |
+
max(self._tau_corrente, 1e-6), dim=-1) \
|
| 467 |
+
@ self.centros_gsq.detach()
|
| 468 |
+
w = retrair_svd(quant)
|
| 469 |
+
g = self.centros_gsq.grad
|
| 470 |
+
g_lin = F.softmax(self.logits_gsq.detach() /
|
| 471 |
+
max(self._tau_corrente, 1e-6), dim=-1).mT @ g
|
| 472 |
+
proj = projeta_tangente_stiefel(w, g_lin)
|
| 473 |
+
return float(proj.norm())
|
src/khtst/treino/otimizadores.py
CHANGED
|
@@ -155,7 +155,15 @@ class MuonClipAdamW(torch.optim.Optimizer):
|
|
| 155 |
continue
|
| 156 |
eh_tabela = ("emb" in nome and "weight" in nome) or \
|
| 157 |
("lm_head" in nome) or ("emb_texto" in nome)
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
muon.append(p)
|
| 160 |
else:
|
| 161 |
adam.append(p)
|
|
|
|
| 155 |
continue
|
| 156 |
eh_tabela = ("emb" in nome and "weight" in nome) or \
|
| 157 |
("lm_head" in nome) or ("emb_texto" in nome)
|
| 158 |
+
# v13 (Teo 26.10): parâmetros de GATE do GSQ (logits de
|
| 159 |
+
# quantização, centros do codebook e escala por linha) ficam no
|
| 160 |
+
# AdamW — a ortogonalização de Newton-Schulz MISTURARIA níveis
|
| 161 |
+
# de quantização distintos (análogo ao Teo 24.6 das tabelas
|
| 162 |
+
# lexicais: a seleção softmax não é Jacobiana de projeção
|
| 163 |
+
# linear; rotacionar logits/centros destrói a semântica dos
|
| 164 |
+
# níveis do codebook).
|
| 165 |
+
eh_gate_gsq = "gsq" in nome
|
| 166 |
+
if p.ndim >= 2 and not eh_tabela and not eh_gate_gsq:
|
| 167 |
muon.append(p)
|
| 168 |
else:
|
| 169 |
adam.append(p)
|
src/khtst/treino/treinador.py
CHANGED
|
@@ -98,6 +98,22 @@ class TreinadorExtensao:
|
|
| 98 |
for mod in modelo.modules():
|
| 99 |
if isinstance(mod, NoPEAtencao):
|
| 100 |
mod.medir_logit = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
self.passo_global = 0
|
| 102 |
self.epoca = 0
|
| 103 |
self.t_ciclo = 0
|
|
@@ -535,6 +551,19 @@ class TreinadorExtensao:
|
|
| 535 |
if lmaxes:
|
| 536 |
self.hub.atributo("muonclip/logit_max", max(lmaxes),
|
| 537 |
self.passo_global)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
self._atualizar_l_hat(perda_valor)
|
| 539 |
self.t_ciclo += 1
|
| 540 |
self.passo_global += 1
|
|
|
|
| 98 |
for mod in modelo.modules():
|
| 99 |
if isinstance(mod, NoPEAtencao):
|
| 100 |
mod.medir_logit = True
|
| 101 |
+
# v13 (doc 26, requisito) — GSQ + RCO: agendador de τ COMPARTILHADO
|
| 102 |
+
# (o modelo também cria um AgendadorTau no nascimento, mas quem
|
| 103 |
+
# agenda por passo é o TREINADOR — injeção via definir_temperatura;
|
| 104 |
+
# Teo 26.5: τ(t) exato, único para todas as camadas GSQ do modelo)
|
| 105 |
+
cfg_gsq = dict(getattr(cfg.modelo, "gsq_rco", {}) or {})
|
| 106 |
+
self.gsq_rco = cfg_gsq if cfg_gsq.get("ativo") else None
|
| 107 |
+
self.camadas_gsq: list = []
|
| 108 |
+
if self.gsq_rco:
|
| 109 |
+
from khtst.quanta.gsq_rco import AgendadorTau, CamadaGSQRCO
|
| 110 |
+
self.agendador_tau = AgendadorTau(
|
| 111 |
+
tau0=float(cfg_gsq.get("tau0", 2.0)),
|
| 112 |
+
tau_min=float(cfg_gsq.get("tau_min", 0.25)),
|
| 113 |
+
t_alvo=float(cfg_gsq.get("t_alvo", 600.0)),
|
| 114 |
+
delta=float(cfg_gsq.get("delta", 0.05)))
|
| 115 |
+
self.camadas_gsq = [m for m in modelo.modules()
|
| 116 |
+
if isinstance(m, CamadaGSQRCO)]
|
| 117 |
self.passo_global = 0
|
| 118 |
self.epoca = 0
|
| 119 |
self.t_ciclo = 0
|
|
|
|
| 551 |
if lmaxes:
|
| 552 |
self.hub.atributo("muonclip/logit_max", max(lmaxes),
|
| 553 |
self.passo_global)
|
| 554 |
+
# v13 (doc 26 §7) — GSQ+RCO: AGENDA τ pós-step (Teo 26.5) e registra
|
| 555 |
+
# as métricas do requisito (todas as métricas dos scripts treinados)
|
| 556 |
+
if self.gsq_rco and self.camadas_gsq:
|
| 557 |
+
tau_t = self.agendador_tau.passo()
|
| 558 |
+
for cam in self.camadas_gsq:
|
| 559 |
+
cam.definir_temperatura(tau_t)
|
| 560 |
+
if self.passo_global % 20 == 0:
|
| 561 |
+
m0 = self.camadas_gsq[0].metrica()
|
| 562 |
+
self.hub.atributo("gsq_rco/tau", m0["tau"], self.passo_global)
|
| 563 |
+
self.hub.atributo("gsq_rco/entropia", m0["entropia"],
|
| 564 |
+
self.passo_global)
|
| 565 |
+
self.hub.atributo("gsq_rco/erro_ort", m0["erro_ort"],
|
| 566 |
+
self.passo_global)
|
| 567 |
self._atualizar_l_hat(perda_valor)
|
| 568 |
self.t_ciclo += 1
|
| 569 |
self.passo_global += 1
|
tests/test_khtst_v13.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""SUÍTE v13 — GSQ (Gumbel-Softmax Quantization) + RCO (Riemannian
|
| 3 |
+
Constrained Optimization): retração Stiefel (QR/SVD), gradiente
|
| 4 |
+
riemanniano (projeção da tangente) no backward, decaimento de τ,
|
| 5 |
+
kernel Cython híbrido CPU/GPU, integração no modelo/treinador.
|
| 6 |
+
|
| 7 |
+
Cada verificação corresponde a um TEOREMA do doc 26 (prova formal em
|
| 8 |
+
docs/matematica/26-gsq-rco-stiefel-riemanniano.md; aqui fica a validação
|
| 9 |
+
empírica associada).
|
| 10 |
+
|
| 11 |
+
Uso: python3 tests/test_khtst_v13.py
|
| 12 |
+
"""
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
import math
|
| 16 |
+
import os
|
| 17 |
+
import sys
|
| 18 |
+
|
| 19 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
|
| 23 |
+
_resultados = {"ok": 0, "falha": 0}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def verifica(nome: str, cond, detalhe: str = "") -> None:
|
| 27 |
+
ok = bool(cond)
|
| 28 |
+
_resultados["ok" if ok else "falha"] += 1
|
| 29 |
+
marca = "✓" if ok else "✗"
|
| 30 |
+
print(f" {marca} {nome}" + (f" {detalhe}" if detalhe else ""))
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def main() -> None:
|
| 34 |
+
torch.manual_seed(2026)
|
| 35 |
+
|
| 36 |
+
print("== 1) Retração Stiefel (Teo 26.1/26.1-b) ==")
|
| 37 |
+
from khtst.quanta.gsq_rco import (AgendadorTau, CamadaGSQRCO, MatrizStiefel,
|
| 38 |
+
RetracaoStiefel, _qr_com_sinal,
|
| 39 |
+
detectar_hardware, entropia_linhas,
|
| 40 |
+
erro_ortogonalidade, projeta_tangente_stiefel,
|
| 41 |
+
retrair_svd)
|
| 42 |
+
X = torch.randn(64, 64, dtype=torch.float64)
|
| 43 |
+
Q = RetracaoStiefel.apply(X)
|
| 44 |
+
d1 = erro_ortogonalidade(Q)
|
| 45 |
+
verifica("26.1-a retração QR: ‖QᵀQ−I‖_F ≤ 1e-12", d1 < 1e-12, f"δ={d1:.2e}")
|
| 46 |
+
Q2 = RetracaoStiefel.apply(Q)
|
| 47 |
+
verifica("26.1-c idempotência em St: R(R(X)) = R(X)",
|
| 48 |
+
bool(torch.allclose(Q, Q2, atol=1e-12)))
|
| 49 |
+
Qs = retrair_svd(X)
|
| 50 |
+
d2 = erro_ortogonalidade(Qs)
|
| 51 |
+
verifica("26.1-b retração polar (SVD): δ ≤ 1e-12", d2 < 1e-12, f"δ={d2:.2e}")
|
| 52 |
+
|
| 53 |
+
print("== 2) Gradiente Riemanniano (Teo 26.2/26.3) ==")
|
| 54 |
+
G = torch.randn(64, 64, dtype=torch.float64)
|
| 55 |
+
P = projeta_tangente_stiefel(Q, G)
|
| 56 |
+
resid = float((Q.mT @ P + P.mT @ Q).norm())
|
| 57 |
+
verifica("26.2 P_X(G) ∈ T_X St (XᵀP+PᵀX = 0)", resid < 1e-12,
|
| 58 |
+
f"resíduo={resid:.2e}")
|
| 59 |
+
# adjunto exato no tangente (Teo 26.3-a): direcional vs <g_auto, P>
|
| 60 |
+
Q0, _ = torch.linalg.qr(torch.randn(32, 32, dtype=torch.float64))
|
| 61 |
+
W_lin = torch.linspace(1, 2, 32, dtype=torch.float64)
|
| 62 |
+
|
| 63 |
+
def f(t): return (RetracaoStiefel.apply(t) * W_lin).sum()
|
| 64 |
+
|
| 65 |
+
Xg = Q0.clone().requires_grad_(True)
|
| 66 |
+
g_auto = torch.autograd.grad(f(Xg), Xg)[0]
|
| 67 |
+
Omega = torch.randn(32, 32, dtype=torch.float64)
|
| 68 |
+
Omega = (Omega - Omega.mT) / 2
|
| 69 |
+
P_tan = Q0 @ Omega
|
| 70 |
+
h = 1e-7
|
| 71 |
+
dH = (f(Q0 + h * P_tan) - f(Q0 - h * P_tan)) / (2 * h)
|
| 72 |
+
prod = float((g_auto * P_tan).sum())
|
| 73 |
+
err_dir = abs(float(dH) - prod) / max(abs(float(dH)), 1e-12)
|
| 74 |
+
verifica("26.3-a adjunto exato no tangente (err rel ≤ 1e-5)",
|
| 75 |
+
err_dir < 1e-5, f"err={err_dir:.2e}")
|
| 76 |
+
g_resid = float((Q0.mT @ g_auto + g_auto.mT @ Q0).norm())
|
| 77 |
+
verifica("26.3-b gradiente propagado é tangente (≤ 1e-10)",
|
| 78 |
+
g_resid < 1e-10, f"resíduo={g_resid:.2e}")
|
| 79 |
+
# lema da descida (Teo 26.3-c)
|
| 80 |
+
Xg2 = Q0.clone().requires_grad_(True)
|
| 81 |
+
F0 = f(Xg2).item()
|
| 82 |
+
g2 = torch.autograd.grad(f(Xg2), Xg2)[0]
|
| 83 |
+
F1 = f(Q0 - 1e-2 * g2).item()
|
| 84 |
+
verifica("26.3-c lema da descida no manifold", F1 < F0,
|
| 85 |
+
f"Δ={F1 - F0:+.4f}")
|
| 86 |
+
|
| 87 |
+
print("== 3) Agendador de τ (Teo 26.4/26.5) ==")
|
| 88 |
+
ag = AgendadorTau(tau0=2.0, tau_min=0.25, t_alvo=600.0, delta=0.05)
|
| 89 |
+
verifica("26.5-iii ajuste analítico: t*(δ) = t_alvo",
|
| 90 |
+
abs(ag.tempo_para(0.05) - 600.0) < 1e-6,
|
| 91 |
+
f"T_τ={ag.T_tau:.1f}")
|
| 92 |
+
taus = [ag.tau(t) for t in range(0, 2001)]
|
| 93 |
+
verifica("26.5-i monotônico decrescente em [τ_min, τ_0]",
|
| 94 |
+
all(taus[i] >= taus[i + 1] for i in range(len(taus) - 1))
|
| 95 |
+
and abs(taus[-1] - ag.tau_min) < 0.01,
|
| 96 |
+
f"τ(2000)={taus[-1]:.4f}")
|
| 97 |
+
soma_inv = sum(1.0 / t for t in taus)
|
| 98 |
+
verifica("26.5-ii Σ 1/τ(t) finita (barreira)", soma_inv < 2000 * 4.0,
|
| 99 |
+
f"Σ={soma_inv:.1f}")
|
| 100 |
+
# barreira do gradiente: ‖∂y/∂l‖₂ ≤ 1/(2τ)
|
| 101 |
+
verifica("26.4-ii barreira: 1/(2τ_min) ≤ 2.0", 1.0 / (2 * 0.25) <= 2.0)
|
| 102 |
+
|
| 103 |
+
print("== 4) Camada GSQ+RCO ponta a ponta (Teo 26.9) ==")
|
| 104 |
+
d = 32
|
| 105 |
+
cam = CamadaGSQRCO(d, d, k_niveis=8,
|
| 106 |
+
agendador=AgendadorTau(tau0=2.0, tau_min=0.25,
|
| 107 |
+
t_alvo=300.0))
|
| 108 |
+
Xd = torch.randn(64, d)
|
| 109 |
+
Q0d, _ = torch.linalg.qr(torch.randn(d, d))
|
| 110 |
+
alvo = Xd @ (0.7 * Q0d)
|
| 111 |
+
opt = torch.optim.AdamW(cam.parameters(), lr=1e-2)
|
| 112 |
+
p0 = None
|
| 113 |
+
for passo in range(240):
|
| 114 |
+
cam.definir_temperatura(cam.agendador.passo())
|
| 115 |
+
opt.zero_grad()
|
| 116 |
+
perda = ((cam(Xd) - alvo) ** 2).mean()
|
| 117 |
+
perda.backward()
|
| 118 |
+
opt.step()
|
| 119 |
+
if p0 is None:
|
| 120 |
+
p0 = float(perda)
|
| 121 |
+
verifica("26.9 descida ponta a ponta (perda cai ≥ 1.5×)",
|
| 122 |
+
float(perda) < p0 / 1.5,
|
| 123 |
+
f"{p0:.3f}→{float(perda):.3f} ({p0 / float(perda):.2f}×)")
|
| 124 |
+
m = cam.metrica()
|
| 125 |
+
verifica("26.4-iii afiamento: entropia decai com τ",
|
| 126 |
+
m["entropia"] < 0.5 and m["tau"] < 1.0,
|
| 127 |
+
f"τ={m['tau']:.2f} H={m['entropia']:.3f}")
|
| 128 |
+
verifica("26.9-i δ(W_ort) exato no treino (float32 ≤ 1e-4)",
|
| 129 |
+
m["erro_ort"] < 1e-4, f"δ={m['erro_ort']:.1e}")
|
| 130 |
+
vivos = all(p.grad is not None and float(p.grad.norm()) > 0
|
| 131 |
+
for p in (cam.logits_gsq, cam.centros_gsq, cam.escala_gsq))
|
| 132 |
+
verifica("26.9-iii gradientes vivos (logits/centros/escala)", vivos)
|
| 133 |
+
cam.eval()
|
| 134 |
+
with torch.no_grad():
|
| 135 |
+
w1, w2 = cam.peso_efetivo(), cam.peso_efetivo()
|
| 136 |
+
verifica("26.8-e inferência determinística em eval",
|
| 137 |
+
bool(torch.allclose(w1, w2)))
|
| 138 |
+
|
| 139 |
+
print("== 5) Kernel Cython híbrido (Teo 26.6/26.7/26.8) ==")
|
| 140 |
+
try:
|
| 141 |
+
from khtst.acelerado import rco_gsq
|
| 142 |
+
hw = rco_gsq.detect_hardware_acceleration()
|
| 143 |
+
verifica("26.6/M1 detecção de hardware automática",
|
| 144 |
+
"device" in hw, f"{hw.get('device')}")
|
| 145 |
+
N, K = 96, 8
|
| 146 |
+
import numpy as np
|
| 147 |
+
rng = np.random.RandomState(2026)
|
| 148 |
+
logits = rng.randn(N, K)
|
| 149 |
+
centros = rng.randn(K, N)
|
| 150 |
+
g = rco_gsq.gerar_gumbel(N * K, seed=2026)
|
| 151 |
+
Qc = rco_gsq.forward_gsq_rco(logits, centros, 0.5, g)
|
| 152 |
+
dc = rco_gsq.metrica_ortogonalidade(Qc)
|
| 153 |
+
verifica("26.1-a (CPU) δ ≤ 1e-12", dc < 1e-12, f"δ={dc:.2e}")
|
| 154 |
+
# Teo 26.8-c: mesma retração (QR-sign), RANK COMPLETO (N=K — com
|
| 155 |
+
# rank-deficiência o completamento de base difere por LAPACK, Teo
|
| 156 |
+
# 26.7; a comparação de MATRIZ exige rank cheio; para rank-deficiente
|
| 157 |
+
# a métrica correta é o projetor — verificação seguinte)
|
| 158 |
+
Nr = 64
|
| 159 |
+
logits_r = rng.randn(Nr, Nr)
|
| 160 |
+
centros_r = rng.randn(Nr, Nr)
|
| 161 |
+
g_r = rco_gsq.gerar_gumbel(Nr * Nr, seed=2026)
|
| 162 |
+
Qc_r = rco_gsq.forward_gsq_rco(logits_r, centros_r, 0.5, g_r)
|
| 163 |
+
l_r = torch.from_numpy(logits_r).double()
|
| 164 |
+
c_r = torch.from_numpy(centros_r).double()
|
| 165 |
+
g_t_r = torch.from_numpy(g_r.reshape(Nr, Nr)).double()
|
| 166 |
+
Q_t_r = _qr_com_sinal(torch.softmax((l_r + g_t_r) / 0.5, dim=-1) @ c_r)
|
| 167 |
+
div = float((torch.from_numpy(Qc_r) - Q_t_r).norm() / Q_t_r.norm())
|
| 168 |
+
verifica("26.8-c divergência Cython×PyTorch (mesma retração, rank cheio) ≤ 1e-9",
|
| 169 |
+
div < 1e-9, f"div={div:.2e}")
|
| 170 |
+
# Teo 26.8-b: invariante do projetor contra a retração polar
|
| 171 |
+
# (rank-deficiente: N=96, K=8 — o completamento difere, o projetor
|
| 172 |
+
# NÃO: projeção ortogonal no span é única, Teo 26.7-ii)
|
| 173 |
+
Q_pol = retrair_svd(torch.softmax(
|
| 174 |
+
(torch.from_numpy(logits).double()
|
| 175 |
+
+ torch.from_numpy(g.reshape(N, K)).double()) / 0.5, dim=-1)
|
| 176 |
+
@ torch.from_numpy(centros).double())
|
| 177 |
+
Pc = torch.from_numpy(Qc) @ torch.from_numpy(Qc).mT
|
| 178 |
+
Pp = Q_pol @ Q_pol.mT
|
| 179 |
+
divp = float((Pc - Pp).norm() / Pp.norm())
|
| 180 |
+
verifica("26.8-b projetor invariante QR×polar (rank-deficiente) ≤ 1e-9",
|
| 181 |
+
divp < 1e-9, f"div={divp:.2e}")
|
| 182 |
+
except Exception as e: # kernel ausente → falha explícita (sem fallback)
|
| 183 |
+
verifica("kernel Cython rco_gsq importável", False, str(e))
|
| 184 |
+
|
| 185 |
+
print("== 6) Integração no modelo/treinador (Teo 26.10/26.11) ==")
|
| 186 |
+
from khtst.config import Config
|
| 187 |
+
from khtst.nucleo.modelo import KHTSTModel
|
| 188 |
+
cfg0 = Config()
|
| 189 |
+
m0 = KHTSTModel(cfg0)
|
| 190 |
+
verifica("26.11 nascimento neutro: modo default é 'qr'",
|
| 191 |
+
m0.modo_alinhamento == "qr"
|
| 192 |
+
and type(m0.alinhamento).__name__ == "CamadaOrtogonal")
|
| 193 |
+
cfg1 = Config()
|
| 194 |
+
cfg1.modelo.gsq_rco["ativo"] = True
|
| 195 |
+
m1 = KHTSTModel(cfg1)
|
| 196 |
+
verifica("modo gsq_rco ativa CamadaGSQRCO",
|
| 197 |
+
m1.modo_alinhamento == "gsq_rco"
|
| 198 |
+
and type(m1.alinhamento).__name__ == "CamadaGSQRCO")
|
| 199 |
+
ids = torch.randint(0, cfg1.modelo.vocab, (2, 16))
|
| 200 |
+
logits, perda = m1(ids, alvo=torch.randint(0, cfg1.modelo.vocab, (2, 16)),
|
| 201 |
+
tarefa="lm")
|
| 202 |
+
p_som = m1.alinhar_som(ids, tarefa="lm")
|
| 203 |
+
(perda + p_som).backward()
|
| 204 |
+
vivos = all(p.grad is not None and float(p.grad.norm()) > 0
|
| 205 |
+
for p in m1.alinhamento.parameters())
|
| 206 |
+
verifica("treino ponta a ponta no modelo completo (grads > 0)", vivos)
|
| 207 |
+
from khtst.treino.otimizadores import MuonClipAdamW
|
| 208 |
+
muon, adam = MuonClipAdamW.separar_parametros(m1)
|
| 209 |
+
n_gsq_adam = sum(1 for n, p in m1.named_parameters()
|
| 210 |
+
if "gsq" in n and any(p is q for q in adam))
|
| 211 |
+
n_gsq_muon = sum(1 for n, p in m1.named_parameters()
|
| 212 |
+
if "gsq" in n and any(p is q for q in muon))
|
| 213 |
+
verifica("26.10 gate GSQ no AdamW (3 params) e FORA do Muon (0)",
|
| 214 |
+
n_gsq_adam == 3 and n_gsq_muon == 0,
|
| 215 |
+
f"adam={n_gsq_adam} muon={n_gsq_muon}")
|
| 216 |
+
me = m1.metricas_estruturais()
|
| 217 |
+
verifica("metricas_estruturais compatível (ortogonalidade registrada)",
|
| 218 |
+
"ortogonalidade" in me)
|
| 219 |
+
|
| 220 |
+
print("== 7) Agendador no treinador (hook pós-step) ==")
|
| 221 |
+
cfg2 = Config()
|
| 222 |
+
cfg2.modelo.gsq_rco["ativo"] = True
|
| 223 |
+
from khtst.telemetria.hub import TelemetryHub
|
| 224 |
+
hub = TelemetryHub(os.path.join(os.path.dirname(__file__),
|
| 225 |
+
"..", "telemetria_out", "teste_v13_hub.jsonl"))
|
| 226 |
+
from khtst.dados.tokenizador import TokenizadorKHTST
|
| 227 |
+
tk = TokenizadorKHTST.carregar(
|
| 228 |
+
os.path.join(os.path.dirname(__file__), "..", "cache_dados",
|
| 229 |
+
"tokenizador.json"))
|
| 230 |
+
from khtst.treino.treinador import TreinadorExtensao
|
| 231 |
+
registros = [{"texto": "exemplo de texto para o teste v13 do gsq rco",
|
| 232 |
+
"tarefa": "lm"} for _ in range(6)]
|
| 233 |
+
tre = TreinadorExtensao(cfg2, m1, tk, hub, registros)
|
| 234 |
+
verifica("treinador injeta agendador e coleta camadas GSQ",
|
| 235 |
+
tre.gsq_rco is not None and len(tre.camadas_gsq) >= 1)
|
| 236 |
+
tre._passo()
|
| 237 |
+
verifica("hook pós-step agenda τ e registra telemetria gsq_rco/*",
|
| 238 |
+
tre.agendador_tau.t == 1 and tre.passo_global == 1)
|
| 239 |
+
|
| 240 |
+
print()
|
| 241 |
+
if _resultados["falha"]:
|
| 242 |
+
print(f"===== v13: {_resultados['ok']} OK, "
|
| 243 |
+
f"{_resultados['falha']} FALHAS =====")
|
| 244 |
+
sys.exit(1)
|
| 245 |
+
print(f"===== v13: TODAS VERDES ({_resultados['ok']} ✓) =====")
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
if __name__ == "__main__":
|
| 249 |
+
main()
|