ProfRod100 commited on
Commit
1c2502c
Β·
verified Β·
1 Parent(s): 9a9ecdb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -47
app.py CHANGED
@@ -1,63 +1,109 @@
1
 
2
  import os
3
- import json
4
  import numpy as np
5
  import gradio as gr
6
-
7
- # ---------- 1) Sentiment Baseline (TF-IDF + LogisticRegression) ----------
8
  import joblib
 
9
  from sklearn.pipeline import Pipeline
10
  from sklearn.feature_extraction.text import TfidfVectorizer
11
  from sklearn.linear_model import LogisticRegression
12
 
 
 
 
 
 
 
 
13
  BASELINE_PATH = os.getenv("MODEL_PATH", "baseline_pipe.pkl")
14
 
15
- def train_small_baseline(save_path=BASELINE_PATH, sample_pct="train[:0.5%]"):
 
 
 
 
 
 
16
  from datasets import load_dataset
17
  import pandas as pd
18
- ds = load_dataset("amazon_polarity", split=sample_pct)
 
19
  df = pd.DataFrame({"text": ds["content"], "label": ds["label"]})
20
- pipe = Pipeline([
21
- ("tfidf", TfidfVectorizer(max_features=30000, ngram_range=(1,2))),
22
- ("clf", LogisticRegression(max_iter=1000)),
23
- ])
 
 
 
 
24
  pipe.fit(df["text"], df["label"])
25
  joblib.dump(pipe, save_path)
26
  return pipe
27
 
 
28
  def load_or_bootstrap_baseline():
 
 
 
 
29
  if os.path.exists(BASELINE_PATH):
30
  return joblib.load(BASELINE_PATH)
31
- if os.getenv("DISABLE_AUTOTRAIN", "0") == "1":
 
 
32
  return None
 
33
  return train_small_baseline()
34
 
35
- baseline = load_or_bootstrap_baseline()
36
 
37
- def classify_only(text):
38
- if not text or not text.strip():
 
 
 
 
 
 
39
  return {"erro": "Digite um texto."}
40
- if baseline is None:
41
- return {"erro": "Modelo baseline nao encontrado. Envie baseline_pipe.pkl ou remova DISABLE_AUTOTRAIN."}
42
- proba = baseline.predict_proba([text])[0]
 
 
 
 
 
 
 
43
  pred = int(np.argmax(proba))
44
  label = "positivo" if pred == 1 else "negativo"
45
  conf = float(np.max(proba))
 
46
  return {"sentimento": label, "confianca": round(conf, 3)}
47
 
48
- # ---------- 2) Generative Assistant (FLAN-T5) ----------
49
- from transformers import pipeline
 
 
 
50
  GEN_MODEL_ID = os.getenv("GEN_MODEL_ID", "google/flan-t5-base")
51
- generator = pipeline("text2text-generation", model=GEN_MODEL_ID)
 
52
 
53
  SYSTEM_PROMPT = (
54
  "Voce e um atendente virtual educado que responde em portugues do Brasil. "
55
  "Use poucas frases, tom empatico e pratico."
56
  )
57
 
58
- def generate_reply(user_text, sentimento_json):
59
- if not user_text or not user_text.strip():
 
 
 
 
60
  return "Digite uma mensagem."
 
61
  sentimento = None
62
  if isinstance(sentimento_json, dict) and "sentimento" in sentimento_json:
63
  sentimento = sentimento_json["sentimento"]
@@ -65,53 +111,91 @@ def generate_reply(user_text, sentimento_json):
65
  if sentimento == "negativo":
66
  intent = (
67
  "A avaliacao do cliente e NEGATIVA. "
68
- "Peca desculpas, reconheca o problema, ofereca ajuda objetiva e solicite informacoes adicionais."
 
69
  )
70
  elif sentimento == "positivo":
71
  intent = (
72
  "A avaliacao do cliente e POSITIVA. "
73
- "Agradeca de forma calorosa, reforce pontos fortes mencionados e convide para novas compras."
 
74
  )
75
  else:
76
- intent = "O sentimento nao foi identificado. Responda de forma neutra, util e cordial."
 
 
 
77
 
78
  prompt = (
79
- f"{SYSTEM_PROMPT}\n\n{intent}\n\n"
 
80
  f"Mensagem do cliente:\n\"{user_text}\"\n\n"
81
- f"Escreva uma resposta curta (2-4 frases)."
82
  )
83
- out = generator(prompt, max_length=128, do_sample=True, temperature=0.6, top_p=0.9)[0]["generated_text"]
 
 
 
 
 
 
 
 
84
  return out
85
 
