ScamShield-nlp / train_model.py
swara242's picture
Upload 8 files
16816fc verified
Raw
History Blame Contribute Delete
7.71 kB
"""ScamShield NLP — Model training script.
This script retrains the calibrated Linear SVM model and TF-IDF vectorizer
using scikit-learn 1.4, compatible with Python 3.10. It uses publicly available
spam datasets to replicate the original model's performance.
Usage:
python train_model.py
"""
import os
import joblib
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
# Import preprocessing function to ensure consistency
from preprocessing import clean_text
# Create models directory if it doesn't exist
MODELS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models")
os.makedirs(MODELS_DIR, exist_ok=True)
def load_and_prepare_datasets():
"""Load and combine publicly available spam datasets.
Downloads and processes the SMS Spam Collection and other public datasets
to create a training corpus similar to the original ScamShield dataset.
"""
print("Loading datasets...")
# First, load the UCI SMS Spam Collection (most widely used spam dataset)
# We'll download it and process it
try:
# Try to fetch from UCI repository using ucimlrepo
from ucimlrepo import fetch_ucirepo
sms_spam = fetch_ucirepo(id=228)
X = sms_spam.data.features
y = sms_spam.data.targets
# Convert to DataFrame
df = pd.DataFrame({
'text': X['v2'],
'label': y['v1'].map({'ham': 0, 'spam': 1})
})
print(f"Loaded UCI SMS Spam Collection: {len(df)} messages")
except Exception as e:
print(f"Could not load from ucimlrepo, falling back to downloading directly: {e}")
# Alternative: download the dataset directly
import requests
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip"
import zipfile
import io
r = requests.get(url)
z = zipfile.ZipFile(io.BytesIO(r.content))
with z.open('SMSSpamCollection') as f:
lines = f.readlines()
data = []
for line in lines:
line = line.decode('utf-8').strip()
if line:
label, text = line.split('\t', 1)
data.append({'text': text, 'label': 1 if label == 'spam' else 0})
df = pd.DataFrame(data)
print(f"Loaded UCI SMS Spam Collection (direct download): {len(df)} messages")
# Let's also add some additional phishing data to enrich the dataset
# Try to load the phishing dataset from Hugging Face if available, else continue with what we have
try:
from datasets import load_dataset
hf_dataset = load_dataset("ealvaradob/phishing-dataset", "combined", split="train")
hf_df = pd.DataFrame(hf_dataset)
# Only keep text and label columns, filter where label is 0 or 1
hf_df = hf_df[hf_df['label'].isin([0, 1])][['text', 'label']]
# Combine with our existing dataframe
df = pd.concat([df, hf_df], ignore_index=True)
print(f"Added Hugging Face phishing dataset: {len(hf_df)} messages, total now: {len(df)}")
except Exception as e:
print(f"Could not load Hugging Face dataset, continuing with SMS dataset only: {e}")
# Remove duplicates
df = df.drop_duplicates(subset=['text']).reset_index(drop=True)
print(f"After removing duplicates: {len(df)} messages")
# Apply the same preprocessing used in inference
print("Applying text preprocessing...")
df['cleaned_text'] = df['text'].apply(clean_text)
# Remove any empty texts after preprocessing
df = df[df['cleaned_text'].str.strip() != ''].reset_index(drop=True)
print(f"Final dataset size after cleaning: {len(df)} messages")
print(f"Legitimate messages (0): {len(df[df['label'] == 0])}, Scam messages (1): {len(df[df['label'] == 1])}")
return df
def train_and_evaluate_model(df):
"""Train the TF-IDF vectorizer and calibrated LinearSVC model, then evaluate."""
print("\nStarting model training...")
# Split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
df['cleaned_text'],
df['label'],
test_size=0.2,
random_state=42,
stratify=df['label']
)
print(f"Train set size: {len(X_train)}, Test set size: {len(X_test)}")
# Initialize and fit TF-IDF Vectorizer with the same parameters as original
print("\nFitting TF-IDF vectorizer...")
vectorizer = TfidfVectorizer(
ngram_range=(1, 2),
max_features=10000,
min_df=2,
sublinear_tf=True,
lowercase=True
)
X_train_tfidf = vectorizer.fit_transform(X_train)
X_test_tfidf = vectorizer.transform(X_test)
print(f"TF-IDF vectorizer fitted. Vocabulary size: {len(vectorizer.vocabulary_)}")
# Train LinearSVC and calibrate with CalibratedClassifierCV (same as original)
print("\nTraining LinearSVC with calibration...")
base_svm = LinearSVC(C=1.0, random_state=42, max_iter=10000)
calibrated_svm = CalibratedClassifierCV(base_svm, cv=5)
calibrated_svm.fit(X_train_tfidf, y_train)
print("Model training complete.")
# Evaluate on test set
print("\nEvaluating model on test set...")
y_pred = calibrated_svm.predict(X_test_tfidf)
y_pred_proba = calibrated_svm.predict_proba(X_test_tfidf)[:, 1]
# Calculate metrics
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred_proba)
print(f"Model Performance Metrics:")
print(f" Accuracy: {accuracy:.4f} ({accuracy*100:.2f}%)")
print(f" Precision: {precision:.4f} ({precision*100:.2f}%)")
print(f" Recall: {recall:.4f} ({recall*100:.2f}%)")
print(f" F1-score: {f1:.4f} ({f1*100:.2f}%)")
print(f" ROC-AUC: {roc_auc:.4f} ({roc_auc*100:.2f}%)")
# Calculate number of missed scams (false negatives)
fn_count = np.sum((y_test == 1) & (y_pred == 0))
print(f" Missed scams (FN): {fn_count} / {len(y_test[y_test == 1])} ({fn_count/len(y_test[y_test == 1])*100:.2f}%)")
return vectorizer, calibrated_svm
def save_models(vectorizer, model):
"""Save the trained vectorizer and model to the models directory."""
print("\nSaving trained models...")
vectorizer_path = os.path.join(MODELS_DIR, "tfidf_vectorizer.joblib")
model_path = os.path.join(MODELS_DIR, "svm.joblib")
# Save with joblib
joblib.dump(vectorizer, vectorizer_path)
joblib.dump(model, model_path)
print(f"Vectorizer saved to: {vectorizer_path}")
print(f"Model saved to: {model_path}")
print("Models saved successfully!")
def main():
"""Main training workflow."""
print("="*60)
print("ScamShield NLP - Model Retraining Script")
print("Using Python 3.10+ and scikit-learn 1.4")
print("="*60)
# Load and prepare datasets
df = load_and_prepare_datasets()
# Train and evaluate the model
vectorizer, model = train_and_evaluate_model(df)
# Save the models
save_models(vectorizer, model)
print("\n" + "="*60)
print("Training process completed successfully!")
print("The new models are now ready for use with the Gradio app.")
print("="*60)
if __name__ == "__main__":
main()