{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "import pickle\n",
    "import os\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "from sklearn.model_selection import train_test_split\n",
    "from collections import Counter\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get song genere data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Read CSV File\n",
    "tracks_info_df = pd.read_csv('data.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Read Processed song data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded 13309 processed tracks\n"
     ]
    }
   ],
   "source": [
    "results_file = 'audio_features.pkl'\n",
    "\n",
    "if os.path.exists(results_file):\n",
    "    with open(results_file, 'rb') as file:\n",
    "        saved_data = pickle.load(file)\n",
    "    X = saved_data.get('X', [])\n",
    "    y_labels = saved_data.get('y_labels', [])\n",
    "    processed_files = set(saved_data.get('processed_files', []))\n",
    "    print(f\"Loaded {len(processed_files)} processed tracks\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Preprocess"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "shape of feature matrix X:  (13309, 2398)\n"
     ]
    }
   ],
   "source": [
    "# --- Turn X into an np array ---\n",
    "X = np.array(X)\n",
    "print(\"shape of feature matrix X: \", X.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# --- Replacing Sub-genres with the genres ---\n",
    "genre_mapping = {\n",
    "    'lo-fi beats': 'lo-fi',\n",
    "    'chillwave': 'lo-fi',\n",
    "\n",
    "    'nu metal': 'metal',\n",
    "    'folk metal': 'metal',\n",
    "    'heavy metal': 'metal',\n",
    "    'metalcore': 'metal',\n",
    "    'power metal': 'metal',\n",
    "    'industrial metal': 'metal',\n",
    "    'glam metal': 'metal',\n",
    "    'melodic death metal': 'metal',\n",
    "    'progressive metal': 'metal',\n",
    "    'thrash metal': 'metal',\n",
    "    'gothic metal': 'metal',\n",
    "    'groove metal': 'metal',\n",
    "    'death metal': 'metal',\n",
    "    'alternative metal': 'metal',\n",
    "    'black metal': 'metal',\n",
    "    'speed metal': 'metal',\n",
    "\n",
    "    'acid house': 'acid',\n",
    "    'acid techno': 'acid',\n",
    "\n",
    "    'afrobeat': 'afro',\n",
    "    'afropop': 'afro',\n",
    "\n",
    "    'art rock': 'rock',\n",
    "\n",
    "    'britpop': 'alternative rock',\n",
    "    \n",
    "    'art pop': 'pop',\n",
    "    'acoustic pop': 'pop',\n",
    "    'bedroom pop': 'pop',\n",
    "    'soft pop': 'pop', \n",
    "    'folk pop': 'pop',\n",
    "    'norwegian pop': 'pop',\n",
    "    'acoustic pop': 'pop',\n",
    "    'dream pop': 'pop',\n",
    "    'chamber pop': 'pop',\n",
    "    'german pop': 'pop',\n",
    "    'bedroom pop': 'pop',\n",
    "    'city pop': 'pop',\n",
    "    'dance pop': 'pop',\n",
    "    'art pop': 'pop',\n",
    "    'indie pop': 'pop',\n",
    "\n",
    "    'alt country': 'country',\n",
    "    'traditional country': 'country',\n",
    "\n",
    "    'blues rock': 'blues',\n",
    "\n",
    "    'brooklyn drill': 'drill',\n",
    "\n",
    "    'gangster rap': 'rap',\n",
    "    'memphis rap': 'rap',\n",
    "    'rap rap': 'rock',\n",
    "    'meme rap': 'rap',\n",
    "    'punk rap': 'rap',\n",
    "    'emo rap': 'rap',\n",
    "    'jazz rap': 'rap',\n",
    "    'melodic rap': 'rap',\n",
    "    'cloud rap': 'rap',\n",
    "    'k-rap': 'rap',\n",
    "    'rage rap': 'rap',\n",
    "\n",
    "    'edm trap': 'trap',\n",
    "    'italian trap': 'trap',\n",
    "    'dark trap': 'trap',\n",
    "\n",
    "    'southern hip hop': 'hip hop',\n",
    "    'west coast hip hop': 'hip hop',\n",
    "    'east coast hip hop': 'hip hop',\n",
    "    'finnish hip hop': 'hip hop',\n",
    "    'german hip hop': 'hip hop',\n",
    "    'underground hip hop': 'hip hop',\n",
    "    'norwegian hip hop': 'hip hop',\n",
    "    'experimental hip hop': 'hip hop',\n",
    "    'alternative hip hop': 'hip hop',\n",
    "    'latin hip hop': 'hip hop',\n",
    "\n",
    "    'jazz blues': 'jazz',\n",
    "    'vocal jazz': 'jazz',\n",
    "    'french jazz': 'jazz',\n",
    "    'free jazz': 'jazz',\n",
    "    'soul jazz': 'jazz',\n",
    "    'jazz fusion': 'jazz',\n",
    "    'nu jazz': 'jazz',\n",
    "    'jazz funk': 'jazz',\n",
    "    'cool jazz': 'jazz',\n",
    "    'jazz beats': 'jazz',\n",
    "    'jazz house': 'jazz',\n",
    "    'indie jazz': 'jazz',\n",
    "    'smooth jazz': 'jazz',\n",
    "\n",
    "    'melodic techno': 'techno',\n",
    "    'minimal techno': 'techno',\n",
    "    'hypertechno': 'techno',\n",
    "    'hard techno': 'techno',\n",
    "    'hardcore techno': 'techno',\n",
    "    'dub techno': 'techno',\n",
    "\n",
    "    'future house': 'house',\n",
    "    'tech house': 'house',\n",
    "    'bass house': 'house',\n",
    "    'electro house': 'house',\n",
    "    'tropical house': 'house',\n",
    "    'french house': 'house',\n",
    "    'melodic house': 'house',\n",
    "    'organic house': 'house',\n",
    "    'progressive house': 'house',\n",
    "    'latin house': 'house',\n",
    "    'deep house': 'house',\n",
    "    'slap house': 'house',\n",
    "    'chicago house': 'house'\n",
    "    \n",
    "} \n",
    "\n",
    "# remove: adult standards, americana?, anime?, baroque pop, bebop, big band, big beat, big room, bluegrass\n",
    "# boom bap, bossa nova brazilian bass breakbeat breakcore 'calypso' 'canzone napoletana'\n",
    "# 'celtic rock' 'chamber pop' 'chicha' \"children's music\"\n",
    "\n",
    "y_labels_normalized = [genre_mapping.get(label, label) for label in y_labels]\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# If above is not run\n",
    "y_labels_normalized = y_labels\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 335672 characters]",
      "text/plain": [
       "<Figure size 1500x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# --- Encoding the Genre Labels ---\n",
    "#le = LabelEncoder()\n",
    "#y = le.fit_transform(y_labels_normalized)\n",
    "#print(\"Shape of label array y: \", y.shape)\n",
    "\n",
    "y = y_labels_normalized\n",
    "\n",
    "\n",
    "# -- Count and show the genres ---\n",
    "freq_original = Counter(y_labels)\n",
    "\n",
    "orig_genres = list(freq_original.keys())\n",
    "orig_counts = list(freq_original.values())\n",
    "\n",
    "fig, axs = plt.subplots(figsize=(15, 6))\n",
    "\n",
    "sns.barplot(x=orig_genres, y=orig_counts, ax=axs)\n",
    "axs.set_title(\"Original Genre Frequencies\")\n",
    "axs.set_xlabel(\"Genre\")\n",
    "axs.set_ylabel(\"Count\")\n",
    "axs.tick_params(axis='x', rotation=45)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Remove all songs with genres which do not come up more than n times"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Class counts: Counter({'rap': 2100, 'hip hop': 1239, 'lo-fi': 945, 'metal': 808, 'pop': 576, 'phonk': 479, 'jazz': 442, 'indie': 395, 'house': 280, 'psychedelic rock': 263, 'trap': 258, 'techno': 231, 'folk punk': 211, 'country': 188, 'classic rock': 185, 'flamenco': 165, 'rock': 161, 'christmas': 156, 'vaporwave': 152, 'punk': 146, 'synthwave': 144, 'midwest emo': 137, 'drift phonk': 128, 'hardstyle': 120, 'progressive rock': 108, 'ska': 94, 'g-funk': 88, 'alternative rock': 83, 'post-punk': 83, 'djent': 80, 'edm': 79, 'blues': 78, 'anime': 75, 'rap metal': 74, 'emo': 72, 'hardcore': 71, 'surf rock': 66, 'drum and bass': 61, 'folk rock': 60, 'funk rock': 56, 'psychedelic pop': 54, 'nerdcore': 49, 'schlager': 48, 'garage rock': 48, 'pop punk': 47, 'psytrance': 45, 'neue deutsche welle': 44, 'honky tonk': 43, 'dubstep': 43, 'reggae': 42, 'spoken word': 39, 'big band': 38, 'gothic country': 35, 'darkwave': 35, 'baroque pop': 34, 'soundtrack': 34, 'deathcore': 31, 'big room': 31, 'breakcore': 30, 'motown': 30, 'r&b': 29, 'indie rock': 28, 'folk': 27, 'soft rock': 27, 'new wave': 25, 'bebop': 24, 'adult standards': 23, 'chinese hip hop': 23, 'medieval': 22, 'drill': 22, 'schlagerparty': 22, 'hard bop': 22, 'hyperpop': 21, 'rockabilly': 21, 'glam rock': 21, 'electro swing': 20, 'retro soul': 20, 'electronic rock': 19, 'slowcore': 18, 'j-dance': 17, 'deathstep': 16, 'witch house': 16, 'soul': 16, 'modern rock': 16, 'boom bap': 16, 'moombahton': 16, 'christian country': 15, 'acid': 15, 'grime': 14, 'melbourne bounce': 14, 'hard rock': 14, 'comedy': 14, 'arena rock': 14, 'future bass': 14, 'afro': 13, 'shoegaze': 13, 'christian hip hop': 13, 'noise rock': 13, 'disco': 13, 'frenchcore': 12, 'trip hop': 12, 'stoner rock': 11, 'alternative r&b': 11, 'doo-wop': 11, 'ebm': 11, 'idm': 11, 'rocksteady': 11, 'horrorcore': 11, 'southern rock': 10, 'dub': 10, 'proto-punk': 10, 'bossa nova': 10, 'brazilian bass': 9, 'skate punk': 9, 'k-pop': 9, 'bluegrass': 9, 'industrial': 9, 'exotica': 8, 'stutter house': 8, 'lovers rock': 8, 'rap rock': 8, 'latin': 8, 'eurodance': 8, 'big beat': 7, 'space music': 7, 'reggaeton': 7, 'indie folk': 7, 'medieval metal': 7, 'musicals': 7, 'symphonic metal': 7, None: 7, 'grunge': 7, 'corrido': 6, 'glitch': 6, 'chicha': 6, 'speedcore': 6, 'trance': 6, 'j-rock': 6, 'celtic rock': 6, 'power pop': 6, 'g-house': 6, \"children's music\": 5, 'gabber': 5, 'post-rock': 5, 'pop urbaine': 5, 'classical': 5, 'polka': 5, 'singer-songwriter': 5, 'celtic': 5, 'synthpop': 5, 'dansktop': 5, 'latin alternative': 4, 'post-hardcore': 4, 'red dirt': 4, 'northern soul': 4, 'italo disco': 4, 'dark ambient': 4, 'alternative dance': 4, 'v-pop': 4, 'roots reggae': 4, 'finnish pop': 4, 'khaleeji': 4, 'french indie pop': 4, 'industrial rock': 4, 'neo soul': 4, 'afro house': 4, 'downtempo': 4, 'uk drill': 4, 'europop': 3, 'country rock': 3, 'ethiopian jazz': 3, 'drumstep': 3, 'happy hardcore': 3, 'lo-fi house': 3, 'epadunk': 3, 'swing music': 3, 'sea shanties': 3, 'hyphy': 3, 'indian indie': 3, 'americana': 2, 'norteño': 2, 'dark cabaret': 2, 'tekno': 2, 'new rave': 2, 'hardcore punk': 2, 'sexy drill': 2, 'gnawa': 2, 'latin indie': 2, 'breakbeat': 2, 'shibuya-kei': 2, 'french pop': 2, 'melodic bass': 2, 'acoustic rock': 2, 'latin pop': 2, 'latin jazz': 2, 'electroclash': 2, 'classical piano': 2, 'bolero': 2, 'acid jazz': 2, 'bassline': 2, 'dancehall': 2, 'bhangra': 2, 'ballroom vogue': 2, 'italian singer-songwriter': 2, 'symphonic rock': 2, 'lounge': 2, 'space rock': 2, 'christian rock': 2, 'ragtime': 2, 'nu disco': 2, 'ambient': 2, 'japanese vgm': 2, 'album rock': 2, 'calypso': 1, 'rally house': 1, 'indonesian pop': 1, 'shatta': 1, 'canzone napoletana': 1, 'neofolk': 1, 'russelåter': 1, 'disco house': 1, 'modern blues': 1, 'colombian pop': 1, 'cumbia': 1, 'cumbia norteña': 1, 'tollywood': 1, 'math rock': 1, 'rock en español': 1, 'champeta': 1, 'traditional music': 1, 'brazilian phonk': 1, 'ccm': 1, 'avant-garde': 1, 'philly soul': 1, 'c-pop': 1, 'electro': 1, 'new age': 1, 'miami bass': 1, 'dansband': 1, 'cold wave': 1, 'aussie drill': 1, 'orchestra': 1, 'pop urbano': 1, 'ska punk': 1, 'finnish rock': 1, 'punta': 1, 'disco polo': 1, 'indie r&b': 1, 'soca': 1, 'reggae rock': 1, 'tango': 1, 'post-grunge': 1, 'mariachi': 1, 'trap latino': 1, 'krautrock': 1, 'neomelodico': 1, 'melodic hardcore': 1, 'fado': 1, 'opera': 1, 'jersey club': 1, 'italo dance': 1, 'raï': 1, 'taiwanese indie': 1, 'bollywood': 1, 'choral': 1, 'banda': 1, 'chanson': 1, 'jungle': 1, 'dembow': 1, 'azonto': 1, 'cumbia sonidera': 1, 'pirate metal': 1, 'nederpop': 1, 'samba': 1, 'funk': 1, 'grupera': 1, 'mambo': 1, 'afrobeats': 1, 'neoclassical': 1, 'japanese indie': 1, 'hard house': 1, 'nightcore': 1, 'kizomba': 1, 'rock and roll': 1, 'madchester': 1, 'son cubano': 1, 'corridos tumbados': 1, 'variété française': 1, 'brazilian jazz': 1})\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 32508 characters]",
      "text/plain": [
       "<Figure size 1500x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.preprocessing import StandardScaler\n",
    "import numpy as np\n",
    "\n",
    "# --- Encoding/Standardizing X ---\n",
    "# Remove compex object variables\n",
    "if np.iscomplexobj(X):\n",
    "    X = np.abs(X)\n",
    "\n",
    "# Standardize the features\n",
    "scaler = StandardScaler()\n",
    "X_scaled = scaler.fit_transform(X)\n",
    "\n",
    "\n",
    "# --- Counting Labels ---\n",
    "# Check class counts.\n",
    "counter = Counter(y)\n",
    "print(\"Class counts:\", counter)\n",
    "\n",
    "# Keep only classes with at least n samples. At least 2  or any other % 2 == 0 number\n",
    "n = 200\n",
    "classes_to_keep = {cls for cls, count in counter.items() if count >= n}\n",
    "indices_to_keep = [i for i, label in enumerate(y) if label in classes_to_keep]\n",
    "\n",
    "y = np.array(y)\n",
    "\n",
    "# Filter your data.\n",
    "X_filtered = X_scaled[indices_to_keep]\n",
    "y_filtered = y[indices_to_keep]\n",
    "\n",
    "le = LabelEncoder()\n",
    "y_encoded = le.fit_transform(y_filtered)\n",
    "\n",
    "freq_leftover = Counter(y_encoded)\n",
    "leftover_genres = list(freq_leftover.keys())\n",
    "leftover_counts = list(freq_leftover.values())\n",
    "\n",
    "fig, axs = plt.subplots(figsize=(15, 6))\n",
    "\n",
    "# Plot normalized label frequencies.\n",
    "sns.barplot(x=leftover_genres, y=leftover_counts, ax=axs)\n",
    "axs.set_title(\"Normalized Genre Frequencies\")\n",
    "axs.set_xlabel(\"Genre\")\n",
    "axs.set_ylabel(\"Count\")\n",
    "axs.tick_params(axis='x', rotation=45)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create a Test-Train split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training set shape: (6581, 2398) (6581,)\n",
      "Test set shape: (1646, 2398) (1646,)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X_filtered, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded\n",
    ")\n",
    "\n",
    "print(\"Training set shape:\", X_train.shape, y_train.shape)\n",
    "print(\"Test set shape:\", X_test.shape, y_test.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Define a testable ANN model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'algorithm': 'auto', 'n_neighbors': 7, 'weights': 'distance'}\n"
     ]
    }
   ],
   "source": [
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "\n",
    "\n",
    "knn = KNeighborsClassifier()\n",
    "\n",
    "param_grid = {'n_neighbors': [1, 3, 5, 7, 9, 11, 13, 15, 17],\n",
    "              'weights': ['uniform', 'distance'],\n",
    "              'algorithm': ['auto', 'ball_tree', 'kd_tree', 'brute']\n",
    "            }\n",
    "\n",
    "grid = GridSearchCV(knn, param_grid)\n",
    "\n",
    "grid.fit(X_train, y_train)\n",
    "\n",
    "print(grid.best_params_)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-04-05 23:58:03.289842: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.\n",
      "2025-04-05 23:58:03.292099: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.\n",
      "2025-04-05 23:58:03.298353: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\n",
      "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n",
      "E0000 00:00:1743890283.310278 2626576 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n",
      "E0000 00:00:1743890283.314011 2626576 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n",
      "W0000 00:00:1743890283.323494 2626576 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
      "W0000 00:00:1743890283.323509 2626576 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
      "W0000 00:00:1743890283.323511 2626576 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
      "W0000 00:00:1743890283.323512 2626576 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
      "2025-04-05 23:58:03.326692: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n",
      "To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/100\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/agres/Services/predictify/.venv/lib/python3.11/site-packages/keras/src/layers/core/dense.py:87: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(activity_regularizer=activity_regularizer, **kwargs)\n",
      "2025-04-05 23:58:04.597051: E external/local_xla/xla/stream_executor/cuda/cuda_platform.cc:51] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 5ms/step - accuracy: 0.3411 - loss: 4.5206 - val_accuracy: 0.5349 - val_loss: 3.0895\n",
      "Epoch 2/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.5484 - loss: 2.8608 - val_accuracy: 0.5996 - val_loss: 2.3424\n",
      "Epoch 3/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.6337 - loss: 2.1497 - val_accuracy: 0.6003 - val_loss: 1.9984\n",
      "Epoch 4/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.6632 - loss: 1.7858 - val_accuracy: 0.6204 - val_loss: 1.8324\n",
      "Epoch 5/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.6748 - loss: 1.5789 - val_accuracy: 0.6190 - val_loss: 1.7230\n",
      "Epoch 6/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.6892 - loss: 1.4335 - val_accuracy: 0.6384 - val_loss: 1.6747\n",
      "Epoch 7/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7112 - loss: 1.3490 - val_accuracy: 0.6039 - val_loss: 1.6982\n",
      "Epoch 8/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.6972 - loss: 1.3584 - val_accuracy: 0.6139 - val_loss: 1.6679\n",
      "Epoch 9/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7371 - loss: 1.2829 - val_accuracy: 0.6276 - val_loss: 1.6413\n",
      "Epoch 10/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7245 - loss: 1.2971 - val_accuracy: 0.6449 - val_loss: 1.6084\n",
      "Epoch 11/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7430 - loss: 1.2341 - val_accuracy: 0.6032 - val_loss: 1.7611\n",
      "Epoch 12/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7279 - loss: 1.2907 - val_accuracy: 0.6319 - val_loss: 1.6688\n",
      "Epoch 13/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7382 - loss: 1.2498 - val_accuracy: 0.6175 - val_loss: 1.7435\n",
      "Epoch 14/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7513 - loss: 1.2322 - val_accuracy: 0.6262 - val_loss: 1.7219\n",
      "Epoch 15/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7378 - loss: 1.2731 - val_accuracy: 0.5981 - val_loss: 1.7940\n",
      "Epoch 16/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7505 - loss: 1.2188 - val_accuracy: 0.6247 - val_loss: 1.7235\n",
      "Epoch 17/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7525 - loss: 1.2036 - val_accuracy: 0.6348 - val_loss: 1.7124\n",
      "Epoch 18/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7430 - loss: 1.2430 - val_accuracy: 0.6096 - val_loss: 1.7416\n",
      "Epoch 19/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7462 - loss: 1.2336 - val_accuracy: 0.6125 - val_loss: 1.7306\n",
      "Epoch 20/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7561 - loss: 1.2209 - val_accuracy: 0.6290 - val_loss: 1.7558\n",
      "Epoch 21/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7572 - loss: 1.2147 - val_accuracy: 0.6219 - val_loss: 1.7325\n",
      "Epoch 22/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7871 - loss: 1.1505 - val_accuracy: 0.6348 - val_loss: 1.6833\n",
      "Epoch 23/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7695 - loss: 1.1755 - val_accuracy: 0.6326 - val_loss: 1.6869\n",
      "Epoch 24/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7885 - loss: 1.1277 - val_accuracy: 0.6276 - val_loss: 1.7540\n",
      "Epoch 25/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7819 - loss: 1.1687 - val_accuracy: 0.6341 - val_loss: 1.7422\n",
      "Epoch 26/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7888 - loss: 1.1129 - val_accuracy: 0.6477 - val_loss: 1.6814\n",
      "Epoch 27/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 4ms/step - accuracy: 0.7909 - loss: 1.0977 - val_accuracy: 0.6197 - val_loss: 1.8437\n",
      "Epoch 28/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7750 - loss: 1.1373 - val_accuracy: 0.6175 - val_loss: 1.8563\n",
      "Epoch 29/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7884 - loss: 1.1443 - val_accuracy: 0.6254 - val_loss: 1.7933\n",
      "Epoch 30/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7736 - loss: 1.1650 - val_accuracy: 0.6053 - val_loss: 1.8382\n",
      "Epoch 31/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7841 - loss: 1.1517 - val_accuracy: 0.6197 - val_loss: 1.8281\n",
      "Epoch 32/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7711 - loss: 1.1590 - val_accuracy: 0.6319 - val_loss: 1.7508\n",
      "Epoch 33/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8103 - loss: 1.0762 - val_accuracy: 0.6017 - val_loss: 1.8529\n",
      "Epoch 34/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7696 - loss: 1.1864 - val_accuracy: 0.6039 - val_loss: 1.9320\n",
      "Epoch 35/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7800 - loss: 1.1683 - val_accuracy: 0.6046 - val_loss: 1.8572\n",
      "Epoch 36/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7944 - loss: 1.1188 - val_accuracy: 0.6132 - val_loss: 1.8586\n",
      "Epoch 37/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7857 - loss: 1.1204 - val_accuracy: 0.6154 - val_loss: 1.7803\n",
      "Epoch 38/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7884 - loss: 1.1271 - val_accuracy: 0.6254 - val_loss: 1.7674\n",
      "Epoch 39/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7980 - loss: 1.1001 - val_accuracy: 0.6370 - val_loss: 1.8327\n",
      "Epoch 40/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7874 - loss: 1.1218 - val_accuracy: 0.6262 - val_loss: 1.8205\n",
      "Epoch 41/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8104 - loss: 1.0763 - val_accuracy: 0.6197 - val_loss: 1.8896\n",
      "Epoch 42/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7836 - loss: 1.1179 - val_accuracy: 0.6118 - val_loss: 1.8370\n",
      "Epoch 43/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7905 - loss: 1.0841 - val_accuracy: 0.6326 - val_loss: 1.8489\n",
      "Epoch 44/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7972 - loss: 1.0944 - val_accuracy: 0.6276 - val_loss: 1.8524\n",
      "Epoch 45/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7840 - loss: 1.1471 - val_accuracy: 0.6520 - val_loss: 1.7942\n",
      "Epoch 46/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8091 - loss: 1.0514 - val_accuracy: 0.6139 - val_loss: 1.8187\n",
      "Epoch 47/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8016 - loss: 1.0794 - val_accuracy: 0.6341 - val_loss: 1.8843\n",
      "Epoch 48/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7999 - loss: 1.0648 - val_accuracy: 0.6485 - val_loss: 1.7315\n",
      "Epoch 49/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8119 - loss: 1.0654 - val_accuracy: 0.6262 - val_loss: 1.8305\n",
      "Epoch 50/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 4ms/step - accuracy: 0.8149 - loss: 1.0298 - val_accuracy: 0.6463 - val_loss: 1.8008\n",
      "Epoch 51/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 7ms/step - accuracy: 0.8036 - loss: 1.0771 - val_accuracy: 0.6219 - val_loss: 1.8563\n",
      "Epoch 52/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8115 - loss: 1.0371 - val_accuracy: 0.6463 - val_loss: 1.7375\n",
      "Epoch 53/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 4ms/step - accuracy: 0.8109 - loss: 1.0397 - val_accuracy: 0.6377 - val_loss: 1.7935\n",
      "Epoch 54/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8122 - loss: 1.0426 - val_accuracy: 0.6470 - val_loss: 1.8285\n",
      "Epoch 55/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8144 - loss: 1.0332 - val_accuracy: 0.6441 - val_loss: 1.8021\n",
      "Epoch 56/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8126 - loss: 1.0741 - val_accuracy: 0.6312 - val_loss: 1.8910\n",
      "Epoch 57/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7840 - loss: 1.1292 - val_accuracy: 0.6355 - val_loss: 1.8861\n",
      "Epoch 58/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8179 - loss: 1.0336 - val_accuracy: 0.6341 - val_loss: 1.9678\n",
      "Epoch 59/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8161 - loss: 1.0324 - val_accuracy: 0.6262 - val_loss: 1.8549\n",
      "Epoch 60/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7937 - loss: 1.0867 - val_accuracy: 0.6132 - val_loss: 1.9866\n",
      "Epoch 61/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8205 - loss: 1.0296 - val_accuracy: 0.6405 - val_loss: 1.8488\n",
      "Epoch 62/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8198 - loss: 1.0115 - val_accuracy: 0.6032 - val_loss: 1.8631\n",
      "Epoch 63/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8063 - loss: 1.0490 - val_accuracy: 0.6413 - val_loss: 1.8017\n",
      "Epoch 64/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8174 - loss: 0.9997 - val_accuracy: 0.6197 - val_loss: 1.8436\n",
      "Epoch 65/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8034 - loss: 1.0605 - val_accuracy: 0.6075 - val_loss: 1.9614\n",
      "Epoch 66/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8151 - loss: 1.0273 - val_accuracy: 0.6183 - val_loss: 1.8458\n",
      "Epoch 67/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8037 - loss: 1.0240 - val_accuracy: 0.6240 - val_loss: 1.7883\n",
      "Epoch 68/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8204 - loss: 1.0191 - val_accuracy: 0.6384 - val_loss: 1.7944\n",
      "Epoch 69/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8005 - loss: 1.0109 - val_accuracy: 0.6427 - val_loss: 1.8350\n",
      "Epoch 70/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8209 - loss: 1.0010 - val_accuracy: 0.6283 - val_loss: 1.8672\n",
      "Epoch 71/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8353 - loss: 0.9611 - val_accuracy: 0.6183 - val_loss: 1.9225\n",
      "Epoch 72/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.7982 - loss: 1.0620 - val_accuracy: 0.6240 - val_loss: 1.8110\n",
      "Epoch 73/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8223 - loss: 1.0142 - val_accuracy: 0.6254 - val_loss: 1.8390\n",
      "Epoch 74/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8102 - loss: 1.0546 - val_accuracy: 0.6219 - val_loss: 1.8163\n",
      "Epoch 75/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8171 - loss: 1.0146 - val_accuracy: 0.6305 - val_loss: 1.8273\n",
      "Epoch 76/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8333 - loss: 0.9782 - val_accuracy: 0.6334 - val_loss: 1.9018\n",
      "Epoch 77/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8202 - loss: 1.0168 - val_accuracy: 0.6276 - val_loss: 1.9746\n",
      "Epoch 78/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8128 - loss: 1.0271 - val_accuracy: 0.6384 - val_loss: 1.8262\n",
      "Epoch 79/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8226 - loss: 1.0072 - val_accuracy: 0.6233 - val_loss: 1.9870\n",
      "Epoch 80/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8169 - loss: 1.0239 - val_accuracy: 0.6290 - val_loss: 1.9001\n",
      "Epoch 81/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8245 - loss: 0.9803 - val_accuracy: 0.6362 - val_loss: 1.9038\n",
      "Epoch 82/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8173 - loss: 1.0129 - val_accuracy: 0.6283 - val_loss: 1.9385\n",
      "Epoch 83/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8154 - loss: 1.0140 - val_accuracy: 0.6226 - val_loss: 1.8552\n",
      "Epoch 84/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8329 - loss: 0.9534 - val_accuracy: 0.6240 - val_loss: 1.9500\n",
      "Epoch 85/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8227 - loss: 0.9952 - val_accuracy: 0.6197 - val_loss: 1.8474\n",
      "Epoch 86/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8186 - loss: 1.0049 - val_accuracy: 0.6262 - val_loss: 1.8873\n",
      "Epoch 87/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8273 - loss: 0.9724 - val_accuracy: 0.6456 - val_loss: 1.8459\n",
      "Epoch 88/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8363 - loss: 0.9538 - val_accuracy: 0.6434 - val_loss: 1.8749\n",
      "Epoch 89/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8385 - loss: 0.9139 - val_accuracy: 0.6434 - val_loss: 1.8689\n",
      "Epoch 90/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8270 - loss: 0.9827 - val_accuracy: 0.6290 - val_loss: 1.9017\n",
      "Epoch 91/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8333 - loss: 0.9743 - val_accuracy: 0.6312 - val_loss: 1.8886\n",
      "Epoch 92/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8175 - loss: 1.0250 - val_accuracy: 0.6384 - val_loss: 1.9568\n",
      "Epoch 93/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8272 - loss: 0.9904 - val_accuracy: 0.6513 - val_loss: 1.8937\n",
      "Epoch 94/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8328 - loss: 0.9656 - val_accuracy: 0.6334 - val_loss: 1.9626\n",
      "Epoch 95/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8325 - loss: 0.9645 - val_accuracy: 0.6068 - val_loss: 1.9343\n",
      "Epoch 96/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8241 - loss: 0.9700 - val_accuracy: 0.6326 - val_loss: 1.9275\n",
      "Epoch 97/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8257 - loss: 0.9839 - val_accuracy: 0.6334 - val_loss: 1.9277\n",
      "Epoch 98/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8206 - loss: 0.9919 - val_accuracy: 0.6362 - val_loss: 1.8887\n",
      "Epoch 99/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8238 - loss: 0.9913 - val_accuracy: 0.6175 - val_loss: 1.9874\n",
      "Epoch 100/100\n",
      "\u001b[1m87/87\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step - accuracy: 0.8186 - loss: 0.9961 - val_accuracy: 0.6269 - val_loss: 1.8465\n",
      "\u001b[1m55/55\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 1ms/step - accuracy: 0.6354 - loss: 1.7091\n",
      "Test Accuracy: 0.6388729214668274\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "from sklearn.metrics import confusion_matrix, classification_report\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Dropout\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras import regularizers\n",
    "import tensorflow as tf\n",
    "\n",
    "# For reproducibility\n",
    "np.random.seed(42)\n",
    "tf.random.set_seed(42)\n",
    "\n",
    "# Assuming X_train, X_test, y_train, y_test are already defined.\n",
    "# Also assume that y are integer-encoded class labels.\n",
    "\n",
    "num_features = X_train.shape[1]\n",
    "num_classes = len(np.unique(y_train))\n",
    "\n",
    "# Build a simple feedforward neural network.\n",
    "model = Sequential([\n",
    "    Dense(128, activation='relu', input_dim=num_features, kernel_regularizer=regularizers.l2(0.01)), \n",
    "    Dropout(0.1),\n",
    "    Dense(112, activation='relu'),\n",
    "    Dropout(0.1),\n",
    "    Dense(num_classes, activation='softmax')\n",
    "])\n",
    "\n",
    "model.compile(optimizer='adam',\n",
    "              loss='sparse_categorical_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "# Use early stopping to prevent overfitting.\n",
    "early_stop = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\n",
    "\n",
    "# Train the model.\n",
    "history = model.fit(X_train, y_train, epochs=100, batch_size=64,\n",
    "                    validation_split=0.2)\n",
    "\n",
    "# Evaluate on the test set.\n",
    "test_loss, test_acc = model.evaluate(X_test, y_test)\n",
    "print(\"Test Accuracy:\", test_acc)\n",
    "\n",
    "best_model = model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m52/52\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 912us/step\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 61800 characters]",
      "text/plain": [
       "<Figure size 800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Confusion Matrix\n",
    "# Get predictions on the test set.\n",
    "y_pred = np.argmax(best_model.predict(X_test), axis=1)\n",
    "\n",
    "decoded_y_test = le.inverse_transform(y_test)\n",
    "decoded_y_pred = le.inverse_transform(y_pred)\n",
    "\n",
    "# Create confusion matrix.\n",
    "cm = confusion_matrix(decoded_y_test, decoded_y_pred)\n",
    "\n",
    "plt.figure(figsize=(8,6))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='viridis')\n",
    "plt.xlabel('Predicted')\n",
    "plt.ylabel('True')\n",
    "plt.title('Confusion Matrix')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 73188 characters]",
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot training vs. validation loss and accuracy.\n",
    "plt.figure(figsize=(12,5))\n",
    "\n",
    "# Loss Plot\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.plot(history.history['loss'], label='Training Loss')\n",
    "plt.plot(history.history['val_loss'], label='Validation Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training vs. Validation Loss')\n",
    "plt.legend()\n",
    "\n",
    "# Accuracy Plot\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.plot(history.history['accuracy'], label='Training Accuracy')\n",
    "plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.title('Training vs. Validation Accuracy')\n",
    "plt.legend()\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a DataFrame for features. If X has many features, consider selecting a subset.\n",
    "df_features = pd.DataFrame(X, columns=[f'feature_{i}' for i in range(X.shape[1])])\n",
    "\n",
    "# Compute the correlation matrix.\n",
    "corr = df_features.corr()\n",
    "\n",
    "plt.figure(figsize=(12,10))\n",
    "sns.heatmap(corr, cmap='coolwarm', annot=False)\n",
    "plt.title('Feature Correlation Matrix')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'best_model' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 22\u001b[39m\n\u001b[32m     19\u001b[39m         \u001b[38;5;28;01mreturn\u001b[39;00m np.mean(y_pred == y)\n\u001b[32m     21\u001b[39m \u001b[38;5;66;03m# Wrap our trained model.\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m22\u001b[39m wrapper = KerasClassifierWrapper(\u001b[43mbest_model\u001b[49m)\n\u001b[32m     24\u001b[39m \u001b[38;5;66;03m# Compute permutation importance on the test set.\u001b[39;00m\n\u001b[32m     25\u001b[39m result = permutation_importance(wrapper, X_test, y_test, n_repeats=\u001b[32m10\u001b[39m,\n\u001b[32m     26\u001b[39m                                 random_state=\u001b[32m42\u001b[39m, scoring=\u001b[33m'\u001b[39m\u001b[33maccuracy\u001b[39m\u001b[33m'\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'best_model' is not defined"
     ]
    }
   ],
   "source": [
    "from sklearn.base import BaseEstimator, ClassifierMixin\n",
    "from sklearn.inspection import permutation_importance\n",
    "\n",
    "# Wrapper for the Keras model to work with sklearn's permutation_importance.\n",
    "class KerasClassifierWrapper(BaseEstimator, ClassifierMixin):\n",
    "    def __init__(self, model):\n",
    "        self.model = model\n",
    "        \n",
    "    def fit(self, X, y):\n",
    "        # Model is already trained.\n",
    "        return self\n",
    "    \n",
    "    def predict(self, X):\n",
    "        preds = self.model.predict(X)\n",
    "        return np.argmax(preds, axis=1)\n",
    "    \n",
    "    def score(self, X, y):\n",
    "        y_pred = self.predict(X)\n",
    "        return np.mean(y_pred == y)\n",
    "\n",
    "# Wrap our trained model.\n",
    "wrapper = KerasClassifierWrapper(best_model)\n",
    "\n",
    "# Compute permutation importance on the test set.\n",
    "result = permutation_importance(wrapper, X_test, y_test, n_repeats=10,\n",
    "                                random_state=42, scoring='accuracy')\n",
    "importance_means = result.importances_mean\n",
    "\n",
    "# Plot the permutation importance.\n",
    "plt.figure(figsize=(12, 6))\n",
    "features = [f'feature_{i}' for i in range(X.shape[1])]\n",
    "plt.bar(features, importance_means)\n",
    "plt.xticks(rotation=90)\n",
    "plt.xlabel('Feature')\n",
    "plt.ylabel('Mean Importance')\n",
    "plt.title('Permutation Feature Importance')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.11.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
