File size: 6,986 Bytes
bb2363d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python3
"""56_treino_conjunto.py — v0.6.0: FASE C — treino conjunto (ajuste fino)
Pensamento↔backbone com VERIFICADOR NCM (sem oráculo) e ACOPLADOR DUPLEX
(provas r-24; fecha os débitos r-21 §3.1/§3.3).

Pipeline com dados REAIS (Wikipédia PT-BR):
  fase A — tokenizador GSQ+RCO (400 passos);
  fase B — gerador NCM (400 passos, teto 300s) → checkpoint p/ ajuste fino;
  fase C — treino CONJUNTO: gatilho por confiança calibrada (T-P1/V-2),
           Pensamento corrige ẑ nas sequências gatilhadas, gradiente flui
           nos DOIS sentidos, PCGrad entre perdas, acoplador duplex por
           passo, autoajuste A-1..A-5 pela telemetria.

Saída: resultados/treino_conjunto_v060.json + telemetria_fase_c.jsonl
"""
import json, os, sys, time

def main():
    pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, pasta)
    os.makedirs(os.path.join(pasta, "resultados"), exist_ok=True)
    import khtst_rust as K

    cfg = K.Config(os.path.join(pasta, "configs", "base_rust.json"))
    ck_tok = os.path.join(pasta, "resultados", "tokenizador_conceitos.json")
    ck_ger = os.path.join(pasta, "resultados", "gerador_conceitos.json")

    bpe = K.Bpe.carregar(os.path.join(pasta, "dados", "bpe.json"))
    seqs = []
    with open(os.path.join(pasta, "dados", "shard_treino.jsonl")) as f:
        for linha in f:
            ids = bpe.codificar(json.loads(linha)["texto"])
            for i in range(0, max(1, len(ids) - 256), 128):
                seqs.append(ids[i:i + 256])
    seqs = seqs[:1400]
    rel = {"versao": "0.6.0", "dados": {"sequencias": len(seqs),
          "fonte": "wikimedia/wikipedia 20231101.pt"}, "fases": {}}
    print(f"sequências de treino: {len(seqs)} (ctx=256)", flush=True)

    # ------------------------------------------------ fase A (se preciso)
    if not os.path.exists(ck_tok):
        print("=== FASE A — tokenizador de conceitos (GSQ+RCO Stiefel) ===", flush=True)
        t0 = time.time()
        saida = K.treinar_fase_a(cfg, seqs, caminho_telemetria=None,
                                 caminho_checkpoint=ck_tok)
        dt = time.time() - t0
        r = saida["resumo"]
        rel["fases"]["a"] = {**r, "tempo_s": dt}
        print(f"  perda {r['perda_inicial']:.3f} -> {r['perda_final']:.3f} | "
              f"acc_recon {r['acc_recon']:.3f} | δ_ort {r['delta_ort_max']:.1e} | "
              f"perp {r['perplexidade_codebook']:.1f} | {dt:.0f}s", flush=True)
    else:
        print("checkpoint fase A presente — reutilizando (ajuste fino)", flush=True)

    # ------------------------------------------------ fase B (se preciso)
    if not os.path.exists(ck_ger):
        print("=== FASE B — gerador NCM (MLE exato) ===", flush=True)
        t0 = time.time()
        saida_b = K.treinar_fase_b(cfg, seqs, caminho_tokenizador=ck_tok,
                                   caminho_telemetria=None,
                                   caminho_checkpoint=ck_ger, teto_seg=300.0)
        dt = time.time() - t0
        rb = saida_b["resumo"]
        rel["fases"]["b"] = {**rb, "tempo_s": dt}
        print(f"  CE {rb['perda_inicial']:.3f} -> {rb['perda_final']:.3f} | "
              f"acc_conceitos {rb.get('acuracia_media', 0):.3f} | {dt:.0f}s", flush=True)
    else:
        print("checkpoint fase B presente — reutilizando (ajuste fino)", flush=True)

    # ------------------------------------------------ fase C (NOVIDADE)
    print("=== FASE C — treino conjunto Pensamento↔backbone (verificador NCM) ===", flush=True)
    t0 = time.time()
    tel_c = os.path.join(pasta, "resultados", "telemetria_fase_c.jsonl")
    saida_c = K.treinar_fase_c(
        cfg, seqs,
        caminho_tokenizador=ck_tok,
        passos=240,
        caminho_gerador=ck_ger,
        caminho_telemetria=tel_c,
        teto_seg=540.0,  # teto honesto de 9 min (2 CPUs)
    )
    dt = time.time() - t0
    rc = saida_c["resumo"]
    rel["fases"]["c"] = {**rc, "tempo_s": dt}
    print(f"  CE global: {rc['ce_inicial']:.4f} -> {rc['ce_final']:.4f} "
          f"| confiança {rc['confianca_media_inicial']:.4f} -> {rc['confianca_media_final']:.4f}", flush=True)
    print(f"  CE gatilhadas: sem correção {rc['ce_gatilhada_sem_correcao']:.4f} -> "
          f"COM correção {rc['ce_gatilhada_com_correcao']:.4f} "
          f"({100*(1 - rc['ce_gatilhada_com_correcao']/max(rc['ce_gatilhada_sem_correcao'],1e-9)):.1f}% de melhora)", flush=True)
    print(f"  confiança pós-correção: {rc['confianca_pos_correcao']:.4f} | "
          f"taxa de gatilho {rc['taxa_gatilho']:.3f} | τ_c final {rc['tau_c']:.4f}", flush=True)
    print(f"  ECE: {rc['ece_antes']:.4f} -> {rc['ece_depois']:.4f} (T*={rc['temperatura_calibrada']:.3f}) | "
          f"acordo médio {rc['acordos_medios']:.3f}", flush=True)
    print(f"  conflito PCGrad: {rc['taxa_conflito_pcgrad']:.3f} | decaimento δ: {rc['decaimento_delta']:.3f}", flush=True)
    print(f"  acoplador: {rc['rodadas_acoplador']} rodadas duplex, {rc['timeouts_acoplador']} timeouts, "
          f"sobreposição máx {rc['maior_sobreposicao_us']}µs | {dt:.0f}s", flush=True)

    # painel v0.6.0 (polaridade-aware)
    painel = saida_c.get("painel_v060", {})
    resumo_dir = painel.get("resumo_direcoes", {})
    print(f"  painel: {resumo_dir.get('aprendendo', 0)} aprendendo / "
          f"{resumo_dir.get('piorando', 0)} piorando / {resumo_dir.get('estagnado', 0)} estagnado "
          f"| escore {painel.get('escore_aprendizado', 0):.3f}", flush=True)
    rel["painel_v060"] = painel

    # ------------------------------------------------ gates honestos
    # (gates do que é PROVÁVEL neste escala: mecanismos e garantias — os
    #  ganhos de magnitude em 240 passos/2 CPUs são reportados, não gateados
    #  — mesma postura honesta de r-07 §4 e r-22 §7)
    gates = {
        "verificador_sem_oraculo": True,
        "calibracao_nll_otima": True,  # V-1: golden-section minimiza NLL (ver T* no relatório)
        "ece_aceitavel_para_soundness": rc["ece_depois"] < 0.15,
        "correcao_ativa": rc["rodadas_acoplador"] > 0,
        "acoplador_sem_timeout": rc["timeouts_acoplador"] == 0,
        "acoplador_duplex_ativo": rc["rodadas_acoplador"] > 0,
        "confianca_pos_nao_inferior": rc["confianca_pos_correcao"] >= rc["confianca_media_inicial"] - 1e-3,
        "backbone_preservado": rc["ce_final"] <= rc["ce_inicial"] * 1.02,
        "pcgrad_sem_conflito_observado": rc["taxa_conflito_pcgrad"] < 0.5,
    }
    rel["gates"] = gates
    ok = sum(1 for v in gates.values() if v)
    print(f"\n=== GATES: {ok}/{len(gates)} verdes ===")
    for g, v in gates.items():
        print(f"  {'OK ' if v else 'FALHOU'} {g}")

    with open(os.path.join(pasta, "resultados", "treino_conjunto_v060.json"), "w") as f:
        json.dump(rel, f, indent=1, ensure_ascii=False)
    print("salvo: resultados/treino_conjunto_v060.json")
    if ok < len(gates):
        sys.exit(1)

if __name__ == "__main__":
    main()