From 90fd556b277e6ccca78374024efc4f4d791c7d26 Mon Sep 17 00:00:00 2001 From: SUJALGOYALL Date: Tue, 28 Oct 2025 00:43:40 +0530 Subject: [PATCH] fix(docs): minor update in EEG Arithmetic (re-PR for tracking) Closes #6 --- lstm-hyperparameter-tuning.ipynb | 712 ++++++++++++++++++++++++++++++- 1 file changed, 711 insertions(+), 1 deletion(-) diff --git a/lstm-hyperparameter-tuning.ipynb b/lstm-hyperparameter-tuning.ipynb index a5aa175..8bce587 100644 --- a/lstm-hyperparameter-tuning.ipynb +++ b/lstm-hyperparameter-tuning.ipynb @@ -1 +1,711 @@ -{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"accelerator":"GPU","colab":{"gpuType":"T4","provenance":[]},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":13406794,"sourceType":"datasetVersion","datasetId":8508427}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#### Import Required Libraries","metadata":{"id":"vglX3vOPxy6w"}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (classification_report, confusion_matrix,\n accuracy_score, precision_recall_fscore_support)\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, models, callbacks\nfrom tensorflow.keras.utils import plot_model\n\n# Set random seeds for reproducibility\nnp.random.seed(42)\ntf.random.set_seed(42)","metadata":{"id":"m7uJEJA1x0bL","trusted":true,"execution":{"iopub.status.busy":"2025-10-18T10:16:49.872512Z","iopub.execute_input":"2025-10-18T10:16:49.873048Z","iopub.status.idle":"2025-10-18T10:17:04.793070Z","shell.execute_reply.started":"2025-10-18T10:16:49.873025Z","shell.execute_reply":"2025-10-18T10:17:04.792505Z"}},"outputs":[{"name":"stderr","text":"2025-10-18 10:16:52.757443: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\nWARNING: All log messages before absl::InitializeLog() is called are written to STDERR\nE0000 00:00:1760782613.004187 37 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\nE0000 00:00:1760782613.083030 37 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n","output_type":"stream"}],"execution_count":1},{"cell_type":"code","source":"print(\"Loading preprocessed data...\")\ndata = np.load('/kaggle/input/ecg-mitdb-processed/ecg_mitdb_processed.npz')\nX = data['X']\ny = data['y']\nlabel_names = data['label_names']\n\nprint(f\"Data shape: X={X.shape}, y={y.shape}\")\nprint(f\"Classes: {label_names}\")\n","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Y-8J1FOlx4j3","outputId":"b7aa49a8-61ee-4726-d46e-4badf0c17a87","trusted":true,"execution":{"iopub.status.busy":"2025-10-18T10:17:04.794198Z","iopub.execute_input":"2025-10-18T10:17:04.794624Z","iopub.status.idle":"2025-10-18T10:17:10.393915Z","shell.execute_reply.started":"2025-10-18T10:17:04.794606Z","shell.execute_reply":"2025-10-18T10:17:10.393262Z"}},"outputs":[{"name":"stdout","text":"Loading preprocessed data...\nData shape: X=(112559, 250, 1), y=(112559, 5)\nClasses: ['F' 'N' 'Q' 'S' 'V']\n","output_type":"stream"}],"execution_count":2},{"cell_type":"code","source":"print(\"\\nSplitting data...\")\n# First split: 80% train+val, 20% test\nX_temp, X_test, y_temp, y_test = train_test_split(\n X, y, test_size=0.2, random_state=42, stratify=y.argmax(axis=1)\n)\n\n# Second split: 80% train, 20% validation (of the 80%)\nX_train, X_val, y_train, y_val = train_test_split(\n X_temp, y_temp, test_size=0.2, random_state=42, stratify=y_temp.argmax(axis=1)\n)\n\nprint(f\"Train set: {X_train.shape[0]} samples\")\nprint(f\"Validation set: {X_val.shape[0]} samples\")\nprint(f\"Test set: {X_test.shape[0]} samples\")\n","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"aZGPXypTx-N5","outputId":"1ce032cd-c734-43a4-c75a-e3732020573d","trusted":true,"execution":{"iopub.status.busy":"2025-10-18T10:17:10.394707Z","iopub.execute_input":"2025-10-18T10:17:10.394972Z","iopub.status.idle":"2025-10-18T10:17:10.625868Z","shell.execute_reply.started":"2025-10-18T10:17:10.394947Z","shell.execute_reply":"2025-10-18T10:17:10.625222Z"}},"outputs":[{"name":"stdout","text":"\nSplitting data...\nTrain set: 72037 samples\nValidation set: 18010 samples\nTest set: 22512 samples\n","output_type":"stream"}],"execution_count":3},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===========================================================\n# 🎯 Focused Bayesian LSTM Hyperparameter Scan (Quick Version)\n# ===========================================================\n\nimport keras_tuner as kt\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, callbacks, models\n\nprint(\"\\n🔧 Starting Focused Quick Bayesian Scan...\")\n\n# ----------------------------\n# Early stopping & LR reduction\n# ----------------------------\nearly_stop = callbacks.EarlyStopping(\n monitor='val_loss',\n patience=2,\n restore_best_weights=True\n)\n\nreduce_lr = callbacks.ReduceLROnPlateau(\n monitor='val_loss',\n factor=0.5,\n patience=2,\n min_lr=1e-5\n)\n\n# ----------------------------\n# Build model\n# ----------------------------\ndef build_lstm_model(hp):\n model = keras.Sequential()\n\n # --- First LSTM layer ---\n units1 = hp.Int('lstm1_units', 96, 160, step=32)\n bidir = hp.Boolean('bidirectional', default=False)\n dropout1 = hp.Float('dropout1', 0.2, 0.4, step=0.1)\n\n lstm1 = layers.LSTM(units1, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2]))\n if bidir:\n lstm1 = layers.Bidirectional(lstm1)\n model.add(lstm1)\n model.add(layers.Dropout(dropout1))\n model.add(layers.BatchNormalization())\n\n # --- Second LSTM layer ---\n units2 = hp.Int('lstm2_units', 24, 40, step=8)\n dropout2 = hp.Float('dropout2', 0.2, 0.4, step=0.1)\n lstm2 = layers.LSTM(units2)\n if bidir:\n lstm2 = layers.Bidirectional(lstm2)\n model.add(lstm2)\n model.add(layers.Dropout(dropout2))\n model.add(layers.BatchNormalization())\n\n # --- Dense layers ---\n dense1 = hp.Int('dense1_units', 48, 80, step=16)\n dense2 = hp.Int('dense2_units', 24, 32, step=8)\n model.add(layers.Dense(dense1, activation='relu'))\n model.add(layers.Dropout(0.3))\n model.add(layers.Dense(dense2, activation='relu'))\n model.add(layers.Dropout(0.2))\n\n # --- Output layer ---\n model.add(layers.Dense(y_train.shape[1], activation='softmax'))\n\n # --- Optimizer ---\n lr = hp.Choice('learning_rate', [1e-3, 7e-4, 5e-4])\n opt = keras.optimizers.Adam(learning_rate=lr)\n\n model.compile(\n optimizer=opt,\n loss='categorical_crossentropy',\n metrics=['accuracy']\n )\n\n return model\n\n\n# ----------------------------\n# Focused Bayesian tuner (quick version)\n# ----------------------------\ntuner = kt.BayesianOptimization(\n build_lstm_model,\n objective='val_accuracy',\n max_trials=12, \n num_initial_points=2, \n overwrite=True\n)\n\n# ----------------------------\n# Search (short epochs to estimate trend)\n# ----------------------------\ntuner.search( \n X_train, y_train,\n validation_data=(X_val, y_val),\n epochs=10,\n batch_size=128,\n callbacks=[early_stop, reduce_lr],\n verbose=1\n)\n\n# ----------------------------\n# Get best hyperparameters\n# ----------------------------\nbest_hp = tuner.get_best_hyperparameters(1)[0]\nbest_model = tuner.get_best_models(1)[0]\n\nprint(\"\\n🏆 Best Hyperparameters (Focused Quick Scan):\")\nfor k, v in best_hp.values.items():\n print(f\"{k}: {v}\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T10:17:10.627188Z","iopub.execute_input":"2025-10-18T10:17:10.627405Z","iopub.status.idle":"2025-10-18T11:17:31.906590Z","shell.execute_reply.started":"2025-10-18T10:17:10.627389Z","shell.execute_reply":"2025-10-18T11:17:31.905729Z"}},"outputs":[{"name":"stdout","text":"Trial 12 Complete [00h 05m 18s]\nval_accuracy: 0.9738478660583496\n\nBest val_accuracy So Far: 0.9738478660583496\nTotal elapsed time: 01h 00m 17s\n\n🏆 Best Hyperparameters (Focused Quick Scan):\nlstm1_units: 96\nbidirectional: True\ndropout1: 0.4\nlstm2_units: 40\ndropout2: 0.2\ndense1_units: 48\ndense2_units: 32\nlearning_rate: 0.001\n","output_type":"stream"},{"name":"stderr","text":"/usr/local/lib/python3.11/dist-packages/keras/src/saving/saving_lib.py:757: UserWarning: Skipping variable loading for optimizer 'adam', because it has 2 variables whereas the saved optimizer has 46 variables. \n saveable.load_own_variables(weights_store.get(inner_path))\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"best_hp = tuner.get_best_hyperparameters(1)[0]\nbest_model = tuner.get_best_models(1)[0]\n\nprint(\"\\n🏆 Best Hyperparameters (Quick Scan):\")\nfor k, v in best_hp.values.items():\n print(f\"{k}: {v}\")\n \n# Callbacks\n# ----------------------------\nearly_stop = callbacks.EarlyStopping(\n monitor='val_loss', patience=5, restore_best_weights=True\n)\nreduce_lr = callbacks.ReduceLROnPlateau(\n monitor='val_loss', factor=0.5, patience=3, min_lr=1e-5\n)\n\n\ncheckpoint_cb = callbacks.ModelCheckpoint(\n \"best_lstm_model.h5\", \n monitor=\"val_loss\", \n save_best_only=True, \n mode=\"min\", \n verbose=1\n)\n\n# Final Training with adaptive callbacks\nhistory = best_model.fit(\n X_train, y_train,\n validation_data=(X_val, y_val),\n epochs=50,\n batch_size=128,\n callbacks=[early_stop, reduce_lr, checkpoint_cb], \n verbose=1\n)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T11:17:31.907353Z","iopub.execute_input":"2025-10-18T11:17:31.907583Z","iopub.status.idle":"2025-10-18T11:32:05.985841Z","shell.execute_reply.started":"2025-10-18T11:17:31.907565Z","shell.execute_reply":"2025-10-18T11:32:05.985301Z"}},"outputs":[{"name":"stdout","text":"\n🏆 Best Hyperparameters (Quick Scan):\nlstm1_units: 96\nbidirectional: True\ndropout1: 0.4\nlstm2_units: 40\ndropout2: 0.2\ndense1_units: 48\ndense2_units: 32\nlearning_rate: 0.001\nEpoch 1/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9664 - loss: 0.1198\nEpoch 1: val_loss improved from inf to 0.10141, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 58ms/step - accuracy: 0.9664 - loss: 0.1198 - val_accuracy: 0.9707 - val_loss: 0.1014 - learning_rate: 0.0010\nEpoch 2/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9696 - loss: 0.1091\nEpoch 2: val_loss improved from 0.10141 to 0.09198, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9696 - loss: 0.1091 - val_accuracy: 0.9721 - val_loss: 0.0920 - learning_rate: 0.0010\nEpoch 3/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 49ms/step - accuracy: 0.9701 - loss: 0.1064\nEpoch 3: val_loss did not improve from 0.09198\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9701 - loss: 0.1064 - val_accuracy: 0.9700 - val_loss: 0.1053 - learning_rate: 0.0010\nEpoch 4/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9697 - loss: 0.1074\nEpoch 4: val_loss did not improve from 0.09198\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9697 - loss: 0.1074 - val_accuracy: 0.9722 - val_loss: 0.0940 - learning_rate: 0.0010\nEpoch 5/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9721 - loss: 0.0998\nEpoch 5: val_loss improved from 0.09198 to 0.08294, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9721 - loss: 0.0998 - val_accuracy: 0.9760 - val_loss: 0.0829 - learning_rate: 0.0010\nEpoch 6/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9718 - loss: 0.0991\nEpoch 6: val_loss improved from 0.08294 to 0.07310, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9718 - loss: 0.0991 - val_accuracy: 0.9792 - val_loss: 0.0731 - learning_rate: 0.0010\nEpoch 7/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9736 - loss: 0.0909\nEpoch 7: val_loss did not improve from 0.07310\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9736 - loss: 0.0909 - val_accuracy: 0.9774 - val_loss: 0.0767 - learning_rate: 0.0010\nEpoch 8/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9748 - loss: 0.0889\nEpoch 8: val_loss did not improve from 0.07310\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9748 - loss: 0.0889 - val_accuracy: 0.9757 - val_loss: 0.0875 - learning_rate: 0.0010\nEpoch 9/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9746 - loss: 0.0916\nEpoch 9: val_loss did not improve from 0.07310\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9746 - loss: 0.0916 - val_accuracy: 0.9739 - val_loss: 0.0881 - learning_rate: 0.0010\nEpoch 10/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9769 - loss: 0.0824\nEpoch 10: val_loss improved from 0.07310 to 0.06606, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9769 - loss: 0.0823 - val_accuracy: 0.9810 - val_loss: 0.0661 - learning_rate: 5.0000e-04\nEpoch 11/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9792 - loss: 0.0739\nEpoch 11: val_loss improved from 0.06606 to 0.06340, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9792 - loss: 0.0739 - val_accuracy: 0.9813 - val_loss: 0.0634 - learning_rate: 5.0000e-04\nEpoch 12/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9786 - loss: 0.0712\nEpoch 12: val_loss improved from 0.06340 to 0.06142, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9786 - loss: 0.0711 - val_accuracy: 0.9827 - val_loss: 0.0614 - learning_rate: 5.0000e-04\nEpoch 13/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9816 - loss: 0.0629\nEpoch 13: val_loss improved from 0.06142 to 0.05994, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9816 - loss: 0.0629 - val_accuracy: 0.9827 - val_loss: 0.0599 - learning_rate: 5.0000e-04\nEpoch 14/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9816 - loss: 0.0639\nEpoch 14: val_loss improved from 0.05994 to 0.05945, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9816 - loss: 0.0639 - val_accuracy: 0.9829 - val_loss: 0.0594 - learning_rate: 5.0000e-04\nEpoch 15/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9815 - loss: 0.0629\nEpoch 15: val_loss improved from 0.05945 to 0.05821, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9815 - loss: 0.0629 - val_accuracy: 0.9836 - val_loss: 0.0582 - learning_rate: 5.0000e-04\nEpoch 16/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9824 - loss: 0.0624\nEpoch 16: val_loss did not improve from 0.05821\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9824 - loss: 0.0624 - val_accuracy: 0.9834 - val_loss: 0.0587 - learning_rate: 5.0000e-04\nEpoch 17/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9826 - loss: 0.0594\nEpoch 17: val_loss improved from 0.05821 to 0.05767, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9826 - loss: 0.0594 - val_accuracy: 0.9841 - val_loss: 0.0577 - learning_rate: 5.0000e-04\nEpoch 18/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9831 - loss: 0.0586\nEpoch 18: val_loss did not improve from 0.05767\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9831 - loss: 0.0586 - val_accuracy: 0.9826 - val_loss: 0.0598 - learning_rate: 5.0000e-04\nEpoch 19/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9837 - loss: 0.0564\nEpoch 19: val_loss improved from 0.05767 to 0.05402, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9837 - loss: 0.0564 - val_accuracy: 0.9850 - val_loss: 0.0540 - learning_rate: 5.0000e-04\nEpoch 20/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9841 - loss: 0.0556\nEpoch 20: val_loss did not improve from 0.05402\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9841 - loss: 0.0556 - val_accuracy: 0.9835 - val_loss: 0.0631 - learning_rate: 5.0000e-04\nEpoch 21/50\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9837 - loss: 0.0583\nEpoch 21: val_loss did not improve from 0.05402\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9837 - loss: 0.0583 - val_accuracy: 0.9795 - val_loss: 0.0684 - learning_rate: 5.0000e-04\nEpoch 22/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9835 - loss: 0.0554\nEpoch 22: val_loss did not improve from 0.05402\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9835 - loss: 0.0554 - val_accuracy: 0.9823 - val_loss: 0.0616 - learning_rate: 5.0000e-04\nEpoch 23/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9845 - loss: 0.0525\nEpoch 23: val_loss improved from 0.05402 to 0.05138, saving model to best_lstm_model.h5\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9845 - loss: 0.0525 - val_accuracy: 0.9861 - val_loss: 0.0514 - learning_rate: 2.5000e-04\nEpoch 24/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 51ms/step - accuracy: 0.9868 - loss: 0.0451\nEpoch 24: val_loss did not improve from 0.05138\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 56ms/step - accuracy: 0.9868 - loss: 0.0451 - val_accuracy: 0.9863 - val_loss: 0.0530 - learning_rate: 2.5000e-04\nEpoch 25/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9874 - loss: 0.0438\nEpoch 25: val_loss did not improve from 0.05138\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9874 - loss: 0.0438 - val_accuracy: 0.9857 - val_loss: 0.0544 - learning_rate: 2.5000e-04\nEpoch 26/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9873 - loss: 0.0429\nEpoch 26: val_loss did not improve from 0.05138\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9873 - loss: 0.0429 - val_accuracy: 0.9856 - val_loss: 0.0546 - learning_rate: 2.5000e-04\nEpoch 27/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9878 - loss: 0.0421\nEpoch 27: val_loss did not improve from 0.05138\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9878 - loss: 0.0421 - val_accuracy: 0.9865 - val_loss: 0.0515 - learning_rate: 1.2500e-04\nEpoch 28/50\n\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9885 - loss: 0.0376\nEpoch 28: val_loss did not improve from 0.05138\n\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9885 - loss: 0.0376 - val_accuracy: 0.9867 - val_loss: 0.0527 - learning_rate: 1.2500e-04\n","output_type":"stream"}],"execution_count":5},{"cell_type":"code","source":"print(\"\\n\" + \"=\"*70)\nprint(\"EVALUATION ON TEST SET\")\nprint(\"=\"*70)\n\n\ny_pred_probs = best_model.predict(X_test)\ny_pred = np.argmax(y_pred_probs, axis=1)\ny_true = np.argmax(y_test, axis=1)\n\n# Evaluate on test data \n\ntest_loss, test_acc = best_model.evaluate(X_test, y_test, verbose=0)\nprint(f\"\\nTest Loss: {test_loss:.4f}\")\nprint(f\"Test Accuracy: {test_acc:.4f}\")\n\n# Classification Report\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_true, y_pred, target_names=label_names, digits=4))\n\n# Per-class metrics\n\nprecision, recall, f1, support = precision_recall_fscore_support(y_true, y_pred)\nprint(\"\\nPer-Class Detailed Metrics:\")\nprint(\"-\" * 70)\nprint(f\"{'Class':<10} {'Precision':<12} {'Recall':<12} {'F1-Score':<12} {'Support':<10}\")\nprint(\"-\" * 70)\nfor i, label in enumerate(label_names):\n print(f\"{label:<10} {precision[i]:<12.4f} {recall[i]:<12.4f} {f1[i]:<12.4f} {support[i]:<10}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T11:32:05.986845Z","iopub.execute_input":"2025-10-18T11:32:05.987117Z","iopub.status.idle":"2025-10-18T11:32:25.170918Z","shell.execute_reply.started":"2025-10-18T11:32:05.987074Z","shell.execute_reply":"2025-10-18T11:32:25.170136Z"}},"outputs":[{"name":"stdout","text":"\n======================================================================\nEVALUATION ON TEST SET\n======================================================================\n\u001b[1m704/704\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m9s\u001b[0m 13ms/step\n\nTest Loss: 0.0528\nTest Accuracy: 0.9854\n\nClassification Report:\n precision recall f1-score support\n\n F 0.8477 0.8000 0.8232 160\n N 0.9903 0.9951 0.9927 18119\n Q 0.9789 0.9767 0.9778 2230\n S 0.9510 0.8381 0.8910 556\n V 0.9597 0.9537 0.9567 1447\n\n accuracy 0.9854 22512\n macro avg 0.9455 0.9127 0.9283 22512\nweighted avg 0.9852 0.9854 0.9852 22512\n\n\nPer-Class Detailed Metrics:\n----------------------------------------------------------------------\nClass Precision Recall F1-Score Support \n----------------------------------------------------------------------\nF 0.8477 0.8000 0.8232 160 \nN 0.9903 0.9951 0.9927 18119 \nQ 0.9789 0.9767 0.9778 2230 \nS 0.9510 0.8381 0.8910 556 \nV 0.9597 0.9537 0.9567 1447 \n","output_type":"stream"}],"execution_count":6},{"cell_type":"code","source":"print(\"\\n\" + \"=\"*70)\nprint(\"CONFUSION MATRIX\")\nprint(\"=\"*70)\n\nfrom sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Compute confusion matrix\n\ncm = confusion_matrix(y_true, y_pred)\n\n# Print numeric matrix\n\nprint(\"\\nConfusion Matrix (raw counts):\")\nprint(cm)\n\n# Plot without saving\n\nplt.figure(figsize=(10, 8))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\nxticklabels=label_names, yticklabels=label_names)\nplt.title(\"Confusion Matrix Heatmap\", fontsize=14)\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"True Label\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T11:32:25.171777Z","iopub.execute_input":"2025-10-18T11:32:25.172135Z","iopub.status.idle":"2025-10-18T11:32:25.550400Z","shell.execute_reply.started":"2025-10-18T11:32:25.172111Z","shell.execute_reply":"2025-10-18T11:32:25.549714Z"}},"outputs":[{"name":"stdout","text":"\n======================================================================\nCONFUSION MATRIX\n======================================================================\n\nConfusion Matrix (raw counts):\n[[ 128 25 0 0 7]\n [ 10 18031 32 21 25]\n [ 0 31 2178 1 20]\n [ 0 80 4 466 6]\n [ 13 41 11 2 1380]]\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"
","image/png":"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R44cWr169X3fu3v37g5/xl1dXW2dDCbO/AwAeLJIVgGkGYcPH1Z0dLReeOEFZcmSJdH2mjVrStJ9n4n67LPPJlp37x/RV69eTdE472natKkkqVKlSuratasWLVqkixcvJmnfqKgo/fHHHypatOh9E/onea4VKlRQ+fLlNWPGDEl3J1ZydXVVixYt/nG/3bt36+2331bBggXl6upqu6dyyZIlunPnTpI/C3svvPBCsvd577339Morr2jOnDlav369OnXqpNdeey3Zx3n33XdltVrvu9z7bOxt3rxZknTu3DkNGjQo0XL48GFJsv2/JF2/fl0DBw5UuXLllC1bNttn1rhxY0l37x1Nrvu1Vd/74uZB2+Lj43Xu3DmH9T/88IOCgoKUN29eZcyYURaLRS4uLoqKinpgXFWrVk20LjAwUBkzZkx07zIAIP3hnlUARvL29tbhw4d1+vRphyrgP4mKipKkB97jeu8f4PfG2cuRI0eidRkz3v0VGR8fn6T3T64333xTixcv1ueff67JkydrwoQJslgsqlmzpkaPHv2P92aadq7t2rXTe++9p9WrV+vbb79VgwYN/nH23U2bNqlWrVqSpDp16qhYsWK25Gvx4sXas2ePYmJikh3Hw+5vvh+LxaLg4GCtWLFCktStW7dkH+NRXL58WdLde5eXLVv2wHE3b96UdLdzoEaNGtq5c6cqVKigli1bKk+ePMqYMaNOnDihWbNmPdJn9k/Xwz9ts698jx49Wh988IHy5s2rOnXqKH/+/LaOgbFjxz4wrvv9vDJkyKA8efLo2rVryT4XAEDaQrIKwEiVK1fW+vXrtWbNGltS8zD3/mH994rPPfdai+/3D/CUcO+5o/d7puiD/uHdsGFDW7vxb7/9ph9++EHTpk1T3bp1dfjwYeXMmfO++zn7XP+uefPm6t27t9q0aaOoqCiFhIT84/hPPvlEMTEx+uWXXxJNeLV582bt2bPnkeKwb4dOqoiICPXu3Vu5c+fWlStX9M4772jjxo3KkCHDI8WQVPd+Nl988YW6du360PE//vijdu7cqZCQEH399dcO27799ltbG/aTFhcXp6FDh8rHx0e7d++Wl5eXbZvVatWoUaMeuO+5c+cSfRkVHx+vS5cuPdIXDwCAtIU2YABGatOmjTJkyKApU6bowoUL/zj2XtXm3uNbtm3bplu3biUad++x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V04TJky47/azZ886LNOnT5fFYlHjxo0dxg0ZMsRhXLdu3WzboqKiVKdOHRUqVEg7duzQp59+qkGDBmnKlCm2MZs2bVKzZs0UEhKiXbt2KTg4WMHBwdq/f3+yzsditVqtydojFbgd6+wIkNbwqC0AAIDkc0+l07lmDujt7BBsbm/59OGD7sNisWjRokUKDg5+4Jjg4GBdv35da9assa0rXLiwevTooR49etx3n0mTJqlfv36KjIyUq6urJKlv375avHixDh8+LElq0qSJbt68qaVLl9r2q1SpksqXL6/Jkycn+RyorAIAAACAPYuLMUtMTIyioqIclpiYmH99iufOndOyZcsUEhKSaNuIESOUJ08eVahQQZ9++qni4uJs28LDw1WtWjVboipJQUFBOnLkiK5cuWIbU7t2bYdjBgUFKTw8PFkxkqwCAAAAgKHCwsLk4eHhsISFhf3r486aNUvZs2dXo0aNHNa/9957+vbbb7Vu3Tq9++67Gj58uD788EPb9sjISOXLl89hn3uvIyMj/3HMve1JlUoL8wAAAADwmBh0D1hoaKh69erlsM7Nze1fH3f69Olq3ry53N3dHdbbv1fZsmXl6uqqd999V2FhYSnyvslBsgoAAAAAhnJzc0vxJPGXX37RkSNHNH/+/IeODQgIUFxcnE6cOCF/f395e3vr3LlzDmPuvfb29rb9//3G3NueVLQBAwAAAEA6Mm3aND377LMqV67cQ8fu3r1bLi4u8vLykiQFBgZq48aNio3936y2q1atkr+/v3LlymUbYz9p070xgYGByYqTyioAAAAA2LOkzprejRs3dOzYMdvriIgI7d69W7lz51bBggUl3X30zMKFCzV69OhE+4eHh2vLli2qWbOmsmfPrvDwcPXs2VMtWrSwJaJvv/22Bg8erJCQEPXp00f79+/XuHHjNGbMGNtxunfvrurVq2v06NGqX7++vv32W23fvt3h8TZJwaNrgCQw6LYFAACAVCPVPrrmxY+cHYLN7U3Dkzx2/fr1qlmzZqL1rVu31syZMyVJU6ZMUY8ePXT27Fl5eHg4jNu5c6c6d+6sw4cPKyYmRn5+fmrZsqV69erl0Iq8d+9edenSRdu2bZOnp6e6deumPn36OBxr4cKF6t+/v06cOKFixYpp1KhRqlevXjLOnGQVSBKSVQAAgOQjWf33kpOspjWp9PIB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Gkue8oPWODsEm92DXnqk/SwWixYtWqTg4OAHjtm2bZteeOEF/fnnnyr439b1woULq0ePHurRo8d995k0aZL69eunyMhIubq6SpL69u2rxYsX6/Dhw5KkJk2a6ObNm1q6dKltv0qVKql8+fKaPHly0s/BmcnqqFGjNHLkSHl7e2v48OFq2LBhihyXZBUpLS0mq3AeklUAQHqRWpPVCoPXPnzQE7K5b+VEE8rer7v075KSrK5evVp16tTR1atXlSPH3ac5FC5cWNHR0YqNjVXBggX19ttvq2fPnrZHx7Vq1UpRUVEOLcbr1q1TrVq1dPnyZeXKlUsFCxZUr169HBLegQMHavHixdqzZ0+Sz92pl0/fvn2VOXNmFS1aVLNmzdKsWbPuO+6HH354wpEBAAAAgPPdb0LZgQMHatCgQf/quNHR0erTp4+aNWtmS1Ql6b333lPFihWVO3dubdq0SaGhoTp79qw+//xzSVJkZKT8/By7MPPly2fblitXLkVGRtrW2Y+JjEzeLV5OTVZbtWrFM4wAAAAAGMWkFCW0b6h69erlsO7fTjoYGxurt956S1arVZMmTXLYZv9eZcuWlaurq959912FhYU98ckOnZqszpw505lvDwAAAABGS0rLb3LcS1T//PNPrV271qGqej8BAQGKi4vTiRMn5O/vL29vb507d85hzL3X3v99vNODxngn85FiTn10DQAAAADgybiXqB49elSrV69Wnjx5HrrP7t275eLiIi8vL0lSYGCgNm7cqNjYWNuYVatWyd/fX7ly5bKNWbPGcZKqVatWKTAwMFnxptJbngEAAADg8UittyreuHFDx44ds72OiIjQ7t27lTt3bvn4+OiNN97Qzp07tXTpUsXHx9vuIc2dO7dcXV0VHh6uLVu2qGbNmsqePbvCw8PVs2dPtWjRwpaIvv322xo8eLBCQkLUp08f7d+/X+PGjdOYMWNs79u9e3dVr15do0ePVv369fXtt99q+/btmjJlSrLOx+mPrnkcmA0YKY3ZgJGSmA0YAJBepNbZgJ8dus7ZIdjsGFAzyWPXr1+vmjUTj2/durUGDRqUaGKke9atW6caNWpo586d6ty5sw4fPqyYmBj5+fmpZcuW6tWrl0Mr8t69e9WlSxdt27ZNnp6e6tatm/r06eNwzIULF6p///46ceKEihUrplGjRqlevXpJPheJ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\n"},"metadata":{}}],"execution_count":7},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vglX3vOPxy6w" + }, + "source": [ + "#### Import Required Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "execution": { + "iopub.execute_input": "2025-10-18T10:16:49.873048Z", + "iopub.status.busy": "2025-10-18T10:16:49.872512Z", + "iopub.status.idle": "2025-10-18T10:17:04.793070Z", + "shell.execute_reply": "2025-10-18T10:17:04.792505Z", + "shell.execute_reply.started": "2025-10-18T10:16:49.873025Z" + }, + "id": "m7uJEJA1x0bL", + "trusted": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-10-18 10:16:52.757443: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] 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:1760782613.004187 37 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n", + "E0000 00:00:1760782613.083030 37 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n" + ] + } + ], + "source": [ + "# Minor doc/comment update for clarity (re-PR for tracking)\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import (classification_report, confusion_matrix,\n", + " accuracy_score, precision_recall_fscore_support)\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "from tensorflow.keras import layers, models, callbacks\n", + "from tensorflow.keras.utils import plot_model\n", + "\n", + "# Set random seeds for reproducibility\n", + "np.random.seed(42)\n", + "tf.random.set_seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2025-10-18T10:17:04.794624Z", + "iopub.status.busy": "2025-10-18T10:17:04.794198Z", + "iopub.status.idle": "2025-10-18T10:17:10.393915Z", + "shell.execute_reply": "2025-10-18T10:17:10.393262Z", + "shell.execute_reply.started": "2025-10-18T10:17:04.794606Z" + }, + "id": "Y-8J1FOlx4j3", + "outputId": "b7aa49a8-61ee-4726-d46e-4badf0c17a87", + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading preprocessed data...\n", + "Data shape: X=(112559, 250, 1), y=(112559, 5)\n", + "Classes: ['F' 'N' 'Q' 'S' 'V']\n" + ] + } + ], + "source": [ + "print(\"Loading preprocessed data...\")\n", + "data = np.load('/kaggle/input/ecg-mitdb-processed/ecg_mitdb_processed.npz')\n", + "X = data['X']\n", + "y = data['y']\n", + "label_names = data['label_names']\n", + "\n", + "print(f\"Data shape: X={X.shape}, y={y.shape}\")\n", + "print(f\"Classes: {label_names}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "execution": { + "iopub.execute_input": "2025-10-18T10:17:10.394972Z", + "iopub.status.busy": "2025-10-18T10:17:10.394707Z", + "iopub.status.idle": "2025-10-18T10:17:10.625868Z", + "shell.execute_reply": "2025-10-18T10:17:10.625222Z", + "shell.execute_reply.started": "2025-10-18T10:17:10.394947Z" + }, + "id": "aZGPXypTx-N5", + "outputId": "1ce032cd-c734-43a4-c75a-e3732020573d", + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Splitting data...\n", + "Train set: 72037 samples\n", + "Validation set: 18010 samples\n", + "Test set: 22512 samples\n" + ] + } + ], + "source": [ + "print(\"\\nSplitting data...\")\n", + "# First split: 80% train+val, 20% test\n", + "X_temp, X_test, y_temp, y_test = train_test_split(\n", + " X, y, test_size=0.2, random_state=42, stratify=y.argmax(axis=1)\n", + ")\n", + "\n", + "# Second split: 80% train, 20% validation (of the 80%)\n", + "X_train, X_val, y_train, y_val = train_test_split(\n", + " X_temp, y_temp, test_size=0.2, random_state=42, stratify=y_temp.argmax(axis=1)\n", + ")\n", + "\n", + "print(f\"Train set: {X_train.shape[0]} samples\")\n", + "print(f\"Validation set: {X_val.shape[0]} samples\")\n", + "print(f\"Test set: {X_test.shape[0]} samples\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2025-10-18T10:17:10.627405Z", + "iopub.status.busy": "2025-10-18T10:17:10.627188Z", + "iopub.status.idle": "2025-10-18T11:17:31.906590Z", + "shell.execute_reply": "2025-10-18T11:17:31.905729Z", + "shell.execute_reply.started": "2025-10-18T10:17:10.627389Z" + }, + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Trial 12 Complete [00h 05m 18s]\n", + "val_accuracy: 0.9738478660583496\n", + "\n", + "Best val_accuracy So Far: 0.9738478660583496\n", + "Total elapsed time: 01h 00m 17s\n", + "\n", + "🏆 Best Hyperparameters (Focused Quick Scan):\n", + "lstm1_units: 96\n", + "bidirectional: True\n", + "dropout1: 0.4\n", + "lstm2_units: 40\n", + "dropout2: 0.2\n", + "dense1_units: 48\n", + "dense2_units: 32\n", + "learning_rate: 0.001\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.11/dist-packages/keras/src/saving/saving_lib.py:757: UserWarning: Skipping variable loading for optimizer 'adam', because it has 2 variables whereas the saved optimizer has 46 variables. \n", + " saveable.load_own_variables(weights_store.get(inner_path))\n" + ] + } + ], + "source": [ + "# ===========================================================\n", + "# 🎯 Focused Bayesian LSTM Hyperparameter Scan (Quick Version)\n", + "# ===========================================================\n", + "\n", + "import keras_tuner as kt\n", + "from tensorflow import keras\n", + "from tensorflow.keras import layers, callbacks, models\n", + "\n", + "print(\"\\n🔧 Starting Focused Quick Bayesian Scan...\")\n", + "\n", + "# ----------------------------\n", + "# Early stopping & LR reduction\n", + "# ----------------------------\n", + "early_stop = callbacks.EarlyStopping(\n", + " monitor='val_loss',\n", + " patience=2,\n", + " restore_best_weights=True\n", + ")\n", + "\n", + "reduce_lr = callbacks.ReduceLROnPlateau(\n", + " monitor='val_loss',\n", + " factor=0.5,\n", + " patience=2,\n", + " min_lr=1e-5\n", + ")\n", + "\n", + "# ----------------------------\n", + "# Build model\n", + "# ----------------------------\n", + "def build_lstm_model(hp):\n", + " model = keras.Sequential()\n", + "\n", + " # --- First LSTM layer ---\n", + " units1 = hp.Int('lstm1_units', 96, 160, step=32)\n", + " bidir = hp.Boolean('bidirectional', default=False)\n", + " dropout1 = hp.Float('dropout1', 0.2, 0.4, step=0.1)\n", + "\n", + " lstm1 = layers.LSTM(units1, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2]))\n", + " if bidir:\n", + " lstm1 = layers.Bidirectional(lstm1)\n", + " model.add(lstm1)\n", + " model.add(layers.Dropout(dropout1))\n", + " model.add(layers.BatchNormalization())\n", + "\n", + " # --- Second LSTM layer ---\n", + " units2 = hp.Int('lstm2_units', 24, 40, step=8)\n", + " dropout2 = hp.Float('dropout2', 0.2, 0.4, step=0.1)\n", + " lstm2 = layers.LSTM(units2)\n", + " if bidir:\n", + " lstm2 = layers.Bidirectional(lstm2)\n", + " model.add(lstm2)\n", + " model.add(layers.Dropout(dropout2))\n", + " model.add(layers.BatchNormalization())\n", + "\n", + " # --- Dense layers ---\n", + " dense1 = hp.Int('dense1_units', 48, 80, step=16)\n", + " dense2 = hp.Int('dense2_units', 24, 32, step=8)\n", + " model.add(layers.Dense(dense1, activation='relu'))\n", + " model.add(layers.Dropout(0.3))\n", + " model.add(layers.Dense(dense2, activation='relu'))\n", + " model.add(layers.Dropout(0.2))\n", + "\n", + " # --- Output layer ---\n", + " model.add(layers.Dense(y_train.shape[1], activation='softmax'))\n", + "\n", + " # --- Optimizer ---\n", + " lr = hp.Choice('learning_rate', [1e-3, 7e-4, 5e-4])\n", + " opt = keras.optimizers.Adam(learning_rate=lr)\n", + "\n", + " model.compile(\n", + " optimizer=opt,\n", + " loss='categorical_crossentropy',\n", + " metrics=['accuracy']\n", + " )\n", + "\n", + " return model\n", + "\n", + "\n", + "# ----------------------------\n", + "# Focused Bayesian tuner (quick version)\n", + "# ----------------------------\n", + "tuner = kt.BayesianOptimization(\n", + " build_lstm_model,\n", + " objective='val_accuracy',\n", + " max_trials=12, \n", + " num_initial_points=2, \n", + " overwrite=True\n", + ")\n", + "\n", + "# ----------------------------\n", + "# Search (short epochs to estimate trend)\n", + "# ----------------------------\n", + "tuner.search( \n", + " X_train, y_train,\n", + " validation_data=(X_val, y_val),\n", + " epochs=10,\n", + " batch_size=128,\n", + " callbacks=[early_stop, reduce_lr],\n", + " verbose=1\n", + ")\n", + "\n", + "# ----------------------------\n", + "# Get best hyperparameters\n", + "# ----------------------------\n", + "best_hp = tuner.get_best_hyperparameters(1)[0]\n", + "best_model = tuner.get_best_models(1)[0]\n", + "\n", + "print(\"\\n🏆 Best Hyperparameters (Focused Quick Scan):\")\n", + "for k, v in best_hp.values.items():\n", + " print(f\"{k}: {v}\")\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2025-10-18T11:17:31.907583Z", + "iopub.status.busy": "2025-10-18T11:17:31.907353Z", + "iopub.status.idle": "2025-10-18T11:32:05.985841Z", + "shell.execute_reply": "2025-10-18T11:32:05.985301Z", + "shell.execute_reply.started": "2025-10-18T11:17:31.907565Z" + }, + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🏆 Best Hyperparameters (Quick Scan):\n", + "lstm1_units: 96\n", + "bidirectional: True\n", + "dropout1: 0.4\n", + "lstm2_units: 40\n", + "dropout2: 0.2\n", + "dense1_units: 48\n", + "dense2_units: 32\n", + "learning_rate: 0.001\n", + "Epoch 1/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9664 - loss: 0.1198\n", + "Epoch 1: val_loss improved from inf to 0.10141, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 58ms/step - accuracy: 0.9664 - loss: 0.1198 - val_accuracy: 0.9707 - val_loss: 0.1014 - learning_rate: 0.0010\n", + "Epoch 2/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9696 - loss: 0.1091\n", + "Epoch 2: val_loss improved from 0.10141 to 0.09198, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9696 - loss: 0.1091 - val_accuracy: 0.9721 - val_loss: 0.0920 - learning_rate: 0.0010\n", + "Epoch 3/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 49ms/step - accuracy: 0.9701 - loss: 0.1064\n", + "Epoch 3: val_loss did not improve from 0.09198\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9701 - loss: 0.1064 - val_accuracy: 0.9700 - val_loss: 0.1053 - learning_rate: 0.0010\n", + "Epoch 4/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9697 - loss: 0.1074\n", + "Epoch 4: val_loss did not improve from 0.09198\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9697 - loss: 0.1074 - val_accuracy: 0.9722 - val_loss: 0.0940 - learning_rate: 0.0010\n", + "Epoch 5/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9721 - loss: 0.0998\n", + "Epoch 5: val_loss improved from 0.09198 to 0.08294, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9721 - loss: 0.0998 - val_accuracy: 0.9760 - val_loss: 0.0829 - learning_rate: 0.0010\n", + "Epoch 6/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9718 - loss: 0.0991\n", + "Epoch 6: val_loss improved from 0.08294 to 0.07310, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9718 - loss: 0.0991 - val_accuracy: 0.9792 - val_loss: 0.0731 - learning_rate: 0.0010\n", + "Epoch 7/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9736 - loss: 0.0909\n", + "Epoch 7: val_loss did not improve from 0.07310\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9736 - loss: 0.0909 - val_accuracy: 0.9774 - val_loss: 0.0767 - learning_rate: 0.0010\n", + "Epoch 8/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9748 - loss: 0.0889\n", + "Epoch 8: val_loss did not improve from 0.07310\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9748 - loss: 0.0889 - val_accuracy: 0.9757 - val_loss: 0.0875 - learning_rate: 0.0010\n", + "Epoch 9/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9746 - loss: 0.0916\n", + "Epoch 9: val_loss did not improve from 0.07310\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9746 - loss: 0.0916 - val_accuracy: 0.9739 - val_loss: 0.0881 - learning_rate: 0.0010\n", + "Epoch 10/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9769 - loss: 0.0824\n", + "Epoch 10: val_loss improved from 0.07310 to 0.06606, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9769 - loss: 0.0823 - val_accuracy: 0.9810 - val_loss: 0.0661 - learning_rate: 5.0000e-04\n", + "Epoch 11/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9792 - loss: 0.0739\n", + "Epoch 11: val_loss improved from 0.06606 to 0.06340, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9792 - loss: 0.0739 - val_accuracy: 0.9813 - val_loss: 0.0634 - learning_rate: 5.0000e-04\n", + "Epoch 12/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9786 - loss: 0.0712\n", + "Epoch 12: val_loss improved from 0.06340 to 0.06142, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9786 - loss: 0.0711 - val_accuracy: 0.9827 - val_loss: 0.0614 - learning_rate: 5.0000e-04\n", + "Epoch 13/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9816 - loss: 0.0629\n", + "Epoch 13: val_loss improved from 0.06142 to 0.05994, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9816 - loss: 0.0629 - val_accuracy: 0.9827 - val_loss: 0.0599 - learning_rate: 5.0000e-04\n", + "Epoch 14/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9816 - loss: 0.0639\n", + "Epoch 14: val_loss improved from 0.05994 to 0.05945, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9816 - loss: 0.0639 - val_accuracy: 0.9829 - val_loss: 0.0594 - learning_rate: 5.0000e-04\n", + "Epoch 15/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9815 - loss: 0.0629\n", + "Epoch 15: val_loss improved from 0.05945 to 0.05821, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9815 - loss: 0.0629 - val_accuracy: 0.9836 - val_loss: 0.0582 - learning_rate: 5.0000e-04\n", + "Epoch 16/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9824 - loss: 0.0624\n", + "Epoch 16: val_loss did not improve from 0.05821\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9824 - loss: 0.0624 - val_accuracy: 0.9834 - val_loss: 0.0587 - learning_rate: 5.0000e-04\n", + "Epoch 17/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9826 - loss: 0.0594\n", + "Epoch 17: val_loss improved from 0.05821 to 0.05767, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9826 - loss: 0.0594 - val_accuracy: 0.9841 - val_loss: 0.0577 - learning_rate: 5.0000e-04\n", + "Epoch 18/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9831 - loss: 0.0586\n", + "Epoch 18: val_loss did not improve from 0.05767\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9831 - loss: 0.0586 - val_accuracy: 0.9826 - val_loss: 0.0598 - learning_rate: 5.0000e-04\n", + "Epoch 19/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9837 - loss: 0.0564\n", + "Epoch 19: val_loss improved from 0.05767 to 0.05402, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9837 - loss: 0.0564 - val_accuracy: 0.9850 - val_loss: 0.0540 - learning_rate: 5.0000e-04\n", + "Epoch 20/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9841 - loss: 0.0556\n", + "Epoch 20: val_loss did not improve from 0.05402\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9841 - loss: 0.0556 - val_accuracy: 0.9835 - val_loss: 0.0631 - learning_rate: 5.0000e-04\n", + "Epoch 21/50\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9837 - loss: 0.0583\n", + "Epoch 21: val_loss did not improve from 0.05402\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9837 - loss: 0.0583 - val_accuracy: 0.9795 - val_loss: 0.0684 - learning_rate: 5.0000e-04\n", + "Epoch 22/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9835 - loss: 0.0554\n", + "Epoch 22: val_loss did not improve from 0.05402\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9835 - loss: 0.0554 - val_accuracy: 0.9823 - val_loss: 0.0616 - learning_rate: 5.0000e-04\n", + "Epoch 23/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9845 - loss: 0.0525\n", + "Epoch 23: val_loss improved from 0.05402 to 0.05138, saving model to best_lstm_model.h5\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9845 - loss: 0.0525 - val_accuracy: 0.9861 - val_loss: 0.0514 - learning_rate: 2.5000e-04\n", + "Epoch 24/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 51ms/step - accuracy: 0.9868 - loss: 0.0451\n", + "Epoch 24: val_loss did not improve from 0.05138\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 56ms/step - accuracy: 0.9868 - loss: 0.0451 - val_accuracy: 0.9863 - val_loss: 0.0530 - learning_rate: 2.5000e-04\n", + "Epoch 25/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9874 - loss: 0.0438\n", + "Epoch 25: val_loss did not improve from 0.05138\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 55ms/step - accuracy: 0.9874 - loss: 0.0438 - val_accuracy: 0.9857 - val_loss: 0.0544 - learning_rate: 2.5000e-04\n", + "Epoch 26/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9873 - loss: 0.0429\n", + "Epoch 26: val_loss did not improve from 0.05138\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9873 - loss: 0.0429 - val_accuracy: 0.9856 - val_loss: 0.0546 - learning_rate: 2.5000e-04\n", + "Epoch 27/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9878 - loss: 0.0421\n", + "Epoch 27: val_loss did not improve from 0.05138\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9878 - loss: 0.0421 - val_accuracy: 0.9865 - val_loss: 0.0515 - learning_rate: 1.2500e-04\n", + "Epoch 28/50\n", + "\u001b[1m562/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m━\u001b[0m \u001b[1m0s\u001b[0m 50ms/step - accuracy: 0.9885 - loss: 0.0376\n", + "Epoch 28: val_loss did not improve from 0.05138\n", + "\u001b[1m563/563\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m31s\u001b[0m 54ms/step - accuracy: 0.9885 - loss: 0.0376 - val_accuracy: 0.9867 - val_loss: 0.0527 - learning_rate: 1.2500e-04\n" + ] + } + ], + "source": [ + "best_hp = tuner.get_best_hyperparameters(1)[0]\n", + "best_model = tuner.get_best_models(1)[0]\n", + "\n", + "print(\"\\n🏆 Best Hyperparameters (Quick Scan):\")\n", + "for k, v in best_hp.values.items():\n", + " print(f\"{k}: {v}\")\n", + " \n", + "# Callbacks\n", + "# ----------------------------\n", + "early_stop = callbacks.EarlyStopping(\n", + " monitor='val_loss', patience=5, restore_best_weights=True\n", + ")\n", + "reduce_lr = callbacks.ReduceLROnPlateau(\n", + " monitor='val_loss', factor=0.5, patience=3, min_lr=1e-5\n", + ")\n", + "\n", + "\n", + "checkpoint_cb = callbacks.ModelCheckpoint(\n", + " \"best_lstm_model.h5\", \n", + " monitor=\"val_loss\", \n", + " save_best_only=True, \n", + " mode=\"min\", \n", + " verbose=1\n", + ")\n", + "\n", + "# Final Training with adaptive callbacks\n", + "history = best_model.fit(\n", + " X_train, y_train,\n", + " validation_data=(X_val, y_val),\n", + " epochs=50,\n", + " batch_size=128,\n", + " callbacks=[early_stop, reduce_lr, checkpoint_cb], \n", + " verbose=1\n", + ")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2025-10-18T11:32:05.987117Z", + "iopub.status.busy": "2025-10-18T11:32:05.986845Z", + "iopub.status.idle": "2025-10-18T11:32:25.170918Z", + "shell.execute_reply": "2025-10-18T11:32:25.170136Z", + "shell.execute_reply.started": "2025-10-18T11:32:05.987074Z" + }, + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "EVALUATION ON TEST SET\n", + "======================================================================\n", + "\u001b[1m704/704\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m9s\u001b[0m 13ms/step\n", + "\n", + "Test Loss: 0.0528\n", + "Test Accuracy: 0.9854\n", + "\n", + "Classification Report:\n", + " precision recall f1-score support\n", + "\n", + " F 0.8477 0.8000 0.8232 160\n", + " N 0.9903 0.9951 0.9927 18119\n", + " Q 0.9789 0.9767 0.9778 2230\n", + " S 0.9510 0.8381 0.8910 556\n", + " V 0.9597 0.9537 0.9567 1447\n", + "\n", + " accuracy 0.9854 22512\n", + " macro avg 0.9455 0.9127 0.9283 22512\n", + "weighted avg 0.9852 0.9854 0.9852 22512\n", + "\n", + "\n", + "Per-Class Detailed Metrics:\n", + "----------------------------------------------------------------------\n", + "Class Precision Recall F1-Score Support \n", + "----------------------------------------------------------------------\n", + "F 0.8477 0.8000 0.8232 160 \n", + "N 0.9903 0.9951 0.9927 18119 \n", + "Q 0.9789 0.9767 0.9778 2230 \n", + "S 0.9510 0.8381 0.8910 556 \n", + "V 0.9597 0.9537 0.9567 1447 \n" + ] + } + ], + "source": [ + "print(\"\\n\" + \"=\"*70)\n", + "print(\"EVALUATION ON TEST SET\")\n", + "print(\"=\"*70)\n", + "\n", + "\n", + "y_pred_probs = best_model.predict(X_test)\n", + "y_pred = np.argmax(y_pred_probs, axis=1)\n", + "y_true = np.argmax(y_test, axis=1)\n", + "\n", + "# Evaluate on test data \n", + "\n", + "test_loss, test_acc = best_model.evaluate(X_test, y_test, verbose=0)\n", + "print(f\"\\nTest Loss: {test_loss:.4f}\")\n", + "print(f\"Test Accuracy: {test_acc:.4f}\")\n", + "\n", + "# Classification Report\n", + "\n", + "print(\"\\nClassification Report:\")\n", + "print(classification_report(y_true, y_pred, target_names=label_names, digits=4))\n", + "\n", + "# Per-class metrics\n", + "\n", + "precision, recall, f1, support = precision_recall_fscore_support(y_true, y_pred)\n", + "print(\"\\nPer-Class Detailed Metrics:\")\n", + "print(\"-\" * 70)\n", + "print(f\"{'Class':<10} {'Precision':<12} {'Recall':<12} {'F1-Score':<12} {'Support':<10}\")\n", + "print(\"-\" * 70)\n", + "for i, label in enumerate(label_names):\n", + " print(f\"{label:<10} {precision[i]:<12.4f} {recall[i]:<12.4f} {f1[i]:<12.4f} {support[i]:<10}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2025-10-18T11:32:25.172135Z", + "iopub.status.busy": "2025-10-18T11:32:25.171777Z", + "iopub.status.idle": "2025-10-18T11:32:25.550400Z", + "shell.execute_reply": "2025-10-18T11:32:25.549714Z", + "shell.execute_reply.started": "2025-10-18T11:32:25.172111Z" + }, + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "CONFUSION MATRIX\n", + "======================================================================\n", + "\n", + "Confusion Matrix (raw counts):\n", + "[[ 128 25 0 0 7]\n", + " [ 10 18031 32 21 25]\n", + " [ 0 31 2178 1 20]\n", + " [ 0 80 4 466 6]\n", + " [ 13 41 11 2 1380]]\n" + ] + }, + { + "data": { + "image/png": "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R44cWr169X3fu3v37g5/xl1dXW2dDCbO/AwAeLJIVgGkGYcPH1Z0dLReeOEFZcmSJdH2mjVrStJ9n4n67LPPJlp37x/RV69eTdE472natKkkqVKlSuratasWLVqkixcvJmnfqKgo/fHHHypatOh9E/onea4VKlRQ+fLlNWPGDEl3J1ZydXVVixYt/nG/3bt36+2331bBggXl6upqu6dyyZIlunPnTpI/C3svvPBCsvd577339Morr2jOnDlav369OnXqpNdeey3Zx3n33XdltVrvu9z7bOxt3rxZknTu3DkNGjQo0XL48GFJsv2/JF2/fl0DBw5UuXLllC1bNttn1rhxY0l37x1Nrvu1Vd/74uZB2+Lj43Xu3DmH9T/88IOCgoKUN29eZcyYURaLRS4uLoqKinpgXFWrVk20LjAwUBkzZkx07zIAIP3hnlUARvL29tbhw4d1+vRphyrgP4mKipKkB97jeu8f4PfG2cuRI0eidRkz3v0VGR8fn6T3T64333xTixcv1ueff67JkydrwoQJslgsqlmzpkaPHv2P92aadq7t2rXTe++9p9WrV+vbb79VgwYN/nH23U2bNqlWrVqSpDp16qhYsWK25Gvx4sXas2ePYmJikh3Hw+5vvh+LxaLg4GCtWLFCktStW7dkH+NRXL58WdLde5eXLVv2wHE3b96UdLdzoEaNGtq5c6cqVKigli1bKk+ePMqYMaNOnDihWbNmPdJn9k/Xwz9ts698jx49Wh988IHy5s2rOnXqKH/+/LaOgbFjxz4wrvv9vDJkyKA8efLo2rVryT4XAEDaQrIKwEiVK1fW+vXrtWbNGltS8zD3/mH994rPPfdai+/3D/CUcO+5o/d7puiD/uHdsGFDW7vxb7/9ph9++EHTpk1T3bp1dfjwYeXMmfO++zn7XP+uefPm6t27t9q0aaOoqCiFhIT84/hPPvlEMTEx+uWXXxJNeLV582bt2bPnkeKwb4dOqoiICPXu3Vu5c+fWlStX9M4772jjxo3KkCHDI8WQVPd+Nl988YW6du360PE//vijdu7cqZCQEH399dcO27799ltbG/aTFhcXp6FDh8rHx0e7d++Wl5eXbZvVatWoUaMeuO+5c+cSfRkVHx+vS5cuPdIXDwCAtIU2YABGatOmjTJkyKApU6bowoUL/zj2XtXm3uNbtm3bplu3biUad++x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V04TJky47/azZ886LNOnT5fFYlHjxo0dxg0ZMsRhXLdu3WzboqKiVKdOHRUqVEg7duzQp59+qkGDBmnKlCm2MZs2bVKzZs0UEhKiXbt2KTg4WMHBwdq/f3+yzsditVqtydojFbgd6+wIkNbwqC0AAIDkc0+l07lmDujt7BBsbm/59OGD7sNisWjRokUKDg5+4Jjg4GBdv35da9assa0rXLiwevTooR49etx3n0mTJqlfv36KjIyUq6urJKlv375avHixDh8+LElq0qSJbt68qaVLl9r2q1SpksqXL6/Jkycn+RyorAIAAACAPYuLMUtMTIyioqIclpiYmH99iufOndOyZcsUEhKSaNuIESOUJ08eVahQQZ9++qni4uJs28LDw1WtWjVboipJQUFBOnLkiK5cuWIbU7t2bYdjBgUFKTw8PFkxkqwCAAAAgKHCwsLk4eHhsISFhf3r486aNUvZs2dXo0aNHNa/9957+vbbb7Vu3Tq9++67Gj58uD788EPb9sjISOXLl89hn3uvIyMj/3HMve1JlUoL8wAAAADwmBh0D1hoaKh69erlsM7Nze1fH3f69Olq3ry53N3dHdbbv1fZsmXl6uqqd999V2FhYSnyvslBsgoAAAAAhnJzc0vxJPGXX37RkSNHNH/+/IeODQgIUFxcnE6cOCF/f395e3vr3LlzDmPuvfb29rb9//3G3NueVLQBAwAAAEA6Mm3aND377LMqV67cQ8fu3r1bLi4u8vLykiQFBgZq48aNio3936y2q1atkr+/v3LlymUbYz9p070xgYGByYqTyioAAAAA2LOkzprejRs3dOzYMdvriIgI7d69W7lz51bBggUl3X30zMKFCzV69OhE+4eHh2vLli2qWbOmsmfPrvDwcPXs2VMtWrSwJaJvv/22Bg8erJCQEPXp00f79+/XuHHjNGbMGNtxunfvrurVq2v06NGqX7++vv32W23fvt3h8TZJwaNrgCQw6LYFAACAVCPVPrrmxY+cHYLN7U3Dkzx2/fr1qlmzZqL1rVu31syZMyVJU6ZMUY8ePXT27Fl5eHg4jNu5c6c6d+6sw4cPKyYmRn5+fmrZsqV69erl0Iq8d+9edenSRdu2bZOnp6e6deumPn36OBxr4cKF6t+/v06cOKFixYpp1KhRqlevXjLOnGQVSBKSVQAAgOQjWf33kpOspjWp9PIBAAAAgMeESoURUmczNgAAAAAgTSNZBQAAAAAYhzZgAAAAALCXSmcDTmv4KQAAAAAAjENlFQAAAADsMcGSEaisAgAAAACMQ7IKAAAAADAObcAAAAAAYI8JlozATwEAAAAAYBySVQAAAACAcWgDBgAAAAB7tAEbgZ8CAAAAAMA4JKsAAAAAAOPQBgwAAAAA9lwszo4AorIKAAAAADAQlVUAAAAAsMcES0bgpwAAAAAAMA7JKgAAAADAOLQBAwAAAIA9CxMsmYDKKgAAAADAOCSrAAAAAADj0AYMAAAAAPaYDdgI/BQAAAAAAMYhWQUAAAAAGIc2YAAAAACwx2zARqCyCgAAAAAwDpVVAAAAALDHBEtG4KcAAAAAADAOySoAAAAAwDi0AQMAAACAPSZYMgKVVQAAAACAcUhWAQAAAADGoQ0YAAAAAOwxG7AR+CkAAAAAAIxDsgoAAAAAMA5twAAAAABgj9mAjUCymsbs2L5Ns2ZM06GD+3XhwgV9Pm6Car1UW5IUGxurCV+M1a+/bNSpU38pe7ZsCqj0ot7r+b68vPLZjvHniQiNGT1Ku3ftVGxsrIoV91eXbt31/AuVnHVaMMS0qV9pzaqfFRHxh9zc3VW+fAX16PWBCvs9bRsT0qaltm/b6rDfG2810YCBQ550uEilvv1mrmbNmKaLFy+ouH8J9f1ogMqULevssJCKcU0hJbzyci2dOXM60fomTd/WRwMGOiEiIO2jDTiNuX37lor7+yu0X+JfmtHR0Tp08KDav9tJ3y74QaPHfqkTJyLUo2snh3HdunRUXFy8pkybpW8W/KDi/iXUrUtHXbx44UmdBgy1fdtWNWnWXLPnLdBXU2coLi5OHduH6NatWw7jGr/xltas/9W29Hz/QydFjNRm5Yrl+mxUmN7t3EXfLlwkf/8S6vRuiC5duuTs0JBKcU0hpcyd/53D321ffT1DkvRyUF0nR4bHwuJizpKOWaxWq9XZQaS027HOjsAM5Uv7O1RW72f/vr1q0exNrVi1Tj4+vrpy5bJqVg3U9FlzVfHZ5yRJN2/eUOWAZzV56gxVCnzxSYVvFDpB7u/y5XvXyxw9+9zzku5WVv39S+jD0H5Ojg6pUfOmb6pU6TL6qP/HkqSEhATVeam6mr3dUiHtOzg5OqRGXFN4XEaFfaKNG9ZryYqfZeEfCg/knkr7ODPXG+fsEGxuL+/u7BCcJn2n6tCNGzdksViUPXsOSVLOnLlU2M9PS/5vsW7fuqW4uDh9t2C+cufOo5IlSzk5WpjmxvXrkqQcHh4O65cvW6LqlQPUqOGrGjdmtG7fvu2M8JDKxN65o0MHDzh8Kebi4qJKlV7U3j27nBgZUiuuKTwusXfuaNnS/1Nwo8YkqsBj5NTvOv744w/5+fnxh9xJYmJiNG7MZ6pbr76yZcsmSbJYLPpq6kz1fK+zXgyoKBcXF+XOnVsTv/o6UUKC9C0hIUGjRg5X+QoVVaxYcdv6V+q9Kh9fX3l5een3349o7Oef6cSJCI0Z96UTo0VqcOXqFcXHxytPnjwO6/PkyaOIiD+cFBVSM64pPC5r167W9evX9Vrw684OBY8L+YkRnJqsFitWTGfPnpWXl5ckqUmTJho/frzy5cv3kD3/JyYmRjExMQ7rElzc5ObmlqKxpjWxsbH68P3uslqt6jdgsG291WpV2CeDlStPHk2fNVfu7u764fuFeq9rR8399jvlzevlxKhhkuHDBuv40aOaOfsbh/VvvNXE9t/FivvL0zOvOoS00V8nT6pAwYJPOkwAAFLcou+/V+Uq1RwmqASQ8pzaBvz322WXL1+umzdvJusYYWFh8vDwcFg+HRmWkmGmOXcT1R46e+aMJk+dbquqStLWLZu1ccN6jfx0jCpUfFbPlCylfgMGyc3NXUt+XOy8oGGU4cOGaOOG9Zo6Y5byeXv/49gyZctJkk6e/PNJhIZULFfOXMqQIUOiiW8uXbokT09PJ0WF1IxrCo/DmTOntWXzJjV64w1nhwKkean+ntXQ0FBdu3bNYendJ9TZYRnrXqJ68uSfmvz1TOXMmcthe3T03XsLXVwcWx9cXCxKSEh4YnHCTFarVcOHDdHaNas0dfos5c9f4KH7HDl8SJKUN2/exx0eUrlMrq56pmQpbdkcbluXkJCgLVvCVbZcBSdGhtSKawqPw4+LflDu3HlUtVoNZ4eCx8nZMwAzG7AkJ7cBWyyWRPerJvf+VTe3xC2/6Xk24Fu3burkyZO216dPn9Lhw4fk4eEhT8+86t3rPR06eFDjJ3ylhIR42+NoPDw8lCmTq8qWK68cOXJowEd91aFjF7m7u+n77xbo9KnT/FKGhg8drBXLl2rsFxOVNUtWXbxw9/rJlj273N3d9dfJk1q+bImqVqsuj5w5dfTIEX06KkzPPve8ivuXcHL0SA1atm6rAR/1UalSpVW6TFnNmT1Lt2/fVvDrjZwdGlIprimkpISEBP246Ac1aBisjBlT6TS3QCri1EfXuLi46JVXXrElm0uWLFGtWrWUNWtWh3E//PBDso6bnpPVbVu3qH27VonWN2j4ujp27qr6QS/dd7+p0/+j518IkCQd2L9PX44fq4MH9isuLlZFihZTh46dVaVq9ccau8m4x/6ucqX877t+yLAwNXy9kSLPntVHfXvr2NGjun37lry9fVTrpdpq37GzQ7s58E/mzZ2jWTOm6eLFC/Iv8Yz6fNRfZf/bTg48Cq4ppJRNv/2qTh1C9OOylSpc2M/Z4aQKqfbRNa+aMzHk7aVdnR2C0zg1WW3btm2Sxs2YMSNZx03PySoeD5JVAACA5Eu1yWqDic4Oweb2ks7ODsFpnHr5JDcJBQAAAACkD6n0uw4AAAAAeExoqzNC+p5eCgAAAABgJJJVAAAAAIBxaAMGAAAAAHvp/PmmpuCnAAAAAAAwDskqAAAAAMA4tAEDAAAAgD1mAzYClVUAAAAAgHFIVgEAAAAAxqENGAAAAADsMRuwEfgpAAAAAACMQ2UVAAAAAOwxwZIRqKwCAAAAAIxDsgoAAAAAMA5twAAAAABgx0IbsBGorAIAAAAAjEOyCgAAAAAwDm3AAAAAAGCHNmAzUFkFAAAAABiHyioAAAAA2KOwagQqqwAAAAAA45CsAgAAAACMQxswAAAAANhhgiUzUFkFAAAAABiHZBUAAAAAYBzagAEAAADADm3AZqCyCgAAAAAwDskqAAAAAMA4tAEDAAAAgB3agM1AZRUAAAAAYBwqqwAAAABgh8qqGaisAgAAAACMQ7IKAAAAADAObcAAAAAAYI8uYCNQWQUAAAAAGIdkFQAAAABgHNqAAQAAAMAOswGbgcoqAAAAAKQBGzduVIMGDeTr6yuLxaLFixc7bG/Tpo0sFovDUrduXYcxly9fVvPmzZUjRw7lzJlTISEhunHjhsOYvXv3qmrVqnJ3d1eBAgU0atSoRLEsXLhQJUqUkLu7u8qUKaPly5cn+3xIVgEAAAAgDbh586bKlSunCRMmPHBM3bp1dfbsWdsyb948h+3NmzfXgQMHtGrVKi1dulQbN25Uhw4dbNujoqJUp04dFSpUSDt27NCnn36qQYMGacqUKbYxmzZtUrNmzRQSEqJdu3YpODhYwcHB2r9/f7LOx2K1Wq3J2iMVuB3r7AiQ1tAJAgAAkHzuqfSmw1wt5jo7BJsrc5o/0n4Wi0WLFi1ScHCwbV2bNm109erVRBXXew4dOqSSJUtq27Zteu655yRJK1euVL169XTq1Cn5+vpq0qRJ6tevnyIjI+Xq6ipJ6tu3rxYvXqzDhw9Lkpo0aaKbN29q6dKltmNXqlRJ5cuX1+TJk5N8DlRWAQAAACCdWL9+vby8vOTv769OnTrp0qVLtm3h4eHKmTOnLVGVpNq1a8vFxUVbtmyxjalWrZotUZWkoKAgHTlyRFeuXLGNqV27tsP7BgUFKTw8PFmxptLvOgAAAADg8TBpgqWYmBjFxMQ4rHNzc5Obm1uyj1W3bl01atRIfn5+On78uD766CO98sorCg8PV4YMGRQZGSkvLy+HfTJmzKjcuXMrMjJSkhQZGSk/Pz+HMfny5bNty5UrlyIjI23r7MfcO0ZSUVkFAAAAAEOFhYXJw8PDYQkLC3ukYzVt2lSvvfaaypQpo+DgYC1dulTbtm3T+vXrUzboFEKyCgAAAACGCg0N1bVr1xyW0NDQFDn2008/LU9PTx07dkyS5O3trfPnzzuMiYuL0+XLl+Xt7W0bc+7cOYcx914/bMy97UlFsgoAAAAAdv7+eBdnLm5ubsqRI4fD8igtwPdz6tQpXbp0ST4+PpKkwMBAXb16VTt27LCNWbt2rRISEhQQEGAbs3HjRsXG/m9W21WrVsnf31+5cuWyjVmzZo3De61atUqBgYHJio9kFQAAAADSgBs3bmj37t3avXu3JCkiIkK7d+/WyZMndePGDfXu3VubN2/WiRMntGbNGjVs2FBFixZVUFCQJOmZZ55R3bp11b59e23dulW//fabunbtqqZNm8rX11eS9Pbbb8vV1VUhISE6cOCA5s+fr3HjxqlXr162OLp3766VK1dq9OjROnz4sAYNGqTt27era9euyTofHl0DJIFB99gDAACkGqn10TV5Ws17+KAn5NJ/miV57Pr161WzZs1E61u3bq1JkyYpODhYu3bt0tWrV+Xr66s6depo6NChDpMhXb58WV27dtWSJUvk4uKixo0ba/z48cqWLZttzN69e9WlSxdt27ZNnp6e6tatm/r06ePwngsXLlT//v114sQJFStWTKNGjVK9evWSde4kq0ASkKwCAAAkX6pNVlsblKzOSnqymtbQBgwAAAAAMA7JKgAAAADAOKm0MA8AAAAAj4eFe8CMQGUVAAAAAGAcKqsAAAAAYIfKqhmorAIAAAAAjEOyCgAAAAAwDm3AAAAAAGCHNmAzUFkFAAAAABiHZBUAAAAAYBzagAEAAADAHl3ARqCyCgAAAAAwDskqAAAAAMA4tAEDAAAAgB1mAzYDlVUAAAAAgHGorAIAAACAHSqrZkiTySrXFlJarue7OjsEpCFXtn3p7BAAAACMRxswAAAAAMA4abKyCgAAAACPijZgM1BZBQAAAAAYh2QVAAAAAGAc2oABAAAAwA5twGagsgoAAAAAMA7JKgAAAADAOLQBAwAAAIA9uoCNQGUVAAAAAGAcKqsAAAAAYIcJlsxAZRUAAAAAYBySVQAAAACAcWgDBgAAAAA7tAGbgcoqAAAAAMA4JKsAAAAAAOPQBgwAAAAAdmgDNgOVVQAAAACAcUhWAQAAAADGoQ0YAAAAAOzRBWwEKqsAAAAAAONQWQUAAAAAO0ywZAYqqwAAAAAA45CsAgAAAACMQxswAAAAANihDdgMVFYBAAAAAMYhWQUAAAAAGIc2YAAAAACwQxuwGaisAgAAAACMQ7IKAAAAADAObcAAAAAAYIc2YDNQWQUAAAAAGIfKKgAAAADYo7BqBCqrAAAAAADjkKwCAAAAAIxDGzAAAAAA2GGCJTNQWQUAAAAAGIdkFQAAAABgHNqAAQAAAMAObcBmoLIKAAAAADAOySoAAAAAwDi0AQMAAACAHbqAzUBlFQAAAABgHCqrAAAAAGCHCZbMQGUVAAAAAGAcklUAAAAAgHFoAwYAAAAAO3QBm4HKKgAAAADAOCSrAAAAAADj0AYMAAAAAHaYDdgMVFYBAAAAAMYhWQUAAAAAGIc2YAAAAACwQxewGaisAgAAAACMQ2UVAAAAAOy4uFBaNQGVVQAAAACAcUhWAQAAAADGoQ0YAAAAAOwwwZIZqKwCAAAAAIxDsgoAAAAAMA5twAAAAABgx0IfsBGorAIAAAAAjEOyCgAAAAAwDm3AAAAAAGCHLmAzkKymcTu2b9PM6dN06OB+XbhwQWPGT1Ctl2rbtlutVk38crx++G6hrl+PUvkKFdXv40EqVKiw84LGE1G5YhH1bFVbFUsWlE9eD73Vc4qWrN9r2541s6uGvddQDWqWVW6PrDpx5pImztugr7/71TbGzTWjRvRqpDeDnpWba0atDj+k7sPn6/zl65Kk3B5ZNeOT1ipT/Cnl9siiC5dvaOn6vfr4yyW6fjNakuTtmUMjejVSxZIFVaSApybO26Den33/ZD8MOMWCb7/RgvnzdOb0aUlSkaLF9G6nzqpStbquXb2qiRO+UPimXxV59qxy5cqtmi/VVpdu3ZU9e3YnRw4TTZv6ldas+lkREX/Izd1d5ctXUI9eH6iw39O2Md8tmK8Vy5fq0MEDunnzpn4J36YcOXI4MWqYLCnXVEibltq+bavDfm+81UQDBg550uECaRJtwGnc7du35O/vr9D+A++7fca0qZo3d7b6DxykOfMWKHPmzOrUIUQxMTFPOFI8aVkzu2nf76fVI2z+fbePfL+xXn6xpNr2+4/KNxqmL+eu15g+b6p+9TK2MaM+aKz61Uqr+YfTVOedsfLJ66FvR79j256QkKClG/bqjR5fqWzwELUfOFs1A/z1Rb+mtjGumTLq4pXrGvH1Su39/fTjO2EYxyuft7r3/EDzFv6gbxZ8rxcCKql71y46duyozl84rwvnz6vXB330/eKlGvJJmH779RcNGtDP2WHDUNu3bVWTZs01e94CfTV1huLi4tSxfYhu3bplGxMdfVsvVq6qkPYdnRgpUoukXFOS1PiNt7Rm/a+2pef7HzopYqQki8VizJKeUVlN46pUra4qVavfd5vVatXc2f9R+3c7qWatu9XWYWGjVKvai1q7ZrVeqVf/SYaKJ+zn3w7q598OPnB7pXJ+mrN0i37ZcVSSNP2H3xTSuLKeK1VIyzbsU45s7moTHKg2H83Uhm2/S5I6DJyjPYsG6IUyhbV13wldvX5bUxf+rxJ78uwVTVn4i3q2qm237rI++PRuJbV1w8DHcaowVI2atRxed+veUwu+nae9e3arUeM39fm4L2zbChQsqG7de+ijPr0VFxenjBn56wuOJk2Z5vB6yCcjVLNqoA4dPKBnn3tektSiVRtJ0ratW550eEiFknJNSZK7u7s88+Z90uEB6QKV1XTs9KlTunjxggIqvWhblz17dpUpW0579+xyYmQwweY9EXq1ehn55vWQJFV7rpiKFfLS6s2HJEkVniko10wZtXbzEds+v584p5NnLyugrN99j+mT10MNa5W3JcDAPfHx8VqxfJlu376lcuUq3HfMjes3lC1bNhJVJMmN63dvR8jh4eHkSJBWPOiaWr5siapXDlCjhq9q3JjRun37tjPCA9Ik/sZPxy5evCBJyuOZx2F9njx5dPHiRWeEBIP0GrlQEwY00/GfP1FsbLwSrAnqPHSeftt5XJLknSeHYu7E6toNx7+Uz1+KUr48jveAzQpro1erl1WWzK5aumGfOg355omdB8x29Pcjavl2U925E6MsWbJozPgJKlK0aKJxV65c1pTJE9X4zSZOiBKpTUJCgkaNHK7yFSqqWLHizg4HacCDrqlX6r0qH19feXl56fffj2js55/pxIkIjRn3pROjRUpI7+23pnBqsuri4vLQC8FisSguLu6B22NiYhLdX2nN4CY3N7cUiRFIrzo3ra4XyhRW4+6TdfLsZVWpWFRj+76lsxeuad2WIw8/gJ0PP/ten3y1QsUKeWlIt9c08v1G6hG24DFFjtSkcGE/Lfh+sW7cuK5VP/+kAR/10bSZcxwS1hs3bqhrp3f1dJEi6ti5qxOjRWoxfNhgHT96VDNn88UYUsaDrqk33vrfF2jFivvL0zOvOoS00V8nT6pAwYJPOkwgzXFqsrpo0aIHbgsPD9f48eOVkJDwj8cICwvT4MGDHdb1GzBQ/T8elBIhpmmennfvr7h08ZLy5vWyrb906ZL8S5RwVlgwgLtbJg3u1kBNek3Vyl8PSJL2Hz2jsv751aPlS1q35YgiL0XJzTWTPLJldqiueuXJoXOXohyOd+7SdZ27dF2/nzinK9duas2MXhoxdaUiLzqOQ/qTydVVBQsVkiSVLFVaB/bv09w5/9HHg+7OpHnz5g11fvcdZc2aVWPGT1CmTJmcGS5SgeHDhmjjhvWaPmuO8nl7OzscpAHJuabKlC0nSTp58k+SVSAFOPWe1YYNGyZaSpQooZkzZ+qzzz7Tm2++qSNH/rmCExoaqmvXrjksvfuEPqEzSN2eyp9fnp55tWVLuG3djRs3tG/vHpV9wD1jSB8yZcwg10wZlWC1OqyPj0+Qi8vdbohdh07qTmycagb427YXK+Slgj65tWVvxAOPbfnv/q6ZuAsBiSUkJCj2zh1Jd38fdWwfokyZMmncl5PomME/slqtGj5siNauWaWp02cpf/4Czg4JqdyjXFNHDt+d1yEvEy6lehaLOUtybNy4UQ0aNJCvr68sFosWL15s2xYbG6s+ffqoTJkyypo1q3x9fdWqVSudOXPG4RiFCxdONCPxiBEjHMbs3btXVatWlbu7uwoUKKBRo0YlimXhwoUqUaKE3N3dVaZMGS1fvjx5JyOD7lk9c+aMBg4cqFmzZikoKEi7d+9W6dKlH7qfm1vilt/oB3cNpzu3bt7UyZMnba9Pnzqlw4cOycPDQz6+vmrespWmfjVJhQoW0lP582vCF+OU18vL4VmsSJuyZnZVkQL/+8u08FN5VLb4U7oSdUt/RV7Rxu1HNbxHsG5Hx+rk2cuq+mxRNX/1BfX5/AdJUtSNaM1cHK6R7zfS5Ws3df1mtD7v86Y27/lDW/edkCQFVSkpr9w5tOPAn7pxK0Yli/hoeM9gbdp1XCfPXra9d9niT92NKYubPHNlU9niT+lOXLwO/xH55D4QPHHjxoxWlarV5O3jo1s3b2r5sqXavm2rJk2Z9t9EtZ2io29r+IhPdfPGDd28cUOSlCt3bmXIkMHJ0cM0w4cO1orlSzX2i4nKmiWrLl64Oy9DtuzZ5e7uLkm6eOGCLl68qL/++/fisaO/K0uWrPLx8ZFHzpzOCh2Getg19dfJk1q+bImqVqsuj5w5dfTIEX06KkzPPve8ivvToQbnuHnzpsqVK6d27dqpUaNGDttu3bqlnTt3asCAASpXrpyuXLmi7t2767XXXtP27dsdxg4ZMkTt27e3vbZ/xnlUVJTq1Kmj2rVra/Lkydq3b5/atWunnDlzqkOHDpKkTZs2qVmzZgoLC9Orr76qb775RsHBwdq5c2eScrx7LFbr30onT9i1a9c0fPhwffHFFypfvrxGjhypqlWr/qtjkqz+z7atW/RO21aJ1r/W8HUNHT5CVqtVE78cr+8XLtD161GqUPFZfTRgoAoXvv9srulVrufT3n1yVZ8tpp+/7p5o/ez/26wOA+coX57sGtKtoWoHllCuHFl08uxlTf9hk8bPWWsb6+aaUSN6NdJbdZ+Vm2tGrd50SN3D5uvcpbszJlZ7rpgGd22gEk97yy1TRp06d1U/rt2tz6avcmgdvr0r8UQUf565pBL17/984NTuyjYm3pCkgQM+0tbNm3Xhwnlly55dxYv7q21IewW+WPmBv7skafnPa/TUU/mfcLQwXblS/vddP2RYmBq+fvcfbJMmfKHJExP/+bMfA9zzsGsq8uxZfdS3t44dParbt2/J29tHtV6qrfYdOytbtmxPOFpzuRtTGkue8oPWODsEm92DXnqk/SwWixYtWqTg4OAHjtm2bZteeOEF/fnnnyr439b1woULq0ePHurRo8d995k0aZL69eunyMhIubq6SpL69u2rxYsX6/Dhw5KkJk2a6ObNm1q6dKltv0qVKql8+fKaPHly0s/BmcnqqFGjNHLkSHl7e2v48OFq2LBhihyXZBUpLS0mq3AeklUAQHqRWpPVCoPXPnzQE7K5b+VEE8rer7v075KSrK5evVp16tTR1atXlSPH3ac5FC5cWNHR0YqNjVXBggX19ttvq2fPnrZHx7Vq1UpRUVEOLcbr1q1TrVq1dPnyZeXKlUsFCxZUr169HBLegQMHavHixdqzZ0+Sz92pl0/fvn2VOXNmFS1aVLNmzdKsWbPuO+6HH354wpEBAAAAgPPdb0LZgQMHatCgQf/quNHR0erTp4+aNWtmS1Ql6b333lPFihWVO3dubdq0SaGhoTp79qw+//xzSVJkZKT8/By7MPPly2fblitXLkVGRtrW2Y+JjEzeLV5OTVZbtWrFM4wAAAAAGMWkFCW0b6h69erlsO7fTjoYGxurt956S1arVZMmTXLYZv9eZcuWlaurq959912FhYU98ckOnZqszpw505lvDwAAAABGS0rLb3LcS1T//PNPrV271qGqej8BAQGKi4vTiRMn5O/vL29vb507d85hzL3X3v99vNODxngn85FiTn10DQAAAADgybiXqB49elSrV69Wnjx5HrrP7t275eLiIi8vL0lSYGCgNm7cqNjYWNuYVatWyd/fX7ly5bKNWbPGcZKqVatWKTAwMFnxptJbngEAAADg8UittyreuHFDx44ds72OiIjQ7t27lTt3bvn4+OiNN97Qzp07tXTpUsXHx9vuIc2dO7dcXV0VHh6uLVu2qGbNmsqePbvCw8PVs2dPtWjRwpaIvv322xo8eLBCQkLUp08f7d+/X+PGjdOYMWNs79u9e3dVr15do0ePVv369fXtt99q+/btmjJlSrLOx+mPrnkcmA0YKY3ZgJGSmA0YAJBepNbZgJ8dus7ZIdjsGFAzyWPXr1+vmjUTj2/durUGDRqUaGKke9atW6caNWpo586d6ty5sw4fPqyYmBj5+fmpZcuW6tWrl0Mr8t69e9WlSxdt27ZNnp6e6tatm/r06eNwzIULF6p///46ceKEihUrplGjRqlevXpJPheJ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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"\\n\" + \"=\"*70)\n", + "print(\"CONFUSION MATRIX\")\n", + "print(\"=\"*70)\n", + "\n", + "from sklearn.metrics import confusion_matrix\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Compute confusion matrix\n", + "\n", + "cm = confusion_matrix(y_true, y_pred)\n", + "\n", + "# Print numeric matrix\n", + "\n", + "print(\"\\nConfusion Matrix (raw counts):\")\n", + "print(cm)\n", + "\n", + "# Plot without saving\n", + "\n", + "plt.figure(figsize=(10, 8))\n", + "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n", + "xticklabels=label_names, yticklabels=label_names)\n", + "plt.title(\"Confusion Matrix Heatmap\", fontsize=14)\n", + "plt.xlabel(\"Predicted Label\")\n", + "plt.ylabel(\"True Label\")\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kaggle": { + "accelerator": "nvidiaTeslaT4", + "dataSources": [ + { + "datasetId": 8508427, + "sourceId": 13406794, + "sourceType": "datasetVersion" + } + ], + "dockerImageVersionId": 31154, + "isGpuEnabled": true, + "isInternetEnabled": true, + "language": "python", + "sourceType": "notebook" + }, + "kernelspec": { + "display_name": "Python 3", + "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.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}