Spaces:
Sleeping
Sleeping
File size: 9,134 Bytes
f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 523a6d7 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 871fceb f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 19d63e9 f5a6aa3 523a6d7 93158ef 523a6d7 19d63e9 523a6d7 f5a6aa3 4f73e64 f5a6aa3 19d63e9 523a6d7 19d63e9 f5a6aa3 19d63e9 a2134d6 f5a6aa3 523a6d7 a910edd 19d63e9 523a6d7 19d63e9 523a6d7 19d63e9 523a6d7 19d63e9 523a6d7 871fceb 523a6d7 19d63e9 ff0ba3a 523a6d7 871fceb 523a6d7 19d63e9 523a6d7 19d63e9 523a6d7 19d63e9 523a6d7 19d63e9 523a6d7 f5a6aa3 19d63e9 523a6d7 19d63e9 | 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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | """
HuggingFace Space App - News Sentiment Analyzer
Compatible with Gradio 6.20.0 (provided by Space) + ZeroGPU
"""
# IMPORTANT: `spaces` MUST be imported first, before torch/transformers or
# any other CUDA-touching library. ZeroGPU intercepts CUDA initialization,
# and importing it late causes:
# RuntimeError: CUDA has been initialized before importing the `spaces` package.
import spaces
import gradio as gr
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from scipy.special import softmax
import numpy as np
print("π Loading Sentiment Models...")
# ============================================
# 1. LOAD ROBERTA MODEL (loaded on CPU; moved to GPU per-call by @spaces.GPU)
# ============================================
print("π₯ Loading RoBERTa model...")
roberta_model_name = "cardiffnlp/twitter-roberta-base-sentiment-latest"
roberta_tokenizer = AutoTokenizer.from_pretrained(roberta_model_name)
roberta_model = AutoModelForSequenceClassification.from_pretrained(roberta_model_name)
roberta_model.eval()
print("β
RoBERTa model loaded!")
# ============================================
# 2. LOAD SIEBERT MODEL
# ============================================
print("π₯ Loading Siebert model...")
siebert_model_name = "siebert/sentiment-roberta-large-english"
siebert_tokenizer = AutoTokenizer.from_pretrained(siebert_model_name)
siebert_model = AutoModelForSequenceClassification.from_pretrained(siebert_model_name)
siebert_model.eval()
print("β
Siebert model loaded!")
print("π― All models ready!")
# ============================================
# SENTIMENT ANALYSIS FUNCTIONS
# ============================================
def analyze_roberta(text, device):
"""Analyze sentiment using RoBERTa model"""
try:
if not text or len(text.strip()) < 5:
return {'negative': 0.33, 'neutral': 0.34, 'positive': 0.33}
model = roberta_model.to(device)
encoded = roberta_tokenizer(
text,
return_tensors='pt',
truncation=True,
max_length=512,
padding=True
).to(device)
with torch.no_grad():
output = model(**encoded)
scores = softmax(output.logits.cpu().numpy()[0])
return {
'negative': float(scores[0]),
'neutral': float(scores[1]),
'positive': float(scores[2])
}
except Exception as e:
print(f"β RoBERTa error: {e}")
return {'negative': 0.33, 'neutral': 0.34, 'positive': 0.33}
def analyze_siebert(text, device):
"""Analyze sentiment using Siebert model"""
try:
if not text or len(text.strip()) < 5:
return {'negative': 0.5, 'positive': 0.5}
model = siebert_model.to(device)
encoded = siebert_tokenizer(
text,
return_tensors='pt',
truncation=True,
max_length=512,
padding=True
).to(device)
with torch.no_grad():
output = model(**encoded)
scores = softmax(output.logits.cpu().numpy()[0])
return {
'negative': float(scores[0]),
'positive': float(scores[1])
}
except Exception as e:
print(f"β Siebert error: {e}")
return {'negative': 0.5, 'positive': 0.5}
@spaces.GPU(duration=30)
def get_ensemble_sentiment(text):
"""Combine both models for accurate results. Runs inside a ZeroGPU allocation."""
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
roberta_result = analyze_roberta(text, device)
siebert_result = analyze_siebert(text, device)
siebert_neg = siebert_result['negative']
siebert_pos = siebert_result['positive']
diff = abs(siebert_pos - siebert_neg)
if diff < 0.2:
siebert_neutral = 1 - diff
siebert_neg = siebert_neg * (1 - siebert_neutral / 2)
siebert_pos = siebert_pos * (1 - siebert_neutral / 2)
else:
siebert_neutral = 0.1
siebert_neg = siebert_neg * 0.95
siebert_pos = siebert_pos * 0.95
ensemble_neg = (roberta_result['negative'] + siebert_neg) / 2
ensemble_neu = (roberta_result['neutral'] + siebert_neutral) / 2
ensemble_pos = (roberta_result['positive'] + siebert_pos) / 2
total = ensemble_neg + ensemble_neu + ensemble_pos
if total > 0:
ensemble_neg /= total
ensemble_neu /= total
ensemble_pos /= total
max_score = max(ensemble_neg, ensemble_neu, ensemble_pos)
if max_score == ensemble_neg:
label = 'Negative'
elif max_score == ensemble_pos:
label = 'Positive'
else:
label = 'Neutral'
return {
'sentiment': label,
'confidence': round(max_score, 4),
'negative_score': round(ensemble_neg, 4),
'neutral_score': round(ensemble_neu, 4),
'positive_score': round(ensemble_pos, 4)
}
def predict_single(text):
"""Predict sentiment for single text"""
if not text or len(text.strip()) < 5:
return "β οΈ Please enter some text to analyze."
result = get_ensemble_sentiment(text)
sentiment_emoji = {'Positive': 'β
', 'Negative': 'β', 'Neutral': 'βͺ'}
emoji = sentiment_emoji.get(result['sentiment'], 'βͺ')
confidence_pct = result['confidence'] * 100
if confidence_pct > 80:
confidence_color = "π’"
elif confidence_pct > 60:
confidence_color = "π‘"
else:
confidence_color = "π΄"
return f"""
# π Sentiment Analysis Results
---
### {emoji} **Final Sentiment: {result['sentiment']}**
**Confidence:** {confidence_color} {confidence_pct:.1f}%
---
### π Score Breakdown
| Aspect | Score |
|--------|-------|
| β
Positive | {result['positive_score']*100:.1f}% |
| β Negative | {result['negative_score']*100:.1f}% |
| βͺ Neutral | {result['neutral_score']*100:.1f}% |
---
### π Analyzed Text
> {text[:200]}{'...' if len(text) > 200 else ''}
"""
# ============================================
# API ENDPOINT FOR EXTERNAL CALLS
# ============================================
def api_predict(text: str) -> dict:
"""API endpoint for external calls (JSON response)"""
if not text or len(text.strip()) < 5:
return {
'error': 'Text too short or empty',
'sentiment': 'Neutral',
'confidence': 0.0
}
return get_ensemble_sentiment(text)
# ============================================
# GRADIO INTERFACE (Gradio 6.20.0)
# ============================================
with gr.Blocks(title="π° News Sentiment Analyzer") as demo:
gr.Markdown("""
# π° News Sentiment Analyzer
### π― Ensemble Model: RoBERTa + Siebert
This analyzer combines two powerful models:
- **RoBERTa**: Twitter-based sentiment model (negative, neutral, positive)
- **Siebert**: Large-scale sentiment model (negative, positive)
""")
gr.api(api_predict, api_name="predict")
with gr.Row():
with gr.Column(scale=2):
text_input = gr.Textbox(
label="π Enter News Text",
placeholder="Paste your news article here...",
lines=10,
max_lines=20
)
with gr.Row():
submit_btn = gr.Button("π Analyze Sentiment", variant="primary")
clear_btn = gr.Button("ποΈ Clear", variant="secondary")
gr.Markdown("### π‘ Try these examples:")
examples = [
["Apple reported record profits with revenue growth of 15% this quarter."],
["The company faces severe criticism over data breach affecting millions of users."],
["Government announced new policies to boost economic growth."],
["Defense ministry successfully tested new hypersonic missile system."],
["The bank reported $2 billion in losses due to failed investments."]
]
for example in examples:
gr.Examples(
inputs=text_input,
examples=[example]
)
with gr.Column(scale=1):
output = gr.Markdown(label="π Analysis Results", value="Enter text and click 'Analyze Sentiment' to see results here.")
submit_btn.click(
fn=predict_single,
inputs=text_input,
outputs=output
)
clear_btn.click(
fn=lambda: "",
inputs=[],
outputs=text_input
)
text_input.submit(
fn=predict_single,
inputs=text_input,
outputs=output
)
if __name__ == "__main__":
print("\n" + "=" * 60)
print("π News Sentiment Analyzer")
print("=" * 60)
print("π€ Models: RoBERTa + Siebert (ZeroGPU)")
print("=" * 60 + "\n")
demo.launch(theme="soft") |