86
- # ---------- 3) Gradio UI ----------
87
- with gr.Blocks(title="Classificador + Chatbot (Sentimentos)") as demo:
 
 
 
 
88
  gr.Markdown(
89
- \"\"\"
90
- # Chatbot de Sentimentos (ML + IA Generativa)
91
- Professor Rodrigo - Projeto Final ML & DL
92
 
93
- - Classificacao: TF-IDF + Regressao Logistica (autotreino minimo se nao houver `baseline_pipe.pkl`).
94
- - Geracao: `google/flan-t5-base` para redigir respostas educadas em PT-BR.
95
 
96
- > Dica: Envie `baseline_pipe.pkl` em *Files* para pular o autotrain.
97
- \"\"\"
98
  )
99
 
 
100
  with gr.Tab("Analise de Sentimento"):
101
- in_text = gr.Textbox(label="Digite uma avaliacao de produto", lines=4, placeholder="Ex.: O produto chegou rapido e funciona muito bem.")
102
- out_json = gr.JSON(label="Resultado")
103
- btn = gr.Button("Analisar")
104
- btn.click(classify_only, inputs=in_text, outputs=out_json)
 
 
 
 
105
 
 
106
  with gr.Tab("Chatbot (Analise + Resposta)"):
107
- chat_in = gr.Textbox(label="Mensagem do cliente", lines=4)
 
 
 
 
108
  with gr.Row():
109
- analyze_btn = gr.Button("1) Analisar sentimento")
110
- gen_btn = gr.Button("2) Gerar resposta")
111
- analysis_box = gr.JSON(label="Sentimento detectado")
112
- reply_box = gr.Textbox(label="Resposta gerada", lines=6)
113
- analyze_btn.click(classify_only, inputs=chat_in, outputs=analysis_box)
114
- gen_btn.click(generate_reply, inputs=[chat_in, analysis_box], outputs=reply_box)
 
 
 
 
 
 
 
 
 
 
115
 
116
  if __name__ == "__main__":
117
  demo.launch()
 
1
 
2
  import os
 
3
  import numpy as np
4
  import gradio as gr
 
 
5
  import joblib
6
+
7
  from sklearn.pipeline import Pipeline
8
  from sklearn.feature_extraction.text import TfidfVectorizer
9
  from sklearn.linear_model import LogisticRegression
10
 
11
+ from transformers import pipeline as hf_pipeline
12
+
13
+
14
+ # ======================================================================
15
+ # 1. Baseline de Sentimentos (TF-IDF + Logistic Regression)
16
+ # ======================================================================
17
+
18
  BASELINE_PATH = os.getenv("MODEL_PATH", "baseline_pipe.pkl")
19
 
20
+
21
+ def train_small_baseline(save_path: str = BASELINE_PATH,
22
+ sample_split: str = "train[:0.5%]"):
23
+ """
24
+ Treina um baseline pequeno usando uma amostra do dataset amazon_polarity.
25
+ Usado apenas se baseline_pipe.pkl nao existir no Space.
26
+ """
27
  from datasets import load_dataset
28
  import pandas as pd
29
+
30
+ ds = load_dataset("amazon_polarity", split=sample_split)
31
  df = pd.DataFrame({"text": ds["content"], "label": ds["label"]})
32
+
33
+ pipe = Pipeline(
34
+ [
35
+ ("tfidf", TfidfVectorizer(max_features=30000, ngram_range=(1, 2))),
36
+ ("clf", LogisticRegression(max_iter=1000)),
37
+ ]
38
+ )
39
+
40
  pipe.fit(df["text"], df["label"])
41
  joblib.dump(pipe, save_path)
42
  return pipe
43
 
44
+
45
  def load_or_bootstrap_baseline():
46
+ """
47
+ Se existir baseline_pipe.pkl, carrega.
48
+ Se nao existir e DISABLE_AUTOTRAIN != 1, treina um baseline pequeno.
49
+ """
50
  if os.path.exists(BASELINE_PATH):
51
  return joblib.load(BASELINE_PATH)
52
+
53
+ disable_auto = os.getenv("DISABLE_AUTOTRAIN", "0")
54
+ if disable_auto == "1":
55
  return None
56
+
57
  return train_small_baseline()
58
 
 
59
 
60
+ baseline_model = load_or_bootstrap_baseline()
61
+
62
+
63
+ def classify_only(text: str):
64
+ """
65
+ Apenas classifica o sentimento (positivo/negativo) e retorna JSON.
66
+ """
67
+ if not text or text.strip() == "":
68
  return {"erro": "Digite um texto."}
69
+
70
+ if baseline_model is None:
71
+ return {
72
+ "erro": (
73
+ "Modelo baseline nao encontrado. "
74
+ "Envie baseline_pipe.pkl na aba Files ou remova DISABLE_AUTOTRAIN."
75
+ )
76
+ }
77
+
78
+ proba = baseline_model.predict_proba([text])[0]
79
  pred = int(np.argmax(proba))
80
  label = "positivo" if pred == 1 else "negativo"
81
  conf = float(np.max(proba))
82
+
83
  return {"sentimento": label, "confianca": round(conf, 3)}
