Text Classification
Transformers
Safetensors
Arabic
Stance Detection
Text Classification
arabic-nlp
stanceeval-2026
few-shot-learning
retrieval-augmented
Mawqif-v2
ensemble
LoRA
AraBERT
MARBERT
Instructions to use zaher-m/stanceeval2026 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use zaher-m/stanceeval2026 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="zaher-m/stanceeval2026")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("zaher-m/stanceeval2026", device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """Have a chat LLM make up labeled tweets for a target -- handy for a dev set | |
| on unseen targets, or to pad out the retrieval pool. Varies dialect and tone | |
| for a bit of diversity. | |
| python -m src.gen_synth --target "Women Driving" \\ | |
| --desc "قيادة المرأة للسيارة" --per_class 150 \\ | |
| --out data/synth/women_driving.csv | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import re | |
| import urllib.request | |
| from concurrent.futures import ThreadPoolExecutor | |
| import numpy as np | |
| import pandas as pd | |
| STANCE_AR = { | |
| "Favor": "مؤيدة وداعمة", | |
| "Against": "معارضة ورافضة", | |
| "None": "لا تُظهر موقفاً واضحاً (خبر أو سؤال أو تعليق محايد)", | |
| } | |
| DIALECTS = ["سعودية خليجية", "مصرية", "شامية", "عربية فصحى"] | |
| TONES = ["جادة", "ساخرة تهكمية", "على شكل سؤال", "انفعالية عاطفية", | |
| "عامية عفوية"] | |
| SYSTEM = ( | |
| "أنت مستخدم عربي على منصة إكس (تويتر). اكتب تغريدة واحدة واقعية قصيرة " | |
| "كما يكتبها الناس فعلاً: عفوية، قد تحوي وسماً أو إيموجي أو اختصاراً، " | |
| "بلا ترقيم زائد. اكتب التغريدة فقط دون أي شرح أو علامات اقتباس." | |
| ) | |
| SYSTEM_HARD = ( | |
| "أنت مستخدم عربي على منصة إكس (تويتر). اكتب تغريدة واحدة واقعية يصعب " | |
| "تصنيف موقفها بسهولة: عبّر عن الموقف بشكل غير مباشر أو ضمني، استخدم " | |
| "السخرية والتلميح والمبالغة واللهجة العامية والأخطاء الإملائية، وقد لا " | |
| "تذكر الهدف صراحة. اكتب التغريدة فقط دون شرح أو علامات اقتباس." | |
| ) | |
| SYSTEM_STYLE = ( | |
| "أنت مستخدم سعودي على منصة إكس (تويتر). سأعطيك أمثلة حقيقية لتغريدات " | |
| "حول موضوع معيّن. اكتب تغريدة جديدة تحاكي أسلوبها ولهجتها ووسومها " | |
| "وطريقة كتابتها (السخرية، الإيموجي، تطويل الحروف، الأخطاء الإملائية) " | |
| "لكنها تعبّر بوضوح عن الموقف المطلوب. اكتب التغريدة فقط دون شرح أو " | |
| "علامات اقتباس." | |
| ) | |
| def build_msgs_style(target, desc, stance, anchors, tag=None): | |
| tgt = f"{target} ({desc})" if desc else target | |
| ex = "\n".join(f"- {a}" for a in anchors) | |
| tag_line = f"استعمل الوسم {tag} إن ناسب. " if tag else "" | |
| user = ( | |
| f"أمثلة على أسلوب التغريدات حول {tgt}:\n{ex}\n\n" | |
| f"اكتب تغريدة جديدة موقفها {STANCE_AR[stance]} تجاه {tgt} بنفس " | |
| f"الأسلوب واللهجة والطابع. {tag_line}اجعلها واقعية وغير مكررة." | |
| ) | |
| return [{"role": "system", "content": SYSTEM_STYLE}, | |
| {"role": "user", "content": user}] | |
| def build_msgs(target, desc, stance, dialect, tone, hard=False): | |
| tgt = f"{target} ({desc})" if desc else target | |
| if hard: | |
| user = ( | |
| f"اكتب تغريدة موقفها {STANCE_AR[stance]} تجاه موضوع: {tgt}، " | |
| f"لكن بشكل غير مباشر وضمني وصعب. اللهجة: {dialect}. " | |
| f"الأسلوب: {tone}. اجعلها واقعية وغير مكررة." | |
| ) | |
| return [{"role": "system", "content": SYSTEM_HARD}, | |
| {"role": "user", "content": user}] | |
| user = ( | |
| f"اكتب تغريدة {STANCE_AR[stance]} تجاه: {tgt}.\n" | |
| f"اللهجة: {dialect}. الأسلوب: {tone}.\n" | |
| "اجعلها مختلفة وغير مكررة وواقعية." | |
| ) | |
| return [{"role": "system", "content": SYSTEM}, | |
| {"role": "user", "content": user}] | |
| def call(base_url, model, msgs, temperature, max_tokens=90, timeout=180): | |
| body = json.dumps({ | |
| "model": model, "temperature": temperature, "top_p": 0.95, | |
| "max_tokens": max_tokens, "messages": msgs, | |
| }).encode("utf-8") | |
| req = urllib.request.Request( | |
| base_url.rstrip("/") + "/chat/completions", data=body, | |
| headers={"Content-Type": "application/json"}, | |
| ) | |
| with urllib.request.urlopen(req, timeout=timeout) as r: | |
| return json.load(r)["choices"][0]["message"]["content"] | |
| def clean(text): | |
| t = text.strip().strip('"').strip("«»").strip() | |
| t = re.sub(r"\s+", " ", t) | |
| return t | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--target", required=True) | |
| ap.add_argument("--desc", default="") | |
| ap.add_argument("--per_class", type=int, default=150) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--hard", action="store_true", | |
| help="generate subtle/implicit hard-to-classify tweets") | |
| ap.add_argument("--style_file", default=None, | |
| help="CSV of real (unlabeled) tweets used as style " | |
| "anchors; switches to style-grounded generation") | |
| ap.add_argument("--style_col", default="tweet_text") | |
| ap.add_argument("--seed_tags", default="", | |
| help="comma-separated hashtags to seed into prompts") | |
| ap.add_argument("--n_anchors", type=int, default=3) | |
| ap.add_argument("--temperature", type=float, default=1.0) | |
| ap.add_argument("--concurrency", type=int, default=24) | |
| ap.add_argument("--base_url", default=os.environ.get("AUG_BASE_URL", "")) | |
| ap.add_argument("--model", default=os.environ.get("AUG_MODEL", "")) | |
| args = ap.parse_args() | |
| if not args.base_url or not args.model: | |
| raise SystemExit("set AUG_BASE_URL/AUG_MODEL") | |
| anchors_pool, tags = [], [] | |
| if args.style_file: | |
| sdf = pd.read_csv(args.style_file) | |
| anchors_pool = [str(t) for t in sdf[args.style_col].tolist() | |
| if isinstance(t, str) or not pd.isna(t)] | |
| if args.seed_tags: | |
| tags = [t.strip() for t in args.seed_tags.split(",") if t.strip()] | |
| rng = np.random.RandomState(0) | |
| jobs = [] | |
| for stance in ["Favor", "Against", "None"]: | |
| for i in range(args.per_class): | |
| if anchors_pool: | |
| idx = rng.choice(len(anchors_pool), | |
| size=min(args.n_anchors, len(anchors_pool)), | |
| replace=False) | |
| anchors = [anchors_pool[k] for k in idx] | |
| tag = tags[i % len(tags)] if tags else None | |
| jobs.append((stance, "style", anchors, tag)) | |
| else: | |
| dialect = DIALECTS[i % len(DIALECTS)] | |
| tone = TONES[i % len(TONES)] | |
| jobs.append((stance, "plain", dialect, tone)) | |
| def work(job): | |
| stance = job[0] | |
| try: | |
| if job[1] == "style": | |
| _, _, anchors, tag = job | |
| msgs = build_msgs_style(args.target, args.desc, stance, | |
| anchors, tag) | |
| else: | |
| _, _, dialect, tone = job | |
| msgs = build_msgs(args.target, args.desc, stance, | |
| dialect, tone, args.hard) | |
| out = call(args.base_url, args.model, msgs, args.temperature) | |
| return stance, clean(out) | |
| except Exception: | |
| return stance, "" | |
| rows = [] | |
| with ThreadPoolExecutor(max_workers=args.concurrency) as ex: | |
| for j, (stance, text) in enumerate(ex.map(work, jobs)): | |
| if text and len(text) > 8: | |
| rows.append({"text": text, "target": args.target, | |
| "stance": stance}) | |
| if (j + 1) % 100 == 0: | |
| print(f" {j + 1}/{len(jobs)}", flush=True) | |
| df = pd.DataFrame(rows).drop_duplicates(subset=["text"]) | |
| df = df.sample(frac=1.0, random_state=np.random.RandomState(0)) | |
| os.makedirs(os.path.dirname(os.path.abspath(args.out)), exist_ok=True) | |
| df.to_csv(args.out, index=False, encoding="utf-8-sig") | |
| print(f"[write] {len(df)} rows -> {args.out}") | |
| print(df["stance"].value_counts().to_dict()) | |
| if __name__ == "__main__": | |
| main() | |