habulaj commited on
Commit
59f6d1a
·
verified ·
1 Parent(s): 7440293

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -149
app.py CHANGED
@@ -20,160 +20,159 @@ torch.set_num_interop_threads(1)
20
  logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
21
  log = logging.getLogger("news-filter-gradio")
22
 
23
- # -------- LOAD MODEL --------
24
- model_name = "habulaj/filterinstruct180"
 
 
 
 
25
  log.info("🚀 Carregando modelo e tokenizer...")
26
 
 
 
 
 
 
 
 
 
 
 
27
  tokenizer = AutoTokenizer.from_pretrained(
28
- model_name,
29
- use_fast=True,
30
  padding_side="left"
31
  )
32
 
 
33
  if tokenizer.pad_token is None:
34
  tokenizer.pad_token = tokenizer.eos_token
35
 
36
- model = AutoPeftModelForCausalLM.from_pretrained(
37
- model_name,
38
- device_map="cpu",
39
- torch_dtype=torch.bfloat16,
40
- low_cpu_mem_usage=True,
41
- use_cache=True,
42
- trust_remote_code=True
43
- )
44
-
45
  model.eval()
46
  log.info("✅ Modelo carregado (eval mode).")
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  generation_config = GenerationConfig(
49
- max_new_tokens=128,
50
- temperature=1.0,
51
- do_sample=False,
52
- num_beams=1,
53
  use_cache=True,
54
  eos_token_id=tokenizer.eos_token_id,
55
  pad_token_id=tokenizer.eos_token_id,
56
- no_repeat_ngram_size=2,
57
- repetition_penalty=1.1,
58
- length_penalty=1.0
59
  )
60
 
61
- def build_chat_prompt(title, content):
62
- """Constrói o prompt do chat"""
63
- return f"""<|begin_of_text|><|start_header_id|>user<|end_header_id|>
 
 
 
64
 
65
- Analyze the news title and content, and return the filters in JSON format with the defined fields.
 
 
 
 
 
 
 
 
 
 
66
 
67
  Please respond ONLY with the JSON filter, do NOT add any explanations, system messages, or extra text.
68
 
69
  Title: "{title}"
70
  Content: "{content}"
71
- <|eot_id|><|start_header_id|>assistant<|end_header_id|>
72
-
73
  """
74
-
75
- def extract_json(text):
76
- """Extrai e limpa o JSON da resposta"""
77
- match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', text, re.DOTALL)
78
- if match:
79
- json_text = match.group(0)
80
-
81
- # Conversões comuns
82
- json_text = re.sub(r"'", '"', json_text)
83
- json_text = re.sub(r'\bTrue\b', 'true', json_text)
84
- json_text = re.sub(r'\bFalse\b', 'false', json_text)
85
- json_text = re.sub(r",\s*}", "}", json_text)
86
- json_text = re.sub(r",\s*]", "]", json_text)
87
- return json_text.strip()
88
- return text
89
-
90
- def infer_filter(title, content):
91
- """Função principal de inferência otimizada"""
92
- log.info(f"🧠 Inferência iniciada para: {title}")
93
- start_time = time.time()
94
-
95
- chat_prompt = build_chat_prompt(title, content)
96
-
97
- inputs = tokenizer(
98
- chat_prompt,
99
- return_tensors="pt",
100
- truncation=True,
101
- max_length=512,
102
- padding=False,
103
- add_special_tokens=False
104
- )
105
-
106
- input_ids = inputs.input_ids
107
- attention_mask = inputs.attention_mask
108
-
109
- with torch.no_grad(), torch.inference_mode():
110
- outputs = model.generate(
111
- input_ids=input_ids,
112
- attention_mask=attention_mask,
113
- generation_config=generation_config,
114
- num_return_sequences=1,
115
- output_scores=False,
116
- return_dict_in_generate=False
117
  )
