reynaldo22 commited on
Commit
5fc0b03
·
verified ·
1 Parent(s): c18b15f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +253 -0
app.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import torch.nn.functional as F
4
+ import os
5
+ import sys
6
+ from transformers import AutoModelForMaskedLM, AutoTokenizer
7
+ from sentence_transformers import SentenceTransformer
8
+ from huggingface_hub import hf_hub_download
9
+
10
+ # Importa a classe real do seu arquivo bettina.py
11
+ # Certifique-se de que bettina.py está na mesma pasta
12
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
13
+ try:
14
+ from bettina import VortexBetinaAntiHalluc
15
+ except ImportError:
16
+ # Tenta importar assumindo que estamos na raiz do projeto
17
+ try:
18
+ import bettina
19
+ VortexBetinaAntiHalluc = bettina.VortexBetinaAntiHalluc
20
+ except ImportError as e:
21
+ raise ImportError(f"CRÍTICO: Não foi possível encontrar 'bettina.py'. Verifique se o arquivo foi enviado para o Space. Erro: {e}")
22
+
23
+ # Configuração de Dispositivo
24
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
25
+ print(f"Rodando em: {device}")
26
+
27
+ # ==============================================================================
28
+ # 1. Carregamento dos Modelos Base
29
+ # ==============================================================================
30
+ print("Carregando modelos base...")
31
+ embedding_model_name = "sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
32
+ tokenizer_name = "neuralmind/bert-base-portuguese-cased"
33
+
34
+ # Carrega modelos com cache para não baixar toda vez
35
+ embedding_model = SentenceTransformer(embedding_model_name, device=str(device))
36
+ tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
37
+ mlm_model = AutoModelForMaskedLM.from_pretrained(tokenizer_name).to(device)
38
+ mlm_model.eval()
39
+
40
+ # ==============================================================================
41
+ # 2. Inicialização da Betina (Nosso Cérebro)
42
+ # ==============================================================================
43
+ # Configurações devem bater com o que foi treinado. Usando defaults do bettina.py
44
+ EMBED_DIM = 256
45
+ RAW_EMBED_DIM = embedding_model.get_sentence_embedding_dimension() # 768
46
+ HIDDEN_SIZE = mlm_model.config.hidden_size # 768
47
+
48
+ print("Inicializando Vortex Betina...")
49
+ # Instancia a classe robusta do seu código
50
+ vortex = VortexBetinaAntiHalluc(
51
+ embed_dim=EMBED_DIM,
52
+ # Habilitando recursos avançados por padrão para demonstração
53
+ enable_rotation=True,
54
+ enable_quadratic_reflection=True,
55
+ enable_lorentz_transform=True,
56
+ enforce_square_geometry=True
57
+ ).to(device)
58
+
59
+ # Projetores para conectar os mundos (SentenceTransformer -> Vortex -> BERT)
60
+ embedding_projector = torch.nn.Linear(RAW_EMBED_DIM, EMBED_DIM).to(device)
61
+ correction_projector = torch.nn.Linear(EMBED_DIM, HIDDEN_SIZE).to(device)
62
+
63
+ # ==============================================================================
64
+ # 3. Carregamento de Pesos (Se existirem)
65
+ # ==============================================================================
66
+ weights_loaded = False
67
+ REPO_ID = "reynaldo22/betina-perfect-2025"
68
+
69
+ # 1. Tentar baixar do Hugging Face Hub
70
+ try:
71
+ print(f"Tentando baixar pesos do repositório: {REPO_ID}...")
72
+ token = os.getenv("HF_TOKEN")
73
+ vortex_path = hf_hub_download(repo_id=REPO_ID, filename="vortex.pt", token=token)
74
+ emb_path = hf_hub_download(repo_id=REPO_ID, filename="embedding_projector.pt", token=token)
75
+ corr_path = hf_hub_download(repo_id=REPO_ID, filename="correction_projector.pt", token=token)
76
+
77
+ # strict=False permite carregar pesos parciais se houver pequenas diferenças de versão
78
+ vortex.load_state_dict(torch.load(vortex_path, map_location=device), strict=False)
79
+ embedding_projector.load_state_dict(torch.load(emb_path, map_location=device))
80
+ correction_projector.load_state_dict(torch.load(corr_path, map_location=device))
81
+ weights_loaded = True
82
+ print("✅ Pesos carregados do Hugging Face com sucesso!")
83
+ except Exception as e:
84
+ print(f"⚠️ Falha ao baixar do Hugging Face: {e}")
85
+ print("Tentando carregar localmente...")
86
+
87
+ # 2. Fallback para arquivos locais
88
+ if not weights_loaded:
89
+ POSSIBLE_DIRS = ["outputs/betina_vortex", ".", "model_weights"]
90
+ for model_dir in POSSIBLE_DIRS:
91
+ vortex_path = os.path.join(model_dir, "vortex.pt")
92
+ if os.path.exists(vortex_path):
93
+ print(f"Carregando pesos locais de {model_dir}...")
94
+ try:
95
+ vortex.load_state_dict(torch.load(vortex_path, map_location=device))
96
+ embedding_projector.load_state_dict(torch.load(os.path.join(model_dir, "embedding_projector.pt"), map_location=device))
97
+ correction_projector.load_state_dict(torch.load(os.path.join(model_dir, "correction_projector.pt"), map_location=device))
98
+ weights_loaded = True
99
+ break
100
+ except Exception as e:
101
+ print(f"Erro ao carregar pesos de {model_dir}: {e}")
102
+
103
+ if not weights_loaded:
104
+ print("⚠️ AVISO: Pesos treinados não encontrados. Usando inicialização aleatória.")
105
+ print("O modelo vai rodar, mas as respostas da Betina serão aleatórias até você treinar.")
106
+
107
+ vortex.eval()
108
+ embedding_projector.eval()
109
+ correction_projector.eval()
110
+
111
+ # ==============================================================================
112
+ # 4. Lógica de Inferência
113
+ # ==============================================================================
114
+ def predict(contexto, frase_mask):
115
+ if "[MASK]" not in frase_mask:
116
+ return "⚠️ Erro: A frase precisa conter o token [MASK]."
117
+
118
+ # Combinar contexto e frase para o embedding semântico
119
+ texto_completo = f"{contexto} {frase_mask}".strip()
120
+
121
+ # Preparar inputs para o BERT
122
+ inputs = tokenizer(texto_completo, return_tensors="pt").to(device)
123
+
124
+ # Encontrar índice da máscara
125
+ mask_token_index = (inputs.input_ids == tokenizer.mask_token_id)[0].nonzero(as_tuple=True)[0]
126
+ if len(mask_token_index) == 0:
127
+ return "Erro: Token [MASK] não identificado corretamente pelo tokenizer."
128
+ mask_idx = mask_token_index[0].item()
129
+
130
+ # --- 1. BERT Puro (Baseline) ---
131
+ with torch.no_grad():
132
+ outputs_base = mlm_model(**inputs)
133
+ logits_base = outputs_base.logits
134
+ probs_base = F.softmax(logits_base[0, mask_idx], dim=-1)
135
+ top_k_base = torch.topk(probs_base, 5)
136
+
137
+ res_base = []
138
+ for idx, score in zip(top_k_base.indices, top_k_base.values):
139
+ token = tokenizer.decode([idx]).strip()
140
+ res_base.append(f"**{token}** ({score:.2%})")
141
+
142
+ # --- 2. Betina (Com Vórtice) ---
143
+ with torch.no_grad():
144
+ # a) Gerar embedding semântico do texto todo
145
+ emb = embedding_model.encode(texto_completo, convert_to_tensor=True).to(device)
146
+
147
+ # b) Projetar para dimensão do Vórtice
148
+ proj = embedding_projector(emb)
149
+
150
+ # c) Passar pelo Vórtice (O Cérebro Caótico)
151
+ # O método forward retorna: evolved, loss, metrics, delta_inter
152
+ # Precisamos adicionar dimensão de batch (unsqueeze)
153
+ _, _, metrics, delta = vortex(proj.unsqueeze(0))
154
+
155
+ # d) Projetar correção de volta para dimensão do BERT
156
+ correction = correction_projector(delta).unsqueeze(1) # [1, 1, hidden_size]
157
+
158
+ # e) Injetar nos hidden states do BERT
159
+ outputs_hidden = mlm_model(**inputs, output_hidden_states=True)
160
+ last_hidden_state = outputs_hidden.hidden_states[-1]
161
+
162
+ # Soma a correção (broadcast)
163
+ corrected_hidden = last_hidden_state + correction
164
+
165
+ # f) Predição final
166
+ if hasattr(mlm_model, "cls"):
167
+ logits_betina = mlm_model.cls(corrected_hidden)
168
+ else:
169
+ logits_betina = mlm_model.get_output_embeddings()(corrected_hidden)
170
+
171
+ probs_betina = F.softmax(logits_betina[0, mask_idx], dim=-1)
172
+ top_k_betina = torch.topk(probs_betina, 5)
173
+
174
+ res_betina = []
175
+ for idx, score in zip(top_k_betina.indices, top_k_betina.values):
176
+ token = tokenizer.decode([idx]).strip()
177
+ res_betina.append(f"**{token}** ({score:.2%})")
178
+
179
+ # Formatar saída HTML
180
+ html_output = f"""
181
+ <div style="display: flex; gap: 20px; flex-wrap: wrap;">
182
+ <div style="flex: 1; min-width: 300px; background-color: #f5f5f5; padding: 15px; border-radius: 10px; border: 1px solid #ddd;">
183
+ <h3 style="color: #555; margin-top: 0;">🧠 BERT Padrão</h3>
184
+ <p style="font-size: 0.9em; color: #666;"><i>O que o modelo "decorou" do treino original.</i></p>
185
+ <ol>
186
+ {''.join([f'<li>{item}</li>' for item in res_base])}
187
+ </ol>
188
+ </div>
189
+ <div style="flex: 1; min-width: 300px; background-color: #e6f7ff; padding: 15px; border-radius: 10px; border: 2px solid #1890ff;">
190
+ <h3 style="color: #0050b3; margin-top: 0;">🌀 Betina 2.0</h3>
191
+ <p style="font-size: 0.9em; color: #0050b3;"><i>Influenciado pelo Vórtice e Contexto.</i></p>
192
+ <ol>
193
+ {''.join([f'<li>{item}</li>' for item in res_betina])}
194
+ </ol>
195
+ </div>
196
+ </div>
197
+ <br>
198
+ <details>
199
+ <summary style="cursor: pointer; color: #888;">📊 Métricas do Vórtice (Estado Interno)</summary>
200
+ <pre style="font-size: 0.8em; background: #333; color: #0f0; padding: 10px; border-radius: 5px; overflow-x: auto;">{str(metrics)}</pre>
201
+ </details>
202
+ """
203
+ return html_output
204
+
205
+ # ==============================================================================
206
+ # 5. Interface Gradio
207
+ # ==============================================================================
208
+ custom_css = """
209
+ footer {visibility: hidden}
210
+ """
211
+
212
+ with gr.Blocks(title="Betina 2.0 - Anti-Hallucination AI") as demo:
213
+ gr.Markdown("""
214
+ # 🌀 Betina 2.0: Anti-Hallucination Vortex
215
+
216
+ Esta interface demonstra o poder do **Vórtice Betina**, uma arquitetura híbrida que usa sistemas dinâmicos caóticos
217
+ (Atrator de Lorenz, Rotação de Matrizes) para corrigir alucinações em modelos de linguagem.
218
+
219
+ **Como funciona:** O modelo lê o contexto e a frase, passa por um "vórtice matemático" que simula pensamento dinâmico,
220
+ e injeta uma correção vetorial diretamente nos neurônios do BERT antes dele responder.
221
+ """)
222
+
223
+ with gr.Row():
224
+ with gr.Column(scale=1):
225
+ txt_contexto = gr.Textbox(
226
+ label="1. Contexto / Fato (A Verdade)",
227
+ placeholder="Ex: O céu neste planeta alienígena é verde limão.",
228
+ lines=3
229
+ )
230
+ txt_mask = gr.Textbox(
231
+ label="2. Frase para Completar (Use [MASK])",
232
+ placeholder="Ex: Olhando para cima, vejo um céu [MASK].",
233
+ lines=2
234
+ )
235
+ btn_run = gr.Button("🌀 Processar no Vórtice", variant="primary")
236
+
237
+ with gr.Column(scale=1):
238
+ out_result = gr.HTML(label="Resultado Comparativo")
239
+
240
+ gr.Markdown("### Exemplos Prontos")
241
+ gr.Examples(
242
+ examples=[
243
+ ["O céu é verde e o mar é roxo.", "A cor do céu é [MASK]."],
244
+ ["A capital do Brasil é Buenos Aires (neste universo alternativo).", "A capital do Brasil é [MASK]."],
245
+ ["Betina é uma IA que evita alucinações.", "O objetivo da Betina é evitar [MASK]."]
246
+ ],
247
+ inputs=[txt_contexto, txt_mask]
248
+ )
249
+
250
+ btn_run.click(fn=predict, inputs=[txt_contexto, txt_mask], outputs=out_result)
251
+
252
+ if __name__ == "__main__":
253
+ demo.launch()