diff --git a/bilstm_sequential.ipynb b/bilstm_sequential.ipynb new file mode 100644 index 0000000..c00a06c --- /dev/null +++ b/bilstm_sequential.ipynb @@ -0,0 +1,906 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "21ce2b41", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n", + "\n", + "import tensorflow as tf\n", + "from tensorflow.keras.models import Sequential\n", + "from tensorflow.keras.layers import (\n", + " LSTM,\n", + " Bidirectional,\n", + " Dense,\n", + " Dropout,\n", + " Masking\n", + ")\n", + "\n", + "from tensorflow.keras.callbacks import (\n", + " EarlyStopping,\n", + " ReduceLROnPlateau\n", + ")\n", + "\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7b5e2a73", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
match_iddelivery_numberlegal_ballover_ballscorewicketstarget_score
01001349110.100168
11001349220.200168
21001349330.310168
31001349440.430168
41001349550.530168
\n", + "
" + ], + "text/plain": [ + " match_id delivery_number legal_ball over_ball score wickets \\\n", + "0 1001349 1 1 0.1 0 0 \n", + "1 1001349 2 2 0.2 0 0 \n", + "2 1001349 3 3 0.3 1 0 \n", + "3 1001349 4 4 0.4 3 0 \n", + "4 1001349 5 5 0.5 3 0 \n", + "\n", + " target_score \n", + "0 168 \n", + "1 168 \n", + "2 168 \n", + "3 168 \n", + "4 168 " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = pd.read_csv(\n", + " \"data/run_prediction_dataset_raw.csv\"\n", + ")\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4b258bec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
match_iddelivery_numberlegal_ballover_ballscorewicketstarget_scoreruns_this_ballprevious_scorelast_6_runslast_12_runsboundarieslast_12_boundariesdotslast_12_dotsballs_remainingrun_rate
514915211028110.1001790.00.00.00.000.010.01190.0
514916211028220.2101791.00.00.00.000.000.01183.0
514917211028330.3101790.01.00.00.000.010.01172.0
514918211028440.4101790.01.00.00.000.010.01161.5
514919211028550.5101790.01.00.00.000.010.01151.2
\n", + "
" + ], + "text/plain": [ + " match_id delivery_number legal_ball over_ball score wickets \\\n", + "514915 211028 1 1 0.1 0 0 \n", + "514916 211028 2 2 0.2 1 0 \n", + "514917 211028 3 3 0.3 1 0 \n", + "514918 211028 4 4 0.4 1 0 \n", + "514919 211028 5 5 0.5 1 0 \n", + "\n", + " target_score runs_this_ball previous_score last_6_runs \\\n", + "514915 179 0.0 0.0 0.0 \n", + "514916 179 1.0 0.0 0.0 \n", + "514917 179 0.0 1.0 0.0 \n", + "514918 179 0.0 1.0 0.0 \n", + "514919 179 0.0 1.0 0.0 \n", + "\n", + " last_12_runs boundaries last_12_boundaries dots last_12_dots \\\n", + "514915 0.0 0 0.0 1 0.0 \n", + "514916 0.0 0 0.0 0 0.0 \n", + "514917 0.0 0 0.0 1 0.0 \n", + "514918 0.0 0 0.0 1 0.0 \n", + "514919 0.0 0 0.0 1 0.0 \n", + "\n", + " balls_remaining run_rate \n", + "514915 119 0.0 \n", + "514916 118 3.0 \n", + "514917 117 2.0 \n", + "514918 116 1.5 \n", + "514919 115 1.2 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = df.sort_values(\n", + " [\n", + " \"match_id\",\n", + " \"delivery_number\"\n", + " ]\n", + ")\n", + "\n", + "\n", + "# runs scored this ball\n", + "df[\"runs_this_ball\"] = (\n", + " df.groupby(\"match_id\")[\"score\"]\n", + " .diff()\n", + " .fillna(\n", + " df[\"score\"]\n", + " )\n", + ")\n", + "\n", + "\n", + "# previous score\n", + "df[\"previous_score\"] = (\n", + " df[\"score\"]\n", + " -\n", + " df[\"runs_this_ball\"]\n", + ")\n", + "\n", + "\n", + "# rolling scoring form\n", + "\n", + "df[\"last_6_runs\"] = (\n", + " df.groupby(\"match_id\")\n", + " [\"runs_this_ball\"]\n", + " .rolling(6)\n", + " .sum()\n", + " .reset_index(level=0,drop=True)\n", + " .fillna(0)\n", + ")\n", + "\n", + "\n", + "df[\"last_12_runs\"] = (\n", + " df.groupby(\"match_id\")\n", + " [\"runs_this_ball\"]\n", + " .rolling(12)\n", + " .sum()\n", + " .reset_index(level=0,drop=True)\n", + " .fillna(0)\n", + ")\n", + "\n", + "\n", + "df[\"boundaries\"] = (\n", + " df[\"runs_this_ball\"] >= 4\n", + ").astype(int)\n", + "\n", + "\n", + "df[\"last_12_boundaries\"] = (\n", + " df.groupby(\"match_id\")\n", + " [\"boundaries\"]\n", + " .rolling(12)\n", + " .sum()\n", + " .reset_index(level=0,drop=True)\n", + " .fillna(0)\n", + ")\n", + "\n", + "\n", + "df[\"dots\"] = (\n", + " df[\"runs_this_ball\"] == 0\n", + ").astype(int)\n", + "\n", + "\n", + "df[\"last_12_dots\"] = (\n", + " df.groupby(\"match_id\")\n", + " [\"dots\"]\n", + " .rolling(12)\n", + " .sum()\n", + " .reset_index(level=0,drop=True)\n", + " .fillna(0)\n", + ")\n", + "\n", + "# Existing features above...\n", + "\n", + "\n", + "# New features\n", + "df[\"balls_remaining\"] = 120 - df[\"legal_ball\"]\n", + "\n", + "df[\"run_rate\"] = (\n", + " df[\"score\"] / (df[\"legal_ball\"] / 6)\n", + ").replace(\n", + " [float(\"inf\"), -float(\"inf\")],\n", + " 0\n", + ").fillna(0)\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1fc0e0b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(440081, 17)\n" + ] + } + ], + "source": [ + "df = df[\n", + " df[\"legal_ball\"] >= 30\n", + "]\n", + "\n", + "\n", + "print(\n", + " df.shape\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "370d0cfb", + "metadata": {}, + "outputs": [], + "source": [ + "FEATURES = [\n", + " \"score\",\n", + " \"wickets\",\n", + " \"legal_ball\",\n", + " \"runs_this_ball\",\n", + " \"last_6_runs\",\n", + " \"last_12_runs\",\n", + " \"last_12_boundaries\",\n", + " \"last_12_dots\",\n", + " \"run_rate\",\n", + " \"balls_remaining\"\n", + "]\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d43f7fc0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training matches: 3686\n", + "Testing matches : 922\n", + "Training rows: 352215\n", + "Testing rows : 87866\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "\n", + "# ==========================================\n", + "# Split by MATCH, not by sequence\n", + "# ==========================================\n", + "\n", + "all_matches = df[\"match_id\"].unique()\n", + "\n", + "\n", + "train_matches, test_matches = train_test_split(\n", + " all_matches,\n", + " test_size=0.2,\n", + " random_state=42\n", + ")\n", + "\n", + "\n", + "train_df = df[\n", + " df[\"match_id\"].isin(train_matches)\n", + "].copy()\n", + "\n", + "\n", + "test_df = df[\n", + " df[\"match_id\"].isin(test_matches)\n", + "].copy()\n", + "\n", + "\n", + "print(\"Training matches:\", len(train_matches))\n", + "print(\"Testing matches :\", len(test_matches))\n", + "\n", + "print(\"Training rows:\", len(train_df))\n", + "print(\"Testing rows :\", len(test_df))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "55e46374", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "print(\n", + " len(\n", + " set(train_matches)\n", + " &\n", + " set(test_matches)\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "66f4ae4e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train sequences: 241635\n", + "Test sequences: 60206\n" + ] + } + ], + "source": [ + "def create_sequences(data):\n", + "\n", + " X = []\n", + " y = []\n", + "\n", + " for match_id, innings in data.groupby(\"match_id\"):\n", + "\n", + " innings = innings.sort_values(\n", + " \"legal_ball\"\n", + " )\n", + "\n", + " sequence = innings[FEATURES].values\n", + "\n", + " target = innings[\"target_score\"].iloc[0]\n", + "\n", + "\n", + " for i in range(30, len(sequence)):\n", + "\n", + " X.append(\n", + " sequence[:i+1]\n", + " )\n", + "\n", + " y.append(\n", + " target\n", + " )\n", + "\n", + " return X, y\n", + "\n", + "\n", + "\n", + "X_train_sequences, y_train_targets = create_sequences(train_df)\n", + "\n", + "X_test_sequences, y_test_targets = create_sequences(test_df)\n", + "\n", + "\n", + "print(\n", + " \"Train sequences:\",\n", + " len(X_train_sequences)\n", + ")\n", + "\n", + "print(\n", + " \"Test sequences:\",\n", + " len(X_test_sequences)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1aa07812", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train: (241635, 120, 10) (241635,)\n", + "Test : (60206, 120, 10) (60206,)\n" + ] + } + ], + "source": [ + "from tensorflow.keras.preprocessing.sequence import pad_sequences\n", + "import numpy as np\n", + "\n", + "\n", + "# X_train_sequences and X_test_sequences\n", + "# should already be created from separate match splits\n", + "\n", + "X_train = pad_sequences(\n", + " X_train_sequences,\n", + " maxlen=120,\n", + " dtype=\"float32\",\n", + " padding=\"pre\"\n", + ")\n", + "\n", + "X_test = pad_sequences(\n", + " X_test_sequences,\n", + " maxlen=120,\n", + " dtype=\"float32\",\n", + " padding=\"pre\"\n", + ")\n", + "\n", + "\n", + "y_train = np.array(y_train_targets)\n", + "y_test = np.array(y_test_targets)\n", + "\n", + "\n", + "print(\"Train:\", X_train.shape, y_train.shape)\n", + "print(\"Test :\", X_test.shape, y_test.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e470e33f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/keshav/code/cricket-dls/.venv/lib/python3.12/site-packages/keras/src/layers/core/masking.py:48: 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__(**kwargs)\n" + ] + }, + { + "data": { + "text/html": [ + "
Model: \"sequential\"\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1mModel: \"sequential\"\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
+       "┃ Layer (type)                     Output Shape                  Param # ┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
+       "│ masking (Masking)               │ (None, 120, 10)        │             0 │\n",
+       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
+       "│ bidirectional (Bidirectional)   │ (None, 64)             │        11,008 │\n",
+       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
+       "│ dropout (Dropout)               │ (None, 64)             │             0 │\n",
+       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
+       "│ dense (Dense)                   │ (None, 32)             │         2,080 │\n",
+       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
+       "│ dense_1 (Dense)                 │ (None, 1)              │            33 │\n",
+       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
+       "
\n" + ], + "text/plain": [ + "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n", + "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n", + "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n", + "│ masking (\u001b[38;5;33mMasking\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m120\u001b[0m, \u001b[38;5;34m10\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", + "│ bidirectional (\u001b[38;5;33mBidirectional\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m11,008\u001b[0m │\n", + "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", + "│ dropout (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", + "│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m2,080\u001b[0m │\n", + "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", + "│ dense_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m33\u001b[0m │\n", + "└─────────────────────────────────┴────────────────────────┴───────────────┘\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
 Total params: 13,121 (51.25 KB)\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m13,121\u001b[0m (51.25 KB)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
 Trainable params: 13,121 (51.25 KB)\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m13,121\u001b[0m (51.25 KB)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
 Non-trainable params: 0 (0.00 B)\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model = Sequential([\n", + "\n", + " Masking(\n", + " mask_value=0.,\n", + " input_shape=(120,len(FEATURES))\n", + " ),\n", + "\n", + " Bidirectional(\n", + " LSTM(32)\n", + " ),\n", + "\n", + " Dropout(0.25),\n", + "\n", + " Dense(\n", + " 32,\n", + " activation=\"relu\"\n", + " ),\n", + "\n", + " Dense(1)\n", + "\n", + "])\n", + "\n", + "model.compile(\n", + " optimizer=tf.keras.optimizers.Adam(\n", + " learning_rate=0.001\n", + " ),\n", + "\n", + " loss=\"mse\",\n", + "\n", + " metrics=[\n", + " \"mae\"\n", + " ]\n", + ")\n", + "\n", + "\n", + "model.summary()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "495daca8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m32s\u001b[0m 254ms/step - loss: 19128.5391 - mae: 131.3299 - val_loss: 12421.8320 - val_mae: 103.6737 - learning_rate: 0.0010\n", + "Epoch 2/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 321ms/step - loss: 6210.3130 - mae: 65.5333 - val_loss: 1969.1512 - val_mae: 33.6969 - learning_rate: 0.0010\n", + "Epoch 3/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 321ms/step - loss: 1224.2498 - mae: 24.7959 - val_loss: 525.3225 - val_mae: 15.5029 - learning_rate: 0.0010\n", + "Epoch 4/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 322ms/step - loss: 610.5830 - mae: 17.7209 - val_loss: 320.0849 - val_mae: 12.2769 - learning_rate: 0.0010\n", + "Epoch 5/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m38s\u001b[0m 322ms/step - loss: 483.1981 - mae: 16.2305 - val_loss: 249.6277 - val_mae: 11.1048 - learning_rate: 0.0010\n", + "Epoch 6/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m41s\u001b[0m 345ms/step - loss: 434.6412 - mae: 15.6104 - val_loss: 226.9275 - val_mae: 10.6671 - learning_rate: 0.0010\n", + "Epoch 7/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m40s\u001b[0m 341ms/step - loss: 414.1877 - mae: 15.3427 - val_loss: 211.9790 - val_mae: 10.3182 - learning_rate: 0.0010\n", + "Epoch 8/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m39s\u001b[0m 331ms/step - loss: 395.6093 - mae: 15.0844 - val_loss: 207.4465 - val_mae: 10.3751 - learning_rate: 0.0010\n", + "Epoch 9/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m44s\u001b[0m 376ms/step - loss: 385.9764 - mae: 14.9372 - val_loss: 198.0012 - val_mae: 10.2073 - learning_rate: 0.0010\n", + "Epoch 10/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m47s\u001b[0m 395ms/step - loss: 374.2676 - mae: 14.7318 - val_loss: 194.2088 - val_mae: 10.1279 - learning_rate: 0.0010\n", + "Epoch 11/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m46s\u001b[0m 392ms/step - loss: 367.1200 - mae: 14.5911 - val_loss: 188.0659 - val_mae: 9.9135 - learning_rate: 0.0010\n", + "Epoch 12/100\n", + "\u001b[1m118/118\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m49s\u001b[0m 412ms/step - loss: 362.3481 - mae: 14.5265 - val_loss: 188.1493 - val_mae: 9.9198 - learning_rate: 0.0010\n", + "Epoch 13/100\n", + "\u001b[1m 27/118\u001b[0m \u001b[32m━━━━\u001b[0m\u001b[37m━━━━━━━━━━━━━━━━\u001b[0m \u001b[1m41s\u001b[0m 451ms/step - loss: 357.6455 - mae: 14.4617" + ] + } + ], + "source": [ + "callbacks = [\n", + "\n", + " EarlyStopping(\n", + " patience=10,\n", + " restore_best_weights=True\n", + " ),\n", + "\n", + " ReduceLROnPlateau(\n", + " patience=5,\n", + " factor=0.5\n", + " )\n", + "\n", + "]\n", + "\n", + "\n", + "history = model.fit(\n", + "\n", + " X_train,\n", + " y_train,\n", + "\n", + " validation_data=(\n", + " X_test,\n", + " y_test\n", + " ),\n", + "\n", + " epochs=100,\n", + "\n", + " batch_size=2048,\n", + "\n", + " callbacks=callbacks,\n", + "\n", + " verbose=1\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv (3.14.6)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}