84
 
85
+
86
+ # ======================================================================
87
+ # 2. IA Generativa (FLAN-T5) para resposta ao cliente
88
+ # ======================================================================
89
+
90
  GEN_MODEL_ID = os.getenv("GEN_MODEL_ID", "google/flan-t5-base")
91
+
92
+ generator = hf_pipeline("text2text-generation", model=GEN_MODEL_ID)
93
 
94
  SYSTEM_PROMPT = (
95
  "Voce e um atendente virtual educado que responde em portugues do Brasil. "
96
  "Use poucas frases, tom empatico e pratico."
97
  )
98
 
99
+
100
+ def generate_reply(user_text: str, sentimento_json):
101
+ """
102
+ Gera uma resposta em PT-BR condicionada ao sentimento detectado.
103
+ """
104
+ if not user_text or user_text.strip() == "":
105
  return "Digite uma mensagem."
106
+
107
  sentimento = None
108
  if isinstance(sentimento_json, dict) and "sentimento" in sentimento_json:
109
  sentimento = sentimento_json["sentimento"]
 
111
  if sentimento == "negativo":
112
  intent = (
113
  "A avaliacao do cliente e NEGATIVA. "
114
+ "Peca desculpas, reconheca o problema, ofereca ajuda objetiva "
115
+ "e solicite informacoes adicionais (numero do pedido, produto, contato)."
116
  )
117
  elif sentimento == "positivo":
118
  intent = (
119
  "A avaliacao do cliente e POSITIVA. "
120
+ "Agradeca de forma calorosa, reforce os pontos positivos citados "
121
+ "e convide o cliente a continuar comprando."
122
  )
123
  else:
124
+ intent = (
125
+ "O sentimento nao foi identificado. "
126
+ "Responda de forma neutra, cordial e util."
127
+ )
128
 
129
  prompt = (
130
+ f"{SYSTEM_PROMPT}\n\n"
131
+ f"{intent}\n\n"
132
  f"Mensagem do cliente:\n\"{user_text}\"\n\n"
133
+ f"Escreva uma resposta curta (2 a 4 frases)."
134
  )
135
+
136
+ out = generator(
137
+ prompt,
138
+ max_length=128,
139
+ do_sample=True,
140
+ temperature=0.6,
141
+ top_p=0.9,
142
+ )[0]["generated_text"]
143
+
144
  return out
145
 
146
+
147
+ # ======================================================================
148
+ # 3. Interface Gradio - duas abas (Analise e Chatbot)
149
+ # ======================================================================
150
+
151
+ with gr.Blocks(title="Chatbot de Sentimentos - Professor Rodrigo") as demo:
152
  gr.Markdown(
153
+ """
154
+ # Chatbot de Sentimentos (ML + IA Generativa)
155
+ **Professor Rodrigo** - Projeto Final ML & DL
156
 
157
+ - Classificacao: TF-IDF + Regressao Logistica (baseline).
158
+ - Geracao: `google/flan-t5-base` para redigir respostas em PT-BR.
159
 
160
+ > Dica: envie `baseline_pipe.pkl` na aba *Files* para usar um modelo treinado por voce.
161
+ """
162
  )
163
 
164
+ # Aba 1 - somente analise de sentimento
165
  with gr.Tab("Analise de Sentimento"):
166
+ input_text = gr.Textbox(
167
+ label="Digite uma avaliacao de produto",
168
+ lines=4,
169
+ placeholder="Ex.: O produto chegou rapido e superou minhas expectativas.",
170
+ )
171
+ output_json = gr.JSON(label="Resultado da classificacao")
172
+ btn_analisar = gr.Button("Analisar")
173
+ btn_analisar.click(classify_only, inputs=input_text, outputs=output_json)
174
 
175
+ # Aba 2 - Chatbot (analise + resposta generativa)
176
  with gr.Tab("Chatbot (Analise + Resposta)"):
177
+ chat_input = gr.Textbox(
178
+ label="Mensagem do cliente",
179
+ lines=4,
180
+ placeholder="Ex.: Estou chateado, o produto chegou quebrado.",
181
+ )
182
  with gr.Row():
183
+ btn_analise_chat = gr.Button("1) Analisar sentimento")
184
+ btn_resposta = gr.Button("2) Gerar resposta")
185
+
186
+ chat_analysis = gr.JSON(label="Sentimento detectado")
187
+ chat_reply = gr.Textbox(
188
+ label="Resposta gerada pela IA",
189
+ lines=6,
190
+ )
191
+
192
+ btn_analise_chat.click(
193
+ classify_only, inputs=chat_input, outputs=chat_analysis
194
+ )
195
+ btn_resposta.click(
196
+ generate_reply, inputs=[chat_input, chat_analysis], outputs=chat_reply
197
+ )
198
+
199
 
200
  if __name__ == "__main__":
201
  demo.launch()