# Sebastian Raschka, 2015 (http://sebastianraschka.com) # Python Machine Learning - Code Examples # # Chapter 6 - Learning Best Practices for Model Evaluation # and Hyperparameter Tuning # # S. Raschka. Python Machine Learning. Packt Publishing Ltd., 2015. # GitHub Repo: https://github.com/rasbt/python-machine-learning-book # # License: MIT # https://github.com/rasbt/python-machine-learning-book/blob/master/LICENSE.txt import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.metrics import confusion_matrix from sklearn.metrics import f1_score from sklearn.metrics import recall_score from sklearn.metrics import precision_score from sklearn.metrics import make_scorer from sklearn.metrics import roc_curve from sklearn.metrics import auc from sklearn.metrics import roc_auc_score from sklearn.metrics import accuracy_score from scipy import interp # for sklearn 0.18's alternative syntax from distutils.version import LooseVersion as Version from sklearn import __version__ as sklearn_version if Version(sklearn_version) < '0.18': from sklearn.grid_search import train_test_split from sklearn.cross_validation import StratifiedKFold from sklearn.cross_validation import cross_val_score from sklearn.learning_curve import learning_curve from sklearn.learning_curve import validation_curve from sklearn.grid_search import GridSearchCV else: from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedKFold from sklearn.model_selection import cross_val_score from sklearn.model_selection import learning_curve from sklearn.model_selection import validation_curve from sklearn.model_selection import GridSearchCV ############################################################################# print(50 * '=') print('Section: Loading the Breast Cancer Wisconsin dataset') print(50 * '-') df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases' '/breast-cancer-wisconsin/wdbc.data', header=None) print('Breast Cancer dataset excerpt:\n\n') print(df.head()) print('Breast Cancer dataset dimensions:\n\n') print(df.shape) X = df.loc[:, 2:].values y = df.loc[:, 1].values le = LabelEncoder() y = le.fit_transform(y) y_enc = le.transform(['M', 'B']) print("Label encoding example, le.transform(['M', 'B'])") print(le.transform(['M', 'B'])) X_train, X_test, y_train, y_test = \ train_test_split(X, y, test_size=0.20, random_state=1) ############################################################################# print(50 * '=') print('Section: Combining transformers and estimators in a pipeline') print(50 * '-') pipe_lr = Pipeline([('scl', StandardScaler()), ('pca', PCA(n_components=2)), ('clf', LogisticRegression(random_state=1))]) pipe_lr.fit(X_train, y_train) print('Test Accuracy: %.3f' % pipe_lr.score(X_test, y_test)) y_pred = pipe_lr.predict(X_test) ############################################################################# print(50 * '=') print('Section: K-fold cross-validation') print(50 * '-') if Version(sklearn_version) < '0.18': kfold = StratifiedKFold(y=y_train, n_folds=10, random_state=1) else: kfold = StratifiedKFold(n_splits=10, random_state=1).split(X_train, y_train) scores = [] for k, (train, test) in enumerate(kfold): pipe_lr.fit(X_train[train], y_train[train]) score = pipe_lr.score(X_train[test], y_train[test]) scores.append(score) print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k + 1, np.bincount(y_train[train]), score)) print('\nCV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores))) print('Using StratifiedKFold') if Version(sklearn_version) < '0.18': kfold = StratifiedKFold(y=y_train, n_folds=10, random_state=1) else: kfold = StratifiedKFold(n_splits=10, random_state=1).split(X_train, y_train) scores = [] for k, (train, test) in enumerate(kfold): pipe_lr.fit(X_train[train], y_train[train]) score = pipe_lr.score(X_train[test], y_train[test]) scores.append(score) print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k + 1, np.bincount(y_train[train]), score)) print('\nCV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores))) print('Using cross_val_score') scores = cross_val_score(estimator=pipe_lr, X=X_train, y=y_train, cv=10, n_jobs=1) print('CV accuracy scores: %s' % scores) print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores))) ############################################################################# print(50 * '=') print('Section: Diagnosing bias and variance problems with learning curves') print(50 * '-') pipe_lr = Pipeline([('scl', StandardScaler()), ('clf', LogisticRegression(penalty='l2', random_state=0))]) train_sizes, train_scores, test_scores =\ learning_curve(estimator=pipe_lr, X=X_train, y=y_train, train_sizes=np.linspace(0.1, 1.0, 10), cv=10, n_jobs=1) train_mean = np.mean(train_scores, axis=1) train_std = np.std(train_scores, axis=1) test_mean = np.mean(test_scores, axis=1) test_std = np.std(test_scores, axis=1) plt.plot(train_sizes, train_mean, color='blue', marker='o', markersize=5, label='training accuracy') plt.fill_between(train_sizes, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue') plt.plot(train_sizes, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy') plt.fill_between(train_sizes, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green') plt.grid() plt.xlabel('Number of training samples') plt.ylabel('Accuracy') plt.legend(loc='lower right') plt.ylim([0.8, 1.0]) # plt.tight_layout() # plt.savefig('./figures/learning_curve.png', dpi=300) plt.show() ############################################################################# print(50 * '=') print('Section: Addressing over- and underfitting with validation curves') print(50 * '-') param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0] train_scores, test_scores = validation_curve( estimator=pipe_lr, X=X_train, y=y_train, param_name='clf__C', param_range=param_range, cv=10) train_mean = np.mean(train_scores, axis=1) train_std = np.std(train_scores, axis=1) test_mean = np.mean(test_scores, axis=1) test_std = np.std(test_scores, axis=1) plt.plot(param_range, train_mean, color='blue', marker='o', markersize=5, label='training accuracy') plt.fill_between(param_range, train_mean + train_std, train_mean - train_std, alpha=0.15, color='blue') plt.plot(param_range, test_mean, color='green', linestyle='--', marker='s', markersize=5, label='validation accuracy') plt.fill_between(param_range, test_mean + test_std, test_mean - test_std, alpha=0.15, color='green') plt.grid() plt.xscale('log') plt.legend(loc='lower right') plt.xlabel('Parameter C') plt.ylabel('Accuracy') plt.ylim([0.8, 1.0]) # plt.tight_layout() # plt.savefig('./figures/validation_curve.png', dpi=300) plt.show() ############################################################################# print(50 * '=') print('Section: Tuning hyperparameters via grid search') print(50 * '-') pipe_svc = Pipeline([('scl', StandardScaler()), ('clf', SVC(random_state=1))]) param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0] param_grid = [{'clf__C': param_range, 'clf__kernel': ['linear']}, {'clf__C': param_range, 'clf__gamma': param_range, 'clf__kernel': ['rbf']}] gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=10, n_jobs=-1) gs = gs.fit(X_train, y_train) print('Validation accuracy', gs.best_score_) print('Best parameters', gs.best_params_) clf = gs.best_estimator_ clf.fit(X_train, y_train) print('Test accuracy: %.3f' % clf.score(X_test, y_test)) ############################################################################# print(50 * '=') print('Section: Algorithm selection with nested cross-validation') print(50 * '-') gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=2) # Note: Optionally, you could use cv=2 # in the GridSearchCV above to produce # the 5 x 2 nested CV that is shown in the figure. scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5) print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores))) gs = GridSearchCV(estimator=DecisionTreeClassifier(random_state=0), param_grid=[{'max_depth': [1, 2, 3, 4, 5, 6, 7, None]}], scoring='accuracy', cv=2) scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5) print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores))) ############################################################################# print(50 * '=') print('Section: Reading a confusion matrix') print(50 * '-') pipe_svc.fit(X_train, y_train) y_pred = pipe_svc.predict(X_test) confmat = confusion_matrix(y_true=y_test, y_pred=y_pred) print('Confusion matrix', confmat) fig, ax = plt.subplots(figsize=(2.5, 2.5)) ax.matshow(confmat, cmap=plt.cm.Blues, alpha=0.3) for i in range(confmat.shape[0]): for j in range(confmat.shape[1]): ax.text(x=j, y=i, s=confmat[i, j], va='center', ha='center') plt.xlabel('predicted label') plt.ylabel('true label') # plt.tight_layout() # plt.savefig('./figures/confusion_matrix.png', dpi=300) plt.show() ############################################################################# print(50 * '=') print('Section: Optimizing the precision and recall of a classification model') print(50 * '-') print('Precision: %.3f' % precision_score(y_true=y_test, y_pred=y_pred)) print('Recall: %.3f' % recall_score(y_true=y_test, y_pred=y_pred)) print('F1: %.3f' % f1_score(y_true=y_test, y_pred=y_pred)) scorer = make_scorer(f1_score, pos_label=0) c_gamma_range = [0.01, 0.1, 1.0, 10.0] param_grid = [{'clf__C': c_gamma_range, 'clf__kernel': ['linear']}, {'clf__C': c_gamma_range, 'clf__gamma': c_gamma_range, 'clf__kernel': ['rbf']}] gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring=scorer, cv=10, n_jobs=-1) gs = gs.fit(X_train, y_train) print(gs.best_score_) print(gs.best_params_) ############################################################################# print(50 * '=') print('Section: Plotting a receiver operating characteristic') print(50 * '-') pipe_lr = Pipeline([('scl', StandardScaler()), ('pca', PCA(n_components=2)), ('clf', LogisticRegression(penalty='l2', random_state=0, C=100.0))]) X_train2 = X_train[:, [4, 14]] if Version(sklearn_version) < '0.18': cv = StratifiedKFold(y_train, n_folds=3, random_state=1) else: cv = list(StratifiedKFold(n_splits=3, random_state=1).split(X_train, y_train)) fig = plt.figure(figsize=(7, 5)) mean_tpr = 0.0 mean_fpr = np.linspace(0, 1, 100) all_tpr = [] for i, (train, test) in enumerate(cv): probas = pipe_lr.fit(X_train2[train], y_train[train]).predict_proba(X_train2[test]) fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1) mean_tpr += interp(mean_fpr, fpr, tpr) mean_tpr[0] = 0.0 roc_auc = auc(fpr, tpr) plt.plot(fpr, tpr, lw=1, label='ROC fold %d (area = %0.2f)' % (i + 1, roc_auc)) plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='random guessing') mean_tpr /= len(cv) mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) plt.plot(mean_fpr, mean_tpr, 'k--', label='mean ROC (area = %0.2f)' % mean_auc, lw=2) plt.plot([0, 0, 1], [0, 1, 1], lw=2, linestyle=':', color='black', label='perfect performance') plt.xlim([-0.05, 1.05]) plt.ylim([-0.05, 1.05]) plt.xlabel('false positive rate') plt.ylabel('true positive rate') plt.title('Receiver Operator Characteristic') plt.legend(loc="lower right") # plt.tight_layout() # plt.savefig('./figures/roc.png', dpi=300) plt.show() pipe_lr = pipe_lr.fit(X_train2, y_train) y_pred2 = pipe_lr.predict(X_test[:, [4, 14]]) print('ROC AUC: %.3f' % roc_auc_score(y_true=y_test, y_score=y_pred2)) print('Accuracy: %.3f' % accuracy_score(y_true=y_test, y_pred=y_pred2)) ############################################################################# print(50 * '=') print('Section: The scoring metrics for multiclass classification') print(50 * '-') pre_scorer = make_scorer(score_func=precision_score, pos_label=1, greater_is_better=True, average='micro')