{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "368adbee", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "7934046b", "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('powerplant_data.csv')" ] }, { "cell_type": "code", "execution_count": 3, "id": "70d9385d", "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", "
ATVAPRHPE
08.3440.771010.8490.01480.48
123.6458.491011.4074.20445.75
229.7456.901007.1541.91438.76
319.0749.691007.2276.79453.09
411.8040.661017.1397.20464.43
\n", "
" ], "text/plain": [ " AT V AP RH PE\n", "0 8.34 40.77 1010.84 90.01 480.48\n", "1 23.64 58.49 1011.40 74.20 445.75\n", "2 29.74 56.90 1007.15 41.91 438.76\n", "3 19.07 49.69 1007.22 76.79 453.09\n", "4 11.80 40.66 1017.13 97.20 464.43" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head()" ] }, { "cell_type": "code", "execution_count": 4, "id": "0cb7f240", "metadata": {}, "outputs": [], "source": [ "# At -> Temperatur\n", "# V -> Vacuum\n", "# Ap -> Pressure\n", "# Rh -> Humidity\n", "\n", "\n", "# Pe -> Produced Energy\n", "\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "d426a0f5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "AT 0\n", "V 0\n", "AP 0\n", "RH 0\n", "PE 0\n", "dtype: int64" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.isnull().sum()" ] }, { "cell_type": "code", "execution_count": 6, "id": "cb116d0a", "metadata": {}, "outputs": [], "source": [ "X = df.drop(\"PE\", axis=1)\n", "y = df[\"PE\"]" ] }, { "cell_type": "code", "execution_count": 7, "id": "91837976", "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", "
ATVAPRH
08.3440.771010.8490.01
123.6458.491011.4074.20
229.7456.901007.1541.91
319.0749.691007.2276.79
411.8040.661017.1397.20
\n", "
" ], "text/plain": [ " AT V AP RH\n", "0 8.34 40.77 1010.84 90.01\n", "1 23.64 58.49 1011.40 74.20\n", "2 29.74 56.90 1007.15 41.91\n", "3 19.07 49.69 1007.22 76.79\n", "4 11.80 40.66 1017.13 97.20" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.head()" ] }, { "cell_type": "code", "execution_count": 8, "id": "55fc041a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 480.48\n", "1 445.75\n", "2 438.76\n", "3 453.09\n", "4 464.43\n", "Name: PE, dtype: float64" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.head()" ] }, { "cell_type": "code", "execution_count": 9, "id": "ecb79f4a", "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)" ] }, { "cell_type": "code", "execution_count": 10, "id": "e65c588f", "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", "
ATVAPRH
548725.2463.471011.3066.21
352226.0970.401007.4185.37
691626.6373.681015.1585.13
754432.0671.851007.9056.44
760028.7071.641007.1169.85
...............
573426.2561.021011.4771.22
519129.1764.791016.4361.05
539018.0043.701015.4061.28
86026.7368.841010.7566.83
72706.4039.181024.6086.83
\n", "

7654 rows × 4 columns

\n", "
" ], "text/plain": [ " AT V AP RH\n", "5487 25.24 63.47 1011.30 66.21\n", "3522 26.09 70.40 1007.41 85.37\n", "6916 26.63 73.68 1015.15 85.13\n", "7544 32.06 71.85 1007.90 56.44\n", "7600 28.70 71.64 1007.11 69.85\n", "... ... ... ... ...\n", "5734 26.25 61.02 1011.47 71.22\n", "5191 29.17 64.79 1016.43 61.05\n", "5390 18.00 43.70 1015.40 61.28\n", "860 26.73 68.84 1010.75 66.83\n", "7270 6.40 39.18 1024.60 86.83\n", "\n", "[7654 rows x 4 columns]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_train\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "acd7288a", "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", "
ATVAPRH
251329.7057.351005.6357.35
941125.7171.641008.8577.31
874517.8344.921025.0470.58
90859.4641.401026.7887.58
495029.9064.791016.9048.24
...............
720420.4651.431010.0683.79
159929.7067.171007.3166.56
569714.6439.581011.4671.90
35029.4771.321008.0767.00
621017.7050.881015.4489.57
\n", "

1914 rows × 4 columns

