Spaces:
Runtime error
Runtime error
Upload 3 files
Browse files- README.md +28 -7
- app.py +117 -0
- requirements.txt +13 -0
README.md
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 5.
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
-
short_description: Modelo Teste Projeto Final
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
---
|
| 3 |
+
title: Projeto Final - Chatbot de Sentimentos (ML + IA Generativa)
|
| 4 |
+
emoji: π§
|
| 5 |
+
colorFrom: yellow
|
| 6 |
+
colorTo: blue
|
| 7 |
sdk: gradio
|
| 8 |
+
sdk_version: 5.7.1
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
license: mit
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# Projeto Final - Chatbot de Sentimentos (Discriminativo + Generativo)
|
| 15 |
+
|
| 16 |
+
Curso: Machine Learning e Deep Learning
|
| 17 |
+
Data: 2025-11-12
|
| 18 |
+
Autor(es): _(preencher)_
|
| 19 |
+
|
| 20 |
+
## O que este Space demonstra
|
| 21 |
+
- Classificacao de Sentimentos (ML): TF-IDF + Regressao Logistica.
|
| 22 |
+
- IA Generativa (LLM): `google/flan-t5-base` (PT-BR) para redigir respostas educadas.
|
| 23 |
+
- Interface: Gradio com duas abas (Analise e Chatbot).
|
| 24 |
+
|
| 25 |
+
## Como funciona
|
| 26 |
+
- Se `baseline_pipe.pkl` nao estiver nos *Files*, o Space treina automaticamente um baseline pequeno (aprox. 0.5% do `amazon_polarity`) e salva localmente.
|
| 27 |
+
- Para pular o autotrain, envie `baseline_pipe.pkl` gerado no notebook ou defina `DISABLE_AUTOTRAIN=1` em Settings -> Variables.
|
| 28 |
+
|
| 29 |
+
## Como usar
|
| 30 |
+
1) Aba "Analise de Sentimento" -> digite um texto -> Analisar.
|
| 31 |
+
2) Aba "Chatbot" -> clique em "1) Analisar sentimento" e depois "2) Gerar resposta".
|
| 32 |
+
|
| 33 |
+
## Trocas e extensoes
|
| 34 |
+
- Mude o gerador via env var `GEN_MODEL_ID` (ex.: `pierreguillou/gpt2-small-portuguese`).
|
| 35 |
+
- Substitua o baseline por transformer de classificacao (custo maior, melhores metricas).
|
app.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"]
|
| 64 |
+
|
| 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()
|
requirements.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
datasets==3.0.1
|
| 3 |
+
pandas==2.2.2
|
| 4 |
+
numpy==2.1.3
|
| 5 |
+
scikit-learn==1.5.2
|
| 6 |
+
matplotlib==3.9.2
|
| 7 |
+
joblib==1.4.2
|
| 8 |
+
torch==2.4.1
|
| 9 |
+
tqdm==4.66.5
|
| 10 |
+
gradio==5.7.1
|
| 11 |
+
transformers==4.45.2
|
| 12 |
+
sentencepiece==0.2.0
|
| 13 |
+
accelerate==0.34.2
|