|
import gradio as gr |
|
import torch |
|
from transformers import DebertaV2Model, DebertaV2Config, AutoTokenizer, PreTrainedModel |
|
from transformers.models.deberta.modeling_deberta import ContextPooler |
|
from transformers import pipeline |
|
import torch.nn as nn |
|
|
|
|
|
BASE_MODEL = "microsoft/mdeberta-v3-base" |
|
SENT_SUBJ_MODEL = "MatteoFasulo/mdeberta-v3-base-subjectivity-sentiment-multilingual-no-arabic" |
|
SUBJ_ONLY_MODEL = "MatteoFasulo/mdeberta-v3-base-subjectivity-multilingual-no-arabic" |
|
|
|
|
|
from functools import partial |
|
|
|
def build_custom_model(sentiment_dim=0): |
|
class CustomModel(PreTrainedModel): |
|
config_class = DebertaV2Config |
|
def __init__(self, config, *args, **kwargs): |
|
super().__init__(config, *args, **kwargs) |
|
self.deberta = DebertaV2Model(config) |
|
self.pooler = ContextPooler(config) |
|
self.dropout = nn.Dropout(0.1) |
|
hidden_dim = self.pooler.output_dim + sentiment_dim |
|
self.classifier = nn.Linear(hidden_dim, config.num_labels) |
|
def forward(self, input_ids, attention_mask=None, **sent_kwargs): |
|
x = self.deberta(input_ids=input_ids, attention_mask=attention_mask)[0] |
|
pooled = self.pooler(x) |
|
if sentiment_dim: |
|
sent_feats = torch.stack((sent_kwargs['positive'], sent_kwargs['neutral'], sent_kwargs['negative']), dim=1) |
|
pooled = torch.cat((pooled, sent_feats), dim=1) |
|
return self.classifier(self.dropout(pooled)) |
|
return CustomModel |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL) |
|
|
|
|
|
cfg1 = DebertaV2Config.from_pretrained(SENT_SUBJ_MODEL, num_labels=2, id2label={0:'OBJ',1:'SUBJ'}, label2id={'OBJ':0,'SUBJ':1}) |
|
Model1Cls = build_custom_model(sentiment_dim=3) |
|
model1 = Model1Cls.from_pretrained(SENT_SUBJ_MODEL, config=cfg1, ignore_mismatched_sizes=True) |
|
|
|
|
|
cfg2 = DebertaV2Config.from_pretrained(SUBJ_ONLY_MODEL, num_labels=2, id2label={0:'OBJ',1:'SUBJ'}, label2id={'OBJ':0,'SUBJ':1}) |
|
Model2Cls = build_custom_model(sentiment_dim=0) |
|
model2 = Model2Cls.from_pretrained(SUBJ_ONLY_MODEL, config=cfg2) |
|
|
|
|
|
sentiment_pipe = pipeline("sentiment-analysis", model="cardiffnlp/twitter-xlm-roberta-base-sentiment", tokenizer="cardiffnlp/twitter-xlm-roberta-base-sentiment", top_k=None) |
|
|
|
def get_sentiment_scores(text): |
|
out = sentiment_pipe(text)[0] |
|
return {list(d.keys())[0]: list(d.values())[0] for d in out} |
|
|
|
|
|
def analyze(text): |
|
|
|
inputs = tokenizer(text, truncation=True, padding=True, max_length=256, return_tensors='pt') |
|
|
|
scores = get_sentiment_scores(text) |
|
pos, neu, neg = scores['positive'], scores['neutral'], scores['negative'] |
|
|
|
logits1 = model1(input_ids=inputs.input_ids, attention_mask=inputs.attention_mask, positive=torch.tensor([pos]), neutral=torch.tensor([neu]), negative=torch.tensor([neg])) |
|
p1 = torch.softmax(logits1, dim=1)[0] |
|
|
|
logits2 = model2(input_ids=inputs.input_ids, attention_mask=inputs.attention_mask) |
|
p2 = torch.softmax(logits2, dim=1)[0] |
|
|
|
return { |
|
'Positive': f"{pos:.2%}", 'Neutral': f"{neu:.2%}", 'Negative': f"{neg:.2%}", |
|
'Sent-Subj OBJ': f"{p1[0]:.2%}", 'Sent-Subj SUBJ': f"{p1[1]:.2%}", |
|
'TextOnly OBJ': f"{p2[0]:.2%}", 'TextOnly SUBJ': f"{p2[1]:.2%}" |
|
} |
|
|
|
|
|
dark_theme = gr.themes.Dark() |
|
|
|
with gr.Blocks(theme=dark_theme, css=""" |
|
#result_table td { padding: 8px; font-size: 1rem; } |
|
#header { text-align: center; font-size: 2rem; font-weight: bold; margin-bottom: 10px; } |
|
""") as demo: |
|
gr.Markdown("<div id='header'>π Advanced Subjectivity & Sentiment Dashboard π</div>") |
|
with gr.Row(): |
|
txt = gr.Textbox(label="Enter text to analyze", placeholder="Paste news sentence here...", lines=2) |
|
btn = gr.Button("Analyze π", variant="primary") |
|
with gr.Tabs(): |
|
with gr.TabItem("Overview π"): |
|
chart = gr.BarPlot(x="category", y="value", label="Results", elem_id="result_chart") |
|
with gr.TabItem("Raw Scores π"): |
|
table = gr.Dataframe(headers=["Metric", "Value"], datatype=["str","str"], interactive=False, elem_id="result_table") |
|
with gr.TabItem("About βΉοΈ"): |
|
gr.Markdown("This dashboard uses two DeBERTa-based models (with and without sentiment integration) to detect subjectivity, alongside sentiment scores from an XLM-RoBERTa model.") |
|
gr.Markdown("**Threshold** for subjective classification is adjustable in code (default: 0.65). Feel free to fork and customize! π") |
|
|
|
btn.click(fn=analyze, inputs=txt, outputs=[chart, table]) |
|
|
|
btn.js_on_event("click", { |
|
"type": "confetti", |
|
"props": {"particleCount": 100, "spread": 60} |
|
}) |
|
|
|
|
|
demo.queue().launch(server_name="0.0.0.0", share=True) |
|
|