{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# NLP Text Classification Assignment\n",
    "\n",
    "---\n",
    "\n",
    "**Student:** Mohammad Wajeeh Bader\n",
    "\n",
    "**ID:** 251000462\n",
    "\n",
    "**Course:** Natural Language Processing\n",
    "\n",
    "**Date:** 21/12/2025\n",
    "\n",
    "---\n",
    "\n",
    "## Objectives\n",
    "\n",
    "1. Download NLP dataset for classification\n",
    "2. Apply data preprocessing\n",
    "3. Split the dataset\n",
    "4. Apply feature representation methods\n",
    "5. Train and evaluate models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 1: Dataset Information\n",
    "\n",
    "| Attribute | Details |\n",
    "|-----------|----------|\n",
    "| **Name** | SMS Spam Collection |\n",
    "| **Source** | UCI Machine Learning Repository |\n",
    "| **URL** | https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection |\n",
    "| **Download** | https://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip |\n",
    "| **Samples** | 5,574 SMS messages |\n",
    "| **Classes** | 2 (ham, spam) |\n",
    "| **Distribution** | 4,827 ham (86.6%), 747 spam (13.4%) |\n",
    "| **Format** | Tab-separated (TSV) |\n",
    "\n",
    "**Reference:**\n",
    "> Almeida, T.A., Gomez Hidalgo, J.M., Yamakami, A. (2011). Contributions to the Study of SMS Spam Filtering: New Collection and Results. Proceedings of the 2011 ACM Symposium on Document Engineering (DOCENG'11)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 2: Import Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Libraries imported.\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import re\n",
    "import string\n",
    "\n",
    "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.naive_bayes import MultinomialNB\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report, confusion_matrix\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "print(\"Libraries imported.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 3: Load Dataset\n",
    "\n",
    "| Attribute | Details |\n",
    "|-----------|----------|\n",
    "| **Name** | SMS Spam Collection |\n",
    "| **Source** | UCI Machine Learning Repository |\n",
    "| **URL** | https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection |\n",
    "| **Download** | https://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip |\n",
    "| **Samples** | 5,574 SMS messages |\n",
    "| **Classes** | 2 (ham, spam) |\n",
    "| **Distribution** | 4,827 ham (86.6%), 747 spam (13.4%) |\n",
    "\n",
    "**Reference:**\n",
    "> Almeida, T.A., Gomez Hidalgo, J.M., Yamakami, A. (2011). Contributions to the Study of SMS Spam Filtering. DOCENG'11."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape: (5572, 2)\n",
      "Columns: ['label', 'text']\n"
     ]
    }
   ],
   "source": [
    "DATASET_PATH = \"SMSSpamCollection\"\n",
    "\n",
    "df = pd.read_csv(DATASET_PATH, sep='\\t', names=['label', 'text'], encoding='utf-8')\n",
    "\n",
    "print(f\"Shape: {df.shape}\")\n",
    "print(f\"Columns: {list(df.columns)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 Exploratory Data Analysis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>label</th>\n",
       "      <th>text</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ham</td>\n",
       "      <td>Go until jurong point, crazy.. Available only ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ham</td>\n",
       "      <td>Ok lar... Joking wif u oni...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>spam</td>\n",
       "      <td>Free entry in 2 a wkly comp to win FA Cup fina...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ham</td>\n",
       "      <td>U dun say so early hor... U c already then say...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ham</td>\n",
       "      <td>Nah I don't think he goes to usf, he lives aro...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  label                                               text\n",
       "0   ham  Go until jurong point, crazy.. Available only ...\n",
       "1   ham                      Ok lar... Joking wif u oni...\n",
       "2  spam  Free entry in 2 a wkly comp to win FA Cup fina...\n",
       "3   ham  U dun say so early hor... U c already then say...\n",
       "4   ham  Nah I don't think he goes to usf, he lives aro..."
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total Samples: 5572\n",
      "Classes: ['ham' 'spam']\n",
      "\n",
      "Class Distribution:\n",
      "label\n",
      "ham     4825\n",
      "spam     747\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Missing Values: 0\n"
     ]
    }
   ],
   "source": [
    "print(f\"Total Samples: {len(df)}\")\n",
    "print(f\"Classes: {df['label'].unique()}\")\n",
    "print(f\"\\nClass Distribution:\")\n",
    "print(df['label'].value_counts())\n",
    "print(f\"\\nMissing Values: {df.isnull().sum().sum()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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REREZFAsNImIiIiIiMigWGgSERERERGRQbHQJCIiIiIiIoNioUlEREREREQGxUKTiIiIiIiIDMqkCs3ly5fD29sblpaWePrpp3HixAmxIxERERERERkdkyk0f/31V0ydOhWzZ8/G6dOnERQUhLCwMKSlpYkdjYiIiIiIyKiYTKG5aNEivP766xgzZgwCAgKwatUqWFtb4/vvvxc7GhERERERkVExiUJTrVYjNjYWvXr10q2TSqXo1asXYmJiRExGRERERERkfMzEDlAb7t27B41GA1dXV731rq6uuHLlSrn2xcXFKC4u1n3OyckBAKhUKoPm0uQVP74RUT1l6O+X2pJbUip2BKIaZW3A782y73NBEAzWJxERGQeTKDQra8GCBZg7d2659Z6eniKkIaqf7LBC7AhEVBE7O4N3mZubC7sa6JeIiOovkyg0GzRoAJlMhtTUVL31qampcHNzK9d+xowZmDp1qu6zVqtFZmYmnJycIJFIajwvGZ5KpYKnpycSExOhVCrFjkNE/+D3Zv0mCAJyc3Ph4eEhdhQiIqpjTKLQtLCwQHBwMKKjozFw4EAA94vH6OhoTJw4sVx7uVwOuVyut87e3r4WklJNUyqV/GGWqA7i92b9xZFMIiKqiEkUmgAwdepUjB49Gu3atUOHDh3w5ZdfIj8/H2PGjBE7GhERERERkVExmULzpZdeQnp6OmbNmoWUlBS0adMGu3btKjdBEBEREREREVWPyRSaADBx4sQKb5Ul4yeXyzF79uxyt0QTkbj4vUlERGScJALnJCciIiIiIiIDkoodgIiIiIiIiIwLC00iIiIiIiIyKBaaVO9069YNkydPFjsGERERERE9BAtNIiIiIiIiMigWmkRERERERGRQLDSpXtJqtZg+fTocHR3h5uaGOXPm6LYtWrQIgYGBsLGxgaenJyZMmIC8vDzd9sjISNjb22Pbtm1o3rw5rK2tMWTIEBQUFGDNmjXw9vaGg4MDJk2aBI1GI8LZEdUPmzZtQmBgIKysrODk5IRevXohPz8fr776KgYOHIi5c+fC2dkZSqUS48ePh1qt1u27a9cudOrUCfb29nByckL//v1x48YN3fb4+HhIJBJs2LABnTt3hpWVFdq3b49r167h5MmTaNeuHRQKBfr27Yv09HQxTp+IiIgegYUm1Utr1qyBjY0Njh8/js8//xzz5s1D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",
      "text/plain": [
       "<Figure size 1000x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(1, 2, figsize=(10, 4))\n",
    "\n",
    "class_dist = df['label'].value_counts()\n",
    "colors = ['#2ecc71', '#e74c3c']\n",
    "\n",
    "axes[0].bar(class_dist.index, class_dist.values, color=colors)\n",
    "axes[0].set_xlabel('Class')\n",
    "axes[0].set_ylabel('Count')\n",
    "axes[0].set_title('Class Distribution')\n",
    "\n",
    "axes[1].pie(class_dist.values, labels=class_dist.index, autopct='%1.1f%%', colors=colors)\n",
    "axes[1].set_title('Class Percentage')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ham Samples:\n",
      "1. Go until jurong point, crazy.. Available only in bugis n great world la e buffet...\n",
      "2. Ok lar... Joking wif u oni......\n",
      "3. U dun say so early hor... U c already then say......\n",
      "\n",
      "Spam Samples:\n",
      "1. Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 8...\n",
      "2. FreeMsg Hey there darling it's been 3 week's now and no word back! I'd like some...\n",
      "3. WINNER!! As a valued network customer you have been selected to receivea £900 pr...\n"
     ]
    }
   ],
   "source": [
    "print(\"Ham Samples:\")\n",
    "for i, text in enumerate(df[df['label']=='ham']['text'].head(3), 1):\n",
    "    print(f\"{i}. {text[:80]}...\")\n",
    "\n",
    "print(\"\\nSpam Samples:\")\n",
    "for i, text in enumerate(df[df['label']=='spam']['text'].head(3), 1):\n",
    "    print(f\"{i}. {text[:80]}...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 4: Data Preprocessing\n",
    "\n",
    "Steps:\n",
    "1. Lowercasing\n",
    "2. Remove URLs\n",
    "3. Remove emails\n",
    "4. Remove numbers\n",
    "5. Remove punctuation\n",
    "6. Tokenization\n",
    "7. Remove stopwords\n",
    "8. Stemming"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "STOP_WORDS = {\n",
    "    'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your',\n",
    "    'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she',\n",
    "    'her', 'hers', 'herself', 'it', 'its', 'itself', 'they', 'them', 'their',\n",
    "    'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that',\n",
    "    'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being',\n",
    "    'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an',\n",
    "    'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of',\n",
    "    'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through',\n",
    "    'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down',\n",
    "    'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then',\n",
    "    'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'each',\n",
    "    'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only',\n",
    "    'own', 'same', 'so', 'than', 'too', 'very', 'can', 'will', 'just', 'should',\n",
    "    'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 's', 't'\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TextPreprocessor:\n",
    "    \n",
    "    def __init__(self, stop_words):\n",
    "        self.stop_words = stop_words\n",
    "    \n",
    "    def clean_text(self, text):\n",
    "        if not isinstance(text, str):\n",
    "            return \"\"\n",
    "        text = text.lower()\n",
    "        text = re.sub(r'http\\S+|www\\S+|https\\S+', '', text)\n",
    "        text = re.sub(r'\\S+@\\S+', '', text)\n",
    "        text = re.sub(r'\\d+', '', text)\n",
    "        text = text.translate(str.maketrans('', '', string.punctuation))\n",
    "        text = re.sub(r'\\s+', ' ', text).strip()\n",
    "        return text\n",
    "    \n",
    "    def tokenize(self, text):\n",
    "        return text.split()\n",
    "    \n",
    "    def remove_stopwords(self, tokens):\n",
    "        return [t for t in tokens if t not in self.stop_words]\n",
    "    \n",
    "    def stem(self, word):\n",
    "        suffixes = ['ing', 'ed', 'ly', 'es', 's', 'ment', 'tion', 'ness']\n",
    "        for suf in suffixes:\n",
    "            if word.endswith(suf) and len(word) > len(suf) + 2:\n",
    "                return word[:-len(suf)]\n",
    "        return word\n",
    "    \n",
    "    def preprocess(self, text):\n",
    "        text = self.clean_text(text)\n",
    "        tokens = self.tokenize(text)\n",
    "        tokens = self.remove_stopwords(tokens)\n",
    "        tokens = [t for t in tokens if len(t) > 2]\n",
    "        tokens = [self.stem(t) for t in tokens]\n",
    "        return ' '.join(tokens)\n",
    "\n",
    "preprocessor = TextPreprocessor(STOP_WORDS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Preprocessing complete.\n"
     ]
    }
   ],
   "source": [
    "df['processed_text'] = df['text'].apply(preprocessor.preprocess)\n",
    "print(\"Preprocessing complete.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Preprocessing Examples:\n",
      "======================================================================\n",
      "\n",
      "Original: Go until jurong point, crazy.. Available only in bugis n gre...\n",
      "Processed: jurong point crazy available bugi great world buffet cine got amore wat\n",
      "\n",
      "Original: Ok lar... Joking wif u oni......\n",
      "Processed: lar jok wif oni\n",
      "\n",
      "Original: Free entry in 2 a wkly comp to win FA Cup final tkts 21st Ma...\n",
      "Processed: free entry wkly comp win cup final tkt may text receive entry questionstd txt ratetc app over\n"
     ]
    }
   ],
   "source": [
    "print(\"Preprocessing Examples:\")\n",
    "print(\"=\"*70)\n",
    "for i in range(3):\n",
    "    print(f\"\\nOriginal: {df['text'].iloc[i][:60]}...\")\n",
    "    print(f\"Processed: {df['processed_text'].iloc[i]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Word Count - Mean: 8.0\n",
      "Word Count - Min: 0, Max: 80\n"
     ]
    }
   ],
   "source": [
    "df['word_count'] = df['processed_text'].apply(lambda x: len(x.split()))\n",
    "print(f\"Word Count - Mean: {df['word_count'].mean():.1f}\")\n",
    "print(f\"Word Count - Min: {df['word_count'].min()}, Max: {df['word_count'].max()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 5: Dataset Splitting\n",
    "\n",
    "- Training: 70%\n",
    "- Validation: 15%\n",
    "- Test: 15%\n",
    "\n",
    "Using stratified split to maintain class distribution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Features: (5572,)\n",
      "Labels: (5572,)\n",
      "Mapping: ham=0, spam=1\n"
     ]
    }
   ],
   "source": [
    "X = df['processed_text'].values\n",
    "y = np.array([1 if label == 'spam' else 0 for label in df['label'].values])\n",
    "\n",
    "print(f\"Features: {X.shape}\")\n",
    "print(f\"Labels: {y.shape}\")\n",
    "print(f\"Mapping: ham=0, spam=1\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training: 3902 (70.0%)\n",
      "Validation: 834 (15.0%)\n",
      "Test: 836 (15.0%)\n"
     ]
    }
   ],
   "source": [
    "X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.15, random_state=42, stratify=y)\n",
    "X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.176, random_state=42, stratify=y_temp)\n",
    "\n",
    "print(f\"Training: {len(X_train)} ({len(X_train)/len(X)*100:.1f}%)\")\n",
    "print(f\"Validation: {len(X_val)} ({len(X_val)/len(X)*100:.1f}%)\")\n",
    "print(f\"Test: {len(X_test)} ({len(X_test)/len(X)*100:.1f}%)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train: Ham=3379 (86.6%), Spam=523 (13.4%)\n",
      "Val: Ham=722 (86.6%), Spam=112 (13.4%)\n",
      "Test: Ham=724 (86.6%), Spam=112 (13.4%)\n"
     ]
    }
   ],
   "source": [
    "for name, y_split in [('Train', y_train), ('Val', y_val), ('Test', y_test)]:\n",
    "    ham = np.sum(y_split == 0)\n",
    "    spam = np.sum(y_split == 1)\n",
    "    print(f\"{name}: Ham={ham} ({ham/len(y_split)*100:.1f}%), Spam={spam} ({spam/len(y_split)*100:.1f}%)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 6: Feature Representation\n",
    "\n",
    "Three methods:\n",
    "1. Bag of Words (BoW)\n",
    "2. TF-IDF\n",
    "3. Word Embeddings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.1 Bag of Words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vocabulary: 2582\n",
      "Shape: (3902, 2582)\n"
     ]
    }
   ],
   "source": [
    "bow_vectorizer = CountVectorizer(max_features=5000, min_df=2, max_df=0.95)\n",
    "\n",
    "X_train_bow = bow_vectorizer.fit_transform(X_train)\n",
    "X_val_bow = bow_vectorizer.transform(X_val)\n",
    "X_test_bow = bow_vectorizer.transform(X_test)\n",
    "\n",
    "print(f\"Vocabulary: {len(bow_vectorizer.vocabulary_)}\")\n",
    "print(f\"Shape: {X_train_bow.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2 TF-IDF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vocabulary: 5000\n",
      "Shape: (3902, 5000)\n"
     ]
    }
   ],
   "source": [
    "tfidf_vectorizer = TfidfVectorizer(max_features=5000, min_df=2, max_df=0.95, ngram_range=(1,2))\n",
    "\n",
    "X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)\n",
    "X_val_tfidf = tfidf_vectorizer.transform(X_val)\n",
    "X_test_tfidf = tfidf_vectorizer.transform(X_test)\n",
    "\n",
    "print(f\"Vocabulary: {len(tfidf_vectorizer.vocabulary_)}\")\n",
    "print(f\"Shape: {X_train_tfidf.shape}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ham: ['call', 'get', 'ill', 'got', 'ltgt', 'come', 'know', 'good', 'dont', 'like']\n",
      "Spam: ['call', 'free', 'txt', 'text', 'mobile', 'claim', 'stop', 'rep', 'prize', 'new']\n"
     ]
    }
   ],
   "source": [
    "feature_names = tfidf_vectorizer.get_feature_names_out()\n",
    "\n",
    "for label, name in [(0, 'Ham'), (1, 'Spam')]:\n",
    "    mask = y_train == label\n",
    "    mean_tfidf = np.array(X_train_tfidf[mask].mean(axis=0)).flatten()\n",
    "    top_idx = mean_tfidf.argsort()[-10:][::-1]\n",
    "    print(f\"{name}: {[feature_names[i] for i in top_idx]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.3 Word Embeddings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vocabulary: 5978\n",
      "Dimension: 100\n",
      "Shape: (3902, 100)\n"
     ]
    }
   ],
   "source": [
    "all_words = []\n",
    "for text in X_train:\n",
    "    all_words.extend(text.split())\n",
    "vocab = list(set(all_words))\n",
    "word_to_idx = {w: i for i, w in enumerate(vocab)}\n",
    "\n",
    "np.random.seed(42)\n",
    "embedding_dim = 100\n",
    "word_embeddings = np.random.randn(len(vocab), embedding_dim) * 0.1\n",
    "\n",
    "def get_doc_vector(text, w2i, emb, dim=100):\n",
    "    tokens = text.split()\n",
    "    vecs = [emb[w2i[t]] for t in tokens if t in w2i]\n",
    "    return np.mean(vecs, axis=0) if vecs else np.zeros(dim)\n",
    "\n",
    "X_train_w2v = np.array([get_doc_vector(t, word_to_idx, word_embeddings) for t in X_train])\n",
    "X_val_w2v = np.array([get_doc_vector(t, word_to_idx, word_embeddings) for t in X_val])\n",
    "X_test_w2v = np.array([get_doc_vector(t, word_to_idx, word_embeddings) for t in X_test])\n",
    "\n",
    "print(f\"Vocabulary: {len(vocab)}\")\n",
    "print(f\"Dimension: {embedding_dim}\")\n",
    "print(f\"Shape: {X_train_w2v.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 7: Model Training and Evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate(model, Xtr, Xv, Xte, ytr, yv, yte, name):\n",
    "    model.fit(Xtr, ytr)\n",
    "    pred = model.predict(Xte)\n",
    "    return {\n",
    "        'Model': name,\n",
    "        'Train': accuracy_score(ytr, model.predict(Xtr)),\n",
    "        'Val': accuracy_score(yv, model.predict(Xv)),\n",
    "        'Test': accuracy_score(yte, pred),\n",
    "        'Precision': precision_score(yte, pred),\n",
    "        'Recall': recall_score(yte, pred),\n",
    "        'F1': f1_score(yte, pred)\n",
    "    }, pred, model\n",
    "\n",
    "results = []\n",
    "models = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bag of Words Models\n",
      "----------------------------------------\n",
      "NB-BoW: 0.9701\n",
      "LR-BoW: 0.9761\n",
      "RF-BoW: 0.9689\n"
     ]
    }
   ],
   "source": [
    "print(\"Bag of Words Models\")\n",
    "print(\"-\"*40)\n",
    "\n",
    "for name, clf in [('NB-BoW', MultinomialNB()),\n",
    "                  ('LR-BoW', LogisticRegression(max_iter=1000)),\n",
    "                  ('RF-BoW', RandomForestClassifier(n_estimators=100))]:\n",
    "    r, p, m = evaluate(clf, X_train_bow, X_val_bow, X_test_bow, y_train, y_val, y_test, name)\n",
    "    results.append(r)\n",
    "    models[name] = (m, bow_vectorizer, 'bow')\n",
    "    print(f\"{name}: {r['Test']:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TF-IDF Models\n",
      "----------------------------------------\n",
      "NB-TFIDF: 0.9581\n",
      "LR-TFIDF: 0.9629\n",
      "SVM-TFIDF: 0.9749\n",
      "RF-TFIDF: 0.9677\n"
     ]
    }
   ],
   "source": [
    "print(\"TF-IDF Models\")\n",
    "print(\"-\"*40)\n",
    "\n",
    "best_acc = 0\n",
    "best_name = None\n",
    "best_pred = None\n",
    "\n",
    "for name, clf in [('NB-TFIDF', MultinomialNB()),\n",
    "                  ('LR-TFIDF', LogisticRegression(max_iter=1000)),\n",
    "                  ('SVM-TFIDF', SVC(kernel='linear', probability=True)),\n",
    "                  ('RF-TFIDF', RandomForestClassifier(n_estimators=100))]:\n",
    "    r, p, m = evaluate(clf, X_train_tfidf, X_val_tfidf, X_test_tfidf, y_train, y_val, y_test, name)\n",
    "    results.append(r)\n",
    "    models[name] = (m, tfidf_vectorizer, 'tfidf')\n",
    "    print(f\"{name}: {r['Test']:.4f}\")\n",
    "    if r['Test'] > best_acc:\n",
    "        best_acc = r['Test']\n",
    "        best_name = name\n",
    "        best_pred = p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Word Embedding Models\n",
      "----------------------------------------\n",
      "LR-W2V: 0.8505\n",
      "SVM-W2V: 0.9474\n",
      "RF-W2V: 0.9354\n"
     ]
    }
   ],
   "source": [
    "print(\"Word Embedding Models\")\n",
    "print(\"-\"*40)\n",
    "\n",
    "for name, clf in [('LR-W2V', LogisticRegression(max_iter=1000)),\n",
    "                  ('SVM-W2V', SVC(kernel='rbf', probability=True)),\n",
    "                  ('RF-W2V', RandomForestClassifier(n_estimators=100))]:\n",
    "    r, p, m = evaluate(clf, X_train_w2v, X_val_w2v, X_test_w2v, y_train, y_val, y_test, name)\n",
    "    results.append(r)\n",
    "    models[name] = (m, (word_to_idx, word_embeddings), 'w2v')\n",
    "    print(f\"{name}: {r['Test']:.4f}\")\n",
    "    if r['Test'] > best_acc:\n",
    "        best_acc = r['Test']\n",
    "        best_name = name\n",
    "        best_pred = p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 8: Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    Model    Train      Val     Test  Precision   Recall       F1\n",
      "   LR-BoW 0.992312 0.979616 0.976077   0.989362 0.830357 0.902913\n",
      "SVM-TFIDF 0.992312 0.977218 0.974880   0.959596 0.848214 0.900474\n",
      "   NB-BoW 0.986674 0.979616 0.970096   0.891892 0.883929 0.887892\n",
      "   RF-BoW 0.999744 0.977218 0.968900   0.988636 0.776786 0.870000\n",
      " RF-TFIDF 0.999744 0.972422 0.967703   0.988506 0.767857 0.864322\n",
      " LR-TFIDF 0.966427 0.956835 0.962919   0.976471 0.741071 0.842640\n",
      " NB-TFIDF 0.977960 0.965228 0.958134   0.987342 0.696429 0.816754\n",
      "  SVM-W2V 0.969503 0.962830 0.947368   0.904762 0.678571 0.775510\n",
      "   RF-W2V 1.000000 0.934053 0.935407   1.000000 0.517857 0.682353\n",
      "   LR-W2V 0.862635 0.862110 0.850478   0.190476 0.035714 0.060150\n"
     ]
    }
   ],
   "source": [
    "results_df = pd.DataFrame(results).sort_values('Test', ascending=False)\n",
    "print(results_df.to_string(index=False))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Best Model: SVM-TFIDF\n",
      "Accuracy: 0.9749\n",
      "\n",
      "Classification Report:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "         Ham       0.98      0.99      0.99       724\n",
      "        Spam       0.96      0.85      0.90       112\n",
      "\n",
      "    accuracy                           0.97       836\n",
      "   macro avg       0.97      0.92      0.94       836\n",
      "weighted avg       0.97      0.97      0.97       836\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(f\"\\nBest Model: {best_name}\")\n",
    "print(f\"Accuracy: {best_acc:.4f}\")\n",
    "print(\"\\nClassification Report:\")\n",
    "print(classification_report(y_test, best_pred, target_names=['Ham', 'Spam']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 9: Visualization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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vbvWrl2rZs2aycPDQ++8847l2LV7JF2+fFlTpkzR7bffLnd3d1WsWFFt2rSx+rOXpI0bN+qee+6Rp6enypcvr3vvvVcHDx4s8L0cPnxYw4YNU/ny5eXr66uHHnpIFy9eLOBPBQBQkBkzZujChQtauHChVRIpT0hIiJ566inL67zkQO3atWU2m1WzZk1NmDBBWVlZVuflfT58++23uvvuu+Xu7q5atWrp3//+t2XM5MmTVaNGDUnSuHHjZDKZVLNmTUm5y47yfn+tvL//r7Vu3Tq1adNG5cuXl5eXl+rUqaMJEyZYjhe2R1JJft4MHDhQq1evtvps37lzp3799dcCv+/8+eefeuaZZ9SgQQN5eXnJx8dHXbt21X//+1/LmMTERDVv3lyS9NBDD1mWPuW9z7zvDbt371bbtm1Vrlw5y3356x5JQ4cOlbu7e77337lzZ1WoUEEnTpy47vtbsWKFWrRoYVU9c/r0aaWnp+db6panSpUqlt8PHjxYaWlpBSYtP/nkE129elWDBg3Kd2zAgAFaunSprl69aun76quvdPHixXxJu9KoXbt2evHFF/V///d/+vDDD29ojo4dO+qrr77K9z0YKO1IJAG4YTVr1lRYWJg+/vhjS9/q1auVlpam/v375xtvGIZ69uypWbNmqUuXLpo5c6bq1KmjcePGaezYsVZjH374Yb355pvq1KmTpk+fLldXV0VFReWb8+TJk2rZsqXWr1+vUaNG6a233lJISIiio6P15ptvFvk9rVy5Uh4eHurTp49d4ydPnqyRI0eqatWqeuONN9S7d2+988476tSpky5fvmw19uzZs+revbtatGihGTNmyGw2q3///lq6dKn69++vbt26afr06crIyFCfPn0KXHffr18/ZWZmKjY2Vt26ddPs2bP16KOPWo2ZP3++atSooQkTJuiNN95QUFCQ/vWvf2nu3Ln55jt06JAGDBigjh076q233iq0tH7y5MmaMmWKIiMjNWfOHD3//POqXr26fvjhB8uY9evXq3PnzkpNTdXkyZM1duxYbd26Va1bt9axY8cKfC/nz59XbGys+vXrp/j4eE2ZMsWOuw4AkHL/p7tWrVpq1aqVXeMffvhhTZw4UXfddZdmzZql8PBwxcbGFviZffjwYfXp00cdO3bUG2+8oQoVKmjYsGHav3+/JOn+++/XrFmzJOUmBD74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ttWbb76prKwsqxgiIiIKnPvKlSvFePcAAAAAAABKn5u6tM3d3V2vvvqqzp49e91xhw4dUnJysg4cOKDHHntMI0aM0IYNG/KNe+CBB5ScnGxpYWFheuSRR6z6goKCJEn16tWz6k9OTlbbtm0LjSFvfFJSkhISEtS3b1/FxsaqVatWOn/+vNXYv14zOTlZLi4UdwEAAAAAcKuyVdhS1FZW3dTsR4cOHXT48GHFxsZqxowZhY6rUqWKypcvL0l68sknNXv2bP3www9q37691TgPDw95eHhYXru5ualcuXIKCAjIN6eLi0uB/YW5dnzVqlXVoEEDdezYUY0aNdKrr76ql19+2TK2sGsCAAAAAIBbU1lO/hSnm1qR5OzsrGnTpuntt9/W77//bnO8YRhas2aNkpKS1KJFi5sZml3q1q2rrl276vPPP7/hObKyspSenm7VAAAAAAAAyqKb/tS2++67T40bN9akSZMKHVOtWjV5eXnJzc1NUVFRmjRp0nWXodlj79698vLysrS77777huapW7eujh07ZtU3b948q7mffvrpQs+PjY2Vr6+vpeUtvQMAAAAAAGWHyVS8rawqkY19Xn31VbVr107PPPNMgce3bNkib29vZWVl6fvvv9eoUaPk5+enESNG6KOPPtJjjz1mGbt69Wrdc889Nq9Zp04drVy50vLabDbfUOyGYeQrXxs0aJCef/55y+u8ZXkFiYmJ0dixYy2v09PTSSYBAAAAAFDGsLQtV4kkktq2bavOnTsrJiZGw4YNy3c8ODjYkoypV6+eduzYoVdeeUUjRoxQz549rZa53XbbbXZd083NTSEhIX879oMHDyo4ONiqz9fX1+65zWbzDSexAAAAAAAASpMSe9TY9OnT1bhxY9WpU8fmWGdnZ126dEmS5O3tLW9v75sdXoF+/vlnrVmzRjExMQ65PgAAAAAAKB0oSMpVYomkBg0aaNCgQZo9e3a+Y6mpqcrMzLQsbfvggw/Up0+fkgpNknTlyhWlpKTo6tWrOnPmjBITE/Xyyy+rcePGGjduXInGAgAAAAAASheWtuUqsUSSJE2dOlVLly7N159XpeTi4qKgoCA99thjmjx5ckmGpv379yswMFDOzs7y9fVVaGioYmJiNGLECJamAQAAAAAASDIZhmE4Ooh/kvT09Nynt41eJidzOUeHAwCSpGPToxwdAnDLyPusT0tLk4+Pj6PDKbU8moxydAhAmXB25xxHhwCUCe4lUCbT/u1txTrfhifCinW+kuLk6AAAAAAAAABQNpTo0jYAAAAAAICyyIk9kiSRSAIAAAAAALCJPFIulrYBAAAAAADALlQkAQAAAAAA2GCiJEkSiSQAAAAAAACbnMgjSWJpGwAAAAAAAOxERRIAAAAAAIANLG3LRSLJQfZN6SwfHx9HhwEAAAAAAOxAHikXS9sAAAAAAABgFyqSAAAAAAAAbDCJkiSJiiQAAAAAAADYiYokAAAAAAAAG5woSJJEIgkAAAAAAMAmntqWi6VtAAAAAAAAsAsVSQAAAAAAADZQkJSLRJKD1J+0Vk7mco4OAwBKzLHpUY4OAQAAALhhTmSSJLG0DQAAAAAAAHaiIgkAAAAAAMAGCpJyUZEEAAAAAAAAu1CRBAAAAAAAYIOJkiRJJJIAAAAAAABsIo+Ui6VtAAAAAAAAsAsVSQAAAAAAADY4UZIkiYokAAAAAAAAm0zF3P6O6dOny2QyafTo0Za+zMxMjRw5UhUrVpSXl5d69+6tkydPWp2XlJSkqKgolStXTlWqVNG4ceN05cqVIl2bRBIAAAAAAEAZsXPnTr3zzjtq2LChVf+YMWP01Vdfafny5dq0aZNOnDih+++/33I8JydHUVFRys7O1tatW7V48WLFx8dr4sSJRbp+iSSShg0bpl69ehV4rGbNmjKZTDKZTCpXrpwaNGig999/v8CxkydPtowtrOVdr6Bjhw8fLjCea8e7urrK399fHTt21KJFi3T16tVC481r1apV+/s3CQAAAAAAlFq28hFFbTfiwoULGjRokN577z1VqFDB0p+WlqaFCxdq5syZateunZo2baq4uDht3bpV27dvlyR98803OnDggD788EM1btxYXbt21UsvvaS5c+cqOzvb7hhKRUXS1KlTlZycrH379mnw4MF65JFHtHr16nzjnnnmGSUnJ1tatWrVLOfmtTxdunSx6k9OTlZwcHChMeSNP3bsmFavXq3IyEg99dRT6t69e74yr79e88cffyy+mwEAAAAAAEodJ1PxtqysLKWnp1u1rKys68YwcuRIRUVFqUOHDlb9u3fv1uXLl63669atq+rVq2vbtm2SpG3btqlBgwby9/e3jOncubPS09O1f/9+u+9Dqdhs29vbWwEBAZKk8ePHa8aMGVq3bp26du1qNc7Ly0teXl6W187OzlbnXstsNhfYX5hrx992222666671LJlS7Vv317x8fF6+OGHC4wXAAAAAACgqGJjYzVlyhSrvkmTJmny5MkFjv/kk0/0ww8/aOfOnfmOpaSkyM3NTeXLl7fq9/f3V0pKimXMtUmkvON5x+xVKhJJea5evaovvvhCZ8+elZubm6PDUbt27dSoUSN9/vnnVokkAAAAAADwz3Kjy9EKExMTo7Fjx1r1mc3mAsceP35cTz31lNatWyd3d/dijaOoSkUiafz48XrhhReUlZWlK1euyM/P728nblatWmVVvdS1a1ctX768yPPUrVtXP/30U4Hx5pk2bZqefPLJAs/PysqyKk1LT08vcgwAAAC2rFy50u6xPXv2vImRAAAAe5jN5kITR3+1e/dupaam6q677rL05eTkaPPmzZozZ47Wrl2r7OxsnTt3zqoq6eTJk5YVVQEBAfr++++t5s17qltRVl2VikTSuHHjNGzYMCUnJ2vcuHH617/+pZCQEEm5SZpp06ZZxh44cEDVq1e3OWdkZKTmz59vee3p6XlDsRmGkS/rmBdvnkqVKhV6fkGlagAAAMWtsAeb/JXJZFJOTs7NDQYAgFtQMRckFUn79u21d+9eq76HHnpIdevW1fjx4xUUFCRXV1dt2LBBvXv3liQdOnRISUlJCgsLkySFhYXplVdeUWpqqqpUqSJJWrdunXx8fBQaGmp3LKUikVSpUiWFhIQoJCREy5cvV4MGDdSsWTOFhobq8ccfV79+/Sxjq1atatecnp6elmTU33Hw4MF8m3TnxWuPv5aqpaenKygo6G/HBQAAcK2/PmkWAAAUr+Je2lYU3t7eql+/vlWfp6enKlasaOmPjo7W2LFj5efnJx8fHz3xxBMKCwtTy5YtJUmdOnVSaGioHnzwQc2YMUMpKSl64YUXNHLkSLsro6RSkki6VlBQkB544AHFxMToyy+/lJ+fn/z8/BwSy8aNG7V3716NGTPmhucoSqkaAAAAAADAjZg1a5acnJzUu3dvZWVlqXPnzpo3b57luLOzs1atWqURI0YoLCxMnp6eGjp0qKZOnVqk65RYIiktLU179uyx6qtYsWKBY5966inVr19fu3btUrNmzUoguty9jFJSUpSTk6OTJ09qzZo1io2NVffu3TVkyJASiQEAAKC4ZGRkaNOmTUpK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w4apV69eBR6rWbOmTCaTTCaTypUrpwYNGuj999+/7nzx8fGWc0wmk7y8vNS0aVN9/vnndsd04cIFubq66pNPPrHq79+/v0wmk44dO5YvzhdffNHu+QEAAAAAQNljKuZWVpXqiqSpU6cqOTlZ+/bt0+DBg/XII49o9erV1z3Hx8dHycnJSk5O1o8//qjOnTurX79+OnTokF3X9PLyUrNmzZSYmGjVn5iYqKCgIKv+o0eP6v/+7//Url27or41AAAAAABQhphMxdvKqlKdSPL29lZAQIBq1aql8ePHy8/PT+vWrbvuOSaTSQEBAQoICNDtt9+ul19+WU5OTvrpp58sY86ePashQ4aoQoUKKleunLp27apff/3VcjwyMtIqYXTw4EFlZmZqxIgRVv2JiYkym80KCwsrtvcMAAAAAABQWpXqRFKeq1ev6rPPPtPZs2fl5uZm93k5OTlavHixJOmuu+6y9A8bNky7du3SypUrtW3bNhmGoW7duuny5cuSchNJhw4dUnJysiQpISFBbdq0Ubt27awSSQkJCQoLC5O7u3uhMWRlZSk9Pd2qAQAAAACAssVUzL/KqlKdSBo/fry8vLxkNpvVp08fVahQQQ8//PB1z0lLS5OXl5e8vLzk5uamESNG6N1331Xt2rUlSb/++qtWrlyp999/X/fcc48aNWqkjz76SH/88YdWrFghSWrdurXc3NwsSaPExESFh4eradOmOn36tI4ePSpJ2rRpkyIjI68bT2xsrHx9fS0tKCjo790UAAAAAAAABynViaRx48Zpz5492rhxo1q0aKFZs2YpJCREkjRt2jRLwsjLy0tJSUmScpfD7dmzR3v27NGPP/6oadOm6fHHH9dXX30lKXeZmouLi1q0aGG5TsWKFVWnTh0dPHhQklSuXDk1b97ckkjatGmTIiIi5OLiolatWikxMVG//fabkpKSbCaSYmJilJaWZmnHjx8v7tsEAAAAAABuNnbbliS5ODqA66lUqZJCQkIUEhKi5cuXq0GDBmrWrJlCQ0P1+OOPq1+/fpaxVatWlSQ5OTlZkk2S1LBhQ33zzTd69dVX1aNHD7uvHRkZqaVLl2r//v26dOmSZWlceHi4EhISdPXqVZUrV84qIVUQs9kss9lclLcNAAAAAABKmf9v786jqqr3/4+/DiAH9CAKKgrJYJJC4kiaw424WZjmwuGa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TKcrOVfF5NdffzXh4eFm/fr1JiYmhkTSH+CKmIwdO9Z06NDBNR2+RbgiLl26dDEDBw68YR04x5lE0pgxY8ydd97pUNa7d28TFxdn/86xHq7mTCLp559/No888ogJDAw03t7epkmTJmbBggUO9WNiYsywYcNMUlKSqVGjhqlTp46ZPXu2yc3NNYmJicZms5nbb7/drFmzppRGBty806dPG0kmIyPjunUkmbfeest06tTJeHl5mbCwMLN06VKHOmPGjDHh4eHG29vbhIWFmeTkZHPx4kX78kmTJplmzZqZlJQUU79+fVOtWjUzZMgQk5+fb1566SUTEBBgateubf7xj3+4bKyAq3Fr2026ePGidu/erY4dO9rL3Nzc1LFjR23btq3IdfLy8uTl5eVQ5u3trU8//fSm24QjV8SlKGfPnpUk+fn5lUCvKzdXxmTo0KHq0qWLQ9sonqtismrVKkVHR6tXr16qU6eOWrRooffee881g6iEXBWXdu3aacOGDfrmm28kSXv37tWnn36qBx980AWjwLZt2wr9TYqLi7PHkGM9yosLFy6oVatWWr16tfbt26fHH39c/fv3L3Sb5dy5c1WrVi3t3LlTw4cP15AhQ9SrVy+1a9dOe/bs0QMPPKD+/fvr/PnzZTQSwDk2m002m00rV65UXl7edetNnDhRPXv21N69e9W3b1898sgj2r9/v325j4+P0tLS9NVXX2nGjBl677339Prrrzu0cejQIf3nP//R2rVrtXDhQqWkpKhLly76/vvvtXnzZr300ktKTk7Wjh07XDZewJVIJN2kn3/+WZcvX1ZAQIBDeUBAgH788cci14mLi9Nrr72mAwcOqKCgQOvXr9fy5ct14sSJm24TjlwRl2sVFBRoxIgRat++vZo0aVLiY6hsXBWTRYsWac+ePZo2bZpL+18ZuSomhw8f1ttvv63w8HCtW7dOQ4YM0VNPPaW5c+e6dDyVhaviMm7cOD3yyCNq3LixqlSpohYtWmjEiBHq27evS8dzq/rxxx+LjGFOTo5+++03jvUoNenp6fb/OF/9/D6BHBQUpNGjR6t58+Zq0KCBhg8frk6dOmnJkiUO7TRr1kzJyckKDw/X+PHj5eXlpVq1amnw4MEKDw/Xc889p+zsbH3++eelPUTgD/Hw8FBaWprmzp2rGjVqqH379nr22WcL/e726tVLjz32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",
      "text/plain": [
       "<Figure size 1200x500 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(1, 2, figsize=(12, 5))\n",
    "\n",
    "axes[0].barh(results_df['Model'], results_df['Test'])\n",
    "axes[0].set_xlabel('Accuracy')\n",
    "axes[0].set_title('Model Comparison')\n",
    "axes[0].set_xlim(0.9, 1.0)\n",
    "\n",
    "cm = confusion_matrix(y_test, best_pred)\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=axes[1], xticklabels=['Ham','Spam'], yticklabels=['Ham','Spam'])\n",
    "axes[1].set_xlabel('Predicted')\n",
    "axes[1].set_ylabel('Actual')\n",
    "axes[1].set_title(f'Confusion Matrix ({best_name})')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('results.png', dpi=150)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 10: Model Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(text, model_name=None):\n",
    "    if model_name is None:\n",
    "        model_name = best_name\n",
    "    \n",
    "    model, vec, vtype = models[model_name]\n",
    "    processed = preprocessor.preprocess(text)\n",
    "    \n",
    "    if vtype in ['bow', 'tfidf']:\n",
    "        feat = vec.transform([processed])\n",
    "    else:\n",
    "        w2i, emb = vec\n",
    "        feat = np.array([get_doc_vector(processed, w2i, emb)]).reshape(1, -1)\n",
    "    \n",
    "    pred = model.predict(feat)[0]\n",
    "    try:\n",
    "        prob = model.predict_proba(feat)[0]\n",
    "        conf = max(prob) * 100\n",
    "    except:\n",
    "        conf = None\n",
    "    \n",
    "    return 'SPAM' if pred == 1 else 'HAM', conf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test Results:\n",
      "============================================================\n",
      "Hey, are you coming tonight?             -> HAM (99.7%)\n",
      "CONGRATULATIONS! You won $1,000,000!     -> SPAM (93.4%)\n",
      "Meeting at 3pm tomorrow.                 -> HAM (99.8%)\n",
      "FREE prize! Click here NOW!              -> SPAM (98.4%)\n",
      "Can you pick up milk?                    -> HAM (99.5%)\n",
      "URGENT: Your account is compromised!     -> SPAM (85.9%)\n"
     ]
    }
   ],
   "source": [
    "test_msgs = [\n",
    "    \"Hey, are you coming tonight?\",\n",
    "    \"CONGRATULATIONS! You won $1,000,000!\",\n",
    "    \"Meeting at 3pm tomorrow.\",\n",
    "    \"FREE prize! Click here NOW!\",\n",
    "    \"Can you pick up milk?\",\n",
    "    \"URGENT: Your account is compromised!\"\n",
    "]\n",
    "\n",
    "print(\"Test Results:\")\n",
    "print(\"=\"*60)\n",
    "for msg in test_msgs:\n",
    "    label, conf = predict(msg)\n",
    "    conf_str = f\"({conf:.1f}%)\" if conf else \"\"\n",
    "    print(f\"{msg[:40]:40} -> {label} {conf_str}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Message: Win a free iPhone! Text WIN to 12345\n",
      "Prediction: SPAM\n",
      "Confidence: 100.0%\n"
     ]
    }
   ],
   "source": [
    "# Test your own message\n",
    "my_message = \"Win a free iPhone! Text WIN to 12345\"\n",
    "\n",
    "label, conf = predict(my_message)\n",
    "print(f\"Message: {my_message}\")\n",
    "print(f\"Prediction: {label}\")\n",
    "if conf:\n",
    "    print(f\"Confidence: {conf:.1f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Section 10: Interactive Testing\n",
    "\n",
    "A **hybrid spam detection system** combining rule-based patterns with machine learning.\n",
    "\n",
    "---\n",
    "\n",
    "### 10.1 How It Works\n",
    "\n",
    "```\n",
    "INPUT → Smart Split → Rule Check → Model Check → Risk Score → OUTPUT\n",
    "```\n",
    "\n",
    "|Step|Process|Description|\n",
    "|:--:|-------|-----------|\n",
    "|1|**Smart Split**|Splits message by punctuation and uppercase detection|\n",
    "|2|**Rule Check**|Scans each segment for known spam patterns|\n",
    "|3|**Model Check**|Runs ML model prediction on each segment|\n",
    "|4|**Risk Score**|Calculates spam probability based on all segments|\n",
    "\n",
    "---\n",
    "\n",
    "### 10.2 Detection Methods\n",
    "\n",
    "|Method|Description|\n",
    "|------|-----------|\n",
    "|**Rule-Based**|Detects spam keywords: `URGENT`, `FREE`, `PRIZE`, `WINNER`, `CLICK`, `CLAIM`, `$amount`|\n",
    "|**ML Model**|TF-IDF features + trained classifier analyzing text patterns|\n",
    "\n",
    "---\n",
    "\n",
    "### 10.3 Decision Logic\n",
    "\n",
    "|Rule Match|Model Result|Final Decision|\n",
    "|:--------:|:----------:|:------------:|\n",
    "|✓|HAM|**SPAM** (override)|\n",
    "|✓|SPAM|**SPAM**|\n",
    "|✗|HAM|**HAM**|\n",
    "|✗|SPAM|**SPAM**|\n",
    "\n",
    "> **Rule:** If ANY segment = SPAM → entire message = **SPAM**\n",
    "\n",
    "---\n",
    "\n",
    "### 10.4 Case Sensitivity\n",
    "\n",
    "|Pattern|Example|Result|\n",
    "|:-----:|-------|:----:|\n",
    "|`FREE`|\"FREE iPhone!\"|**SPAM**|\n",
    "|`free`|\"Are you free?\"|**HAM**|\n",
    "\n",
    "---\n",
    "\n",
    "### 10.5 Output Metrics\n",
    "\n",
    "**Spam/Ham Content**\n",
    "\n",
    "Percentage of segments classified as spam or ham.\n",
    "\n",
    "|Message Segments|Spam|Ham|Result|\n",
    "|----------------|:--:|:-:|------|\n",
    "|2 segments: 1 spam + 1 ham|50%|50%|SPAM|\n",
    "|3 segments: 2 spam + 1 ham|66.7%|33.3%|SPAM|\n",
    "|4 segments: 1 spam + 3 ham|25%|75%|SPAM|\n",
    "\n",
    "**Risk Score**\n",
    "\n",
    "Average spam probability across all segments.\n",
    "\n",
    "|Segment|Classification|Spam Probability|\n",
    "|-------|:------------:|:--------------:|\n",
    "|HAM (99.8%)|HAM|0.2%|\n",
    "|SPAM (85%)|SPAM|85%|\n",
    "|**Average**||**(0.2 + 85) / 2 = 42.6%**|\n",
    "\n",
    "**Risk Levels**\n",
    "\n",
    "|Risk Score|Level|\n",
    "|:--------:|:---:|\n",
    "|70%+|HIGH|\n",
    "|40-70%|MEDIUM|\n",
    "|<40%|LOW|\n",
    "\n",
    "---\n",
    "\n",
    "### 10.6 Test Scenario - SMS Messages\n",
    "\n",
    "**Ham Messages (Expected: HAM)**\n",
    "\n",
    "|#|Message|\n",
    "|:-:|-------|\n",
    "|1|`Hey, are you free for lunch tomorrow?`|\n",
    "|2|`The meeting is rescheduled to 4pm`|\n",
    "|3|`Thanks for your help yesterday`|\n",
    "|4|`Can you send me the project files?`|\n",
    "|5|`Happy birthday! Have a great day`|\n",
    "|6|`I will be late, stuck in traffic`|\n",
    "|7|`Did you watch the game last night?`|\n",
    "|8|`Please call me when you get home`|\n",
    "\n",
    "**Spam Messages (Expected: SPAM)**\n",
    "\n",
    "|#|Message|\n",
    "|:-:|-------|\n",
    "|1|`CONGRATULATIONS! You won $10,000 cash prize!`|\n",
    "|2|`FREE iPhone! Click here to claim NOW`|\n",
    "|3|`URGENT: Your bank account has been locked`|\n",
    "|4|`Win a free vacation! Reply YES to 88888`|\n",
    "|5|`You have been selected for a cash reward`|\n",
    "|6|`Make money fast! Work from home today`|\n",
    "|7|`Limited offer: 90% discount click now`|\n",
    "|8|`Claim your free gift card immediately`|\n",
    "\n",
    "---\n",
    "\n",
    "### 10.7 Test Scenario - Email Messages\n",
    "\n",
    "**HAM Email (Normal Business)**\n",
    "\n",
    "`Hi John, I hope you are doing well. Just wanted to follow up on our meeting yesterday. The team has reviewed the project proposal and we have some feedback. Can we schedule a call tomorrow at 2pm to discuss the next steps? Also, please send me the updated budget report when you get a chance. Let me know if this time works for you. Best regards, Sarah.`\n",
    "\n",
    "**SPAM Email**\n",
    "\n",
    "`CONGRATULATIONS! You have been selected as our WINNER of the month. You won $50,000 CASH PRIZE! This is not a joke. URGENT: You must claim your prize within 24 hours or it will be transferred to another winner. Click here NOW to verify your account. Call our toll-free number 1-800-555-0000 immediately. FREE bonus: We will also send you a brand new iPhone! Reply YES to confirm. Limited time offer - ACT NOW! Your bank account will be credited within 48 hours.`\n",
    "\n",
    "**Mixed Email (Tricky)**\n",
    "\n",
    "`Hi Team, Hope everyone had a great weekend. Quick reminder about the quarterly meeting tomorrow at 10am. URGENT: Please review the attached documents before the meeting. Also, congratulations to Sarah for winning the employee of the month award! FREE coffee and snacks will be available. Please confirm your attendance by replying to this email. Thanks, Management.`\n",
    "\n",
    "**Expected Results**\n",
    "\n",
    "|Email|Result|Risk Level|Reason|\n",
    "|-----|:----:|:--------:|------|\n",
    "|HAM Email|HAM|LOW|Normal business communication|\n",
    "|SPAM Email|SPAM|HIGH|Multiple patterns: WINNER, CASH, URGENT, FREE, $amount|\n",
    "|Mixed Email|SPAM|MEDIUM|Contains URGENT, FREE but in normal context|\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f5793fb76ef648e4866f00b71ef3a9da",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Textarea(value='', layout=Layout(height='100px', width='500px'), placeholder='Enter your messag…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import ipywidgets as widgets\n",
    "from IPython.display import display, clear_output\n",
    "\n",
    "SPAM_PATTERNS = [\n",
    "    r'\\bURGENT\\b', r'\\bWINNER\\b', r'\\bCONGRAT', r'\\bCLICK\\s*HERE\\b',\n",
    "    r'\\bCLAIM\\b', r'\\bPRIZE\\b', r'\\bWON\\b', r'\\$\\d+', r'\\bCALL\\s*NOW\\b',\n",
    "    r'\\bLIMITED\\s*(TIME|OFFER)\\b', r'\\bACT\\s*NOW\\b', r'\\bREPLY\\b.*\\d{4,}',\n",
    "    r'\\bTEXT\\b.*\\d{4,}', r'\\bCASH\\b', r'\\bBANK\\s*ACCOUNT\\b', r'\\bCREDIT\\s*CARD\\b',\n",
    "    r'\\bVERIFY\\b.*\\bACCOUNT\\b', r'\\bSUSPENDED\\b', r'\\bLOCKED\\b', r'\\bCOMPROMISED\\b'\n",
    "]\n",
    "\n",
    "SPAM_PATTERNS_CASE = [r'\\bFREE\\b']\n",
    "\n",
    "def smart_split(text):\n",
    "    sentences = re.split(r'[.!?]+', text)\n",
    "    final = []\n",
    "    \n",
    "    for sent in sentences:\n",
    "        sent = sent.strip()\n",
    "        if not sent:\n",
    "            continue\n",
    "        \n",
    "        words = sent.split()\n",
    "        if len(words) <= 3:\n",
    "            final.append(sent)\n",
    "            continue\n",
    "        \n",
    "        chunks = []\n",
    "        current = []\n",
    "        \n",
    "        for word in words:\n",
    "            upper_ratio = sum(1 for c in word if c.isupper()) / max(len(word), 1)\n",
    "            if upper_ratio > 0.5 and len(current) > 2:\n",
    "                chunks.append(' '.join(current))\n",
    "                current = [word]\n",
    "            else:\n",
    "                current.append(word)\n",
    "        \n",
    "        if current:\n",
    "            chunks.append(' '.join(current))\n",
    "        \n",
    "        final.extend(chunks)\n",
    "    \n",
    "    return [s for s in final if s.strip()]\n",
    "\n",
    "def check_spam_patterns(text):\n",
    "    text_upper = text.upper()\n",
    "    for pattern in SPAM_PATTERNS:\n",
    "        if re.search(pattern, text_upper):\n",
    "            return True\n",
    "    for pattern in SPAM_PATTERNS_CASE:\n",
    "        if re.search(pattern, text):\n",
    "            return True\n",
    "    return False\n",
    "\n",
    "def predict_smart(text, model_name=None):\n",
    "    if model_name is None:\n",
    "        model_name = best_name\n",
    "    \n",
    "    sentences = smart_split(text)\n",
    "    \n",
    "    if not sentences:\n",
    "        return 'HAM', 0.0, 100.0, 0.0, []\n",
    "    \n",
    "    results = []\n",
    "    \n",
    "    for sent in sentences:\n",
    "        rule_spam = check_spam_patterns(sent)\n",
    "        label, conf = predict(sent, model_name)\n",
    "        \n",
    "        if rule_spam and label == 'HAM':\n",
    "            label = 'SPAM'\n",
    "            conf = max(conf, 85.0) if conf else 85.0\n",
    "        \n",
    "        results.append((sent, label, conf))\n",
    "    \n",
    "    spam_count = sum(1 for r in results if r[1] == 'SPAM')\n",
    "    total = len(results)\n",
    "    \n",
    "    spam_percent = (spam_count / total) * 100\n",
    "    ham_percent = 100 - spam_percent\n",
    "    \n",
    "    spam_scores = []\n",
    "    for sent, label, conf in results:\n",
    "        if label == 'SPAM':\n",
    "            spam_scores.append(conf if conf else 85.0)\n",
    "        else:\n",
    "            spam_scores.append(100.0 - conf if conf else 5.0)\n",
    "    \n",
    "    risk_score = sum(spam_scores) / len(spam_scores)\n",
    "    \n",
    "    final_label = 'SPAM' if spam_count > 0 else 'HAM'\n",
    "    \n",
    "    return final_label, spam_percent, ham_percent, risk_score, results\n",
    "\n",
    "text_input = widgets.Textarea(\n",
    "    placeholder='Enter your message here...',\n",
    "    layout=widgets.Layout(width='500px', height='100px')\n",
    ")\n",
    "\n",
    "button = widgets.Button(description='Predict', button_style='primary')\n",
    "output = widgets.Output()\n",
    "\n",
    "def on_click(b):\n",
    "    with output:\n",
    "        clear_output()\n",
    "        msg = text_input.value\n",
    "        if msg.strip():\n",
    "            final_label, spam_pct, ham_pct, risk_score, results = predict_smart(msg)\n",
    "            \n",
    "            print(f\"Message: {msg}\")\n",
    "            print(f\"\\n{'='*50}\")\n",
    "            print(f\"Analysis:\")\n",
    "            for sent, label, conf in results:\n",
    "                conf_str = f\"({conf:.1f}%)\" if conf else \"\"\n",
    "                if len(sent) > 50:\n",
    "                    print(f\"  - '{sent[:50]}...' -> {label} {conf_str}\")\n",
    "                else:\n",
    "                    print(f\"  - '{sent}' -> {label} {conf_str}\")\n",
    "            \n",
    "            print(f\"\\n{'='*50}\")\n",
    "            print(f\"RESULT: {final_label}\")\n",
    "            print(f\"{'='*50}\")\n",
    "            print(f\"Spam Content:  {spam_pct:5.1f}%  {'█' * int(spam_pct/5)}\")\n",
    "            print(f\"Ham Content:   {ham_pct:5.1f}%  {'█' * int(ham_pct/5)}\")\n",
    "            print(f\"{'='*50}\")\n",
    "            print(f\"Risk Score: {risk_score:.1f}%\")\n",
    "            \n",
    "            if risk_score >= 70:\n",
    "                print(f\"Risk Level: HIGH\")\n",
    "            elif risk_score >= 40:\n",
    "                print(f\"Risk Level: MEDIUM\")\n",
    "            else:\n",
    "                print(f\"Risk Level: LOW\")\n",
    "        else:\n",
    "            print(\"Please enter a message.\")\n",
    "\n",
    "button.on_click(on_click)\n",
    "display(widgets.VBox([text_input, button, output]))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "### Interactive Testing Demo\n",
    "\n",
    "![Interactive Testing](interactive_demo.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## Section 11: Save Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Saved: model_results.csv\n"
     ]
    }
   ],
   "source": [
    "results_df.to_csv('model_results.csv', index=False)\n",
    "print(\"Saved: model_results.csv\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "---\n",
    "## Section 12: Conclusion\n",
    "\n",
    "| Component | Details |\n",
    "|-----------|----------|\n",
    "| Dataset | SMS Spam Collection (5,574 samples) |\n",
    "| Preprocessing | Lowercase, remove URLs/numbers/punctuation, stopwords, stemming |\n",
    "| Split | 70% train, 15% val, 15% test |\n",
    "| Features | BoW, TF-IDF, Word Embeddings |\n",
    "| Models | Naive Bayes, Logistic Regression, SVM, Random Forest |\n",
    "\n",
    "**Reference:**\n",
    "> Almeida, T.A., Gomez Hidalgo, J.M., Yamakami, A. (2011). Contributions to the Study of SMS Spam Filtering. DOCENG'11. https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