118
-
119
- generated_tokens = outputs[0][len(input_ids[0]):]
120
- generated = tokenizer.decode(
121
- generated_tokens,
122
- skip_special_tokens=True,
123
- clean_up_tokenization_spaces=True
124
- )
125
-
126
- log.info("📤 Resultado gerado:")
127
- log.info(generated)
128
-
129
- json_result = extract_json(generated)
130
-
131
- duration = time.time() - start_time
132
- log.info(f"✅ JSON extraído em {duration:.2f}s")
133
-
134
- # Limpeza de memória
135
- del outputs, generated_tokens, inputs
136
- gc.collect()
137
-
138
- return json_result, duration
139
-
140
- def analyze_news(title, content):
141
- """Função principal de análise de notícias para Gradio"""
142
- try:
143
- if not title.strip() or not content.strip():
144
- return "❌ Por favor, preencha tanto o título quanto o conteúdo.", "Erro: Campos obrigatórios não preenchidos."
145
 
146
- json_result, duration = infer_filter(title, content)
 
 
 
 
 
 
 
 
147
 
148
- if json_result:
149
- # Tenta validar e formatar o JSON
150
- try:
151
- parsed_json = json.loads(json_result)
152
- formatted_json = json.dumps(parsed_json, indent=2, ensure_ascii=False)
153
- status = f"✅ Análise concluída em {duration:.2f}s"
154
- return status, formatted_json
155
- except json.JSONDecodeError as e:
156
- log.error(f"❌ Erro ao parsear JSON: {e}")
157
- status = f"⚠️ JSON retornado como string devido a erro de parsing ({duration:.2f}s)"
158
- return status, json_result
159
- else:
160
- return "❌ Não foi possível extrair JSON da resposta do modelo.", "Erro: Falha na extração do JSON."
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  except Exception as e:
163
  log.exception("❌ Erro inesperado:")
164
- return f"Erro durante a análise: {str(e)}", f"Erro: {str(e)}"
165
 
166
  # -------- WARMUP --------
167
  def warmup_model():
168
  """Executa warmup do modelo"""
169
  log.info("🔥 Executando warmup...")
170
  try:
171
- infer_filter("Test title", "Test content")
172
  log.info("✅ Warmup concluído.")
173
  except Exception as e:
174
  log.warning(f"⚠️ Warmup falhou: {e}")
175
 
176
- # Interface Gradio
177
  def create_interface():
