aidawm/Hate-Speach-Detection-for-Persian-Social-Media

0

stars

25

commits

Python

primary language

Jun 9, 2026

updated

README

Persian Hate Speech Detection: JCLA

Hate speech detection for Persian social media using cross-lingual attention on the PHate benchmark (Delbari et al., AAAI 2024).

The main model is JCLA — Joint Cross-Lingual Attention, formerly referred to as CLA-Live, with a live trainable P1 encoder.

Active label: HateSpeech (binary, primary); Violence / Hate / Vulgar / target (auxiliary in JCLA)
Dataset: 7,056 Persian tweets — 3,528 train / 706 val / 2,822 test
Primary metric: Macro-F1


Results

SystemTest Macro-F1Test Macro-AUCNotes
P1 LoRA (twitter-RoBERTa-hate)0.8404 ± 0.00090.9186 ± 0.0004English translations via GPT
P2 only (XLM-R-large + LoRA)0.8216 ± 0.00840.9045 ± 0.0059MLM-adapted on Persian tweets
JCLA (joint cross-lingual attention)0.8502 ± 0.00320.9282 ± 0.0024formerly CLA-Live

All results averaged over 5 random seeds (42, 1, 2, 3, 4). Full per-seed metrics in experiments/*/summary.json.

JCLA Binary Hate-Class Metrics

JCLA is the presentation name for the model previously called CLA-Live. The table below reports the positive-class HateSpeech metrics over 5 seeds as mean ± standard deviation.

MetricMean ± Std
Binary F10.8292 ± 0.0023
Precision0.8423 ± 0.0232
Recall0.8173 ± 0.0176

Architectures

JCLA (Joint Cross-Lingual Attention)

Persian tweet
     │
     ├─── Branch U  [English, trainable]
     │    twitter-roberta-base-hate-latest + LoRA
     │         ↓ Mean Pool  →  h_U (768-d, live forward pass)
     │    proj_u: Linear(768 → 512) + LayerNorm  →  u  (512-d)
     │
     ├─── Branch P  [Persian, trainable]
     │    XLM-R-large + LoRA (r=32, α=64, targets: query/key/value)
     │         ↓ token hidden states  (N × T × 1024)
     │    Self-Attention Pooling  →  h_P  (1024-d)
     │    proj_p: Linear(1024 → 512) + LayerNorm  →  p  (512-d)
     │
     └─── Cross-Lingual Attention
           Q = p  (Persian queries the English representation)
           K = V = u
           MHA (8 heads, dim=512)  →  h_cross  (512-d)
           h_fused = LayerNorm(p + h_cross)       ← residual
           FFN: Linear(512 → 256) + GELU + Dropout(0.1)  →  h  (256-d)
                │
                ├── head_top:    σ(Linear(256 → 1))   ← HateSpeech
                ├── head_sub:    σ(Linear(256 → 3))   ← Violence / Hate / Vulgar
                └── head_target: σ(Linear(256 → 2))   ← Individual / Group

Loss:  L_total = L_task + 0.1 × SupConLoss(h, y_top)
       L_task  = BCE(top) + BCE(sub)[hate only] + BCE(target)[hate only]

In code and experiment paths this model still appears as cla_live / CLALiveModel; in figures, tables, and reporting it is referred to as JCLA.


Setup

conda create -n jcla python=3.10 -y
conda activate jcla
pip install -r requirements.txt

Data Setup

Get the raw Persian PHate data:

cd ~/Hate-Speach-Detection-for-Persian-Social-Media
git clone https://github.com/Zahra-D/Phate.git data/raw/Phate

Inference Testing

For interactive inference testing, open and run:

notebooks/jcla_demo.ipynb

The notebook loads the JCLA checkpoint, runs predictions on Persian text with its English translation, and displays the model outputs and test-set summaries.


Training

Full pipeline (run in this order)

Step 1 — P1 LoRA (LoRA fine-tuning on English translations):

bash scripts/submit_p1_lora.sh

Results: experiments/p1_lora/summary.json

Step 2 — P2 MLM domain adaptation:

bash scripts/submit_p2_mlm.sh

Outputs: experiments/p2_mlm/best_checkpoint/ (LoRA adapter).

Step 3 — P2 supervised fine-tuning:

bash scripts/submit_p2_supervised.sh

Results: experiments/p2_supervised/with_mlm/summary.json
Needs: Step 2 complete (MLM checkpoint must exist).

Step 4 — JCLA live cross-lingual attention (5 seeds):

bash scripts/submit_cla_live.sh

Results: experiments/cla_live/with_p2/summary.json

Monitoring jobs

runai logs <job-name> -f                              # stream logs
runai describe job <job-name> -p course-ee-559-<GASPAR_USERNAME>  # status
runai delete job <job-name> -p course-ee-559-<GASPAR_USERNAME>    # cancel/delete

Contributors

aidawm

24 commits

SaraHeip

1 commits

aidawm/Hate-Speach-Detection-for-Persian-Social-Media

0

stars

25

commits

Python

primary language

Jun 9, 2026

updated

README

Persian Hate Speech Detection: JCLA

Hate speech detection for Persian social media using cross-lingual attention on the PHate benchmark (Delbari et al., AAAI 2024).

The main model is JCLA — Joint Cross-Lingual Attention, formerly referred to as CLA-Live, with a live trainable P1 encoder.

Active label: HateSpeech (binary, primary); Violence / Hate / Vulgar / target (auxiliary in JCLA)
Dataset: 7,056 Persian tweets — 3,528 train / 706 val / 2,822 test
Primary metric: Macro-F1


Results

SystemTest Macro-F1Test Macro-AUCNotes
P1 LoRA (twitter-RoBERTa-hate)0.8404 ± 0.00090.9186 ± 0.0004English translations via GPT
P2 only (XLM-R-large + LoRA)0.8216 ± 0.00840.9045 ± 0.0059MLM-adapted on Persian tweets
JCLA (joint cross-lingual attention)0.8502 ± 0.00320.9282 ± 0.0024formerly CLA-Live

All results averaged over 5 random seeds (42, 1, 2, 3, 4). Full per-seed metrics in experiments/*/summary.json.

