MarvinRoque commited on
Commit
e65cc68
·
verified ·
1 Parent(s): aef9db2
Files changed (1) hide show
  1. app.py +98 -75
app.py CHANGED
@@ -4,7 +4,6 @@ from sentence_transformers import SentenceTransformer, util
4
  import unicodedata
5
  import torch
6
 
7
- # ========= Normalização =========
8
  def normalize_text(text):
9
  """Remove acentos e coloca em minúsculas para comparação robusta."""
10
  return "".join(
@@ -12,83 +11,98 @@ def normalize_text(text):
12
  if unicodedata.category(c) != "Mn"
13
  )
14
 
15
- # ========= Modelo de linguagem (Falcon 1B Instruct) =========
 
 
16
  model_id = "tiiuae/Falcon3-1B-Instruct"
17
  tokenizer = AutoTokenizer.from_pretrained(model_id)
18
  model = AutoModelForCausalLM.from_pretrained(
19
  model_id,
20
- dtype=torch.float32, # ou "auto" se tiver GPU
21
- device_map="auto" # ou None para CPU
22
  )
23
 
24
- # ========= Modelo de embeddings =========
25
- # Recomendado: intfloat/multilingual-e5-base (bom em português, leve)
26
- embedder = SentenceTransformer("intfloat/multilingual-e5-base")
27
-
28
- # ========= Domínios fitness =========
 
 
 
 
 
 
 
 
 
29
  fitness_domains = [
30
  # 🏋️‍♂️ Treino
31
- "treino para pernas e braços",
32
- "exercícios para hipertrofia muscular",
33
- "como dividir treino de força e resistência",
34
  "tipos de treino: força, resistência, hipertrofia",
35
- "programa de periodização de treino",
36
-
 
37
  # 🍎 Nutrição
38
- "dieta para ganho de massa muscular",
39
- "o que comer antes e depois do treino",
40
  "alimentos que ajudam na recuperação muscular",
41
- "suplementos esportivos para performance",
42
- "creatina, whey protein e bcaas",
43
- "hidratação e nutrição esportiva",
44
- "planejamento alimentar para atletas",
45
-
 
 
 
 
 
46
  # 🛌 Recuperação
47
  "descanso e recuperação muscular",
48
- "alongamento e aquecimento antes do treino",
49
- "prevenção de lesões no treino de academia",
 
50
  "sono e hidratação no desempenho físico",
51
-
52
  # 🩺 Lesões e reabilitação
53
- "treino adaptado para lesão no joelho",
54
- "fisioterapia esportiva para ombro e lombar",
55
-
56
  # 🎯 Objetivos gerais
57
  "como ganhar massa muscular",
58
- "como perder peso com exercícios",
 
 
59
  "plano de treino para iniciantes",
60
- "estratégias de motivação e metas fitness"
61
  ]
62
 
63
- # ========= Contra-domínios (mais específicos) =========
64
  contra_domains = [
65
  # Finanças
66
- "como ganhar dinheiro na bolsa de valores",
67
- "investimentos em ações e criptomoedas",
68
- "finanças pessoais e planejamento financeiro",
69
-
 
70
  # Tecnologia
71
- "melhores celulares android de 2025",
72
- "comparativo de computadores gamer",
73
- "como deixar o telemóvel mais rápido",
74
- "novidades em inteligência artificial",
75
-
76
- # Jogos e entretenimento
77
- "melhores jogos para playstation 5",
78
- "review de consoles xbox e nintendo switch",
79
-
80
- # Viagens e turismo
81
- "destinos turísticos mais baratos da europa",
82
- "como planejar uma viagem internacional",
83
-
84
- # Educação e curiosidades
85
- "história da vida na terra",
86
- "resumo sobre a segunda guerra mundial",
87
- "como funciona a política brasileira",
88
- "questões de matemática básica como 2 + 2"
89
  ]
90
 