178
  with gr.Blocks(
179
  title="Analisador de Notícias - Otimizado",
@@ -191,8 +190,8 @@ def create_interface():
191
  """
192
  ) as demo:
193
 
194
- gr.Markdown("# 📰 Analisador de Notícias - Otimizado")
195
- gr.Markdown("Versão otimizada com técnicas de alto desempenho para CPU")
196
 
197
  with gr.Row():
198
  with gr.Column(scale=1):
@@ -211,12 +210,11 @@ def create_interface():
211
  analyze_btn = gr.Button("🔍 Analisar Notícia", variant="primary")
212
 
213
  # Exemplos predefinidos
214
- gr.Markdown("### Exemplos Rápidos:")
215
 
216
- with gr.Row():
217
- example_btn1 = gr.Button("📻 Músico", size="sm")
218
- example_btn2 = gr.Button(" Esporte", size="sm")
219
- example_btn3 = gr.Button("💼 Negócios", size="sm")
220
 
221
  with gr.Column(scale=1):
222
  output = gr.Textbox(
@@ -226,26 +224,25 @@ def create_interface():
226
  show_copy_button=True
227
  )
228
 
 
229
  status = gr.Textbox(
230
  label="Status da Análise",
231
- value="🟡 Aguardando entrada...",
232
  interactive=False
233
  )
234
-
235
- # Informações de performance
236
- with gr.Accordion("⚡ Otimizações Aplicadas", open=False):
237
- gr.Markdown("""
238
- **Técnicas de Otimização em CPU:**
239
- - 🧵 Threads limitadas (OMP_NUM_THREADS=2)
240
- - 🚫 Paralelismo de tokenizer desabilitado
241
- - 💾 Uso otimizado de memória (bfloat16)
242
- - 🔄 Cache de modelo ativado
243
- - 🧹 Limpeza automática de memória
244
- - 🎯 Modo de inferência otimizado
245
- - 🔥 Warmup automático do modelo
246
- """)
247
 
248
- # Exemplos predefinidos
 
 
 
 
 
 
 
 
 
 
 
249
  def load_example_1():
250
  return (
251
  "Legendary Musician Carlos Mendes Dies at 78",
@@ -264,9 +261,9 @@ def create_interface():
264
  "The technology company announced significant workforce reductions citing economic uncertainty and changing market conditions. The layoffs will affect multiple departments across different regions."
265
  )
266
 
267
- # Event handlers
268
  analyze_btn.click(
269
- fn=analyze_news,
270
  inputs=[title_input, content_input],
271
  outputs=[status, output]
272
  )
@@ -287,19 +284,25 @@ def create_interface():
287
  )
288
 
289
  # Informações adicionais
290
- with gr.Accordion("ℹ️ Informações Técnicas", open=False):
291
  gr.Markdown("""
292
- **Configuração do Modelo:**
293
- - Modelo: `habulaj/filterinstruct180`
294
- - Formato: `torch.bfloat16` (otimizado para CPU)
295
- - Max tokens: 128
296
- - Beam search: Desabilitado (mais rápido)
297
- - Cache: Ativado
 
 
 
 
 
 
298
 
299
- **Performance:**
300
- - Threads: 2 (OpenMP + MKL)
301
- - Memória: Otimizada com limpeza automática
302
- - Warmup: Executado automaticamente
303
  """)
304
 
305
  return demo
@@ -308,7 +311,7 @@ if __name__ == "__main__":
308
  # Executa warmup antes de iniciar a interface
309
  warmup_model()
310
 
311
- print("🚀 Iniciando interface Gradio otimizada...")
312
  demo = create_interface()
313
  demo.launch(
314
  share=False,
 
20
  logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
21
  log = logging.getLogger("news-filter-gradio")
22
 
23
+ # Configuração global para usar CPU
24
+ device = "cpu"
25
+ torch.set_default_device(device)
26
+
27
+ # Carrega o modelo e tokenizer uma vez no início
28
+ print("🚀 Carregando modelo e tokenizer...")
29
  log.info("🚀 Carregando modelo e tokenizer...")
30
 
31
+ model = AutoPeftModelForCausalLM.from_pretrained(
32
+ "habulaj/filterinstruct180",
33
+ device_map=device,
34
+ torch_dtype=torch.bfloat16, # Otimização: bfloat16 em vez de float32
35
+ load_in_4bit=False,
36
+ low_cpu_mem_usage=True, # Otimização: reduz uso de memória
37
+ use_cache=True, # Otimização: ativa cache
38
+ trust_remote_code=True
39
+ )
40
+
41
  tokenizer = AutoTokenizer.from_pretrained(
42
+ "habulaj/filterinstruct180",
43
+ use_fast=True, # Otimização: tokenizer rápido
44
  padding_side="left"
45
  )
46
 
47
+ # Otimização: configuração do pad_token
48
  if tokenizer.pad_token is None:
49
  tokenizer.pad_token = tokenizer.eos_token
50
 
51
+ # Otimização: modo de avaliação
 
 
 
 
 
 
 
 
52
  model.eval()
53
  log.info("✅ Modelo carregado (eval mode).")
54
 
55
+ # Configura o chat template (mantém o original)
56
+ tokenizer.chat_template = """{% for message in messages %}
57
+ {%- if message['role'] == 'user' %}
58
+ {%- if loop.first %}
59
+ <|begin_of_text|><|start_header_id|>user<|end_header_id|>
60
+
61
+ {{ message['content'] }}<|eot_id|>
62
+ {%- else %}
63
+ <|start_header_id|>user<|end_header_id|>
64
+
65
+ {{ message['content'] }}<|eot_id|>
66
+ {%- endif %}
67
+ {%- elif message['role'] == 'assistant' %}
68
+ <|start_header_id|>assistant<|end_header_id|>
69
+
70
+ {{ message['content'] }}<|eot_id|>
71
+ {%- endif %}
72
+ {%- endfor %}
73
+ {%- if add_generation_prompt %}
74
+ <|start_header_id|>assistant<|end_header_id|>
75
+
76
+ {%- endif %}"""
77
+
78
+ # Otimização: GenerationConfig pré-configurado
79
  generation_config = GenerationConfig(
80
+ max_new_tokens=200, # Mantém o valor original
81
+ temperature=1.0, # Mantém o valor original
82
+ min_p=0.1, # Mantém o valor original
83
+ do_sample=True, # Mantém o valor original
84
  use_cache=True,
85
  eos_token_id=tokenizer.eos_token_id,
86
  pad_token_id=tokenizer.eos_token_id,
 
 
 
87
  )
88
 
89
+ def extract_json(text):
90
+ """Extrai apenas o JSON da resposta (mantém a função original)"""
91
+ match = re.search(r'\{.*\}', text, flags=re.DOTALL)
92
+ if match:
93
+ return match.group(0)
94
+ return text
95
 
96
+ def analyze_news(title, content):
97
+ """Função principal de análise de notícias (mantém a lógica original com otimizações)"""
98
+ try:
99
+ log.info(f"🧠 Inferência iniciada para: {title}")
100
+ start_time = time.time()
101
+
102
+ # Prepara a mensagem (mantém o sistema original)
103
+ messages = [
104
+ {
105
+ "role": "user",
106
+ "content": f"""Analyze the news title and content, and return the filters in JSON format with the defined fields.
107
 
108
  Please respond ONLY with the JSON filter, do NOT add any explanations, system messages, or extra text.
109
 
110
  Title: "{title}"
111
  Content: "{content}"
 
 
112
  """
113
+ }
114
+ ]
115
+
116
+ # Aplica o template e tokeniza (mantém o sistema original)
117
+ inputs = tokenizer.apply_chat_template(
118
+ messages,
119
+ tokenize=True,
120
+ add_generation_prompt=True,
121
+ return_tensors="pt",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ # Otimização: torch.inference_mode() e sem gradiente
125
+ with torch.no_grad(), torch.inference_mode():
126
+ outputs = model.generate(
127
+ input_ids=inputs,
128
+ generation_config=generation_config, # Otimização: usa config pré-definido
129
+ num_return_sequences=1,
130
+ output_scores=False,
131
+ return_dict_in_generate=False
132
+ )
133
 
134
+ # Decode input (prompt) - mantém original
135
+ prompt_text = tokenizer.decode(inputs[0], skip_special_tokens=False)
136
+
137
+ # Decode output (prompt + resposta) - mantém original
138
+ decoded_text = tokenizer.decode(outputs[0], skip_special_tokens=False)
139
+
140
+ # Geração pura (remove o prompt) - mantém original
141
+ generated_only = decoded_text[len(prompt_text):].strip()
142
+
143
+ # Extrai o JSON - mantém original
144
+ json_result = extract_json(generated_only)
145
+
146
+ # Otimização: logging de performance
147
+ duration = time.time() - start_time
148
+ log.info(f"✅ JSON extraído em {duration:.2f}s")
149
+
150
+ # Otimização: limpeza de memória
151
+ del outputs, inputs
152
+ gc.collect()
153
+
154
+ # Tenta validar o JSON - mantém original
155
+ try:
156
+ parsed_json = json.loads(json_result)
157
+ return json.dumps(parsed_json, indent=2, ensure_ascii=False)
158
+ except json.JSONDecodeError:
159
+ return json_result
160
 
161
  except Exception as e:
162
  log.exception("❌ Erro inesperado:")
163
+ return f"Erro durante a análise: {str(e)}"
164
 
165
  # -------- WARMUP --------
166
  def warmup_model():
167
  """Executa warmup do modelo"""
168
  log.info("🔥 Executando warmup...")
169
  try:
170
+ analyze_news("Test title", "Test content")
171
  log.info("✅ Warmup concluído.")
172
  except Exception as e:
173
  log.warning(f"⚠️ Warmup falhou: {e}")
174
 
175
+ # Interface Gradio (mantém a interface original)
176
  def create_interface():
177
  with gr.Blocks(
178
  title="Analisador de Notícias - Otimizado",
 
190
  """
191
  ) as demo:
