ProfRod100 commited on
Commit
b222eb2
·
verified ·
1 Parent(s): 630354a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -184
app.py CHANGED
@@ -2,41 +2,30 @@ import os
2
  import gradio as gr
3
  import joblib
4
  import numpy as np
5
-
6
  from sklearn.pipeline import Pipeline
7
  from sklearn.feature_extraction.text import TfidfVectorizer
8
  from sklearn.linear_model import LogisticRegression
9
 
10
- from transformers import pipeline as hf_pipeline
11
-
12
 
13
  # ============================================================
14
- # 1. Baseline de Sentimentos (TF-IDF + Regressão Logística)
15
  # ============================================================
16
 
17
  BASELINE_PATH = "baseline_pipe.pkl"
18
 
19
-
20
  def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
21
- """
22
- Treina um baseline pequeno usando o dataset amazon_polarity.
23
- Só é executado se baseline_pipe.pkl não existir no Space.
24
- """
25
  from datasets import load_dataset
26
  import pandas as pd
27
 
28
- # Carrega split de treino completo e amostra max_samples exemplos
29
  ds = load_dataset("amazon_polarity", split="train")
30
  ds_small = ds.shuffle(seed=42).select(range(max_samples))
31
-
32
  df = pd.DataFrame({"text": ds_small["content"], "label": ds_small["label"]})
33
 
34
- pipe = Pipeline(
35
- [
36
- ("tfidf", TfidfVectorizer(max_features=25000, ngram_range=(1, 2))),
37
- ("clf", LogisticRegression(max_iter=1200)),
38
- ]
39
- )
40
 
41
  pipe.fit(df["text"], df["label"])
42
  joblib.dump(pipe, save_path)
@@ -44,9 +33,6 @@ def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
44
 
45
 
46
  def load_baseline():
47
- """
48
- Carrega o baseline de disco ou treina um novo se não existir.
49
- """
50
  if os.path.exists(BASELINE_PATH):
51
  return joblib.load(BASELINE_PATH)
52
  return train_small_baseline()
@@ -55,218 +41,99 @@ def load_baseline():
55
  baseline_model = load_baseline()
56
 
57
 
58
- def classify_sentiment(text):
59
- """
60
- Classifica o texto como positivo/negativo e devolve UMA STRING.
61
- Exemplo:
62
-
63
- Resultado da classificação:
64
- Sentimento: positivo
65
- Confiança: 0.942
66
- """
67
  text = (text or "").strip()
68
  if not text:
69
- return "Erro: digite algo para analisar."
70
 
71
  proba = baseline_model.predict_proba([text])[0]
72
  pred = int(np.argmax(proba))
73
- label = "positivo" if pred == 1 else "negativo"
74
  conf = float(np.max(proba))
75
 
76
- out = []
77
- out.append("Resultado da classificação:")
78
- out.append(f"Sentimento: {label}")
79
- out.append(f"Confiança: {conf:.3f}")
80
- return "\n".join(out)
81
 
82
 
83
  # ============================================================
84
- # 2. IA Generativa FLAN-T5 (modelo aberto, sem token)
85
  # ============================================================
86
 
87
- GEN_MODEL = os.getenv("GEN_MODEL_ID", "google/flan-t5-base")
88
-
89
- generator = hf_pipeline(
90
  "text2text-generation",
91
- model=GEN_MODEL,
92
  )
93
 
94
  SYSTEM_PROMPT = (
95
- "Você é um atendente virtual educado, empático e profissional "
96
- "de uma loja online. Responda SEMPRE em português do Brasil, "
97
- "com 2 a 4 frases claras, naturais e diretas. "
98
- "Não mencione que é um modelo de IA e não explique o processo interno."
99
  )
100
 
101
 
102
- def detect_sentiment_raw(text):
103
  """
104
- Versão simplificada para o chatbot.
105
- Retorna (label, conf) ou (None, None) em caso de erro.
106
  """
107
- text = (text or "").strip()
108
- if not text:
109
- return None, None
110
 
