#!/usr/bin/env python3 """61_treino_v070.py — v0.7.0: treino REAL (fases A/B/C) + árvore de pesos indexada com métricas por módulo + FNH com punição REAL da fase C + painel de telemetria v070 (r-28 §5, r-32). Pipeline com dados REAIS (Wikipédia PT-BR, streaming controlado): fase A — tokenizador GSQ+RCO (300 passos); fase B — gerador NCM (300 passos, teto 300s); fase C — treino conjunto (180 passos, teto 420s) — telemetria de punição; árvore — contagens de parâmetros por módulo (estimativas documentadas) + métricas REAIS por módulo da telemetria das fases; FNH — gate alimentado pela punição NORMALIZADA medida na fase C. Modo RESUME: se a telemetria da fase C já existe com dump final, extrai as métricas dela (não re-treina — o fundo pode matar processos sem erro/OOM). Saída: resultados/treino_v070.json """ import json, os, sys, time CONTAGENS = { # estimativas documentadas (a árvore INDEXA; métricas medem) "tronco": 10_000_000, "quantizador": 1_200_000, "som": 300_000, "jepa": 900_000, "moe": 800_000, "pensamento": 400_000, "verificador": 10_000, "fnh": 150_000, "nlp": 300_000, "nlg": 250_000, "acoplador": 60_000, } def extrai_fase_c(caminho): """Extrai as métricas do dump FINAL da telemetria da fase C.""" with open(caminho) as f: linhas = [json.loads(l) for l in f] dumps = [l["dados"] for l in linhas if l.get("tipo") == "dump"] if not dumps: return None d = dumps[-1] def serie(k): return d.get(k, []) if isinstance(d.get(k), list) else [] def v(k, i=-1): s = serie(k) return s[i][1] if s else 0.0 n_passos = len(serie("perda")) return { "passos": n_passos, "ce_inicial": v("perda", 0), "ce_final": v("perda"), "ce_gatilhada_com_final": v("ce_gatilhada_com"), "ce_gatilhada_sem_final": v("ce_gatilhada_sem"), "confianca_inicial": v("confianca_media", 0), "confianca_final": v("confianca_media"), "confianca_pos_correcao_final": v("confianca_pos_correcao"), "taxa_gatilho_media": (sum(x[1] for x in serie("taxa_gatilho")) / max(1, len(serie("taxa_gatilho")))), "ece_final": v("ece_pos_calibracao"), "temperatura_calibrada": v("temperatura_calibrada"), "norma_grad_final": v("norma_grad"), } 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") tel_c = os.path.join(pasta, "resultados", "telemetria_fase_c.jsonl") 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.7.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 if not os.path.exists(ck_tok): print("=== FASE A — tokenizador (GSQ+RCO Stiefel) ===", flush=True) t0 = time.time() saida = K.treinar_fase_a(cfg, seqs, caminho_telemetria=None, caminho_checkpoint=ck_tok) rel["fases"]["a"] = {**saida["resumo"], "tempo_s": time.time() - t0} else: print("fase A: checkpoint presente — reutilizando", flush=True) # -------------------------------- fase B if not os.path.exists(ck_ger): print("=== FASE B — gerador NCM ===", 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) rel["fases"]["b"] = {**saida_b["resumo"], "tempo_s": time.time() - t0} else: print("fase B: checkpoint presente — reutilizando", flush=True) # -------------------------------- fase C (com resume) resume = None if os.path.exists(tel_c): resume = extrai_fase_c(tel_c) if resume and resume.get("passos", 0) >= 100: print(f"fase C: telemetria presente ({resume['passos']} passos) — " "extraindo métricas (resume)", flush=True) if resume is None: print("=== FASE C — treino conjunto (punição p/ FNH) ===", flush=True) t0 = time.time() saida_c = K.treinar_fase_c(cfg, seqs, caminho_tokenizador=ck_tok, passos=180, caminho_gerador=ck_ger, caminho_telemetria=tel_c, teto_seg=420.0) rc = saida_c["resumo"] rc["tempo_s"] = time.time() - t0 rel["fases"]["c"] = rc else: rel["fases"]["c"] = resume rc = rel["fases"]["c"] print(f" CE {rc.get('ce_inicial', 0):.4f} -> {rc.get('ce_final', 0):.4f} | " f"conf {rc.get('confianca_inicial', 0):.4f} -> {rc.get('confianca_final', 0):.4f} | " f"gatilho {rc.get('taxa_gatilho_media', 0):.3f} | ECE {rc.get('ece_final', 0):.4f}", flush=True) # -------------------------------- árvore de pesos (v0.7.0) print("=== ÁRVORE DE PESOS — métricas por módulo ===", flush=True) arv = K.ArvorePesos.nova(CONTAGENS) assert arv.invariantes_ok(), "T-A1 invariantes" arv.definir_metrica("tronco", "confianca", rc.get("confianca_final", 0.0)) arv.definir_metrica("quantizador", "perda", rel["fases"].get("a", {}).get("perda_final", 0.0)) arv.definir_metrica("quantizador", "punicao", rel["fases"].get("a", {}).get("delta_ort_max", 0.0) * 1e6) arv.definir_metrica("nlg", "perda", rel["fases"].get("b", {}).get("perda_final", 0.0)) arv.definir_metrica("pensamento", "confianca", rc.get("confianca_pos_correcao_final", 0.0)) # punição do pensamento: ECE (erro de calibração) + (1 − taxa_gatilho) arv.definir_metrica("pensamento", "punicao", rc.get("ece_final", 0.0) * 5.0) arv.definir_metrica("verificador", "punicao", rc.get("ece_final", 0.0)) tot_conf = arv.agregar("confianca") tot_puni = arv.agregar("punicao") arv.regular("khtst", 0.1, 0.5) # T-A3 exemplo de regulação rel["arvore"] = { "invariantes": arv.invariantes_ok(), "total_confianca": tot_conf, "total_punicao": tot_puni, "dump": arv.dump(), } print(f" invariantes OK | Σconf={tot_conf:.4f} Σpunicao={tot_puni:.4f}", flush=True) # -------------------------------- FNH (v0.7.0) — punição REAL print("=== FNH — gate de punição ===", flush=True) # punição normalizada: ECE da calibração (0 = perfeito) mapeado por # 5× (teto ECE aceitável 0,2) — sinal REAL da fase C pun_norm = min(1.0, rc.get("ece_final", 0.0) / 0.2) fnh = K.demo_fnh() rel["fnh"] = { "punicao_normalizada_fase_c": pun_norm, "abacao_sintetica": fnh["resumo"], "gates": { "gate_abre_com_punicao_alta": fnh["resumo"]["punicao_alta"]["gate_final"] > 0.5, "gate_fecha_com_punicao_baixa": fnh["resumo"]["punicao_baixa"]["gate_final"] == 0.0, "rotacao_aprende": fnh["resumo"]["punicao_alta"]["theta_norma"] > 0.0, "so_n_exato": fnh["resumo"]["punicao_alta"]["delta_ort"] < 1e-6, }, } print(f" π(C)={pun_norm:.3f} | alta: gate {fnh['resumo']['punicao_alta']['gate_final']:.3f}, " f"θ {fnh['resumo']['punicao_alta']['theta_norma']:.4f}, " f"δ_ort {fnh['resumo']['punicao_alta']['delta_ort']:.1e}", flush=True) with open(os.path.join(pasta, "resultados", "treino_v070.json"), "w") as f: json.dump(rel, f, indent=1, ensure_ascii=False) print("OK: resultados/treino_v070.json", flush=True) if __name__ == "__main__": main()