ppryan commited on
Commit
e02998b
·
verified ·
1 Parent(s): ec1ae16

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +122 -14
  2. app.py +74 -0
  3. requirements.txt +10 -0
  4. setup.py +14 -0
README.md CHANGED
@@ -1,14 +1,122 @@
1
- ---
2
- title: Multilingual Sentiment Analysis
3
- emoji: 🏢
4
- colorFrom: yellow
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 6.21.0
8
- python_version: '3.12'
9
- app_file: app.py
10
- pinned: false
11
- short_description: Analyze sentiment using a fine-tuned XLM-RoBERTa model.
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ # Multilingual Sentiment Analysis
4
+
5
+ An end-to-end NLP project for three-class sentiment classification: **negative**, **neutral**, and **positive**. The repository contains two XLM-RoBERTa experiments:
6
+
7
+ 1. an archived English airline-tweet model, and
8
+ 2. the current general multilingual Twitter-sentiment model.
9
+
10
+ The current model is exposed through a Gradio app and is evaluated both overall and separately for every supported language.
11
+
12
+ ## 🎮 Demo: Gradio App
13
+
14
+ Run the interactive demo locally:
15
+ ```bash
16
+ python app.py
17
+ ```
18
+
19
+ ![Gradio app batch-analysis demo](assets/demo-screenshot.png)
20
+
21
+ ## Results at a glance
22
+
23
+ | Experiment | Data | Evaluation | Result |
24
+ | --- | --- | --- | --- |
25
+ | Airline tweets (legacy) | English airline tweets | Validation set | 84.24% accuracy, 0.842 weighted F1 |
26
+ | General multilingual (current) | 8-language Twitter sentiment dataset | Held-out test set (6,960 tweets) | 66.52% accuracy, 0.663 macro F1 |
27
+
28
+ The two scores are **not directly comparable**: the airline model was evaluated on a narrow, English-only domain, whereas the multilingual model is evaluated across eight languages and broader Twitter content.
29
+
30
+ ### Current multilingual model: confusion matrix
31
+
32
+ <img src="results/multilingual/eval/confusion_matrix.png" width="500">
33
+
34
+ ### Airline-tweet model: evaluation plots
35
+
36
+ <img src="results/airline_tweets/eval/confusion_matrix.png" width="400">
37
+
38
+ <img src="results/airline_tweets/eval/loss_curve.png" width="400">
39
+
40
+
41
+ ### Current multilingual model: test macro F1 by language
42
+
43
+ | Language | Macro F1 |
44
+ | --- | ---: |
45
+ | German | 0.731 |
46
+ | French | 0.721 |
47
+ | Portuguese | 0.699 |
48
+ | English | 0.692 |
49
+ | Spanish | 0.667 |
50
+ | Arabic | 0.647 |
51
+ | Italian | 0.625 |
52
+ | Hindi | 0.516 |
53
+
54
+
55
+ The neutral class is the most difficult overall (0.588 F1). Hindi is the weakest supported language, so this project does not claim equal quality across all languages.
56
+
57
+ ## How it works
58
+
59
+ 1. `multilingual_sentiment_analysis.preprocess` downloads and prepares the multilingual dataset.
60
+ 2. Text is cleaned by removing URLs and mentions, normalizing whitespace, and removing only the `#` marker. Unicode text, accents, emojis, and non-Latin scripts are preserved.
61
+ 3. `multilingual_sentiment_analysis.train` fine-tunes `xlm-roberta-base` for three-class sequence classification.
62
+ 4. `multilingual_sentiment_analysis.evaluate` reports overall metrics, per-language metrics, and a confusion matrix.
63
+ 5. `app.py` provides single-text and batch inference with the saved model.
64
+
65
+ ## Dataset
66
+
67
+ The current experiment uses [Cardiff NLP's Tweet Sentiment Multilingual dataset](https://huggingface.co/datasets/cardiffnlp/tweet_sentiment_multilingual). It provides the same label mapping used by this project:
68
+
69
+ ```text
70
+ 0 = negative
71
+ 1 = neutral
72
+ 2 = positive
73
+ ```
74
+
75
+ It includes Arabic, English, French, German, Hindi, Italian, Portuguese, and Spanish. The preparation script loads each language configuration separately so it can preserve the official train/validation/test split and retain the language for evaluation.
76
+
77
+ ## Repository layout
78
+
79
+ ```text
80
+ src/
81
+ multilingual_sentiment_analysis/
82
+ config.py # shared paths, labels, and dataset settings
83
+ preprocess.py # dataset download, cleaning, tokenization, and saving
84
+ train.py # fine-tuning entry point
85
+ evaluate.py # test and per-language evaluation
86
+ infer.py # lazy model loading and inference helpers
87
+ data/
88
+ airline_tweets/ # archived English-only source data and prepared splits
89
+ multilingual/ # prepared multilingual splits
90
+ results/
91
+ airline_tweets/ # archived English-only checkpoints
92
+ multilingual/ # current checkpoints and evaluation outputs
93
+ sentiment_model/
94
+ airline-tweets-sentiment-model/ # archived English-only model
95
+ general-multilingual-sentiment-model/ # current app model
96
+ ```
97
+
98
+ Data, checkpoints, and model weights are intentionally ignored by Git.
99
+
100
+
101
+ ## Training configuration
102
+
103
+ - Base checkpoint: `xlm-roberta-base`
104
+ - Maximum token length: 128
105
+ - Epochs: 3
106
+ - Learning rate: 2e-5 with the Trainer's linear schedule
107
+ - Train/evaluation batch sizes: 16 / 32
108
+ - Best checkpoint criterion: weighted F1 on the validation set
109
+ - Random seed: 42
110
+
111
+ The multilingual training run completed in approximately 4 minutes 40 seconds on an NVIDIA RTX 5060 Laptop GPU.
112
+
113
+ ## Limitations and next steps
114
+
115
+ - The model was evaluated on Twitter-style text only; it is not validated for reviews, support tickets, or other domains.
116
+ - It is evaluated for eight languages, not all languages supported by the XLM-R tokenizer.
117
+ - Hindi and neutral-sentiment performance require further improvement.
118
+ - A useful next experiment is to initialize from a Twitter-adapted multilingual encoder such as XLM-T and compare per-language macro F1 against this baseline.
119
+
120
+ ## Attribution
121
+
122
+ The multilingual dataset is provided by Cardiff NLP. See its [dataset card](https://huggingface.co/datasets/cardiffnlp/tweet_sentiment_multilingual) and the associated [XLM-T paper](https://aclanthology.org/2022.lrec-1.27/).
app.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from multilingual_sentiment_analysis.infer import predict, predict_batch
4
+
5
+
6
+ def analyze_single(text: str):
7
+ if not text or not text.strip():
8
+ return "⚠️ Please enter some text", ""
9
+ try:
10
+ result = predict(text.strip())
11
+ emoji = {"positive": "🟢", "neutral": "🟡", "negative": "🔴"}.get(result["label"], "⚪")
12
+ return f"{emoji} {result['label'].upper()}", f"{result['confidence'] * 100:.1f}%"
13
+ except Exception as error:
14
+ return f"❌ Error: {error}", ""
15
+
16
+
17
+ def analyze_batch(batch_text: str):
18
+ if not batch_text or not batch_text.strip():
19
+ return "⚠️ Please enter texts (one per line)", []
20
+ texts = [line.strip() for line in batch_text.splitlines() if line.strip()]
21
+ if not texts:
22
+ return "⚠️ Please enter at least one text", []
23
+ try:
24
+ results = predict_batch(texts)
25
+ emojis = {"positive": "🟢", "neutral": "🟡", "negative": "🔴"}
26
+ rows = [
27
+ [text[:70] + "..." if len(text) > 70 else text,
28
+ f"{emojis.get(result['label'], '⚪')} {result['label'].upper()}",
29
+ f"{result['confidence'] * 100:.1f}%"]
30
+ for text, result in zip(texts, results)
31
+ ]
32
+ sentiments = [result["label"] for result in results]
33
+ summary = (
34
+ f"✅ Analyzed {len(texts)} texts | 😊 Positive: {sentiments.count('positive')} | "
35
+ f"😐 Neutral: {sentiments.count('neutral')} | 😞 Negative: {sentiments.count('negative')}"
36
+ )
37
+ return summary, rows
38
+ except Exception as error:
39
+ return f"❌ Error: {error}", []
40
+
41
+
42
+ custom_theme = gr.themes.Base(primary_hue="cyan", secondary_hue="slate").set(
43
+ body_background_fill="#000000", body_text_color="#00FFFF",
44
+ button_primary_background_fill="#00FFFF", button_primary_text_color="#000000",
45
+ button_primary_background_fill_hover="#00DDDD", block_title_text_color="#00FFFF",
46
+ block_label_text_color="#00FFFF", input_background_fill="#111111",
47
+ input_border_color="#00FFFF", input_placeholder_color="#666666", border_color_primary="#00FFFF",
48
+ )
49
+
50
+ with gr.Blocks(title="🌍 Multilingual Sentiment Analysis", theme=custom_theme) as demo:
51
+ gr.Markdown("# 🌍 Multilingual Sentiment Analysis")
52
+ gr.Markdown("Analyze sentiment using a fine-tuned XLM-RoBERTa model.")
53
+ with gr.Tabs():
54
+ with gr.TabItem("📝 Single Text"):
55
+ with gr.Row():
56
+ with gr.Column(scale=3):
57
+ text_input = gr.Textbox(label="Enter text to analyze", placeholder="Type something to analyze...", lines=4)
58
+ with gr.Column(scale=1):
59
+ analyze_btn = gr.Button("🔍 Analyze", size="lg", variant="primary")
60
+ with gr.Row():
61
+ sentiment_output = gr.Textbox(label="Sentiment", interactive=False)
62
+ confidence_output = gr.Textbox(label="Confidence", interactive=False)
63
+ analyze_btn.click(analyze_single, inputs=text_input, outputs=[sentiment_output, confidence_output])
64
+ with gr.TabItem("📚 Batch Analysis"):
65
+ batch_input = gr.Textbox(label="Enter multiple texts (one per line)", placeholder="Text 1...\nText 2...", lines=8)
66
+ batch_btn = gr.Button("🚀 Batch Analyze", size="lg", variant="primary")
67
+ batch_summary = gr.Textbox(label="Summary", interactive=False)
68
+ batch_results = gr.Dataframe(headers=["Text", "Sentiment", "Confidence"], label="Results", interactive=False)
69
+ batch_btn.click(analyze_batch, inputs=batch_input, outputs=[batch_summary, batch_results])
70
+ gr.Markdown("---\nBuilt with ❤️ using Gradio • XLM-RoBERTa")
71
+
72
+
73
+ if __name__ == "__main__":
74
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ transformers>=4.46,<6
2
+ datasets>=2.19,<4
3
+ scikit-learn>=1.4,<2
4
+ matplotlib>=3.8,<4
5
+ seaborn>=0.13,<1
6
+ pandas>=2.2,<3
7
+ torch>=2.2,<3
8
+ accelerate>=0.30,<2
9
+ gradio>=4.0,<7
10
+ -e .
setup.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="multilingual_sentiment_analysis",
5
+ version="0.1.0",
6
+ packages=find_packages("src"),
7
+ package_dir={"": "src"},
8
+ install_requires=[
9
+ "transformers",
10
+ "datasets",
11
+ "torch",
12
+ "gradio"
13
+ ],
14
+ )