JCLA Binary Hate-Class Metrics

JCLA is the presentation name for the model previously called CLA-Live. The table below reports the positive-class HateSpeech metrics over 5 seeds as mean ± standard deviation.

MetricMean ± Std
Binary F10.8292 ± 0.0023
Precision0.8423 ± 0.0232
Recall0.8173 ± 0.0176

Architectures

JCLA (Joint Cross-Lingual Attention)

Persian tweet
     │
     ├─── Branch U  [English, trainable]
     │    twitter-roberta-base-hate-latest + LoRA
     │         ↓ Mean Pool  →  h_U (768-d, live forward pass)
     │    proj_u: Linear(768 → 512) + LayerNorm  →  u  (512-d)
     │
     ├─── Branch P  [Persian, trainable]
     │    XLM-R-large + LoRA (r=32, α=64, targets: query/key/value)
     │         ↓ token hidden states  (N × T × 1024)
     │    Self-Attention Pooling  →  h_P  (1024-d)
     │    proj_p: Linear(1024 → 512) + LayerNorm  →  p  (512-d)
     │
     └─── Cross-Lingual Attention
           Q = p  (Persian queries the English representation)
           K = V = u
           MHA (8 heads, dim=512)  →  h_cross  (512-d)
           h_fused = LayerNorm(p + h_cross)       ← residual
           FFN: Linear(512 → 256) + GELU + Dropout(0.1)  →  h  (256-d)
                │
                ├── head_top:    σ(Linear(256 → 1))   ← HateSpeech
                ├── head_sub:    σ(Linear(256 → 3))   ← Violence / Hate / Vulgar
                └── head_target: σ(Linear(256 → 2))   ← Individual / Group

Loss:  L_total = L_task + 0.1 × SupConLoss(h, y_top)
       L_task  = BCE(top) + BCE(sub)[hate only] + BCE(target)[hate only]

In code and experiment paths this model still appears as cla_live / CLALiveModel; in figures, tables, and reporting it is referred to as JCLA.


Setup

conda create -n jcla python=3.10 -y
conda activate jcla
pip install -r requirements.txt

Data Setup

Get the raw Persian PHate data:

cd ~/Hate-Speach-Detection-for-Persian-Social-Media
git clone https://github.com/Zahra-D/Phate.git data/raw/Phate

Inference Testing

For interactive inference testing, open and run:

notebooks/jcla_demo.ipynb

The notebook loads the JCLA checkpoint, runs predictions on Persian text with its English translation, and displays the model outputs and test-set summaries.


Training

Full pipeline (run in this order)

Step 1 — P1 LoRA (LoRA fine-tuning on English translations):

bash scripts/submit_p1_lora.sh

Results: experiments/p1_lora/summary.json

Step 2 — P2 MLM domain adaptation:

bash scripts/submit_p2_mlm.sh

Outputs: experiments/p2_mlm/best_checkpoint/ (LoRA adapter).

Step 3 — P2 supervised fine-tuning:

bash scripts/submit_p2_supervised.sh

Results: experiments/p2_supervised/with_mlm/summary.json
Needs: Step 2 complete (MLM checkpoint must exist).

Step 4 — JCLA live cross-lingual attention (5 seeds):

bash scripts/submit_cla_live.sh

Results: experiments/cla_live/with_p2/summary.json

Monitoring jobs

runai logs <job-name> -f                              # stream logs
runai describe job <job-name> -p course-ee-559-<GASPAR_USERNAME>  # status
runai delete job <job-name> -p course-ee-559-<GASPAR_USERNAME>    # cancel/delete

Contributors

aidawm

24 commits

SaraHeip

1 commits

Languages

Python

69.0%

Jupyter Notebook

23.1%

Shell

7.9%