{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Classification on Wine Dataset\n", "\n", "## IMPORTANT: make sure to rerun all the code from the beginning to obtain the results for the final version of your notebook, since this is the way we will do it before evaluting your notebook!!!\n", "\n", "### Dataset description\n", "\n", "We will be working with a dataset on wines from the UCI machine learning repository\n", "(http://archive.ics.uci.edu/ml/datasets/Wine). It contains data for 178 instances. \n", "The dataset is the results of a chemical analysis of wines grown in the same region\n", "in Italy but derived from three different cultivars. The analysis determined the\n", "quantities of 13 constituents found in each of the three types of wines. \n", "\n", "### The features in the dataset are:\n", "\n", "- Alcohol\n", "- Malic acid\n", "- Ash\n", "- Alcalinity of ash\n", "- Magnesium\n", "- Total phenols\n", "- Flavanoids\n", "- Nonflavanoid phenols\n", "- Proanthocyanins\n", "- Color intensity\n", "- Hue\n", "- OD280/OD315 of diluted wines\n", "-Proline\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first import all the packages that are needed" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "\n", "import numpy as np\n", "import scipy as sp\n", "from scipy import stats\n", "from sklearn import datasets\n", "from sklearn import linear_model\n", "from sklearn.model_selection import train_test_split" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Perceptron\n", "We will implement the perceptron and use it to learn a halfspace with 0-1 loss." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Set the random seed to your ID (matricola)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "IDnumber = 4 #COMPLETE\n", "np.random.seed(IDnumber)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load the dataset from scikit learn and then split in training set and test set (50%-50%) after applying a random permutation to the datset." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "# Load the dataset from scikit learn\n", "wine = datasets.load_wine()\n", "\n", "m = wine.data.shape[0]\n", "permutation = np.random.permutation(m)\n", "\n", "X = wine.data[permutation]\n", "Y = wine.target[permutation]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1.423e+01 1.710e+00 2.430e+00 ... 1.040e+00 3.920e+00 1.065e+03]\n", " [1.320e+01 1.780e+00 2.140e+00 ... 1.050e+00 3.400e+00 1.050e+03]\n", " [1.316e+01 2.360e+00 2.670e+00 ... 1.030e+00 3.170e+00 1.185e+03]\n", " ...\n", " [1.327e+01 4.280e+00 2.260e+00 ... 5.900e-01 1.560e+00 8.350e+02]\n", " [1.317e+01 2.590e+00 2.370e+00 ... 6.000e-01 1.620e+00 8.400e+02]\n", " [1.413e+01 4.100e+00 2.740e+00 ... 6.100e-01 1.600e+00 5.600e+02]]\n" ] }, { "data": { "text/plain": [ "178" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(wine.data)\n", "wine.data.shape[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are going to classify class \"1\" vs the other two classes (0 and 2). We are going to relabel the other classes (0 and 2) as \"-1\" so that we can use it directly with the perceptron." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "#let's relabel classes 0 and 2 as -1\n", "\n", "for i in range(len(Y)):\n", " if Y[i] != 1:\n", " Y[i] = -1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Divide the data into training set and test set (50% of the data each). **Note**: we do not normalize the features since it is not needed for this dataset and task." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-1 1 -1 -1 1 -1 1 1 -1 -1 -1 -1 1 -1 1 1 1 -1 1 -1 1 1 -1 -1\n", " 1 -1 -1 -1 -1 1 -1 1 -1 1 -1 1 -1 -1 1 -1 1 -1 -1 -1 -1 1 -1 1\n", " -1 1 -1 1 1 -1 1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 1\n", " 1 -1 1 1 -1 1 -1 -1 1 -1 1 -1 1 -1 -1 -1 1]\n" ] } ], "source": [ "#Divide in training and test: make sure that your training set\n", "#contains at least 10 elements from class 1 and at least 10 elements\n", "#from class -1! If it does not, modify the code so to apply more random\n", "#permutations (or the same permutation multiple times) until this happens.\n", "\n", "#m_training needs to be the number of samples in the training set\n", "# m_training = #COMPLETE\n", "\n", "#m_test needs to be the number of samples in the test set\n", "# m_test = #COMPLETE\n", "\n", "#X_training = instances for training set\n", "# X_training = #COMPLETE\n", "#Y_training = labels for the training set\n", "# Y_training = #COMPLETE\n", "\n", "#X_test = instances for test set\n", "# X_test = #COMPLETE\n", "#Y_test = labels for the test set\n", "# Y_test = #COMPLETE\n", "\n", "X_training, X_test, Y_training, Y_test = train_test_split(X, Y, test_size=0.50, random_state=12, stratify=Y)\n", " # stratify makes sure y id divided proportionally\n", "m_training = X_training.shape[0]\n", "m_test = X_test.shape[0]\n", "\n", "print(Y_training) #to make sure that Y_training contains both 1 and -1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Now add a 1 in front of each sample so that we can use a vector to describe all the coefficients of the model. You can use the function $hstack$ in $numpy$" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "#add a 1 to each sample\n", "train_ones = np.ones((m_training,1))\n", "test_ones = np.ones((m_test,1))\n", "\n", "X_training = np.hstack((train_ones, X_training)) #COMPLETE\n", "X_test = np.hstack((test_ones, X_test)) #COMPLETE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Now complete the function *perceptron*. Since the perceptron does not terminate if the data is not linearly separable, your implementation should return the desired output (see below) if it reached the termination condition seen in class or if a maximum number of iterations have already been run, where 1 iteration corresponds to 1 update of the perceptron weights. If the perceptron returns because the maximum number of iterations has been reached, you should return an appropriate model. \n", "\n", "The input parameters to pass are:\n", "- $X$: the matrix of input features, one row for each sample\n", "- $Y$: the vector of labels for the input features matrix X\n", "- $max\\_num\\_iterations$: the maximum number of iterations for running the perceptron\n", "\n", "The output values are:\n", "- $best\\_w$: the vector with the coefficients of the best model\n", "- $best\\_error$: the *fraction* of missclassified samples for the best model" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "from sklearn.linear_model import Perceptron\n", "from sklearn.metrics import accuracy_score\n", "\n", "def perceptron(X, Y, max_num_iterations):\n", " \n", " p = Perceptron(max_iter= max_num_iterations, random_state= 5)\n", " p.fit(X,Y)\n", " best_w = p.coef_\n", " predict_train = p.predict(X)\n", " training_score = accuracy_score(predict_train, Y)\n", " best_error = 1 - training_score\n", " \n", " return best_w, best_error, p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we use the implementation above of the perceptron to learn a model from the training data using 100 iterations and print the error of the best model we have found." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "jupyter": { "outputs_hidden": true }, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Training error with 100 iterations: 0.2471910112359551\n" ] } ], "source": [ "#now run the perceptron for 100 iterations\n", "w_found, training_error, p = perceptron(X_training,Y_training, 100)\n", "print(\"Training error with 100 iterations: \"+str(training_error))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** use the best model $w\\_found$ to predict the labels for the test dataset and print the fraction of missclassified samples in the test set (that is an estimate of the true loss)." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Estimated true loss with 100 iterations:0.29213483146067415\n" ] } ], "source": [ "#now use the w_found to make predictions on test dataset\n", "\n", "#num_errors = number of errors in the test set\n", "num_errors = 0.\n", "\n", "#ADD CODE!\n", "prediction = p.predict(X_test)\n", "for i in range(len(prediction)):\n", " num_errors = np.sum(prediction != Y_test)\n", "\n", "true_loss_estimate = num_errors/m_test\n", "\n", "#NOTE: you can avoid using num_errors if you prefer, as long as true_loss_estimate is correct\n", "print(\"Estimated true loss with 100 iterations:\"+str(true_loss_estimate))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO**: [Answer the following] what relation do you observe between the training error and the (estimated) true loss? Is this what you expected? Explain what you observe and why it does or does not conform to your expectations. [Write the answer in this cell]\n", "\n", "**ANSWER**: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Copy the code from the last 2 cells above in the cell below and repeat the training with 10000 iterations. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Training error with 10000 iterations: 0.2471910112359551\n", "Estimated true loss with 10000 iterations:0.29213483146067415\n" ] } ], "source": [ "#now run the perceptron for 10000 iterations here!\n", "\n", "#ADD CODE!\n", "w_found, training_error, p = perceptron(X_training,Y_training, 10000)\n", "#training_error = error on the training set\n", "print(\"Training error with 10000 iterations: \"+str(training_error))\n", "\n", "#num_errors = number of errors in the test set\n", "num_errors = 0.\n", "\n", "#ADD CODE!\n", "prediction = p.predict(X_test)\n", "for i in range(len(prediction)):\n", " num_errors = np.sum(prediction != Y_test)\n", "\n", "\n", "true_loss_estimate = num_errors/m_test\n", "\n", "#NOTE: you can avoid using num_errors if you prefer, as long as true_loss_estimate is correct\n", "print(\"Estimated true loss with 10000 iterations:\"+str(true_loss_estimate))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** [Answer the following] What changes in the training error and in the test error (in terms of fraction of missclassified samples)? Explain what you observe. [Write the answer in this cell]\n", "\n", "**ANSWER**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Logistic Regression\n", "Now we use logistic regression, as implemented in Scikit-learn, to predict labels. We first do it for 2 labels and then for 3 labels. We will also plot the decision region of logistic regression.\n", "\n", "We first load the dataset again." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "# Load the dataset from scikit learn\n", "wine = datasets.load_wine()\n", "\n", "m = wine.data.shape[0]\n", "permutation = np.random.permutation(m)\n", "\n", "X = wine.data[permutation]\n", "Y = wine.target[permutation]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** As for the previous part, divide the data into training and test (50%-50%), relabel classes 0 and 2 as -1. Here there is no need to add a 1 at the beginning of each row, since it will be done automatically by the function we will use." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "#Divide in training and test: make sure that your training set\n", "#contains at least 10 elements from class 1 and at least 10 elements\n", "#from class -1! If it does not, modify the code so to apply more random\n", "#permutations (or the same permutation multiple times) until this happens.\n", "#IMPORTANT: do not change the random seed.\n", "\n", "# m_training = #COMPLETE\n", "# m_test = #COMPLETE\n", "\n", "# X_training = #COMPLETE\n", "# Y_training = #COMPLETE\n", "\n", "# X_test = #COMPLETE\n", "# Y_test = #COMPLETE\n", "\n", "\n", "\n", "#let's relabel classes 0 and 2 as -1\n", "\n", "for i in range(len(Y)):\n", " if Y[i] != 1:\n", " Y[i] = -1\n", " \n", "X_training, X_test, Y_training, Y_test = train_test_split(X, Y, test_size=0.50, random_state=12, stratify=Y)\n", "\n", "m_training = X_training.shape[0]\n", "m_test = X_test.shape[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To define a logistic regression model in Scikit-learn use the instruction\n", "\n", "$linear\\_model.LogisticRegression(C=1e5)$\n", "\n", "($C$ is a parameter related to *regularization*, a technique that\n", "we will see later in the course. Setting it to a high value is almost\n", "as ignoring regularization, so the instruction above corresponds to the\n", "logistic regression you have seen in class.)\n", "\n", "To learn the model you need to use the $fit(...)$ instruction and to predict you need to use the $predict(...)$ function. See the Scikit-learn documentation for how to use it.\n", "\n", "**TO DO** Define the logistic regression model, then learn the model using the training set and predict on the test set. Then print the fraction of samples missclassified in the training set and in the test set." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error rate on training set: 0.0337078651685393\n", "Error rate on test set: 0.0561797752808989\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\gagan\\anaconda3\\lib\\site-packages\\sklearn\\linear_model\\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):\n", "STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.\n", "\n", "Increase the number of iterations (max_iter) or scale the data as shown in:\n", " https://scikit-learn.org/stable/modules/preprocessing.html\n", "Please also refer to the documentation for alternative solver options:\n", " https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression\n", " n_iter_i = _check_optimize_result(\n" ] } ], "source": [ "#part on logistic regression for 2 classes\n", "logreg = linear_model.LogisticRegression(max_iter=50) #COMPLETE\n", "\n", "#learn from training set\n", "\n", "#ADD CODE!\n", "logreg.fit(X_training,Y_training)\n", "\n", "\n", "#predict on training set\n", "\n", "#ADD CODE!\n", "prediction_train = logreg.predict(X_training)\n", "error_rate_training = 1 - accuracy_score(prediction_train, Y_training)\n", "#print the error rate = fraction of missclassified samples\n", "print(\"Error rate on training set: \"+str(error_rate_training))\n", "\n", "#predict on test set\n", "\n", "#ADD CODE!\n", "prediction_test = logreg.predict(X_test)\n", "error_rate_test = 1 - accuracy_score(prediction_test, Y_test)\n", "\n", "#print the error rate = fraction of missclassified samples\n", "\n", "print(\"Error rate on test set: \"+str(error_rate_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we do logistic regression for classification with 3 classes.\n", "\n", "**TO DO** First: let's load the data once again (with the same permutation from before)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 1, 0, 1, 2, 0, 1, 1, 0, 2, 2, 1, 1, 0, 1, 2, 2, 1, 0, 0, 0,\n", " 0, 0, 0, 1, 0, 1, 2, 2, 1, 2, 2, 1, 1, 1, 2, 2, 1, 0, 1, 0, 2, 1,\n", " 0, 1, 0, 0, 1, 2, 0, 1, 2, 2, 2, 1, 0, 2, 0, 2, 2, 1, 1, 0, 1, 0,\n", " 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 2, 2, 1, 0, 0, 0, 0, 2, 2, 0, 0, 0,\n", " 1])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#part on logistic regression for 3 classes\n", "\n", "#Divide in training and test: make sure that your training set\n", "#contains at least 10 elements from each of the 3 classes!\n", "#If it does not, modify the code so to apply more random\n", "#permutations (or the same permutation multiple times) until this happens.\n", "#IMPORTANT: do not change the random seed.\n", "X = wine.data[permutation]\n", "Y = wine.target[permutation]\n", "\n", "# X_training = #COMPLETE\n", "# Y_training = #COMPLETE\n", "\n", "# X_test = #COMPLETE\n", "# Y_test = #COMPLETE \n", "X_training, X_test, Y_training, Y_test = train_test_split(X, Y, test_size=0.50, random_state=12, stratify=Y)\n", "Y_training" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Now perform logistic regression (instructions as before) for 3 classes, learning a model from the training set and predicting on the test set. Print the fraction of missclassified samples on the training set and the fraction of missclassified samples on the test set." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error rate on training set: 0.0561797752808989\n", "Error rate on test set: 0.101123595505618\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\taran\\anaconda3\\lib\\site-packages\\sklearn\\linear_model\\_logistic.py:762: ConvergenceWarning: lbfgs failed to converge (status=1):\n", "STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.\n", "\n", "Increase the number of iterations (max_iter) or scale the data as shown in:\n", " https://scikit-learn.org/stable/modules/preprocessing.html\n", "Please also refer to the documentation for alternative solver options:\n", " https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression\n", " n_iter_i = _check_optimize_result(\n" ] } ], "source": [ "#part on logistic regression for 3 classes\n", "logreg = linear_model.LogisticRegression(max_iter=50) #COMPLETE\n", "\n", "#learn from training set\n", "\n", "#ADD CODE!\n", "logreg.fit(X_training,Y_training)\n", "\n", "#predict on training set\n", "\n", "#ADD CODE!\n", "prediction_train = logreg.predict(X_training)\n", "error_rate_training = 1 - accuracy_score(prediction_train, Y_training)\n", "#print the error rate = fraction of missclassified samples\n", "print(\"Error rate on training set: \"+str(error_rate_training))\n", "\n", "#predict on test set\n", "\n", "#ADD CODE!\n", "prediction_test = logreg.predict(X_test)\n", "error_rate_test = 1 - accuracy_score(prediction_test, Y_test)\n", "\n", "#print the error rate = fraction of missclassified samples\n", "print(\"Error rate on test set: \"+str(error_rate_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**TO DO** Now pick two features and restrict the dataset to include only two features, whose indices are specified in the $feature$ vector below. Then split into training and test." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "#to make the plot we need to reduce the data to 2D, so we choose two features\n", "\n", "features_list = ['Alcohol',\n", "'Malic acid',\n", "'Ash',\n", "'Alcalinity of ash',\n", "'Magnesium',\n", "'Total phenols',\n", "'Flavanoids',\n", "'Nonflavanoid phenols',\n", "'Proanthocyanins',\n", "'Color intensity',\n", "'Hue',\n", "'OD280/OD315 of diluted wines',\n", "'Proline']\n", "labels_list = ['class_0', 'class_1', 'class_2']\n", "\n", "index_feature1 = 0 # Alcohol #COMPLETE\n", "index_feature2 = 5 # Total phenols #COMPLETE\n", "features = [index_feature1, index_feature2]\n", "\n", "feature_name0 = features_list[features[0]]\n", "feature_name1 = features_list[features[1]]\n", "\n", "#X_red is X reduced to include only the 2 features of\n", "#indices index_feature1 and index_feature2\n", "X_red = X[:,features]\n", "\n", "# X_red_training = #COMPLETE\n", "# Y_training = #COMPLETE\n", "\n", "# X_red_test = #COMPLETE\n", "# Y_test = #COMPLETE\n", "\n", "X_red_training, X_red_test, Y_training, Y_test = train_test_split(X_red, Y, test_size=0.50, random_state=12, stratify=Y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now learn a model using the training data." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "data": { "text/plain": [ "0.8314606741573034" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#ADD CODE! (only for learning from training data)\n", "logreg = linear_model.LogisticRegression(max_iter=50) #COMPLETE\n", "logreg.fit(X_red_training,Y_training)\n", "logreg.score(X_red_training,Y_training)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If everything is ok, the code below uses the model in $logreg$ to plot the decision region for the two features chosen above, with colors denoting the predicted value. It also plots the points (with correct labels) in the training set. It makes a similar plot for the test set." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ ":14: MatplotlibDeprecationWarning: shading='flat' when X and Y have the same dimensions as C is deprecated since 3.3. Either specify the corners of the quadrilaterals with X and Y, or pass shading='auto', 'nearest' or 'gouraud', or set rcParams['pcolor.shading']. This will become an error two minor releases later.\n", " plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stderr", "output_type": "stream", "text": [ ":32: MatplotlibDeprecationWarning: shading='flat' when X and Y have the same dimensions as C is deprecated since 3.3. Either specify the corners of the quadrilaterals with X and Y, or pass shading='auto', 'nearest' or 'gouraud', or set rcParams['pcolor.shading']. This will become an error two minor releases later.\n", " plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the decision boundary. For that, we will assign a color to each\n", "# point in the mesh [x_min, x_max]x[y_min, y_max].\n", "h = .02 # step size in the mesh\n", "x_min, x_max = X_red[:, 0].min() - .5, X_red[:, 0].max() + .5\n", "y_min, y_max = X_red[:, 1].min() - .5, X_red[:, 1].max() + .5\n", "xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))\n", "\n", "Z = logreg.predict(np.c_[xx.ravel(), yy.ravel()])\n", "\n", "# Put the result into a color plot\n", "Z = Z.reshape(xx.shape)\n", "\n", "plt.figure(1, figsize=(4, 3))\n", "plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)\n", "\n", "# Plot also the training points\n", "plt.scatter(X_red_training[:, 0], X_red_training[:, 1], c=Y_training, edgecolors='k', cmap=plt.cm.Paired)\n", "plt.xlabel(feature_name0)\n", "plt.ylabel(feature_name1)\n", "\n", "plt.xlim(xx.min(), xx.max())\n", "plt.ylim(yy.min(), yy.max())\n", "plt.xticks(())\n", "plt.yticks(())\n", "plt.title('Training set')\n", "\n", "plt.show()\n", "\n", "# Put the result into a color plot\n", "Z = Z.reshape(xx.shape)\n", "plt.figure(1, figsize=(4, 3))\n", "plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)\n", "\n", "# Plot also the test points \n", "plt.scatter(X_red_test[:, 0], X_red_test[:, 1], c=Y_test, edgecolors='k', cmap=plt.cm.Paired, marker='s')\n", "plt.xlabel(feature_name0)\n", "plt.ylabel(feature_name1)\n", "\n", "plt.xlim(xx.min(), xx.max())\n", "plt.ylim(yy.min(), yy.max())\n", "plt.xticks(())\n", "plt.yticks(())\n", "plt.title('Test set')\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "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.8.8" } }, "nbformat": 4, "nbformat_minor": 4 }