khtst-rust / scripts /56_treino_conjunto.py
PowerMachine's picture
v0.6.0: verificador NCM calibrado (sem oráculo, V-1..V-4) + treino conjunto Pensamento↔backbone (fase C, PCGrad, acoplador duplex AC) + MoEs redirecionáveis c/ atenção própria (M-R1..M-R5, JEPA+SOM) + autoajuste por telemetria (A-1..A-5, UCB1) — 46/46 gates, provas r-23..r-27
bb2363d verified
Raw History Blame Contribute Delete
6.99 kB
#!/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()