\n", "
" ], "text/plain": [ " AT V AP RH\n", "2513 29.70 57.35 1005.63 57.35\n", "9411 25.71 71.64 1008.85 77.31\n", "8745 17.83 44.92 1025.04 70.58\n", "9085 9.46 41.40 1026.78 87.58\n", "4950 29.90 64.79 1016.90 48.24\n", "... ... ... ... ...\n", "7204 20.46 51.43 1010.06 83.79\n", "1599 29.70 67.17 1007.31 66.56\n", "5697 14.64 39.58 1011.46 71.90\n", "350 29.47 71.32 1008.07 67.00\n", "6210 17.70 50.88 1015.44 89.57\n", "\n", "[1914 rows x 4 columns]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_test" ] }, { "cell_type": "code", "execution_count": 12, "id": "4b00a2e0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(9568, 5)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.shape" ] }, { "cell_type": "code", "execution_count": 13, "id": "24ba1767", "metadata": {}, "outputs": [], "source": [ "from sklearn.preprocessing import StandardScaler\n", "\n", "scaler = StandardScaler()\n", "X_train_scaled = scaler.fit_transform(X_train)\n", "X_test_scaled = scaler.transform(X_test)" ] }, { "cell_type": "code", "execution_count": 14, "id": "c176dd57", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.74805289, 0.72006931, -0.32660017, -0.49711722],\n", " [ 0.86181948, 1.26515721, -0.98521113, 0.8181501 ],\n", " [ 0.93409473, 1.52314975, 0.32523844, 0.80167494],\n", " ...,\n", " [-0.22097078, -0.834965 , 0.36756563, -0.83554456],\n", " [ 0.94747903, 1.14245344, -0.41971997, -0.45455637],\n", " [-1.77355014, -1.19049131, 1.92520594, 0.91837402]],\n", " shape=(7654, 4))" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_train_scaled" ] }, { "cell_type": "code", "execution_count": 15, "id": "649a7035", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1.34499288, 0.23869298, -1.28658067, -1.10532538],\n", " [ 0.81095912, 1.36269098, -0.74140656, 0.26485915],\n", " [-0.2437241 , -0.73900436, 1.99970178, -0.19713193],\n", " ...,\n", " [-0.67068342, -1.15902881, -0.29951077, -0.10651852],\n", " [ 1.31420898, 1.33752097, -0.87346737, -0.44288647],\n", " [-0.2611237 , -0.27021304, 0.37433797, 1.10646548]],\n", " shape=(1914, 4))" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_test_scaled" ] }, { "cell_type": "code", "execution_count": 16, "id": "d0d83bf9", "metadata": {}, "outputs": [], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "X_train_tensor = torch.tensor(X_train_scaled, dtype=torch.float32)\n", "X_test_tensor = torch.tensor(X_test_scaled, dtype=torch.float32)\n", "y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).view(-1, 1)\n", "y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).view(-1, 1)\n", "\n" ] }, { "cell_type": "code", "execution_count": 17, "id": "e49a468d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "numpy.ndarray" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(X_train_scaled)" ] }, { "cell_type": "code", "execution_count": 18, "id": "5d7e374d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pandas.Series" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(y_train)" ] }, { "cell_type": "code", "execution_count": 19, "id": "c4481595", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(7654,)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_train.shape" ] }, { "cell_type": "code", "execution_count": 20, "id": "dce3841e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5487 442.75\n", "3522 432.52\n", "6916 428.80\n", "7544 426.07\n", "7600 436.58\n", " ... \n", "5734 436.44\n", "5191 441.20\n", "5390 464.26\n", "860 440.45\n", "7270 484.44\n", "Name: PE, Length: 7654, dtype: float64" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_train" ] }, { "cell_type": "code", "execution_count": 21, "id": "95daa94c", "metadata": {}, "outputs": [], "source": [ "from torch.utils.data import TensorDataset, DataLoader\n", "train_dataset = TensorDataset(X_train_tensor, y_train_tensor)\n", "test_dataset = TensorDataset(X_test_tensor, y_test_tensor)" ] }, { "cell_type": "code", "execution_count": 22, "id": "33e40147", "metadata": {}, "outputs": [], "source": [ "train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n", "test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)" ] }, { "cell_type": "markdown", "id": "b32256a8", "metadata": {}, "source": [ "# Deep Learning\n" ] }, { "cell_type": "code", "execution_count": 23, "id": "1c0b562f", "metadata": {}, "outputs": [], "source": [ "# Build our Ann Model\n", "class ANN(nn.Module):\n", " def __init__(self):\n", " super(ANN, self).__init__()\n", "\n", " self.model = nn.Sequential(\n", " #1st hidden Layer\n", " nn.Linear(X_train.shape[1], 6),\n", " nn.ReLU(),\n", "\n", " #2nd hidden Layer\n", " nn.Linear(6, 6),\n", " nn.ReLU(),\n", "\n", " #Output Layer\n", " nn.Linear(6, 1),\n", "\n", " )\n", "\n", " def forward(self, x):\n", " return self.model(x)" ] }, { "cell_type": "code", "execution_count": 24, "id": "97383db1", "metadata": {}, "outputs": [], "source": [ "import torch.optim as optim\n", "model = ANN()\n", "\n", "#loss, optimizer\n", "criterion = nn.MSELoss()\n", "optimizer = optim.Adam(model.parameters(), lr=0.001)\n" ] }, { "cell_type": "code", "execution_count": 25, "id": "bf314192", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/100, Training Loss: 206483.9676, Validation Loss: 204971.6432\n", "Epoch 2/100, Training Loss: 200501.9493, Validation Loss: 192637.6995\n", "Epoch 3/100, Training Loss: 178943.0768, Validation Loss: 162144.0701\n", "Epoch 4/100, Training Loss: 145166.7542, Validation Loss: 127858.9832\n", "Epoch 5/100, Training Loss: 118374.5281, Validation Loss: 110312.7915\n", "Epoch 6/100, Training Loss: 106832.0395, Validation Loss: 100434.8103\n", "Epoch 7/100, Training Loss: 93098.9721, Validation Loss: 78609.2303\n", "Epoch 8/100, Training Loss: 62566.8509, Validation Loss: 41745.1653\n", "Epoch 9/100, Training Loss: 26629.9881, Validation Loss: 12750.4689\n", "Epoch 10/100, Training Loss: 6921.2975, Validation Loss: 2994.7181\n", "Epoch 11/100, Training Loss: 1918.3511, Validation Loss: 1321.6775\n", "Epoch 12/100, Training Loss: 1040.6924, Validation Loss: 920.0537\n", "Epoch 13/100, Training Loss: 752.0679, Validation Loss: 706.1089\n", "Epoch 14/100, Training Loss: 580.8715, Validation Loss: 552.9809\n", "Epoch 15/100, Training Loss: 455.1619, Validation Loss: 432.5796\n", "Epoch 16/100, Training Loss: 358.5538, Validation Loss: 342.4778\n", "Epoch 17/100, Training Loss: 285.1631, Validation Loss: 272.2899\n", "Epoch 18/100, Training Loss: 228.3353, Validation Loss: 217.5428\n", "Epoch 19/100, Training Loss: 185.0122, Validation Loss: 176.9093\n", "Epoch 20/100, Training Loss: 152.3275, Validation Loss: 145.6500\n", "Epoch 21/100, Training Loss: 126.8477, Validation Loss: 121.9971\n", "Epoch 22/100, Training Loss: 106.6526, Validation Loss: 101.0624\n", "Epoch 23/100, Training Loss: 89.4958, Validation Loss: 84.7042\n", "Epoch 24/100, Training Loss: 76.0221, Validation Loss: 72.5410\n", "Epoch 25/100, Training Loss: 64.7989, Validation Loss: 62.1438\n", "Epoch 26/100, Training Loss: 55.7778, Validation Loss: 52.2099\n", "Epoch 27/100, Training Loss: 48.3958, Validation Loss: 45.5748\n", "Epoch 28/100, Training Loss: 42.3756, Validation Loss: 39.9509\n", "Epoch 29/100, Training Loss: 37.6235, Validation Loss: 36.1595\n", "Epoch 30/100, Training Loss: 33.8859, Validation Loss: 32.5338\n", "Epoch 31/100, Training Loss: 31.0004, Validation Loss: 29.9545\n", "Epoch 32/100, Training Loss: 28.8229, Validation Loss: 28.0271\n", "Epoch 33/100, Training Loss: 27.1157, Validation Loss: 27.1537\n", "Epoch 34/100, Training Loss: 25.7490, Validation Loss: 25.1189\n", "Epoch 35/100, Training Loss: 24.6246, Validation Loss: 24.0732\n", "Epoch 36/100, Training Loss: 23.8271, Validation Loss: 23.0742\n", "Epoch 37/100, Training Loss: 23.0539, Validation Loss: 23.3100\n", "Epoch 38/100, Training Loss: 22.5906, Validation Loss: 21.7138\n", "Epoch 39/100, Training Loss: 22.1233, Validation Loss: 21.8973\n", "Epoch 40/100, Training Loss: 21.6649, Validation Loss: 21.3739\n", "Epoch 41/100, Training Loss: 21.4146, Validation Loss: 21.0125\n", "Epoch 42/100, Training Loss: 21.3382, Validation Loss: 20.6257\n", "Epoch 43/100, Training Loss: 21.0423, Validation Loss: 20.0444\n", "Epoch 44/100, Training Loss: 20.8834, Validation Loss: 19.9183\n", "Epoch 45/100, Training Loss: 20.7903, Validation Loss: 19.7027\n", "Epoch 46/100, Training Loss: 20.7357, Validation Loss: 20.3074\n", "Epoch 47/100, Training Loss: 20.6489, Validation Loss: 19.5436\n", "Epoch 48/100, Training Loss: 20.5285, Validation Loss: 19.9974\n", "Epoch 49/100, Training Loss: 20.4500, Validation Loss: 19.7680\n", "Epoch 50/100, Training Loss: 20.5514, Validation Loss: 19.4443\n", "Epoch 51/100, Training Loss: 20.4186, Validation Loss: 19.4542\n", "Epoch 52/100, Training Loss: 20.5206, Validation Loss: 19.4731\n", "Epoch 53/100, Training Loss: 20.4054, Validation Loss: 19.1452\n", "Epoch 54/100, Training Loss: 20.5889, Validation Loss: 19.3947\n", "Epoch 55/100, Training Loss: 20.4007, Validation Loss: 19.1225\n", "Epoch 56/100, Training Loss: 20.2112, Validation Loss: 19.3416\n", "Epoch 57/100, Training Loss: 20.2432, Validation Loss: 20.2822\n", "Epoch 58/100, Training Loss: 20.2435, Validation Loss: 19.3285\n", "Epoch 59/100, Training Loss: 20.2103, Validation Loss: 19.0883\n", "Epoch 60/100, Training Loss: 20.2646, Validation Loss: 19.8556\n", "Epoch 61/100, Training Loss: 20.5203, Validation Loss: 19.4788\n", "Epoch 62/100, Training Loss: 20.2363, Validation Loss: 19.0894\n", "Epoch 63/100, Training Loss: 20.2424, Validation Loss: 19.4397\n", "Epoch 64/100, Training Loss: 20.2166, Validation Loss: 19.0888\n", "Epoch 65/100, Training Loss: 20.2660, Validation Loss: 19.2093\n", "Epoch 66/100, Training Loss: 20.1363, Validation Loss: 19.6015\n", "Epoch 67/100, Training Loss: 20.3435, Validation Loss: 19.0241\n", "Epoch 68/100, Training Loss: 20.2448, Validation Loss: 19.1516\n", "Epoch 69/100, Training Loss: 20.2065, Validation Loss: 19.5343\n", "Epoch 70/100, Training Loss: 20.2429, Validation Loss: 18.9719\n", "Epoch 71/100, Training Loss: 20.1763, Validation Loss: 19.0917\n", "Epoch 72/100, Training Loss: 20.1780, Validation Loss: 19.0166\n", "Epoch 73/100, Training Loss: 20.1551, Validation Loss: 19.1326\n", "Epoch 74/100, Training Loss: 20.3070, Validation Loss: 19.1101\n", "Epoch 75/100, Training Loss: 20.1309, Validation Loss: 19.4456\n", "Epoch 76/100, Training Loss: 20.2518, Validation Loss: 19.2267\n", "Epoch 77/100, Training Loss: 20.8316, Validation Loss: 19.3149\n", "Epoch 78/100, Training Loss: 20.4429, Validation Loss: 18.9212\n", "Epoch 79/100, Training Loss: 20.1919, Validation Loss: 18.8843\n", "Epoch 80/100, Training Loss: 20.0085, Validation Loss: 18.8347\n", "Epoch 81/100, Training Loss: 20.1430, Validation Loss: 18.8544\n", "Epoch 82/100, Training Loss: 20.0481, Validation Loss: 18.8208\n", "Epoch 83/100, Training Loss: 20.1351, Validation Loss: 19.7139\n", "Epoch 84/100, Training Loss: 20.1366, Validation Loss: 20.0132\n", "Epoch 85/100, Training Loss: 20.1183, Validation Loss: 19.5391\n", "Epoch 86/100, Training Loss: 20.1242, Validation Loss: 19.4825\n", "Epoch 87/100, Training Loss: 20.0286, Validation Loss: 18.8148\n", "Epoch 88/100, Training Loss: 19.9369, Validation Loss: 18.9827\n", "Epoch 89/100, Training Loss: 20.0695, Validation Loss: 18.9963\n", "Epoch 90/100, Training Loss: 20.0451, Validation Loss: 18.7765\n", "Epoch 91/100, Training Loss: 20.0198, Validation Loss: 19.5036\n", "Epoch 92/100, Training Loss: 19.9242, Validation Loss: 19.9364\n", "Epoch 93/100, Training Loss: 20.0899, Validation Loss: 18.6824\n", "Epoch 94/100, Training Loss: 19.9531, Validation Loss: 18.7557\n", "Epoch 95/100, Training Loss: 20.1416, Validation Loss: 18.9370\n", "Epoch 96/100, Training Loss: 20.0730, Validation Loss: 18.9688\n", "Epoch 97/100, Training Loss: 20.3054, Validation Loss: 19.0414\n", "Epoch 98/100, Training Loss: 20.0963, Validation Loss: 18.8696\n", "Epoch 99/100, Training Loss: 19.7927, Validation Loss: 18.6048\n", "Epoch 100/100, Training Loss: 19.9331, Validation Loss: 18.6141\n" ] } ], "source": [ "epochs = 100\n", "train_losses = []\n", "val_losses = []\n", "\n", "best_val_loss = float('inf') # Initialize best validation loss to infinity\n", "\n", "for epoch in range(epochs):\n", " model.train()\n", " running_loss = 0.0 # total training loss for one epoch\n", "\n", "\n", " for xb, yb in train_loader:\n", " optimizer.zero_grad() # clear gradients for this batch\n", "\n", " outputs = model(xb) # forward propagation - predicted outputs for this batch\n", " loss = criterion(outputs, yb) #compute loss for this batch\n", " loss.backward() # backward propagation - compute gradients\n", " optimizer.step() # update weights\n", "\n", " running_loss += loss.item() # accumulate loss for this batch loss is a tensor, so we use .item() to get the scalar py float value \n", " epoch_loss = running_loss / len(train_loader) # average loss for this epoch\n", " train_losses.append(epoch_loss)\n", "\n", "\n", " model.eval() # set model to evaluation mode\n", " running_val_loss = 0.0\n", "\n", " with torch.no_grad(): # we don't need to compute gradients for validation\n", "\n", " for xb, yb in test_loader:\n", "\n", " outputs = model(xb) # forward propagation - predicted outputs for this batch\n", " loss = criterion(outputs, yb) #compute loss for this batch\n", " running_val_loss += loss.item() # accumulate loss for this batch\n", "\n", " epoch_val_loss = running_val_loss / len(test_loader) # \n", " val_losses.append(epoch_val_loss)\n", "\n", " print(f\"Epoch {epoch+1}/{epochs}, Training Loss: {epoch_loss:.4f}, Validation Loss: {epoch_val_loss:.4f}\")\n", "\n", " if epoch_val_loss < best_val_loss:\n", " best_val_loss = epoch_val_loss\n", " torch.save(model.state_dict(), 'best_model.pth') # Save the model's state_dict\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 27, "id": "aa5ba24e", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABAkAAAK8CAYAAACN0OZ9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlcelbwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAb4JJREFUeJzt3QmYXGWZP+yn93Q6+06AkLBvgiCCsqsIoiKIoqLo4I7LqAyK+ziuOCP4d/tG1HEb94VRRMBRcEBAQDYRlJ0kBJKQPel0kl7ru95TXZXuTifpJN1d1V33fV19VdWp010np4uQ86vnfZ6qXC6XCwAAAKDiVVf8GQAAAAAyQgIAAAAgIyQAAAAAMkICAAAAICMkAAAAADJCAgAAACAjJAAAAAAyQgIAAAAgIyQAgF1w0UUXxWmnnVay7x9J/uM//iMOPfTQkrz2s571rLjkkku2u21rXvziF8e73/3uYTkuACil2pK+OgAMkXThvXDhwu3u9/KXv3yXLtKeeuqpeOyxx0r2/SPJsmXL4sEHH9zq8+3t7XHcccfF3Llz4+c///lW9/vnf/7nuOGGG+Lmm2+OiRMnDui1H3rooXj66ae3u21rHn/88aivr4+dkYKRN7zhDXHxxRcP6LiGy8knnxyzZs2Kn/70pyV5fQDKk5AAgFHp61//erS1tRUff+tb34ovfvGL2QXR4YcfXtw+0IvMrUk/c9OmTSX7/tGkrq4ujjjiiPjmN78ZDz/8cOy///5b7LN69ersd/miF71ol393d999d0yaNCmGWgpGUkBSymPoz6OPPuq9B8AWhAQAjEp77713r8fTp0/Pbvfaa6848MADB+11Zs+eXdLvH23e+ta3ZiHBd77znfj85z+/xfM/+MEPorW1NdtvV/UXQgy3cjgGAOhJTwIAKla60EzLDVpaWuJDH/pQHHPMMcWLz7QEIYUJ6evggw+OY489Ni688MJYtGjRdnsKFH7uxo0b48Mf/nA8+9nPjhNOOCGrbuhrV78/+cMf/hBnnHFGPPOZz4xzzz03++R6oOv/B/rn3NFjuv7667c4poE46qijsu/5/ve/Hx0dHVs8/+1vfzt23333rJIgSeeucPzpz3vKKafEZZddlgUJO9MPoKurK7761a9mf7b0Z0xLBNL7oz/be+20jCA919nZGd/97neL+77jHe/Y5jEkP/7xj+OlL31pHHbYYXHSSSdl+/Q8jvQz08/68pe/nFUEnHfeedl5S99zyy23xGBpbm6Oz3zmM3HiiSdmFTgve9nLtlgKksvlst9Xeu1UCXL66adn778NGzbs0D4AlAeVBABUrHQhvGTJknjd614XL3nJS7KL2rTOPXnzm9+cXRQn6WI1rUlPn2ynNfP33XdfsdS9v54ChZ+bfsbzn//8OPvss+Oqq66Kd77zndm69rS9YFe/P12wveY1r4l/+qd/ig9+8IOxdu3arMHejBkzBnRhPtA/544c0xVXXBGvetWrsgvXdExr1qyJCy64YMBVE295y1uyP8PVV18dZ555ZnH7HXfcEX/729/i4x//eNTU1GyxrCRdcN55553Z86mM/0c/+tE2X6e/fgApDPnhD38Yn/3sZ7ML4/RzUj+B/mzvtadOnRq//vWvswAh/TkKPQnGjRu3zWNIf/bLL788/vVf/zX7eWmf97///fHLX/4ybrrpphg7dmx20Z2233vvvfG///u/8a53vStbtpCO+wUveEH2XKqa2dWAIL0Pli9fHpdeemnsu+++2e8khT7pd/GFL3wh2y+9Zgox0tKZo48+OlatWpWFRO9973uzpSED3QeAMpEDgApwySWX5NL/9m699dbittNOOy1XVVWV+5//+Z/itq6urq3+jDVr1uRqampyX/va14rbXv3qV+f22WefXvuln1tdXZ275pprem0/6aSTcoccckivbbvy/W1tbblZs2blnv/85/fab/ny5blx48Zlx7oz+vtzDvSY2tvbc7vttlvu5JNP7rXfkiVLcmPHjh3QMa1evTrX2NiYO+OMM3ptf9vb3pYdw/z587f5/T/96U+z3/WCBQuK25qamnLvfe97e+3Xd9ttt92Wfd+ll17aa78f/vCH2fvkzDPP3O6x9/fa6c980UUX9bt/32P4y1/+kn3/xz/+8V773XTTTdn2T3/608XznB5PnTo1+30VrFixItfQ0JD74Ac/uN1j3X333XPHHHPMVp9Px5Be4/bbb++1Pf3stP2ee+7JHh9++OG5c845Z4vvT+/PgoHsA0B5sNwAgIqWPpXt+Wl1VVVVdps+If7//r//Lxt9l8qsU2l3Wo6QDOQT+gkTJmQl1T2lT1AfeeSR7FPgwfj+9In10qVL47WvfW2v/aZNm5Z9mjwQO/LnHOgxFaozekpd9FM5/kCk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pqBgxowZu7T0QIwFAAAwRDo7O7Pb+vp655ghVQih2tvbd+nnCAkAAACG2K6uE4fheo8JCQAAAICMkAAAAIBhccopp8SHPvShIf8edp6QAAAAgMz69euzsvVtfV1wwQWj8my98pWvjJe+9KVR6Uw3AAAAIDNu3LhspF7B1772tXj/+98fmzZtGpQzdN111w3L97DzVBIAAAAwYM95znPi3e9+d5x99tkxfvz4OOOMM7Lt5513XrHaYPr06XHWWWfFggULtrl0IP2sf/7nf45Xv/rV2ei+mTNnxsUXX9wrqNiZ71m3bl289rWvzUKPPffcMzve0047Ld73vvft9G964cKFceaZZ2Y/c8KECfGKV7winnrqqV77fPazn425c+dGU1NTHHPMMb0Cjm09V06EBAAAAMMkXchuaOsoyVfPi+hddfnll2cXzEuXLo2rrroq2/bDH/4we430de+992YX0uecc852X/cb3/hGFig8/vjjccUVV2TVC7/4xS926Xve8573ZMdw2223xT333JONovz973+/03/ezs7OLAxJtw8++GDcf//9sXbt2uwcFP58v/nNb+LSSy+Nn/3sZ7Fy5cr4+te/Hj/4wQ+2+1y5sdwAAABgmGxs74yD//V/S3K+//Gp02Js/eBcAqaL43/6p3/a6vOzZ8+Or3zlKzF58uSYP39+7L333lvd9zWveU2ce+652f3jjz8+XvjCF8ZNN90Ur3rVq3bqe1atWpUFFim8OPTQQ7N9vvSlL8Uvf/nLnf7zXnvttVk48Ic//CGrXEi+853vxLx58+L666/Pqh1S1USqWkhVAsmRRx4Z3//+97P723qu3KgkAAAAYIcccsghW2xLn9iffvrp2VKDtOQgBQTJE088sc2ftd9++/V6nL5v9erVO/09jzzySPaJ/7Oe9azi8w0NDfGMZzwjdtY//vGPbKlAISBI5syZk4Uh6bnk5S9/eVYl8OxnPzs+97nPxV/+8pfivtt6rtyoJAAAABgmjXU12Sf6pXrtwVJfX9/rcXt7exYQpD4A3/zmN2PWrFnZhXpjY2N0dHRs82elQGFHbet7CuX/fffZleUWua18b9peeJ1UKfDQQw9lVQd//OMf42Uve1kWpqTH23qu77ksNZUEAAAAwyRdUKaS/1J87czF+EA99thj8fTTT2cNBNMFcV1dXck+Ld9///2jpqYm7rzzzuK2tra2+Pvf/77TP/OQQw7JlgwsW7asuG3RokWxZMmSOOigg4rbUlPD1Ich9RxIPRFSIPDnP/95u8+VEyEBAAAAu2SPPfbIuvZ/+9vfjg0bNsTdd98d73jHO0pyVqdMmZJNWvjABz6QLQVIZf4XXnhhLF++fKd/5umnnx4HHnhgvOUtb8kmGqSA4M1vfnMcccQR8YIXvCDb57LLLst6H6QwYePGjfG73/0uamtrY6+99trmc+VGSAAAAMAuSZ+S//SnP8069qf+AOkT8zSmsFRS08TDDjss6wFw+OGHZ9tOPPHErDfBtlx99dXFMY6Fr1e+8pVZZUJqhJgeH3DAAXHwwQdnf+Yrr7yyWKFx/vnnZ+HBySefHNOmTcuOIU1cSM0Nt/VcuanKDeYcDLYrzeucOHFiNi4jjQQBAABGr02bNmXd/dPF4JgxY0p9OBUr9UdIv4OPfOQjccEFF0SlvdfW7cB1qMaFAAAAjCqpIiD1C0hTBVpbW+Mzn/lMdoF89tlnl/rQyp7lBgAAAIwqJ510Uta4MI09PPTQQ+Phhx+OG2+8MWbMmFHqQyt7QgL6df9Ta+Mt378jnly9wRkCAABGlNQv4PLLL4/FixfHqlWr4rrrrotnPvOZpT6sEUFIQL8uufaBuO6BZfHl6x5xhgAAACqEkIB+XXTqAdntFXc/GY8ua3aWAAAAKoCQgH4dOWdyvPDgmdGVi/jiHx52lgAAACqAkID+PXlnXDLl6jio+om45r6lcd+Ta50pAACAUU5IQP/+/NWYducX459n56sI/uN/H3SmAAAARjkhAf2bd0J28/yGB6O2uipuemRF3PrYSmcLAABgFBMS0L+5J2Y3Y5beFa8/amaxmiCXyzljAADAgLz//e+PH/7wh1t93J+Pfexj8d3vfneXz/BAXostCQno37T9IsbNiuhsjffsvzrG1FXHPU+siesfWOaMAQDAKHbxxRfHpz/96X6fW7FiRZx++ulx8803D+hn3XbbbfHoo49u9XF/7rzzznjooYd26JgvvPDC+MlPfrLN1x4qn/jEJ+Kzn/1sjBZCAvpXVVVccjB52W1x/rHzsvuX/v6h6EojDwAAgFFpr732is9//vPR3LzlKPQf/OAHccstt8QRRxyxUz/7sssui/POOy8G26233hqPPfbYsLxWX3fddVfcc889MVrUlvoAKGPzToy47xcR82+Kd5z7wfjR7QvjwaXN8Zt7F8dZR+xe6qMDAACGwOte97qsVP+nP/1pvPWtb+313He+8514zWteE01NTdmn5zfddFNUVVXF9OnT48QTT4w3velNUV299c+if/GLX8Qzn/nM2HfffYvb/vu//zuuvPLKmDp1apx99tlbfM/2Xid9kv/ggw/GqlWrihUOP//5z/t9rfQ6v/zlL7MA5Kijjop//ud/jokTJxaff9e73hUnn3xyrF27Nm688caora2N888/P0466aSdPp9tbW3xzW9+M/7v//4v+3mnnXZa9jN7nqd03Ok8pD/DYYcdFu95z3ti0qRJ231uKKgkYOvm5isJ4qk7Y2JtW1xw0j7Zwy/+4eFo6+hy5gAAYEelHl9tLaX5GmB/sXQB+opXvCK+/e1v99r+l7/8Je6///5485vfnD1+yUteEu973/vive99b3bhfumll8YFF1ywzZ/ddwnAF77whXj3u98dxx13XBx//PFZOJEqFXra3uuceeaZsdtuu8Vzn/vcbL/01djYuMVrfeUrX4lzzz03Dj300DjnnHOywCBd/Hd0dBT3ueWWW+Itb3lL9r3p586YMSOe//znx9/+9rfYWSlU+eIXv5j9OVIA8aEPfSgLJ3qekxe+8IVZBUcKaFIYUghLtvXcUFFJwNZNnhsxcc+ItYsinrg1zj/25PjuLfPjiVUb4ud3LorznrOXswcAADuifUPE52aX5px9ZHFEfdOAdk0Xys973vPiH//4Rxx88MHZthQapAvsY445JnucPqXvKW1P29KFf89P57dm/fr1WZVAunhPn6wn6aK95yf/A3mdI488Mrvdb7/94kUvetFWX+tf//Vfs4v1QsDw4he/OLv4/t73vpf9eQtOOOGE+K//+q/s/itf+cqsAuCKK67IPsXfUakK4Ne//nUWrhTOYzrOVE2QQo/9998/brjhhjj66KPjox/9aPb8y1/+8qxaINnWc0NFJQHb6UuQn3KQlhw0NdTGu5+X/w/2K9c/EhvbOp09AAAYhQoX62l5QbJhw4Zs+UGhiiBZt25ddtGd1v2nZoYXXXRRdHV1xeOPPz6g13jggQeysv50sV6QLtpTENHTrr5OkpYjpNc644wzitsmT56cVSbcfvvtvfY99thjez2eN29eLFmyJHZGqgTYZ599igFBcsopp2SVDnfccUcx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9Oupp56KO++8M1avXu0MUTaWLFkS9957bzQ3N291n/Rceu8uXLhwWI8N+mptbY2bb745/vGPf/R7cpYtWxZ33HFHLF++3MmjZDo6OuK+++7b5t+ZCxYsyP5eXb9+/bAeG/T8+/SBBx7Y5r8Bcrlcts9f//rXaG9vd/IYFsuWLcv+X7+ta6aBXFeV27WXkIBe0l+qr3vd62LfffeNN7zhDTF79uz493//d2eJkrruuuviqKOOimc961nZ+3LWrFlx0UUXZf8g6Okb3/hG9tzrX//6OPjgg+OMM86IDRs2lOy4qWwf+MAH4qSTToqLL7641/b0vn3Pe94Tc+bMifPPPz/23HPPeP/731+y46Ry/fjHP47ddtstzj777Hjxi18cL33pS2Pt2rXF51taWrLthx56aJx33nkxc+bM+Pa3v13SY6by/OQnP4ndd989Xvayl2X/f0//n//Upz7Va5/HH388DjvssDjxxBPjrLPOyv5+/dOf/lSyY2b0+9vf/hbnnntu9r474YQT4pZbbtmp66qyvfbKQQ+f+cxncjNmzMgtWLAge/z73/8+V1VVlbv++uudJ0rm8ssvz911113Fx3feeWeusbEx941vfKO47Z577sneqz//+c+zx08//XRuzpw5uQsvvLAkx0xlu/LKK3OHHnpo7iUveUn21dN//dd/5caNG5f729/+lj2+4447cg0NDbkf/ehHJTpaKtG1116bq66uzv3kJz8pbvvd736Xe+SRR4qP3/3ud+f23nvv3PLly7PHP/jBD7Lvue+++0pyzFSe9evX5+rq6nKf+tSnitt+/etfp08Isr87C5773OfmTj311Fx7e3v2+L3vfW/279nm5uaSHDej349//OPsK/17M70fr7rqqp26rirXay8hAb2kfwy8//3v77XtOc95Tu51r3udM0VZOfnkk3Ovf/3ri4/f85735A488MBe+6S/eCdPnpzr7OwswRFSqRYtWpSbPXt27t5778294hWv2CIkOPbYY3PnnXder21nnXVW7gUveMEwHymV7NnPfnbu7LPP3urz6WJrwoQJuUsvvbTX9nnz5uUuuuiiYThCyOWeeuqp7ALsuuuuK56OFFqlbddcc032+B//+Ef2+IYbbijus2zZslxNTU2vEAyGwurVq7caEgzkuqpcr70sN6Bo3bp1WblWKunu6eijj4577rnH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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "loss_df = pd.DataFrame({'Training Loss': train_losses, 'Validation Loss': val_losses})\n", "plt.figure(figsize=(12, 8))\n", "plt.plot(loss_df['Training Loss'], label='Training Loss')\n", "plt.plot(loss_df['Validation Loss'], label='Validation Loss')\n", "plt.xlabel('Epochs')\n", "plt.ylabel('Losses')\n", "plt.title('Training and Validation Loss')\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 28, "id": "511659d2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Loading the best model\n", "model.load_state_dict(torch.load('best_model.pth'))" ] }, { "cell_type": "code", "execution_count": 29, "id": "da7782a9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Train MSE Loss: 19.5831\n", "Test MSE Loss: 18.6137\n" ] } ], "source": [ "# Evaluate our Model\n", "\n", "model.eval()\n", "with torch.no_grad():\n", " train_preds = model(X_train_tensor)\n", " test_preds = model(X_test_tensor)\n", "\n", " train_mse_loss = criterion(train_preds, y_train_tensor)\n", " test_mse_loss = criterion(test_preds, y_test_tensor)\n", "\n", "print(f\"Train MSE Loss: {train_mse_loss.item():.4f}\")\n", "print(f\"Test MSE Loss: {test_mse_loss.item():.4f}\")" ] }, { "cell_type": "code", "execution_count": 30, "id": "ddb29856", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "R^2 Score for Train Set: 0.9330540895462036\n", "R^2 Score for Test Set: 0.9349498748779297\n" ] } ], "source": [ "from sklearn.metrics import mean_squared_error, r2_score\n", "\n", "print(\"R^2 Score for Train Set:\", r2_score(y_train_tensor.numpy(), train_preds.numpy()))\n", "print(\"R^2 Score for Test Set:\", r2_score(y_test_tensor.numpy(), test_preds.numpy()))" ] }, { "cell_type": "code", "execution_count": null, "id": "5951e152", "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", "
Actual_ValuesPredicted_Values
0433.269989435.928589
1438.160004437.338593
2458.420013461.189423
3480.820007475.738678
4441.410004435.962860
.........
1909456.700012451.425446
1910438.040009432.240143
1911467.799988467.555328
1912437.140015431.679840
1913456.779999456.370483
\n", "

1914 rows × 2 columns

\n", "
" ], "text/plain": [ " Actual_Values Predicted_Values\n", "0 433.269989 435.928589\n", "1 438.160004 437.338593\n", "2 458.420013 461.189423\n", "3 480.820007 475.738678\n", "4 441.410004 435.962860\n", "... ... ...\n", "1909 456.700012 451.425446\n", "1910 438.040009 432.240143\n", "1911 467.799988 467.555328\n", "1912 437.140015 431.679840\n", "1913 456.779999 456.370483\n", "\n", "[1914 rows x 2 columns]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "predicted_df = pd.DataFrame(test_preds.numpy(), columns=['Predicted_Values'])\n", "actual_df = pd.DataFrame(y_test_tensor.numpy(), columns=['Actual_Values'])\n", "\n", "pd.concat([actual_df, predicted_df], axis=1)" ] }, { "cell_type": "code", "execution_count": 32, "id": "5e66339a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ All artifacts exported\n" ] } ], "source": [ "import joblib\n", "torch.save(model.state_dict(), \"gridsync_model.pth\")\n", "joblib.dump(scaler, \"scaler.pkl\")\n", "print(\"✅ All artifacts exported\")" ] }, { "cell_type": "code", "execution_count": null, "id": "a759ef5b", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "gridsync (3.13.13)", "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.13.13" } }, "nbformat": 4, "nbformat_minor": 5 }