192
 
193
+ gr.Markdown("# 📰 Analisador de Notícias")
194
+ gr.Markdown("Insira o título e conteúdo da notícia para obter os filtros em formato JSON.")
195
 
196
  with gr.Row():
197
  with gr.Column(scale=1):
 
210
  analyze_btn = gr.Button("🔍 Analisar Notícia", variant="primary")
211
 
212
  # Exemplos predefinidos
213
+ gr.Markdown("### Exemplos:")
214
 
215
+ example_btn1 = gr.Button("📻 Exemplo: Músico", size="sm")
216
+ example_btn2 = gr.Button(" Exemplo: Esporte", size="sm")
217
+ example_btn3 = gr.Button("💼 Exemplo: Negócios", size="sm")
 
218
 
219
  with gr.Column(scale=1):
220
  output = gr.Textbox(
 
224
  show_copy_button=True
225
  )
226
 
227
+ gr.Markdown("### Status:")
228
  status = gr.Textbox(
229
  label="Status da Análise",
230
+ value="Aguardando entrada...",
231
  interactive=False
232
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
+ # Função para atualizar status (mantém original)
235
+ def update_status_and_analyze(title, content):
236
+ if not title.strip() or not content.strip():
237
+ return "❌ Por favor, preencha tanto o título quanto o conteúdo.", "Erro: Campos obrigatórios não preenchidos."
238
+
239
+ try:
240
+ result = analyze_news(title, content)
241
+ return f"✅ Análise concluída com sucesso!", result
242
+ except Exception as e:
243
+ return f"❌ Erro na análise: {str(e)}", f"Erro: {str(e)}"
244
+
245
+ # Exemplos predefinidos (mantém originais)
246
  def load_example_1():
247
  return (
248
  "Legendary Musician Carlos Mendes Dies at 78",
 
261
  "The technology company announced significant workforce reductions citing economic uncertainty and changing market conditions. The layoffs will affect multiple departments across different regions."
262
  )
263
 
264
+ # Event handlers (mantém originais)
265
  analyze_btn.click(
266
+ fn=update_status_and_analyze,
267
  inputs=[title_input, content_input],
268
  outputs=[status, output]
269
  )
 
284
  )
285
 
286
  # Informações adicionais
287
+ with gr.Accordion("ℹ️ Informações", open=False):
288
  gr.Markdown("""
289
+ **Como usar:**
290
+ 1. Insira o título da notícia
291
+ 2. Insira o conteúdo da notícia
292
+ 3. Clique em "Analisar Notícia"
293
+ 4. O resultado será exibido em formato JSON
294
+
295
+ **Otimizações aplicadas:**
296
+ - Threads otimizadas para CPU
297
+ - Modo de inferência acelerado
298
+ - Limpeza automática de memória
299
+ - Cache de modelo ativado
300
+ - Warmup automático
301
 
302
+ **Notas:**
303
+ - O modelo está rodando em CPU
304
+ - O processamento pode levar alguns segundos
305
+ - Use os exemplos predefinidos para testar rapidamente
306
  """)
307
 
308
  return demo
 
311
  # Executa warmup antes de iniciar a interface
312
  warmup_model()
313
 
314
+ print("🚀 Iniciando interface Gradio...")
315
  demo = create_interface()
316
  demo.launch(
317
  share=False,