From 62d35cee502bbd64dacbbab40d8244ab7ad705ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Wa=C5=82=C4=99sa?= Date: Fri, 6 May 2022 22:45:36 +0200 Subject: [PATCH] =?UTF-8?q?Prze=C5=9Blij=20pliki=20do=20''?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ml_pytorch_results.ipynb | 351 +++++++++++++++++++++++++++++++++++++++ ml_pytorch_results.py | 208 +++++++++++++++++++++++ 2 files changed, 559 insertions(+) create mode 100644 ml_pytorch_results.ipynb create mode 100644 ml_pytorch_results.py diff --git a/ml_pytorch_results.ipynb b/ml_pytorch_results.ipynb new file mode 100644 index 0000000..1f09985 --- /dev/null +++ b/ml_pytorch_results.ipynb @@ -0,0 +1,351 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "98cddc6a-2ce1-4933-a2b7-96d2c2d197f4", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "if (window.IPython && IPython.notebook.kernel) IPython.notebook.kernel.execute('jovian.utils.jupyter.get_notebook_name_saved = lambda: \"' + IPython.notebook.notebook_name + '\"')" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import torch\n", + "import jovian\n", + "import torchvision\n", + "import matplotlib\n", + "import torch.nn as nn\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "import torch.nn.functional as F\n", + "from torchvision.datasets.utils import download_url\n", + "from torch.utils.data import DataLoader, TensorDataset, random_split\n", + "import random\n", + "import os\n", + "import sys\n", + "from sklearn.metrics import mean_squared_error\n", + "from sklearn.metrics import mean_absolute_error" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7bb63556-d009-4d9f-9de0-033a30ad3fc4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(['matches', 'wins', 'draws', 'loses', 'scored', 'missed', 'pts'],\n", + " ['position'])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#load data\n", + "dataframe = pd.read_csv(\"understat.csv\")\n", + "\n", + "#choose columns\n", + "input_cols=list(dataframe.columns)[4:11]\n", + "output_cols = ['position']\n", + "input_cols, output_cols" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c8151c46-c234-42b7-a786-50c73e3aa2f5", + "metadata": {}, + "outputs": [], + "source": [ + "def dataframe_to_arrays(dataframe):\n", + " dataframe_loc = dataframe.copy(deep=True)\n", + " inputs_array = dataframe_loc[input_cols].to_numpy()\n", + " targets_array = dataframe_loc[output_cols].to_numpy()\n", + " return inputs_array, targets_array\n", + "\n", + "inputs_array, targets_array = dataframe_to_arrays(dataframe)\n", + "\n", + "inputs = torch.from_numpy(inputs_array).type(torch.float)\n", + "targets = torch.from_numpy(targets_array).type(torch.float)\n", + "\n", + "dataset = TensorDataset(inputs, targets)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8c89947b-c2fe-407d-9588-3f0087df5955", + "metadata": {}, + "outputs": [], + "source": [ + "train_ds, val_ds = random_split(dataset, [548, 136])\n", + "batch_size=50\n", + "train_loader = DataLoader(train_ds, batch_size, shuffle=True)\n", + "val_loader = DataLoader(val_ds, batch_size)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3b1426a0-5b15-46f8-aea9-871462ca9467", + "metadata": {}, + "outputs": [], + "source": [ + "class Model_xPosition(nn.Module):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.linear = nn.Linear(input_size,output_size) \n", + " \n", + " def forward(self, xb): \n", + " out = self.linear(xb)\n", + " return out\n", + " \n", + " def training_step(self, batch):\n", + " inputs, targets = batch \n", + " # Generate predictions\n", + " out = self(inputs) \n", + " # Calcuate loss\n", + " loss = F.l1_loss(out,targets) \n", + " return loss\n", + " \n", + " def validation_step(self, batch):\n", + " inputs, targets = batch\n", + " out = self(inputs)\n", + " loss = F.l1_loss(out,targets) \n", + " return {'val_loss': loss.detach()}\n", + " \n", + " def validation_epoch_end(self, outputs):\n", + " batch_losses = [x['val_loss'] for x in outputs]\n", + " epoch_loss = torch.stack(batch_losses).mean() \n", + " return {'val_loss': epoch_loss.item()}\n", + " \n", + " def epoch_end(self, epoch, result, num_epochs):\n", + " if (epoch+1) % 100 == 0 or epoch == num_epochs-1:\n", + " print(\"Epoch {} loss: {:.4f}\".format(epoch+1, result['val_loss']))\n", + " \n", + " \n", + "def evaluate(model, val_loader):\n", + " outputs = [model.validation_step(batch) for batch in val_loader]\n", + " return model.validation_epoch_end(outputs)\n", + "\n", + "def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):\n", + " history = []\n", + " optimizer = opt_func(model.parameters(), lr)\n", + " for epoch in range(epochs):\n", + " for batch in train_loader:\n", + " loss = model.training_step(batch)\n", + " loss.backward()\n", + " optimizer.step()\n", + " optimizer.zero_grad()\n", + " result = evaluate(model, val_loader)\n", + " model.epoch_end(epoch, result, epochs)\n", + " history.append(result)\n", + " return history" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f2e22e9a-8724-4084-b706-0be266846c05", + "metadata": {}, + "outputs": [], + "source": [ + "input_size = len(input_cols)\n", + "output_size = len(output_cols)\n", + "model=Model_xPosition()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "efacafe4-797a-4588-b0d8-2e4d883e639a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 100 loss: 2.2152\n", + "Epoch 200 loss: 1.8737\n", + "Epoch 300 loss: 1.8362\n", + "Epoch 400 loss: 1.7904\n", + "Epoch 500 loss: 1.7507\n", + "Epoch 600 loss: 1.7174\n", + "Epoch 700 loss: 1.6977\n", + "Epoch 800 loss: 1.6847\n", + "Epoch 900 loss: 1.6743\n", + "Epoch 1000 loss: 1.6645\n" + ] + } + ], + "source": [ + "epochs = 1000\n", + "lr = 1e-5\n", + "learning_proccess = fit(epochs, lr, model, train_loader, val_loader)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7007ab5a-dc79-4321-beed-cd54dd197858", + "metadata": {}, + "outputs": [], + "source": [ + "def predict_single(input, target, model):\n", + " inputs = input.unsqueeze(0)\n", + " predictions = model(inputs)\n", + " prediction = predictions[0].detach()\n", + "\n", + " return \"Target: \"+str(target)+\" Predicted: \"+str(prediction)+\"\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c946417a-693a-463a-b123-54348266ff6e", + "metadata": {}, + "outputs": [], + "source": [ + "def prediction(input, target, model):\n", + " inputs = input.unsqueeze(0)\n", + " predictions = model(inputs)\n", + " predicted = predictions[0].detach()\n", + " return predicted" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "50c62065-5094-4595-995c-6d0b71f1f28a", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"result.txt\", \"a+\") as file:\n", + " for i in range(0, len(val_ds), 1):\n", + " input_, target = val_ds[i]\n", + " file.write(str(predict_single(input_, target, model)))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fdd6468c-3d50-4131-b2d2-e3190d8b19e5", + "metadata": {}, + "outputs": [], + "source": [ + "expected = []\n", + "predicted = []\n", + "for i in range(0, len(val_ds), 1):\n", + " input_, target = val_ds[i]\n", + " expected.append(float(target))\n", + " predicted.append(float(prediction(input_, target, model)))\n", + "\n", + "MSE = mean_squared_error(expected, predicted)\n", + "MAE = mean_absolute_error(expected, predicted)\n", + "\n", + "with open(\"metrics.txt\", \"a+\") as file:\n", + " file.write(\"Mean squared error: MSE = \"+ str(MSE) + \"\\n\")\n", + " file.write(\"Mean absolute error: MAE = \"+ str(MAE)+ \"\\n\")\n", + "\n", + "with open(\"MSE.txt\", \"a+\") as file:\n", + " file.write(str(MSE) + \"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4a1e5b16-7d80-47b0-8313-e44667687779", + "metadata": {}, + "outputs": [], + "source": [ + "with open('MSE.txt') as file:\n", + " y_MSE = [float(line) for line in file if line]\n", + " x_builds = list(range(1, len(y_MSE) + 1))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1da02242-846a-499c-92eb-4c80fe5f43c4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEGCAYAAAB/+QKOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAAy/UlEQVR4nO3dd3hUZfbA8e9JgUDoVXqTKgQIISCBBJQqCIKyggUVaYKAsO6qu8q6Vlx3QxNEsKCCoCIgSguoEDok1NBbKCImVOkQcn5/zMAvhoEEyGSSyfk8zzyZe9/33jk34py8t5xXVBVjjDEmNR9PB2CMMSZrsgRhjDHGJUsQxhhjXLIEYYwxxiVLEMYYY1zy83QAGalYsWJasWJFT4dhjDHZRmxs7FFVLe6qzasSRMWKFYmJifF0GMYYk22IyP4btdkpJmOMMS5ZgjDGGOOSJQhjjDEuedU1CGOMw+XLlzl06BAXLlzwdCgmiwgICKBs2bL4+/unextLEMZ4oUOHDpE/f34qVqyIiHg6HONhqsqxY8c4dOgQlSpVSvd2dorJGC904cIFihYtasnBACAiFC1a9JZHlJYgjPFSlhxMSrfz78GtCUJEConIdBHZLiLbROTeVO2Pi8gm52uFiNRN0RYvIptFZIOIuPXhhtE/7WLjwZPu/AhjjMl23D2CGAXMV9UaQF1gW6r2fUCEqgYBbwITUrW3UNV6qhrirgBPnrvEV6sP0Hncct6Zu43zl66466OMyVFEhCeffPLaclJSEsWLF6dDhw4ejMpz8uXL5+kQbpnbEoSIFADCgU8AVPWSqp5M2UdVV6jqCefiKqCsu+K5kUJ5cxE1NJxHG5ZnQvRe2o2KZuWeY5kdhjFeJzAwkLi4OM6fPw/AwoULKVOmjIejylhJSUmZ8jlXrly56bIrqkpycvIdfa47RxCVgUTgMxFZLyIfi0jgTfo/C8xLsaxAlIjEikifG20kIn1EJEZEYhITE28r0AIB/rzbpQ5f9W6EAt0nruIfMzfzx4XLt7U/Y4xDu3btmDNnDgBTp06le/fu19rOnj1Lz549adiwIfXr1+f7778HID4+nmbNmhEcHExwcDArVqwAYPHixTRv3pxHHnmEGjVq8Pjjj+NqRszRo0dTq1YtgoKC6NatGwDHjh2jdevW1K9fn759+1KhQgWOHj1KfHw8tWvXvrbtf//7X15//XUAJk6cSMOGDalbty4PP/ww586dA+Dpp59m6NChtGjRgpdeeok9e/bQtm1bGjRoQLNmzdi+fTsA+/bt495776Vhw4a89tprN/wdTZ48mdDQUOrVq0ffvn2vffnny5ePYcOG0ahRI1auXHndcmRkJLVr16Z27dqMHDny2u+uZs2a9O/fn+DgYA4ePHjL/81Scudtrn5AMDBQVVeLyCjgZeC635SItMCRIJqmWB2mqodFpASwUES2q2p06m1VdQLOU1MhISF3NH9qkyrFmD84nMiFO/hk2T5+3pbA251rc3/NkneyW2M86t8/bGHr4T8ydJ+1ShfgXw/ek2a/bt268cYbb9ChQwc2bdpEz549Wbp0KQBvv/029913H59++iknT54kNDSUli1bUqJECRYuXEhAQAC7du2ie/fu12qsrV+/ni1btlC6dGnCwsJYvnw5TZs2/dNnDh8+nH379pE7d25Onjzp+B38+980bdqUYcOGMWfOHCZMSH02+3pdunShd+/eALz66qt88sknDBw4EICdO3eyaNEifH19uf/++xk/fjxVq1Zl9erV9O/fn59//pnBgwfz3HPP0aNHD8aOHevyM7Zt28bXX3/N8uXL8ff3p3///kyZMoUePXpw9uxZateuzRtvvAHwp+XY2Fg+++wzVq9ejarSqFEjIiIiKFy4MDt27OCzzz5j3LhxaR5jWtw5gjgEHFLV1c7l6TgSxp+ISBDwMdBJVa+d21HVw86fCcBMINSNsV6TJ5cv/2xfixn9wyiYx59nP49h0NT1HDtzMTM+3hivEhQURHx8PFOnTuWBBx74U1tUVBTDhw+nXr16NG/enAsXLnDgwAEuX75M7969qVOnDl27dmXr1q3XtgkNDaVs2bL4+PhQr1494uPjXX7m448/zuTJk/Hzc/wNHB0dzRNPPAFA+/btKVy4cJqxx8XF0axZM+rUqcOUKVPYsmXLtbauXbvi6+vLmTNnWLFiBV27dr02Avjtt98AWL58+bURU8prMSn99NNPxMbG0rBhQ+rVq8dPP/3E3r17AfD19eXhhx++1jfl8rJly+jcuTOBgYHky5ePLl26XEu8FSpUoHHjxmkeX3q4bQShqkdE5KCIVFfVHcD9wNaUfUSkPDADeFJVd6ZYHwj4qOpp5/vWwBvuitWVeuUK8cPApoxbvJuxv+xm2e6j/OvB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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.xlabel('Number of builds')\n", + "plt.ylabel('MSE')\n", + "plt.plot(x_builds, y_MSE, label='Mean squared error')\n", + "plt.legend()\n", + "plt.show()\n", + "plt.savefig('RMSplot.png')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dffe789-1ad5-44f1-8f21-92b9c89ed974", + "metadata": {}, + "outputs": [], + "source": [ + "!jupyter nbconvert --to script ml_pytorch.ipynb" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ml_pytorch_results.py b/ml_pytorch_results.py new file mode 100644 index 0000000..d5db0ca --- /dev/null +++ b/ml_pytorch_results.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[1]: + + +import torch +import jovian +import torchvision +import matplotlib +import torch.nn as nn +import pandas as pd +import matplotlib.pyplot as plt +import seaborn as sns +import torch.nn.functional as F +from torchvision.datasets.utils import download_url +from torch.utils.data import DataLoader, TensorDataset, random_split +import random +import os +import sys +from sklearn.metrics import mean_squared_error +from sklearn.metrics import mean_absolute_error + + +# In[2]: + + +#load data +dataframe = pd.read_csv("understat.csv") + +#choose columns +input_cols=list(dataframe.columns)[4:11] +output_cols = ['position'] +input_cols, output_cols + + +# In[3]: + + +def dataframe_to_arrays(dataframe): + dataframe_loc = dataframe.copy(deep=True) + inputs_array = dataframe_loc[input_cols].to_numpy() + targets_array = dataframe_loc[output_cols].to_numpy() + return inputs_array, targets_array + +inputs_array, targets_array = dataframe_to_arrays(dataframe) + +inputs = torch.from_numpy(inputs_array).type(torch.float) +targets = torch.from_numpy(targets_array).type(torch.float) + +dataset = TensorDataset(inputs, targets) + + +# In[4]: + + +train_ds, val_ds = random_split(dataset, [548, 136]) +batch_size=50 +train_loader = DataLoader(train_ds, batch_size, shuffle=True) +val_loader = DataLoader(val_ds, batch_size) + + +# In[5]: + + +class Model_xPosition(nn.Module): + def __init__(self): + super().__init__() + self.linear = nn.Linear(input_size,output_size) + + def forward(self, xb): + out = self.linear(xb) + return out + + def training_step(self, batch): + inputs, targets = batch + # Generate predictions + out = self(inputs) + # Calcuate loss + loss = F.l1_loss(out,targets) + return loss + + def validation_step(self, batch): + inputs, targets = batch + out = self(inputs) + loss = F.l1_loss(out,targets) + return {'val_loss': loss.detach()} + + def validation_epoch_end(self, outputs): + batch_losses = [x['val_loss'] for x in outputs] + epoch_loss = torch.stack(batch_losses).mean() + return {'val_loss': epoch_loss.item()} + + def epoch_end(self, epoch, result, num_epochs): + if (epoch+1) % 100 == 0 or epoch == num_epochs-1: + print("Epoch {} loss: {:.4f}".format(epoch+1, result['val_loss'])) + + +def evaluate(model, val_loader): + outputs = [model.validation_step(batch) for batch in val_loader] + return model.validation_epoch_end(outputs) + +def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD): + history = [] + optimizer = opt_func(model.parameters(), lr) + for epoch in range(epochs): + for batch in train_loader: + loss = model.training_step(batch) + loss.backward() + optimizer.step() + optimizer.zero_grad() + result = evaluate(model, val_loader) + model.epoch_end(epoch, result, epochs) + history.append(result) + return history + + +# In[6]: + + +input_size = len(input_cols) +output_size = len(output_cols) +model=Model_xPosition() + + +# In[7]: + + +epochs = 1000 +lr = 1e-5 +learning_proccess = fit(epochs, lr, model, train_loader, val_loader) + + +# In[8]: + + +def predict_single(input, target, model): + inputs = input.unsqueeze(0) + predictions = model(inputs) + prediction = predictions[0].detach() + + return "Target: "+str(target)+" Predicted: "+str(prediction)+"\n" + + +# In[9]: + + +def prediction(input, target, model): + inputs = input.unsqueeze(0) + predictions = model(inputs) + predicted = predictions[0].detach() + return predicted + + +# In[10]: + + +with open("result.txt", "a+") as file: + for i in range(0, len(val_ds), 1): + input_, target = val_ds[i] + file.write(str(predict_single(input_, target, model))) + + +# In[11]: + + +expected = [] +predicted = [] +for i in range(0, len(val_ds), 1): + input_, target = val_ds[i] + expected.append(float(target)) + predicted.append(float(prediction(input_, target, model))) + +MSE = mean_squared_error(expected, predicted) +MAE = mean_absolute_error(expected, predicted) + +with open("metrics.txt", "a+") as file: + file.write("Mean squared error: MSE = "+ str(MSE) + "\n") + file.write("Mean absolute error: MAE = "+ str(MAE)+ "\n") + +with open("MSE.txt", "a+") as file: + file.write(str(MSE) + "\n") + + +# In[12]: + + +with open('MSE.txt') as file: + y_MSE = [float(line) for line in file if line] + x_builds = list(range(1, len(y_MSE) + 1)) + + +# In[13]: + + +plt.xlabel('Number of builds') +plt.ylabel('MSE') +plt.plot(x_builds, y_MSE, label='Mean squared error') +plt.legend() +plt.show() +plt.savefig('RMSplot.png') + + +# In[ ]: + + +# get_ipython().system('jupyter nbconvert --to script ml_pytorch.ipynb') +