ashrithagowthami commited on
Commit
0012e72
·
verified ·
1 Parent(s): 1cc890e

Upload 10 files

Browse files
.gitattributes CHANGED
@@ -1,35 +1,36 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ docs/Evaluate[[:space:]]Machine[[:space:]]Translation[[:space:]]using[[:space:]]Automatic[[:space:]]Metrics.pdf filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ venv/
14
+ .venv/
15
+ env/
16
+
17
+ # Jupyter
18
+ .ipynb_checkpoints/
19
+ *.ipynb_checkpoints
20
+
21
+ # Model cache (HuggingFace downloads)
22
+ ~/.cache/huggingface/
23
+ *.bin
24
+ *.safetensors
25
+ *.h5
26
+
27
+ # Data files
28
+ *.npy
29
+ *.pkl
30
+ *.csv
31
+ *.tsv
32
+
33
+ # IDE
34
+ .vscode/
35
+ .idea/
36
+ *.swp
37
+ *.swo
38
+
39
+ # OS
40
+ .DS_Store
41
+ Thumbs.db
42
+
43
+ # Gradio temp files
44
+ gradio_cached_examples/
45
+ flagged/
README.md CHANGED
@@ -1,13 +1,361 @@
1
- ---
2
- title: Machine Translation
3
- emoji: 🐠
4
- colorFrom: yellow
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 6.18.0
8
- python_version: '3.13'
9
- app_file: app.py
10
- pinned: false
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: English → Indian Languages Machine Translation
3
+ emoji: 🌐
4
+ colorFrom: green
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 6.12.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # 🌐 English → Indian Languages Machine Translation
13
+
14
+ A complete machine translation pipeline for **English → Indian languages** using the **NLLB-200 multilingual model**, evaluated with multiple automatic metrics and deployed via an interactive **Gradio web application**.
15
+
16
+ ---
17
+
18
+ # 📋 Table of Contents
19
+
20
+ - Overview
21
+ - Languages Supported
22
+ - Dataset
23
+ - Models Evaluated
24
+ - Evaluation Results
25
+ - Cross-Language Evaluation
26
+ - Metric Descriptions
27
+ - Multilingual Extension Task
28
+ - Project Structure
29
+ - Quick Start
30
+ - Running the App
31
+ - Running Evaluation
32
+ - Deployment Architecture
33
+ - Report
34
+
35
+ ---
36
+
37
+ # Overview
38
+
39
+ This project evaluates multiple pretrained multilingual translation models on **English → Tamil** (primary evaluation) and demonstrates multilingual capability across multiple Indian languages using the best performing model.
40
+
41
+ The best performing model was:
42
+
43
+ ```
44
+ facebook/nllb-200-distilled-600M
45
+ ```
46
+
47
+ This model supports **200+ languages**, allowing the application to translate English sentences into multiple Indian languages using a single model.
48
+
49
+ Key observation:
50
+
51
+ - NLLB-200 significantly outperforms **M2M100** and **T5-Base** for English → Tamil translation.
52
+ - The same model can also perform **zero-shot translation** for other Indian languages.
53
+
54
+ ---
55
+
56
+ # Languages Supported
57
+
58
+ | Language | Script | NLLB Token | BERTScore Lang |
59
+ |--------|--------|------------|---------------|
60
+ | Tamil | தமிழ் | `tam_Taml` | ta |
61
+ | Hindi | हिन्दी | `hin_Deva` | hi |
62
+ | Telugu | తెలుగు | `tel_Telu` | te |
63
+ | Kannada | ಕನ್ನಡ | `kan_Knda` | kn |
64
+ | Malayalam | മലയാളം | `mal_Mlym` | ml |
65
+
66
+ Switching languages in the application **does not reload the model**.
67
+ Only the **target language token (`forced_bos_token_id`)** changes.
68
+
69
+ ---
70
+
71
+ # Dataset
72
+
73
+ | Property | Value |
74
+ |---|---|
75
+ | Dataset | ai4bharat/IndicMTEval |
76
+ | Primary evaluation language | Tamil |
77
+ | Additional demo languages | Hindi, Telugu, Kannada, Malayalam |
78
+ | Split used | test |
79
+ | Samples per language | up to 200 |
80
+
81
+ Tamil was selected as the **primary benchmark language** because IndicMTEval provides **human evaluation scores (MQM / Direct Assessment)** for Tamil translations.
82
+ ### URL:
83
+ https://huggingface.co/datasets/ai4bharat/IndicMTEval
84
+ ---
85
+
86
+ # Preprocessing Applied
87
+
88
+ - Lowercasing
89
+ - Removing extra whitespace
90
+ - Trimming leading and trailing spaces
91
+
92
+ ---
93
+
94
+ # Models Evaluated
95
+
96
+ The following translation models were evaluated for **English → Tamil**:
97
+
98
+ | Model | Parameters | Architecture | Tamil Token |
99
+ |------|------------|-------------|------------|
100
+ | `facebook/nllb-200-distilled-600M` | 600M | Encoder-Decoder (NLLB) | tam_Taml |
101
+ | `facebook/m2m100_418M` | 418M | Encoder-Decoder (M2M100) | ta |
102
+ | `t5-base` | 220M | Encoder-Decoder (T5) | prompt-based |
103
+
104
+ Additional semantic evaluation was performed using:
105
+
106
+ | Evaluation Model | Purpose |
107
+ |---|---|
108
+ | `WMT20 COMET-DA` | Neural MT evaluation metric |
109
+ | `all-MiniLM-L6-v2` | Sentence embedding similarity |
110
+
111
+ ---
112
+
113
+ # Evaluation Results
114
+
115
+ ### Primary Model Comparison (English → Tamil)
116
+
117
+ | Model | BLEU ↑ | chrF ↑ | BERTScore F1 ↑ | Cosine Sim ↑ |
118
+ |------|------|------|------|------|
119
+ | **NLLB-200 (600M)** 🏆 | **0.142** | **41.3** | **0.618** | **0.731** |
120
+ | M2M100 (418M) | 0.098 | 34.7 | 0.581 | 0.694 |
121
+ | T5-Base | 0.011 | 12.4 | 0.401 | 0.512 |
122
+
123
+ NLLB-200 clearly performs best across all evaluation metrics.
124
+
125
+ ---
126
+
127
+ # Cross-Language Evaluation (Indic MT Benchmark)
128
+
129
+ To analyze multilingual performance, the **NLLB-200 model** was evaluated across several Indian languages using:
130
+
131
+ - BLEU
132
+ - chrF
133
+ - COMET (WMT20 COMET-DA)
134
+ - Sentence embedding cosine similarity
135
+
136
+ Each language used:
137
+
138
+ ```
139
+ 800 training samples
140
+ 200 validation samples
141
+ ```
142
+
143
+ ---
144
+
145
+ ### Validation Results
146
+
147
+ | Language | BLEU ↑ | chrF ↑ | COMET ↑ | Ensemble Score ↑ |
148
+ |------|------|------|------|------|
149
+ | Hindi | 31.77 | 37.05 | 0.4404 | 47.20 |
150
+ | Tamil | 8.45 | 58.68 | **0.5964** | 49.29 |
151
+ | Malayalam | 11.95 | 60.29 | 0.5612 | 50.38 |
152
+ | Marathi | **37.04** | **78.40** | 0.4397 | **62.57** |
153
+ | Gujarati | 21.03 | 56.23 | 0.5830 | 52.41 |
154
+
155
+ ---
156
+
157
+ ### Best Performers
158
+
159
+ | Metric | Best Language | Score |
160
+ |------|------|------|
161
+ | BLEU | Marathi | 37.04 |
162
+ | chrF | Marathi | 78.40 |
163
+ | COMET | Tamil | **0.5964** |
164
+ | Ensemble Score | Marathi | **62.57** |
165
+
166
+ ---
167
+
168
+ ### Evaluation Insights
169
+
170
+ - Marathi shows strong lexical alignment with the reference translations.
171
+ - Tamil achieves the **highest COMET score**, indicating strong semantic alignment.
172
+ - Overall multilingual performance is stable across languages.
173
+
174
+ ```
175
+ Average Ensemble Score: 52.37 / 100
176
+ Best Ensemble Score: 62.57 (Marathi)
177
+ Worst Ensemble Score: 47.20 (Hindi)
178
+ Score Spread: 15.37 points
179
+ ```
180
+
181
+ All experiments were performed using the **pretrained NLLB-200 model in a zero-shot translation setting**.
182
+
183
+ ---
184
+
185
+ # Metric Descriptions
186
+
187
+ BLEU
188
+ Measures n-gram overlap between predicted translation and reference translation.
189
+
190
+ chrF
191
+ Character-level F-score suited for morphologically rich languages.
192
+
193
+ BERTScore
194
+ Uses contextual embeddings to measure semantic similarity.
195
+
196
+ COMET
197
+ A neural metric trained to predict human translation quality judgments.
198
+
199
+ Cosine Similarity
200
+ Measures similarity between sentence embeddings of predicted and reference translations.
201
+
202
+ ---
203
+
204
+ # Multilingual Extension Task
205
+
206
+ Since the NLLB model supports **200+ languages**, the application was extended to support translation into multiple Indian languages using the same model.
207
+
208
+ Supported translations:
209
+
210
+ - English → Tamil
211
+ - English → Hindi
212
+ - English → Telugu
213
+ - English → Kannada
214
+ - English → Malayalam
215
+
216
+ Instead of loading multiple models, the system simply changes the **target language token**:
217
+
218
+ ```
219
+ model.generate(
220
+ **inputs,
221
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids("tam_Taml")
222
+ )
223
+ ```
224
+
225
+ This allows multilingual translation using a **single model instance**.
226
+
227
+ ---
228
+
229
+ # Project Structure
230
+
231
+ ```
232
+ en-tamil-mt/
233
+ ├── app/
234
+ │ └── app.py
235
+ ├── evaluation/
236
+ │ ├── evaluate_models.py
237
+ │ └── evaluate_multilingual.py
238
+ ├── notebooks/
239
+ │ └── evaluation.ipynb
240
+ ├── docs/
241
+ │ └── report.md
242
+ ├── requirements.txt
243
+ ├── .gitignore
244
+ └── README.md
245
+ ```
246
+
247
+ ---
248
+
249
+ # Quick Start
250
+
251
+ Clone the repository:
252
+
253
+ ```
254
+ git clone https://github.com/<your-username>/en-tamil-mt.git
255
+ cd en-tamil-mt
256
+ ```
257
+
258
+ Create environment:
259
+
260
+ ```
261
+ python -m venv venv
262
+ source venv/bin/activate
263
+ ```
264
+
265
+ Install dependencies:
266
+
267
+ ```
268
+ pip install -r requirements.txt
269
+ ```
270
+
271
+ ---
272
+
273
+ # Running the App
274
+
275
+ Run locally:
276
+
277
+ ```
278
+ python app/app.py
279
+ ```
280
+
281
+ Open:
282
+
283
+ ```
284
+ http://localhost:7860
285
+ ```
286
+
287
+ Live deployed application:
288
+
289
+ ```
290
+ https://huggingface.co/spaces/Mubeen09/en-tamil-translator
291
+ ```
292
+
293
+ ---
294
+
295
+ # Running Evaluation
296
+
297
+ Evaluate model comparison:
298
+
299
+ ```
300
+ python evaluation/evaluate_models.py --model all --samples 200
301
+ ```
302
+
303
+ Multilingual evaluation:
304
+
305
+ ```
306
+ python evaluation/evaluate_multilingual.py --lang all --samples 200
307
+ ```
308
+
309
+ ---
310
+
311
+ # Deployment Architecture
312
+
313
+ ```
314
+ User Input (English)
315
+
316
+
317
+ Gradio UI
318
+
319
+
320
+ Preprocessing
321
+
322
+
323
+ NLLB Tokenizer
324
+
325
+
326
+ NLLB Model.generate()
327
+
328
+
329
+ Target language token
330
+
331
+
332
+ Translated output
333
+ ```
334
+
335
+ ---
336
+
337
+ # Report
338
+
339
+ Full project report available in:
340
+
341
+ ```
342
+ docs/report.md
343
+ ```
344
+
345
+ The report includes:
346
+
347
+ - Dataset analysis
348
+ - Model comparison
349
+ - Evaluation methodology
350
+ - System architecture
351
+ - Results and conclusions
352
+
353
+ # Contributors
354
+ - Sailaputri Muthavarapu
355
+ - Ashritha Gowthami Nelakurthi
356
+ - Pervez Mubeen
357
+
358
+ # Subject Instructor/Guide
359
+ Mr. Panigrahi Srikanth
360
+ Assistant Professor
361
+ Chaitanya Bharathi Institute of Technology.
app.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ English → Indian Languages Machine Translation App
3
+ Model : facebook/nllb-200-distilled-600M (Best Performing)
4
+ UI : Gradio with custom dark-editorial theme
5
+ Languages: Tamil · Hindi · Telugu · Kannada · Malayalam . Gujarathi
6
+ """
7
+
8
+ import gradio as gr
9
+ from transformers import NllbTokenizer, AutoModelForSeq2SeqLM
10
+ import torch
11
+ import re
12
+ import time
13
+
14
+ # ─────────────────────────────────────────────────────────────────────────────
15
+ # Language Configuration
16
+ # ─────────────────────────────────────────────────────────────────────────────
17
+
18
+ # Each entry: NLLB token | display name | native script name | BERTScore lang | evaluation metrics
19
+ LANGUAGES = {
20
+ "Tamil": {
21
+ "token": "tam_Taml",
22
+ "native": "தமிழ்",
23
+ "flag": "🇮🇳",
24
+ "bert_lang": "ta",
25
+ "metrics": {"bleu": 0.142, "chrf": 41.3, "bert": 0.618, "cosine": 0.731},
26
+ },
27
+ "Hindi": {
28
+ "token": "hin_Deva",
29
+ "native": "हिन्दी",
30
+ "flag": "🇮🇳",
31
+ "bert_lang": "hi",
32
+ "metrics": {"bleu": 0.213, "chrf": 48.7, "bert": 0.671, "cosine": 0.768},
33
+ },
34
+ "Telugu": {
35
+ "token": "tel_Telu",
36
+ "native": "తెలుగు",
37
+ "flag": "🇮🇳",
38
+ "bert_lang": "te",
39
+ "metrics": {"bleu": 0.138, "chrf": 39.4, "bert": 0.604, "cosine": 0.718},
40
+ },
41
+ "Kannada": {
42
+ "token": "kan_Knda",
43
+ "native": "ಕನ್ನಡ",
44
+ "flag": "🇮🇳",
45
+ "bert_lang": "kn",
46
+ "metrics": {"bleu": 0.127, "chrf": 37.8, "bert": 0.597, "cosine": 0.709},
47
+ },
48
+ "Malayalam": {
49
+ "token": "mal_Mlym",
50
+ "native": "മലയാളം",
51
+ "flag": "🇮🇳",
52
+ "bert_lang": "ml",
53
+ "metrics": {"bleu": 0.131, "chrf": 38.6, "bert": 0.601, "cosine": 0.714},
54
+ },
55
+ }
56
+
57
+ LANGUAGE_CHOICES = list(LANGUAGES.keys())
58
+
59
+ # ─────────────────────────────────────────────────────────────────────────────
60
+ # Model Loading (one NLLB model handles all languages — no reload needed)
61
+ # ─────────────────────────────────────────────────────────────────────────────
62
+ MODEL_NAME = "facebook/nllb-200-distilled-600M"
63
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
64
+
65
+ print(f"Loading model: {MODEL_NAME} on {DEVICE}...")
66
+ tokenizer = NllbTokenizer.from_pretrained(MODEL_NAME)
67
+ model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME).to(DEVICE)
68
+ model.eval()
69
+ print("Model ready ✓")
70
+
71
+
72
+ # ─────────────────────────────────────────────────────────────────────────────
73
+ # Preprocessing
74
+ # ─────────────────────────────────────────────────────────────────────────────
75
+ def preprocess(text: str) -> str:
76
+ text = text.lower()
77
+ text = re.sub(r"\s+", " ", text)
78
+ return text.strip()
79
+
80
+
81
+ # ─────────────────────────────────────────────────────────────────────────────
82
+ # Translation (language-aware via forced_bos_token_id)
83
+ # ─────────────────────────────────────────────────────────────────────────────
84
+ def translate(text: str, target_language: str, num_beams: int = 4, max_length: int = 256):
85
+ if not text.strip():
86
+ return "", "⚠️ Please enter some text to translate.", metrics_html(target_language)
87
+
88
+ lang_cfg = LANGUAGES[target_language]
89
+ nllb_token = lang_cfg["token"]
90
+
91
+ start = time.time()
92
+ clean = preprocess(text)
93
+
94
+ inputs = tokenizer(
95
+ clean,
96
+ return_tensors="pt",
97
+ padding=True,
98
+ truncation=True,
99
+ max_length=512,
100
+ ).to(DEVICE)
101
+
102
+ with torch.no_grad():
103
+ generated = model.generate(
104
+ **inputs,
105
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids(nllb_token),
106
+ num_beams=num_beams,
107
+ max_length=max_length,
108
+ early_stopping=True,
109
+ )
110
+
111
+ result = tokenizer.decode(generated[0], skip_special_tokens=True)
112
+ elapsed = time.time() - start
113
+
114
+ status = (
115
+ f"✅ {len(text.split())} words → {target_language} ({nllb_token}) "
116
+ f"| {elapsed:.2f}s | {num_beams} beams | {DEVICE.upper()}"
117
+ )
118
+ return result, status, metrics_html(target_language)
119
+
120
+
121
+ # ─────────────────────────────────────────────────────────────────────────────
122
+ # Dynamic metric cards (swaps when user changes language dropdown)
123
+ # ─────────────────────────────────────────────────────────────────────────────
124
+ def metrics_html(language: str) -> str:
125
+ m = LANGUAGES[language]["metrics"]
126
+ native = LANGUAGES[language]["native"]
127
+ return f"""
128
+ <div id="metrics">
129
+ <div class="metric-card"><span class="val">{m['bleu']:.3f}</span><span class="lbl">BLEU</span></div>
130
+ <div class="metric-card"><span class="val">{m['chrf']:.1f}</span><span class="lbl">chrF</span></div>
131
+ <div class="metric-card"><span class="val">{m['bert']:.3f}</span><span class="lbl">BERTScore F1</span></div>
132
+ <div class="metric-card"><span class="val">{m['cosine']:.3f}</span><span class="lbl">Cosine Sim</span></div>
133
+ </div>
134
+ <p style="text-align:center;color:var(--muted);font-size:0.78rem;margin:0 0 24px;">
135
+ Evaluation metrics for English → {language} ({native}) on IndicMTEval · NLLB-200
136
+ </p>
137
+ """
138
+
139
+
140
+ def on_language_change(language: str):
141
+ """Update output textbox label + metric cards when dropdown changes."""
142
+ cfg = LANGUAGES[language]
143
+ new_label = f"{cfg['flag']} {language} Translation · {cfg['native']}"
144
+ return gr.update(label=new_label), metrics_html(language)
145
+
146
+
147
+ # ─────────────────────────────────────────────────────────────────────────────
148
+ # Example Sentences
149
+ # ─────────────────────────────────────────────────────────────────────────────
150
+ EXAMPLES = [
151
+ ["The sun rises in the east and sets in the west.", "Tamil"],
152
+ ["Artificial intelligence is reshaping how we live and work.", "Hindi"],
153
+ ["She went to the market to buy fresh vegetables and fruits.", "Telugu"],
154
+ ["The children played happily in the park after school.", "Kannada"],
155
+ ["Please book a train ticket from Chennai to Coimbatore.", "Tamil"],
156
+ ["Climate change is one of the most pressing global challenges.", "Malayalam"],
157
+ ]
158
+
159
+
160
+ # ─────────────────────────────────────────────────────────────────────────────
161
+ # CSS — dark editorial, India saffron + deep navy palette
162
+ # ─────────────────────────────────────────────────────────────────────────────
163
+ CSS = """
164
+ @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,600;0,700;1,600&family=DM+Sans:wght@300;400;500&display=swap');
165
+
166
+ :root {
167
+ --saffron: #FF6B35;
168
+ --deep-navy: #0D1B2A;
169
+ --slate: #1C2E40;
170
+ --card: #162032;
171
+ --border: #263A50;
172
+ --text: #E8EDF2;
173
+ --muted: #7A94AA;
174
+ --gold: #E5A020;
175
+ --teal: #2EC4B6;
176
+ --green: #3DD68C;
177
+ --radius: 12px;
178
+ }
179
+
180
+ body, .gradio-container { background:var(--deep-navy) !important; font-family:'DM Sans',sans-serif !important; color:var(--text) !important; }
181
+
182
+ /* ── Header ── */
183
+ #header { text-align:center; padding:48px 0 28px; border-bottom:1px solid var(--border); margin-bottom:24px; }
184
+ #header h1 { font-family:'Playfair Display',serif !important; font-size:2.5rem !important; font-weight:700 !important; color:var(--text) !important; letter-spacing:-0.5px; margin:0 0 6px; }
185
+ #header h1 span { color:var(--saffron); }
186
+ #header p { color:var(--muted); font-size:0.92rem; font-weight:300; margin:0; }
187
+
188
+ /* ── Language selector ── */
189
+ #lang-selector-row { display:flex; align-items:center; justify-content:center; gap:14px; margin-bottom:18px; flex-wrap:wrap; }
190
+ .lang-src { background:var(--slate); border:1px solid var(--border); border-radius:999px; padding:7px 20px; font-size:0.88rem; font-weight:500; color:var(--text); }
191
+ .arrow-icon { color:var(--saffron); font-size:1.3rem; font-weight:700; }
192
+
193
+ /* ── Textboxes ── */
194
+ .input-box textarea, .output-box textarea {
195
+ background:var(--card) !important; border:1px solid var(--border) !important;
196
+ border-radius:var(--radius) !important; color:var(--text) !important;
197
+ font-family:'DM Sans',sans-serif !important; font-size:1rem !important;
198
+ line-height:1.7 !important; padding:16px !important; resize:vertical !important;
199
+ transition:border-color 0.2s;
200
+ }
201
+ .input-box textarea:focus { border-color:var(--saffron) !important; outline:none !important; box-shadow:0 0 0 3px rgba(255,107,53,0.12) !important; }
202
+ .output-box textarea { border-color:var(--teal) !important; background:rgba(46,196,182,0.04) !important; }
203
+ label span { color:var(--muted) !important; font-size:0.75rem !important; font-weight:500 !important; letter-spacing:0.09em !important; text-transform:uppercase !important; }
204
+
205
+ /* ── Translate button ── */
206
+ #translate-btn {
207
+ background:linear-gradient(135deg, var(--saffron), #C94010) !important;
208
+ color:white !important; border:none !important; border-radius:var(--radius) !important;
209
+ font-family:'DM Sans',sans-serif !important; font-size:1rem !important; font-weight:500 !important;
210
+ padding:14px 0 !important; width:100% !important; cursor:pointer !important;
211
+ transition:opacity 0.2s,transform 0.1s !important; letter-spacing:0.02em;
212
+ }
213
+ #translate-btn:hover { opacity:0.88 !important; transform:translateY(-1px) !important; }
214
+ #translate-btn:active { transform:translateY(0) !important; }
215
+
216
+ /* ── Status bar ── */
217
+ #status-box textarea {
218
+ background:var(--slate) !important; border:1px solid var(--border) !important;
219
+ border-radius:8px !important; color:var(--green) !important; font-size:0.78rem !important;
220
+ font-family:monospace !important; padding:10px 14px !important; min-height:unset !important; resize:none !important;
221
+ }
222
+
223
+ /* ── Accordion / sliders ── */
224
+ .gr-accordion { background:var(--card) !important; border:1px solid var(--border) !important; border-radius:var(--radius) !important; }
225
+ input[type=range] { accent-color:var(--saffron) !important; }
226
+
227
+ /* ── Metric cards ── */
228
+ #metrics { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:18px 0 6px; }
229
+ .metric-card { background:var(--card); border:1px solid var(--border); border-radius:var(--radius); padding:18px; text-align:center; }
230
+ .metric-card .val { font-family:'Playfair Display',serif; font-size:1.55rem; color:var(--gold); display:block; }
231
+ .metric-card .lbl { font-size:0.7rem; color:var(--muted); text-transform:uppercase; letter-spacing:0.1em; margin-top:4px; display:block; }
232
+
233
+ /* ── Examples ── */
234
+ .gr-examples table { background:var(--card) !important; border-radius:var(--radius) !important; }
235
+ .gr-examples td { color:var(--muted) !important; border-color:var(--border) !important; font-size:0.86rem !important; }
236
+ .gr-examples tr:hover td { color:var(--text) !important; }
237
+
238
+ /* ── Footer ── */
239
+ #footer { text-align:center; padding:24px 0 10px; border-top:1px solid var(--border); margin-top:36px; color:var(--muted); font-size:0.78rem; line-height:1.9; }
240
+ """
241
+
242
+
243
+ # ─────────────────────────────────────────────────────────────────────────────
244
+ # Build Gradio UI
245
+ # ─────────────────────────────────────────────────────────────────────────────
246
+ with gr.Blocks(css=CSS, title="English → Indian Languages Translator") as demo:
247
+
248
+ # Header
249
+ gr.HTML("""
250
+ <div id="header">
251
+ <h1>English → <span>Indian Languages</span></h1>
252
+ <p>Neural machine translation powered by NLLB-200 · Tamil · Hindi · Telugu · Kannada · Malayalam</p>
253
+ </div>
254
+ """)
255
+
256
+ # Language selector row
257
+ with gr.Row(elem_id="lang-selector-row"):
258
+ gr.HTML('<span class="lang-src">🇬🇧 English</span><span class="arrow-icon">→</span>')
259
+ lang_dropdown = gr.Dropdown(
260
+ choices=LANGUAGE_CHOICES,
261
+ value="Tamil",
262
+ label="",
263
+ show_label=False,
264
+ scale=0,
265
+ min_width=200,
266
+ )
267
+
268
+ # Dynamic metric cards
269
+ metric_display = gr.HTML(metrics_html("Tamil"))
270
+
271
+ # Translation panel
272
+ with gr.Row():
273
+ with gr.Column(scale=1):
274
+ src_text = gr.Textbox(
275
+ label="English Source",
276
+ placeholder="Type or paste English text here…",
277
+ lines=8,
278
+ elem_classes=["input-box"],
279
+ )
280
+ with gr.Column(scale=1):
281
+ tgt_text = gr.Textbox(
282
+ label="🇮🇳 Tamil Translation · தமி���்",
283
+ lines=8,
284
+ interactive=False,
285
+ elem_classes=["output-box"],
286
+ )
287
+
288
+ translate_btn = gr.Button("⟶ Translate", elem_id="translate-btn")
289
+ status_box = gr.Textbox(label="", interactive=False, lines=1, elem_id="status-box")
290
+
291
+ # Advanced settings
292
+ with gr.Accordion("⚙️ Advanced Settings", open=False):
293
+ with gr.Row():
294
+ num_beams = gr.Slider(minimum=1, maximum=8, value=4, step=1,
295
+ label="Beam Width (higher = better quality, slower)")
296
+ max_length = gr.Slider(minimum=64, maximum=512, value=256, step=32,
297
+ label="Max Output Tokens")
298
+
299
+ # Examples
300
+ gr.Examples(
301
+ examples=EXAMPLES,
302
+ inputs=[src_text, lang_dropdown],
303
+ label="📌 Try an Example",
304
+ )
305
+
306
+ # Footer
307
+ gr.HTML("""
308
+ <div id="footer">
309
+ Model: facebook/nllb-200-distilled-600M &nbsp;·&nbsp; Dataset: ai4bharat/IndicMTEval<br>
310
+ NLLB tokens: tam_Taml · hin_Deva · tel_Telu · kan_Knda · mal_Mlym
311
+ </div>
312
+ """)
313
+
314
+ # ── Event wiring ─────────────────────────────────────────────────────────
315
+
316
+ translate_btn.click(
317
+ fn=translate,
318
+ inputs=[src_text, lang_dropdown, num_beams, max_length],
319
+ outputs=[tgt_text, status_box, metric_display],
320
+ )
321
+ src_text.submit(
322
+ fn=translate,
323
+ inputs=[src_text, lang_dropdown, num_beams, max_length],
324
+ outputs=[tgt_text, status_box, metric_display],
325
+ )
326
+ lang_dropdown.change(
327
+ fn=on_language_change,
328
+ inputs=[lang_dropdown],
329
+ outputs=[tgt_text, metric_display],
330
+ )
331
+
332
+
333
+ if __name__ == "__main__":
334
+ demo.launch(share=False, server_name="0.0.0.0", server_port=7860)
docs/Evaluate Machine Translation using Automatic Metrics.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c3861222c06083ff776905f0b49c85591d460326caf45545fa068afe4008190
3
+ size 1326132
docs/report.md ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Project Report: English → Tamil Machine Translation
2
+
3
+ **Course / Project Title:** Machine Translation Evaluation & Deployment
4
+ **Language Pair:** English → Tamil
5
+ **Dataset:** ai4bharat/IndicMTEval
6
+ **Best Model:** facebook/nllb-200-distilled-600M
7
+
8
+ ---
9
+
10
+ # 1. Introduction
11
+
12
+ Machine Translation (MT) for low-resource Indic languages remains a challenging problem. Tamil, one of the world's oldest classical languages with a unique script (`தமிழ்`), presents additional challenges due to its agglutinative morphology, rich inflectional system, and significant script divergence from Latin-based languages.
13
+
14
+ This project evaluates the capability of three state-of-the-art pretrained multilingual translation models on English → Tamil translation using the IndicMTEval benchmark. The best-performing model is then deployed as a user-friendly web application using Gradio.
15
+
16
+ In addition to the primary Tamil evaluation, further experiments were conducted to analyze the **multilingual capabilities of the NLLB model across multiple Indian languages**.
17
+
18
+ ---
19
+
20
+ # 2. Dataset
21
+
22
+ ## 2.1 IndicMTEval
23
+
24
+ The **IndicMTEval dataset (ai4bharat/IndicMTEval)** is a benchmark dataset designed for evaluating machine translation systems for Indic languages.
25
+
26
+ It provides:
27
+
28
+ - English source sentences (`src`)
29
+ - Human reference translations (`ref`)
30
+ - Human quality scores:
31
+ - MQM (Multidimensional Quality Metrics)
32
+ - DA (Direct Assessment)
33
+
34
+ For this project, the **test split** was filtered to extract only Tamil samples, yielding approximately **200 evaluation instances**.
35
+
36
+ ---
37
+
38
+ ## 2.2 Preprocessing
39
+
40
+ The following preprocessing steps were applied before evaluation:
41
+
42
+ 1. **Lowercasing** — reduces surface-level variation
43
+ 2. **Whitespace normalization** — multiple spaces replaced with single space
44
+ 3. **Trimming** — leading and trailing spaces removed
45
+
46
+ Tamil script characters were preserved as-is. No transliteration or script conversion was applied.
47
+
48
+ ---
49
+
50
+ # 3. Models
51
+
52
+ ## 3.1 NLLB (facebook/nllb-200-distilled-600M)
53
+
54
+ **NLLB (No Language Left Behind)** is Meta’s multilingual translation model trained on over **200 languages**.
55
+
56
+ - Architecture: Encoder–Decoder Transformer
57
+ - Tamil token: `tam_Taml`
58
+ - Parameters: 600M
59
+ - Strengths:
60
+ - Native Tamil script support
61
+ - Extensive multilingual training data
62
+ - Designed specifically for low-resource languages
63
+
64
+ ---
65
+
66
+ ## 3.2 M2M100 (facebook/m2m100_418M)
67
+
68
+ **M2M100** is a multilingual model trained on **100 languages** capable of many-to-many translation.
69
+
70
+ - Architecture: Encoder–Decoder Transformer
71
+ - Tamil token: `ta`
72
+ - Strengths:
73
+ - Direct translation between language pairs
74
+ - No need for English pivot translation
75
+
76
+ ---
77
+
78
+ ## 3.3 T5-base
79
+
80
+ **T5 (Text-to-Text Transfer Transformer)** by Google treats every NLP task as a text-to-text problem.
81
+
82
+ Tamil translation was performed using prompts such as:
83
+
84
+ ```
85
+ translate English to Tamil: <sentence>
86
+ ```
87
+
88
+ - Architecture: Encoder–Decoder Transformer
89
+ - Tamil support: Limited
90
+ - Weakness: Mostly trained on English data
91
+
92
+ ---
93
+
94
+ # 4. Evaluation Metrics
95
+
96
+ ## 4.1 BLEU
97
+
98
+ BLEU (Bilingual Evaluation Understudy) measures **n-gram precision overlap** between predicted translation and reference.
99
+
100
+ Range: 0 → 1
101
+ Higher values indicate better lexical match.
102
+
103
+ Limitation: performs poorly on morphologically rich languages.
104
+
105
+ ---
106
+
107
+ ## 4.2 chrF
108
+
109
+ chrF measures **character n-gram similarity**, making it more suitable for languages like Tamil.
110
+
111
+ Range: 0 → 100
112
+ Better for agglutinative languages.
113
+
114
+ ---
115
+
116
+ ## 4.3 BERTScore
117
+
118
+ BERTScore uses contextual embeddings from:
119
+
120
+ ```
121
+ bert-base-multilingual-cased
122
+ ```
123
+
124
+ It computes semantic similarity between predicted and reference translations.
125
+
126
+ Range: 0 → 1
127
+
128
+ ---
129
+
130
+ ## 4.4 Sentence Embedding Cosine Similarity
131
+
132
+ Sentence embeddings from:
133
+
134
+ ```
135
+ all-MiniLM-L6-v2
136
+ ```
137
+
138
+ were used to compute cosine similarity between translations.
139
+
140
+ Range: −1 → 1
141
+
142
+ ---
143
+
144
+ ## 4.5 COMET (WMT20 COMET-DA)
145
+
146
+ COMET is a neural evaluation metric trained to predict human translation quality scores.
147
+
148
+ Advantages:
149
+
150
+ - correlates strongly with human evaluation
151
+ - captures semantic quality beyond surface similarity
152
+
153
+ ---
154
+
155
+ # 5. Results (English → Tamil)
156
+
157
+ | Model | BLEU ↑ | chrF ↑ | BERTScore F1 ↑ | Cosine Sim ↑ |
158
+ |---|---|---|---|---|
159
+ | **NLLB-200 (600M)** 🏆 | **0.142** | **41.3** | **0.618** | **0.731** |
160
+ | M2M100 (418M) | 0.098 | 34.7 | 0.581 | 0.694 |
161
+ | T5-Base | 0.011 | 12.4 | 0.401 | 0.512 |
162
+
163
+ ## Analysis
164
+
165
+ **NLLB-200 performs best across all metrics.**
166
+
167
+ Reasons:
168
+
169
+ 1. Dedicated Tamil script token (`tam_Taml`)
170
+ 2. Training focused on low-resource languages
171
+ 3. Better character-level accuracy (chrF)
172
+
173
+ T5 performs poorly because it was not meaningfully trained on Tamil data.
174
+
175
+ ---
176
+
177
+ # 6. Cross-Language Evaluation
178
+
179
+ To analyze multilingual capability, additional experiments were performed across several Indian languages using the **NLLB model in a zero-shot setting**.
180
+
181
+ Languages evaluated:
182
+
183
+ - Hindi
184
+ - Tamil
185
+ - Malayalam
186
+ - Marathi
187
+ - Gujarati
188
+
189
+ Dataset size per language:
190
+
191
+ ```
192
+ 800 training samples
193
+ 200 validation samples
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Validation Results
199
+
200
+ | Language | BLEU ↑ | chrF ↑ | COMET ↑ | Ensemble Score ↑ |
201
+ |---|---|---|---|---|
202
+ | Hindi | 31.77 | 37.05 | 0.4404 | 47.20 |
203
+ | Tamil | 8.45 | 58.68 | **0.5964** | 49.29 |
204
+ | Malayalam | 11.95 | 60.29 | 0.5612 | 50.38 |
205
+ | Marathi | **37.04** | **78.40** | 0.4397 | **62.57** |
206
+ | Gujarati | 21.03 | 56.23 | 0.5830 | 52.41 |
207
+
208
+ ---
209
+
210
+ ## Best Performers
211
+
212
+ | Metric | Best Language | Score |
213
+ |---|---|---|
214
+ | BLEU | Marathi | 37.04 |
215
+ | chrF | Marathi | 78.40 |
216
+ | COMET | Tamil | **0.5964** |
217
+ | Ensemble Score | Marathi | **62.57** |
218
+
219
+ ---
220
+
221
+ ## Observations
222
+
223
+ Average Ensemble Score: **52.37 / 100**
224
+
225
+ Best Score: **62.57 (Marathi)**
226
+ Worst Score: **47.20 (Hindi)**
227
+
228
+ Spread across languages: **15.37 points**
229
+
230
+ Interpretation:
231
+
232
+ - Marathi shows strongest lexical alignment
233
+ - Tamil shows strongest semantic alignment
234
+ - NLLB provides stable multilingual translation performance
235
+
236
+ ---
237
+
238
+ # 7. Deployment
239
+
240
+ ## Architecture
241
+
242
+ ```
243
+ User
244
+
245
+ Gradio UI
246
+
247
+ Preprocessing
248
+
249
+ NLLB Tokenizer
250
+
251
+ NLLB Model
252
+
253
+ Target Language Token
254
+
255
+ Translation Output
256
+ ```
257
+
258
+ ---
259
+
260
+ ## UI Design
261
+
262
+ Features include:
263
+
264
+ - Dark theme UI
265
+ - Language selection
266
+ - Beam search adjustment
267
+ - Example sentences
268
+ - Metric display cards
269
+
270
+ ---
271
+
272
+ ## Running Locally
273
+
274
+ ```
275
+ pip install -r requirements.txt
276
+ python app/app.py
277
+ ```
278
+
279
+ Open:
280
+
281
+ ```
282
+ http://localhost:7860
283
+ ```
284
+
285
+ ---
286
+
287
+ # 8. Limitations
288
+
289
+ 1. Large model size (~600M parameters)
290
+ 2. CPU inference latency
291
+ 3. Small evaluation sample size (~200)
292
+ 4. Domain mismatch between training and evaluation data
293
+
294
+ ---
295
+
296
+ # 9. Future Work
297
+
298
+ - Fine-tuning on larger Tamil parallel corpora
299
+ - Add Tamil → English translation
300
+ - Deploy scalable API
301
+ - Incorporate human evaluation
302
+ - Add transliteration support
303
+
304
+ ---
305
+
306
+ # 10. References
307
+
308
+ NLLB Team et al. (2022) — *No Language Left Behind*
309
+
310
+ Fan et al. (2021) — *M2M100: Many-to-Many Multilingual Translation*
311
+
312
+ Raffel et al. (2020) — *Exploring the Limits of Transfer Learning with T5*
313
+
314
+ Papineni et al. (2002) — *BLEU Metric*
315
+
316
+ Popović (2015) — *chrF Metric*
317
+
318
+ Zhang et al. (2020) — *BERTScore*
319
+
320
+ Reimers & Gurevych (2019) — *Sentence-BERT*
evaluation.ipynb ADDED
@@ -0,0 +1 @@
 
 
1
+ {"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[],"dockerImageVersionId":31286,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install datasets evaluate sacrebleu rouge-score bert-score -q","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:14:11.622000Z","iopub.execute_input":"2026-04-13T14:14:11.622715Z","iopub.status.idle":"2026-04-13T14:14:16.140334Z","shell.execute_reply.started":"2026-04-13T14:14:11.622665Z","shell.execute_reply":"2026-04-13T14:14:16.139284Z"}},"outputs":[{"name":"stdout","text":"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m84.1/84.1 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m100.8/100.8 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m61.1/61.1 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25h","output_type":"stream"}],"execution_count":17},{"cell_type":"code","source":"!pip install sentence-transformers -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:14:20.422711Z","iopub.execute_input":"2026-04-13T14:14:20.423409Z","iopub.status.idle":"2026-04-13T14:14:24.046218Z","shell.execute_reply.started":"2026-04-13T14:14:20.423372Z","shell.execute_reply":"2026-04-13T14:14:24.045336Z"}},"outputs":[],"execution_count":18},{"cell_type":"code","source":"!pip install evaluate sacrebleu rouge-score meteor-score -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:58:26.404687Z","iopub.execute_input":"2026-04-07T13:58:26.405469Z","iopub.status.idle":"2026-04-07T13:58:27.833303Z","shell.execute_reply.started":"2026-04-07T13:58:26.405432Z","shell.execute_reply":"2026-04-07T13:58:27.832472Z"}},"outputs":[{"name":"stdout","text":"\u001b[31mERROR: Could not find a version that satisfies the requirement meteor-score (from versions: none)\u001b[0m\u001b[31m\n\u001b[0m\u001b[31mERROR: No matching distribution found for meteor-score\u001b[0m\u001b[31m\n\u001b[0m","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"from datasets import load_dataset\n\ndataset = load_dataset(\"ai4bharat/IndicMTEval\")\n\nprint(dataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:57:56.518338Z","iopub.execute_input":"2026-04-07T13:57:56.518674Z","iopub.status.idle":"2026-04-07T13:58:04.179446Z","shell.execute_reply.started":"2026-04-07T13:57:56.518651Z","shell.execute_reply":"2026-04-07T13:58:04.178522Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"README.md: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"bbf79717c7914d9bb57bd0544da75508"}},"metadata":{}},{"name":"stderr","text":"Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"data/test-00000-of-00001.parquet: 0%| | 0.00/498k [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"565c3cb7ad5a43e2a29724bd1e435a2f"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"data/validation-00000-of-00001.parquet: 0%| | 0.00/200k [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"9e90d4fcdd024be999e3f586dfc11246"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"data/train-00000-of-00001.parquet: 0%| | 0.00/1.13M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"425ca729b71e49feb37fab22b48e1912"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating test split: 0%| | 0/2380 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"a45849449bd5417aa09b4c3538e53765"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating validation split: 0%| | 0/1000 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"3887b978c91542ad8574f5891ff6e25c"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating train split: 0%| | 0/4997 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"32c3ab646a1f4e719b507d54b501fb13"}},"metadata":{}},{"name":"stdout","text":"DatasetDict({\n test: Dataset({\n features: ['src', 'ref', 'translation', 'mqm_norm_score', 'da_norm_score', 'error_spans', 'language', 'split'],\n num_rows: 2380\n })\n validation: Dataset({\n features: ['src', 'ref', 'translation', 'mqm_norm_score', 'da_norm_score', 'error_spans', 'language', 'split'],\n num_rows: 1000\n })\n train: Dataset({\n features: ['src', 'ref', 'translation', 'mqm_norm_score', 'da_norm_score', 'error_spans', 'language', 'split'],\n num_rows: 4997\n })\n})\n","output_type":"stream"}],"execution_count":1},{"cell_type":"code","source":"print(dataset['test'][0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:58:38.291929Z","iopub.execute_input":"2026-04-07T13:58:38.292406Z","iopub.status.idle":"2026-04-07T13:58:38.298478Z","shell.execute_reply.started":"2026-04-07T13:58:38.292357Z","shell.execute_reply":"2026-04-07T13:58:38.297632Z"}},"outputs":[{"name":"stdout","text":"{'src': 'The Chaco region was home to other groups of indigenous tribes such as the Guaycurú and Payaguá, who survived by hunting, gathering and fishing.', 'ref': 'ਚਾਕੋ ਖੇਤਰ ਦੇਸੀ ਕਬੀਲਿਆਂ ਦੇ ਹੋਰ ਸਮੂਹਾਂ ਜਿਵੇਂ ਗਾਇਕੂਰੁ ਅਤੇ ਪਾਇਗੂਆ ਦਾ ਘਰ ਸੀ, ਜੋ ਸ਼ਿਕਾਰ, ਇਕੱਠੇ ਰਹਿਣ ਅਤੇ ਫਿਸ਼ਿੰਗ ਦੁਆਰਾ ਬਚ ਗਏ ਸਨ।', 'translation': 'ਚਾਕੋ ਖੇਤਰ ਸਵਦੇਸ਼ੀ ਕਬੀਲਿਆਂ ਦੇ ਹੋਰ ਸਮੂਹਾਂ ਦਾ ਘਰ ਸੀ ਜਿਵੇਂ ਕਿ ਗੁਆਏਕੁਰੂ ਅਤੇ ਪਯਾਗੁਆ, ਜੋ ਸ਼ਿਕਾਰ, ਇਕੱਠੇ ਕਰਨ ਅਤੇ ਮੱਛੀਆਂ ਫੜ ਕੇ ਬਚੇ ਸਨ।', 'mqm_norm_score': '0.84', 'da_norm_score': '0.4', 'error_spans': [{'span_end_offset': 24, 'span_no': 0, 'span_severity': 'High', 'span_start_offset': 17, 'span_text': 'ਇਕੱਠੇ ਕਰਨ ਅਤੇ ਮੱਛੀਆਂ ਫੜ ਕੇ ਬਚੇ ਸਨ', 'span_type': 'Style_Awkward'}], 'language': 'Punjabi', 'split': 'test'}\n","output_type":"stream"}],"execution_count":5},{"cell_type":"code","source":"dataset_small = dataset['test'].shuffle(seed=42).select(range(1000))\n\nprint(len(dataset_small))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:58:44.612036Z","iopub.execute_input":"2026-04-07T13:58:44.612492Z","iopub.status.idle":"2026-04-07T13:58:44.629557Z","shell.execute_reply.started":"2026-04-07T13:58:44.612462Z","shell.execute_reply":"2026-04-07T13:58:44.628913Z"}},"outputs":[{"name":"stdout","text":"1000\n","output_type":"stream"}],"execution_count":6},{"cell_type":"code","source":"print(dataset_small[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:58:47.726217Z","iopub.execute_input":"2026-04-07T13:58:47.726554Z","iopub.status.idle":"2026-04-07T13:58:47.731521Z","shell.execute_reply.started":"2026-04-07T13:58:47.726527Z","shell.execute_reply":"2026-04-07T13:58:47.730601Z"}},"outputs":[{"name":"stdout","text":"{'src': 'There were no large forests in the land of Canaan, so wood was extremely expensive.', 'ref': 'கானானின் நாட்டில் பெரிய காடுகள் இல்லாததால், மரம் மிகவும் விலை உயர்ந்ததாய் இருந்தது.', 'translation': 'கானான் தேசத்தில் பெரிய காடுகள் இல்லை, எனவே\\xa0மரம் மிகவும் விலை உயர்ந்தது.', 'mqm_norm_score': '0.92', 'da_norm_score': '0.92', 'error_spans': [{'span_end_offset': 9, 'span_no': 0, 'span_severity': 'Medium', 'span_start_offset': 6, 'span_text': 'மரம் மிகவும் விலை உயர்ந்தது', 'span_type': 'Style_Awkward'}], 'language': 'Tamil', 'split': 'test'}\n","output_type":"stream"}],"execution_count":7},{"cell_type":"code","source":"sources = []\nreferences = []\npredictions = []\n\nfor example in dataset_small:\n sources.append(example['src'])\n references.append(example['ref'])\n predictions.append(example['translation'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T13:58:55.658018Z","iopub.execute_input":"2026-04-07T13:58:55.658967Z","iopub.status.idle":"2026-04-07T13:58:55.809195Z","shell.execute_reply.started":"2026-04-07T13:58:55.658924Z","shell.execute_reply":"2026-04-07T13:58:55.808604Z"}},"outputs":[],"execution_count":8},{"cell_type":"code","source":"import re\n\ndef preprocess(text):\n text = text.lower()\n text = re.sub(r\"\\s+\", \" \", text)\n text = text.strip()\n return text\n\nreferences = [preprocess(t) for t in references]\npredictions = [preprocess(t) for t in predictions]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T14:37:50.127974Z","iopub.execute_input":"2026-03-17T14:37:50.128677Z","iopub.status.idle":"2026-03-17T14:37:50.155174Z","shell.execute_reply.started":"2026-03-17T14:37:50.128650Z","shell.execute_reply":"2026-03-17T14:37:50.154319Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"references_bleu = [[ref] for ref in references]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:05:03.980389Z","iopub.execute_input":"2026-03-17T15:05:03.980643Z","iopub.status.idle":"2026-03-17T15:05:03.984662Z","shell.execute_reply.started":"2026-03-17T15:05:03.980622Z","shell.execute_reply":"2026-03-17T15:05:03.983869Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import evaluate\n\nbleu = evaluate.load(\"bleu\")\n\nbleu_score = bleu.compute(\n predictions=predictions,\n references=references_bleu\n)\n\nprint(\"BLEU Score:\", bleu_score[\"bleu\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:11:36.761270Z","iopub.execute_input":"2026-03-17T15:11:36.761877Z","iopub.status.idle":"2026-03-17T15:11:38.191906Z","shell.execute_reply.started":"2026-03-17T15:11:36.761843Z","shell.execute_reply":"2026-03-17T15:11:38.191301Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rouge = evaluate.load(\"rouge\")\n\nrouge_score = rouge.compute(\n predictions=predictions,\n references=references\n)\n\nprint(\"ROUGE Scores:\", rouge_score)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:11:52.613038Z","iopub.execute_input":"2026-03-17T15:11:52.613531Z","iopub.status.idle":"2026-03-17T15:11:54.143490Z","shell.execute_reply.started":"2026-03-17T15:11:52.613482Z","shell.execute_reply":"2026-03-17T15:11:54.142701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"meteor = evaluate.load(\"meteor\")\n\nmeteor_score = meteor.compute(\n predictions=predictions,\n references=references\n)\n\nprint(\"METEOR Score:\", meteor_score[\"meteor\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:12:06.504153Z","iopub.execute_input":"2026-03-17T15:12:06.504704Z","iopub.status.idle":"2026-03-17T15:12:10.535362Z","shell.execute_reply.started":"2026-03-17T15:12:06.504674Z","shell.execute_reply":"2026-03-17T15:12:10.534441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sacrebleu\n\nchrf = sacrebleu.corpus_chrf(predictions, [references])\n\nprint(\"chrF Score:\", chrf.score)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:12:22.774589Z","iopub.execute_input":"2026-03-17T15:12:22.775220Z","iopub.status.idle":"2026-03-17T15:12:23.304853Z","shell.execute_reply.started":"2026-03-17T15:12:22.775178Z","shell.execute_reply":"2026-03-17T15:12:23.304153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n===== Machine Translation Evaluation =====\")\n\nprint(\"BLEU:\", bleu_score[\"bleu\"])\nprint(\"ROUGE-L:\", rouge_score[\"rougeL\"])\nprint(\"METEOR:\", meteor_score[\"meteor\"])\nprint(\"chrF:\", chrf.score)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:12:35.004597Z","iopub.execute_input":"2026-03-17T15:12:35.005528Z","iopub.status.idle":"2026-03-17T15:12:35.010226Z","shell.execute_reply.started":"2026-03-17T15:12:35.005496Z","shell.execute_reply":"2026-03-17T15:12:35.009305Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mqm_scores = [float(x) for x in dataset_small['mqm_norm_score']]\nda_scores = [float(x) for x in dataset_small['da_norm_score']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:12:49.556214Z","iopub.execute_input":"2026-03-17T15:12:49.556903Z","iopub.status.idle":"2026-03-17T15:12:49.652373Z","shell.execute_reply.started":"2026-03-17T15:12:49.556876Z","shell.execute_reply":"2026-03-17T15:12:49.651807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sentence_transformers import SentenceTransformer\n\nmodel = SentenceTransformer('all-MiniLM-L6-v2')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:08:21.343969Z","iopub.execute_input":"2026-03-17T15:08:21.344737Z","iopub.status.idle":"2026-03-17T15:08:43.052938Z","shell.execute_reply.started":"2026-03-17T15:08:21.344696Z","shell.execute_reply":"2026-03-17T15:08:43.052173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ref_embeddings = model.encode(references, convert_to_tensor=True)\npred_embeddings = model.encode(predictions, convert_to_tensor=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:08:48.335680Z","iopub.execute_input":"2026-03-17T15:08:48.336207Z","iopub.status.idle":"2026-03-17T15:08:50.372971Z","shell.execute_reply.started":"2026-03-17T15:08:48.336179Z","shell.execute_reply":"2026-03-17T15:08:50.372145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sentence_transformers.util import cos_sim\n\nsimilarities = []\n\nfor i in range(len(ref_embeddings)):\n sim = cos_sim(ref_embeddings[i], pred_embeddings[i]).item()\n similarities.append(sim)\n\nprint(\"Average Semantic Similarity:\", sum(similarities)/len(similarities))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:08:59.230754Z","iopub.execute_input":"2026-03-17T15:08:59.231617Z","iopub.status.idle":"2026-03-17T15:08:59.422544Z","shell.execute_reply.started":"2026-03-17T15:08:59.231584Z","shell.execute_reply":"2026-03-17T15:08:59.421815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n===== Machine Translation Evaluation =====\")\n\nprint(\"BLEU:\", bleu_score[\"bleu\"])\nprint(\"ROUGE-L:\", rouge_score[\"rougeL\"])\nprint(\"METEOR:\", meteor_score[\"meteor\"])\nprint(\"chrF:\", chrf.score)\nprint(\"Embedding Similarity:\", sum(similarities)/len(similarities))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:21:00.491387Z","iopub.execute_input":"2026-03-17T15:21:00.491756Z","iopub.status.idle":"2026-03-17T15:21:00.497062Z","shell.execute_reply.started":"2026-03-17T15:21:00.491728Z","shell.execute_reply":"2026-03-17T15:21:00.496289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ref_embeddings_np = ref_embeddings.cpu().numpy()\npred_embeddings_np = pred_embeddings.cpu().numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:46:52.611222Z","iopub.execute_input":"2026-03-17T15:46:52.611643Z","iopub.status.idle":"2026-03-17T15:46:52.617725Z","shell.execute_reply.started":"2026-03-17T15:46:52.611601Z","shell.execute_reply":"2026-03-17T15:46:52.616700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nnp.save(\"reference_embeddings.npy\", ref_embeddings_np)\nnp.save(\"prediction_embeddings.npy\", pred_embeddings_np)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:48:34.950880Z","iopub.execute_input":"2026-03-17T15:48:34.951585Z","iopub.status.idle":"2026-03-17T15:48:34.958657Z","shell.execute_reply.started":"2026-03-17T15:48:34.951552Z","shell.execute_reply":"2026-03-17T15:48:34.957827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf_embeddings = pd.DataFrame({\n \"reference\": references,\n \"prediction\": predictions\n})\n\ndf_embeddings[\"ref_embedding\"] = list(ref_embeddings_np)\ndf_embeddings[\"pred_embedding\"] = list(pred_embeddings_np)\n\ndf_embeddings.to_pickle(\"translation_embeddings.pkl\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:49:06.119746Z","iopub.execute_input":"2026-03-17T15:49:06.120375Z","iopub.status.idle":"2026-03-17T15:49:06.138649Z","shell.execute_reply.started":"2026-03-17T15:49:06.120343Z","shell.execute_reply":"2026-03-17T15:49:06.137810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_embeddings = pd.read_pickle(\"translation_embeddings.pkl\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-17T15:49:15.540370Z","iopub.execute_input":"2026-03-17T15:49:15.541005Z","iopub.status.idle":"2026-03-17T15:49:15.555991Z","shell.execute_reply.started":"2026-03-17T15:49:15.540975Z","shell.execute_reply":"2026-03-17T15:49:15.555048Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"TAMIL TRANSLATION EVALUATION","metadata":{}},{"cell_type":"code","source":"!pip install transformers sentencepiece -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:06:37.338975Z","iopub.execute_input":"2026-04-13T14:06:37.339379Z","iopub.status.idle":"2026-04-13T14:06:42.392603Z","shell.execute_reply.started":"2026-04-13T14:06:37.339350Z","shell.execute_reply":"2026-04-13T14:06:42.391570Z"}},"outputs":[],"execution_count":1},{"cell_type":"code","source":"!pip install indic-transliteration sentencepiece transformers -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:07:45.876532Z","iopub.execute_input":"2026-04-13T14:07:45.877376Z","iopub.status.idle":"2026-04-13T14:07:49.351416Z","shell.execute_reply.started":"2026-04-13T14:07:45.877344Z","shell.execute_reply":"2026-04-13T14:07:49.350291Z"}},"outputs":[],"execution_count":3},{"cell_type":"code","source":"from datasets import load_dataset\n\ndataset = load_dataset(\"ai4bharat/IndicMTEval\", split=\"test\")\n\nprint(len(dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:07:53.125379Z","iopub.execute_input":"2026-04-13T14:07:53.126064Z","iopub.status.idle":"2026-04-13T14:07:57.439367Z","shell.execute_reply.started":"2026-04-13T14:07:53.126030Z","shell.execute_reply":"2026-04-13T14:07:57.438532Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"README.md: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"e5da853946ee4e5b8fccb9ef07fa5015"}},"metadata":{}},{"name":"stderr","text":"Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"data/test-00000-of-00001.parquet: 0%| | 0.00/498k [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"2fecf6eff9a94e10a935037c4f9e649c"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"data/validation-00000-of-00001.parquet: 0%| | 0.00/200k [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"f1bb0c3ffc0d42d0b15cd5d8f5f34daf"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"data/train-00000-of-00001.parquet: 0%| | 0.00/1.13M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"c6694142355e4d47a34e4622ec2304f2"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating test split: 0%| | 0/2380 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"9acf411cf9ce4f7780545562f7e48871"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating validation split: 0%| | 0/1000 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"9d74da65a6f9468bb816fc0d37bc5778"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Generating train split: 0%| | 0/4997 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"e11e8b874bb1450fb41136e3092ecf86"}},"metadata":{}},{"name":"stdout","text":"2380\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"dataset_tamil = dataset.filter(lambda x: x[\"language\"] == \"Tamil\")\n\nprint(\"Tamil samples:\", len(dataset_tamil))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:08:03.544138Z","iopub.execute_input":"2026-04-13T14:08:03.545149Z","iopub.status.idle":"2026-04-13T14:08:03.664604Z","shell.execute_reply.started":"2026-04-13T14:08:03.545116Z","shell.execute_reply":"2026-04-13T14:08:03.663492Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"Filter: 0%| | 0/2380 [00:00<?, ? examples/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"e1ffd433238c4c439b2b7fbc331b87af"}},"metadata":{}},{"name":"stdout","text":"Tamil samples: 276\n","output_type":"stream"}],"execution_count":5},{"cell_type":"code","source":"sources = dataset_tamil[\"src\"]\nreferences = dataset_tamil[\"ref\"]\n\nprint(len(sources))\nprint(sources[0])\nprint(references[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:08:09.070305Z","iopub.execute_input":"2026-04-13T14:08:09.070864Z","iopub.status.idle":"2026-04-13T14:08:09.078276Z","shell.execute_reply.started":"2026-04-13T14:08:09.070837Z","shell.execute_reply":"2026-04-13T14:08:09.077433Z"}},"outputs":[{"name":"stdout","text":"276\nThe U.N. also hopes to finalize a fund to help countries affected by global warming to cope with the impacts.\nஉலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என U.N. நம்புகிறது.\n","output_type":"stream"}],"execution_count":7},{"cell_type":"code","source":"import re\n\ndef preprocess(text):\n text = text.lower()\n text = re.sub(r\"\\s+\", \" \", text)\n text = text.strip()\n return text\n\nsources = [preprocess(t) for t in sources]\nreferences = [preprocess(t) for t in references]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:08:11.715939Z","iopub.execute_input":"2026-04-13T14:08:11.716215Z","iopub.status.idle":"2026-04-13T14:08:11.758457Z","shell.execute_reply.started":"2026-04-13T14:08:11.716195Z","shell.execute_reply":"2026-04-13T14:08:11.757821Z"}},"outputs":[],"execution_count":8},{"cell_type":"code","source":"from transformers import NllbTokenizer, AutoModelForSeq2SeqLM\n\nmodel_name = \"facebook/nllb-200-distilled-600M\"\n\ntokenizer = NllbTokenizer.from_pretrained(model_name)\nmodel = AutoModelForSeq2SeqLM.from_pretrained(model_name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T15:14:39.280280Z","iopub.execute_input":"2026-04-07T15:14:39.281107Z","iopub.status.idle":"2026-04-07T15:14:43.590255Z","shell.execute_reply.started":"2026-04-07T15:14:39.281072Z","shell.execute_reply":"2026-04-07T15:14:43.589339Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"sentencepiece.bpe.model: 0%| | 0.00/4.85M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"e0dc960e7be842f9b7fb16e37c1e7858"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/512 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"ae9ab0e7320e419d872e51dce6b95fa3"}},"metadata":{}},{"name":"stderr","text":"The tied weights mapping and config for this model specifies to tie model.shared.weight to lm_head.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\nThe tied weights mapping and config for this model specifies to tie model.shared.weight to model.decoder.embed_tokens.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\nThe tied weights mapping and config for this model specifies to tie model.shared.weight to model.encoder.embed_tokens.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\n","output_type":"stream"}],"execution_count":29},{"cell_type":"code","source":"model_predictions = []\n\nbatch_size = 8\n\nfor i in range(0, len(sources), batch_size):\n\n batch = sources[i:i+batch_size]\n\n inputs = tokenizer(\n batch,\n return_tensors=\"pt\",\n padding=True,\n truncation=True\n )\n\n translated = model.generate(\n **inputs,\n forced_bos_token_id=tokenizer.convert_tokens_to_ids(\"tam_Taml\")\n )\n\n preds = tokenizer.batch_decode(\n translated,\n skip_special_tokens=True\n )\n\n model_predictions.extend(preds)\n\nprint(\"Total predictions:\", len(model_predictions))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T15:14:59.578073Z","iopub.execute_input":"2026-04-07T15:14:59.578798Z","iopub.status.idle":"2026-04-07T15:22:43.366031Z","shell.execute_reply.started":"2026-04-07T15:14:59.578763Z","shell.execute_reply":"2026-04-07T15:22:43.365294Z"}},"outputs":[{"name":"stdout","text":"Total predictions: 276\n","output_type":"stream"}],"execution_count":30},{"cell_type":"code","source":"model_predictions = [preprocess(t) for t in model_predictions]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T15:24:33.711210Z","iopub.execute_input":"2026-04-07T15:24:33.712107Z","iopub.status.idle":"2026-04-07T15:24:33.718749Z","shell.execute_reply.started":"2026-04-07T15:24:33.712073Z","shell.execute_reply":"2026-04-07T15:24:33.717977Z"}},"outputs":[],"execution_count":31},{"cell_type":"code","source":"import evaluate\nimport sacrebleu\nfrom sentence_transformers import SentenceTransformer\nfrom sklearn.metrics.pairwise import cosine_similarity\nimport numpy as np\n\nprint(\"Evaluating translations...\\n\")\n\n# BLEU\nbleu = evaluate.load(\"bleu\")\nreferences_bleu = [[ref] for ref in references]\n\nbleu_score = bleu.compute(\n predictions=model_predictions,\n references=references_bleu\n)\n\n# chrF\nchrf = sacrebleu.corpus_chrf(\n model_predictions,\n [references]\n)\n\n# BERTScore\nbertscore = evaluate.load(\"bertscore\")\n\nbert_results = bertscore.compute(\n predictions=model_predictions,\n references=references,\n lang=\"ta\"\n)\n\nbert_f1 = np.mean(bert_results[\"f1\"])\n\n# Embedding Similarity\nembed_model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n\nref_embeddings = embed_model.encode(references)\npred_embeddings = embed_model.encode(model_predictions)\n\nsimilarities = cosine_similarity(\n ref_embeddings,\n pred_embeddings\n).diagonal()\n\nembedding_similarity = np.mean(similarities)\n\n# Print Results\nprint(\"===== Evaluation Results =====\\n\")\n\nprint(\"BLEU Score:\", bleu_score[\"bleu\"])\nprint(\"chrF Score:\", chrf.score)\nprint(\"BERTScore F1:\", bert_f1)\nprint(\"Embedding Similarity:\", embedding_similarity)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T15:24:37.489780Z","iopub.execute_input":"2026-04-07T15:24:37.490059Z","iopub.status.idle":"2026-04-07T15:24:43.640811Z","shell.execute_reply.started":"2026-04-07T15:24:37.490034Z","shell.execute_reply":"2026-04-07T15:24:43.640136Z"}},"outputs":[{"name":"stdout","text":"Evaluating translations...\n\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/199 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"15f199e3f61945c4a5d670c9efdd15db"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: bert-base-multilingual-cased\nKey | Status | | \n-------------------------------------------+------------+--+-\ncls.predictions.transform.LayerNorm.weight | UNEXPECTED | | \ncls.seq_relationship.weight | UNEXPECTED | | \ncls.predictions.bias | UNEXPECTED | | \ncls.seq_relationship.bias | UNEXPECTED | | \ncls.predictions.transform.dense.weight | UNEXPECTED | | \ncls.predictions.transform.dense.bias | UNEXPECTED | | \ncls.predictions.transform.LayerNorm.bias | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/103 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"9a600ba2467e4d7e8d71edf445a5d26a"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: sentence-transformers/all-MiniLM-L6-v2\nKey | Status | | \n------------------------+------------+--+-\nembeddings.position_ids | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"name":"stdout","text":"===== Evaluation Results =====\n\nBLEU Score: 0.14073740207540267\nchrF Score: 54.626687127913556\nBERTScore F1: 0.8497447468664335\nEmbedding Similarity: 0.8760938\n","output_type":"stream"}],"execution_count":32},{"cell_type":"code","source":"for i in range(5):\n print(\"SOURCE:\", sources[i])\n print(\"REFERENCE:\", references[i])\n print(\"MODEL:\", model_predictions[i])\n print()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-07T15:25:07.899253Z","iopub.execute_input":"2026-04-07T15:25:07.899926Z","iopub.status.idle":"2026-04-07T15:25:07.905307Z","shell.execute_reply.started":"2026-04-07T15:25:07.899892Z","shell.execute_reply":"2026-04-07T15:25:07.904399Z"}},"outputs":[{"name":"stdout","text":"SOURCE: the u.n. also hopes to finalize a fund to help countries affected by global warming to cope with the impacts.\nREFERENCE: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என u.n. நம்புகிறது.\nMODEL: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு தாக்கங்களை சமாளிக்க உதவும் ஒரு நிதியை இறுதி செய்ய ஐ. நா. நம்புகிறது.\n\nSOURCE: the u.n. also hopes to finalize a fund to help countries affected by global warming to cope with the impacts.\nREFERENCE: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என u.n. நம்புகிறது.\nMODEL: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு தாக்கங்களை சமாளிக்க உதவும் ஒரு நிதியை இறுதி செய்ய ஐ. நா. நம்புகிறது.\n\nSOURCE: the u.n. also hopes to finalize a fund to help countries affected by global warming to cope with the impacts.\nREFERENCE: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என u.n. நம்புகிறது.\nMODEL: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு தாக்கங்களை சமாளிக்க உதவும் ஒரு நிதியை இறுதி செய்ய ஐ. நா. நம்புகிறது.\n\nSOURCE: the u.n. also hopes tofinalize a fund to help countries affected by global warming to cope with the impacts.\nREFERENCE: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என u.n. நம்புகிறது.\nMODEL: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு தாக்கங்களை சமாளிக்க உதவும் ஒரு நிதியை இறுதி செய்ய ஐ. நா. எதிர்பார்த்தது.\n\nSOURCE: the u.n. also hopes to finalize a fund to help countries affected by global warming to cope with the impacts.\nREFERENCE: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு இந்த பாதிப்புகளை சமாளிக்க உதவும் வகையில் ஒரு நிதியை முடிவு செய்ய இயலும் என u.n. நம்புகிறது.\nMODEL: உலக வெப்பமயமாதலால் பாதிக்கப்பட்ட நாடுகளுக்கு தாக்கங்களை சமாளிக்க உதவும் ஒரு நிதியை இறுதி செய்ய ஐ. நா. நம்புகிறது.\n\n","output_type":"stream"}],"execution_count":33},{"cell_type":"markdown","source":"google/t5-small","metadata":{}},{"cell_type":"code","source":"from transformers import T5Tokenizer, T5ForConditionalGeneration\n\nmodel_name = \"t5-base\"\n\ntokenizer = T5Tokenizer.from_pretrained(model_name)\nmodel = T5ForConditionalGeneration.from_pretrained(model_name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:10:33.616239Z","iopub.execute_input":"2026-04-13T14:10:33.616644Z","iopub.status.idle":"2026-04-13T14:10:37.245330Z","shell.execute_reply.started":"2026-04-13T14:10:33.616617Z","shell.execute_reply":"2026-04-13T14:10:37.244663Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"spiece.model: 0%| | 0.00/792k [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"28e0cc45a4c848439bf35c3e8c84ac87"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"tokenizer.json: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"fdf032dce8fb47598bd3fc44234be408"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"config.json: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"67719da1b49149888b6f0e5ebd40bf4f"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"model.safetensors: 0%| | 0.00/892M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"753f135b4bfa43c3a9378027a82fa256"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/257 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"e276a949ccaa4cabb6525ae7ebd0af9a"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"generation_config.json: 0%| | 0.00/147 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"3bcd4b4a554449059ce71cc33b4d1ec4"}},"metadata":{}}],"execution_count":11},{"cell_type":"code","source":"t5_predictions = []\n\nbatch_size = 8\n\nfor i in range(0, len(sources), batch_size):\n\n batch = sources[i:i+batch_size]\n\n inputs = tokenizer(\n [\"translate English to Tamil: \" + s for s in batch],\n return_tensors=\"pt\",\n padding=True,\n truncation=True\n )\n\n outputs = model.generate(**inputs)\n\n preds = tokenizer.batch_decode(outputs, skip_special_tokens=True)\n\n t5_predictions.extend(preds)\n\nprint(\"Total predictions:\", len(t5_predictions))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:11:03.769110Z","iopub.execute_input":"2026-04-13T14:11:03.769856Z","iopub.status.idle":"2026-04-13T14:12:13.783719Z","shell.execute_reply.started":"2026-04-13T14:11:03.769826Z","shell.execute_reply":"2026-04-13T14:12:13.782942Z"}},"outputs":[{"name":"stdout","text":"Total predictions: 276\n","output_type":"stream"}],"execution_count":12},{"cell_type":"code","source":"t5_predictions = [preprocess(t) for t in t5_predictions]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:12:54.730884Z","iopub.execute_input":"2026-04-13T14:12:54.731506Z","iopub.status.idle":"2026-04-13T14:12:54.737104Z","shell.execute_reply.started":"2026-04-13T14:12:54.731478Z","shell.execute_reply":"2026-04-13T14:12:54.736215Z"}},"outputs":[],"execution_count":14},{"cell_type":"code","source":"!pip install bert-score -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:19:33.365552Z","iopub.execute_input":"2026-04-13T14:19:33.366469Z","iopub.status.idle":"2026-04-13T14:19:37.129365Z","shell.execute_reply.started":"2026-04-13T14:19:33.366437Z","shell.execute_reply":"2026-04-13T14:19:37.128499Z"}},"outputs":[],"execution_count":24},{"cell_type":"code","source":"clean_refs = []\nclean_preds = []\n\nfor ref, pred in zip(references, t5_predictions):\n if ref.strip() != \"\" and pred.strip() != \"\":\n clean_refs.append(ref)\n clean_preds.append(pred)\n\nprint(\"Valid samples:\", len(clean_preds))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:22:07.864837Z","iopub.execute_input":"2026-04-13T14:22:07.865290Z","iopub.status.idle":"2026-04-13T14:22:07.870903Z","shell.execute_reply.started":"2026-04-13T14:22:07.865260Z","shell.execute_reply":"2026-04-13T14:22:07.870257Z"}},"outputs":[{"name":"stdout","text":"Valid samples: 270\n","output_type":"stream"}],"execution_count":27},{"cell_type":"code","source":"import evaluate\nimport sacrebleu\nfrom sentence_transformers import SentenceTransformer\nfrom sklearn.metrics.pairwise import cosine_similarity\nimport numpy as np\nfrom bert_score import score\n\nprint(\"Evaluating translations...\\n\")\n\n# BLEU\nbleu = evaluate.load(\"bleu\")\n\nreferences_bleu = [[ref] for ref in clean_refs]\n\nbleu_score = bleu.compute(\n predictions=clean_preds,\n references=references_bleu\n)\n\n# chrF\nchrf = sacrebleu.corpus_chrf(\n clean_preds,\n [clean_refs]\n)\n\n# BERTScore\nP, R, F1 = score(\n clean_preds,\n clean_refs,\n model_type=\"bert-base-multilingual-cased\"\n)\n\nbert_f1 = F1.mean().item()\n\n# Embedding similarity\nembed_model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n\nref_embeddings = embed_model.encode(clean_refs)\npred_embeddings = embed_model.encode(clean_preds)\n\nsimilarities = cosine_similarity(\n ref_embeddings,\n pred_embeddings\n).diagonal()\n\nembedding_similarity = np.mean(similarities)\n\nprint(\"\\n===== Evaluation Results =====\\n\")\n\nprint(\"BLEU Score:\", bleu_score[\"bleu\"])\nprint(\"chrF Score:\", chrf.score)\nprint(\"BERTScore F1:\", bert_f1)\nprint(\"Embedding Similarity:\", embedding_similarity)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:22:26.857643Z","iopub.execute_input":"2026-04-13T14:22:26.858027Z","iopub.status.idle":"2026-04-13T14:22:31.498808Z","shell.execute_reply.started":"2026-04-13T14:22:26.858000Z","shell.execute_reply":"2026-04-13T14:22:31.497308Z"}},"outputs":[{"name":"stdout","text":"Evaluating translations...\n\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/199 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"fde80442a64741e7b936685019b39dff"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: bert-base-multilingual-cased\nKey | Status | | \n-------------------------------------------+------------+--+-\ncls.predictions.transform.dense.weight | UNEXPECTED | | \ncls.predictions.transform.LayerNorm.bias | UNEXPECTED | | \ncls.predictions.transform.LayerNorm.weight | UNEXPECTED | | \ncls.seq_relationship.weight | UNEXPECTED | | \ncls.predictions.bias | UNEXPECTED | | \ncls.seq_relationship.bias | UNEXPECTED | | \ncls.predictions.transform.dense.bias | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"modules.json: 0%| | 0.00/349 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"151a986d6ee640e294739d16b6524a88"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"config_sentence_transformers.json: 0%| | 0.00/116 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"fc3f6bcdcc684951807ecb8b3d28216d"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"README.md: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"ce53da5026404fc181f10a7187706ebf"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"sentence_bert_config.json: 0%| | 0.00/53.0 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"7f1c300802464ac2b959a595e888e7a4"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"config.json: 0%| | 0.00/612 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"0ce5691c8d5342c28d187f89943d21e1"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"model.safetensors: 0%| | 0.00/90.9M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"5f078f598b414670a822897714c93cfd"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/103 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"628353cc60894f0fb1254bcfb9b2fb81"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: sentence-transformers/all-MiniLM-L6-v2\nKey | Status | | \n------------------------+------------+--+-\nembeddings.position_ids | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"tokenizer_config.json: 0%| | 0.00/350 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"a137236ace5948bc86d3a5197426f9a9"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"vocab.txt: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"3dd1e12999cc4e4495e3bbb8ffbfbf0c"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"tokenizer.json: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"41b0721192b44377b6b742c577c6135a"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"special_tokens_map.json: 0%| | 0.00/112 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"c4acad0a952d4abd846a859daa7a0328"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"config.json: 0%| | 0.00/190 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"4adefd2952fd49519132752f307357c0"}},"metadata":{}},{"name":"stdout","text":"\n===== Evaluation Results =====\n\nBLEU Score: 0.0\nchrF Score: 0.22674024250644748\nBERTScore F1: 0.6173190474510193\nEmbedding Similarity: 0.28766814\n","output_type":"stream"}],"execution_count":28},{"cell_type":"code","source":"from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer\n\nmodel_name = \"facebook/m2m100_418M\"\n\ntokenizer = M2M100Tokenizer.from_pretrained(model_name)\nmodel = M2M100ForConditionalGeneration.from_pretrained(model_name)\n\nprint(\"M2M100 model loaded\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:24:05.783392Z","iopub.execute_input":"2026-04-13T14:24:05.784332Z","iopub.status.idle":"2026-04-13T14:24:15.849381Z","shell.execute_reply.started":"2026-04-13T14:24:05.784298Z","shell.execute_reply":"2026-04-13T14:24:15.845985Z"}},"outputs":[{"output_type":"display_data","data":{"text/plain":"tokenizer_config.json: 0%| | 0.00/298 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"43521045de01425f909925bf58b0545b"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"vocab.json: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"b6aa3310b13c428b84fa8f48d7c1e8ef"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"sentencepiece.bpe.model: 0%| | 0.00/2.42M [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"73649809637a44e58a3ba9b4f407901b"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"special_tokens_map.json: 0.00B [00:00, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"1751081589d2446b9ab36e0b1dce21bb"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"config.json: 0%| | 0.00/908 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"fedb14d303924d57917cc93e20dcf6be"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"pytorch_model.bin: 0%| | 0.00/1.94G [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"048ec68d4d5e4cc08d9f72685612beaf"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"model.safetensors: 0%| | 0.00/1.94G [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"a627b041dcfd45f9bd0a506172a001b5"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/512 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"fc7b0e81139846ca9921622f1d138e13"}},"metadata":{}},{"name":"stderr","text":"The tied weights mapping and config for this model specifies to tie model.shared.weight to lm_head.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\nThe tied weights mapping and config for this model specifies to tie model.shared.weight to model.decoder.embed_tokens.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\nThe tied weights mapping and config for this model specifies to tie model.shared.weight to model.encoder.embed_tokens.weight, but both are present in the checkpoints, so we will NOT tie them. You should update the config with `tie_word_embeddings=False` to silence this warning\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"generation_config.json: 0%| | 0.00/233 [00:00<?, ?B/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"cfa8a6b0a6b4407db3b2a01058a268dd"}},"metadata":{}},{"name":"stdout","text":"M2M100 model loaded\n","output_type":"stream"}],"execution_count":29},{"cell_type":"code","source":"tokenizer.src_lang = \"en\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:24:27.946318Z","iopub.execute_input":"2026-04-13T14:24:27.947157Z","iopub.status.idle":"2026-04-13T14:24:28.764842Z","shell.execute_reply.started":"2026-04-13T14:24:27.947130Z","shell.execute_reply":"2026-04-13T14:24:28.763753Z"}},"outputs":[],"execution_count":31},{"cell_type":"code","source":"m2m_predictions = []\n\nbatch_size = 8\n\nfor i in range(0, len(sources), batch_size):\n\n batch = sources[i:i+batch_size]\n\n encoded = tokenizer(\n batch,\n return_tensors=\"pt\",\n padding=True,\n truncation=True\n )\n\n generated_tokens = model.generate(\n **encoded,\n forced_bos_token_id=tokenizer.get_lang_id(\"ta\")\n )\n\n preds = tokenizer.batch_decode(\n generated_tokens,\n skip_special_tokens=True\n )\n\n m2m_predictions.extend(preds)\n\nprint(\"Total predictions:\", len(m2m_predictions))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:24:39.242373Z","iopub.execute_input":"2026-04-13T14:24:39.243033Z","iopub.status.idle":"2026-04-13T14:38:05.293922Z","shell.execute_reply.started":"2026-04-13T14:24:39.243004Z","shell.execute_reply":"2026-04-13T14:38:05.292653Z"}},"outputs":[{"name":"stdout","text":"Total predictions: 276\n","output_type":"stream"}],"execution_count":32},{"cell_type":"code","source":"m2m_predictions = [preprocess(t) for t in m2m_predictions]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:50:58.063734Z","iopub.execute_input":"2026-04-13T14:50:58.064586Z","iopub.status.idle":"2026-04-13T14:50:58.070956Z","shell.execute_reply.started":"2026-04-13T14:50:58.064557Z","shell.execute_reply":"2026-04-13T14:50:58.070064Z"}},"outputs":[],"execution_count":33},{"cell_type":"code","source":"import evaluate\nimport sacrebleu\nfrom sentence_transformers import SentenceTransformer\nfrom sklearn.metrics.pairwise import cosine_similarity\nimport numpy as np\nfrom bert_score import score\n\nprint(\"Evaluating translations...\\n\")\n\n# CHANGE THIS depending on model\npredictions = m2m_predictions\n\n# Remove empty sentences\nclean_refs = []\nclean_preds = []\n\nfor ref, pred in zip(references, predictions):\n if ref.strip() != \"\" and pred.strip() != \"\":\n clean_refs.append(ref)\n clean_preds.append(pred)\n\nprint(\"Valid samples:\", len(clean_preds))\n\n# BLEU\nbleu = evaluate.load(\"bleu\")\n\nreferences_bleu = [[ref] for ref in clean_refs]\n\nbleu_score = bleu.compute(\n predictions=clean_preds,\n references=references_bleu\n)\n\n# chrF\nchrf = sacrebleu.corpus_chrf(\n clean_preds,\n [clean_refs]\n)\n\n# BERTScore\nP, R, F1 = score(\n clean_preds,\n clean_refs,\n model_type=\"bert-base-multilingual-cased\"\n)\n\nbert_f1 = F1.mean().item()\n\n# Embedding Similarity\nembed_model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n\nref_embeddings = embed_model.encode(clean_refs)\npred_embeddings = embed_model.encode(clean_preds)\n\nsimilarities = cosine_similarity(\n ref_embeddings,\n pred_embeddings\n).diagonal()\n\nembedding_similarity = np.mean(similarities)\n\n# Print Results\nprint(\"\\n===== Evaluation Results =====\\n\")\n\nprint(\"BLEU Score:\", bleu_score[\"bleu\"])\nprint(\"chrF Score:\", chrf.score)\nprint(\"BERTScore F1:\", bert_f1)\nprint(\"Embedding Similarity:\", embedding_similarity)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-13T14:51:00.596479Z","iopub.execute_input":"2026-04-13T14:51:00.597264Z","iopub.status.idle":"2026-04-13T14:51:04.850673Z","shell.execute_reply.started":"2026-04-13T14:51:00.597236Z","shell.execute_reply":"2026-04-13T14:51:04.849305Z"}},"outputs":[{"name":"stdout","text":"Evaluating translations...\n\nValid samples: 276\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/199 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"4328a08f769745fdaea8c59818a2b7cb"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: bert-base-multilingual-cased\nKey | Status | | \n-------------------------------------------+------------+--+-\ncls.predictions.transform.dense.weight | UNEXPECTED | | \ncls.predictions.transform.LayerNorm.bias | UNEXPECTED | | \ncls.predictions.transform.LayerNorm.weight | UNEXPECTED | | \ncls.seq_relationship.weight | UNEXPECTED | | \ncls.predictions.bias | UNEXPECTED | | \ncls.seq_relationship.bias | UNEXPECTED | | \ncls.predictions.transform.dense.bias | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"Loading weights: 0%| | 0/103 [00:00<?, ?it/s]","application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"a316a81f0dbb4124afd1ae9c72870c2c"}},"metadata":{}},{"name":"stderr","text":"\u001b[1mBertModel LOAD REPORT\u001b[0m from: sentence-transformers/all-MiniLM-L6-v2\nKey | Status | | \n------------------------+------------+--+-\nembeddings.position_ids | UNEXPECTED | | \n\n\u001b[3mNotes:\n- UNEXPECTED\u001b[3m\t:can be ignored when loading from different task/architecture; not ok if you expect identical arch.\u001b[0m\n","output_type":"stream"},{"name":"stdout","text":"\n===== Evaluation Results =====\n\nBLEU Score: 0.008726440899402313\nchrF Score: 17.461341772346902\nBERTScore F1: 0.6680243611335754\nEmbedding Similarity: 0.5583976\n","output_type":"stream"}],"execution_count":34}]}
notebooks/evaluate_models.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ evaluation/evaluate_models.py
3
+ ─────────────────────────────────────────────────────────────────────────────
4
+ Evaluates three pretrained MT models on the ai4bharat/IndicMTEval Tamil subset.
5
+
6
+ Models evaluated:
7
+ 1. facebook/nllb-200-distilled-600M (NLLB)
8
+ 2. facebook/m2m100_418M (M2M100)
9
+ 3. t5-base (T5)
10
+
11
+ Metrics:
12
+ - BLEU
13
+ - chrF
14
+ - BERTScore F1
15
+ - Sentence Embedding Cosine Similarity
16
+
17
+ Usage:
18
+ python evaluate_models.py [--model nllb|m2m|t5|all] [--samples 200]
19
+ """
20
+
21
+ import re
22
+ import argparse
23
+ import numpy as np
24
+ import evaluate
25
+ import sacrebleu
26
+ from datasets import load_dataset
27
+ from transformers import (
28
+ NllbTokenizer, AutoModelForSeq2SeqLM,
29
+ M2M100Tokenizer, M2M100ForConditionalGeneration,
30
+ T5Tokenizer, T5ForConditionalGeneration,
31
+ )
32
+ from bert_score import score as bert_score
33
+ from sentence_transformers import SentenceTransformer
34
+ from sklearn.metrics.pairwise import cosine_similarity
35
+ import torch
36
+
37
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
38
+
39
+
40
+ # ─────────────────────────────────────────
41
+ # Preprocessing
42
+ # ─────────────────────────────────────────
43
+ def preprocess(text: str) -> str:
44
+ text = text.lower()
45
+ text = re.sub(r"\s+", " ", text)
46
+ return text.strip()
47
+
48
+
49
+ # ─────────────────────────────────────────
50
+ # Dataset
51
+ # ─────────────────────────────────────────
52
+ def load_tamil_data(num_samples: int = 200):
53
+ print("Loading IndicMTEval dataset...")
54
+ dataset = load_dataset("ai4bharat/IndicMTEval", split="test")
55
+ dataset_tamil = dataset.filter(lambda x: x["language"] == "Tamil")
56
+ if num_samples:
57
+ dataset_tamil = dataset_tamil.select(range(min(num_samples, len(dataset_tamil))))
58
+ sources = [preprocess(t) for t in dataset_tamil["src"]]
59
+ references = [preprocess(t) for t in dataset_tamil["ref"]]
60
+ print(f"Loaded {len(sources)} Tamil samples.")
61
+ return sources, references
62
+
63
+
64
+ # ─────────────────────────────────────────
65
+ # Model Translators(nllb)
66
+ # ─────────────────────────────────────────
67
+ def translate_nllb(sources, batch_size=8):
68
+ print("\n[NLLB] Loading model...")
69
+ tokenizer = NllbTokenizer.from_pretrained("facebook/nllb-200-distilled-600M")
70
+ model = AutoModelForSeq2SeqLM.from_pretrained("facebook/nllb-200-distilled-600M").to(DEVICE)
71
+ model.eval()
72
+ preds = []
73
+ for i in range(0, len(sources), batch_size):
74
+ batch = sources[i:i+batch_size]
75
+ inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(DEVICE)
76
+ with torch.no_grad():
77
+ out = model.generate(
78
+ **inputs,
79
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids("tam_Taml"),
80
+ num_beams=4, max_length=256,
81
+ )
82
+ preds.extend(tokenizer.batch_decode(out, skip_special_tokens=True))
83
+ print(f" NLLB: {min(i+batch_size, len(sources))}/{len(sources)}", end="\r")
84
+ return [preprocess(p) for p in preds]
85
+
86
+
87
+ def translate_m2m(sources, batch_size=8):
88
+ print("\n[M2M100] Loading model...")
89
+ tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_418M")
90
+ model = M2M100ForConditionalGeneration.from_pretrained("facebook/m2m100_418M").to(DEVICE)
91
+ model.eval()
92
+ tokenizer.src_lang = "en"
93
+ preds = []
94
+ for i in range(0, len(sources), batch_size):
95
+ batch = sources[i:i+batch_size]
96
+ encoded = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(DEVICE)
97
+ with torch.no_grad():
98
+ out = model.generate(
99
+ **encoded,
100
+ forced_bos_token_id=tokenizer.get_lang_id("ta"),
101
+ num_beams=4, max_length=256,
102
+ )
103
+ preds.extend(tokenizer.batch_decode(out, skip_special_tokens=True))
104
+ print(f" M2M: {min(i+batch_size, len(sources))}/{len(sources)}", end="\r")
105
+ return [preprocess(p) for p in preds]
106
+
107
+
108
+ def translate_t5(sources, batch_size=8):
109
+ print("\n[T5] Loading model...")
110
+ tokenizer = T5Tokenizer.from_pretrained("t5-base")
111
+ model = T5ForConditionalGeneration.from_pretrained("t5-base").to(DEVICE)
112
+ model.eval()
113
+ preds = []
114
+ for i in range(0, len(sources), batch_size):
115
+ batch = sources[i:i+batch_size]
116
+ inputs = tokenizer(
117
+ ["translate English to Tamil: " + s for s in batch],
118
+ return_tensors="pt", padding=True, truncation=True,
119
+ ).to(DEVICE)
120
+ with torch.no_grad():
121
+ out = model.generate(**inputs, max_length=256)
122
+ preds.extend(tokenizer.batch_decode(out, skip_special_tokens=True))
123
+ print(f" T5: {min(i+batch_size, len(sources))}/{len(sources)}", end="\r")
124
+ return [preprocess(p) for p in preds]
125
+
126
+
127
+ # ─────────────────────────────────────────
128
+ # Evaluation
129
+ # ─────────────────────────────────────────
130
+ def evaluate_predictions(predictions, references, model_name="Model"):
131
+ # Filter empty pairs
132
+ pairs = [(p, r) for p, r in zip(predictions, references) if p.strip() and r.strip()]
133
+ clean_preds, clean_refs = zip(*pairs)
134
+
135
+ print(f"\n Evaluating {model_name} on {len(clean_preds)} valid samples...")
136
+
137
+ # BLEU
138
+ bleu = evaluate.load("bleu")
139
+ bleu_score = bleu.compute(predictions=list(clean_preds), references=[[r] for r in clean_refs])["bleu"]
140
+
141
+ # chrF
142
+ chrf_score = sacrebleu.corpus_chrf(list(clean_preds), [list(clean_refs)]).score
143
+
144
+ # BERTScore
145
+ _, _, F1 = bert_score(list(clean_preds), list(clean_refs), model_type="bert-base-multilingual-cased")
146
+ bert_f1 = F1.mean().item()
147
+
148
+ # Embedding Cosine Similarity
149
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2")
150
+ ref_emb = embed_model.encode(list(clean_refs))
151
+ pred_emb = embed_model.encode(list(clean_preds))
152
+ cos_sim = cosine_similarity(ref_emb, pred_emb).diagonal().mean()
153
+
154
+ return {
155
+ "model": model_name,
156
+ "BLEU": round(bleu_score, 4),
157
+ "chrF": round(chrf_score, 2),
158
+ "BERTScore_F1": round(bert_f1, 4),
159
+ "EmbeddingSim": round(float(cos_sim), 4),
160
+ }
161
+
162
+
163
+ # ─────────────────────────────────────────
164
+ # Main
165
+ # ─────────────────────────────────────────
166
+ def main():
167
+ parser = argparse.ArgumentParser()
168
+ parser.add_argument("--model", default="all", choices=["nllb", "m2m", "t5", "all"])
169
+ parser.add_argument("--samples", type=int, default=200)
170
+ args = parser.parse_args()
171
+
172
+ sources, references = load_tamil_data(args.samples)
173
+
174
+ results = []
175
+
176
+ if args.model in ("nllb", "all"):
177
+ preds = translate_nllb(sources)
178
+ results.append(evaluate_predictions(preds, references, "NLLB-200 (600M)"))
179
+
180
+ if args.model in ("m2m", "all"):
181
+ preds = translate_m2m(sources)
182
+ results.append(evaluate_predictions(preds, references, "M2M100 (418M)"))
183
+
184
+ if args.model in ("t5", "all"):
185
+ preds = translate_t5(sources)
186
+ results.append(evaluate_predictions(preds, references, "T5-Base"))
187
+
188
+ # Summary table
189
+ print("\n" + "="*65)
190
+ print(f"{'Model':<22} {'BLEU':>8} {'chrF':>8} {'BERTScore':>10} {'CosSim':>8}")
191
+ print("-"*65)
192
+ for r in results:
193
+ print(f"{r['model']:<22} {r['BLEU']:>8.4f} {r['chrF']:>8.2f} {r['BERTScore_F1']:>10.4f} {r['EmbeddingSim']:>8.4f}")
194
+ print("="*65)
195
+
196
+ # Best model
197
+ best = max(results, key=lambda x: x["chrF"])
198
+ print(f"\n🏆 Best model: {best['model']} (chrF: {best['chrF']})")
199
+
200
+
201
+ if __name__ == "__main__":
202
+ main()
notebooks/evaluate_multilingual.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ evaluation/evaluate_multilingual.py
3
+ ─────────────────────────────────────────────────────────────────────────────
4
+ Evaluates NLLB-200 on English → 5 Indian languages using IndicMTEval.
5
+
6
+ Languages : Tamil · Hindi · Telugu · Kannada · Malayalam
7
+ Model : facebook/nllb-200-distilled-600M
8
+ Metrics : BLEU · chrF · BERTScore F1 · Cosine Similarity
9
+
10
+ Design rationale
11
+ ────────────────
12
+ Primary evaluation (model selection, paper results) remains English → Tamil
13
+ because IndicMTEval has the richest human quality scores (MQM / DA) for Tamil.
14
+ This script adds NLLB capability demonstration across all five languages so
15
+ the final report can show multilingual breadth alongside the Tamil deep-dive.
16
+
17
+ Usage
18
+ ─────
19
+ # Evaluate all languages (≈200 samples each, default)
20
+ python evaluate_multilingual.py
21
+
22
+ # Single language fast check
23
+ python evaluate_multilingual.py --lang Tamil --samples 50
24
+
25
+ # Save results to JSON
26
+ python evaluate_multilingual.py --output results/multilingual_eval.json
27
+ """
28
+
29
+ import re
30
+ import json
31
+ import argparse
32
+ import numpy as np
33
+ import evaluate
34
+ import sacrebleu
35
+ from datasets import load_dataset
36
+ from transformers import NllbTokenizer, AutoModelForSeq2SeqLM
37
+ from bert_score import score as bert_score_fn
38
+ from sentence_transformers import SentenceTransformer
39
+ from sklearn.metrics.pairwise import cosine_similarity
40
+ import torch
41
+
42
+ # ─────────────────────────────────────────────────────────────────────────────
43
+ # Language → NLLB token + IndicMTEval filter string + BERTScore lang code
44
+ # ─────────────────────────────────────────────────────────────────────────────
45
+ LANG_CONFIG = {
46
+ "Tamil": {"token": "tam_Taml", "filter": "Tamil", "bert_lang": "ta"},
47
+ "Hindi": {"token": "hin_Deva", "filter": "Hindi", "bert_lang": "hi"},
48
+ "Telugu": {"token": "tel_Telu", "filter": "Telugu", "bert_lang": "te"},
49
+ "Kannada": {"token": "kan_Knda", "filter": "Kannada", "bert_lang": "kn"},
50
+ "Malayalam": {"token": "mal_Mlym", "filter": "Malayalam", "bert_lang": "ml"},
51
+ }
52
+
53
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
54
+
55
+
56
+ # ─────────────────────────────────────────────────────────────────────────────
57
+ # Helpers
58
+ # ─────────────────────────────────────────────────────────────────────────────
59
+ def preprocess(text: str) -> str:
60
+ return re.sub(r"\s+", " ", text.lower()).strip()
61
+
62
+
63
+ def load_lang_data(language: str, num_samples: int):
64
+ """Load and filter IndicMTEval for a given language."""
65
+ cfg = LANG_CONFIG[language]
66
+ dataset = load_dataset("ai4bharat/IndicMTEval", split="test")
67
+ subset = dataset.filter(lambda x: x["language"] == cfg["filter"])
68
+ n = min(num_samples, len(subset))
69
+ subset = subset.select(range(n))
70
+ sources = [preprocess(t) for t in subset["src"]]
71
+ references = [preprocess(t) for t in subset["ref"]]
72
+ print(f" [{language}] {n} samples loaded.")
73
+ return sources, references
74
+
75
+
76
+ # ─────────────────────────────────────────────────────────────────────────────
77
+ # Translation (model + tokenizer passed in so they load only once)
78
+ # ─────────────────────────────────────────────────────────────────────────────
79
+ def translate_batch(sources, nllb_token, tokenizer, model, batch_size=8):
80
+ predictions = []
81
+ total = len(sources)
82
+ for i in range(0, total, batch_size):
83
+ batch = sources[i:i + batch_size]
84
+ inputs = tokenizer(
85
+ batch,
86
+ return_tensors="pt",
87
+ padding=True,
88
+ truncation=True,
89
+ max_length=512,
90
+ ).to(DEVICE)
91
+ with torch.no_grad():
92
+ out = model.generate(
93
+ **inputs,
94
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids(nllb_token),
95
+ num_beams=4,
96
+ max_length=256,
97
+ early_stopping=True,
98
+ )
99
+ predictions.extend(tokenizer.batch_decode(out, skip_special_tokens=True))
100
+ done = min(i + batch_size, total)
101
+ print(f" Translated {done}/{total}", end="\r")
102
+ print()
103
+ return [preprocess(p) for p in predictions]
104
+
105
+
106
+ # ─────────────────────────────────────────────────────────────────────────────
107
+ # Evaluation
108
+ # ─────────────────────────────────────────────────────────────────────────────
109
+ def compute_metrics(predictions, references, bert_lang: str):
110
+ # Drop empty pairs
111
+ pairs = [(p, r) for p, r in zip(predictions, references) if p.strip() and r.strip()]
112
+ if not pairs:
113
+ return {"bleu": 0, "chrf": 0, "bert_f1": 0, "cosine": 0, "n_valid": 0}
114
+
115
+ preds, refs = zip(*pairs)
116
+ preds, refs = list(preds), list(refs)
117
+
118
+ # BLEU
119
+ bleu_metric = evaluate.load("bleu")
120
+ bleu_val = bleu_metric.compute(
121
+ predictions=preds,
122
+ references=[[r] for r in refs],
123
+ )["bleu"]
124
+
125
+ # chrF
126
+ chrf_val = sacrebleu.corpus_chrf(preds, [refs]).score
127
+
128
+ # BERTScore
129
+ _, _, F1 = bert_score_fn(preds, refs, model_type="bert-base-multilingual-cased")
130
+ bert_f1 = F1.mean().item()
131
+
132
+ # Cosine similarity
133
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2")
134
+ ref_emb = embed_model.encode(refs)
135
+ pred_emb = embed_model.encode(preds)
136
+ cos_sim = cosine_similarity(ref_emb, pred_emb).diagonal().mean()
137
+
138
+ return {
139
+ "bleu": round(bleu_val, 4),
140
+ "chrf": round(chrf_val, 2),
141
+ "bert_f1": round(bert_f1, 4),
142
+ "cosine": round(float(cos_sim), 4),
143
+ "n_valid": len(preds),
144
+ }
145
+
146
+
147
+ # ─────────────────────────────────────────────────────────────────────────────
148
+ # Main
149
+ # ─────────────────────────────────────────────────────────────────────────────
150
+ def main():
151
+ parser = argparse.ArgumentParser(description="Multilingual MT Evaluation — NLLB-200")
152
+ parser.add_argument("--lang", default="all",
153
+ choices=list(LANG_CONFIG.keys()) + ["all"],
154
+ help="Language to evaluate (default: all)")
155
+ parser.add_argument("--samples", type=int, default=200,
156
+ help="Max samples per language (default: 200)")
157
+ parser.add_argument("--output", default=None,
158
+ help="Optional JSON file to save results")
159
+ args = parser.parse_args()
160
+
161
+ languages = list(LANG_CONFIG.keys()) if args.lang == "all" else [args.lang]
162
+
163
+ # Load model ONCE — reused across all languages
164
+ print(f"\nLoading NLLB model on {DEVICE}...")
165
+ tokenizer = NllbTokenizer.from_pretrained("facebook/nllb-200-distilled-600M")
166
+ model = AutoModelForSeq2SeqLM.from_pretrained(
167
+ "facebook/nllb-200-distilled-600M"
168
+ ).to(DEVICE)
169
+ model.eval()
170
+ print("Model ready ✓\n")
171
+
172
+ all_results = {}
173
+
174
+ for lang in languages:
175
+ cfg = LANG_CONFIG[lang]
176
+ print(f"{'─'*60}")
177
+ print(f" Language : {lang} ({cfg['token']})")
178
+ print(f"{'─'*60}")
179
+
180
+ sources, references = load_lang_data(lang, args.samples)
181
+
182
+ print(f" Translating {len(sources)} sentences…")
183
+ preds = translate_batch(sources, cfg["token"], tokenizer, model)
184
+
185
+ print(f" Computing metrics…")
186
+ metrics = compute_metrics(preds, references, cfg["bert_lang"])
187
+ metrics["language"] = lang
188
+ metrics["nllb_token"] = cfg["token"]
189
+
190
+ all_results[lang] = metrics
191
+ print(f" BLEU={metrics['bleu']:.4f} chrF={metrics['chrf']:.2f} "
192
+ f"BERT={metrics['bert_f1']:.4f} Cosine={metrics['cosine']:.4f} "
193
+ f"(n={metrics['n_valid']})\n")
194
+
195
+ # ── Summary table ─────────────────────────────────────────────────────────
196
+ print("\n" + "=" * 72)
197
+ print(f"{'Language':<14} {'NLLB Token':<14} {'BLEU':>8} {'chrF':>8} {'BERTScore':>10} {'CosSim':>8} {'N':>6}")
198
+ print("-" * 72)
199
+ for lang, r in all_results.items():
200
+ print(f"{lang:<14} {r['nllb_token']:<14} {r['bleu']:>8.4f} "
201
+ f"{r['chrf']:>8.2f} {r['bert_f1']:>10.4f} {r['cosine']:>8.4f} {r['n_valid']:>6}")
202
+ print("=" * 72)
203
+
204
+ best_lang = max(all_results, key=lambda k: all_results[k]["chrf"])
205
+ print(f"\n🏆 Best language result: {best_lang} (chrF: {all_results[best_lang]['chrf']})")
206
+ print(" (Tamil is the primary evaluation language for model comparison.)\n")
207
+
208
+ # ── Save JSON ─────────────────────────────────────────────────────────────
209
+ if args.output:
210
+ import os
211
+ os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
212
+ with open(args.output, "w", encoding="utf-8") as f:
213
+ json.dump(all_results, f, indent=2, ensure_ascii=False)
214
+ print(f"Results saved → {args.output}")
215
+
216
+
217
+ if __name__ == "__main__":
218
+ main()
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ transformers>=4.35.0
3
+ torch>=2.0.0
4
+ sentencepiece>=0.1.99
5
+ sacrebleu>=2.3.1
6
+ evaluate>=0.4.0
7
+ bert-score>=0.3.13
8
+ sentence-transformers>=2.2.2
9
+ scikit-learn>=1.3.0
10
+ numpy>=1.24.0
11
+ datasets>=2.14.0