111
- proba = baseline_model.predict_proba([text])[0]
 
112
  pred = int(np.argmax(proba))
113
- label = "positivo" if pred == 1 else "negativo"
114
  conf = float(np.max(proba))
115
- return label, conf
116
-
117
-
118
- def build_prompt(history, user_msg, sent_label, sent_conf):
119
- """
120
- Monta o prompt para o FLAN-T5 usando histórico + sentimento.
121
- Tudo aqui é texto simples.
122
- """
123
 
124
- if sent_label == "negativo":
125
- orientacao = (
126
- "O cliente está insatisfeito. Mostre empatia, peça desculpas, "
127
- "demonstre interesse em resolver e, se fizer sentido, peça "
128
- "dados adicionais (pedido, produto, canal de contato)."
129
- )
130
- elif sent_label == "positivo":
131
- orientacao = (
132
- "O cliente está satisfeito. Agradeça com entusiasmo, "
133
- "reforce os pontos positivos e convide o cliente a continuar "
134
- "comprando ou usando o serviço."
135
- )
136
  else:
137
- orientacao = (
138
- "O sentimento não está claro. Responda de forma neutra, cordial "
139
- "e prestativa, buscando ajudar o cliente."
140
- )
141
-
142
- hist_txt = ""
143
- if history:
144
- for u, b in history:
145
- hist_txt += f"Cliente: {u}\nAtendente: {b}\n"
146
 
147
  prompt = (
148
  f"{SYSTEM_PROMPT}\n\n"
149
- "Use as informações abaixo apenas como contexto, sem citá-las:"
150
- f"\n- Sentimento detectado: {sent_label}"
151
- f"\n- Confiança aproximada: {sent_conf}"
152
- f"\n- Orientação de tom: {orientacao}\n\n"
153
- "Histórico da conversa (se houver):\n"
154
- f"{hist_txt}\n"
155
- "Nova mensagem do cliente:\n"
156
- f"Cliente: {user_msg}\n\n"
157
- "Escreva apenas a resposta do Atendente em português do Brasil."
158
  )
159
 
160
- return prompt
161
-
162
-
163
- def generate_reply(history, user_msg):
164
- """
165
- Função usada pelo botão do chatbot.
166
-
167
- - Recebe o histórico atual e a nova mensagem
168
- - Detecta sentimento
169
- - Gera resposta com FLAN-T5
170
- - Retorna:
171
- history_atualizado, "", texto_sentimento
172
- """
173
- user_msg = (user_msg or "").strip()
174
- if not user_msg:
175
- return history, "", "Erro: digite uma mensagem antes de enviar."
176
-
177
- sent_label, sent_conf = detect_sentiment_raw(user_msg)
178
-
179
- prompt = build_prompt(history, user_msg, sent_label, sent_conf)
180
-
181
- gen_out = generator(
182
  prompt,
183
  max_length=200,
184
  do_sample=True,
185
  temperature=0.7,
186
- top_p=0.9,
187
- )[0]["generated_text"].strip()
188
-
189
- new_history = history + [(user_msg, gen_out)]
190
 
191
- sentiment_text = (
192
- "Sentimento detectado para a última mensagem:\n"
193
- f"- Rótulo: {sent_label}\n"
194
- f"- Confiança aproximada: {sent_conf:.3f}"
195
- if sent_label is not None
196
- else "Não foi possível detectar o sentimento."
197
- )
198
-
199
- # history → Chatbot
200
- # "" → limpa a caixa do usuário
201
- # sentiment_text → textbox com sentimento
202
- return new_history, "", sentiment_text
203
 
204
 
205
  # ============================================================
206
- # 3. Interface Gradio (tipos simples + API desativada)
207
  # ============================================================
208
 
