ProfRod100 commited on
Commit
cc19934
·
verified ·
1 Parent(s): 8ea0e2a

Update app.py

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