{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Programming Exercise 2: Logistic Regression\n", "\n", "## Introduction\n", "\n", "In this exercise, you will implement logistic regression and apply it to two different datasets. Before starting on the programming exercise, we strongly recommend watching the video lectures and completing the review questions for the associated topics.\n", "\n", "All the information you need for solving this assignment is in this notebook, and all the code you will be implementing will take place within this notebook. The assignment can be promptly submitted to the coursera grader directly from this notebook (code and instructions are included below).\n", "\n", "Before we begin with the exercises, we need to import all libraries required for this programming exercise. Throughout the course, we will be using [`numpy`](http://www.numpy.org/) for all arrays and matrix operations, and [`matplotlib`](https://matplotlib.org/) for plotting. In this assignment, we will also use [`scipy`](https://docs.scipy.org/doc/scipy/reference/), which contains scientific and numerical computation functions and tools. \n", "\n", "You can find instructions on how to install required libraries in the README file in the [github repository](https://github.com/dibgerge/ml-coursera-python-assignments)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# used for manipulating directory paths\n", "import os\n", "\n", "# Scientific and vector computation for python\n", "import numpy as np\n", "\n", "# Plotting library\n", "from matplotlib import pyplot\n", "\n", "# Optimization module in scipy\n", "from scipy import optimize\n", "\n", "# library written for this exercise providing additional functions for assignment submission, and others\n", "import utils\n", "\n", "# define the submission/grader object for this exercise\n", "grader = utils.Grader()\n", "\n", "# tells matplotlib to embed plots within the notebook\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Submission and Grading\n", "\n", "\n", "After completing each part of the assignment, be sure to submit your solutions to the grader. The following is a breakdown of how each part of this exercise is scored.\n", "\n", "\n", "| Section | Part | Submission function | Points \n", "| :- |:- | :- | :-:\n", "| 1 | [Sigmoid Function](#section1) | [`sigmoid`](#sigmoid) | 5 \n", "| 2 | [Compute cost for logistic regression](#section2) | [`costFunction`](#costFunction) | 30 \n", "| 3 | [Gradient for logistic regression](#section2) | [`costFunction`](#costFunction) | 30 \n", "| 4 | [Predict Function](#section4) | [`predict`](#predict) | 5 \n", "| 5 | [Compute cost for regularized LR](#section5) | [`costFunctionReg`](#costFunctionReg) | 15 \n", "| 6 | [Gradient for regularized LR](#section5) | [`costFunctionReg`](#costFunctionReg) | 15 \n", "| | Total Points | | 100 \n", "\n", "\n", "\n", "You are allowed to submit your solutions multiple times, and we will take only the highest score into consideration.\n", "\n", "
\n", "At the end of each section in this notebook, we have a cell which contains code for submitting the solutions thus far to the grader. Execute the cell to see your score up to the current section. For all your work to be submitted properly, you must execute those cells at least once. They must also be re-executed everytime the submitted function is updated.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 Logistic Regression\n", "\n", "In this part of the exercise, you will build a logistic regression model to predict whether a student gets admitted into a university. Suppose that you are the administrator of a university department and\n", "you want to determine each applicant’s chance of admission based on their results on two exams. You have historical data from previous applicants that you can use as a training set for logistic regression. For each training example, you have the applicant’s scores on two exams and the admissions\n", "decision. Your task is to build a classification model that estimates an applicant’s probability of admission based the scores from those two exams. \n", "\n", "The following cell will load the data and corresponding labels:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Load data\n", "# The first two columns contains the exam scores and the third column\n", "# contains the label.\n", "data = np.loadtxt(os.path.join('Data', 'ex2data1.txt'), delimiter=',')\n", "X, y = data[:, 0:2], data[:, 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1.1 Visualizing the data\n", "\n", "Before starting to implement any learning algorithm, it is always good to visualize the data if possible. We display the data on a 2-dimensional plot by calling the function `plotData`. You will now complete the code in `plotData` so that it displays a figure where the axes are the two exam scores, and the positive and negative examples are shown with different markers.\n", "\n", "To help you get more familiar with plotting, we have left `plotData` empty so you can try to implement it yourself. However, this is an optional (ungraded) exercise. We also provide our implementation below so you can\n", "copy it or refer to it. If you choose to copy our example, make sure you learn\n", "what each of its commands is doing by consulting the `matplotlib` and `numpy` documentation.\n", "\n", "```python\n", "# Find Indices of Positive and Negative Examples\n", "pos = y == 1\n", "neg = y == 0\n", "\n", "# Plot Examples\n", "pyplot.plot(X[pos, 0], X[pos, 1], 'k*', lw=2, ms=10)\n", "pyplot.plot(X[neg, 0], X[neg, 1], 'ko', mfc='y', ms=8, mec='k', mew=1)\n", "```" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def plotData(X, y):\n", " \"\"\"\n", " Plots the data points X and y into a new figure. Plots the data \n", " points with * for the positive examples and o for the negative examples.\n", " \n", " Parameters\n", " ----------\n", " X : array_like\n", " An Mx2 matrix representing the dataset. \n", " \n", " y : array_like\n", " Label values for the dataset. A vector of size (M, ).\n", " \n", " Instructions\n", " ------------\n", " Plot the positive and negative examples on a 2D plot, using the\n", " option 'k*' for the positive examples and 'ko' for the negative examples. \n", " \"\"\"\n", " # Create New Figure\n", " fig = pyplot.figure()\n", "\n", " # ====================== YOUR CODE HERE ======================\n", "\n", " pos_mask = y == 1\n", " pyplot.plot(X[pos_mask, 0], X[pos_mask, 1], 'k*', lw=2)\n", " pyplot.plot(X[~pos_mask, 0], X[~pos_mask, 1], \"ko\", color=\"red\", lw=2)\n", " \n", " \n", " # ============================================================" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we call the implemented function to display the loaded data:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/png": "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LtORRx6piRMnasmSJTr++ON1wAEHqL29XZL0/PPP69BDD9U777yj/fffX/vss4+ampp05ZVXat9999V+++2npqYmPfTQQ1q/fr2mT5+uSZMmadKkSXrkkUckSS+//LKOO+44jR8/Xi0tLWpoaNCGDRuG9G8dqJIPNDsXt2IGyRM2MYIkzT4q5W2oSSGqXZ1uueUWfeUrX5EkTZ48WUuWLNHPf/5zHXvssdq6dav+/ve/a4899tguKVx33XWSpIsuukjjxo3TG2+8ofXr12vEiBGS3k8KknTjjTdqzpw53ee74oordPXVV3c/PuOMM/ZQOF0AAA6/SURBVPTwww9LklavXq2DDz5YknTBBRfoO9/5jiTp7rvvFuBJwblySNhspb6SQiyL15Iiqk01FixYwEUXXQTAjBkzWLBgAe+99x5nnHEGtbW17LfffhxzzDHbPefkk08GYNy4cbz11lvstttu7LbbbmQyGV577bWizn///fezfPny7sdvvPEGb775Jg899FB3bfjPf/7z7LXXXkP5ZzrnBirXrZmCxZZVnRTg/V2dem6qMRQbN27kN7/5DcuWLcPM2LZtG2bGtGnT+pyaucsuuwBQU1PT/XXu8datW4uKoauri8ceeyzv5uY+Xde5mMS9gnyAkjT7KBaLFy+mra2NpqYm2trahrzL0qJFizjrrLNYvXo1q1at4oUXXmDMmDEMHz6c2267jW3btpHNZvntb3876HPstttuvPnmmwUfH3/88fzXf/1X9+OlS5cCMGXKFOaHU+DuueceXn311UHHEIuUzPN2Ls2qPimU2oIFC5g2bdp2x77whS/w4osvMnbsWMaNG0draytHH330oM/xmc98huXLlzN+/HgWLlzIP/3TP3HHHXcwfvx4Hn74Ya699lo6Ozs5/PDD+ehHP8r1118PwBVXXMFDDz3EhAkTuPfee2lI05z+FM3zdi7NKm4/hWeeeYZDDjkkpohcIUP+vSSh/r9zFSKR+yk4V5Q0zfN2LsU8Kbh08LIWzpWFJwWXDl7Wwrmy8KTg0sHLWjhXFlW/TsGlSErmeTuXZt5ScM45182TQgQLosyMr3/9692Pr7nmGr797W/3+Zw777xzu9IUg1FsKey77rqLK6+8Mu/5b7rpJtatK26fo1WrVnHYYYcV9RznXLJUd1KIaEHULrvswuLFi4v6gC5FUijWySefzKWXXpr3/INJCs659KvupBDRxhfDhg2jpaWFH/zgBzt8b/Xq1UydOpXDDz+cqVOnsmbNGh599FHuuusu/vVf/5Xx48fzt7/9bbvn/PKXv+TjH/84RxxxBMceeywvvfQSENRZOv744zniiCM477zzyC1EXLVqFQcffDBf/epXOeyww2hubub+++/nqKOOYuzYsfzxj38Egg/+888/f4fzX3XVVXR2dtLc3Mz48ePZvHkzS5Ys4eijj2bixIl89rOf7a4RtWTJEpqampg8eTJtbW1Det+ccwlQqHxqGm5DLZ0ts/w1zs0G/hp57Lrrrnr99dfV2Nio1157TVdffbWuuOIKSdJJJ52km266SZI0b948nXLKKZKks88+u7uUdm+vvPKKurq6JEk/+tGPdPHFF0sqXAr7+eefV21trZ588klt27ZNEyZM0DnnnKOuri7deeed3efsWYK79/mPPvpo/elPf5Ikvfvuu5o8ebLWr18vSbrtttt0zjnnSJLGjRunBx98UJJ0ySWXdJf37s1LZzuXHHjp7AIaGvKXTijBgqjdd9+ds846i2uvvXa7aqWPPfZYd9G9M888k2984xv9vtbatWv50pe+RDab5d1332XMmDEAfZbCHjNmDOPGjQPg0EMPZerUqZgZ48aNY1WRZSFWrFjBsmXLOO644wDYtm0bI0eO5PXXX+e1117rruN05plncs899xT12s65ZKnu7qOIF0RddNFFzJs3j7fffrvgzwyklPUFF1zA+eefz1NPPcUNN9zAli1b+n1+7/LbPUtzF1uKWxKHHnooS5cuZenSpTz11FPce++9SPJS3GnnlWddL9WdFCJeEDV8+HC++MUvMm/evO5jn/jEJ7r3bZ4/fz6f/OQngR3LX/f0+uuv8+EPfxiAm2++uft4KUth91WO+6CDDmLDhg089thjALz33ns8/fTT7Lnnnuyxxx488sgj3f8elyJeedblUfakYGYZM/ujmT1hZk+b2XfC42PM7A9mttLMFprZzmUJqLk5qLLZ1RXcl3hx1Ne//vXtZiFde+213HjjjRx++OHccsst/PCHPwSCHdquvvpqjjjiiB0Gmr/97W9z+umn86lPfYp99tmn+3gpS2H3Pv+Xv/xlZs2axfjx49m2bRuLFi3im9/8Jk1NTYwfP55HH30UgBtvvJE5c+YwefLkvJv6uASL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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plotData(X, y)\n", "# add axes labels\n", "pyplot.xlabel('Exam 1 score')\n", "pyplot.ylabel('Exam 2 score')\n", "pyplot.legend(['Admitted', 'Not admitted'])\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 1.2 Implementation\n", "\n", "#### 1.2.1 Warmup exercise: sigmoid function\n", "\n", "Before you start with the actual cost function, recall that the logistic regression hypothesis is defined as:\n", "\n", "$$ h_\\theta(x) = g(\\theta^T x)$$\n", "\n", "where function $g$ is the sigmoid function. The sigmoid function is defined as: \n", "\n", "$$g(z) = \\frac{1}{1+e^{-z}}$$.\n", "\n", "Your first step is to implement this function `sigmoid` so it can be\n", "called by the rest of your program. When you are finished, try testing a few\n", "values by calling `sigmoid(x)` in a new cell. For large positive values of `x`, the sigmoid should be close to 1, while for large negative values, the sigmoid should be close to 0. Evaluating `sigmoid(0)` should give you exactly 0.5. Your code should also work with vectors and matrices. **For a matrix, your function should perform the sigmoid function on every element.**\n", "" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def sigmoid(z):\n", " \"\"\"\n", " Compute sigmoid function given the input z.\n", " \n", " Parameters\n", " ----------\n", " z : array_like\n", " The input to the sigmoid function. This can be a 1-D vector \n", " or a 2-D matrix. \n", " \n", " Returns\n", " -------\n", " g : array_like\n", " The computed sigmoid function. g has the same shape as z, since\n", " the sigmoid is computed element-wise on z.\n", " \n", " Instructions\n", " ------------\n", " Compute the sigmoid of each value of z (z can be a matrix, vector or scalar).\n", " \"\"\"\n", " # convert input to a numpy array\n", " z = np.array(z)\n", " \n", " # You need to return the following variables correctly \n", " # g = np.zeros(z.shape)\n", "\n", " # ====================== YOUR CODE HERE ======================\n", "\n", " g = 1 / (1 + np.exp(-z))\n", "\n", " # =============================================================\n", " return g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following cell evaluates the sigmoid function at `z=0`. You should get a value of 0.5. You can also try different values for `z` to experiment with the sigmoid function." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g( 0 ) = 0.5\n" ] } ], "source": [ "# Test the implementation of sigmoid function here\n", "z = 0\n", "g = sigmoid(z)\n", "\n", "print('g(', z, ') = ', g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After completing a part of the exercise, you can submit your solutions for grading by first adding the function you modified to the submission object, and then sending your function to Coursera for grading. \n", "\n", "The submission script will prompt you for your login e-mail and submission token. You can obtain a submission token from the web page for the assignment. You are allowed to submit your solutions multiple times, and we will take only the highest score into consideration.\n", "\n", "Execute the following cell to grade your solution to the first part of this exercise.\n", "\n", "*You should now submit your solutions.*" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Submitting Solutions | Programming Exercise logistic-regression\n", "\n", " Part Name | Score | Feedback\n", " --------- | ----- | --------\n", " Regularized Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Sigmoid Function | 0 / 30 | Your answer is incorrect.\n", " Logistic Regression Cost | 0 / 30 | Your answer is incorrect.\n", " Logistic Regression Gradient | 0 / 5 | Your answer is incorrect.\n", " Predict | 0 / 15 | Your answer is incorrect.\n", " Regularized Logistic Regression Cost | 0 / 15 | Your answer is incorrect.\n", " --------------------------------\n", " | 5 / 100 | \n", "\n" ] } ], "source": [ "# appends the implemented function in part 1 to the grader object\n", "grader[1] = sigmoid\n", "\n", "# send the added functions to coursera grader for getting a grade on this part\n", "grader.grade()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "#### 1.2.2 Cost function and gradient\n", "\n", "Now you will implement the cost function and gradient for logistic regression. Before proceeding we add the intercept term to X. " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# Setup the data matrix appropriately, and add ones for the intercept term\n", "m, n = X.shape\n", "\n", "# Add intercept term to X\n", "X = np.concatenate([np.ones((m, 1)), X], axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, complete the code for the function `costFunction` to return the cost and gradient. Recall that the cost function in logistic regression is\n", "\n", "$$ J(\\theta) = \\frac{1}{m} \\sum_{i=1}^{m} \\left[ -y^{(i)} \\log\\left(h_\\theta\\left( x^{(i)} \\right) \\right) - \\left( 1 - y^{(i)}\\right) \\log \\left( 1 - h_\\theta\\left( x^{(i)} \\right) \\right) \\right]$$\n", "\n", "and the gradient of the cost is a vector of the same length as $\\theta$ where the $j^{th}$\n", "element (for $j = 0, 1, \\cdots , n$) is defined as follows:\n", "\n", "$$ \\frac{\\partial J(\\theta)}{\\partial \\theta_j} = \\frac{1}{m} \\sum_{i=1}^m \\left( h_\\theta \\left( x^{(i)} \\right) - y^{(i)} \\right) x_j^{(i)} $$\n", "\n", "Note that while this gradient looks identical to the linear regression gradient, the formula is actually different because linear and logistic regression have different definitions of $h_\\theta(x)$.\n", "" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def costFunction(theta, X, y):\n", " \"\"\"\n", " Compute cost and gradient for logistic regression. \n", " \n", " Parameters\n", " ----------\n", " theta : array_like\n", " The parameters for logistic regression. This a vector\n", " of shape (n+1, ).\n", " \n", " X : array_like\n", " The input dataset of shape (m x n+1) where m is the total number\n", " of data points and n is the number of features. We assume the \n", " intercept has already been added to the input.\n", " \n", " y : arra_like\n", " Labels for the input. This is a vector of shape (m, ).\n", " \n", " Returns\n", " -------\n", " J : float\n", " The computed value for the cost function. \n", " \n", " grad : array_like\n", " A vector of shape (n+1, ) which is the gradient of the cost\n", " function with respect to theta, at the current values of theta.\n", " \n", " Instructions\n", " ------------\n", " Compute the cost of a particular choice of theta. You should set J to \n", " the cost. Compute the partial derivatives and set grad to the partial\n", " derivatives of the cost w.r.t. each parameter in theta.\n", " \"\"\"\n", " # Initialize some useful values\n", " m = y.size # number of training examples\n", "\n", " # You need to return the following variables correctly \n", " # J = 0\n", " # grad = np.zeros(theta.shape)\n", "\n", " # ====================== YOUR CODE HERE ======================\n", "\n", " hypothesis = sigmoid(np.tensordot(theta.T, X, axes=(0, len(X.shape)-1)))\n", "\n", " J = 1/m * (-y * np.log(hypothesis) - (1-y)*np.log(1-hypothesis) ).sum()\n", "\n", " grad = np.zeros(theta.shape)\n", " for i in range(len(grad)):\n", " grad[i] = 1/m * ((hypothesis - y)*X[:, i]).sum()\n", " \n", " # =============================================================\n", " return J, grad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you are done call your `costFunction` using two test cases for $\\theta$ by executing the next cell." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cost at initial theta (zeros): 0.693\n", "Expected cost (approx): 0.693\n", "\n", "Gradient at initial theta (zeros):\n", "\t[-0.1000, -12.0092, -11.2628]\n", "Expected gradients (approx):\n", "\t[-0.1000, -12.0092, -11.2628]\n", "\n", "Cost at test theta: 0.218\n", "Expected cost (approx): 0.218\n", "\n", "Gradient at test theta:\n", "\t[0.043, 2.566, 2.647]\n", "Expected gradients (approx):\n", "\t[0.043, 2.566, 2.647]\n" ] } ], "source": [ "# Initialize fitting parameters\n", "initial_theta = np.zeros(n+1)\n", "\n", "cost, grad = costFunction(initial_theta, X, y)\n", "\n", "print('Cost at initial theta (zeros): {:.3f}'.format(cost))\n", "print('Expected cost (approx): 0.693\\n')\n", "\n", "print('Gradient at initial theta (zeros):')\n", "print('\\t[{:.4f}, {:.4f}, {:.4f}]'.format(*grad))\n", "print('Expected gradients (approx):\\n\\t[-0.1000, -12.0092, -11.2628]\\n')\n", "\n", "# Compute and display cost and gradient with non-zero theta\n", "test_theta = np.array([-24, 0.2, 0.2])\n", "cost, grad = costFunction(test_theta, X, y)\n", "\n", "print('Cost at test theta: {:.3f}'.format(cost))\n", "print('Expected cost (approx): 0.218\\n')\n", "\n", "print('Gradient at test theta:')\n", "print('\\t[{:.3f}, {:.3f}, {:.3f}]'.format(*grad))\n", "print('Expected gradients (approx):\\n\\t[0.043, 2.566, 2.647]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*You should now submit your solutions.*" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Submitting Solutions | Programming Exercise logistic-regression\n", "\n", " Part Name | Score | Feedback\n", " --------- | ----- | --------\n", " Regularized Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Sigmoid Function | 30 / 30 | Nice work!\n", " Logistic Regression Cost | 30 / 30 | Nice work!\n", " Logistic Regression Gradient | 0 / 5 | Your answer is incorrect.\n", " Predict | 0 / 15 | Your answer is incorrect.\n", " Regularized Logistic Regression Cost | 0 / 15 | Your answer is incorrect.\n", " --------------------------------\n", " | 65 / 100 | \n", "\n" ] } ], "source": [ "grader[2] = costFunction\n", "grader[3] = costFunction\n", "grader.grade()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 1.2.3 Learning parameters using `scipy.optimize`\n", "\n", "In the previous assignment, you found the optimal parameters of a linear regression model by implementing gradient descent. You wrote a cost function and calculated its gradient, then took a gradient descent step accordingly. This time, instead of taking gradient descent steps, you will use the [`scipy.optimize` module](https://docs.scipy.org/doc/scipy/reference/optimize.html). SciPy is a numerical computing library for `python`. It provides an optimization module for root finding and minimization. As of `scipy 1.0`, the function `scipy.optimize.minimize` is the method to use for optimization problems(both constrained and unconstrained).\n", "\n", "For logistic regression, you want to optimize the cost function $J(\\theta)$ with parameters $\\theta$.\n", "Concretely, you are going to use `optimize.minimize` to find the best parameters $\\theta$ for the logistic regression cost function, given a fixed dataset (of X and y values). You will pass to `optimize.minimize` the following inputs:\n", "- `costFunction`: A cost function that, when given the training set and a particular $\\theta$, computes the logistic regression cost and gradient with respect to $\\theta$ for the dataset (X, y). It is important to note that we only pass the name of the function without the parenthesis. This indicates that we are only providing a reference to this function, and not evaluating the result from this function.\n", "- `initial_theta`: The initial values of the parameters we are trying to optimize.\n", "- `(X, y)`: These are additional arguments to the cost function.\n", "- `jac`: Indication if the cost function returns the Jacobian (gradient) along with cost value. (True)\n", "- `method`: Optimization method/algorithm to use\n", "- `options`: Additional options which might be specific to the specific optimization method. In the following, we only tell the algorithm the maximum number of iterations before it terminates.\n", "\n", "If you have completed the `costFunction` correctly, `optimize.minimize` will converge on the right optimization parameters and return the final values of the cost and $\\theta$ in a class object. Notice that by using `optimize.minimize`, you did not have to write any loops yourself, or set a learning rate like you did for gradient descent. This is all done by `optimize.minimize`: you only needed to provide a function calculating the cost and the gradient.\n", "\n", "In the following, we already have code written to call `optimize.minimize` with the correct arguments." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cost at theta found by optimize.minimize: 0.203\n", "Expected cost (approx): 0.203\n", "\n", "theta:\n", "\t[-25.161, 0.206, 0.201]\n", "Expected theta (approx):\n", "\t[-25.161, 0.206, 0.201]\n" ] } ], "source": [ "# set options for optimize.minimize\n", "options= {'maxiter': 400}\n", "\n", "# see documention for scipy's optimize.minimize for description about\n", "# the different parameters\n", "# The function returns an object `OptimizeResult`\n", "# We use truncated Newton algorithm for optimization which is \n", "# equivalent to MATLAB's fminunc\n", "# See https://stackoverflow.com/questions/18801002/fminunc-alternate-in-numpy\n", "res = optimize.minimize(costFunction,\n", " initial_theta,\n", " (X, y),\n", " jac=True,\n", " method='TNC',\n", " options=options)\n", "\n", "# the fun property of `OptimizeResult` object returns\n", "# the value of costFunction at optimized theta\n", "cost = res.fun\n", "\n", "# the optimized theta is in the x property\n", "theta = res.x\n", "\n", "# Print theta to screen\n", "print('Cost at theta found by optimize.minimize: {:.3f}'.format(cost))\n", "print('Expected cost (approx): 0.203\\n');\n", "\n", "print('theta:')\n", "print('\\t[{:.3f}, {:.3f}, {:.3f}]'.format(*theta))\n", "print('Expected theta (approx):\\n\\t[-25.161, 0.206, 0.201]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once `optimize.minimize` completes, we want to use the final value for $\\theta$ to visualize the decision boundary on the training data as shown in the figure below. \n", "\n", "![](Figures/decision_boundary1.png)\n", "\n", "To do so, we have written a function `plotDecisionBoundary` for plotting the decision boundary on top of training data. You do not need to write any code for plotting the decision boundary, but we also encourage you to look at the code in `plotDecisionBoundary` to see how to plot such a boundary using the $\\theta$ values. You can find this function in the `utils.py` file which comes with this assignment." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Plot Boundary\n", "utils.plotDecisionBoundary(plotData, theta, X, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "#### 1.2.4 Evaluating logistic regression\n", "\n", "After learning the parameters, you can use the model to predict whether a particular student will be admitted. For a student with an Exam 1 score of 45 and an Exam 2 score of 85, you should expect to see an admission\n", "probability of 0.776. Another way to evaluate the quality of the parameters we have found is to see how well the learned model predicts on our training set. In this part, your task is to complete the code in function `predict`. The predict function will produce “1” or “0” predictions given a dataset and a learned parameter vector $\\theta$. \n", "" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def predict(theta, X):\n", " \"\"\"\n", " Predict whether the label is 0 or 1 using learned logistic regression.\n", " Computes the predictions for X using a threshold at 0.5 \n", " (i.e., if sigmoid(theta.T*x) >= 0.5, predict 1)\n", " \n", " Parameters\n", " ----------\n", " theta : array_like\n", " Parameters for logistic regression. A vecotor of shape (n+1, ).\n", " \n", " X : array_like\n", " The data to use for computing predictions. The rows is the number \n", " of points to compute predictions, and columns is the number of\n", " features.\n", "\n", " Returns\n", " -------\n", " p : array_like\n", " Predictions and 0 or 1 for each row in X. \n", " \n", " Instructions\n", " ------------\n", " Complete the following code to make predictions using your learned \n", " logistic regression parameters.You should set p to a vector of 0's and 1's \n", " \"\"\"\n", " m = X.shape[0] # Number of training examples\n", "\n", " # You need to return the following variables correctly\n", " # p = np.zeros(m)\n", "\n", " # ====================== YOUR CODE HERE ======================\n", "\n", " p = sigmoid(np.tensordot(theta.T, X, axes=(0, len(X.shape)-1)))\n", " p = p >= 0.5\n", " \n", " # ============================================================\n", " return p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After you have completed the code in `predict`, we proceed to report the training accuracy of your classifier by computing the percentage of examples it got correct." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "For a student with scores 45 and 85,we predict an admission probability of 0.776\n", "Expected value: 0.775 +/- 0.002\n", "\n", "Train Accuracy: 89.00 %\n", "Expected accuracy (approx): 89.00 %\n" ] } ], "source": [ "# Predict probability for a student with score 45 on exam 1 \n", "# and score 85 on exam 2 \n", "prob = sigmoid(np.dot([1, 45, 85], theta))\n", "print('For a student with scores 45 and 85,'\n", " 'we predict an admission probability of {:.3f}'.format(prob))\n", "print('Expected value: 0.775 +/- 0.002\\n')\n", "\n", "# Compute accuracy on our training set\n", "p = predict(theta, X)\n", "print('Train Accuracy: {:.2f} %'.format(np.mean(p == y) * 100))\n", "print('Expected accuracy (approx): 89.00 %')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*You should now submit your solutions.*" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Submitting Solutions | Programming Exercise logistic-regression\n", "\n", " Part Name | Score | Feedback\n", " --------- | ----- | --------\n", " Regularized Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Sigmoid Function | 30 / 30 | Nice work!\n", " Logistic Regression Cost | 30 / 30 | Nice work!\n", " Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Predict | 0 / 15 | Your answer is incorrect.\n", " Regularized Logistic Regression Cost | 0 / 15 | Your answer is incorrect.\n", " --------------------------------\n", " | 70 / 100 | \n", "\n" ] } ], "source": [ "grader[4] = predict\n", "grader.grade()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2 Regularized logistic regression\n", "\n", "In this part of the exercise, you will implement regularized logistic regression to predict whether microchips from a fabrication plant passes quality assurance (QA). During QA, each microchip goes through various tests to ensure it is functioning correctly.\n", "Suppose you are the product manager of the factory and you have the test results for some microchips on two different tests. From these two tests, you would like to determine whether the microchips should be accepted or rejected. To help you make the decision, you have a dataset of test results on past microchips, from which you can build a logistic regression model.\n", "\n", "First, we load the data from a CSV file:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# Load Data\n", "# The first two columns contains the X values and the third column\n", "# contains the label (y).\n", "data = np.loadtxt(os.path.join('Data', 'ex2data2.txt'), delimiter=',')\n", "X = data[:, :2]\n", "y = data[:, 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Visualize the data\n", "\n", "Similar to the previous parts of this exercise, `plotData` is used to generate a figure, where the axes are the two test scores, and the positive (y = 1, accepted) and negative (y = 0, rejected) examples are shown with\n", "different markers." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plotData(X, y)\n", "# Labels and Legend\n", "pyplot.xlabel('Microchip Test 1')\n", "pyplot.ylabel('Microchip Test 2')\n", "\n", "# Specified in plot order\n", "pyplot.legend(['y = 1', 'y = 0'], loc='upper right')\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above figure shows that our dataset cannot be separated into positive and negative examples by a straight-line through the plot. Therefore, a straight-forward application of logistic regression will not perform well on this dataset since logistic regression will only be able to find a linear decision boundary.\n", "\n", "### 2.2 Feature mapping\n", "\n", "One way to fit the data better is to create more features from each data point. In the function `mapFeature` defined in the file `utils.py`, we will map the features into all polynomial terms of $x_1$ and $x_2$ up to the sixth power.\n", "\n", "$$ \\text{mapFeature}(x) = \\begin{bmatrix} 1 & x_1 & x_2 & x_1^2 & x_1 x_2 & x_2^2 & x_1^3 & \\dots & x_1 x_2^5 & x_2^6 \\end{bmatrix}^T $$\n", "\n", "As a result of this mapping, our vector of two features (the scores on two QA tests) has been transformed into a 28-dimensional vector. A logistic regression classifier trained on this higher-dimension feature vector will have a more complex decision boundary and will appear nonlinear when drawn in our 2-dimensional plot.\n", "While the feature mapping allows us to build a more expressive classifier, it also more susceptible to overfitting. In the next parts of the exercise, you will implement regularized logistic regression to fit the data and also see for yourself how regularization can help combat the overfitting problem.\n" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "# Note that mapFeature also adds a column of ones for us, so the intercept\n", "# term is handled\n", "X = utils.mapFeature(X[:, 0], X[:, 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 2.3 Cost function and gradient\n", "\n", "Now you will implement code to compute the cost function and gradient for regularized logistic regression. Complete the code for the function `costFunctionReg` below to return the cost and gradient.\n", "\n", "Recall that the regularized cost function in logistic regression is\n", "\n", "$$ J(\\theta) = \\frac{1}{m} \\sum_{i=1}^m \\left[ -y^{(i)}\\log \\left( h_\\theta \\left(x^{(i)} \\right) \\right) - \\left( 1 - y^{(i)} \\right) \\log \\left( 1 - h_\\theta \\left( x^{(i)} \\right) \\right) \\right] + \\frac{\\lambda}{2m} \\sum_{j=1}^n \\theta_j^2 $$\n", "\n", "Note that you should not regularize the parameters $\\theta_0$. The gradient of the cost function is a vector where the $j^{th}$ element is defined as follows:\n", "\n", "$$ \\frac{\\partial J(\\theta)}{\\partial \\theta_0} = \\frac{1}{m} \\sum_{i=1}^m \\left( h_\\theta \\left(x^{(i)}\\right) - y^{(i)} \\right) x_j^{(i)} \\qquad \\text{for } j =0 $$\n", "\n", "$$ \\frac{\\partial J(\\theta)}{\\partial \\theta_j} = \\left( \\frac{1}{m} \\sum_{i=1}^m \\left( h_\\theta \\left(x^{(i)}\\right) - y^{(i)} \\right) x_j^{(i)} \\right) + \\frac{\\lambda}{m}\\theta_j \\qquad \\text{for } j \\ge 1 $$\n", "" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def costFunctionReg(theta, X, y, lambda_):\n", " \"\"\"\n", " Compute cost and gradient for logistic regression with regularization.\n", " \n", " Parameters\n", " ----------\n", " theta : array_like\n", " Logistic regression parameters. A vector with shape (n, ). n is \n", " the number of features including any intercept. If we have mapped\n", " our initial features into polynomial features, then n is the total \n", " number of polynomial features. \n", " \n", " X : array_like\n", " The data set with shape (m x n). m is the number of examples, and\n", " n is the number of features (after feature mapping).\n", " \n", " y : array_like\n", " The data labels. A vector with shape (m, ).\n", " \n", " lambda_ : float\n", " The regularization parameter. \n", " \n", " Returns\n", " -------\n", " J : float\n", " The computed value for the regularized cost function. \n", " \n", " grad : array_like\n", " A vector of shape (n, ) which is the gradient of the cost\n", " function with respect to theta, at the current values of theta.\n", " \n", " Instructions\n", " ------------\n", " Compute the cost `J` of a particular choice of theta.\n", " Compute the partial derivatives and set `grad` to the partial\n", " derivatives of the cost w.r.t. each parameter in theta.\n", " \"\"\"\n", " # Initialize some useful values\n", " m = y.size # number of training examples\n", "\n", " # You need to return the following variables correctly \n", " # J = 0\n", " # grad = np.zeros(theta.shape)\n", "\n", " # ===================== YOUR CODE HERE ======================\n", " # J and grad without regularization\n", " J, grad = costFunction(theta, X, y)\n", " \n", " # regularize J\n", " J += lambda_/(2*m)* (theta[1:]*theta[1:]).sum()\n", "\n", " # regularize gradient\n", " for i in range(1, len(grad)):\n", " grad[i] += lambda_/m * theta[i]\n", " \n", " # =============================================================\n", " return J, grad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you are done with the `costFunctionReg`, we call it below using the initial value of $\\theta$ (initialized to all zeros), and also another test case where $\\theta$ is all ones." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cost at initial theta (zeros): 0.693\n", "Expected cost (approx) : 0.693\n", "\n", "Gradient at initial theta (zeros) - first five values only:\n", "\t[0.0085, 0.0188, 0.0001, 0.0503, 0.0115]\n", "Expected gradients (approx) - first five values only:\n", "\t[0.0085, 0.0188, 0.0001, 0.0503, 0.0115]\n", "\n", "------------------------------\n", "\n", "Cost at test theta : 3.16\n", "Expected cost (approx): 3.16\n", "\n", "Gradient at test theta - first five values only:\n", "\t[0.3460, 0.1614, 0.1948, 0.2269, 0.0922]\n", "Expected gradients (approx) - first five values only:\n", "\t[0.3460, 0.1614, 0.1948, 0.2269, 0.0922]\n" ] } ], "source": [ "# Initialize fitting parameters\n", "initial_theta = np.zeros(X.shape[1])\n", "\n", "# Set regularization parameter lambda to 1\n", "# DO NOT use `lambda` as a variable name in python\n", "# because it is a python keyword\n", "lambda_ = 1\n", "\n", "# Compute and display initial cost and gradient for regularized logistic\n", "# regression\n", "cost, grad = costFunctionReg(initial_theta, X, y, lambda_)\n", "\n", "print('Cost at initial theta (zeros): {:.3f}'.format(cost))\n", "print('Expected cost (approx) : 0.693\\n')\n", "\n", "print('Gradient at initial theta (zeros) - first five values only:')\n", "print('\\t[{:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}]'.format(*grad[:5]))\n", "print('Expected gradients (approx) - first five values only:')\n", "print('\\t[0.0085, 0.0188, 0.0001, 0.0503, 0.0115]\\n')\n", "\n", "\n", "# Compute and display cost and gradient\n", "# with all-ones theta and lambda = 10\n", "test_theta = np.ones(X.shape[1])\n", "cost, grad = costFunctionReg(test_theta, X, y, 10)\n", "\n", "print('------------------------------\\n')\n", "print('Cost at test theta : {:.2f}'.format(cost))\n", "print('Expected cost (approx): 3.16\\n')\n", "\n", "print('Gradient at test theta - first five values only:')\n", "print('\\t[{:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}]'.format(*grad[:5]))\n", "print('Expected gradients (approx) - first five values only:')\n", "print('\\t[0.3460, 0.1614, 0.1948, 0.2269, 0.0922]')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*You should now submit your solutions.*" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Submitting Solutions | Programming Exercise logistic-regression\n", "\n", " Part Name | Score | Feedback\n", " --------- | ----- | --------\n", " Regularized Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Sigmoid Function | 30 / 30 | Nice work!\n", " Logistic Regression Cost | 30 / 30 | Nice work!\n", " Logistic Regression Gradient | 5 / 5 | Nice work!\n", " Predict | 15 / 15 | Nice work!\n", " Regularized Logistic Regression Cost | 15 / 15 | Nice work!\n", " --------------------------------\n", " | 100 / 100 | \n", "\n" ] } ], "source": [ "grader[5] = costFunctionReg\n", "grader[6] = costFunctionReg\n", "grader.grade()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 2.3.1 Learning parameters using `scipy.optimize.minimize`\n", "\n", "Similar to the previous parts, you will use `optimize.minimize` to learn the optimal parameters $\\theta$. If you have completed the cost and gradient for regularized logistic regression (`costFunctionReg`) correctly, you should be able to step through the next part of to learn the parameters $\\theta$ using `optimize.minimize`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.4 Plotting the decision boundary\n", "\n", "To help you visualize the model learned by this classifier, we have provided the function `plotDecisionBoundary` which plots the (non-linear) decision boundary that separates the positive and negative examples. In `plotDecisionBoundary`, we plot the non-linear decision boundary by computing the classifier’s predictions on an evenly spaced grid and then and draw a contour plot where the predictions change from y = 0 to y = 1. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.5 Optional (ungraded) exercises\n", "\n", "In this part of the exercise, you will get to try out different regularization parameters for the dataset to understand how regularization prevents overfitting.\n", "\n", "Notice the changes in the decision boundary as you vary $\\lambda$. With a small\n", "$\\lambda$, you should find that the classifier gets almost every training example correct, but draws a very complicated boundary, thus overfitting the data. See the following figures for the decision boundaries you should get for different values of $\\lambda$. \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", "
\n", " No regularization (overfitting)\n", " \n", " Decision boundary with regularization\n", " \n", " \n", " Decision boundary with too much regularization\n", " \n", "
\n", "\n", "This is not a good decision boundary: for example, it predicts that a point at $x = (−0.25, 1.5)$ is accepted $(y = 1)$, which seems to be an incorrect decision given the training set.\n", "With a larger $\\lambda$, you should see a plot that shows an simpler decision boundary which still separates the positives and negatives fairly well. However, if $\\lambda$ is set to too high a value, you will not get a good fit and the decision boundary will not follow the data so well, thus underfitting the data." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Train Accuracy: 83.1 %\n", "Expected accuracy (with lambda = 1): 83.1 % (approx)\n", "\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Initialize fitting parameters\n", "initial_theta = np.zeros(X.shape[1])\n", "\n", "# Set regularization parameter lambda to 1 (you should vary this)\n", "lambda_ = 1\n", "\n", "# set options for optimize.minimize\n", "options= {'maxiter': 100}\n", "\n", "res = optimize.minimize(costFunctionReg,\n", " initial_theta,\n", " (X, y, lambda_),\n", " jac=True,\n", " method='TNC',\n", " options=options)\n", "\n", "# the fun property of OptimizeResult object returns\n", "# the value of costFunction at optimized theta\n", "cost = res.fun\n", "\n", "# the optimized theta is in the x property of the result\n", "theta = res.x\n", "\n", "utils.plotDecisionBoundary(plotData, theta, X, y)\n", "pyplot.xlabel('Microchip Test 1')\n", "pyplot.ylabel('Microchip Test 2')\n", "pyplot.legend(['y = 1', 'y = 0'])\n", "pyplot.grid(False)\n", "pyplot.title('lambda = %0.2f' % lambda_)\n", "\n", "# Compute accuracy on our training set\n", "p = predict(theta, X)\n", "\n", "print('Train Accuracy: %.1f %%' % (np.mean(p == y) * 100))\n", "print('Expected accuracy (with lambda = 1): 83.1 % (approx)\\n')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*You do not need to submit any solutions for these optional (ungraded) exercises.*" ] } ], "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.7.6" } }, "nbformat": 4, "nbformat_minor": 2 }