HarshithaaaaReddddyyy commited on
Commit
c7d89c8
·
verified ·
1 Parent(s): 22d7161

Delete fasttext_model.py

Browse files
Files changed (1) hide show
  1. fasttext_model.py +0 -257
fasttext_model.py DELETED
@@ -1,257 +0,0 @@
1
- """
2
- FastText-based Language Identification
3
- Uses Facebook's fastText for ultra-fast, high-accuracy language detection.
4
- Two modes:
5
- 1. Pretrained lid.176.ftz (off-the-shelf, 176 languages)
6
- 2. Custom-trained on Pralekha (domain-specific, higher accuracy on Indic)
7
- """
8
- from __future__ import annotations
9
-
10
- import os
11
- import re
12
- import tempfile
13
- import urllib.request
14
- from pathlib import Path
15
- from typing import Dict, List, Optional, Tuple
16
-
17
- import numpy as np
18
- import pandas as pd
19
- from loguru import logger
20
- from tqdm.auto import tqdm
21
-
22
- try:
23
- import fasttext
24
- FASTTEXT_AVAILABLE = True
25
- except ImportError:
26
- FASTTEXT_AVAILABLE = False
27
- logger.warning("fasttext not installed. `pip install fasttext-wheel`")
28
-
29
- from src.utils.languages import LANGUAGE_REGISTRY, CODE_TO_LABEL
30
-
31
-
32
- PRETRAINED_MODEL_URL = "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz"
33
- LANG_PREFIX = "__label__"
34
-
35
- # Pralekha lang codes → fastText ISO codes (for pretrained model)
36
- PRALEKHA_TO_FT = {
37
- "ben": "bn", "eng": "en", "guj": "gu", "hin": "hi",
38
- "kan": "kn", "mal": "ml", "mar": "mr", "ori": "or",
39
- "pan": "pa", "tam": "ta", "tel": "te", "urd": "ur",
40
- }
41
- FT_TO_PRALEKHA = {v: k for k, v in PRALEKHA_TO_FT.items()}
42
-
43
-
44
- class FastTextLIDModel:
45
- """
46
- FastText-based Language Identification with two modes:
47
- - mode='pretrained': Use Facebook's lid.176.ftz (quick, general)
48
- - mode='custom' : Train from scratch on Pralekha (best Indic accuracy)
49
- """
50
-
51
- def __init__(
52
- self,
53
- mode: str = "pretrained", # "pretrained" | "custom"
54
- model_dir: str = "./artifacts/fasttext",
55
- dim: int = 100,
56
- epoch: int = 25,
57
- lr: float = 0.1,
58
- word_ngrams: int = 3,
59
- min_count: int = 5,
60
- thread: int = 8,
61
- ):
62
- assert mode in ("pretrained", "custom")
63
- self.mode = mode
64
- self.model_dir = Path(model_dir)
65
- self.model_dir.mkdir(parents=True, exist_ok=True)
66
- self.dim = dim
67
- self.epoch = epoch
68
- self.lr = lr
69
- self.word_ngrams = word_ngrams
70
- self.min_count = min_count
71
- self.thread = thread
72
- self.model = None
73
- self.is_fitted = False
74
- self._target_langs = list(PRALEKHA_TO_FT.keys())
75
-
76
- # ── Pretrained Mode ───────────────────────────────────────────────────────
77
-
78
- def load_pretrained(self) -> "FastTextLIDModel":
79
- """Download and load Facebook's lid.176.ftz pretrained model."""
80
- assert FASTTEXT_AVAILABLE, "fasttext not installed"
81
- model_path = self.model_dir / "lid.176.ftz"
82
-
83
- if not model_path.exists():
84
- logger.info(f"Downloading pretrained FastText LID model ...")
85
- urllib.request.urlretrieve(
86
- PRETRAINED_MODEL_URL,
87
- str(model_path),
88
- reporthook=self._download_progress,
89
- )
90
- logger.success("Download complete.")
91
-
92
- self.model = fasttext.load_model(str(model_path))
93
- self.is_fitted = True
94
- self.mode = "pretrained"
95
- logger.success("Pretrained FastText model loaded.")
96
- return self
97
-
98
- def _download_progress(self, block_num, block_size, total_size):
99
- downloaded = block_num * block_size
100
- pct = min(100, downloaded * 100 / total_size)
101
- if block_num % 50 == 0:
102
- logger.debug(f"Download: {pct:.1f}%")
103
-
104
- # ── Custom Training Mode ──────────────────────────────────────────────────
105
-
106
- def fit(
107
- self,
108
- train_df: pd.DataFrame,
109
- val_df: Optional[pd.DataFrame] = None,
110
- ) -> "FastTextLIDModel":
111
- """Train custom FastText model on Pralekha data."""
112
- assert FASTTEXT_AVAILABLE, "fasttext not installed"
113
-
114
- logger.info(f"Preparing FastText training data ({len(train_df):,} samples) ...")
115
- train_file = self.model_dir / "train.txt"
116
- self._write_fasttext_format(train_df, train_file)
117
-
118
- logger.info(f"Training FastText | dim={self.dim} | epoch={self.epoch} | lr={self.lr}")
119
- self.model = fasttext.train_supervised(
120
- input=str(train_file),
121
- dim=self.dim,
122
- epoch=self.epoch,
123
- lr=self.lr,
124
- wordNgrams=self.word_ngrams,
125
- minCount=self.min_count,
126
- thread=self.thread,
127
- verbose=2,
128
- loss="softmax",
129
- )
130
-
131
- model_path = self.model_dir / "custom_lid.bin"
132
- self.model.save_model(str(model_path))
133
- self.is_fitted = True
134
- self.mode = "custom"
135
-
136
- if val_df is not None:
137
- results = self.evaluate(val_df)
138
- logger.success(f"Val Accuracy: {results['accuracy']:.4f} | F1: {results['macro_f1']:.4f}")
139
-
140
- return self
141
-
142
- def _write_fasttext_format(self, df: pd.DataFrame, path: Path):
143
- """Write data in fastText supervised format: __label__LANG text"""
144
- with open(path, "w", encoding="utf-8") as f:
145
- for _, row in tqdm(df.iterrows(), total=len(df), desc="Writing FT data"):
146
- text = self._clean_for_fasttext(row["text"])
147
- lang = row["lang"]
148
- label = f"{LANG_PREFIX}{lang}"
149
- f.write(f"{label} {text}\n")
150
-
151
- @staticmethod
152
- def _clean_for_fasttext(text: str) -> str:
153
- """Minimal cleaning for fastText (preserve Unicode)."""
154
- text = re.sub(r"\s+", " ", text).strip()
155
- text = re.sub(r"[\r\n\t]", " ", text)
156
- return text
157
-
158
- # ── Inference ─────────────────────────────────────────────────────────────
159
-
160
- def predict(self, texts: List[str]) -> List[str]:
161
- """Predict language codes."""
162
- assert self.is_fitted
163
- results = []
164
- for text in texts:
165
- pred = self._predict_single(text)
166
- results.append(pred)
167
- return results
168
-
169
- def _predict_single(self, text: str) -> str:
170
- text = self._clean_for_fasttext(text)
171
- labels, probs = self.model.predict(text, k=1)
172
- raw_label = labels[0].replace(LANG_PREFIX, "")
173
-
174
- if self.mode == "pretrained":
175
- return FT_TO_PRALEKHA.get(raw_label, raw_label)
176
- return raw_label
177
-
178
- def predict_proba(self, texts: List[str]) -> Tuple[np.ndarray, List[str]]:
179
- """
180
- Returns (prob_matrix [n × n_classes], class_names).
181
- """
182
- assert self.is_fitted
183
- all_labels = self._target_langs
184
- n = len(texts)
185
- n_classes = len(all_labels)
186
- prob_matrix = np.zeros((n, n_classes))
187
-
188
- for i, text in enumerate(texts):
189
- text_clean = self._clean_for_fasttext(text)
190
- labels, probs = self.model.predict(text_clean, k=-1)
191
- for label, prob in zip(labels, probs):
192
- raw = label.replace(LANG_PREFIX, "")
193
- mapped = FT_TO_PRALEKHA.get(raw, raw) if self.mode == "pretrained" else raw
194
- if mapped in all_labels:
195
- j = all_labels.index(mapped)
196
- prob_matrix[i, j] = prob
197
-
198
- # Normalize
199
- row_sums = prob_matrix.sum(axis=1, keepdims=True)
200
- prob_matrix = np.where(row_sums > 0, prob_matrix / row_sums, prob_matrix)
201
-
202
- return prob_matrix, all_labels
203
-
204
- def predict_with_confidence(self, texts: List[str]) -> List[Dict]:
205
- """Rich predictions with top-K confidences."""
206
- results = []
207
- for text in texts:
208
- text_clean = self._clean_for_fasttext(text)
209
- labels, probs = self.model.predict(text_clean, k=5)
210
- top_k = []
211
- for label, prob in zip(labels, probs):
212
- raw = label.replace(LANG_PREFIX, "")
213
- mapped = FT_TO_PRALEKHA.get(raw, raw) if self.mode == "pretrained" else raw
214
- info = LANGUAGE_REGISTRY.get(mapped)
215
- top_k.append({
216
- "lang": mapped,
217
- "name": info.name if info else mapped,
218
- "probability": float(prob),
219
- })
220
-
221
- results.append({
222
- "text": text[:100],
223
- "predicted_lang": top_k[0]["lang"],
224
- "confidence": top_k[0]["probability"],
225
- "is_confident": top_k[0]["probability"] >= 0.7,
226
- "top_5": top_k,
227
- })
228
- return results
229
-
230
- def evaluate(self, test_df: pd.DataFrame) -> Dict:
231
- """Full evaluation."""
232
- from sklearn.metrics import classification_report, f1_score, accuracy_score
233
-
234
- texts = test_df["text"].tolist()
235
- true_labels = test_df["lang"].tolist()
236
- pred_labels = self.predict(texts)
237
-
238
- accuracy = accuracy_score(true_labels, pred_labels)
239
- macro_f1 = f1_score(true_labels, pred_labels, average="macro", zero_division=0)
240
- report = classification_report(true_labels, pred_labels, output_dict=True, zero_division=0)
241
-
242
- logger.info(f"\n{classification_report(true_labels, pred_labels, zero_division=0)}")
243
- logger.success(f"Accuracy: {accuracy:.4f} | Macro-F1: {macro_f1:.4f}")
244
-
245
- return {
246
- "accuracy": accuracy,
247
- "macro_f1": macro_f1,
248
- "per_language": report,
249
- "predictions": pred_labels,
250
- "true_labels": true_labels,
251
- }
252
-
253
- def load(self, path: str) -> "FastTextLIDModel":
254
- assert FASTTEXT_AVAILABLE
255
- self.model = fasttext.load_model(path)
256
- self.is_fitted = True
257
- return self