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