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()
|