nsr51324's picture
Create README.md
1d618c6 verified
|
Raw
History Blame Contribute Delete
12.5 kB
---
language:
- ar
- en
- fr
license: mit
tags:
- aspect-based-sentiment-analysis
- absa
- arabic-nlp
- arabic-dialects
- franco-arabic
- ensemble
- marbert
- arabert
- camelbert
- xlm-roberta
- text-classification
- multi-label-classification
pipeline_tag: text-classification
---
# DeepX-AI-Hackathon-ABSA
**A multi-dialect, multilingual Aspect-Based Sentiment Analysis (ABSA) system for Arabic reviews (Modern Standard Arabic, dialectal Arabic, Franco-Arabic, English, and French).**
An **ensemble** of 4 independently fine-tuned transformer backbones, combined with a language-aware routing system, purpose-built for real-world user reviews (e.g. Google Maps, Play Store, food delivery apps) written in mixed scripts and dialects β€” including Egyptian, Gulf, and Levantine Arabic, and Franco-Arabic (Arabic written with Latin letters and digits).
---
## πŸ“Œ Overview
This project is a complete **two-stage pipeline** that extracts, for every review:
1. **Aspects** mentioned in the review (e.g. food, service, price, cleanliness…).
2. **Sentiment** associated with each individual aspect (positive / negative / neutral).
The final output for each review is a set of **(aspect, sentiment) tuples**:
```json
{
"review_id": 12345,
"aspects": [
{"aspect": "food", "sentiment": "positive"},
{"aspect": "service", "sentiment": "negative"}
]
}
```
### Why an ensemble of 4 models?
Real-world Arabic reviews are far from uniform β€” the same user might write in Modern Standard Arabic, a regional dialect, Franco-Arabic (Latin letters + digits standing in for Arabic sounds), or plain English/French. To handle this diversity, four different backbones were fine-tuned, each with a different strength, and their outputs are combined via a **language-routed, weighted soft-voting ensemble**.
---
## 🧠 Architecture
### Stage 1 β€” Aspect Detection (Multi-Label Classification)
For every review text, the model predicts which of 9 aspect categories are mentioned:
| Aspect | Description |
|---|---|
| `food` | Food and taste |
| `service` | Service and staff |
| `price` | Price and cost |
| `cleanliness` | Cleanliness and hygiene |
| `delivery` | Delivery and shipping |
| `ambiance` | Ambiance and atmosphere |
| `app_experience` | App or website experience |
| `general` | Overall experience |
| `none` | No clear aspect (rating-only comment) |
**Model architecture:**
```
Review text β†’ Backbone (BERT / RoBERTa) β†’ [CLS] embedding
↓
Metadata Fusion (concatenated with text embedding):
- Star rating β†’ Embedding
- Business category β†’ Embedding
- Platform (Google Maps / Play Store) β†’ Embedding
↓
MLP + LayerNorm + Dropout
↓
Linear layer β†’ 9 logits (multi-label)
↓
Sigmoid + per-class decision threshold
```
### Stage 2 β€” Sentiment Classification (per aspect)
Once the aspects are extracted, for every (review, aspect) pair an **aspect-aware question** is built:
- Arabic backbones: `[Ω†Ψ¬ΩˆΩ…=X] <text> [SEP] Ω…Ψ§ Ψ±Ψ§ΩŠΩƒ فى <aspect> ؟`
- Latin-script backbone (XLM-R): `[stars=X] <text> [SEP] what about the <aspect> ?`
The pair is then classified into one of 3 classes: `positive` / `negative` / `neutral`.
```
(Text + aspect question) β†’ Backbone β†’ [CLS]
↓
Metadata Fusion + Aspect Embedding
↓
MLP (128) β†’ 3 logits
↓
Softmax
```
---
## πŸ”€ Language Router
Before any modeling, every text is automatically routed into one of the following categories using regex heuristics and marker-word dictionaries:
| Route | Description |
|---|---|
| `arabic` | Predominantly Arabic script (Arabic-character ratio > 70%) |
| `mixed` | A mix of Arabic and Latin script |
| `franco` | Franco-Arabic (e.g. `momtaz awy`, `7elw giddan`) |
| `latin` | Fully English or French |
| `other_script` | Other scripts (Chinese, Korean, Russian, etc.) |
| `empty_rating_only` | Empty / too short to classify β†’ falls back to a star-rating rule |
> **Important fix (V2):** Very short texts (1–2 words) without a clear sentiment word (e.g. "Up" or "Shady") are *not* routed to Franco β€” they're routed to `empty_rating_only` instead, since model predictions on such short strings are less reliable than a simple star-based rule.
### Which backbones handle which route?
```python
ROUTE_BACKBONES = {
'arabic': ['marbert', 'arabert', 'camelbert', 'xlmr'],
'mixed': ['marbert', 'camelbert', 'xlmr'],
'franco': ['marbert', 'arabert', 'camelbert', 'xlmr'],
'latin': ['xlmr'],
'other_script': ['xlmr'],
'empty_rating_only': [], # handled entirely by a hand-written star-based rule
}
```
---
## πŸ—οΈ Backbones
| # | Name | Hugging Face ID | Strength | Aspect F1 (val) | Sentiment F1 (val) |
|---|---|---|---|---|---|
| 1 | **MARBERT v2** | `UBC-NLP/MARBERTv2` | Arabic dialects / Twitter-style text | 0.8707 | 0.7811 |
| 2 | **AraBERT (Twitter)** | `aubmindlab/bert-base-arabertv02-twitter` | Best overall performance on MSA/dialectal text | **0.9371** | **0.8144** |
| 3 | **CAMeLBERT-DA** | `CAMeL-Lab/bert-base-arabic-camelbert-da` | Specialized in dialectal Arabic | 0.9371 | 0.8092 |
| 4 | **XLM-RoBERTa base** | `FacebookAI/xlm-roberta-base` | Multilingual β€” English, French, Franco-Arabic | 0.8025 | 0.7533 |
**Training data notes:**
- `MARBERT` and `XLM-R` were trained on an **expanded dataset**: real Arabic reviews + **synthetically generated Franco-Arabic** variants (see below).
- `AraBERT` and `CAMeLBERT` were trained on real Arabic data only.
---
## 🎲 Synthetic Franco-Arabic Data Augmentation
Since real Franco-Arabic examples are scarce in the training set, an **automatic Arabic β†’ Franco-Arabic converter** was built, based on:
1. A **common-word dictionary** (60+ frequent Arabic words/phrases mapped to their typical Franco-Arabic spellings, e.g. `Ω…Ω…ΨͺΨ§Ψ² β†’ mumtaz/momtaz`, `Ω…Ψ΄ β†’ mesh/mish`).
2. A **character-level transliteration map** for the remaining words (e.g. `Ψ­ β†’ 7`, `ΨΉ β†’ 3`, `ΨΊ β†’ 8`).
3. Controlled randomness (75% chance of using the common-word dictionary) to mimic natural spelling variation.
This expanded the `MARBERT`/`XLM-R` training set from **1,971** to **3,809** samples.
---
## βš–οΈ Ensemble & Weight Tuning
After training the four backbones, a **weighted soft-voting ensemble** combines their probability outputs, weighted per model, and thresholded per aspect class.
### Weights (from a grid search over 625 combinations on the validation set):
| Model | Weight |
|---|---|
| MARBERT | 0.5 |
| AraBERT | 1.5 |
| CAMeLBERT | 1.2 |
| XLM-R | 0.5 |
> The fixed weights actually used for final test-set inference were: `{'marbert': 1.2, 'arabert': 1.2, 'camelbert': 1.0, 'xlmr': 0.7}` β€” see [`ensemble_weights.json`](./ensemble_weights.json).
### Per-aspect decision thresholds
Saved in [`thresholds.npy`](./thresholds.npy):
| Aspect | Threshold |
|---|---|
| food | 0.50 |
| service | 0.46 |
| price | 0.36 |
| cleanliness | 0.46 |
| delivery | 0.32 |
| ambiance | 0.46 |
| app_experience | 0.36 |
| general | 0.46 |
| none | 0.52 |
---
## πŸ“ Post-Processing Rules
1. **Empty / rating-only reviews** (`empty_rating_only`) skip the models entirely and are classified directly from the star rating:
- ⭐ β‰₯ 4 β†’ `general: positive`
- ⭐ ≀ 2 β†’ `general: negative`
- ⭐ = 3 β†’ `none: neutral`
2. **Max 6 aspects** per review (kept by highest predicted probability).
3. If `none` co-occurs with other aspects, `none` is dropped (a specific aspect and "no aspect" together are contradictory).
4. If no aspect crosses its threshold, `none: neutral` is used as a default.
5. If a sentiment prediction is unavailable for a given aspect, the star rating is used as a fallback.
---
## πŸ“Š Results (Validation Set β€” 1,971 reviews)
### Main metric: Tuple F1 (aspect + sentiment must both match)
| Metric | Value |
|---|---|
| **Tuple F1** | **0.9056** (90.56%) |
| Precision | 0.9062 |
| Recall | 0.9049 |
| Aspect F1 (aspect detection only) | 0.9860 |
| Sentiment accuracy (given correct aspect) | 0.9184 |
| Review exact match (all tuples correct per review) | 0.8772 |
### Performance by route
| Route | # Samples | F1 |
|---|---|---|
| Arabic | 1,814 | 0.9182 |
| Latin | 4 | 1.0000 |
| Mixed | 24 | 0.8667 |
| Empty / rating-only | 129 | 0.6124 |
### Performance by aspect
| Aspect | F1 |
|---|---|
| food | 0.9989 |
| service | 0.9980 |
| cleanliness | 0.9946 |
| delivery | 0.9938 |
| price | 0.9929 |
| ambiance | 0.9973 |
| app_experience | 0.9956 |
| general | 0.9367 |
| none | 0.6796 |
### Sentiment confusion matrix (when the aspect is correctly detected)
| Gold \ Pred | negative | neutral | positive |
|---|---|---|---|
| **negative** | 1405 | 84 | 38 |
| **neutral** | 10 | 97 | 17 |
| **positive** | 43 | 76 | 1514 |
---
## πŸ“ Repository Contents
```
DeepX-AI-Hackathon-ABSA/
β”œβ”€β”€ models/ # Weights for all 4 backbones (Stage 1 + Stage 2)
β”œβ”€β”€ ensemble_weights.json # Final ensemble weights
β”œβ”€β”€ thresholds.npy # Per-aspect decision thresholds (9 values)
β”œβ”€β”€ submission.json # Predictions on the unlabeled set
β”œβ”€β”€ submission_test.json # Predictions on the hidden test set
β”œβ”€β”€ __huggingface_repos__.json # Repository metadata
└── README.md # This file
```
---
## πŸš€ Usage
> ⚠️ This is **not** a single model loadable with `AutoModel` and a standard `pipeline()`. It is an **ensemble of 4 backbones plus custom language-routing, preprocessing, and post-processing logic**. Running inference requires the full inference code (from the original training notebook), not just the saved weights.
### Inference outline:
```python
from transformers import AutoTokenizer, AutoModel
import torch, json, numpy as np
BACKBONES = {
'marbert': 'UBC-NLP/MARBERTv2',
'arabert': 'aubmindlab/bert-base-arabertv02-twitter',
'camelbert': 'CAMeL-Lab/bert-base-arabic-camelbert-da',
'xlmr': 'FacebookAI/xlm-roberta-base',
}
# 1. Load the Stage-1 (aspect) and Stage-2 (sentiment) checkpoints from models/
# 2. Route each text via detect_language()
# 3. Preprocess it according to its route via preprocess_by_route()
# 4. Run the backbones listed in ROUTE_BACKBONES[route]
# 5. Combine outputs with the weights in ensemble_weights.json
# 6. Apply the per-aspect thresholds in thresholds.npy
# 7. Apply the post-processing rules to build the final prediction
```
For the complete code (model definitions, helper functions, preprocessing, and ensembling logic), see the original training notebook shipped alongside this project.
---
## 🎯 Intended Use
- Multi-dialect customer review analysis (Google Maps / Play Store / food-delivery platforms).
- Extracting per-aspect strengths and weaknesses (food, service, price, cleanliness, …) for business owners.
- Sentiment dashboards for restaurants, hotels, delivery apps, clinics, and e-commerce.
## ⚠️ Limitations
- Performance on very short / empty reviews (`empty_rating_only`) is comparatively weaker (F1 = 0.61) since it relies purely on a star-rating rule rather than the model.
- The `none` aspect has weaker performance (F1 = 0.68), reflecting the difficulty of distinguishing "no clear aspect" from a generic "general" comment.
- Trained on only 1,971 labeled reviews β€” a relatively small dataset, which may limit generalization to domains not well represented in training (e.g. medical or real-estate reviews).
- Franco-Arabic training examples are synthetically generated rather than fully authentic, which may reduce accuracy on unusual real-world Franco-Arabic spelling patterns.
## πŸ† Context
This model was developed as part of the **DeepX AI Hackathon**, addressing an Aspect-Based Sentiment Analysis (ABSA) task on multilingual, multi-dialect Arabic reviews.
## πŸ“„ License
MIT
---
*For questions about this model, please open a Discussion on the Hugging Face repository page.*