import os import pandas as pd import torch from sklearn.model_selection import train_test_split from transformers import DistilBertTokenizerFast, DistilBertForSequenceClassification, Trainer, TrainingArguments from datasets import Dataset import numpy as np from sklearn.metrics import accuracy_score, precision_recall_fscore_support, confusion_matrix from sklearn.utils.class_weight import compute_class_weight # Load dataset df = pd.read_csv("D:\Sentinel\data\sentinel_dataset_expanded.csv") # Basic cleaning df = df.dropna() df["text"] = df["text"].astype(str) print(f"Dataset size: {len(df)}") print(f"Label distribution: {df['label'].value_counts().to_dict()}") print(f"\nAverage text length: {df['text'].str.len().mean():.1f} characters") # Train-test split train_texts, val_texts, train_labels, val_labels = train_test_split( df["text"].tolist(), df["label"].tolist(), test_size=0.2, random_state=42, stratify=df["label"].tolist() # Ensure balanced split ) # Compute class weights for balanced training class_weights = compute_class_weight( class_weight='balanced', classes=np.unique(train_labels), y=train_labels ) class_weights = torch.tensor(class_weights, dtype=torch.float) print(f"\nClass weights: SAFE={class_weights[0]:.4f}, SCAM={class_weights[1]:.4f}") print(f"Training set - SAFE: {train_labels.count(0)}, SCAM: {train_labels.count(1)}") # Tokenizer tokenizer = DistilBertTokenizerFast.from_pretrained("distilbert-base-uncased") train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=128) val_encodings = tokenizer(val_texts, truncation=True, padding=True, max_length=128) train_dataset = Dataset.from_dict({ "input_ids": train_encodings["input_ids"], "attention_mask": train_encodings["attention_mask"], "labels": train_labels }) val_dataset = Dataset.from_dict({ "input_ids": val_encodings["input_ids"], "attention_mask": val_encodings["attention_mask"], "labels": val_labels }) # Model with class weights model = DistilBertForSequenceClassification.from_pretrained( "distilbert-base-uncased", num_labels=2 ) # Custom trainer class to use class weights class WeightedTrainer(Trainer): def compute_loss(self, model, inputs, return_outputs=False, **kwargs): labels = inputs.pop("labels") outputs = model(**inputs) logits = outputs.logits loss_fct = torch.nn.CrossEntropyLoss(weight=class_weights.to(logits.device)) loss = loss_fct(logits.view(-1, self.model.config.num_labels), labels.view(-1)) return (loss, outputs) if return_outputs else loss # Metrics def compute_metrics(pred): labels = pred.label_ids preds = np.argmax(pred.predictions, axis=1) precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='binary') acc = accuracy_score(labels, preds) # Confusion matrix cm = confusion_matrix(labels, preds) print(f"\nConfusion Matrix:") print(f" Predicted SAFE Predicted SCAM") print(f"Actual SAFE: {cm[0][0]:14d} {cm[0][1]:14d}") print(f"Actual SCAM: {cm[1][0]:14d} {cm[1][1]:14d}") return { "accuracy": acc, "f1": f1, "precision": precision, "recall": recall } # Training args - MORE aggressive parameters training_args = TrainingArguments( output_dir="./results", num_train_epochs=10, # Increased to 10 epochs per_device_train_batch_size=16, per_device_eval_batch_size=16, warmup_steps=50, # Reduced warmup weight_decay=0.01, learning_rate=3e-5, # Slightly higher learning rate logging_dir="./logs", logging_steps=5, eval_strategy="epoch", save_strategy="epoch", load_best_model_at_end=True, metric_for_best_model="f1", save_total_limit=2 ) trainer = WeightedTrainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset, compute_metrics=compute_metrics ) print("\n" + "="*60) print("Starting training with 10 epochs...") print("="*60) trainer.train() print("\n" + "="*60) print("Final Evaluation...") print("="*60) eval_results = trainer.evaluate() print(f"\nFinal Evaluation Results:") print(f" Accuracy: {eval_results['eval_accuracy']:.4f}") print(f" F1 Score: {eval_results['eval_f1']:.4f}") print(f" Precision: {eval_results['eval_precision']:.4f}") print(f" Recall: {eval_results['eval_recall']:.4f}") script_dir = os.path.dirname(os.path.abspath(__file__)) save_path = os.path.join(script_dir, "sentinel_model") model.save_pretrained(save_path) tokenizer.save_pretrained(save_path) print(f"\n{'='*60}") print(f"Model training complete and saved to: {save_path}") print("="*60)