Unknownhackerr commited on
Commit
8b1f63b
·
verified ·
1 Parent(s): 2e8d601

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -0
app.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FastAPI app for HF Spaces: 3 sentiment models + tiny test UI
2
+ from fastapi import FastAPI
3
+ from fastapi.responses import HTMLResponse
4
+ from pydantic import BaseModel
5
+ from typing import List, Dict, Any
6
+
7
+ try:
8
+ import torch
9
+ DEVICE = 0 if torch.cuda.is_available() else -1
10
+ except Exception:
11
+ DEVICE = -1
12
+
13
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification, TextClassificationPipeline
14
+
15
+ # ---- Models ----
16
+ FABSA_ID = "Anudeep-Narala/fabsa-roberta-sentiment" # 3-class
17
+ TWITTER_ID = "cardiffnlp/twitter-roberta-base-sentiment-latest" # 3-class
18
+ MOOD_ID = "Priyanshuchaudhary2425/MoodMeter-sentimental-analysis" # 2-class (pos/neg)
19
+
20
+ def load_pipe(model_id: str) -> TextClassificationPipeline:
21
+ tok = AutoTokenizer.from_pretrained(model_id)
22
+ mdl = AutoModelForSequenceClassification.from_pretrained(model_id)
23
+ return TextClassificationPipeline(
24
+ model=mdl, tokenizer=tok, device=DEVICE,
25
+ return_all_scores=True, truncation=True
26
+ )
27
+
28
+ print("Loading models…")
29
+ fabsa = load_pipe(FABSA_ID)
30
+ twitter = load_pipe(TWITTER_ID)
31
+ mood = load_pipe(MOOD_ID)
32
+ print("Models ready.")
33
+
34
+ def norm3(scores: List[Dict[str, Any]]):
35
+ """Map list[{label,score}] -> (pred, scores3, top, margin)."""
36
+ out = {"negative": 0.0, "neutral": 0.0, "positive": 0.0}
37
+ for e in scores:
38
+ lbl = e["label"].lower()
39
+ s = float(e["score"])
40
+ if "neg" in lbl or lbl == "label_0": out["negative"] = s
41
+ elif "neu" in lbl or lbl == "label_1": out["neutral"] = s
42
+ elif "pos" in lbl or lbl == "label_2": out["positive"] = s
43
+ pred = max(out, key=out.get)
44
+ vals = sorted(out.values(), reverse=True)
45
+ top, margin = float(out[pred]), float(vals[0]-vals[1])
46
+ return pred, out, top, margin
47
+
48
+ def norm2(scores: List[Dict[str, Any]]):
49
+ """Embed 2-class model into 3-class dict (neutral=0)."""
50
+ d = {"negative": 0.0, "positive": 0.0}
51
+ for e in scores:
52
+ lbl = e["label"].lower()
53
+ s = float(e["score"])
54
+ if "neg" in lbl or lbl == "label_0": d["negative"] = s
55
+ elif "pos" in lbl or lbl == "label_1": d["positive"] = s
56
+ pred = "negative" if d["negative"] >= d["positive"] else "positive"
57
+ top = float(max(d.values()))
58
+ margin = float(abs(d["negative"] - d["positive"]))
59
+ out3 = {"negative": d["negative"], "neutral": 0.0, "positive": d["positive"]}
60
+ return pred, out3, top, margin
61
+
62
+ def fuse(fabsa_label: str, twitter_label: str) -> str:
63
+ # Ensemble rule you asked for:
64
+ # FABSA=neg -> neg; else if Twitter=neu -> neu; else Twitter
65
+ if fabsa_label == "negative": return "negative"
66
+ if twitter_label == "neutral": return "neutral"
67
+ return twitter_label
68
+
69
+ app = FastAPI(title="HF Space — Sentiment Inference (FABSA + MoodMeter + Twitter)")
70
+
71
+ class PredictIn(BaseModel):
72
+ text: str
73
+
74
+ class BatchIn(BaseModel):
75
+ texts: List[str]
76
+
77
+ @app.get("/health")
78
+ def health():
79
+ return {"ok": True, "device": DEVICE}
80
+
81
+ @app.post("/predict")
82
+ def predict(inp: PredictIn):
83
+ t = (inp.text or "").strip()
84
+ f_raw = fabsa(t)[0]
85
+ t_raw = twitter(t)[0]
86
+ m_raw = mood(t)[0]
87
+
88
+ f_pred, f_scores, f_top, f_margin = norm3(f_raw)
89
+ t_pred, t_scores, t_top, t_margin = norm3(t_raw)
90
+ m_pred, m_scores, m_top, m_margin = norm2(m_raw)
91
+
92
+ return {
93
+ "text": t,
94
+ "fabsa": {"label": f_pred, "scores": f_scores, "top": f_top, "margin": f_margin},
95
+ "twitter": {"label": t_pred, "scores": t_scores, "top": t_top, "margin": t_margin},
96
+ "mood": {"label": m_pred, "scores": m_scores, "top": m_top, "margin": m_margin},
97
+ "ensemble": {"label": fuse(f_pred, t_pred)}
98
+ }
99
+
100
+ @app.post("/batch")
101
+ def batch(inp: BatchIn):
102
+ texts = [(x or "").strip() for x in inp.texts]
103
+ f_raw = fabsa(texts, batch_size=16)
104
+ t_raw = twitter(texts, batch_size=16)
105
+ m_raw = mood(texts, batch_size=16)
106
+
107
+ out = []
108
+ for i, t in enumerate(texts):
109
+ f_pred, f_scores, f_top, f_margin = norm3(f_raw[i])
110
+ t_pred, t_scores, t_top, t_margin = norm3(t_raw[i])
111
+ m_pred, m_scores, m_top, m_margin = norm2(m_raw[i])
112
+ out.append({
113
+ "text": t,
114
+ "fabsa": {"label": f_pred, "scores": f_scores, "top": f_top, "margin": f_margin},
115
+ "twitter": {"label": t_pred, "scores": t_scores, "top": t_top, "margin": t_margin},
116
+ "mood": {"label": m_pred, "scores": m_scores, "top": m_top, "margin": m_margin},
117
+ "ensemble": {"label": fuse(f_pred, t_pred)}
118
+ })
119
+ return {"items": out}
120
+
121
+ # --- Super-simple test page (no backend needed) ---
122
+ INDEX_HTML = """<!doctype html>
123
+ <html lang="en"><head>
124
+ <meta charset="utf-8"/>
125
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
126
+ <title>Sentiment Space — Quick Test</title>
127
+ <style>
128
+ body{font-family: system-ui, Arial, sans-serif; background:#0b0b0b; color:#eee; padding:24px; max-width:900px; margin:0 auto;}
129
+ .card{border:1px solid #333; border-radius:12px; padding:16px; background:#111; margin-top:16px;}
130
+ textarea{width:100%; background:#0e0e0e; color:#eee; border:1px solid #333; border-radius:8px; padding:12px;}
131
+ button{background:#4F46E5; color:#fff; border:none; padding:10px 14px; border-radius:8px; cursor:pointer;}
132
+ pre{background:#0e0e0e; padding:12px; border-radius:8px; overflow:auto;}
133
+ .row{display:grid; grid-template-columns: 1fr 1fr; gap:16px;}
134
+ </style>
135
+ </head><body>
136
+ <h1>Sentiment Model Space — Quick Test</h1>
137
+ <div class="card">
138
+ <textarea id="txt" rows="5" placeholder="Type something like: I feel exhausted and nothing seems to help."></textarea>
139
+ <div style="margin-top:12px; display:flex; gap:12px;">
140
+ <button onclick="run()">Predict</button>
141
+ <button onclick="demo()">Demo Text</button>
142
+ </div>
143
+ <div id="status" style="opacity:.7; margin-top:8px;"></div>
144
+ </div>
145
+
146
+ <div class="row">
147
+ <div class="card"><h3>FABSA</h3><pre id="fabsa"></pre></div>
148
+ <div class="card"><h3>Twitter-RoBERTa</h3><pre id="twitter"></pre></div>
149
+ </div>
150
+ <div class="row">
151
+ <div class="card"><h3>MoodMeter (2-class)</h3><pre id="mood"></pre></div>
152
+ <div class="card"><h3>Ensemble</h3><pre id="ens"></pre></div>
153
+ </div>
154
+
155
+ <script>
156
+ async function run(){
157
+ const s = document.getElementById('txt').value.trim();
158
+ if(!s){ alert('Enter some text'); return; }
159
+ set('#status','Predicting...')
160
+ try{
161
+ const r = await fetch('/predict', {
162
+ method:'POST', headers:{'Content-Type':'application/json'},
163
+ body: JSON.stringify({text:s})
164
+ });
165
+ const j = await r.json();
166
+ set('#fabsa', fmt(j.fabsa));
167
+ set('#twitter', fmt(j.twitter));
168
+ set('#mood', fmt(j.mood));
169
+ set('#ens', JSON.stringify(j.ensemble, null, 2));
170
+ set('#status','Done.')
171
+ }catch(e){
172
+ set('#status','Error: '+e.message)
173
+ }
174
+ }
175
+ function set(sel, val){ document.querySelector(sel).textContent = typeof val==='string'? val : JSON.stringify(val,null,2); }
176
+ function fmt(x){
177
+ if(!x) return '';
178
+ const s = x.scores||{};
179
+ return JSON.stringify({
180
+ label: x.label,
181
+ neg: round(s.negative), neu: round(s.neutral), pos: round(s.positive),
182
+ top: round(x.top), margin: round(x.margin)
183
+ }, null, 2);
184
+ }
185
+ function round(v){ return (v==null? null : Math.round(v*1000)/1000); }
186
+ function demo(){
187
+ document.getElementById('txt').value = "It’s 3 a.m. again. I’m staring at the ceiling replaying everything I might fail tomorrow.";
188
+ }
189
+ </script>
190
+ </body></html>
191
+ """
192
+
193
+ @app.get("/", response_class=HTMLResponse)
194
+ def index():
195
+ return HTMLResponse(INDEX_HTML)