91
- # ========= Palavras-chave relevantes =========
92
  fitness_keywords = [
93
  "treino", "exercício", "academia", "ginasio", "hipertrofia", "musculação",
94
  "condicionamento", "força", "resistência", "alongamento", "aquecimento",
@@ -96,43 +110,50 @@ fitness_keywords = [
96
  "recuperação", "descanso", "sono", "hidratação",
97
  "lesão", "joelho", "ombro", "lombar", "cotovelo","costas", "peito", "pernas", "trapezio",
98
  "bíceps", "tríceps", "abdômen", "core", "quadriceps", "posterior de coxa", "panturrilha",
99
- "reabilitação", "fisioterapia", "explosividade", "mobilidade", "flexibilidade",
100
- "plano de treino", "plano alimentar",
101
- "perder peso", "emagrecer", "ganhar massa", "ganhar músculo", "definição muscular",
102
- "motivação", "metas fitness", "fitness", "personal trainer", "treinador"
103
  ]
104
 
105
- # ========= Pré-calcular embeddings =========
 
 
 
 
 
106
  fitness_embeddings = embedder.encode(fitness_domains, convert_to_tensor=True, normalize_embeddings=True)
107
  contra_embeddings = embedder.encode(contra_domains, convert_to_tensor=True, normalize_embeddings=True)
108
 
109
- # ========= Função de resposta =========
 
 
110
  def responder(prompt):
111
- # Normalizar
112
  prompt_norm = normalize_text(prompt)
113
-
114
- # Embedding
115
  prompt_embedding = embedder.encode(prompt, convert_to_tensor=True, normalize_embeddings=True)
116
 
117
- # Similaridades (usar máximo em vez de top-k média)
118
- max_fitness = torch.max(util.cos_sim(prompt_embedding, fitness_embeddings)).item()
119
- max_contra = torch.max(util.cos_sim(prompt_embedding, contra_embeddings)).item()
 
 
 
120
 
121
  # Palavras-chave
122
- fitness_keywords_norm = [normalize_text(kw) for kw in fitness_keywords]
123
- kw_matches = sum(kw in prompt_norm for kw in fitness_keywords_norm)
124
 
125
- # Score simples (diferença + bonus keywords)
126
- score = (max_fitness - max_contra) + (0.10 * kw_matches)
 
 
127
 
128
  print(f"Prompt: {prompt}")
129
- print(f"Fitness (max): {max_fitness:.3f} | Contra (max): {max_contra:.3f} | KW matches: {kw_matches} | Score: {score:.3f}")
130
 
131
- # Threshold adaptável (ainda pode ser ajustado por testes)
132
- if score < 0.15:
133
  return "Desculpe, só respondo perguntas sobre treino, nutrição e fitness."
134
 
135
- # Prompt explícito para o modelo
136
  system_message = (
137
  "Você é um personal trainer virtual. "
138
  "Sempre responda em PORTUGUÊS, de forma clara, curta e prática. "
@@ -154,12 +175,14 @@ def responder(prompt):
154
  resposta = tokenizer.decode(outputs[0], skip_special_tokens=True)
155
  return resposta.split("Assistente:")[-1].strip()
156
 
157
- # ========= Interface Gradio =========
 
 
158
  demo = gr.Interface(
159
  fn=responder,
160
  inputs=gr.Textbox(lines=3, label="Pergunta"),
161
  outputs=gr.Textbox(label="Resposta"),
162
- title="Personal Trainer AI com Filtro Semântico (Falcon 1B + embeddings + keywords)"
163
  )
164
 
165
  demo.queue().launch()
 
4
  import unicodedata
5
  import torch
6
 
 
7
  def normalize_text(text):
8
  """Remove acentos e coloca em minúsculas para comparação robusta."""
9
  return "".join(
 
11
  if unicodedata.category(c) != "Mn"
12
  )
13
 
14
+ # =========================================================
15
+ # Modelo de linguagem (Falcon 1B Instruct)
16
+ # =========================================================
17
  model_id = "tiiuae/Falcon3-1B-Instruct"
18
  tokenizer = AutoTokenizer.from_pretrained(model_id)
19
  model = AutoModelForCausalLM.from_pretrained(
20
  model_id,
21
+ torch_dtype="float32",
22
+ device_map="auto"
23
  )
24
 
25
+ # =========================================================
26
+ # Modelo de embeddings (BAAI/bge-m3 ou fallback bge-small)
27
+ # =========================================================
28
+ try:
29
+ embedder = SentenceTransformer("BAAI/bge-m3")
30
+ print("✅ Usando embeddings BAAI/bge-m3")
31
+ except Exception as e:
32
+ print(f"⚠️ Erro ao carregar bge-m3: {e}")
33
+ print("➡️ Usando fallback BAAI/bge-small-en-v1.5")
34
+ embedder = SentenceTransformer("BAAI/bge-small-en-v1.5")
35
+
36
+ # =========================================================
37
+ # Domínios fitness
38
+ # =========================================================
39
  fitness_domains = [
40
  # 🏋️‍♂️ Treino
41
+ "treino para",
42
+ "exercícios para",
43
+ "como dividir meu treino",
44
  "tipos de treino: força, resistência, hipertrofia",
45
+ "como melhorar hipertrofia",
46
+ "periodização de treino",
47
+
48
  # 🍎 Nutrição
49
+ "dieta para",
50
+ "o que comer para",
51
  "alimentos que ajudam na recuperação muscular",
52
+ "suplementos para treino",
53
+ "creatina e whey protein",
54
+ "hidratação e desempenho físico",
55
+ "nutrição esportiva",
56
+ "macronutrientes e micronutrientes",
57
+ "planejamento alimentar",
58
+ "proteínas, carboidratos e gorduras",
59
+ "creatina, bcaas, whey protein",
60
+ "o que ajuda na hipertrofia",
61
+
62
  # 🛌 Recuperação
63
  "descanso e recuperação muscular",
64
+ "descanso entre séries",
65
+ "alongamento e aquecimento",
66
+ "prevenção de lesões no treino",
67
  "sono e hidratação no desempenho físico",
68
+
69
  # 🩺 Lesões e reabilitação
70
+ "treino adaptado para lesões",
71
+ "reabilitação e fisioterapia esportiva",
72
+
73
  # 🎯 Objetivos gerais
74
  "como ganhar massa muscular",
75
+ "como perder peso",
76
+ "como ganhar massa",
77
+ "melhor forma de melhorar condicionamento físico",
78
  "plano de treino para iniciantes",
79
+ "estratégias para motivação e metas fitness"
80
  ]
81
 
82
+ # Contra-domínios mais específicos
83
  contra_domains = [
84
  # Finanças
85
+ "como ganhar dinheiro",
86
+ "investir em ações e bolsa de valores",
87
+ "criptomoedas e bitcoin",
88
+ "finanças pessoais e poupança",
89
+
90
  # Tecnologia
91
+ "melhores celulares android",
92
+ "como deixar o computador mais rápido",
93
+ "programação em python",
94
+ "jogos online e consoles",
95
+ "reviews de gadgets e eletrônicos",
96
+
97
+ # Outros
98
+ "viagens e turismo",
99
+ "política e governo no brasil",
100
+ "história da segunda guerra mundial",
101
+ "astrologia e signos",
102
+ "religião e espiritualidade"
 
 
 
 
 
 
103
  ]
104
 
105
+ # Palavras-chave relevantes (normalizadas depois)
106
  fitness_keywords = [
107
  "treino", "exercício", "academia", "ginasio", "hipertrofia", "musculação",
108
  "condicionamento", "força", "resistência", "alongamento", "aquecimento",
 
110
  "recuperação", "descanso", "sono", "hidratação",
111
  "lesão", "joelho", "ombro", "lombar", "cotovelo","costas", "peito", "pernas", "trapezio",
112
  "bíceps", "tríceps", "abdômen", "core", "quadriceps", "posterior de coxa", "panturrilha",
113
+ "reabilitação", "fisioterapia", "explosividade", "mobilidade", "flexibilidade", "plano de treino", "plano alimentar",
114
+ "perder peso", "emagrecer", "ganhar massa", "ganhar músculo", "definição muscular", "motivação", "metas fitness",
115
+ "fitness", "personal trainer", "personal", "treinador"
 
116
  ]
117
 
118
+ # Normalizar keywords
119
+ fitness_keywords_norm = [normalize_text(kw) for kw in fitness_keywords]
120
+
121
+ # =========================================================
122
+ # Pré-calcular embeddings
123
+ # =========================================================
124
  fitness_embeddings = embedder.encode(fitness_domains, convert_to_tensor=True, normalize_embeddings=True)
125
  contra_embeddings = embedder.encode(contra_domains, convert_to_tensor=True, normalize_embeddings=True)
126
 
127
+ # =========================================================
128
+ # Função principal
129
+ # =========================================================
130
  def responder(prompt):
 
131
  prompt_norm = normalize_text(prompt)
 
 
132
  prompt_embedding = embedder.encode(prompt, convert_to_tensor=True, normalize_embeddings=True)
133
 
134
+ # Similaridades
135
+ fitness_sim = util.cos_sim(prompt_embedding, fitness_embeddings)
136
+ contra_sim = util.cos_sim(prompt_embedding, contra_embeddings)
137
+
138
+ max_fitness = torch.max(fitness_sim).item()
139
+ max_contra = torch.max(contra_sim).item()
140
 
141
  # Palavras-chave
142
+ keyword_match = any(kw in prompt_norm for kw in fitness_keywords_norm)
 
143
 
144
+ # Score híbrido
145
+ score = max_fitness - max_contra
146
+ if keyword_match:
147
+ score += 0.25 # pequeno bônus se tem keyword fitness
148
 
149
  print(f"Prompt: {prompt}")
150
+ print(f"Fitness (max): {max_fitness:.3f} | Contra (max): {max_contra:.3f} | KW matches: {int(keyword_match)} | Score: {score:.3f}")
151
 
152
+ # Decisão
153
+ if score < 0.05:
154
  return "Desculpe, só respondo perguntas sobre treino, nutrição e fitness."
155
 
156
+ # === Geração com LLM ===
157
  system_message = (
158
  "Você é um personal trainer virtual. "
159
  "Sempre responda em PORTUGUÊS, de forma clara, curta e prática. "
 
175
  resposta = tokenizer.decode(outputs[0], skip_special_tokens=True)
176
  return resposta.split("Assistente:")[-1].strip()
177
 
178
+ # =========================================================
179
+ # Interface Gradio
180
+ # =========================================================
181
  demo = gr.Interface(
182
  fn=responder,
183
  inputs=gr.Textbox(lines=3, label="Pergunta"),
184
  outputs=gr.Textbox(label="Resposta"),
185
+ title="Personal Trainer AI com Filtro Semântico (BAAI/bge-m3)"
186
  )
187
 
188
  demo.queue().launch()