|
| 1 | +{ |
| 2 | +"cells": [ |
| 3 | +{ |
| 4 | +"cell_type": "markdown", |
| 5 | +"id": "27b09e13", |
| 6 | +"metadata": {}, |
| 7 | +"source": [ |
| 8 | +"# Algorithm Optimization problems" |
| 9 | +] |
| 10 | +}, |
| 11 | +{ |
| 12 | +"cell_type": "markdown", |
| 13 | +"id": "6cb6a105", |
| 14 | +"metadata": {}, |
| 15 | +"source": [ |
| 16 | +"## Exercise 3: Implementing a KNN Algorithm with Scikit-learn\n", |
| 17 | +"\n", |
| 18 | +"In this exercise, we will have a brief introduction to the Scikit-learn library. We will see how this library can be used to implement the KNN algorithm in less than 20 lines of code. Then the task will be to try to optimize the parameter of this algorithm. Don't worry! Scikit-Learn, and also de KNN algorithm will be explained very well in future modules.\n", |
| 19 | +"\n", |
| 20 | +"**The dataset**\n", |
| 21 | +"\n", |
| 22 | +"We are going to use the famous iris data set for our KNN example. The dataset consists of four attributes: sepal-width, sepal-length, petal-width and petal-length. These are the attributes of specific types of iris plant. The goal is to predict the class to which these plants belong. There are three classes in the dataset: Iris-setosa, Iris-versicolor and Iris-virginica.\n", |
| 23 | +"\n", |
| 24 | +"\n", |
| 25 | +"**Library installation**\n", |
| 26 | +"\n", |
| 27 | +"\n", |
| 28 | +"First let's install the Scikit-learn library by entering the following command in the terminal:\n", |
| 29 | +"\n", |
| 30 | +"```py\n", |
| 31 | +"pip install -U scikit-learn\n", |
| 32 | +"```\n", |
| 33 | +"\n", |
| 34 | +"Now let's see how to implement the KNN algorithm:\n", |
| 35 | +"\n", |
| 36 | +"**Importing libraries**" |
| 37 | +] |
| 38 | +}, |
| 39 | +{ |
| 40 | +"cell_type": "code", |
| 41 | +"execution_count": null, |
| 42 | +"id": "9d0f17f0", |
| 43 | +"metadata": {}, |
| 44 | +"outputs": [], |
| 45 | +"source": [ |
| 46 | +"import numpy as np\n", |
| 47 | +"import matplotlib.pyplot as plt\n", |
| 48 | +"import pandas as pd" |
| 49 | +] |
| 50 | +}, |
| 51 | +{ |
| 52 | +"cell_type": "markdown", |
| 53 | +"id": "d3e87b86", |
| 54 | +"metadata": {}, |
| 55 | +"source": [ |
| 56 | +"**Importing and loading the dataset**" |
| 57 | +] |
| 58 | +}, |
| 59 | +{ |
| 60 | +"cell_type": "code", |
| 61 | +"execution_count": null, |
| 62 | +"id": "ca2f71e1", |
| 63 | +"metadata": {}, |
| 64 | +"outputs": [], |
| 65 | +"source": [ |
| 66 | +"url = \"https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data\"\n", |
| 67 | +"\n", |
| 68 | +"# Assign colum names to the dataset\n", |
| 69 | +"names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'Class']\n", |
| 70 | +"\n", |
| 71 | +"# Read dataset to pandas dataframe\n", |
| 72 | +"dataset = pd.read_csv(url, names=names)" |
| 73 | +] |
| 74 | +}, |
| 75 | +{ |
| 76 | +"cell_type": "markdown", |
| 77 | +"id": "9fd366f8", |
| 78 | +"metadata": {}, |
| 79 | +"source": [ |
| 80 | +"**Looking at the first rows of the data**" |
| 81 | +] |
| 82 | +}, |
| 83 | +{ |
| 84 | +"cell_type": "code", |
| 85 | +"execution_count": null, |
| 86 | +"id": "058c3171", |
| 87 | +"metadata": {}, |
| 88 | +"outputs": [], |
| 89 | +"source": [ |
| 90 | +"dataset.head()" |
| 91 | +] |
| 92 | +}, |
| 93 | +{ |
| 94 | +"cell_type": "markdown", |
| 95 | +"id": "e3dae7c8", |
| 96 | +"metadata": {}, |
| 97 | +"source": [ |
| 98 | +"**Pre-processing**\n", |
| 99 | +"\n", |
| 100 | +"Split dataset into attributes and labels. Again, don't worry, this data preprocessing part will also be very well explained in a future module. Now, we will focus on the algorithm optimization." |
| 101 | +] |
| 102 | +}, |
| 103 | +{ |
| 104 | +"cell_type": "code", |
| 105 | +"execution_count": null, |
| 106 | +"id": "ce94dbb1", |
| 107 | +"metadata": {}, |
| 108 | +"outputs": [], |
| 109 | +"source": [ |
| 110 | +"X = dataset.iloc[:, :-1].values\n", |
| 111 | +"y = dataset.iloc[:, 4].values" |
| 112 | +] |
| 113 | +}, |
| 114 | +{ |
| 115 | +"cell_type": "markdown", |
| 116 | +"id": "d8b9dbed", |
| 117 | +"metadata": {}, |
| 118 | +"source": [ |
| 119 | +"**Train Test split**\n", |
| 120 | +"\n", |
| 121 | +"In machine learning, when we are building a model, we have to be careful to do not overfit it. Overfitting means that our model works very well in known data but when it works with unseen data, it has a poor performance. To avoid this, we divide our dataset into training and test datasets. This way our algorithm is tested on un-seen data to evaluate the performance of our algorithm. We will divide it into 80% training data and 20% for testing. This 4 blocks of data would be:\n", |
| 122 | +"\n", |
| 123 | +"-X_train: training features\n", |
| 124 | +"\n", |
| 125 | +"-X_test: test features\n", |
| 126 | +"\n", |
| 127 | +"-y_train: training labels\n", |
| 128 | +"\n", |
| 129 | +"-y_test: test labels" |
| 130 | +] |
| 131 | +}, |
| 132 | +{ |
| 133 | +"cell_type": "code", |
| 134 | +"execution_count": null, |
| 135 | +"id": "8ccaf14e", |
| 136 | +"metadata": {}, |
| 137 | +"outputs": [], |
| 138 | +"source": [ |
| 139 | +"from sklearn.model_selection import train_test_split\n", |
| 140 | +"\n", |
| 141 | +"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20)" |
| 142 | +] |
| 143 | +}, |
| 144 | +{ |
| 145 | +"cell_type": "markdown", |
| 146 | +"id": "21b18743", |
| 147 | +"metadata": {}, |
| 148 | +"source": [ |
| 149 | +"**Feature Scaling**\n", |
| 150 | +"\n", |
| 151 | +"The majority of classifiers calculate the distance between two points by the Euclidean distance. If one of the features has a broad range of values, the distance will be governed by this particular feature. Therefore, the range of all features should be normalized so that each feature contributes approximately proportionately to the final distance.\n", |
| 152 | +"\n", |
| 153 | +"It is a good practice to scale the features so that all of them can be uniformly evaluated.\n", |
| 154 | +"We will read more about feature scaling in the data preprocessing module, but now, let's have a first look at how it is implemented." |
| 155 | +] |
| 156 | +}, |
| 157 | +{ |
| 158 | +"cell_type": "code", |
| 159 | +"execution_count": null, |
| 160 | +"id": "9904f4a0", |
| 161 | +"metadata": {}, |
| 162 | +"outputs": [], |
| 163 | +"source": [ |
| 164 | +"from sklearn.preprocessing import StandardScaler\n", |
| 165 | +"scaler = StandardScaler()\n", |
| 166 | +"scaler.fit(X_train)\n", |
| 167 | +"\n", |
| 168 | +"X_train = scaler.transform(X_train)\n", |
| 169 | +"X_test = scaler.transform(X_test)" |
| 170 | +] |
| 171 | +}, |
| 172 | +{ |
| 173 | +"cell_type": "markdown", |
| 174 | +"id": "6c368418", |
| 175 | +"metadata": {}, |
| 176 | +"source": [ |
| 177 | +"**Train the KNN algorithm**\n", |
| 178 | +"\n", |
| 179 | +"Now our data is ready for training. We will begin by importing the algorithm we want to train from the scikit-learn library, and then we will initialize it with one parameter: n_neigbours. This is basically the value for 'K'. We don't know what is the ideal number of neighbors(K) yet so we will start it with 5. After making the predictions we will try to otpimize this K value.\n", |
| 180 | +"\n", |
| 181 | +"Finally, we train our model using .fit on our training features and training labels (they both represent 80% of the data)." |
| 182 | +] |
| 183 | +}, |
| 184 | +{ |
| 185 | +"cell_type": "code", |
| 186 | +"execution_count": null, |
| 187 | +"id": "fa6cda99", |
| 188 | +"metadata": {}, |
| 189 | +"outputs": [], |
| 190 | +"source": [ |
| 191 | +"from sklearn.neighbors import KNeighborsClassifier\n", |
| 192 | +"classifier = KNeighborsClassifier(n_neighbors=5)\n", |
| 193 | +"classifier.fit(X_train, y_train)" |
| 194 | +] |
| 195 | +}, |
| 196 | +{ |
| 197 | +"cell_type": "markdown", |
| 198 | +"id": "14b37ff9", |
| 199 | +"metadata": {}, |
| 200 | +"source": [ |
| 201 | +"**Make predictions on our test data**\n", |
| 202 | +"\n", |
| 203 | +"The model has been trained, so now we use the same algorithm (stored in the 'classifier' variable) to predict only on the features of the test dataset (20% of the data). This time we don't use the labels, because we want to predict them, and then compare them with the real test labels." |
| 204 | +] |
| 205 | +}, |
| 206 | +{ |
| 207 | +"cell_type": "code", |
| 208 | +"execution_count": null, |
| 209 | +"id": "2dab6f34", |
| 210 | +"metadata": {}, |
| 211 | +"outputs": [], |
| 212 | +"source": [ |
| 213 | +"y_pred = classifier.predict(X_test)" |
| 214 | +] |
| 215 | +}, |
| 216 | +{ |
| 217 | +"cell_type": "markdown", |
| 218 | +"id": "5ebcc483", |
| 219 | +"metadata": {}, |
| 220 | +"source": [ |
| 221 | +"**Evaluate the algorithm**\n", |
| 222 | +"\n", |
| 223 | +"Confusion matrix, precision, recall and f1 score are the most commonly used evaluation metrics for classification problems.\n", |
| 224 | +"The confusion_matrix and classification_report methods of the sklearn.metrics can be used to calculate these metrics. Here we will compare our y_pred results versus our y_test true labels." |
| 225 | +] |
| 226 | +}, |
| 227 | +{ |
| 228 | +"cell_type": "code", |
| 229 | +"execution_count": null, |
| 230 | +"id": "7eaac127", |
| 231 | +"metadata": {}, |
| 232 | +"outputs": [], |
| 233 | +"source": [ |
| 234 | +"from sklearn.metrics import classification_report, confusion_matrix\n", |
| 235 | +"print(confusion_matrix(y_test, y_pred))\n", |
| 236 | +"print(classification_report(y_test, y_pred))" |
| 237 | +] |
| 238 | +}, |
| 239 | +{ |
| 240 | +"cell_type": "markdown", |
| 241 | +"id": "0365c265", |
| 242 | +"metadata": {}, |
| 243 | +"source": [ |
| 244 | +"**Optimizing the KNN algorithm**\n", |
| 245 | +"\n", |
| 246 | +"There is no way to know beforehand which value of 'K' yields the best results since the beginning. We randomly chose 5 as the 'K' value.\n", |
| 247 | +"\n", |
| 248 | +"This is where we optimize our algorithm. To help find the best value of K:\n", |
| 249 | +"\n", |
| 250 | +"1. First create a function that calculates the mean of error for all the predicted values where K ranges from 1 to 40. \n", |
| 251 | +"2. Plot the error values against K values.\n", |
| 252 | +"3. Vary the test and training size along with the K value to see how your results differ and how can you improve the accuracy of your algorithm. \n", |
| 253 | +"\n" |
| 254 | +] |
| 255 | +}, |
| 256 | +{ |
| 257 | +"cell_type": "code", |
| 258 | +"execution_count": null, |
| 259 | +"id": "6418e5e4", |
| 260 | +"metadata": {}, |
| 261 | +"outputs": [], |
| 262 | +"source": [ |
| 263 | +"error = []\n", |
| 264 | +"\n", |
| 265 | +"# Calculating error for K values between 1 and 40\n", |
| 266 | +"for i in range(1, 40):\n", |
| 267 | +" knn = KNeighborsClassifier(n_neighbors=i)\n", |
| 268 | +" knn.fit(X_train, y_train)\n", |
| 269 | +" pred_i = knn.predict(X_test)\n", |
| 270 | +" error.append(np.mean(pred_i != y_test))\n", |
| 271 | +" " |
| 272 | +] |
| 273 | +}, |
| 274 | +{ |
| 275 | +"cell_type": "code", |
| 276 | +"execution_count": null, |
| 277 | +"id": "9483300a", |
| 278 | +"metadata": {}, |
| 279 | +"outputs": [], |
| 280 | +"source": [ |
| 281 | +"# plot the error values against K values. Establish the graph size.\n", |
| 282 | +"\n", |
| 283 | +"plt.figure(figsize=(12, 6))\n", |
| 284 | +"plt.plot(range(1, 40), error, color='red', linestyle='dashed', marker='o',\n", |
| 285 | +" markerfacecolor='blue', markersize=10)\n", |
| 286 | +"\n", |
| 287 | +"# title and labels (uncomment the following code lines)\n", |
| 288 | +"plt.title('Error Rate K Value') #plt.title('Error Rate K Value')\n", |
| 289 | +"plt.xlabel('K Value') #plt.xlabel('K Value')\n", |
| 290 | +"plt.ylabel('Mean Error') #plt.ylabel('Mean Error')" |
| 291 | +] |
| 292 | +}, |
| 293 | +{ |
| 294 | +"cell_type": "markdown", |
| 295 | +"id": "b281af6e", |
| 296 | +"metadata": {}, |
| 297 | +"source": [ |
| 298 | +"Source:\n", |
| 299 | +"\n", |
| 300 | +"https://www.geeksforgeeks.org/\n", |
| 301 | +"\n", |
| 302 | +"https://stackabuse.com/big-o-notation-and-algorithm-analysis-with-python-examples/\n", |
| 303 | +"\n", |
| 304 | +"https://stackabuse.com/quicksort-in-python/\n", |
| 305 | +"\n", |
| 306 | +"https://stackabuse.com/k-nearest-neighbors-algorithm-in-python-and-scikit-learn/" |
| 307 | +] |
| 308 | +} |
| 309 | +], |
| 310 | +"metadata": { |
| 311 | +"interpreter": { |
| 312 | +"hash": "9248718ffe6ce6938b217e69dbcc175ea21f4c6b28a317e96c05334edae734bb" |
| 313 | +}, |
| 314 | +"kernelspec": { |
| 315 | +"display_name": "Python 3 (ipykernel)", |
| 316 | +"language": "python", |
| 317 | +"name": "python3" |
| 318 | +}, |
| 319 | +"language_info": { |
| 320 | +"codemirror_mode": { |
| 321 | +"name": "ipython", |
| 322 | +"version": 3 |
| 323 | +}, |
| 324 | +"file_extension": ".py", |
| 325 | +"mimetype": "text/x-python", |
| 326 | +"name": "python", |
| 327 | +"nbconvert_exporter": "python", |
| 328 | +"pygments_lexer": "ipython3", |
| 329 | +"version": "3.9.12" |
| 330 | +} |
| 331 | +}, |
| 332 | +"nbformat": 4, |
| 333 | +"nbformat_minor": 5 |
| 334 | +} |
0 commit comments