PowerMachine's picture
AURORA v7 — RETREINO OBSERVADO: estados antigos apagados; treino do zero com monitor de RAM integral (Agente Engenheiro); métricas de tests/test_aurora_v6.py comparadas com o treino anterior; gráficos de desempenho publicados no card
7eea4b4 verified
Raw History Blame Contribute Delete
32 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Avaliação v3 — relatório de QUALIDADE DO APRENDIZADO com evidências:
1. perplexidade LM: modelo inicial (aleatório) vs treinado vs por época;
2. perdas por tarefa (9 tarefas) treinado vs aleatório;
3. MoE agrupável (roteamento POR TOKEN causal): top experts POR TAREFA;
4. MTP adaptativo: perdas por cabeça, α e ECONOMIA de termos de CE;
5. W8A8: delta de perda com/sem quantização na lm_head;
6. SOM: taxa de neurônios ativos, EQ, TE por variante;
7. geração: amostras com/sem punição dinâmica de repetição;
8. MICROUNIDADES: ramos ativos, gating α por tarefa, refino recursivo;
9. CONFIANÇA: posterior Beta, cota de Bernstein, ECE de calibração;
10. MEMÓRIA INTERNA: hit-rate, escritas conficientes, compressão VQ;
11. CRESCIMENTO: eventos de expansão/poda das microunidades.
Salva telemetria_out/avaliacao_v5.json (v5: + Agente Engenheiro e difusão)."""
import base64
import json
import math
import os
import random
import sys
import time
sys.path.insert(0, "/home/z/my-project/aurora/src")
import torch
from aurora.config import Config
from aurora.dados.checkpoints import GestorCheckpoints
from aurora.geracao import GeradorMultimodal
from aurora.qualidade import AgenteEngenheiro
from aurora.dados.tokenizador import TokenizadorAurora
from aurora.memoria.orquestrador import OrquestradorSOM
from aurora.nucleo.modelo import AURORAModel
from aurora.telemetria.hub import TelemetryHub
from aurora.treino.treinador import (TAREFAS_AUDIO, TAREFAS_IMAGEM,
_audio_para_tensor, _imagem_para_tensor,
TreinadorExtensao)
CORPUS = "/home/z/my-project/aurora/cache_dados/corpus_v2.jsonl"
TOKENIZADOR = "/home/z/my-project/aurora/cache_dados/tokenizador.json"
RELATORIO = "/home/z/my-project/aurora/telemetria_out/avaliacao_v7.json"
def carregar_registros(caminho):
regs = []
with open(caminho, encoding="utf-8") as f:
for linha in f:
try:
r = json.loads(linha)
except Exception:
continue
if isinstance(r.get("imagem"), str):
r["imagem"] = base64.b64decode(r["imagem"])
if isinstance(r.get("audio"), str):
r["audio"] = base64.b64decode(r["audio"])
regs.append(r)
return regs
@torch.no_grad()
def perda_lote(modelo, tk, registros, tarefa, n_lotes=6, lote=8):
"""Perda CE média da tarefa (multimodais usam o prefixo real treinável-não)."""
pool = [r for r in registros if r["tarefa"] == tarefa]
if not pool:
return None
perdas = []
for _ in range(n_lotes):
amostras = random.sample(pool, min(lote, len(pool)))
L = modelo.cfg.modelo.comprimento_ctx
seqs, extras = [], []
for s in amostras:
inp = tk.encode(s.get("entrada") or "", tarefa=tarefa, max_len=L // 2) \
if tarefa not in ("lm", "noticia") else [1]
saida_fonte = s.get("saida") or s.get("texto") or ""
out = tk.encode(saida_fonte, tarefa=None,
max_len=max(L - len(inp) - 4, 8), com_bos=False)
if len(out) < 4:
continue
seqs.append(inp + out)
extras.append(s)
if not seqs:
continue
Tmax = max(len(s) for s in seqs)
ids = torch.zeros(len(seqs), Tmax, dtype=torch.long)
alvo = torch.full((len(seqs), Tmax), -100, dtype=torch.long)
emb_prefixo = None
for i, (s, seq) in enumerate(zip(extras, seqs)):
ids[i, :len(seq)] = torch.tensor(seq)
if tarefa not in ("lm", "noticia"):
ninp = len(tk.encode(s.get("entrada") or "", tarefa=tarefa,
max_len=L // 2))
alvo[i, ninp:len(seq)] = ids[i, ninp:len(seq)]
else:
alvo[i, 1:len(seq)] = ids[i, 1:len(seq)]
if modelo.usar_multimodal:
entradas = {}
if tarefa in TAREFAS_IMAGEM:
lado = modelo.cfg.modelo.imagem["resolucao"]
entradas["imagem"] = torch.stack([
_imagem_para_tensor(s["imagem"], lado) if s.get("imagem")
else torch.zeros(3, lado, lado) for s in extras])
if tarefa in TAREFAS_AUDIO:
ondas = [_audio_para_tensor(s["audio"]) for s in extras if s.get("audio")]
if ondas:
Nmax = max(o.shape[0] for o in ondas)
pad = torch.zeros(len(ondas), Nmax)
for i, o in enumerate(ondas):
pad[i, :o.shape[0]] = o
entradas["audio"] = pad
if entradas:
emb_prefixo = modelo.codificar_multimodal(entradas)
_, perda = modelo(ids, alvo=alvo, emb_prefixo=emb_prefixo, tarefa=tarefa)
perdas.append(float(perda))
return sum(perdas) / len(perdas) if perdas else None
@torch.no_grad()
def perdas_som_amostra(modelo, tk, registros, orquestrador, n=64):
seqs = []
for r in registros[:n]:
seqs.append(tk.encode(r.get("texto") or (r.get("saida") or r.get("entrada", "")),
tarefa=r["tarefa"], max_len=64)[:64])
Tmax = max(len(s) for s in seqs)
ids = torch.zeros(len(seqs), Tmax, dtype=torch.long)
for i, s in enumerate(seqs):
ids[i, :len(s)] = torch.tensor(s)
ctx = modelo.contexto(ids)
out = {}
for nome, v in orquestrador.variantes.items():
if hasattr(v, "taxa_ativos"):
out[nome] = {"taxa_ativos": v.taxa_ativos(),
"eq": v.eq(ctx) if hasattr(v, "eq") else None}
return out
def _salvar(rel: dict):
"""Salvamento incremental — evidência parcial sobrevive a cortes de tempo."""
os.makedirs(os.path.dirname(RELATORIO), exist_ok=True)
with open(RELATORIO, "w", encoding="utf-8") as f:
json.dump(rel, f, ensure_ascii=False, indent=2, default=str)
def main():
cfg = Config().de_arquivo("/home/z/my-project/aurora/configs/base.json")
random.seed(cfg.dados.semente)
torch.manual_seed(cfg.dados.semente)
hub = TelemetryHub(cfg.telemetria.arquivo_jsonl)
tk = TokenizadorAurora(TOKENIZADOR)
registros = carregar_registros(CORPUS)
modelo = AURORAModel(cfg, usar_multimodal=True)
checkpoints = GestorCheckpoints(dir_local=cfg.checkpoints.dir_local)
tag = checkpoints.ultima_tag()
if tag:
checkpoints.carregar(modelo, tag)
print(f"estado carregado: {tag or 'NENHUM (avaliando aleatório!)'}")
orquestrador = OrquestradorSOM(cfg.modelo.d_modelo, cfg.som, hub=hub,
cfg_atencao=vars(cfg.som_atencao) if hasattr(cfg, "som_atencao") else None)
# v5 — os SOMs são tensors puros (fora do state_dict): re-consolidação
# determinística a partir do modelo treinado (mesmo protocolo do 07 /
# Teorema 16.5) — as evidências de memória refletem o estado TREINADO.
print("== re-consolidação SOM (protocolo doc 16 §4/§5) ==")
relatorio = {"estado": tag}
amostras = random.sample(registros, min(4 * 64, len(registros)))
ctxs = []
for i in range(0, len(amostras), 64):
pedaco = amostras[i:i + 64]
seqs = [tk.encode(r.get("texto") or (r.get("saida") or r.get("entrada", "")),
tarefa=r["tarefa"], max_len=64)[:64] for r in pedaco]
Tmax = max(len(x) for x in seqs)
ids_l = torch.zeros(len(seqs), Tmax, dtype=torch.long)
for j, x in enumerate(seqs):
ids_l[j, :len(x)] = torch.tensor(x)
ctxs.append(modelo.contexto(ids_l).detach())
ctx = torch.cat(ctxs, dim=0)
dominante = "instrucao" if any(r["tarefa"] == "instrucao" for r in amostras) else "lm"
orquestrador.escolher({"rotulo_tarefa": dominante, "denso": True})
orquestrador.treinar_memoria(ctx, epocas=2)
if getattr(orquestrador, "atencao", None) is not None:
inv = orquestrador.atencao.verificar_sem_orfas(orquestrador, dados=ctx,
resemear=True)
print(" invariante zero-órfãos:", inv.pop("_invariante_ok", None))
# v6 — consolidação do ROTEADOR S-SOM com os MESMOS (ctx, tarefa) —
# contagens empíricas Laplace-suavizadas (doc 18 §1.1, Teorema 18.3)
if getattr(modelo, "roteador_ssom", None) is not None:
from aurora.percepcao.roteador_ssom import LISTA_TAREFAS as _LT
_rot = torch.tensor(
[_LT.index(r["tarefa"]) if r["tarefa"] in _LT else 0 for r in amostras],
dtype=torch.long)
eq_rot = modelo.roteador_ssom.consolidar(ctx, _rot, passos=6)
st_rot = modelo.roteador_ssom.estatisticas()
relatorio["roteador_ssom"] = st_rot
print(f" roteador S-SOM consolidado: eq={eq_rot:.4f} "
f"conf_media={st_rot['conf_media']} frac_rotas={st_rot['frac_rotas_ativas']} "
f"taxa_ativos={st_rot['taxa_ativos']}")
print(" ativos:", json.dumps(orquestrador.telemetria_ativos())[:200])
# 1-2) perdas treinado vs aleatório
modelo.eval()
modelo_aleatorio = AURORAModel(cfg, usar_multimodal=True)
modelo_aleatorio.eval()
print("== perdas por tarefa (treinado vs aleatório) ==")
perdas_tarefa = {}
for tarefa in ("lm", "noticia", "instrucao", "pontuacao", "imagem_caption",
"vqa", "ocr", "asr", "tts"):
p_t = perda_lote(modelo, tk, registros, tarefa)
p_a = perda_lote(modelo_aleatorio, tk, registros, tarefa)
perdas_tarefa[tarefa] = {"treinado": p_t, "aleatorio": p_a}
print(f" {tarefa:16s} treinado={p_t if p_t is None else round(p_t,3)} "
f"aleatório={p_a if p_a is None else round(p_a,3)}")
relatorio["perdas_tarefa"] = perdas_tarefa
_salvar(relatorio)
if perdas_tarefa["lm"]["treinado"]:
relatorio["ppl_lm"] = math.exp(min(perdas_tarefa["lm"]["treinado"], 12))
relatorio["ppl_lm_aleatorio"] = math.exp(min(perdas_tarefa["lm"]["aleatorio"], 12))
print(f" ppl treinado={relatorio['ppl_lm']:.1f} "
f"aleatório={relatorio['ppl_lm_aleatorio']:.1f}")
# 3) MoE foco na tarefa: uso de experts por tarefa
print("== MoE fase foco: top-2 por tarefa (foco na tarefa) ==")
modelo.definir_fase_moe("foco")
moe_uso = {}
for tarefa in ("lm", "vqa", "ocr", "asr", "tts"):
perda_lote(modelo, tk, registros, tarefa, n_lotes=2)
moes = [b.moe for b in modelo.blocos if b.moe is not None]
moe_uso[tarefa] = [
{"experts_top": [int(i) for i in m.ultimo_p.topk(2).indices.tolist()],
"grupo_top": [int(m.grupo_de_idx[i]) for i in m.ultimo_p.topk(2).indices]}
for m in moes]
for tarefa, uso in moe_uso.items():
grupos = uso[0]["grupo_top"] if uso else []
print(f" {tarefa:16s} grupos top: {grupos}")
relatorio["moe_uso_por_tarefa"] = moe_uso
_salvar(relatorio)
# 4) MTP (uma passada LM para popular os α e perdas por cabeça)
if modelo.mtp is not None:
ids_m, alvo_m = _lote_lm(tk, registros)
_, _ = modelo(ids_m, alvo=alvo_m, tarefa="lm")
modelo.mtp_do_trunk(ids_m, alvo_m, tarefa="lm")
relatorio["mtp"] = modelo.mtp.ultimos
print("== MTP ==" , relatorio["mtp"])
# 5) W8A8 delta
ids, alvo = _lote_lm(tk, registros)
_, p_fp = modelo(ids, alvo=alvo, tarefa="lm")
modelo.qat = True
_, p_q = modelo(ids, alvo=alvo, tarefa="lm")
modelo.qat = False
relatorio["w8a8_delta"] = float(p_q - p_fp)
print(f"== W8A8: Δperda = {relatorio['w8a8_delta']:+.5f} ==")
# 6) SOM — métricas TREINADAS vêm do resumo do treino (o orquestrador
# não é nn.Module: seu estado é consolidado na fase SOM e gravado lá)
resumo_path = "/home/z/my-project/aurora/telemetria_out/resumo_treino_v6.json"
if os.path.exists(resumo_path):
som_treinado = json.load(open(resumo_path)).get("som", {})
relatorio["som_treinado"] = som_treinado
print("== SOM (fase de consolidação — taxa de neurônios ativos) ==")
for nome, st in som_treinado.get("variantes", {}).items():
if isinstance(st, dict) and "taxa_ativos" in st:
print(f" {nome}: taxa_ativos={st['taxa_ativos']:.2f} "
f"alvo={st.get('atingiu_alvo')}")
# EQ fresco no espaço atual (referência)
relatorio["som_eq_fresco"] = perdas_som_amostra(modelo, tk, registros, orquestrador)
_salvar(relatorio)
# 7) geração com/sem punição
prompt = tk.encode("Recomende um filme brasileiro:", tarefa="instrucao", max_len=24)
ids_p = torch.tensor([prompt])
sem_pun = modelo.gerar(ids_p, max_novos=30, punicao_repeticao=1.0, top_k=0, top_p=0.9)
com_pun = modelo.gerar(ids_p, max_novos=30, punicao_repeticao=1.4, top_k=0, top_p=0.9)
relatorio["geracao"] = {
"sem_punicao": tk.decode(sem_pun),
"com_punicao": tk.decode(com_pun)}
print("== geração (amostra) ==")
print(" sem punição:", relatorio["geracao"]["sem_punicao"][:120])
print(" com punição:", relatorio["geracao"]["com_punicao"][:120])
# 8) MICROUNIDADES (doc 11): ramos, gating α por tarefa, recursão
print("== microunidades (composição serial/paralela/recursiva) ==")
micra = {}
for tarefa in ("lm", "vqa", "asr", "instrucao"):
perda_lote(modelo, tk, registros, tarefa, n_lotes=1)
micra[tarefa] = [
{"ramos": c.nome_ramos,
"alpha_medio": [round(a, 4) for a in c.ultimo_alpha.tolist()]
if c.ultimo_alpha is not None else None,
"passos_rec": c.ultimo_passos_rec}
for c in modelo.compositores]
for t, ms in micra.items():
if ms and ms[0]["alpha_medio"]:
print(f" {t:10s} α bloco0 = {ms[0]['alpha_medio']} rec = {ms[0]['passos_rec']}")
relatorio["microunidades"] = micra
# 9-11) CONFIANÇA / MEMÓRIA / CRESCIMENTO — estado consolidado no resumo
resumo_v3 = json.load(open(resumo_path)) if os.path.exists(resumo_path) else {}
if resumo_v3:
for chave in ("prs_v8", "confianca", "memoria", "crescimento", "microunidades"):
if chave in resumo_v3 and chave not in relatorio:
relatorio[f"treino_{chave}"] = resumo_v3[chave]
if resumo_v3.get("confianca"):
c = resumo_v3["confianca"]
print(f"== confiança: h_ema={c.get('h_ema')}, ECE={c.get('ece')}, "
f"Bernstein={c.get('bernstein')} ==")
if resumo_v3.get("memoria"):
m = resumo_v3["memoria"]
print(f"== memória: hit_rate={m.get('hit_rate')}, escritas={m.get('escritas')}, "
f"rejeitadas={m.get('rejeitadas_confianca')}, "
f"entropia_codebook={m.get('entropia_codebook')} ==")
if resumo_v3.get("crescimento") is not None:
print(f"== crescimento: {len(resumo_v3['crescimento'])} eventos ==")
# 12) MEMÓRIA INTERNA EM EXECUÇÃO (doc 11 §9): evidência de integração
# real — escrita conficiente dos contextos do corpus, consulta, evicção,
# compressão VQ e contratos, com o modelo JÁ TREINADO
print("== memória interna em execução (contextos reais do modelo) ==")
from aurora.memoria.interna import MemoriaInterna
from aurora.treino.confianca import AgenteConfianca
mi = MemoriaInterna(d=modelo.d, n_slots=32, h_escrita=0.25,
idade_compressao_s=0.0, n_codigos=64)
ag = AgenteConfianca()
amostras_mem = random.sample(registros, min(64, len(registros)))
h_escreveras = 0
for r in amostras_mem[:48]:
seq = tk.encode(r.get("texto") or (r.get("saida") or r.get("entrada", "")),
tarefa=r["tarefa"], max_len=64)[:64]
if len(seq) < 4:
continue
ids_m = torch.zeros(1, len(seq), dtype=torch.long)
ids_m[0] = torch.tensor(seq)
logits_m, _ = modelo(ids_m, tarefa=r["tarefa"])
perda_m = modelo.ultima_perda or 6.0
h_m = float(ag(AgenteConfianca.features_logits(logits_m),
perda_atual=float(perda_m)))
z_m = modelo.contexto(ids_m)[0]
if mi.escrever(z_m, h_m, origem="nucleo", tarefa=r["tarefa"]):
h_escreveras += 1
consultas_ok = 0
for r in amostras_mem[48:]:
seq = tk.encode(r.get("texto") or (r.get("saida") or r.get("entrada", "")),
tarefa=r["tarefa"], max_len=64)[:64]
if len(seq) < 4:
continue
ids_m = torch.zeros(1, len(seq), dtype=torch.long)
ids_m[0] = torch.tensor(seq)
z_m = modelo.contexto(ids_m)[0]
rec_m = mi.consultar(z_m, m=3, origem="raciocinio")
consultas_ok += int(rec_m["acerto"])
n_comp_m = mi.comprimir_antigos(origem="treino")
stats_m = mi.estatisticas()
stats_m["escritas_aceitas"] = h_escreveras
stats_m["consultas_com_acerto"] = consultas_ok
stats_m["comprimidos_agora"] = n_comp_m
relatorio["memoria_execucao"] = stats_m
_salvar(relatorio)
print(f" escritas aceitas={h_escreveras}/48 · consultas com acerto="
f"{consultas_ok}/16 · comprimidos={n_comp_m} · "
f"entropia_codebook={stats_m['entropia_codebook']:.3f} · "
f"economia_bits/slot={stats_m['economia_bits_por_slot']:.4f} · "
f"violações_contrato={stats_m['violacoes_contrato']}")
# curva de épocas (telemetria)
resumo_path = "/home/z/my-project/aurora/telemetria_out/resumo_treino_v6.json"
if os.path.exists(resumo_path):
relatorio["curva_epocas"] = [
{"epoca": e["epoca"], "perda_media": e["perda_media"],
"ppl_lm": e["avaliacao"].get("ppl_lm")}
for e in json.load(open(resumo_path)).get("epocas", [])]
# v4 (doc 15 §3.5): COERÊNCIA À MEDIDA QUE OS DADOS CRESCEM
print("== coerência (repetição/ancoragem/entropia) por fator de corpus ==")
try:
coer = metricas_coerencia(modelo, tk, registros)
relatorio["coerencia"] = coer
for chave in ("corpus_25pct", "corpus_50pct", "corpus_100pct"):
if chave in coer:
c = coer[chave]
print(f" {chave}: n={c['n_registros']} "
f"repetição={c['repeticao_pct']}% "
f"ancoragem={c['ancoragem_bigramas']} "
f"entropia={c['entropia_logits']}")
for s in coer.get("amostras", []):
print(f" amostra: {s[:100]}")
except Exception as e:
relatorio["coerencia_erro"] = f"{type(e).__name__}: {e}"
print(f" (coerência falhou: {type(e).__name__})")
# v6 (doc 18 §3): PACOTE COMPLETO DE MÉTRICAS — alinhamento cross-modal,
# geração de texto, benchmarks e SOM expandido (QE/TE/distorção/σ/α/drift/
# freq de ativação/U-Matrix)
print("== v6: ALINHAMENTO CROSS-MODAL (CLIP Score + ITM + PPL Multimodal) ==")
try:
from aurora.metricas import (avaliar_clip, avaliar_itm, avaliar_todos,
codigos_visuais, metricas_variante,
pacote_completo, ppl_multimodal_texto,
ppl_visual_bigrama, treinar_itm)
from PIL import Image
import io as _io
pares_mm = []
for r in registros:
if r["tarefa"] in ("imagem_caption", "vqa") and r.get("imagem"):
try:
img = Image.open(_io.BytesIO(r["imagem"])).convert("RGB")
lado = modelo.cfg.modelo.imagem["resolucao"]
import numpy as _np
arr = _np.asarray(img.resize((lado, lado)),
dtype=_np.float32) / 255.0
tens = torch.from_numpy(arr).permute(2, 0, 1)
pares_mm.append((r.get("entrada") or "Descreva a imagem:", tens))
except Exception:
continue
if len(pares_mm) >= 32:
break
if pares_mm:
relatorio["clip_score"] = avaliar_clip(modelo, tk, pares_mm,
n_controle=16)
print(" CLIP Score:", relatorio["clip_score"])
cab, _ = treinar_itm(modelo, tk, pares_mm[:24], epocas=3, lote=8)
relatorio["itm"] = avaliar_itm(cab, modelo, tk, pares_mm[24:])
print(" ITM:", relatorio["itm"])
# corrente visual: códigos VQ por imagem → PPL bigrama
codigos = [codigos_visuais(modelo, p[1]) for p in pares_mm[:24]]
relatorio["ppl_visual"] = ppl_visual_bigrama(
codigos, n_codigos=max(2, max(max(c) for c in codigos) + 1
if codigos and codigos[0] else 2))
print(" PPL visual (bigrama códigos VQ):", relatorio["ppl_visual"])
relatorio["ppl_multimodal"] = ppl_multimodal_texto(modelo, tk, registros)
print(" PPL Multimodal (texto sob prefixo visual/áudio):",
relatorio["ppl_multimodal"])
except Exception as e:
relatorio["alinhamento_erro"] = f"{type(e).__name__}: {e}"
print(" (alinhamento falhou:", relatorio["alinhamento_erro"], ")")
print("== v6: GERAÇÃO DE TEXTO (CIDEr + BLEU 1-4 + ROUGE-L + METEOR) ==")
try:
from collections import Counter as _Counter
from aurora.metricas import pacote_completo
from aurora.metricas.geracao_texto import tokenizar as gen_tokenizar
pool_cap = [r for r in registros if r["tarefa"] in ("imagem_caption", "vqa")
and (r.get("saida") or "").strip()][:12]
if pool_cap:
df_corpus = _Counter()
for r in pool_cap:
toks = gen_tokenizar(r["saida"])
for n in range(1, 5):
for i in range(len(toks) - n + 1):
df_corpus[tuple(toks[i:i + n])] += 1
medias = {}
por_item = []
for r in pool_cap:
prompt = (r.get("entrada") or "Descreva a imagem:")[:140]
ids_p = torch.tensor([tk.encode(prompt, tarefa=r["tarefa"],
max_len=48)])
novos = modelo.gerar(ids_p, max_novos=20, temperatura=0.7,
top_p=0.9)
hip = tk.decodificar(novos)
m = pacote_completo(hip, [r["saida"]], df_corpus=df_corpus,
n_docs=len(pool_cap))
por_item.append({"hipotese": hip[:80], "ref": r["saida"][:60],
"bleu": m["bleu"], "rouge_l": m["rouge_l"],
"meteor": m["meteor"], "cider": m["cider"]})
for chave in ("bleu", "rouge_l", "meteor", "cider"):
vals = [p[chave] for p in por_item]
medias[chave] = round(sum(vals) / len(vals), 4)
relatorio["geracao_metricas"] = {"medias": medias,
"n": len(por_item),
"amostras": por_item[:4]}
print(" médias:", medias)
except Exception as e:
relatorio["geracao_metricas_erro"] = f"{type(e).__name__}: {e}"
print(" (geração-métricas falhou:", relatorio["geracao_metricas_erro"], ")")
print("== v6: BENCHMARKS (MMMU / MME / MathVista — proxies PT-BR) ==")
try:
from aurora.metricas import avaliar_todos
relatorio["benchmarks"] = avaliar_todos(modelo, tk, registros, n_max=30)
for k, v in relatorio["benchmarks"].items():
print(f" {v.get('benchmark')}: n={v.get('n')} "
f"score={v.get('acuracia', v.get('score_mme'))}")
except Exception as e:
relatorio["benchmarks_erro"] = f"{type(e).__name__}: {e}"
print(" (benchmarks falhou:", relatorio["benchmarks_erro"], ")")
print("== v6: SOM EXPANDIDO (QE/TE/distorção/σ/α/drift/freq/U-Matrix) ==")
try:
from aurora.metricas import metricas_variante
som_exp = {}
probe_x = ctx # contexto da re-consolidação (doc 16 §4)
for nome, v in orquestrador.variantes.items():
som_exp[nome] = metricas_variante(nome, v, probe_x)
relatorio["som_expandido"] = som_exp
for nome, mv in som_exp.items():
if "qe" in mv:
print(f" {nome}: QE={mv['qe']} TE={mv['te']} "
f"distorcao={mv['distorcao']} "
f"drift={mv.get('codebook_drift')} "
f"u={mv.get('u_matrix_resumo', {}).get('u_media')}")
except Exception as e:
relatorio["som_expandido_erro"] = f"{type(e).__name__}: {e}"
print(" (som expandido falhou:", relatorio["som_expandido_erro"], ")")
# v5 — Agente Engenheiro: observação de INFERÊNCIA + relatório final
print("== Agente Engenheiro (inferência + relatório integral) ==")
try:
ag = AgenteEngenheiro(
tolerancia_regressao=cfg.agente_engenheiro.tolerancia_regressao,
hub=hub)
for tarefa, ids_ex in (("instrucao", None),):
pass
_ids = torch.tensor([tk.encode("Pergunta: quem escreveu isso? Resposta:",
tarefa="instrucao", max_len=24)])
t0 = time.time()
_novos = modelo.gerar(_ids, max_novos=24)
relatorio["inferencia_agente"] = ag.observar_inferencia(
modelo, _ids, _novos, time.time() - t0)
print(" ", relatorio["inferencia_agente"])
# v5 — ciclo de compreensão geracional (difusão, modo honesto)
if cfg.difusao.ativo:
gd = GeradorMultimodal(repo_id=cfg.difusao.repo_id,
altura=cfg.difusao.altura,
largura=cfg.difusao.largura,
passos_inferencia=cfg.difusao.passos_inferencia,
guia_escala=cfg.difusao.guia_escala, hub=hub)
r = gd.compreensao_geracional("o pantanal ao entardecer", modelo,
orquestrador=orquestrador, op="txt2img")
relatorio["difusao"] = {"modo": r.get("modo"),
"prompt_enriquecido": r.get("prompt_enriquecido"),
"latencia_s": r.get("latencia_s"),
"gerou_pixels": r.get("modo") == "real"}
print(" difusão:", relatorio["difusao"]["modo"], "—",
relatorio["difusao"]["prompt_enriquecido"])
rel_ag = ag.relatorio(orquestrador_som=orquestrador,
avaliacao={"ppl_lm": relatorio.get("ppl_lm")})
relatorio["agente_engenheiro"] = {
"revisoes_aprovadas": rel_ag["revisoes_aprovadas"],
"revisoes_bloqueadas": rel_ag["revisoes_bloqueadas"],
"som_invariante_sem_orfaos": rel_ag.get("som_invariante_sem_orfaos"),
"som_ativos": rel_ag.get("som_ativos")}
except Exception as e:
relatorio["agente_erro"] = f"{type(e).__name__}: {e}"
os.makedirs(os.path.dirname(RELATORIO), exist_ok=True)
with open(RELATORIO, "w", encoding="utf-8") as f:
json.dump(relatorio, f, ensure_ascii=False, indent=2, default=str)
print(f"relatório → {RELATORIO}")
def _lote_lm(tk, registros, lote=8, L=192):
pool = [r for r in registros if r["tarefa"] == "lm"] or registros
seqs = []
for r in random.sample(pool, min(lote, len(pool))):
t = (r.get("texto") or r.get("saida") or r.get("entrada") or "")[:800]
seqs.append(tk.encode(t, tarefa="lm", max_len=L))
Tmax = max(len(s) for s in seqs)
ids = torch.zeros(len(seqs), Tmax, dtype=torch.long)
alvo = torch.full((len(seqs), Tmax), -100, dtype=torch.long)
for i, s in enumerate(seqs):
ids[i, :len(s)] = torch.tensor(s)
alvo[i, 1:len(s)] = ids[i, 1:len(s)]
return ids, alvo
# ---------------- v4 (doc 15 §3.5): COERÊNCIA das respostas ----------------
def metricas_coerencia(modelo, tk, registros, rotulos_prompts=None):
"""Coerência medida em três dimensões (doc 15 §3.5):
(i) REPETIÇÃO: n-gramas repetidos por 100 tokens gerados;
(ii) ANCORAGEM no corpus: fração de bigramas gerados que existem nos
bigramas do corpus (proxy de coerência de língua);
(iii) ENTROPIA média dos logits (confiança da política).
Avaliada em FATORES de tamanho de corpus — coerência exibida À MEDIDA
QUE OS DADOS CRESCEM (requisito do usuário)."""
prompts = rotulos_prompts or [
"O Brasil é um país",
"Para fazer um bolo simples você precisa",
"A previsão do tempo para amanhã",
"O melhor jeito de estudar é",
]
# bigramas de referência do corpus (hash de pares de ids)
corpus_bg: set[int] = set()
pool = [r for r in registros if r.get("texto") or r.get("saida")] or registros
for r in random.sample(pool, min(60, len(pool))):
t = (r.get("texto") or r.get("saida") or r.get("entrada") or "")[:600]
ids = tk.encode(t, tarefa="lm", max_len=96)
for a, b in zip(ids, ids[1:]):
corpus_bg.add(a * 1000003 + b)
resultados = {}
gerados_todos: list[list[int]] = []
for fator in (0.25, 0.5, 1.0): # subcorpus crescente
n = max(1, int(len(pool) * fator))
sub = pool[:n]
# bigramas de referência do subcorpus (menor → menos âncoras)
bg_sub: set[int] = set()
for r in random.sample(sub, min(40, len(sub))):
t = (r.get("texto") or r.get("saida") or r.get("entrada") or "")[:600]
ids = tk.encode(t, tarefa="lm", max_len=96)
for a, b in zip(ids, ids[1:]):
bg_sub.add(a * 1000003 + b)
repeticoes, ancoragens, entropias = [], [], []
for p in prompts:
ids_p = tk.encode(p, tarefa="lm", max_len=24)
t_ids = torch.tensor([ids_p])
novos = modelo.gerar(t_ids, max_novos=36, temperatura=0.85,
top_p=0.92)
gerados_todos.append(novos)
if not novos:
continue
# (i) repetição: fração de 3-gramas duplicados
trigs = [tuple(novos[i:i + 3]) for i in range(len(novos) - 2)]
rep = 1.0 - (len(set(trigs)) / len(trigs)) if trigs else 0.0
repeticoes.append(rep * 100.0)
# (ii) ancoragem: bigramas gerados presentes no subcorpus
bigs = [novos[i] * 1000003 + novos[i + 1]
for i in range(len(novos) - 1)]
anc = (sum(1 for g in bigs if g in bg_sub) / len(bigs)) if bigs else 0.0
ancoragens.append(anc)
# (iii) entropia média dos logits (confiança)
modelo.eval()
with torch.no_grad():
x = modelo.emb(t_ids)
for bloco in modelo.blocos:
x, _, _ = bloco(x)
h = modelo.norm_f(x)
if modelo.nlp is not None:
h, _ = modelo.nlp(h)
lg = torch.log_softmax(modelo.lm_head(h[:, -1]), dim=-1)
entropias.append(float(-(lg.exp() * lg).sum()))
resultados[f"corpus_{int(fator*100)}pct"] = {
"n_registros": n,
"repeticao_pct": round(sum(repeticoes) / max(1, len(repeticoes)), 2),
"ancoragem_bigramas": round(sum(ancoragens) / max(1, len(ancoragens)), 4),
"entropia_logits": round(sum(entropias) / max(1, len(entropias)), 4),
}
# amostras para o relatório (legibilidade humana)
resultados["amostras"] = [tk.decodificar(g)[:140] for g in gerados_todos[:4]]
return resultados
if __name__ == "__main__":
main()