Upload 10 files
Browse files- .gitattributes +36 -35
- .gitignore +45 -0
- README.md +361 -13
- app.py +334 -0
- docs/Evaluate Machine Translation using Automatic Metrics.pdf +3 -0
- docs/report.md +320 -0
- evaluation.ipynb +1 -0
- notebooks/evaluate_models.py +202 -0
- notebooks/evaluate_multilingual.py +218 -0
- requirements.txt +11 -0
.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:
|
| 5 |
-
colorTo:
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 6.
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 · 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
|