209
- with gr.Blocks(
210
- title="Chatbot de Sentimentos - Prof. Rodrigo",
211
- theme=gr.themes.Default(),
212
- ) as demo:
213
- gr.Markdown(
214
- """
215
- # Chatbot de Sentimentos (ML + IA Generativa)
216
 
 
 
 
 
217
  **Professor Rodrigo — Projeto Final ML & DL**
218
 
219
- - Classificação: TF-IDF + Regressão Logística (baseline, `amazon_polarity`)
220
- - Geração de resposta: `google/flan-t5-base` (modelo aberto, sem token)
221
- - Histórico de conversa mantido na aba de chatbot
222
- - Você pode enviar um `baseline_pipe.pkl` na aba **Files** do Space para usar
223
- um modelo de sentimentos treinado pelo seu grupo.
224
- """
225
- )
226
 
227
- # ---------- Aba 1: Análise de Sentimento ----------
228
  with gr.Tab("Análise de Sentimento"):
229
- text_in = gr.Textbox(
230
- label="Digite um comentário",
231
- lines=4,
232
- placeholder="Ex.: O produto chegou quebrado, fiquei muito chateado.",
233
- )
234
- text_out = gr.Textbox(
235
- label="Resultado da classificação",
236
- lines=4,
237
- )
238
- btn_analisar = gr.Button("Analisar sentimento", variant="primary")
239
 
240
- btn_analisar.click(
241
- fn=classify_sentiment,
242
- inputs=text_in,
243
- outputs=text_out,
244
- )
245
-
246
- # ---------- Aba 2: Chatbot (Análise + Resposta) ----------
247
  with gr.Tab("Chatbot (Análise + Resposta)"):
248
- chatbot = gr.Chatbot(
249
- label="Histórico de conversa com o atendente virtual",
250
- height=400,
251
- )
252
- user_box = gr.Textbox(
253
- label="Mensagem do cliente",
254
- lines=3,
255
- placeholder="Ex.: Estou chateado, o produto é ruim.",
256
- )
257
- sentimento_box = gr.Textbox(
258
- label="Sentimento da última mensagem",
259
- lines=4,
260
- )
261
- send_btn = gr.Button("Enviar e gerar resposta", variant="primary")
262
-
263
- send_btn.click(
264
- fn=generate_reply,
265
- inputs=[chatbot, user_box],
266
- outputs=[chatbot, user_box, sentimento_box],
267
  )
268
 
269
 
270
- if __name__ == "__main__":
271
- # Usa fila do Gradio e desativa a API aberta (evita o bug "No API found")
272
- demo.queue(api_open=False).launch()
 
2
  import gradio as gr
3
  import joblib
4
  import numpy as np
5
+ from transformers import pipeline
6
  from sklearn.pipeline import Pipeline
7
  from sklearn.feature_extraction.text import TfidfVectorizer
8
  from sklearn.linear_model import LogisticRegression
9
 
 
 
10
 
11
  # ============================================================
12
+ # 1. Baseline de Sentimentos
13
  # ============================================================
14
 
15
  BASELINE_PATH = "baseline_pipe.pkl"
16
 
 
17
  def train_small_baseline(save_path=BASELINE_PATH, max_samples=8000):
 
 
 
 
18
  from datasets import load_dataset
19
  import pandas as pd
20
 
 
21
  ds = load_dataset("amazon_polarity", split="train")
22
  ds_small = ds.shuffle(seed=42).select(range(max_samples))
 
23
  df = pd.DataFrame({"text": ds_small["content"], "label": ds_small["label"]})
24
 
25
+ pipe = Pipeline([
26
+ ("tfidf", TfidfVectorizer(max_features=20000)),
27
+ ("clf", LogisticRegression(max_iter=1000)),
28
+ ])
 
 
29
 
30
  pipe.fit(df["text"], df["label"])
31
  joblib.dump(pipe, save_path)
 
33
 
34
 
35
  def load_baseline():
 
 
 
36
  if os.path.exists(BASELINE_PATH):
37
  return joblib.load(BASELINE_PATH)
38
  return train_small_baseline()
 
41
  baseline_model = load_baseline()
42
 
43
 
44
+ def classify_only(text):
 
 
 
 
 
 
 
 
45
  text = (text or "").strip()
