From 0bfce163293acd0e11b7b6436c00d0c2bb5ed7c2 Mon Sep 17 00:00:00 2001 From: ganiyuolalekan Date: Wed, 15 Apr 2020 15:24:05 +0000 Subject: [PATCH 1/3] Ganiyu Olalekan's Implementation of the Logistic Regression Algorithm --- ...ic Regression Scratch Implementation.ipynb | 281 ++++++++++++++++++ ganiyu_olalekan_matthew/README.md | 15 + 2 files changed, 296 insertions(+) create mode 100644 ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb create mode 100644 ganiyu_olalekan_matthew/README.md diff --git a/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb b/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb new file mode 100644 index 0000000..f85c3cd --- /dev/null +++ b/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb @@ -0,0 +1,281 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn import datasets\n", + "from sklearn import linear_model" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "iris = datasets.load_iris()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class LogisticRegression:\n", + " def __init__(self, lr=0.01, num_iter=100, verbose=False, fit_intercept=True):\n", + " self.lr = lr\n", + " self.theta = None\n", + " self.verbose = verbose\n", + " self.num_iter = num_iter\n", + " self.fit_intercept = fit_intercept\n", + "\n", + " @staticmethod\n", + " def __intercept(data):\n", + " intercept = np.ones((data.shape[0], 1))\n", + " return np.concatenate((intercept, data), axis=1)\n", + "\n", + " @staticmethod\n", + " def __sigmoid(z):\n", + " return 1 / (1 + np.exp(-z))\n", + "\n", + " @staticmethod\n", + " def __loss(h, y_hat):\n", + " return (-y_hat * np.log(h) - (1 - y_hat) * np.log(1 - h)).mean()\n", + "\n", + " def fit(self, data, target):\n", + " if self.fit_intercept:\n", + " data = self.__intercept(data)\n", + "\n", + " self.theta = np.zeros((data.shape[1], 1))\n", + "\n", + " for _ in range(self.num_iter):\n", + " z = np.dot(data, self.theta)\n", + " h = self.__sigmoid(z)\n", + " loss = self.__loss(h, target)\n", + " gradient_d = np.dot(data.T, (h - target)) / target.shape[0]\n", + " self.theta -= (self.lr * gradient_d)\n", + "\n", + " if self.verbose:\n", + " print(f\"Loss: {loss}\")\n", + "\n", + " def __predict_probability(self, data):\n", + " return self.__sigmoid(np.dot(self.__intercept(data), self.theta))\n", + "\n", + " def predict(self, test):\n", + " return self.__predict_probability(test).round()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "X = iris.data[:100, :]\n", + "y = iris.target.reshape(150, 1)[:100, :]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "x_train = np.vstack((X[:32, :], X[50:82, :]))\n", + "x_test = np.vstack((X[32:50, :], X[82:100, :]))\n", + "y_train = np.vstack((y[:32, :], y[50:82, :]))\n", + "y_test = np.vstack((y[32:50, :], y[82:100, :]))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def accuracy(prediction, output):\n", + " bool_elem = (prediction == output)\n", + " elem = bool_elem.size\n", + " return (100 / elem) * bool_elem.ravel().tolist().count(True)\n", + "\n", + "\n", + "def data_visualization(data, target, figsize=(10, 6)):\n", + " plt.figure(figsize=figsize)\n", + " plt.scatter(data[:, :1][target == 0], data[:, 1:2][target == 0], color='b', label='0')\n", + " plt.scatter(data[:, :1][target == 1], data[:, 1:2][target == 1], color='r', label='1')\n", + " plt.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "data_visualization(X, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "model = LogisticRegression(num_iter=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 10.6 ms, sys: 0 ns, total: 10.6 ms\n", + "Wall time: 9.62 ms\n" + ] + } + ], + "source": [ + "# Time Measurement\n", + "\n", + "%time model.fit(x_train, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accuracy: 100.0\n" + ] + } + ], + "source": [ + "# Accuracy Measurement in %\n", + "\n", + "prediction = model.predict(x_test)\n", + "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 20.9 ms, sys: 159 µs, total: 21 ms\n", + "Wall time: 20.1 ms\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/olalekan/.local/lib/python3.6/site-packages/sklearn/utils/validation.py:760: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().\n", + " y = column_or_1d(y, warn=True)\n" + ] + }, + { + "data": { + "text/plain": [ + "LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,\n", + " intercept_scaling=1, l1_ratio=None, max_iter=100,\n", + " multi_class='auto', n_jobs=None, penalty='l2',\n", + " random_state=None, solver='lbfgs', tol=0.0001, verbose=0,\n", + " warm_start=False)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Comparing with the scikit learn Logistic Regression class\n", + "\n", + "sk_model = linear_model.LogisticRegression()\n", + "\n", + "%time sk_model.fit(x_train, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accuracy: 50.0\n" + ] + } + ], + "source": [ + "# Comparing with the scikit learn Logistic Regression class\n", + "# Accuracy Measurement in %\n", + "\n", + "prediction = sk_model.predict(x_test)\n", + "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ganiyu_olalekan_matthew/README.md b/ganiyu_olalekan_matthew/README.md new file mode 100644 index 0000000..2658849 --- /dev/null +++ b/ganiyu_olalekan_matthew/README.md @@ -0,0 +1,15 @@ +# ML-Logistic-regression-algorithm-challenge + +Data Science Nigeria (DSN) open challenge on implementing a Logistic Regression algorithm from scratch using Python or R programming. + + +## Logistic Regression + +Logistic regression is the appropriate regression analysis to conduct when the dependent variable is binary. Like all regression analyses, the logistic regression is a predictive analysis. Logistic regression is used to describe data and to explain the relationship between one dependent binary variable and one or more nominal, ordinal, interval or ratio-level independent variables. + +## Implemetation + +The Logistic Regression algorithm was implement with a jupyter notebook using python. + +The iris dataset from sklearn.datasets.load_iris() was used to test the speed and accuracy of the algorithm. Unlike the default iris dataset, my implementation modified it to match the binary requirement of the logistic regression algorithm. Given that the algorithm (Logistic Algorithm) is a Binary Classification Algorithm. + From 30e461e1cb3cc401c86dc57f109e15a762c382f1 Mon Sep 17 00:00:00 2001 From: ganiyuolalekan Date: Fri, 17 Apr 2020 15:07:34 +0000 Subject: [PATCH 2/3] Added Regularization to the Logistic Regression Algorithm --- ...ic Regression Scratch Implementation.ipynb | 156 ++++++++++++++++-- 1 file changed, 142 insertions(+), 14 deletions(-) diff --git a/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb b/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb index f85c3cd..4e0bbe6 100644 --- a/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb +++ b/ganiyu_olalekan_matthew/Logistic Regression Scratch Implementation.ipynb @@ -6,6 +6,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Importing necessary libraries to run and test the Logistic Regression Algorithm\n", + "\n", "%matplotlib inline\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", @@ -19,6 +21,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Loading the sklearn.datasets.load_iris() to use to test the accuracy of the alogorithm\n", + "\n", "iris = datasets.load_iris()" ] }, @@ -28,14 +32,25 @@ "metadata": {}, "outputs": [], "source": [ + "# The logistic regression class\n", + "\n", "class LogisticRegression:\n", - " def __init__(self, lr=0.01, num_iter=100, verbose=False, fit_intercept=True):\n", + " def __init__(self, lr=0.01, num_iter=100, verbose=False, lambd=0.0, fit_intercept=True):\n", " self.lr = lr\n", " self.theta = None\n", + " self.lambd = lambd\n", " self.verbose = verbose\n", " self.num_iter = num_iter\n", " self.fit_intercept = fit_intercept\n", "\n", + " def __l2_regularization(self):\n", + " \"\"\"\n", + " L2 Regularization or Euclidean Normal, used to reduce overfitting.\n", + " \n", + " Could be turned off by leaving lambd set to 0\n", + " \"\"\"\n", + " return (self.lambd / 2) * np.sum(np.square(self.theta)).mean() \n", + " \n", " @staticmethod\n", " def __intercept(data):\n", " intercept = np.ones((data.shape[0], 1))\n", @@ -43,13 +58,23 @@ "\n", " @staticmethod\n", " def __sigmoid(z):\n", + " \"\"\"\n", + " The Sigmoid activation function which is best fit for binary classification function\n", + " \"\"\"\n", " return 1 / (1 + np.exp(-z))\n", "\n", " @staticmethod\n", " def __loss(h, y_hat):\n", + " \"\"\"\n", + " The Cost function\n", + " Turn verbosa (verbosa=True) ON to examine if the cost function is being minimized\n", + " \"\"\"\n", " return (-y_hat * np.log(h) - (1 - y_hat) * np.log(1 - h)).mean()\n", "\n", " def fit(self, data, target):\n", + " \"\"\"\n", + " The fit function used to fit data to model\n", + " \"\"\"\n", " if self.fit_intercept:\n", " data = self.__intercept(data)\n", "\n", @@ -59,16 +84,22 @@ " z = np.dot(data, self.theta)\n", " h = self.__sigmoid(z)\n", " loss = self.__loss(h, target)\n", - " gradient_d = np.dot(data.T, (h - target)) / target.shape[0]\n", + " gradient_d = np.dot(data.T, (h - target)) / target.shape[0] + self.__l2_regularization()\n", " self.theta -= (self.lr * gradient_d)\n", "\n", " if self.verbose:\n", " print(f\"Loss: {loss}\")\n", "\n", " def __predict_probability(self, data):\n", + " \"\"\"\n", + " Probability prediction function of data (mostly test data).\n", + " \"\"\"\n", " return self.__sigmoid(np.dot(self.__intercept(data), self.theta))\n", "\n", " def predict(self, test):\n", + " \"\"\"\n", + " rounded prediction of the probability to either 0 or 1, as this is a binary classification model\n", + " \"\"\"\n", " return self.__predict_probability(test).round()" ] }, @@ -78,6 +109,11 @@ "metadata": {}, "outputs": [], "source": [ + "# Initializing data and its target output\n", + "\n", + "# The iris dataset is grouped in fifty's and is of 3 different sets \n", + "# So to test the binary classification algorithm better, took the first 2 sets (the first 100)\n", + "\n", "X = iris.data[:100, :]\n", "y = iris.target.reshape(150, 1)[:100, :]" ] @@ -88,10 +124,15 @@ "metadata": {}, "outputs": [], "source": [ - "x_train = np.vstack((X[:32, :], X[50:82, :]))\n", - "x_test = np.vstack((X[32:50, :], X[82:100, :]))\n", - "y_train = np.vstack((y[:32, :], y[50:82, :]))\n", - "y_test = np.vstack((y[32:50, :], y[82:100, :]))" + "# Data spilting\n", + "\n", + "# To avoid inaccuracy, the data is splitted evenly between both datasets\n", + "# 70% training and 30% testing in even distribution\n", + "\n", + "x_train = np.vstack((X[:35, :], X[50:85, :]))\n", + "x_test = np.vstack((X[35:50, :], X[85:100, :]))\n", + "y_train = np.vstack((y[:35, :], y[50:85, :]))\n", + "y_test = np.vstack((y[35:50, :], y[85:100, :]))" ] }, { @@ -100,6 +141,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Accuracy and data visualization function\n", + "\n", "def accuracy(prediction, output):\n", " bool_elem = (prediction == output)\n", " elem = bool_elem.size\n", @@ -108,8 +151,8 @@ "\n", "def data_visualization(data, target, figsize=(10, 6)):\n", " plt.figure(figsize=figsize)\n", - " plt.scatter(data[:, :1][target == 0], data[:, 1:2][target == 0], color='b', label='0')\n", - " plt.scatter(data[:, :1][target == 1], data[:, 1:2][target == 1], color='r', label='1')\n", + " plt.scatter(data[:, :1][target == 0], data[:, 1:2][target == 0], color='b', label='Label 0')\n", + " plt.scatter(data[:, :1][target == 1], data[:, 1:2][target == 1], color='r', label='Label 1')\n", " plt.legend()" ] }, @@ -120,7 +163,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlMAAAFlCAYAAADPim3FAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3df3Ac5Z3n8c/XM2RtLeQXFrfBwhYpKIoKeGMsB1yhqBi8cRIo8Ueg4kS5PcJtiZMIIcVu5Y6lCF6qXJcDksvtsnZK/Lhi17qELPsD2Aqs2SQuQl2MSybggE3K7IKNHBYb5eLE0TrE9vf+6NEPTyRNj/qZ6Wd63q+qqVE/3er59tMt6aueZ56vubsAAAAwPwvyDgAAAKCVkUwBAABkQDIFAACQAckUAABABiRTAAAAGZBMAQAAZFBOu6GZlSSNSDrg7ldVrbtO0t2SDlSa7nX3++fa3+LFi727u7uuYAEAAPKwc+fOt9y9c6Z1qZMpSTdL2iPpnbOsf9jdP592Z93d3RoZGanj5QEAAPJhZvtmW5fqbT4z65J0paQ57zYBAAC0m7Rjpr4u6UuSTsyxzSfNbJeZPWJmZ820gZn1m9mImY0cOnSo3lgBAACiUzOZMrOrJB10951zbPa4pG53Xy7pKUkPzbSRuw+5e4+793R2zvi2IwAAQEtJM2bqw5J6zewTkhZKeqeZbXH3z05s4O5j07a/X9JdYcMEAACh/OY3v9Ho6KiOHj2adyjRWbhwobq6unTKKaek/p6ayZS73yrpVkkys49I+pPpiVSl/X3u/kZlsVfJQHUAABCh0dFRnXbaaeru7paZ5R1ONNxdY2NjGh0d1dlnn536++Y9z5SZ3WlmvZXFL5jZS2b2gqQvSLpuvvsFAACNdfToUZ1++ukkUlXMTKeffnrdd+zqmRpB7r5N0rbK11+e1j559woAAMSPRGpm8+kXZkAHAABNd+qpp6bedsOGDbrnnnuC7P/JJ5/Ueeedp3POOUdf+cpX6trnbEimAABAWzh+/LhuvPFGPfHEE9q9e7e++c1vavfu3Zn3SzIFAADmNDwsdXdLCxYkz8PDjXmdxx9/XBdffLFWrFihtWvX6s0335xc98ILL2j16tU699xzdd99902233333Vq1apWWL1+uO+64Y87979ixQ+ecc47e//736x3veIfWr1+vRx99NHPcJFMAAGBWw8NSf7+0b5/knjz39zcmobr00ku1fft2/ehHP9L69et1111TMy3t2rVL3/ve9/TDH/5Qd955p376059q69at2rt3r3bs2KHnn39eO3fu1NNPPz3r/g8cOKCzzpqaV7yrq0sHDhyYdfu0SKYAtI1m/XcNFMltt0nj4ye3jY8n7aGNjo5q3bp1uvDCC3X33XfrpZdemlx39dVXa9GiRVq8eLHWrFmjHTt2aOvWrdq6datWrFihiy66SC+//LL27t0bPrAa6vo0HwC0qon/rif+KEz8dy1JfX35xQXEbv/++tqzuOmmm3TLLbeot7dX27Zt04YNGybXVX/Kzszk7rr11lt1ww03pNr/kiVL9Prrr08uj46OasmSJZnj5s4UgLbQzP+ugSJZurS+9iwOHz48mdw89NDJlekeffRRHT16VGNjY9q2bZtWrVqldevW6cEHH9SRI0ckJW/jHTx4cNb9r1q1Snv37tWrr76qt99+W9/61rfU29s76/ZpcWcKQFto5n/XQJFs3HjyXV1J6uhI2rMYHx9XV1fX5PItt9yiDRs26Nprr9V73vMeXX755Xr11Vcn1y9fvlxr1qzRW2+9pdtvv11nnnmmzjzzTO3Zs0erV6+WlEyHsGXLFp1xxhkzvma5XNa9996rdevW6fjx47r++uv1gQ98INuBSDJ3z7yT+ejp6fGRkZFcXhtA++nuTt7aq7ZsmfTaa82OBsjXnj17dP7556fefng4uYu7f39yR2rjxmK/PT5T/5jZTnfvmWl73uYD0BY2bkz+m54uxH/XQDvo60v+6ThxInkuciI1HyRTANpCX580NJTciTJLnoeG+KMAIDvGTAFoG319JE8AwuPOFAAAQAYkUwAAABmQTAEAAGRAMgUAAJru1FNPTb3thg0bdM899wTZ//XXX68zzjhDF1xwQV37mwvJFAAAaBvXXXednnzyyaD7JJkCAABza1KV8Mcff1wXX3yxVqxYobVr1+rNN9+cXPfCCy9o9erVOvfcc3XfffdNtt99991atWqVli9frjvuuKPma1x22WV673vfGzRupkYAAACza2KV8EsvvVTbt2+Xmen+++/XXXfdpa9+9auSpF27dmn79u361a9+pRUrVujKK6/Uiy++qL1792rHjh1yd/X29urpp5/WZZddFjSuWkimAADA7OaqEh44mRodHdWnPvUpvfHGG3r77bd19tlnT667+uqrtWjRIi1atEhr1qzRjh079Mwzz2jr1q1asWKFJOnIkSPau3cvyRQAAIhIE6uE33TTTbrlllvU29urbdu2acOGDZPrzOykbc1M7q5bb71VN9xwQ/BY6sGYKQAAMLulS+trz+Dw4cNasmSJJOmhhx46ad2jjz6qo0ePamxsTNu2bdOqVau0bt06Pfjggzpy5Igk6cCBAzp48GDwuGohmQIAALNrUJXw8fFxdXV1TT6+9rWvacOGDbr22mu1cuVKLV68+KTtly9frjVr1uiSSy7R7bffrjPPPFMf/ehH9ZnPfEarV6/WhRdeqGuuuUa//OUv53zdT3/601q9erV+8pOfqKurSw888ECm45Akc/fMO5mPnp4eHxkZyeW1AQBoZ3v27NH555+f/huGh5MxUvv3J3ekNm4sdKHLmfrHzHa6e89M2zNmCgAAzI0q4XPibT4AAIAMSKYAAAAyIJkCAKAN5TVmOnbz6ReSKQAN0aTqEwDmYeHChRobGyOhquLuGhsb08KFC+v6PgagAwiuidUnAMxDV1eXRkdHdejQobxDic7ChQvV1dVV1/cwNQKA4Lq7kwSq2rJl0muvNTsaAMhurqkReJsPQHBNrD4BALkjmQIQXBOrTwBA7kimAATXoOoTABAlkikAwfX1SUNDyRgps+R5aIjB5wCKiU/zAWgIqk8AaBfcmQIAAMiAZAoAACADkikAAIAMSKYAAAAyIJkCckLtOgAoBj7NB+SA2nUAUBzcmQJycNttU4nUhPHxpB0A0FpIpoAcULsOAIqDZArIAbXrAKA4SKaAHFC7DgCKg2QKyAG16wCgOPg0H5ATatcBQDFwZwoAACADkikAAIAMSKYAAAAyIJkCAADIIHUyZWYlM/uRmf3jDOt+x8weNrNXzOxZM+sOGSQAxIbaigAm1HNn6mZJe2ZZ958l/T93P0fS/5T0P7IGBgCxmqituG+f5D5VW5GECmhPqZIpM+uSdKWk+2fZ5GpJD1W+fkTSFWZm2cMDgPhQWxHAdGnvTH1d0pcknZhl/RJJr0uSux+TdFjS6dUbmVm/mY2Y2cihQ4fmES4A5I/aigCmq5lMmdlVkg66+86sL+buQ+7e4+49nZ2dWXcHALmgtiKA6dLcmfqwpF4ze03StyRdbmZbqrY5IOksSTKzsqR3SRoLGCcARIPaigCmq5lMufut7t7l7t2S1kv6nrt/tmqzxyT9p8rX11S28aCRAkAkqK0IYLp51+Yzszsljbj7Y5IekPTXZvaKpJ8pSboAoLCorQhgQl3JlLtvk7St8vWXp7UflXRtyMAAAABaATOgAwAAZEAyBQAAkAHJFAAAQAYkUwAAABmQTAGYNDgolcvJx/3L5WQZADC3eU+NAKBYBgelzZunlo8fn1retCmfmACgFXBnCoCkZNLJetoBAAmSKQCSkjtR9bQDABIkUwAkSaVSfe0AgATJFABJUn9/fe0AgAQD0AFImhpkPjSUvLVXKiWJFIPPAWBuJFMAJm3aRPIEAPXibT4AAIAMSKYAAAAyIJkCAADIgGQKAAAgA5IpoA5r1yZ16yYea9fmHRHqMTwsdXdLCxYkz8PDeUcEoAhIpoCU1q6Vvvvdk9u++10SqlYxPJxM9bBvn+SePPf3k1AByM7cPZcX7unp8ZGRkVxeG5gPs9nX5fRjhDp0dycJVLVly6TXXmt2NABajZntdPeemdZxZwpAW9i/v752AEiLZApAW1i6tL52AEiLZApI6Yor6mtHXDZulDo6Tm7r6EjaASALkikgpX/+599OnK64ImlH/Pr6krqDy5Yl49+WLUuW+/ryjgxAq2MAOgAAQA0MQAcAAGgQkikAAIAMSKYAAAAyIJkCAADIgGQKqEOMtd1CxhTj8QFA7Mp5BwC0ionabuPjyfJEbTcpv4/Xh4wpxuMDgFbA1AhASjHWdgsZU4zHBwCxYGoEIIAYa7uFjCnG4wOAVkAyBaQUY223kDHFeHwA0ApIpoCUYqztFjKmGI8PAFoByRSQUoy13ULGFOPxAUArYAA6AABADQxABwAAaBCSKQAAgAxIpgAAADIgmQIAAMiAZKqFFb2OWqjjK3o/AQDyRW2+FlX0Omqhjq/o/QQAyB9TI7SootdRC3V8Re8nAEBzMDVCARW9jlqo4yt6PwEA8kcy1aKKXkct1PEVvZ8AAPkjmWpRRa+jFur4it5PAID8kUy1qKLXUQt1fEXvJwBA/hiADgAAUAMD0AEAABqEZAoAACADkikAAIAMSKYAAAAyqJlMmdlCM9thZi+Y2Utm9mczbHOdmR0ys+crjz9qTLhA/QYHpXI5+TRfuZwsx7CvotcMLPrxAcCENLX5fi3pcnc/YmanSHrGzJ5w9+1V2z3s7p8PHyIwf4OD0ubNU8vHj08tb9qU376KXjOw6McHANPVNTWCmXVIekbSgLs/O639Okk99SRTTI2AZiiXk6SnWqkkHTuW376KXjOw6McHoP1knhrBzEpm9rykg5Kemp5ITfNJM9tlZo+Y2Vmz7KffzEbMbOTQoUOpDwCYr5mSn7nam7WvotcMLPrxAcB0qZIpdz/u7h+U1CXpQ2Z2QdUmj0vqdvflkp6S9NAs+xly9x537+ns7MwSN5BKqVRfe7P2VfSagUU/PgCYrq5P87n7zyV9X9LHqtrH3P3XlcX7Ja0MEx6QzcQ4nbTtzdpX0WsGFv34AGC6NJ/m6zSzd1e+XiTpDyS9XLXN+6Yt9kraEzJIYL42bZIGBqbuHpVKyXK9A8ZD76voNQOLfnwAMF3NAehmtlzJ23YlJcnXt939TjO7U9KIuz9mZv9dSRJ1TNLPlAxQf3nWnYoB6AAAoHXMNQCdQscAAAA1UOgYAACgQUimAAAAMiCZAgAAyIBkCgAAIAOSqRYWYyHZkIWAY4wpZJ9z/hBcjBcV0A7cPZfHypUrHfO3ZYt7R4e7NPXo6Eja8zIwcHI8E4+BgWLEFLLPOX8ILsaLCigQJdNBzZjTMDVCi4qxkGzIQsChxFqcmPOH4GK8qIACYZ6pAlqwIPnXs5qZdOJE8+OZeO3Z5HSZBY0pZJ9z/hBcjBcVUCDMM1VAMRaSDVkIOJRYixNz/hBcjBcV0CZIplpUjIVkQxYCDiXW4sScPwQX40UFtIvZBlM1+sEA9Oy2bHFftszdLHmOYZzpwIB7qZSMfS2V4hi8HDKmkH3O+UNwMV5UQEGIAegAAADzx5gpAACABiGZAgAAyIBkCgAAIAOSKQAAgAxIplB4Ra+nB7QNfgARqXLeAQCNNDyczJM0Pp4s79s3NW9SX19++wJQJ34AETGmRkChFb2eHtA2+AFEzpgaAW1r//762pu1LwB14gcQESOZQqEVvZ4e0Db4AUTESKZQaEWvpwe0DX4AETGSKRRaX580NJQMqzBLnoeG5jdeNeS+ANSJH0BEjAHoAAAANTAAHQAAoEFIpgAAADIgmQIAAMiAZAoAACADkqkUYi0HFSquwUGpXE4+IFMuJ8tFEuv5AyZxkTYffd7aYjt/7p7LY+XKld4Ktmxx7+hwl6YeHR1JexHiGhg4eR8Tj4GBxsTdbLGeP2ASF2nz0eetLafzJ2nEZ8lpmBqhhljLQYWKq1yWjh//7fZSSTp2bL7RxSPW8wdM4iJtPvq8teV0/uaaGoFkqoYFC5K0t5qZdOJE8+OZECous9nX5XRpBBXr+QMmcZE2H33e2nI6f8wzlUGs5aBCxVUq1dfeamI9f8AkLtLmo89bW4Tnj2SqhljLQYWKq7+/vvZWE+v5AyZxkTYffd7aYjx/sw2mavSjVQaguydj2pYtczdLnmMZoxgqroEB91IpGcNXKhVn8PmEWM8fMImLtPno89aWw/kTA9ABAADmjzFTAAAADUIyBQAAkAHJFAAAQAYkUwAAABmQTLWw2EoTSWFjKnrNQABomBh/gcb4RyuQct4BYH6Gh5O5oMbHk+V9+6bmhurra/2YBgelzZunlo8fn1retCl7rABQWDH+Ao3xj1ZATI3QomIsLRUypqLXDASAhonxF2iMf7TqxNQIBbR/f33tzRAyppl+D8zVDgCoiPEXaIx/tAIimWpREZYmChpT0WsGAkDDxPgLNMY/WgGRTLWoGEsThYyp6DUDAaBhYvwFGuMfrYBIplpUX580NJS83WyWPA8N5TuOL2RMmzZJAwNT/0iVSskyg88BoIYYf4HG+EcrIAagAwAA1MAAdAAAgAYhmQIAAMiAZAoAACADkikAAIAMaiZTZrbQzHaY2Qtm9pKZ/dkM2/yOmT1sZq+Y2bNm1t2IYOsVqgxQgcsJBReyHBTnLwexdlaoCyvW44sxrhhjKrqQfc75ay53n/MhySSdWvn6FEnPSrqkaptBSd+ofL1e0sO19rty5UpvpC1b3Ds63KWpR0dH0p7HftrBwMDJ/TTxGBiof1+cvxzE2lmhLqxYjy/GuGKMqehC9jnnryEkjfgsOU1dUyOYWYekZyQNuPuz09r/SdIGd/+hmZUl/ZukTp9j542eGiFUGaAClBNqmpDloDh/OYi1s0JdWLEeX4xxxRhT0YXsc85fQ8w1NUKqZMrMSpJ2SjpH0l+6+3+tWv+ipI+5+2hl+V8kXezub1Vt1y+pX5KWLl26ct9MJzuQBQuSdLyamXTiRPP30w7MZl9X73RmnL8cxNpZoS6sWI8vxrhijKnoQvY5568hMs8z5e7H3f2DkrokfcjMLphPIO4+5O497t7T2dk5n12kFqoMUMHLCQUVshwU5y8HsXZWqAsr1uOLMa4YYyq6kH3O+Wu6uj7N5+4/l/R9SR+rWnVA0lmSVHmb712SxkIEOF+hygAVvJxQUCHLQXH+chBrZ4W6sGI9vhjjijGmogvZ55y/5pttMNXEQ1KnpHdXvl4k6QeSrqra5kadPAD927X22+gB6O7JWLtly9zNkuf5jr0LtZ92MDDgXiol4x1LpfkNPp/A+ctBrJ0V6sKK9fhijCvGmIouZJ9z/oJTlgHoZrZc0kOSSkruZH3b3e80szsrO37MzBZK+mtJKyT9TNJ6d//XufZLbT4AANAq5hozVa71ze6+S0mSVN3+5WlfH5V0bZYgAQAAWhEzoAMAAGRAMgUAAJAByRQAAEAGJFMAAAAZkEylQL1IIGdUvU4n1kK5MfY7MSGk2eZMaPSjGfNMhUC9SCBnVL1OJ9ZCuTH2OzFhHhSq0HFIrTLPFPUigZxR9TqdWAvlxtjvxIR5yFzouBFaJZmiXiSQM6pepxNrodwY+52YMA+ZCx23M+pFAjmj6nU6sRbKjbHfiQmBkUzVQL1IIGdUvU4n1kK5MfY7MSG02QZTNfrRKgPQ3akXCeSOqtfpxFooN8Z+JybUSQxABwAAmD/GTAEAADQIyRQAAEAGJFMAAAAZkEwBAABkQDIFoH0MDkrlcjIRYrmcLGNm9FV6Ra+pV/TjC6CcdwAA0BSDg9LmzVPLx49PLW/alE9MsaKv0hselvr7pfHxZHnfvmRZkvr68osrlKIfXyBMjQCgPZTLSVJQrVSSjh1rfjwxo6/SK3pNvaIfXx2YGgEAZkoO5mpvZ/RVevv319feaop+fIGQTAFoD6VSfe3tjL5Kr+g19Yp+fIGQTAFoDxPjPNK2tzP6Kr2i19Qr+vEFQjIFoD1s2iQNDEzdXSmVkmUGVP82+iq9vj5paCgZQ2SWPA8NFWdwdtGPLxAGoAMAANTAAHQAAIAGIZkCAADIgGQKAAAgA5IpAACADEimgLxQ7yq9GPsqxtp1MfYT0AaozQfkgXpX6cXYVzHWrouxn4A2wdQIQB6od5VejH0VY+26GPsJKBCmRgBiQ72r9GLsqxhr18XYT0CbIJkC8kC9q/Ri7KsYa9fF2E9AmyCZAvJAvav0YuyrGGvXxdhPQJsgmQLyQL2r9GLsqxhr18XYT0CbYAA6AABADQxABwAAaBCSKQAAgAxIpgAAADIgmQIAAMiAZApAY8RYJy5kTDEeH4BcUJsPQHgx1okLGVOMxwcgN0yNACC8GOvEhYwpxuMD0FBMjQCguWKsExcyphiPD0BuSKYAhBdjnbiQMcV4fAByQzIFILwY68SFjCnG4wOQG5IpAOHFWCcuZEwxHh+A3DAAHQAAoAYGoAMAADQIyRQAAEAGJFMAAAAZkEwBAABkUDOZMrOzzOz7ZrbbzF4ys5tn2OYjZnbYzJ6vPL7cmHCBnFGPLT3q4CE0rgNEKk1tvmOS/tjdnzOz0yTtNLOn3H131XY/cPerwocIRIJ6bOlRBw+hcR0gYnVPjWBmj0q6192fmtb2EUl/Uk8yxdQIaDnUY0uPOngIjesAOZtraoS6kikz65b0tKQL3P0X09o/IulvJY1K+qmSxOqlGb6/X1K/JC1dunTlvpl+MIBYLVggzfTzYiadONH8eGIWsq/od0hcB8hdkHmmzOxUJQnTF6cnUhXPSVrm7r8v6S8k/cNM+3D3IXfvcfeezs7OtC8NxIF6bOlRBw+hcR0gYqmSKTM7RUkiNezuf1e93t1/4e5HKl9/R9IpZrY4aKRA3qjHlh518BAa1wEilubTfCbpAUl73P1rs2zze5XtZGYfqux3LGSgQO6ox5YedfAQGtcBIlZzzJSZXSrpB5J+LGnijek/lbRUktz9G2b2eUkDSj759++SbnH3/zvXfhmADgAAWsVcY6ZqTo3g7s9Ishrb3Cvp3vmFBwAA0LqYAR0AACADkikAAIAMSKYAAAAyIJkCAADIgGQK8aKoaWsbHJTK5eRj7OVysgwABZSm0DHQfBQ1bW2Dg9LmzVPLx49PLW/alE9MANAgdRc6DoV5pjAnipq2tnI5SaCqlUrSsWPNjwcAMgpSmw9oqv3762tHXGZKpOZqB4AWRjKFOFHUtLWVSvW1A0ALI5lCnChq2tomxrelbQeAFkYyhThR1LS1bdokDQxM3YkqlZJlBp8DKCAGoAMAANTAAHQAAIAGIZkCAADIgGQKAAAgA5IpAACADEimmoxyczmg0/NBvyM0rilEitp8TUS5uRzQ6fmg3xEa1xQixtQITUS5uRzQ6fmg3xEa1xRyNtfUCCRTTbRggTRTd5tJJ040P562QKfng35HaFxTyBnzTEWCcnM5oNPzQb8jNK4pRIxkqokoN5cDOj0f9DtC45pCxEimmohyczmg0/NBvyM0rilEjDFTAAAANTBmCgAAoEFIpgAAADIgmQIAAMiAZAoAACADkikAKIpYa9fFGhcQCLX5AKAIYq1dF2tcQEBMjQAARRBr7bpY4wLqxNQIAFB0+/fX194sscYFBEQyBQBFEGvtuljjAgIimQKAIoi1dl2scQEBkUwBQBHEWrsu1riAgBiADgAAUAMD0AEAABqEZAoAACADkikAAIAMSKYAAAAyIJkCAADIgGQKAAAgA5IpAACADEimAAAAMiCZAgAAyIBkCgAAIAOSKQAAgAxIpgAAADIgmQIAAMiAZAoAACADkikAAIAMSKYAAAAyqJlMmdlZZvZ9M9ttZi+Z2c0zbGNm9udm9oqZ7TKzixoTLjAPw8NSd7e0YEHyPDycd0QAgAIpp9jmmKQ/dvfnzOw0STvN7Cl33z1tm49LOrfyuFjS5sozkK/hYam/XxofT5b37UuWJamvL7+4AACFUfPOlLu/4e7PVb7+paQ9kpZUbXa1pL/yxHZJ7zaz9wWPFqjXbbdNJVITxseTdgAAAqhrzJSZdUtaIenZqlVLJL0+bXlUv51wycz6zWzEzEYOHTpUX6TAfOzfX187AAB1Sp1Mmdmpkv5W0hfd/RfzeTF3H3L3Hnfv6ezsnM8ugPosXVpfOwAAdUqVTJnZKUoSqWF3/7sZNjkg6axpy12VNiBfGzdKHR0nt3V0JO0AAASQ5tN8JukBSXvc/WuzbPaYpD+sfKrvEkmH3f2NgHEC89PXJw0NScuWSWbJ89AQg88BAMGk+TTfhyX9R0k/NrPnK21/KmmpJLn7NyR9R9InJL0iaVzS58KHCsxTXx/JEwCgYWomU+7+jCSrsY1LujFUUAAAAK2CGdABAAAyIJkCAADIgGQKAAAgA5IpAACADEimAAAAMiCZAgAAyIBkCgAAIAOSKQAAgAxIpgAAADIgmQIAAMiAZAoAACADkikAAIAMLKlRnMMLmx2StC+XF4/DYklv5R1Em6HP80G/Nx99ng/6vfma2efL3L1zphW5JVPtzsxG3L0n7zjaCX2eD/q9+ejzfNDvzRdLn/M2HwAAQAYkUwAAABmQTOVnKO8A2hB9ng/6vfno83zQ780XRZ8zZgoAACAD7kwBAABkQDIFAACQAclUg5lZycx+ZGb/OMO668zskJk9X3n8UR4xFo2ZvWZmP6706cgM683M/tzMXjGzXWZ2UR5xFk2Kfv+ImR2edr1/OY84i8TM3m1mj5jZy2a2x8xWV63nWg8sRZ9znQdmZudN68/nzewXZvbFqm1yvdbLzXyxNnWzpD2S3jnL+ofd/fNNjKddrHH32SZy+7ikcyuPiyVtrjwju7n6XZJ+4O5XNS2a4vtfkp5092vM7B2SOqrWc62HV6vPJa7zoNz9J5I+KCU3KCQdkPT3VZvleq1zZ6qBzKxL0pWS7s87Fpzkakl/5Yntkt5tZu/LOyigHmb2LkmXSXpAktz9bXf/edVmXOsBpexzNNYVkv7F3asrqOR6rZNMNdbXJX1J0ok5tvlk5ZbkI2Z2VpPiKjqXtNXMdmmNz3wAAAI0SURBVJpZ/wzrl0h6fdryaKUN2dTqd0labWYvmNkTZvaBZgZXQGdLOiTpf1eGEtxvZr9btQ3Xelhp+lziOm+k9ZK+OUN7rtc6yVSDmNlVkg66+845NntcUre7L5f0lKSHmhJc8V3q7hcpue17o5ldlndAbaJWvz+npLbV70v6C0n/0OwAC6Ys6SJJm919haRfSfpv+YZUeGn6nOu8QSpvq/ZK+pu8Y6lGMtU4H5bUa2avSfqWpMvNbMv0Ddx9zN1/XVm8X9LK5oZYTO5+oPJ8UMn76h+q2uSApOl3AbsqbcigVr+7+y/c/Ujl6+9IOsXMFjc90OIYlTTq7s9Wlh9R8od+Oq71sGr2Odd5Q31c0nPu/uYM63K91kmmGsTdb3X3LnfvVnJb8nvu/tnp21S9n9urZKA6MjCz3zWz0ya+lvRRSS9WbfaYpD+sfPrjEkmH3f2NJodaKGn63cx+z8ys8vWHlPz+GWt2rEXh7v8m6XUzO6/SdIWk3VWbca0HlKbPuc4b6tOa+S0+KedrnU/zNZmZ3SlpxN0fk/QFM+uVdEzSzyRdl2dsBfEfJP195XdZWdL/cfcnzey/SJK7f0PSdyR9QtIrksYlfS6nWIskTb9fI2nAzI5J+ndJ650SDFndJGm48vbHv0r6HNd6w9Xqc67zBqj8k/YHkm6Y1hbNtU45GQAAgAx4mw8AACADkikAAIAMSKYAAAAyIJkCAADIgGQKAAAgA5IpAACADEimAAAAMvj/ZWVdbztJfMAAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] @@ -141,7 +184,9 @@ "metadata": {}, "outputs": [], "source": [ - "model = LogisticRegression(num_iter=50)" + "# Initializing the model at the default 100 iteration\n", + "\n", + "model = LogisticRegression()" ] }, { @@ -153,8 +198,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 10.6 ms, sys: 0 ns, total: 10.6 ms\n", - "Wall time: 9.62 ms\n" + "CPU times: user 29.4 ms, sys: 4.28 ms, total: 33.7 ms\n", + "Wall time: 29.9 ms\n" ] } ], @@ -193,8 +238,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 20.9 ms, sys: 159 µs, total: 21 ms\n", - "Wall time: 20.1 ms\n" + "CPU times: user 19.5 ms, sys: 7.93 ms, total: 27.5 ms\n", + "Wall time: 203 ms\n" ] }, { @@ -223,6 +268,8 @@ "source": [ "# Comparing with the scikit learn Logistic Regression class\n", "\n", + "# max_iter=100 the default\n", + "\n", "sk_model = linear_model.LogisticRegression()\n", "\n", "%time sk_model.fit(x_train, y_train)" @@ -249,6 +296,87 @@ "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" ] }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 15.8 ms, sys: 0 ns, total: 15.8 ms\n", + "Wall time: 14.3 ms\n", + "Accuracy: 100.0\n" + ] + } + ], + "source": [ + "# Testing model with 50 iterations\n", + "\n", + "\n", + "model = LogisticRegression(num_iter=50)\n", + "\n", + "%time model.fit(x_train, y_train)\n", + "\n", + "prediction = model.predict(x_test)\n", + "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 6.02 ms, sys: 3.61 ms, total: 9.63 ms\n", + "Wall time: 8.48 ms\n", + "Accuracy: 96.66666666666667\n" + ] + } + ], + "source": [ + "# Testing model with 30 iterations\n", + "\n", + "\n", + "model = LogisticRegression(num_iter=30)\n", + "\n", + "%time model.fit(x_train, y_train)\n", + "\n", + "prediction = model.predict(x_test)\n", + "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 13.5 ms, sys: 285 µs, total: 13.8 ms\n", + "Wall time: 12.2 ms\n", + "Accuracy: 100.0\n" + ] + } + ], + "source": [ + "# Testing model with 30 iterations and applying regularization\n", + "\n", + "\n", + "model = LogisticRegression(num_iter=30, lambd=0.5)\n", + "\n", + "%time model.fit(x_train, y_train)\n", + "\n", + "prediction = model.predict(x_test)\n", + "print(f\"Accuracy: {accuracy(prediction, y_test)}\")" + ] + }, { "cell_type": "code", "execution_count": null, From 3d342320ad55776fedec3b6ca43b7d4a17036a02 Mon Sep 17 00:00:00 2001 From: ganiyuolalekan Date: Fri, 17 Apr 2020 22:29:59 +0000 Subject: [PATCH 3/3] Modified README.md --- ganiyu_olalekan_matthew/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ganiyu_olalekan_matthew/README.md b/ganiyu_olalekan_matthew/README.md index 2658849..cf67fb8 100644 --- a/ganiyu_olalekan_matthew/README.md +++ b/ganiyu_olalekan_matthew/README.md @@ -9,7 +9,9 @@ Logistic regression is the appropriate regression analysis to conduct when the d ## Implemetation -The Logistic Regression algorithm was implement with a jupyter notebook using python. +The Logistic Regression algorithm was implemented with python using jupyter notebook. The iris dataset from sklearn.datasets.load_iris() was used to test the speed and accuracy of the algorithm. Unlike the default iris dataset, my implementation modified it to match the binary requirement of the logistic regression algorithm. Given that the algorithm (Logistic Algorithm) is a Binary Classification Algorithm. +L2 Regularization (Euclidean Normal) was also implemented to optimize the algorithm, obtaining same accuracy in fewer iterations as it would have been for longer iterations. +