# coding: utf-8 from sklearn import datasets import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import Perceptron from sklearn.metrics import accuracy_score from matplotlib.colors import ListedColormap import matplotlib.pyplot as plt import matplotlib from distutils.version import LooseVersion from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.linear_model import SGDClassifier from sklearn.tree import DecisionTreeClassifier from sklearn import tree from pydotplus import graph_from_dot_data from sklearn.tree import export_graphviz from sklearn.ensemble import RandomForestClassifier from sklearn.neighbors import KNeighborsClassifier # *Python Machine Learning 3rd Edition* by [Sebastian Raschka](https://sebastianraschka.com), Packt Publishing Ltd. 2019 # # Code Repository: https://github.com/rasbt/python-machine-learning-book-3rd-edition # # Code License: [MIT License](https://github.com/rasbt/python-machine-learning-book-2nd-edition/blob/master/LICENSE.txt) # # Python Machine Learning - Code Examples # # Chapter 3 - A Tour of Machine Learning Classifiers Using Scikit-Learn # Note that the optional watermark extension is a small IPython notebook plugin that I developed to make the code reproducible. You can just skip the following line(s). # *The use of `watermark` is optional. You can install this Jupyter extension via* # # conda install watermark -c conda-forge # # or # # pip install watermark # # *For more information, please see: https://github.com/rasbt/watermark.* # ### Overview # - [Choosing a classification algorithm](#Choosing-a-classification-algorithm) # - [First steps with scikit-learn](#First-steps-with-scikit-learn) # - [Training a perceptron via scikit-learn](#Training-a-perceptron-via-scikit-learn) # - [Modeling class probabilities via logistic regression](#Modeling-class-probabilities-via-logistic-regression) # - [Logistic regression intuition and conditional probabilities](#Logistic-regression-intuition-and-conditional-probabilities) # - [Learning the weights of the logistic cost function](#Learning-the-weights-of-the-logistic-cost-function) # - [Training a logistic regression model with scikit-learn](#Training-a-logistic-regression-model-with-scikit-learn) # - [Tackling overfitting via regularization](#Tackling-overfitting-via-regularization) # - [Maximum margin classification with support vector machines](#Maximum-margin-classification-with-support-vector-machines) # - [Maximum margin intuition](#Maximum-margin-intuition) # - [Dealing with the nonlinearly separable case using slack variables](#Dealing-with-the-nonlinearly-separable-case-using-slack-variables) # - [Alternative implementations in scikit-learn](#Alternative-implementations-in-scikit-learn) # - [Solving nonlinear problems using a kernel SVM](#Solving-nonlinear-problems-using-a-kernel-SVM) # - [Using the kernel trick to find separating hyperplanes in higher dimensional space](#Using-the-kernel-trick-to-find-separating-hyperplanes-in-higher-dimensional-space) # - [Decision tree learning](#Decision-tree-learning) # - [Maximizing information gain – getting the most bang for the buck](#Maximizing-information-gain-–-getting-the-most-bang-for-the-buck) # - [Building a decision tree](#Building-a-decision-tree) # - [Combining weak to strong learners via random forests](#Combining-weak-to-strong-learners-via-random-forests) # - [K-nearest neighbors – a lazy learning algorithm](#K-nearest-neighbors-–-a-lazy-learning-algorithm) # - [Summary](#Summary) # # Choosing a classification algorithm # ... # # First steps with scikit-learn # Loading the Iris dataset from scikit-learn. Here, the third column represents the petal length, and the fourth column the petal width of the flower examples. The classes are already converted to integer labels where 0=Iris-Setosa, 1=Iris-Versicolor, 2=Iris-Virginica. iris = datasets.load_iris() X = iris.data[:, [2, 3]] y = iris.target print('Class labels:', np.unique(y)) # Splitting data into 70% training and 30% test data: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=1, stratify=y) print('Labels count in y:', np.bincount(y)) print('Labels count in y_train:', np.bincount(y_train)) print('Labels count in y_test:', np.bincount(y_test)) # Standardizing the features: sc = StandardScaler() sc.fit(X_train) X_train_std = sc.transform(X_train) X_test_std = sc.transform(X_test) # ## Training a perceptron via scikit-learn ppn = Perceptron(eta0=0.1, random_state=1) ppn.fit(X_train_std, y_train) # **Note** # # - You can replace `Perceptron(n_iter, ...)` by `Perceptron(max_iter, ...)` in scikit-learn >= 0.19. The `n_iter` parameter is used here deliberately, because some people still use scikit-learn 0.18. y_pred = ppn.predict(X_test_std) print('Misclassified examples: %d' % (y_test != y_pred).sum()) print('Accuracy: %.3f' % accuracy_score(y_test, y_pred)) print('Accuracy: %.3f' % ppn.score(X_test_std, y_test)) # To check recent matplotlib compatibility def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02): # setup marker generator and color map markers = ('s', 'x', 'o', '^', 'v') colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan') cmap = ListedColormap(colors[:len(np.unique(y))]) # plot the decision surface x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1 x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution)) Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T) Z = Z.reshape(xx1.shape) plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap) plt.xlim(xx1.min(), xx1.max()) plt.ylim(xx2.min(), xx2.max()) for idx, cl in enumerate(np.unique(y)): plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, color=colors[idx], marker=markers[idx], label=cl, edgecolor='black') # highlight test examples if test_idx: # plot all examples X_test, y_test = X[test_idx, :], y[test_idx] if LooseVersion(matplotlib.__version__) < LooseVersion('0.3.4'): plt.scatter(X_test[:, 0], X_test[:, 1], c='', edgecolor='black', alpha=1.0, linewidth=1, marker='o', s=100, label='test set') else: plt.scatter(X_test[:, 0], X_test[:, 1], c='none', edgecolor='black', alpha=1.0, linewidth=1, marker='o', s=100, label='test set') # Training a perceptron model using the standardized training data: X_combined_std = np.vstack((X_train_std, X_test_std)) y_combined = np.hstack((y_train, y_test)) plot_decision_regions(X=X_combined_std, y=y_combined, classifier=ppn, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_01.png', dpi=300) plt.show() # # Modeling class probabilities via logistic regression # ... # ### Logistic regression intuition and conditional probabilities def sigmoid(z): return 1.0 / (1.0 + np.exp(-z)) z = np.arange(-7, 7, 0.1) phi_z = sigmoid(z) plt.plot(z, phi_z) plt.axvline(0.0, color='k') plt.ylim(-0.1, 1.1) plt.xlabel('z') plt.ylabel('$\phi (z)$') # y axis ticks and gridline plt.yticks([0.0, 0.5, 1.0]) ax = plt.gca() ax.yaxis.grid(True) plt.tight_layout() #plt.savefig('images/03_02.png', dpi=300) plt.show() # ### Learning the weights of the logistic cost function def cost_1(z): return - np.log(sigmoid(z)) def cost_0(z): return - np.log(1 - sigmoid(z)) z = np.arange(-10, 10, 0.1) phi_z = sigmoid(z) c1 = [cost_1(x) for x in z] plt.plot(phi_z, c1, label='J(w) if y=1') c0 = [cost_0(x) for x in z] plt.plot(phi_z, c0, linestyle='--', label='J(w) if y=0') plt.ylim(0.0, 5.1) plt.xlim([0, 1]) plt.xlabel('$\phi$(z)') plt.ylabel('J(w)') plt.legend(loc='best') plt.tight_layout() #plt.savefig('images/03_04.png', dpi=300) plt.show() class LogisticRegressionGD(object): """Logistic Regression Classifier using gradient descent. Parameters ------------ eta : float Learning rate (between 0.0 and 1.0) n_iter : int Passes over the training dataset. random_state : int Random number generator seed for random weight initialization. Attributes ----------- w_ : 1d-array Weights after fitting. cost_ : list Logistic cost function value in each epoch. """ def __init__(self, eta=0.05, n_iter=100, random_state=1): self.eta = eta self.n_iter = n_iter self.random_state = random_state def fit(self, X, y): """ Fit training data. Parameters ---------- X : {array-like}, shape = [n_examples, n_features] Training vectors, where n_examples is the number of examples and n_features is the number of features. y : array-like, shape = [n_examples] Target values. Returns ------- self : object """ rgen = np.random.RandomState(self.random_state) self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) self.cost_ = [] for i in range(self.n_iter): net_input = self.net_input(X) output = self.activation(net_input) errors = (y - output) self.w_[1:] += self.eta * X.T.dot(errors) self.w_[0] += self.eta * errors.sum() # note that we compute the logistic `cost` now # instead of the sum of squared errors cost cost = -y.dot(np.log(output)) - ((1 - y).dot(np.log(1 - output))) self.cost_.append(cost) return self def net_input(self, X): """Calculate net input""" return np.dot(X, self.w_[1:]) + self.w_[0] def activation(self, z): """Compute logistic sigmoid activation""" return 1. / (1. + np.exp(-np.clip(z, -250, 250))) def predict(self, X): """Return class label after unit step""" return np.where(self.net_input(X) >= 0.0, 1, 0) # equivalent to: # return np.where(self.activation(self.net_input(X)) >= 0.5, 1, 0) X_train_01_subset = X_train_std[(y_train == 0) | (y_train == 1)] y_train_01_subset = y_train[(y_train == 0) | (y_train == 1)] lrgd = LogisticRegressionGD(eta=0.05, n_iter=1000, random_state=1) lrgd.fit(X_train_01_subset, y_train_01_subset) plot_decision_regions(X=X_train_01_subset, y=y_train_01_subset, classifier=lrgd) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_05.png', dpi=300) plt.show() # ### Training a logistic regression model with scikit-learn lr = LogisticRegression(C=100.0, random_state=1, solver='lbfgs', multi_class='ovr') lr.fit(X_train_std, y_train) plot_decision_regions(X_combined_std, y_combined, classifier=lr, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() # plt.savefig('images/03_06.png', dpi=300) plt.show() lr.predict_proba(X_test_std[:3, :]) lr.predict_proba(X_test_std[:3, :]).sum(axis=1) lr.predict_proba(X_test_std[:3, :]).argmax(axis=1) lr.predict(X_test_std[:3, :]) lr.predict(X_test_std[0, :].reshape(1, -1)) # ### Tackling overfitting via regularization weights, params = [], [] for c in np.arange(-5, 5): lr = LogisticRegression(C=10.**c, random_state=1, solver='lbfgs', multi_class='ovr') lr.fit(X_train_std, y_train) weights.append(lr.coef_[1]) params.append(10.**c) weights = np.array(weights) plt.plot(params, weights[:, 0], label='petal length') plt.plot(params, weights[:, 1], linestyle='--', label='petal width') plt.ylabel('weight coefficient') plt.xlabel('C') plt.legend(loc='upper left') plt.xscale('log') #plt.savefig('images/03_08.png', dpi=300) plt.show() # # Maximum margin classification with support vector machines # ## Maximum margin intuition # ... # ## Dealing with the nonlinearly separable case using slack variables svm = SVC(kernel='linear', C=1.0, random_state=1) svm.fit(X_train_std, y_train) plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_11.png', dpi=300) plt.show() # ## Alternative implementations in scikit-learn ppn = SGDClassifier(loss='perceptron') lr = SGDClassifier(loss='log') svm = SGDClassifier(loss='hinge') # # Solving non-linear problems using a kernel SVM np.random.seed(1) X_xor = np.random.randn(200, 2) y_xor = np.logical_xor(X_xor[:, 0] > 0, X_xor[:, 1] > 0) y_xor = np.where(y_xor, 1, -1) plt.scatter(X_xor[y_xor == 1, 0], X_xor[y_xor == 1, 1], c='b', marker='x', label='1') plt.scatter(X_xor[y_xor == -1, 0], X_xor[y_xor == -1, 1], c='r', marker='s', label='-1') plt.xlim([-3, 3]) plt.ylim([-3, 3]) plt.legend(loc='best') plt.tight_layout() #plt.savefig('images/03_12.png', dpi=300) plt.show() # ## Using the kernel trick to find separating hyperplanes in higher dimensional space svm = SVC(kernel='rbf', random_state=1, gamma=0.10, C=10.0) svm.fit(X_xor, y_xor) plot_decision_regions(X_xor, y_xor, classifier=svm) plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_14.png', dpi=300) plt.show() svm = SVC(kernel='rbf', random_state=1, gamma=0.2, C=1.0) svm.fit(X_train_std, y_train) plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_15.png', dpi=300) plt.show() svm = SVC(kernel='rbf', random_state=1, gamma=100.0, C=1.0) svm.fit(X_train_std, y_train) plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_16.png', dpi=300) plt.show() # # Decision tree learning # ## Maximizing information gain - getting the most bang for the buck def gini(p): return p * (1 - p) + (1 - p) * (1 - (1 - p)) def entropy(p): return - p * np.log2(p) - (1 - p) * np.log2((1 - p)) def error(p): return 1 - np.max([p, 1 - p]) x = np.arange(0.0, 1.0, 0.01) ent = [entropy(p) if p != 0 else None for p in x] sc_ent = [e * 0.5 if e else None for e in ent] err = [error(i) for i in x] fig = plt.figure() ax = plt.subplot(111) for i, lab, ls, c, in zip([ent, sc_ent, gini(x), err], ['Entropy', 'Entropy (scaled)', 'Gini impurity', 'Misclassification error'], ['-', '-', '--', '-.'], ['black', 'lightgray', 'red', 'green', 'cyan']): line = ax.plot(x, i, label=lab, linestyle=ls, lw=2, color=c) ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15), ncol=5, fancybox=True, shadow=False) ax.axhline(y=0.5, linewidth=1, color='k', linestyle='--') ax.axhline(y=1.0, linewidth=1, color='k', linestyle='--') plt.ylim([0, 1.1]) plt.xlabel('p(i=1)') plt.ylabel('impurity index') #plt.savefig('images/03_19.png', dpi=300, bbox_inches='tight') plt.show() # ## Building a decision tree tree_model = DecisionTreeClassifier(criterion='gini', max_depth=4, random_state=1) tree_model.fit(X_train, y_train) X_combined = np.vstack((X_train, X_test)) y_combined = np.hstack((y_train, y_test)) plot_decision_regions(X_combined, y_combined, classifier=tree_model, test_idx=range(105, 150)) plt.xlabel('petal length [cm]') plt.ylabel('petal width [cm]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_20.png', dpi=300) plt.show() tree.plot_tree(tree_model) #plt.savefig('images/03_21_1.pdf') plt.show() dot_data = export_graphviz(tree_model, filled=True, rounded=True, class_names=['Setosa', 'Versicolor', 'Virginica'], feature_names=['petal length', 'petal width'], out_file=None) graph = graph_from_dot_data(dot_data) graph.write_png('tree.png') # ## Combining weak to strong learners via random forests forest = RandomForestClassifier(criterion='gini', n_estimators=25, random_state=1, n_jobs=2) forest.fit(X_train, y_train) plot_decision_regions(X_combined, y_combined, classifier=forest, test_idx=range(105, 150)) plt.xlabel('petal length [cm]') plt.ylabel('petal width [cm]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_22.png', dpi=300) plt.show() # # K-nearest neighbors - a lazy learning algorithm knn = KNeighborsClassifier(n_neighbors=5, p=2, metric='minkowski') knn.fit(X_train_std, y_train) plot_decision_regions(X_combined_std, y_combined, classifier=knn, test_idx=range(105, 150)) plt.xlabel('petal length [standardized]') plt.ylabel('petal width [standardized]') plt.legend(loc='upper left') plt.tight_layout() #plt.savefig('images/03_24.png', dpi=300) plt.show() # # Summary # ... # --- # # Readers may ignore the next cell.