HarshithaaaaReddddyyy commited on
Commit
b945c6a
·
verified ·
1 Parent(s): 93d29b8

Delete transformer_model.py

Browse files
Files changed (1) hide show
  1. transformer_model.py +0 -303
transformer_model.py DELETED
@@ -1,303 +0,0 @@
1
- """
2
- Transformer-based Language Identification Model
3
- Uses Google MuRIL (Multilingual Universal Representations for Indian Languages)
4
- or XLM-RoBERTa for high-accuracy sequence classification.
5
-
6
- Requires GPU for practical training. Set enabled: true in config.
7
- """
8
- from __future__ import annotations
9
-
10
- import os
11
- from pathlib import Path
12
- from typing import Dict, List, Optional, Tuple
13
-
14
- import numpy as np
15
- import pandas as pd
16
- from loguru import logger
17
-
18
- try:
19
- import torch
20
- from torch.utils.data import Dataset, DataLoader
21
- from transformers import (
22
- AutoTokenizer,
23
- AutoModelForSequenceClassification,
24
- TrainingArguments,
25
- Trainer,
26
- EarlyStoppingCallback,
27
- )
28
- from transformers import DataCollatorWithPadding
29
- TRANSFORMERS_AVAILABLE = True
30
- except ImportError:
31
- TRANSFORMERS_AVAILABLE = False
32
- logger.warning("transformers/torch not installed. Transformer model unavailable.")
33
-
34
- from src.utils.languages import (
35
- LANGUAGE_REGISTRY,
36
- CODE_TO_LABEL,
37
- LABEL_TO_CODE,
38
- NUM_CLASSES,
39
- ALL_LANGUAGE_CODES,
40
- )
41
-
42
-
43
- # ─────────────────────────────────────────────
44
- # Dataset Wrapper
45
- # ─────────────────────────────────────────────
46
-
47
- class IndicLIDDataset:
48
- """Wraps a pandas DataFrame for HuggingFace Trainer."""
49
-
50
- def __init__(self, df: pd.DataFrame, tokenizer, max_length: int = 128):
51
- self.texts = df["text"].tolist()
52
- self.labels = df["label"].tolist()
53
- self.tokenizer = tokenizer
54
- self.max_length = max_length
55
-
56
- def __len__(self):
57
- return len(self.texts)
58
-
59
- def __getitem__(self, idx):
60
- encoding = self.tokenizer(
61
- self.texts[idx],
62
- max_length=self.max_length,
63
- truncation=True,
64
- padding="max_length",
65
- return_tensors="pt",
66
- )
67
- return {
68
- "input_ids": encoding["input_ids"].squeeze(),
69
- "attention_mask": encoding["attention_mask"].squeeze(),
70
- "labels": torch.tensor(self.labels[idx], dtype=torch.long),
71
- }
72
-
73
-
74
- # ─────────────────────────────────────────────
75
- # Transformer LID Model
76
- # ─────────────────────────────────────────────
77
-
78
- class TransformerLIDModel:
79
- """
80
- Fine-tuned transformer for language identification.
81
- Defaults to MuRIL (designed for Indic languages).
82
-
83
- MuRIL advantages over XLM-R for this task:
84
- - Pretrained on 17 Indian languages + transliterated text
85
- - Better subword coverage for Indic scripts
86
- - Transliteration-aware pretraining
87
- """
88
-
89
- SUPPORTED_MODELS = {
90
- "muril": "google/muril-base-cased",
91
- "xlmr": "xlm-roberta-base",
92
- "xlmr-large": "xlm-roberta-large",
93
- "indicbert": "ai4bharat/indic-bert",
94
- }
95
-
96
- def __init__(
97
- self,
98
- base_model: str = "muril",
99
- max_length: int = 128,
100
- batch_size: int = 32,
101
- learning_rate: float = 2e-5,
102
- num_epochs: int = 3,
103
- warmup_ratio: float = 0.1,
104
- weight_decay: float = 0.01,
105
- output_dir: str = "./artifacts/models/transformer",
106
- fp16: bool = True,
107
- ):
108
- assert TRANSFORMERS_AVAILABLE, "transformers + torch required"
109
- self.model_name = self.SUPPORTED_MODELS.get(base_model, base_model)
110
- self.max_length = max_length
111
- self.batch_size = batch_size
112
- self.learning_rate = learning_rate
113
- self.num_epochs = num_epochs
114
- self.warmup_ratio = warmup_ratio
115
- self.weight_decay = weight_decay
116
- self.output_dir = Path(output_dir)
117
- self.output_dir.mkdir(parents=True, exist_ok=True)
118
- self.fp16 = fp16 and torch.cuda.is_available()
119
-
120
- self.tokenizer = None
121
- self.model = None
122
- self.trainer = None
123
- self.is_fitted = False
124
-
125
- # Label mapping
126
- self.id2label = {i: l for i, l in LABEL_TO_CODE.items()}
127
- self.label2id = {l: i for i, l in self.id2label.items()}
128
-
129
- def _load_base_model(self):
130
- logger.info(f"Loading tokenizer and model: {self.model_name}")
131
- self.tokenizer = AutoTokenizer.from_pretrained(
132
- self.model_name,
133
- use_fast=True,
134
- )
135
- self.model = AutoModelForSequenceClassification.from_pretrained(
136
- self.model_name,
137
- num_labels=NUM_CLASSES,
138
- id2label=self.id2label,
139
- label2id=self.label2id,
140
- ignore_mismatched_sizes=True,
141
- )
142
- device = "cuda" if torch.cuda.is_available() else "cpu"
143
- logger.info(f"Using device: {device}")
144
- self.model.to(device)
145
-
146
- def fit(
147
- self,
148
- train_df: pd.DataFrame,
149
- val_df: Optional[pd.DataFrame] = None,
150
- ) -> "TransformerLIDModel":
151
- self._load_base_model()
152
-
153
- train_dataset = IndicLIDDataset(train_df, self.tokenizer, self.max_length)
154
- val_dataset = IndicLIDDataset(val_df, self.tokenizer, self.max_length) if val_df is not None else None
155
-
156
- training_args = TrainingArguments(
157
- output_dir=str(self.output_dir),
158
- num_train_epochs=self.num_epochs,
159
- per_device_train_batch_size=self.batch_size,
160
- per_device_eval_batch_size=self.batch_size * 2,
161
- learning_rate=self.learning_rate,
162
- warmup_ratio=self.warmup_ratio,
163
- weight_decay=self.weight_decay,
164
- evaluation_strategy="epoch" if val_dataset else "no",
165
- save_strategy="epoch",
166
- load_best_model_at_end=val_dataset is not None,
167
- metric_for_best_model="eval_loss",
168
- fp16=self.fp16,
169
- logging_steps=100,
170
- logging_dir=str(self.output_dir / "logs"),
171
- dataloader_num_workers=4,
172
- seed=42,
173
- report_to="none", # Disable wandb/mlflow unless configured
174
- )
175
-
176
- self.trainer = Trainer(
177
- model=self.model,
178
- args=training_args,
179
- train_dataset=train_dataset,
180
- eval_dataset=val_dataset,
181
- tokenizer=self.tokenizer,
182
- callbacks=[EarlyStoppingCallback(early_stopping_patience=2)] if val_dataset else [],
183
- compute_metrics=self._compute_metrics,
184
- )
185
-
186
- logger.info("Starting transformer training ...")
187
- self.trainer.train()
188
- self.is_fitted = True
189
- logger.success("Training complete!")
190
- return self
191
-
192
- def _compute_metrics(self, eval_pred):
193
- from sklearn.metrics import f1_score, accuracy_score
194
- logits, labels = eval_pred
195
- predictions = np.argmax(logits, axis=-1)
196
- acc = accuracy_score(labels, predictions)
197
- f1 = f1_score(labels, predictions, average="macro", zero_division=0)
198
- return {"accuracy": acc, "macro_f1": f1}
199
-
200
- def predict(self, texts: List[str]) -> List[str]:
201
- assert self.is_fitted
202
- probs, _ = self.predict_proba(texts)
203
- label_ids = np.argmax(probs, axis=1)
204
- return [self.id2label[i] for i in label_ids]
205
-
206
- def predict_proba(self, texts: List[str]) -> Tuple[np.ndarray, List[str]]:
207
- assert self.is_fitted
208
- import torch
209
- from torch.nn.functional import softmax
210
-
211
- device = next(self.model.parameters()).device
212
- all_probs = []
213
-
214
- for i in range(0, len(texts), self.batch_size):
215
- batch_texts = texts[i:i + self.batch_size]
216
- encodings = self.tokenizer(
217
- batch_texts,
218
- max_length=self.max_length,
219
- truncation=True,
220
- padding=True,
221
- return_tensors="pt",
222
- ).to(device)
223
-
224
- with torch.no_grad():
225
- outputs = self.model(**encodings)
226
- probs = softmax(outputs.logits, dim=-1).cpu().numpy()
227
- all_probs.append(probs)
228
-
229
- prob_matrix = np.vstack(all_probs)
230
- class_names = [self.id2label[i] for i in range(NUM_CLASSES)]
231
- return prob_matrix, class_names
232
-
233
- def predict_with_confidence(self, texts: List[str]) -> List[Dict]:
234
- probs, class_names = self.predict_proba(texts)
235
- results = []
236
- for i, prob_row in enumerate(probs):
237
- top_k_idx = np.argsort(prob_row)[::-1][:5]
238
- top_k = [
239
- {
240
- "lang": class_names[j],
241
- "name": LANGUAGE_REGISTRY.get(class_names[j], None) and LANGUAGE_REGISTRY[class_names[j]].name,
242
- "probability": float(prob_row[j]),
243
- }
244
- for j in top_k_idx
245
- ]
246
- best = top_k[0]
247
- info = LANGUAGE_REGISTRY.get(best["lang"])
248
- results.append({
249
- "text_preview": texts[i][:80],
250
- "predicted_lang": best["lang"],
251
- "predicted_lang_name": info.name if info else best["lang"],
252
- "script": info.script if info else "?",
253
- "direction": info.direction if info else "LTR",
254
- "confidence": best["probability"],
255
- "is_confident": best["probability"] >= 0.75,
256
- "top_5": top_k,
257
- })
258
- return results
259
-
260
- def evaluate(self, test_df: pd.DataFrame) -> Dict:
261
- from sklearn.metrics import classification_report, f1_score, accuracy_score
262
- texts = test_df["text"].tolist()
263
- true_labels = test_df["lang"].tolist()
264
- pred_labels = self.predict(texts)
265
- probs, _ = self.predict_proba(texts)
266
-
267
- accuracy = accuracy_score(true_labels, pred_labels)
268
- macro_f1 = f1_score(true_labels, pred_labels, average="macro", zero_division=0)
269
- report = classification_report(true_labels, pred_labels, output_dict=True, zero_division=0)
270
-
271
- logger.info(f"\n{classification_report(true_labels, pred_labels, zero_division=0)}")
272
- logger.success(f"Accuracy: {accuracy:.4f} | Macro-F1: {macro_f1:.4f}")
273
-
274
- return {
275
- "accuracy": accuracy,
276
- "macro_f1": macro_f1,
277
- "per_language": report,
278
- "predictions": pred_labels,
279
- "true_labels": true_labels,
280
- "confidences": probs.max(axis=1).tolist(),
281
- }
282
-
283
- def save(self, path: str):
284
- self.model.save_pretrained(path)
285
- self.tokenizer.save_pretrained(path)
286
- logger.success(f"Transformer model saved: {path}")
287
-
288
- @classmethod
289
- def load(cls, path: str) -> "TransformerLIDModel":
290
- assert TRANSFORMERS_AVAILABLE
291
- obj = cls.__new__(cls)
292
- obj.output_dir = Path(path)
293
- obj.tokenizer = AutoTokenizer.from_pretrained(path)
294
- obj.model = AutoModelForSequenceClassification.from_pretrained(path)
295
- obj.is_fitted = True
296
- obj.id2label = {i: l for i, l in LABEL_TO_CODE.items()}
297
- obj.batch_size = 32
298
- obj.max_length = 128
299
- obj.fp16 = False
300
- device = "cuda" if torch.cuda.is_available() else "cpu"
301
- obj.model.to(device)
302
- logger.success(f"Transformer model loaded: {path}")
303
- return obj