Views
No views yet
normalabusiveincitementincitement.csv| Metric | Value |
|---|---|
| Accuracy | 82.40% |
| F1 Macro | 0.8025 |
| F1 Incitement | 0.7752 |
| ID | Label |
|---|---|
| 0 | normal |
| 1 | abusive |
| 2 | incitement |

model.pt weights plus the MARBERTv2 encoder and tokenizer.1import torch
2import torch.nn as nn
3import re
4import unicodedata
5from transformers import AutoTokenizer, AutoModel
6from huggingface_hub import hf_hub_download
7
8# =========================
9# 1. CONFIGURATION
10# =========================
11DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12print("device:", DEVICE)
13
14BASE_MODEL = "UBC-NLP/MARBERTv2"
15REPO_ID = "amitca71/marbertv2-levantine-incitement-detector" # second repo
16MAX_LENGTH = 160
17USE_NORMALIZED_TEXT_FOR_MODEL = False
18POOLING_STRATEGY = "cls" # this checkpoint expects 768-d, not cls_mean_max
19
20ARABIC_DIACRITICS = re.compile(r'[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]')
21
22
23# =========================
24# 2. TEXT NORMALIZATION
25# =========================
26def normalize_arabic(text: str) -> str:
27 text = unicodedata.normalize("NFKC", text or "").strip()
28 text = ARABIC_DIACRITICS.sub("", text)
29 text = text.replace("أ", "ا").replace("إ", "ا").replace("آ", "ا")
30 text = text.replace("ى", "ي").replace("ؤ", "و").replace("ئ", "ي").replace("ة", "ه")
31 text = re.sub(r"[^\w\s#@/]", " ", text)
32 text = re.sub(r"\s+", " ", text)
33 return text.strip().lower()
34
35def text_for_model(text: str, use_normalized: bool = USE_NORMALIZED_TEXT_FOR_MODEL) -> str:
36 return normalize_arabic(text) if use_normalized else (text or "").strip()
37
38
39# =========================
40# 3. MODEL DEFINITION
41# =========================
42class MarbertMultiTask(nn.Module):
43 def __init__(self, base_model_name: str, pooling_strategy: str = "cls"):
44 super().__init__()
45 # IMPORTANT: use "encoder" because the checkpoint keys are encoder.*
46 self.encoder = AutoModel.from_pretrained(base_model_name)
47 self.pooling_strategy = pooling_strategy
48
49 hidden_size = self.encoder.config.hidden_size # 768 for MARBERTv2
50
51 if pooling_strategy in {"cls", "mean", "max"}:
52 rep_dim = hidden_size
53 elif pooling_strategy == "cls_mean_max":
54 rep_dim = hidden_size * 3
55 else:
56 raise ValueError(f"Unknown pooling_strategy: {pooling_strategy}")
57
58 self.classifier = nn.Linear(rep_dim, 3)
59 self.lexicon_head = nn.Linear(rep_dim, 1)
60
61 def masked_mean_pool(self, last_hidden_state, attention_mask):
62 mask = attention_mask.unsqueeze(-1).expand(last_hidden_state.size()).float()
63 masked_embeddings = last_hidden_state * mask
64 summed = masked_embeddings.sum(dim=1)
65 counts = mask.sum(dim=1).clamp(min=1e-9)
66 return summed / counts
67
68 def masked_max_pool(self, last_hidden_state, attention_mask):
69 mask = attention_mask.unsqueeze(-1).bool()
70 masked = last_hidden_state.masked_fill(~mask, float("-inf"))
71 pooled = masked.max(dim=1).values
72 pooled[torch.isinf(pooled)] = 0.0
73 return pooled
74
75 def forward(self, input_ids, attention_mask, token_type_ids=None):
76 outputs = self.encoder(
77 input_ids=input_ids,
78 attention_mask=attention_mask,
79 token_type_ids=token_type_ids,
80 return_dict=True,
81 )
82
83 last_hidden_state = outputs.last_hidden_state
84 cls_vec = last_hidden_state[:, 0, :]
85 mean_vec = self.masked_mean_pool(last_hidden_state, attention_mask)
86 max_vec = self.masked_max_pool(last_hidden_state, attention_mask)
87
88 if self.pooling_strategy == "cls":
89 pooled = cls_vec
90 elif self.pooling_strategy == "mean":
91 pooled = mean_vec
92 elif self.pooling_strategy == "max":
93 pooled = max_vec
94 elif self.pooling_strategy == "cls_mean_max":
95 pooled = torch.cat([cls_vec, mean_vec, max_vec], dim=-1)
96 else:
97 raise ValueError(f"Unknown pooling_strategy: {self.pooling_strategy}")
98
99 logits = self.classifier(pooled)
100 lexicon_logits = self.lexicon_head(pooled)
101
102 return {
103 "logits": logits,
104 "lexicon_logits": lexicon_logits,
105 }
106
107
108# =========================
109# 4. LOAD TOKENIZER + MODEL
110# =========================
111print("Loading model and tokenizer...")
112tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
113
114model = MarbertMultiTask(BASE_MODEL, pooling_strategy=POOLING_STRATEGY)
115
116# Download checkpoint
117model_path = hf_hub_download(repo_id=REPO_ID, filename="model.pt")
118state_dict = torch.load(model_path, map_location="cpu", weights_only=True)
119
120# Sanity checks
121print("Model classifier.in_features:", model.classifier.in_features)
122print("Checkpoint classifier.weight shape:", state_dict["classifier.weight"].shape)
123print("Model lexicon_head.in_features:", model.lexicon_head.in_features)
124print("Checkpoint lexicon_head.weight shape:", state_dict["lexicon_head.weight"].shape)
125
126# This should now load cleanly
127model.load_state_dict(state_dict)
128
129model.to(DEVICE)
130model.eval()
131
132bundle = {
133 "model": model,
134 "tokenizer": tokenizer,
135 "label_map": {0: "normal", 1: "abusive", 2: "incitement"},
136}
137
138
139# =========================
140# 5. PREDICTION FUNCTION
141# =========================
142def predict_one(bundle, text: str):
143 encoded = bundle["tokenizer"](
144 text_for_model(text),
145 truncation=True,
146 padding="max_length",
147 max_length=MAX_LENGTH,
148 return_tensors="pt",
149 )
150
151 encoded = {k: v.to(DEVICE) for k, v in encoded.items()}
152
153 with torch.no_grad():
154 out = bundle["model"](**encoded)
155
156 logits = out["logits"]
157 lexicon_logits = out["lexicon_logits"]
158
159 probs = torch.softmax(logits, dim=-1).squeeze(0).tolist()
160 pred_id = int(torch.argmax(logits, dim=-1).item())
161
162 response = {
163 "pred_label": bundle["label_map"][pred_id],
164 "confidence": probs[pred_id],
165 "prob_normal": probs[0],
166 "prob_abusive": probs[1],
167 "prob_incitement": probs[2],
168 "lexicon_signal_prob": torch.sigmoid(lexicon_logits).squeeze(0).item(),
169 }
170
171 return response
172
173
174# =========================
175# 6. TEST
176# =========================
177sample_text = "انت يا عميل السفارات يا ابن الكلب حسابك عسير"
178response = predict_one(bundle, sample_text)
179
180print("\nPrediction Response:")
181print(response)
182