46
  if not text:
47
+ return "Erro: digite um texto."
48
 
49
  proba = baseline_model.predict_proba([text])[0]
50
  pred = int(np.argmax(proba))
51
+ lbl = "positivo" if pred == 1 else "negativo"
52
  conf = float(np.max(proba))
53
 
54
+ return f"Sentimento: {lbl}\nConfiança: {conf:.3f}"
 
 
 
 
55
 
56
 
57
  # ============================================================
58
+ # 2. IA Generativa - FLAN T5 (modelo aberto)
59
  # ============================================================
60
 
61
+ generator = pipeline(
 
 
62
  "text2text-generation",
63
+ model="google/flan-t5-base",
64
  )
65
 
66
  SYSTEM_PROMPT = (
67
+ "Você é um atendente virtual educado e empático. "
68
+ "Responda SEMPRE em português do Brasil."
 
 
69
  )
70
 
71
 
72
+ def chatbot_logic(user_message, history):
73
  """
74
+ Função usada pelo ChatInterface history é automático.
 
75
  """
 
 
 
76
 
77
+ # Detecta sentimento
78
+ proba = baseline_model.predict_proba([user_message])[0]
79
  pred = int(np.argmax(proba))
80
+ sent = "positivo" if pred == 1 else "negativo"
81
  conf = float(np.max(proba))
 
 
 
 
 
 
 
 
82
 
83
+ if sent == "negativo":
84
+ orient = "O cliente está insatisfeito. Mostre empatia, peça desculpas e ofereça ajuda."
 
 
 
 
 
 
 
 
 
 
85
  else:
86
+ orient = "O cliente está satisfeito. Agradeça e incentive a continuar usando o serviço."
 
 
 
 
 
 
 
 
87
 
88
  prompt = (
89
  f"{SYSTEM_PROMPT}\n\n"
90
+ f"Contexto de sentimento:\n- Sentimento: {sent}\n- Confiança: {conf:.3f}\n"
91
+ f"Orientação: {orient}\n\n"
92
+ f"Histórico prévio:\n{history}\n\n"
93
+ f"Mensagem do cliente: {user_message}\n\n"
94
+ "Gere uma resposta educada e profissional."
 
 
 
 
95
  )
96
 
97
+ out = generator(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  prompt,
99
  max_length=200,
100
  do_sample=True,
101
  temperature=0.7,
102
+ )[0]["generated_text"]
 
 
 
103
 
104
+ return out
 
 
 
 
 
 
 
 
 
 
 
105
 
106
 
107
  # ============================================================
108
+ # 3. Interface Gradio ChatInterface = sem bugs!
109
  # ============================================================
110
 
 
 
 
 
 
 
 
111
 
112
+ with gr.Blocks(title="Chatbot de Sentimentos") as demo:
113
+
114
+ gr.Markdown("""
115
+ # Chatbot de Sentimentos (ML + IA Generativa)
116
  **Professor Rodrigo — Projeto Final ML & DL**
117
 
118
+ - Classificação: TF-IDF + Regressão Logística
119
+ - Geração: `google/flan-t5-base`
120
+ - Sem API aberta (evita erro "No API found")
121
+ """)
 
 
 
122
 
 
123
  with gr.Tab("Análise de Sentimento"):
124
+ txt = gr.Textbox(lines=4, label="Digite um comentário")
125
+ out = gr.Textbox(lines=3, label="Resultado")
126
+ btn = gr.Button("Analisar")
127
+ btn.click(classify_only, txt, out)
 
 
 
 
 
 
128
 
 
 
 
 
 
 
 
129
  with gr.Tab("Chatbot (Análise + Resposta)"):
130
+ gr.ChatInterface(
131
+ fn=chatbot_logic,
132
+ title="Atendente Virtual",
133
+ examples=["O produto chegou quebrado", "Amei o serviço!"],
134
+ retry_btn=None,
135
+ undo_btn=None,
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  )
137
 
138
 
139
+ demo.queue(api_open=False).launch()