import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import rbf_kernel
from sklearn.metrics import pairwise_distances
import scipy
import matplotlib.pyplot as plt
def plot_gallery(images, titles, h, w, n_row=4, n_col=10):
"""Helper function to plot a gallery of portraits"""
plt.figure(figsize=(1.8 * n_col, 2.4 * n_row))
plt.subplots_adjust(bottom=0, left=.01, right=.99, top=.90, hspace=.35)
for i in range(images.shape[0]):
plt.subplot(n_row, n_col, i + 1)
plt.imshow(images[i].reshape((h, w)), cmap=plt.cm.gray)
#plt.colorbar()
if titles is not None:
plt.title(titles[i], size=12)
plt.xticks(())
plt.yticks(())
from sklearn.base import BaseEstimator, ClassifierMixin
class RBFKernelFisherDiscriminant(BaseEstimator, ClassifierMixin):
def __init__(self, gamma=1, alpha=1e-4):
# gamma: parameter for the RBF kernel
# alpha: parameter for making the eigenvalue problem stable. You don't need to touch it
self.gamma = gamma
self.alpha = alpha
def get_params(self, deep=True):
# suppose this estimator has parameters "alpha" and "recursive"
return {"alpha": self.alpha, "gamma": self.gamma}
def set_params(self, **parameters):
for parameter, value in parameters.items():
setattr(self, parameter, value)
return self
def fit(self, X, Y):
"""
X: matrix with observations, each row is an observation
Y: targets
"""
self._X = X.copy()
self._n = X.shape[0]
# compute kernel
K0 = rbf_kernel(X, gamma=self.gamma)
# center kernel
sumK = np.sum(K0,0)
K1 = 1./self._n*np.tile(np.reshape(sumK,(-1,1)), (1,self._n))
K2 = 1./self._n * np.tile(np.reshape(sumK,(1,-1)), (self._n,1))
self._K = K0.copy()
K = K0 - K1 - K2 + np.mean(K0)
n = K.shape[0]
self.v_classes = np.unique(Y)
M_ = np.mean(K,1)
P = len(self.v_classes)
M_Mp = np.empty((n,P))
#Sb
Sb = np.zeros((self._n,self._n))
Sw = np.zeros((self._n,self._n))
for p in range(P):
idx_class_p = np.where(Y == self.v_classes[p])[0]
n_p = len(idx_class_p)
Kp = K[:,idx_class_p]
Mp = np.mean(Kp,1)
M_Mp[:,p] = Mp.copy()
MpM_ = Mp - M_
Sb += n_p* np.outer(MpM_, MpM_.T) # column * row
Sw += 1./n_p* Kp.dot(Kp.T) - np.outer(Mp, Mp.T)
#Sw inv
Sw = 0.5*(Sw+Sw.T) + 1e-6*np.eye(Sw.shape[0])
Sb = 0.5*(Sb+Sb.T) + 1e-6*np.eye(Sb.shape[0])
DD2, UU2 = scipy.linalg.eigh(Sb, Sw)
lam = DD2[-P+1:]
self.A = UU2[:,-P+1:]
self.VMp = M_Mp.T.dot(self.A)
return self
def fit_transform(self, X, Y):
"""
X: matrix with observations, each row is an observation
Y: targets
"""
self._X = X.copy()
self._n = X.shape[0]
# compute kernel
K0 = rbf_kernel(X, gamma=self.gamma)
# center kernel
sumK = np.sum(K0,0)
K1 = 1./self._n*np.tile(np.reshape(sumK,(-1,1)), (1,self._n))
K2 = 1./self._n * np.tile(np.reshape(sumK,(1,-1)), (self._n,1))
self._K = K0.copy()
K = K0 - K1 - K2 + np.mean(K0)
n = K.shape[0]
self.v_classes = np.unique(Y)
M_ = np.mean(K,1)
P = len(self.v_classes)
M_Mp = np.empty((n,P))
#Sb
Sb = np.zeros((self._n,self._n))
Sw = np.zeros((self._n,self._n))
for p in range(P):
idx_class_p = np.where(Y == self.v_classes[p])[0]
n_p = len(idx_class_p)
Kp = K[:,idx_class_p]
Mp = np.mean(Kp,1)
M_Mp[:,p] = Mp.copy()
MpM_ = Mp - M_
Sb += n_p* np.outer(MpM_, MpM_.T) # column * row
Sw += 1./n_p* Kp.dot(Kp.T) - np.outer(Mp, Mp.T)
#Sw inv
Sw = 0.5*(Sw+Sw.T) + 1e-6*np.eye(Sw.shape[0])
Sb = 0.5*(Sb+Sb.T) + 1e-6*np.eye(Sb.shape[0])
DD2, UU2 = scipy.linalg.eigh(Sb, Sw)
lam = DD2[-P+1:]
self.A = UU2[:,-P+1:]
self.VMp = M_Mp.T.dot(self.A)
return K.dot(self.A)
def transform(self, X):
nt = X.shape[0]
K0 = rbf_kernel(X, self._X, gamma=self.gamma)
K1 = (K0 - 1./self._n * np.ones((nt, self._n)).dot(self._K))
K2 = np.eye(self._n)- 1./self._n * np.ones((self._n, self._n))
K = K1.dot(K2)
return K.dot(self.A)
def predict(self, X):
U = self.transform(X)
Distance_sample_mean = pairwise_distances(U, self.VMp)
closest_mean = np.argmin(Distance_sample_mean,1)
output = np.array([self.v_classes[ii] for ii in closest_mean])
return output
def score(self, X, Y):
labels = self.predict(X)
return np.mean(labels==Y)
In this project a face recognition task using multivariate analysis methods will be studied. The dataset (Olivetti Faces) consists of ten different images taken from 40 distinct subjects. For some subjects, the images were taken at different times, varying the lighting, facial expressions (open / closed eyes, smiling / not smiling) and facial details (glasses / no glasses). All the images were taken against a dark homogeneous background with the subjects in an upright, frontal position (with tolerance for some side movement).
The next code includes the lines to download this data set and create the training and test data partitions.
from sklearn.datasets import fetch_olivetti_faces
print('The first time that you download the data it can take a while...')
olivetti_people = fetch_olivetti_faces()
# introspect the images arrays to find the shapes (for plotting)
n_samples, h, w = olivetti_people.images.shape
X = olivetti_people.data
n_features = X.shape[1]
# the label to predict is the id of the person
Y = olivetti_people.target
class_names = np.unique(Y)
n_classes = class_names.shape[0]
print("Dataset size information:")
print("n_features: %d" % n_features)
print("n_classes: %d" % n_classes)
# As example, we plot a face of each class (or person)
titles = ['cl '+str(c)+","+str(ic) for ic,c in enumerate(Y)]
ind_faces = [np.where(Y == c)[0][0] for c in class_names]
plot_gallery(X[:,:], titles, h, w, n_row=40)
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=41)
print("Size of the training set {0:d}".format(X_train.shape[0]))
print("Size of the test set {0:d}".format(X_test.shape[0]))
Output hidden; open in https://colab.research.google.com to view.
Y_train
array([30, 3, 38, 14, 28, 37, 18, 30, 32, 38, 34, 14, 36, 39, 25, 25, 8,
9, 30, 26, 38, 36, 29, 3, 11, 12, 17, 10, 39, 29, 37, 0, 34, 37,
23, 20, 9, 37, 11, 25, 17, 22, 19, 11, 39, 22, 13, 21, 32, 36, 22,
6, 0, 3, 2, 4, 4, 35, 10, 4, 33, 12, 27, 15, 23, 23, 9, 19,
10, 38, 2, 36, 23, 27, 16, 26, 28, 8, 24, 33, 31, 21, 24, 27, 18,
21, 33, 5, 5, 5, 35, 31, 18, 20, 20, 5, 13, 3, 37, 35, 25, 10,
22, 23, 7, 8, 23, 6, 30, 13, 11, 16, 28, 20, 1, 20, 29, 6, 39,
31, 21, 19, 5, 16, 19, 15, 33, 5, 0, 18, 30, 14, 10, 4, 30, 20,
34, 7, 3, 31, 6, 2, 27, 8, 36, 4, 0, 38, 6, 15, 37, 22, 16,
31, 27, 36, 3, 25, 3, 24, 9, 16, 36, 30, 33, 31, 21, 15, 20, 12,
38, 17, 31, 35, 21, 3, 18, 1, 26, 6, 1, 7, 28, 28, 33, 26, 14,
1, 0, 32, 11, 25, 34, 11, 9, 25, 37, 31, 2, 24, 29, 10, 1, 4,
7, 13, 12, 6, 17, 4, 37, 21, 35, 17, 14, 9, 29, 0, 38, 27, 26,
2, 7, 20, 7, 21, 36, 31, 10, 7, 38, 35, 27, 37, 3, 9, 23, 10,
17, 12, 0, 14, 13, 39, 11, 1, 7, 12, 31, 26, 16, 18, 14, 1, 30,
28, 22, 12, 24, 14, 18, 9, 20, 34, 17, 10, 34, 8, 19, 17, 16, 13,
23, 36, 28, 8, 24, 32, 8, 39])
The above schemes will be trained and for each scheme the best hyperparameters will be defined with cross-validation. In particular these are the hyperparameters for each scheme:
kernel: RBF and linear. gamma: for the RBF kernel case onlyC n_components: number of components for the PCAkernel: RBF and linear. gamma: for the RBF kernel caseC for the SVMn_components: number of components for the KPCA.kernel: RBF for the KPCAgamma: for the RBF kernel of the KPCAC: for the SVMkernel: the class of RBFKernelFisherDiscriminant of this notebookgamma: for the RBF kernelFirst, the number of instances of each class has been counted in the training. As it could be seen on the below results after counting each class in Y_train, the majority class is 31 with 10 observations and the minority class is 32 and 15 with only 4 observations. Therefore, it is important to keep this balance while separating the data into folds in cross validation.
from collections import Counter
classes = Counter(Y_train)
print(classes)
plt.rcParams["figure.figsize"] = [10.50, 3.50]
plt.rcParams["figure.autolayout"] = True
plt.bar(classes.keys(), classes.values())
plt.show()
Counter({31: 10, 3: 9, 37: 9, 36: 9, 10: 9, 20: 9, 30: 8, 38: 8, 14: 8, 9: 8, 17: 8, 23: 8, 21: 8, 7: 8, 28: 7, 18: 7, 25: 7, 8: 7, 11: 7, 12: 7, 0: 7, 6: 7, 4: 7, 27: 7, 16: 7, 1: 7, 34: 6, 39: 6, 26: 6, 22: 6, 13: 6, 35: 6, 33: 6, 24: 6, 5: 6, 29: 5, 19: 5, 2: 5, 32: 4, 15: 4})
In the part below, a version of k-fold cross-validation is used which preserves the imbalanced class distribution in each fold. It is called stratified k-fold cross-validation and will enforce the class distribution in each split of the data to match the distribution in the complete training dataset. It means that it will be obtained at least one instance of each class in each fold. This stratified k fold is in the sklearn.model_selection.
# k-fold cross validation iterator of k=3 folds
from sklearn.model_selection import StratifiedKFold,cross_val_score
kfolds = StratifiedKFold(n_splits=3, shuffle=True, random_state=1)
i=0
for train_index, val_index in kfolds.split(X_train, Y_train):
i+=1
# Print the selected number of folds
print(f"FOLD-{i}","\nTRAIN:", train_index, "\nVALIDATION:", val_index)
train_X, val_X = X_train[train_index], X_train[val_index]
train_y, val_y = Y_train[train_index], Y_train[val_index]
train_classes = Counter(train_y)
val_classes = Counter(val_y)
# Print the selected number of folds' classes
print(f"\nTrain Classes:", sorted(train_classes.items(), key=lambda pair: pair[0], reverse=False),
"\nValidation Classes:",sorted(val_classes.items(), key=lambda pair: pair[0], reverse=False))
FOLD-1 TRAIN: [ 0 2 3 4 5 6 8 10 11 12 13 14 15 17 19 20 21 22 23 24 25 27 28 29 31 32 34 35 37 42 46 47 48 49 50 51 53 54 55 57 59 60 61 63 64 65 66 67 68 69 70 72 73 74 75 77 78 79 80 81 82 84 85 87 88 89 91 92 93 94 96 97 98 100 102 105 107 108 110 111 112 116 118 120 121 122 123 124 125 126 128 129 133 134 135 137 139 140 143 144 145 146 147 149 150 151 152 154 156 157 162 163 168 171 172 173 175 176 177 178 179 180 181 182 184 187 188 189 192 193 194 197 198 199 200 201 202 204 205 207 208 209 210 211 212 213 214 217 219 220 222 223 224 226 228 229 230 231 232 233 234 235 237 239 241 243 244 247 248 250 252 254 255 256 257 258 259 261 264 265 266 267 269 271 272 275] VALIDATION: [ 1 7 9 16 18 26 30 33 36 38 39 40 41 43 44 45 52 56 58 62 71 76 83 86 90 95 99 101 103 104 106 109 113 114 115 117 119 127 130 131 132 136 138 141 142 148 153 155 158 159 160 161 164 165 166 167 169 170 174 183 185 186 190 191 195 196 203 206 215 216 218 221 225 227 236 238 240 242 245 246 249 251 253 260 262 263 268 270 273 274 276 277 278 279] Train Classes: [(0, 5), (1, 4), (2, 3), (3, 6), (4, 5), (5, 4), (6, 5), (7, 6), (8, 5), (9, 5), (10, 6), (11, 4), (12, 5), (13, 4), (14, 6), (15, 3), (16, 5), (17, 5), (18, 5), (19, 4), (20, 6), (21, 5), (22, 4), (23, 5), (24, 4), (25, 4), (26, 4), (27, 4), (28, 4), (29, 4), (30, 5), (31, 6), (32, 3), (33, 4), (34, 4), (35, 4), (36, 6), (37, 6), (38, 5), (39, 4)] Validation Classes: [(0, 2), (1, 3), (2, 2), (3, 3), (4, 2), (5, 2), (6, 2), (7, 2), (8, 2), (9, 3), (10, 3), (11, 3), (12, 2), (13, 2), (14, 2), (15, 1), (16, 2), (17, 3), (18, 2), (19, 1), (20, 3), (21, 3), (22, 2), (23, 3), (24, 2), (25, 3), (26, 2), (27, 3), (28, 3), (29, 1), (30, 3), (31, 4), (32, 1), (33, 2), (34, 2), (35, 2), (36, 3), (37, 3), (38, 3), (39, 2)] FOLD-2 TRAIN: [ 0 1 3 7 8 9 10 11 12 13 16 18 19 20 22 24 26 27 28 30 33 34 36 38 39 40 41 42 43 44 45 46 49 51 52 55 56 57 58 59 61 62 63 66 67 69 71 72 74 75 76 78 79 83 85 86 87 88 90 91 93 95 97 98 99 100 101 103 104 105 106 107 108 109 112 113 114 115 117 119 120 123 126 127 128 129 130 131 132 135 136 137 138 139 141 142 143 144 145 148 150 151 153 155 156 157 158 159 160 161 164 165 166 167 169 170 172 174 175 176 177 181 182 183 185 186 187 189 190 191 192 194 195 196 198 200 201 203 205 206 207 208 215 216 217 218 219 221 223 225 227 229 230 231 232 233 235 236 237 238 240 241 242 244 245 246 249 250 251 253 256 257 258 260 262 263 264 268 269 270 272 273 274 276 277 278 279] VALIDATION: [ 2 4 5 6 14 15 17 21 23 25 29 31 32 35 37 47 48 50 53 54 60 64 65 68 70 73 77 80 81 82 84 89 92 94 96 102 110 111 116 118 121 122 124 125 133 134 140 146 147 149 152 154 162 163 168 171 173 178 179 180 184 188 193 197 199 202 204 209 210 211 212 213 214 220 222 224 226 228 234 239 243 247 248 252 254 255 259 261 265 266 267 271 275] Train Classes: [(0, 4), (1, 5), (2, 3), (3, 6), (4, 5), (5, 4), (6, 5), (7, 5), (8, 4), (9, 6), (10, 6), (11, 5), (12, 4), (13, 4), (14, 5), (15, 2), (16, 5), (17, 6), (18, 4), (19, 3), (20, 6), (21, 5), (22, 4), (23, 6), (24, 4), (25, 5), (26, 4), (27, 5), (28, 5), (29, 3), (30, 5), (31, 7), (32, 3), (33, 4), (34, 4), (35, 4), (36, 6), (37, 6), (38, 6), (39, 4)] Validation Classes: [(0, 3), (1, 2), (2, 2), (3, 3), (4, 2), (5, 2), (6, 2), (7, 3), (8, 3), (9, 2), (10, 3), (11, 2), (12, 3), (13, 2), (14, 3), (15, 2), (16, 2), (17, 2), (18, 3), (19, 2), (20, 3), (21, 3), (22, 2), (23, 2), (24, 2), (25, 2), (26, 2), (27, 2), (28, 2), (29, 2), (30, 3), (31, 3), (32, 1), (33, 2), (34, 2), (35, 2), (36, 3), (37, 3), (38, 2), (39, 2)] FOLD-3 TRAIN: [ 1 2 4 5 6 7 9 14 15 16 17 18 21 23 25 26 29 30 31 32 33 35 36 37 38 39 40 41 43 44 45 47 48 50 52 53 54 56 58 60 62 64 65 68 70 71 73 76 77 80 81 82 83 84 86 89 90 92 94 95 96 99 101 102 103 104 106 109 110 111 113 114 115 116 117 118 119 121 122 124 125 127 130 131 132 133 134 136 138 140 141 142 146 147 148 149 152 153 154 155 158 159 160 161 162 163 164 165 166 167 168 169 170 171 173 174 178 179 180 183 184 185 186 188 190 191 193 195 196 197 199 202 203 204 206 209 210 211 212 213 214 215 216 218 220 221 222 224 225 226 227 228 234 236 238 239 240 242 243 245 246 247 248 249 251 252 253 254 255 259 260 261 262 263 265 266 267 268 270 271 273 274 275 276 277 278 279] VALIDATION: [ 0 3 8 10 11 12 13 19 20 22 24 27 28 34 42 46 49 51 55 57 59 61 63 66 67 69 72 74 75 78 79 85 87 88 91 93 97 98 100 105 107 108 112 120 123 126 128 129 135 137 139 143 144 145 150 151 156 157 172 175 176 177 181 182 187 189 192 194 198 200 201 205 207 208 217 219 223 229 230 231 232 233 235 237 241 244 250 256 257 258 264 269 272] Train Classes: [(0, 5), (1, 5), (2, 4), (3, 6), (4, 4), (5, 4), (6, 4), (7, 5), (8, 5), (9, 5), (10, 6), (11, 5), (12, 5), (13, 4), (14, 5), (15, 3), (16, 4), (17, 5), (18, 5), (19, 3), (20, 6), (21, 6), (22, 4), (23, 5), (24, 4), (25, 5), (26, 4), (27, 5), (28, 5), (29, 3), (30, 6), (31, 7), (32, 2), (33, 4), (34, 4), (35, 4), (36, 6), (37, 6), (38, 5), (39, 4)] Validation Classes: [(0, 2), (1, 2), (2, 1), (3, 3), (4, 3), (5, 2), (6, 3), (7, 3), (8, 2), (9, 3), (10, 3), (11, 2), (12, 2), (13, 2), (14, 3), (15, 1), (16, 3), (17, 3), (18, 2), (19, 2), (20, 3), (21, 2), (22, 2), (23, 3), (24, 2), (25, 2), (26, 2), (27, 2), (28, 2), (29, 2), (30, 2), (31, 3), (32, 2), (33, 2), (34, 2), (35, 2), (36, 3), (37, 3), (38, 3), (39, 2)]
Indeed, it can be seen that there are observations for each class in each fold.
Here, a SVM model will be fit by tuning the hyper-parameters with grid search. To make the processes easier, a pipeline is created which contains the scaler and svc process, respectively. For implying SVM, it is important to scale the data. While calling pipeline.fit, it first fit features and transform it, then fit the classifier on the transformed features sequencely.
# SVM
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipe = Pipeline([('scaler', StandardScaler()), ('svc', SVC())])
# Set the parameters by cross-validation
param_grid = [
{"svc__kernel": ["rbf"], "svc__gamma": [1e-3, 1e-4], "svc__C": [1, 10, 100, 1000]},
{"svc__kernel": ["linear"], "svc__C": [1, 10, 100, 1000]},
]
## classification metric
score = "f1"
print("# Tuning hyper-parameters for %s" % score)
print()
svm_clf = GridSearchCV(pipe,
param_grid = param_grid,
scoring="%s_macro" % score,
cv=kfolds,
n_jobs = -1)
# Fit the SVM
svm_clf.fit(X_train, Y_train)
svm_dim = X_train.shape
print("Best parameters set found on development set: ",svm_clf.best_params_)
print("Best score found on development set: ",svm_clf.best_score_)
print("Grid scores on development set:")
print()
means = svm_clf.cv_results_["mean_test_score"]
stds = svm_clf.cv_results_["std_test_score"]
for mean, std, params in zip(means, stds, svm_clf.cv_results_["params"]):
print("%0.3f (+/-%0.03f) for %r" % (mean, std * 2, params))
# save the best model
best_svm_clf = svm_clf.best_estimator_
# Tuning hyper-parameters for f1
Best parameters set found on development set: {'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
Best score found on development set: 0.9308730158730159
Grid scores on development set:
0.617 (+/-0.068) for {'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.611 (+/-0.025) for {'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.695 (+/-0.097) for {'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.931 (+/-0.079) for {'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.695 (+/-0.097) for {'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.931 (+/-0.079) for {'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.695 (+/-0.097) for {'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.931 (+/-0.079) for {'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.928 (+/-0.072) for {'svc__C': 1, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'svc__C': 10, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'svc__C': 100, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'svc__C': 1000, 'svc__kernel': 'linear'}
As seen on the above results, in total 12 different models were tunned, 8 of them using rbf kernel and 4 of them using linear kernel. For both models, different C values have been tried and only for the rbf kernel different gamma values have been tried.
As a scoring criteria in cross validation, f1 score with macro average type has been used. macro has been used, because it calculates metrics for each label, and find their unweighted mean. As the data has imbalance labels, this method does not take label imbalance into account.
As a result of grid search hyper-parameter tuning, the best parameters has been obtained which are {'svcC': 10, 'svcgamma': 0.0001, 'svc__kernel': 'rbf'}, and this model's f1_macro validation score has been found 0.9308.
On the below part, the PCA is fit on X_train with .99, which indicates that the minimum number of principal components 99% of the variance is retained.
# PCA + SVM
from sklearn.decomposition import PCA
## First finding the optimal PCA
pca=PCA()
pca.fit(X_train)
plt.figure(1, figsize=(5,3))
plt.plot(pca.explained_variance_, linewidth=2)
plt.xlabel('Components')
plt.ylabel('Explained Variaces')
plt.show()
print("The optimal number of components: ",pca.n_components_)
The optimal number of components: 280
On the above output, the evaluation of the PCA components by the variances can be seen, and the optimal number of components that PCA choose after fitting the model using pca.n_components_.
Model Fitting
The pcastep must come right after the scaler step because it is necessary to normalize data before performing PCA.
In the param_grid, different number of pca components is defined to find the best one, and the parameters for the SVM model are the same as in the previous part.
pca = PCA()
pipe = Pipeline([('scaler', StandardScaler()), ("pca", pca), ('svc', SVC())])
# Set the parameters by cross-validation
param_grid = [
{"pca__n_components": range(5,100,10),"svc__kernel": ["rbf"], "svc__gamma": [1e-3, 1e-4], "svc__C": [1, 10, 100, 1000]},
{"pca__n_components": range(5,100,10),"svc__kernel": ["linear"], "svc__C": [1, 10, 100, 1000]},
]
## classification metrics
score = "f1"
pca_svm_clf = GridSearchCV(pipe,
param_grid = param_grid,
cv=kfolds,
scoring="%s_macro" % score,
n_jobs = -1)
# Fit the SVM
pca_svm_clf.fit(X_train, Y_train)
print("Best parameters set found on development set: ",pca_svm_clf.best_params_)
print("Best score found on development set: ",pca_svm_clf.best_score_)
print("Grid scores on development set:")
print()
means = pca_svm_clf.cv_results_["mean_test_score"]
stds = pca_svm_clf.cv_results_["std_test_score"]
for mean, std, params in zip(means, stds, pca_svm_clf.cv_results_["params"]):
print("%0.3f (+/-%0.03f) for %r" % (mean, std * 2, params))
# save the best model
best_pca_svm_clf = pca_svm_clf.best_estimator_
Best parameters set found on development set: {'pca__n_components': 85, 'svc__C': 100, 'svc__kernel': 'linear'}
Best score found on development set: 0.9308730158730159
Grid scores on development set:
0.494 (+/-0.110) for {'pca__n_components': 5, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.187 (+/-0.041) for {'pca__n_components': 5, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.661 (+/-0.118) for {'pca__n_components': 5, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.547 (+/-0.072) for {'pca__n_components': 5, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.671 (+/-0.143) for {'pca__n_components': 5, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.661 (+/-0.094) for {'pca__n_components': 5, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.674 (+/-0.152) for {'pca__n_components': 5, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.682 (+/-0.138) for {'pca__n_components': 5, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.811 (+/-0.044) for {'pca__n_components': 15, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.408 (+/-0.097) for {'pca__n_components': 15, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.827 (+/-0.043) for {'pca__n_components': 15, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.867 (+/-0.109) for {'pca__n_components': 15, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.827 (+/-0.043) for {'pca__n_components': 15, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.864 (+/-0.102) for {'pca__n_components': 15, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.827 (+/-0.043) for {'pca__n_components': 15, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.864 (+/-0.102) for {'pca__n_components': 15, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.856 (+/-0.032) for {'pca__n_components': 25, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.544 (+/-0.033) for {'pca__n_components': 25, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.853 (+/-0.061) for {'pca__n_components': 25, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.903 (+/-0.063) for {'pca__n_components': 25, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.853 (+/-0.061) for {'pca__n_components': 25, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.905 (+/-0.063) for {'pca__n_components': 25, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.853 (+/-0.061) for {'pca__n_components': 25, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.901 (+/-0.070) for {'pca__n_components': 25, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.838 (+/-0.066) for {'pca__n_components': 35, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.588 (+/-0.044) for {'pca__n_components': 35, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.843 (+/-0.062) for {'pca__n_components': 35, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.912 (+/-0.088) for {'pca__n_components': 35, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.843 (+/-0.063) for {'pca__n_components': 35, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.909 (+/-0.080) for {'pca__n_components': 35, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.847 (+/-0.065) for {'pca__n_components': 35, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.912 (+/-0.088) for {'pca__n_components': 35, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.835 (+/-0.064) for {'pca__n_components': 45, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.611 (+/-0.039) for {'pca__n_components': 45, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.838 (+/-0.062) for {'pca__n_components': 45, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.916 (+/-0.097) for {'pca__n_components': 45, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.843 (+/-0.068) for {'pca__n_components': 45, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.916 (+/-0.097) for {'pca__n_components': 45, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.840 (+/-0.064) for {'pca__n_components': 45, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.916 (+/-0.097) for {'pca__n_components': 45, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.832 (+/-0.060) for {'pca__n_components': 55, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.627 (+/-0.057) for {'pca__n_components': 55, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.838 (+/-0.056) for {'pca__n_components': 55, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.924 (+/-0.079) for {'pca__n_components': 55, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.838 (+/-0.056) for {'pca__n_components': 55, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.924 (+/-0.079) for {'pca__n_components': 55, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.838 (+/-0.056) for {'pca__n_components': 55, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.924 (+/-0.079) for {'pca__n_components': 55, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.814 (+/-0.054) for {'pca__n_components': 65, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.626 (+/-0.061) for {'pca__n_components': 65, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.839 (+/-0.070) for {'pca__n_components': 65, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.077) for {'pca__n_components': 65, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.839 (+/-0.070) for {'pca__n_components': 65, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.924 (+/-0.087) for {'pca__n_components': 65, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.839 (+/-0.070) for {'pca__n_components': 65, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.077) for {'pca__n_components': 65, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.802 (+/-0.040) for {'pca__n_components': 75, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.626 (+/-0.061) for {'pca__n_components': 75, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.832 (+/-0.060) for {'pca__n_components': 75, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 75, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.832 (+/-0.060) for {'pca__n_components': 75, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 75, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.836 (+/-0.066) for {'pca__n_components': 75, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 75, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.793 (+/-0.045) for {'pca__n_components': 85, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.635 (+/-0.052) for {'pca__n_components': 85, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.832 (+/-0.060) for {'pca__n_components': 85, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 85, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.830 (+/-0.058) for {'pca__n_components': 85, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 85, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.830 (+/-0.058) for {'pca__n_components': 85, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 85, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.784 (+/-0.059) for {'pca__n_components': 95, 'svc__C': 1, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.629 (+/-0.055) for {'pca__n_components': 95, 'svc__C': 1, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.827 (+/-0.055) for {'pca__n_components': 95, 'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 95, 'svc__C': 10, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.828 (+/-0.056) for {'pca__n_components': 95, 'svc__C': 100, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 95, 'svc__C': 100, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.827 (+/-0.055) for {'pca__n_components': 95, 'svc__C': 1000, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
0.928 (+/-0.088) for {'pca__n_components': 95, 'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}
0.668 (+/-0.073) for {'pca__n_components': 5, 'svc__C': 1, 'svc__kernel': 'linear'}
0.659 (+/-0.047) for {'pca__n_components': 5, 'svc__C': 10, 'svc__kernel': 'linear'}
0.659 (+/-0.047) for {'pca__n_components': 5, 'svc__C': 100, 'svc__kernel': 'linear'}
0.659 (+/-0.046) for {'pca__n_components': 5, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.880 (+/-0.072) for {'pca__n_components': 15, 'svc__C': 1, 'svc__kernel': 'linear'}
0.880 (+/-0.072) for {'pca__n_components': 15, 'svc__C': 10, 'svc__kernel': 'linear'}
0.884 (+/-0.083) for {'pca__n_components': 15, 'svc__C': 100, 'svc__kernel': 'linear'}
0.880 (+/-0.072) for {'pca__n_components': 15, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.903 (+/-0.063) for {'pca__n_components': 25, 'svc__C': 1, 'svc__kernel': 'linear'}
0.903 (+/-0.063) for {'pca__n_components': 25, 'svc__C': 10, 'svc__kernel': 'linear'}
0.907 (+/-0.054) for {'pca__n_components': 25, 'svc__C': 100, 'svc__kernel': 'linear'}
0.903 (+/-0.063) for {'pca__n_components': 25, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 35, 'svc__C': 1, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 35, 'svc__C': 10, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 35, 'svc__C': 100, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 35, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 45, 'svc__C': 1, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 45, 'svc__C': 10, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 45, 'svc__C': 100, 'svc__kernel': 'linear'}
0.916 (+/-0.071) for {'pca__n_components': 45, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.920 (+/-0.062) for {'pca__n_components': 55, 'svc__C': 1, 'svc__kernel': 'linear'}
0.920 (+/-0.062) for {'pca__n_components': 55, 'svc__C': 10, 'svc__kernel': 'linear'}
0.920 (+/-0.062) for {'pca__n_components': 55, 'svc__C': 100, 'svc__kernel': 'linear'}
0.920 (+/-0.062) for {'pca__n_components': 55, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 65, 'svc__C': 1, 'svc__kernel': 'linear'}
0.927 (+/-0.072) for {'pca__n_components': 65, 'svc__C': 10, 'svc__kernel': 'linear'}
0.920 (+/-0.062) for {'pca__n_components': 65, 'svc__C': 100, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 65, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.927 (+/-0.072) for {'pca__n_components': 75, 'svc__C': 1, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 75, 'svc__C': 10, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 75, 'svc__C': 100, 'svc__kernel': 'linear'}
0.927 (+/-0.072) for {'pca__n_components': 75, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 85, 'svc__C': 1, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 85, 'svc__C': 10, 'svc__kernel': 'linear'}
0.931 (+/-0.079) for {'pca__n_components': 85, 'svc__C': 100, 'svc__kernel': 'linear'}
0.927 (+/-0.072) for {'pca__n_components': 85, 'svc__C': 1000, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 95, 'svc__C': 1, 'svc__kernel': 'linear'}
0.928 (+/-0.072) for {'pca__n_components': 95, 'svc__C': 10, 'svc__kernel': 'linear'}
0.931 (+/-0.079) for {'pca__n_components': 95, 'svc__C': 100, 'svc__kernel': 'linear'}
0.931 (+/-0.079) for {'pca__n_components': 95, 'svc__C': 1000, 'svc__kernel': 'linear'}
As a result of grid search hyper-parameter tuning, the best parameters are found: {'pcan_components': 85, 'svcC': 100, 'svc__kernel': 'linear'}, and this model's f1_macro validation score has been found 0.9308. The optimal number of components has been found 85.
# Fit the LDA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# the model without tuning the parameters
print('The LDA model without tuning the parameters: ',LinearDiscriminantAnalysis())
## classification metrics
score = "f1"
print("# Results for %s" % score)
print()
lda_scores = cross_val_score(LinearDiscriminantAnalysis(), X_train, Y_train, scoring="%s_macro" % score, cv=kfolds, n_jobs=-1)
print("Scores on development set:")
print()
means = lda_scores.mean()
stds = lda_scores.std()
print("Scores: {}".format(lda_scores))
print("Mean of Scores: {}".format(means))
print("Standard Deviation of Scores: {}".format(stds))
# Fit the LDA
lda = LinearDiscriminantAnalysis()
# Using LDA as a classifier and fitting the model
lda_clf = lda.fit(X_train,Y_train)
The LDA model without tuning the parameters: LinearDiscriminantAnalysis() # Results for f1 Scores on development set: Scores: [0.95583333 0.88761905 0.95833333] Mean of Scores: 0.9339285714285714 Standard Deviation of Scores: 0.0327616798165035
As a result of the cross validation, this model's f1_macro validation score has been found 0.9339. According to the scores, the 1st and the 3rd folds had the same validation score and the least score has been found in the 2nd fold.
In this part, similar to the PCA+SVM implementation, a new step is added in the pipeline as kpca between scaler and svc steps.
In the param_grid, different number of kpca components will be tried to find the best one, and tried the same parameters for the SVM model as in the previous part.
from sklearn.decomposition import KernelPCA
kpca = KernelPCA()
pipe = Pipeline([('scaler', StandardScaler()), ("kpca", kpca), ('svc', SVC())])
pipe.get_params().keys()
dict_keys(['memory', 'steps', 'verbose', 'scaler', 'kpca', 'svc', 'scaler__copy', 'scaler__with_mean', 'scaler__with_std', 'kpca__alpha', 'kpca__coef0', 'kpca__copy_X', 'kpca__degree', 'kpca__eigen_solver', 'kpca__fit_inverse_transform', 'kpca__gamma', 'kpca__iterated_power', 'kpca__kernel', 'kpca__kernel_params', 'kpca__max_iter', 'kpca__n_components', 'kpca__n_jobs', 'kpca__random_state', 'kpca__remove_zero_eig', 'kpca__tol', 'svc__C', 'svc__break_ties', 'svc__cache_size', 'svc__class_weight', 'svc__coef0', 'svc__decision_function_shape', 'svc__degree', 'svc__gamma', 'svc__kernel', 'svc__max_iter', 'svc__probability', 'svc__random_state', 'svc__shrinking', 'svc__tol', 'svc__verbose'])
############
# Set the parameters by cross-validation
param_grid = [
{"kpca__n_components": range(5,100,10),"kpca__kernel": ["rbf"], "kpca__gamma": [1e-3, 1e-4], "svc__C": [1, 10, 100, 1000]},
{"kpca__n_components": range(5,100,10),"kpca__kernel": ["linear"], "svc__C": [1, 10, 100, 1000]},
]
## classification metrics
score = "f1"
kpca_svm_clf = GridSearchCV(pipe,
param_grid = param_grid,
cv=kfolds,
scoring="%s_macro" % score,
n_jobs = -1)
# Fit the KPCA+SVM
kpca_svm_clf.fit(X_train, Y_train)
print("Best parameters set found on development set: ",kpca_svm_clf.best_params_)
print("Best score found on development set: ",kpca_svm_clf.best_score_)
print("Grid scores on development set:")
print()
means = kpca_svm_clf.cv_results_["mean_test_score"]
stds = kpca_svm_clf.cv_results_["std_test_score"]
for mean, std, params in zip(means, stds, kpca_svm_clf.cv_results_["params"]):
print("%0.3f (+/-%0.03f) for %r" % (mean, std * 2, params))
# save the best model
best_kpca_svm_clf = kpca_svm_clf.best_estimator_
# the reduced dimension
best_kpca_svm_components = best_kpca_svm_clf.named_steps["kpca"].n_components
Best parameters set found on development set: {'kpca__kernel': 'linear', 'kpca__n_components': 75, 'svc__C': 10}
Best score found on development set: 0.915595238095238
Grid scores on development set:
0.095 (+/-0.045) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 1}
0.215 (+/-0.122) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 10}
0.279 (+/-0.170) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 100}
0.258 (+/-0.143) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 1000}
0.186 (+/-0.070) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 1}
0.332 (+/-0.199) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 10}
0.423 (+/-0.175) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 100}
0.449 (+/-0.217) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 1000}
0.226 (+/-0.110) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 1}
0.395 (+/-0.147) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 10}
0.445 (+/-0.143) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 100}
0.451 (+/-0.130) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 1000}
0.259 (+/-0.121) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 1}
0.439 (+/-0.096) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 10}
0.505 (+/-0.101) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 100}
0.503 (+/-0.082) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 1000}
0.292 (+/-0.077) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 1}
0.455 (+/-0.152) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 10}
0.460 (+/-0.168) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 100}
0.456 (+/-0.165) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 1000}
0.345 (+/-0.070) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 1}
0.444 (+/-0.110) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 10}
0.447 (+/-0.110) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 100}
0.447 (+/-0.110) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 1000}
0.444 (+/-0.070) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 1}
0.462 (+/-0.121) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 10}
0.456 (+/-0.135) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 100}
0.456 (+/-0.135) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 1000}
0.483 (+/-0.072) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 1}
0.503 (+/-0.085) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 10}
0.503 (+/-0.085) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 100}
0.503 (+/-0.085) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 1000}
0.475 (+/-0.021) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 1}
0.536 (+/-0.024) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 10}
0.536 (+/-0.024) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 100}
0.536 (+/-0.024) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 1000}
0.507 (+/-0.079) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 1}
0.568 (+/-0.017) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 10}
0.568 (+/-0.017) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 100}
0.568 (+/-0.017) for {'kpca__gamma': 0.001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 1000}
0.358 (+/-0.062) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 1}
0.585 (+/-0.162) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 10}
0.614 (+/-0.123) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 100}
0.605 (+/-0.118) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 5, 'svc__C': 1000}
0.719 (+/-0.035) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 1}
0.839 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 10}
0.843 (+/-0.067) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 100}
0.843 (+/-0.067) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 15, 'svc__C': 1000}
0.788 (+/-0.033) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 1}
0.853 (+/-0.075) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 10}
0.853 (+/-0.075) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 100}
0.853 (+/-0.075) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 25, 'svc__C': 1000}
0.820 (+/-0.018) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 1}
0.870 (+/-0.086) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 10}
0.870 (+/-0.086) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 100}
0.870 (+/-0.086) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 35, 'svc__C': 1000}
0.843 (+/-0.044) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 1}
0.885 (+/-0.077) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 10}
0.885 (+/-0.077) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 100}
0.885 (+/-0.077) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 45, 'svc__C': 1000}
0.859 (+/-0.026) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 1}
0.892 (+/-0.057) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 10}
0.892 (+/-0.057) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 100}
0.892 (+/-0.057) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 55, 'svc__C': 1000}
0.846 (+/-0.038) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 1}
0.874 (+/-0.049) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 10}
0.874 (+/-0.049) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 100}
0.874 (+/-0.049) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 65, 'svc__C': 1000}
0.855 (+/-0.047) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 1}
0.878 (+/-0.059) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 10}
0.878 (+/-0.059) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 100}
0.878 (+/-0.059) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 75, 'svc__C': 1000}
0.850 (+/-0.062) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 1}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 10}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 100}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 85, 'svc__C': 1000}
0.851 (+/-0.064) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 1}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 10}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 100}
0.877 (+/-0.072) for {'kpca__gamma': 0.0001, 'kpca__kernel': 'rbf', 'kpca__n_components': 95, 'svc__C': 1000}
0.409 (+/-0.049) for {'kpca__kernel': 'linear', 'kpca__n_components': 5, 'svc__C': 1}
0.627 (+/-0.060) for {'kpca__kernel': 'linear', 'kpca__n_components': 5, 'svc__C': 10}
0.662 (+/-0.139) for {'kpca__kernel': 'linear', 'kpca__n_components': 5, 'svc__C': 100}
0.662 (+/-0.139) for {'kpca__kernel': 'linear', 'kpca__n_components': 5, 'svc__C': 1000}
0.738 (+/-0.037) for {'kpca__kernel': 'linear', 'kpca__n_components': 15, 'svc__C': 1}
0.861 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 15, 'svc__C': 10}
0.861 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 15, 'svc__C': 100}
0.861 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 15, 'svc__C': 1000}
0.819 (+/-0.024) for {'kpca__kernel': 'linear', 'kpca__n_components': 25, 'svc__C': 1}
0.879 (+/-0.055) for {'kpca__kernel': 'linear', 'kpca__n_components': 25, 'svc__C': 10}
0.879 (+/-0.055) for {'kpca__kernel': 'linear', 'kpca__n_components': 25, 'svc__C': 100}
0.879 (+/-0.055) for {'kpca__kernel': 'linear', 'kpca__n_components': 25, 'svc__C': 1000}
0.850 (+/-0.028) for {'kpca__kernel': 'linear', 'kpca__n_components': 35, 'svc__C': 1}
0.900 (+/-0.058) for {'kpca__kernel': 'linear', 'kpca__n_components': 35, 'svc__C': 10}
0.900 (+/-0.058) for {'kpca__kernel': 'linear', 'kpca__n_components': 35, 'svc__C': 100}
0.900 (+/-0.058) for {'kpca__kernel': 'linear', 'kpca__n_components': 35, 'svc__C': 1000}
0.851 (+/-0.004) for {'kpca__kernel': 'linear', 'kpca__n_components': 45, 'svc__C': 1}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 45, 'svc__C': 10}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 45, 'svc__C': 100}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 45, 'svc__C': 1000}
0.858 (+/-0.010) for {'kpca__kernel': 'linear', 'kpca__n_components': 55, 'svc__C': 1}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 55, 'svc__C': 10}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 55, 'svc__C': 100}
0.908 (+/-0.070) for {'kpca__kernel': 'linear', 'kpca__n_components': 55, 'svc__C': 1000}
0.866 (+/-0.030) for {'kpca__kernel': 'linear', 'kpca__n_components': 65, 'svc__C': 1}
0.911 (+/-0.078) for {'kpca__kernel': 'linear', 'kpca__n_components': 65, 'svc__C': 10}
0.911 (+/-0.078) for {'kpca__kernel': 'linear', 'kpca__n_components': 65, 'svc__C': 100}
0.911 (+/-0.078) for {'kpca__kernel': 'linear', 'kpca__n_components': 65, 'svc__C': 1000}
0.872 (+/-0.048) for {'kpca__kernel': 'linear', 'kpca__n_components': 75, 'svc__C': 1}
0.916 (+/-0.076) for {'kpca__kernel': 'linear', 'kpca__n_components': 75, 'svc__C': 10}
0.916 (+/-0.076) for {'kpca__kernel': 'linear', 'kpca__n_components': 75, 'svc__C': 100}
0.916 (+/-0.076) for {'kpca__kernel': 'linear', 'kpca__n_components': 75, 'svc__C': 1000}
0.872 (+/-0.048) for {'kpca__kernel': 'linear', 'kpca__n_components': 85, 'svc__C': 1}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 85, 'svc__C': 10}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 85, 'svc__C': 100}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 85, 'svc__C': 1000}
0.873 (+/-0.050) for {'kpca__kernel': 'linear', 'kpca__n_components': 95, 'svc__C': 1}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 95, 'svc__C': 10}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 95, 'svc__C': 100}
0.912 (+/-0.071) for {'kpca__kernel': 'linear', 'kpca__n_components': 95, 'svc__C': 1000}
As a result of grid search hyper-parameter tuning, the best parameters are {'kpcakernel': 'linear', 'kpcan_components': 75, 'svc__C': 10}, and this model's f1_macro validation score has been found 0.9155. The optimal number of components has been found 75 for kernal pca.
In this part, as indicated in the train detectors part, the predefined RBFKernelFisherDiscriminant will be used to do the Kernal discrimant analysis.
# Creating the pipeline
pipe = Pipeline([('scaler', StandardScaler()),('kda', RBFKernelFisherDiscriminant()) ])
pipe.get_params().keys()
dict_keys(['memory', 'steps', 'verbose', 'scaler', 'kda', 'scaler__copy', 'scaler__with_mean', 'scaler__with_std', 'kda__alpha', 'kda__gamma'])
The custom kernel has a parameter gamma to tune, therefore,the model will be fit with different gamma parameters for the kernel in order to obtain the best estimator.
# Set the parameter 'gamma' of our custom kernel by
param_grid = dict([
('kda__gamma',10.0**np.arange(-10,1))
])
## classification metrics
score = "f1"
# Do grid search to get the best parameter value of 'gamma'.
kda_clf = GridSearchCV(pipe,
param_grid,
cv=kfolds,
scoring="%s_macro" % score,
n_jobs=-1)
# Fit the KDA
kda_clf.fit(X_train , Y_train)
print("Best parameters set found on development set: ",kda_clf.best_params_)
print("Best score found on development set: ",kda_clf.best_score_)
print("Grid scores on development set:")
print()
means = kda_clf.cv_results_["mean_test_score"]
stds = kda_clf.cv_results_["std_test_score"]
for mean, std, params in zip(means, stds, kda_clf.cv_results_["params"]):
print("%0.3f (+/-%0.03f) for %r" % (mean, std * 2, params))
# save the best model
best_kda_clf = kda_clf.best_estimator_
Best parameters set found on development set: {'kda__gamma': 1e-06}
Best score found on development set: 0.9483531746031746
Grid scores on development set:
0.004 (+/-0.007) for {'kda__gamma': 1e-10}
0.377 (+/-0.052) for {'kda__gamma': 1e-09}
0.496 (+/-0.052) for {'kda__gamma': 1e-08}
0.913 (+/-0.029) for {'kda__gamma': 1e-07}
0.948 (+/-0.038) for {'kda__gamma': 1e-06}
0.941 (+/-0.069) for {'kda__gamma': 1e-05}
0.931 (+/-0.043) for {'kda__gamma': 0.0001}
0.688 (+/-0.050) for {'kda__gamma': 0.001}
0.007 (+/-0.016) for {'kda__gamma': 0.01}
0.002 (+/-0.000) for {'kda__gamma': 0.1}
0.002 (+/-0.000) for {'kda__gamma': 1.0}
As a result of grid search hyper-parameter tuning,the best parameter 1e-06 for the gamma of the custom kernel. Moreover, this model's f1_macro validation score has been found 0.9483, which is the highest found validation score among the tuned models.
In this part, the winner estimators after grid search will be assessed in the test set to look for the accuracy of the chosen models.
In the below chunk, all the winning models will be iterated and collected the accuracy score. As a last step, all the scores will be written into a list in order to create a results data frame later.
# model lists to predict
models = [best_svm_clf, best_pca_svm_clf, lda_clf, best_kpca_svm_clf, best_kda_clf]
# empty list to append the classification accuracy
results= []
for model in models:
print(f'Detailed classification report for {model}')
print()
print('The model is trained on the full development set.')
print('The scores are computed on the full evaluation set.')
print()
y_true, y_pred = Y_test, model.predict(X_test)
print(classification_report(y_true, y_pred, zero_division=1))
print()
clf_accuracy = round(model.score(X_test, Y_test),2) ## calculating the score from the pipeline
results.append(clf_accuracy)
Detailed classification report for Pipeline(steps=[('scaler', StandardScaler()), ('svc', SVC(C=10, gamma=0.0001))])
The model is trained on the full development set.
The scores are computed on the full evaluation set.
precision recall f1-score support
0 0.60 1.00 0.75 3
1 1.00 1.00 1.00 3
2 1.00 0.60 0.75 5
3 1.00 1.00 1.00 1
4 1.00 0.67 0.80 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 0.67 1.00 0.80 2
8 1.00 1.00 1.00 3
9 1.00 0.50 0.67 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 1.00 1.00 1.00 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.67 0.80 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 0.50 0.67 2
24 1.00 1.00 1.00 4
25 0.75 1.00 0.86 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 0.83 1.00 0.91 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 0.67 1.00 0.80 4
accuracy 0.93 120
macro avg 0.95 0.93 0.93 120
weighted avg 0.95 0.93 0.93 120
Detailed classification report for Pipeline(steps=[('scaler', StandardScaler()), ('pca', PCA(n_components=85)),
('svc', SVC(C=100, kernel='linear'))])
The model is trained on the full development set.
The scores are computed on the full evaluation set.
precision recall f1-score support
0 0.60 1.00 0.75 3
1 1.00 1.00 1.00 3
2 1.00 1.00 1.00 5
3 1.00 1.00 1.00 1
4 1.00 0.67 0.80 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 0.67 1.00 0.80 2
8 1.00 1.00 1.00 3
9 1.00 0.50 0.67 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 0.75 1.00 0.86 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.67 0.80 6
16 1.00 0.67 0.80 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 0.50 0.67 2
24 1.00 1.00 1.00 4
25 1.00 1.00 1.00 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 0.83 1.00 0.91 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 0.80 1.00 0.89 4
accuracy 0.94 120
macro avg 0.96 0.94 0.93 120
weighted avg 0.96 0.94 0.94 120
Detailed classification report for LinearDiscriminantAnalysis()
The model is trained on the full development set.
The scores are computed on the full evaluation set.
precision recall f1-score support
0 0.50 0.67 0.57 3
1 1.00 1.00 1.00 3
2 1.00 1.00 1.00 5
3 1.00 1.00 1.00 1
4 1.00 1.00 1.00 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 1.00 1.00 1.00 2
8 1.00 1.00 1.00 3
9 1.00 1.00 1.00 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 0.75 1.00 0.86 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.67 0.80 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 1.00 1.00 2
24 1.00 1.00 1.00 4
25 1.00 1.00 1.00 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 1.00 1.00 1.00 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 1.00 1.00 1.00 4
accuracy 0.97 120
macro avg 0.97 0.97 0.97 120
weighted avg 0.98 0.97 0.97 120
Detailed classification report for Pipeline(steps=[('scaler', StandardScaler()),
('kpca', KernelPCA(n_components=75)), ('svc', SVC(C=10))])
The model is trained on the full development set.
The scores are computed on the full evaluation set.
precision recall f1-score support
0 0.60 1.00 0.75 3
1 1.00 1.00 1.00 3
2 1.00 0.60 0.75 5
3 1.00 0.00 0.00 1
4 1.00 0.67 0.80 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 0.67 1.00 0.80 2
8 1.00 1.00 1.00 3
9 1.00 0.50 0.67 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 0.75 1.00 0.86 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.67 0.80 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 0.50 0.67 2
24 1.00 1.00 1.00 4
25 0.75 1.00 0.86 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 0.83 1.00 0.91 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 0.67 1.00 0.80 4
accuracy 0.93 120
macro avg 0.95 0.91 0.90 120
weighted avg 0.95 0.93 0.92 120
Detailed classification report for Pipeline(steps=[('scaler', StandardScaler()),
('kda', RBFKernelFisherDiscriminant(gamma=1e-06))])
The model is trained on the full development set.
The scores are computed on the full evaluation set.
precision recall f1-score support
0 0.60 1.00 0.75 3
1 1.00 1.00 1.00 3
2 1.00 1.00 1.00 5
3 1.00 1.00 1.00 1
4 1.00 1.00 1.00 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 0.67 1.00 0.80 2
8 1.00 1.00 1.00 3
9 1.00 0.50 0.67 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 1.00 1.00 1.00 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.67 0.80 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 1.00 1.00 2
24 1.00 1.00 1.00 4
25 1.00 1.00 1.00 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 1.00 1.00 1.00 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 1.00 1.00 1.00 4
accuracy 0.97 120
macro avg 0.97 0.97 0.96 120
weighted avg 0.98 0.97 0.97 120
Creating the result data frame with the test classification accuracies
models_name = ["SVM", "PCA+SVM","LDA","KPCA+SVM","KDA"]
results_df = pd.DataFrame({'Model Name':models_name, 'Classification score':results}).set_index('Model Name')
# print results_df
results_df
| Classification score | |
|---|---|
| Model Name | |
| SVM | 0.93 |
| PCA+SVM | 0.94 |
| LDA | 0.97 |
| KPCA+SVM | 0.92 |
| KDA | 0.97 |
The accuracy of both models, LDA and KDA is the same which is 97%, the highest classification accuracy result among all the studied models. This fact points out that there is a similarity between these models. This similarity is not suprising because both KDA and LDA are reducing the dimension subspace in order to enhance the separability of the classes, and in both methods, a linear classification is done.
After these models, PCA+SVM, SVM and KPCA+SVM performed better, respectively. In general, the common thing in these three models is that SVM has been used as a classifier. However, the hyper-parameters are varying in each model's best estimator. For instance, PCA+SVM and KPCA+SVM models' best estimators use a linear kernel SVM, and SVM uses a rbf kernel. All in all, there is a slight difference in the performance of these 3 models. This is due to the iml¡plementation of dimension reduction techniques. After comparing the accuracies, The model that PCA used performed sligh better.
In this part,the reduced dimension will be obtained while classifying the test set. As in a pipeline, first steps are for transforming data and the last step is classification, the dimension reduction step used in the pipeline will be gotten by using the function named_steps. For example, for PCA+SVM model, PCA step is the step where dimension reduction takes place.
As in grid search part, for PCA and KernelPCA, the optimal number of components has been chosen, then the model is fit and transformed the test set with these chosen components. For the LDA model, the estimator has a transform function so the reduced dimension could be gotten by using this function. Also, for the custom defined kernel(kda) class, there is a function to transform the data. Thefore, this function will be called to see the reduced dimension space while classifying the images.
#best svm model reduction
svm_dim = X_test.shape
print(f'The reduced dimension space for SVM: {svm_dim}')
#best pca+svm model reduction
best_pca_svm_components = best_pca_svm_clf.named_steps['pca'].n_components_
pca=PCA(n_components=best_pca_svm_components, whiten=True) # chosen components
pca_svm_dim = pca.fit_transform(X_test).shape
print(f'The reduced dimension space for PCA+SVM: {pca_svm_dim}')
#lda model reduction
lda_clf_dim = lda_clf.transform(X_test).shape
print(f'The reduced dimension space for LDA: {lda_clf_dim}')
#best kpca+svm model reduction
best_kpca_svm_components = best_kpca_svm_clf.named_steps["kpca"].n_components
kpca=KernelPCA(n_components=best_kpca_svm_components) # chosen components
kpca_svm_dim = kpca.fit_transform(X_test).shape
print(f'The reduced dimension space for KPCA+SVM: {kpca_svm_dim}')
#best kda model reduction
kda_clf_dim = best_kda_clf.transform(X_test).shape
print(f'The reduced dimension space for KDA: {kda_clf_dim}')
The reduced dimension space for SVM: (120, 4096) The reduced dimension space for PCA+SVM: (120, 85) The reduced dimension space for LDA: (120, 39) The reduced dimension space for KPCA+SVM: (120, 75) The reduced dimension space for KDA: (120, 39)
According to the results obtained above, as there are 4096 features in the data, no feature reduction occured in SVM. And for the other models, the reduced dimensions could be seen.
Putting the reduced dimension space information into a column in results_df.
dimensions = [svm_dim, pca_svm_dim, lda_clf_dim, kpca_svm_dim, kda_clf_dim]
results_df['Reduced Dimension Space'] = dimensions
results_df
| Classification score | Reduced Dimension Space | |
|---|---|---|
| Model Name | ||
| SVM | 0.93 | (120, 4096) |
| PCA+SVM | 0.94 | (120, 85) |
| LDA | 0.97 | (120, 39) |
| KPCA+SVM | 0.92 | (120, 75) |
| KDA | 0.97 | (120, 39) |
On the above table, the reduced dimension spaces in each sheme can be seen. It is apparent that both LDA and KDA reduced the dimension from 4096 to 39. KPCA and PCA reduced to 75 and 85, respectively. As in SVM model no reduction method implied while fitting, the dimension is the same as the original size.
best_pca_svm_clf
Pipeline(steps=[('scaler', StandardScaler()), ('pca', PCA(n_components=85)),
('svc', SVC(C=100, kernel='linear'))])
The evolution of the cross validation as the number of principal components increases
In the part below, the PCA spectrum can be seen while doing cross validation.
# Plot the PCA spectrum
pca.fit(X_train)
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True, figsize=(6, 6))
ax0.plot(
np.arange(1, pca.n_components_ + 1), pca.explained_variance_ratio_, "+", linewidth=2
)
ax0.set_ylabel("PCA explained variance ratio")
ax0.axvline(best_pca_svm_components,linestyle=":",label="n_components chosen")
ax0.legend(prop=dict(size=9))
# For each number of components,the best classifier results
results = pd.DataFrame(pca_svm_clf.cv_results_)
components_col = "param_pca__n_components"
best_clfs = results.groupby(components_col).apply(
lambda g: g.nlargest(1, "mean_test_score")
)
best_clfs.plot(
x=components_col, y="mean_test_score", yerr="std_test_score", legend=False, ax=ax1
)
ax1.set_ylabel("Classification accuracy (val)")
ax1.set_xlabel("n_components")
plt.xlim(-1, 70)
plt.tight_layout()
plt.show()
In the first visual, the evaluation of the variance ratio while the number of components increases can be seen while train fitting with PCA, and in the second visual, the classification accuracy can be seen in the validation set while the number of components increases. But these accuracies are the best classifier result for each number of components.
The evolution of the classification test error as the number of principal components increases
In the part below, the classification test errors for each tried component can be seen.
pca_components = range(5,100,10)
clf_errors = []
from sklearn.metrics import accuracy_score
for comp in pca_components:
model = Pipeline(steps=[('scaler', StandardScaler()), ('pca',PCA(n_components=comp)),('svc', SVC(C=100, kernel='linear'))])
model.fit(X_train,Y_train)
clf_error = 1- model.score(X_test, Y_test)
clf_errors.append(clf_error)
## writing the test errors for each component into a data frame
errors_df = pd.DataFrame({'Number of components':pca_components, 'Classification test error':clf_errors})
print(errors_df)
# show the plot. Label axis
plt.rcParams["figure.figsize"] = (5,4)
errors_df.plot(x ='Number of components', y='Classification test error', kind = 'line')
Number of components Classification test error 0 5 0.366667 1 15 0.083333 2 25 0.058333 3 35 0.050000 4 45 0.050000 5 55 0.050000 6 65 0.058333 7 75 0.058333 8 85 0.058333 9 95 0.058333
<matplotlib.axes._subplots.AxesSubplot at 0x7fa57faf8b50>
Before during the hyper-parameter tunning the pca optimal components was 85. On the above visual, it is clear that the classification test error has the least value with 35, 45 and 55 components. With all these number of components, the same test error is achieved. It is apparent that the chosen number of component (85) does not have the least test classification error.
best_kpca_svm_clf
Pipeline(steps=[('scaler', StandardScaler()),
('kpca', KernelPCA(n_components=75)), ('svc', SVC(C=10))])
The evolution of the cross validation as the number of components increases
In the part below, we can see the Kernel PCA spectrum while doing cross validation.
kpca.fit(X_train)
# For each number of components,the best classifier results
results = pd.DataFrame(kpca_svm_clf.cv_results_)
components_col = "param_kpca__n_components"
best_clfs = results.groupby(components_col).apply(
lambda g: g.nlargest(1, "mean_test_score")
)
best_clfs.plot(
x=components_col, y="mean_test_score", yerr="std_test_score", legend=False
)
plt.xlim(-1, 70)
plt.tight_layout()
plt.show()
The evolution of the classification test error as the number of components increases
In the part below, we are getting the classification test errors for each tried component.
pca_components = range(5,100,10)
clf_errors = []
for comp in pca_components:
model = Pipeline(steps=[('scaler', StandardScaler()),
('kpca', KernelPCA(n_components=comp, kernel = 'linear')), ('svc', SVC(C=10))])
model.fit(X_train,Y_train)
model.predict(X_test)
clf_error = 1- model.score(X_test, Y_test)
clf_errors.append(clf_error)
## writing the test errors for each component into a data frame
errors_df = pd.DataFrame({'Number of components':pca_components, 'Classification test error':clf_errors})
print(errors_df)
# show the plot. Label axis
plt.rcParams["figure.figsize"] = (5,4)
errors_df.plot(x ='Number of components', y='Classification test error', kind = 'line')
Number of components Classification test error 0 5 0.308333 1 15 0.091667 2 25 0.075000 3 35 0.066667 4 45 0.075000 5 55 0.083333 6 65 0.075000 7 75 0.075000 8 85 0.075000 9 95 0.075000
<matplotlib.axes._subplots.AxesSubplot at 0x7fa585b90a90>
Before during the hyper-parameter tunning, 75 was found as a kpca optimal nuber of components. On the above visual, it is clear that there is a drop in the component 35, which means the classification test error has the least value. Therefore the chosen optimal component for kernel pca does not have the least error rate.
After evaluating the models by just changing the number of components in both pca and kpca, it is clear that the model with pca components 35, 45 and 55 the least test error is achieved, even though in cross validation the chosen number of components was 85. Therefore, in this case, these components with the least error rate could be used while training the classifiers. Moreover, for the model with kpca, instead of 75 components, 35 components would be enough to train.
for s_noise in [.01, .1, .5]:
noisy_test = X_test + s_noise * np.random.randn(X_test.shape[0], X_test.shape[1])
plot_gallery(noisy_test[:10,:], None, h, w, n_row=1)
Evaluating the accuracy of the already trained detectors with noisy set
# model lists to predict
models = [best_svm_clf, best_pca_svm_clf, lda_clf, best_kpca_svm_clf, best_kda_clf]
# empty list to append the classification accuracy
results_noise= []
for model in models:
model.predict(noisy_test)
clf_accuracy_noise = round(model.score(noisy_test, Y_test),2) ## calculating the score from the pipeline
results_noise.append(clf_accuracy_noise)
# print results_df
results_df['Classification accuracy with noise'] = results_noise
results_df
| Classification score | Reduced Dimension Space | Classification accuracy with noise | |
|---|---|---|---|
| Model Name | |||
| SVM | 0.93 | (120, 4096) | 0.02 |
| PCA+SVM | 0.94 | (120, 85) | 0.93 |
| LDA | 0.97 | (120, 39) | 0.55 |
| KPCA+SVM | 0.92 | (120, 75) | 0.92 |
| KDA | 0.97 | (120, 39) | 0.92 |
After adding noise to the test set, it is apparent that PCA+SVM, KPCA+SVM and KDA models are more robust with respect to the other models, although the test classification accuracy has decreased a bit for PCA+SVM and KDA compared to the test accuracies on the test set without noise. However, the test accuracy has not changed for the model with KPCA+SVM. On the other hand, the less robust model is SVM, because the accuracy on the noisy test set has sharply decreased from 93% to 2%. Moreover, LDA has not either performed very well on noisy data, as its accuracy has dropped from 97% to 55%.
The KDA and the LDA provide with a reduced subspace that aims at maximizing the separability of the classes, and a linear classification is performed in such space. In this part, these reduced subspaces will be combained with a SVM as classifier. (using the KDA and LDA to transform the data and then learning a SVM with the transformed data)
# Creating the pipeline
pipe = Pipeline([('scaler', StandardScaler()),('lda', LinearDiscriminantAnalysis()) ,('svc', SVC())])
pipe.get_params().keys()
dict_keys(['memory', 'steps', 'verbose', 'scaler', 'lda', 'svc', 'scaler__copy', 'scaler__with_mean', 'scaler__with_std', 'lda__covariance_estimator', 'lda__n_components', 'lda__priors', 'lda__shrinkage', 'lda__solver', 'lda__store_covariance', 'lda__tol', 'svc__C', 'svc__break_ties', 'svc__cache_size', 'svc__class_weight', 'svc__coef0', 'svc__decision_function_shape', 'svc__degree', 'svc__gamma', 'svc__kernel', 'svc__max_iter', 'svc__probability', 'svc__random_state', 'svc__shrinking', 'svc__tol', 'svc__verbose'])
# Set the parameter 'gamma' of our custom kernel by
param_grid = [
{"svc__kernel": ["rbf"], "svc__gamma": [1e-3, 1e-4], "svc__C": [1, 10, 100, 1000]},
{"svc__kernel": ["linear"], "svc__C": [1, 10, 100, 1000]},
]
## classification metrics
score = "f1"
# Do grid search to get the best parameter value of 'gamma'.
lda_svm_clf = GridSearchCV(pipe,
param_grid,
cv=kfolds,
scoring="%s_macro" % score,
n_jobs=-1)
# Fit the KDA
lda_svm_clf.fit(X_train , Y_train)
print("Best parameters set found on development set: ",lda_svm_clf.best_params_)
print("Best score found on development set: ",lda_svm_clf.best_score_)
print()
## Evaluation in the test set
print('The score is computed on the full test set.')
print()
print(lda_svm_clf.score(X_test, Y_test))
Best parameters set found on development set: {'svc__C': 10, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}
Best score found on development set: 0.9465476190476192
The score is computed on the full test set.
0.9668997668997668
y_true, y_pred = Y_test, lda_svm_clf.predict(X_test)
print(classification_report(y_true, y_pred, zero_division=1))
precision recall f1-score support
0 0.67 0.67 0.67 3
1 1.00 1.00 1.00 3
2 1.00 1.00 1.00 5
3 1.00 1.00 1.00 1
4 1.00 1.00 1.00 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 1.00 1.00 1.00 2
8 1.00 1.00 1.00 3
9 1.00 1.00 1.00 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 1.00 1.00 1.00 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.83 0.91 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 1.00 1.00 2
24 1.00 1.00 1.00 4
25 1.00 1.00 1.00 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 1.00 1.00 1.00 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 0.50 1.00 0.67 1
38 0.67 1.00 0.80 2
39 1.00 1.00 1.00 4
accuracy 0.97 120
macro avg 0.97 0.97 0.97 120
weighted avg 0.98 0.97 0.98 120
KDA+SVM
# Creating the pipeline
pipe = Pipeline([('scaler', StandardScaler()),('kda', RBFKernelFisherDiscriminant()) ,('svc', SVC())])
pipe.get_params().keys()
dict_keys(['memory', 'steps', 'verbose', 'scaler', 'kda', 'svc', 'scaler__copy', 'scaler__with_mean', 'scaler__with_std', 'kda__alpha', 'kda__gamma', 'svc__C', 'svc__break_ties', 'svc__cache_size', 'svc__class_weight', 'svc__coef0', 'svc__decision_function_shape', 'svc__degree', 'svc__gamma', 'svc__kernel', 'svc__max_iter', 'svc__probability', 'svc__random_state', 'svc__shrinking', 'svc__tol', 'svc__verbose'])
Model Fitting
# Set the parameter 'gamma' of our custom kernel by
param_grid = dict([
('kda__gamma',10.0**np.arange(-10,1)),
('svc__C', [1, 10, 100, 1000])
])
## classification metrics
score = "f1"
# Do grid search to get the best parameter value of 'gamma'.
kda_svm_clf = GridSearchCV(pipe,
param_grid,
cv=kfolds,
scoring="%s_macro" % score,
n_jobs=-1)
# Fit the KDA
kda_svm_clf.fit(X_train , Y_train)
print("Best parameters set found on development set: ",kda_svm_clf.best_params_)
print("Best score found on development set: ",kda_svm_clf.best_score_)
print()
## Evaluation in the test set
print('The score is computed on the full test set.')
print()
y_true, y_pred = Y_test, kda_svm_clf.predict(X_test)
print(kda_svm_clf.score(X_test, Y_test))
Best parameters set found on development set: {'kda__gamma': 1e-06, 'svc__C': 10}
Best score found on development set: 0.955
The score is computed on the full test set.
0.9666555666555666
y_true, y_pred = Y_test, kda_svm_clf.predict(X_test)
print(classification_report(y_true, y_pred, zero_division=1))
precision recall f1-score support
0 0.75 1.00 0.86 3
1 1.00 1.00 1.00 3
2 1.00 1.00 1.00 5
3 1.00 1.00 1.00 1
4 1.00 1.00 1.00 3
5 1.00 1.00 1.00 4
6 1.00 1.00 1.00 3
7 0.67 1.00 0.80 2
8 1.00 1.00 1.00 3
9 1.00 0.50 0.67 2
10 1.00 1.00 1.00 1
11 1.00 1.00 1.00 3
12 1.00 1.00 1.00 3
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 2
15 1.00 0.83 0.91 6
16 1.00 1.00 1.00 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 3
19 1.00 1.00 1.00 5
20 1.00 1.00 1.00 1
21 1.00 0.50 0.67 2
22 1.00 1.00 1.00 4
23 1.00 1.00 1.00 2
24 1.00 1.00 1.00 4
25 1.00 1.00 1.00 3
26 1.00 1.00 1.00 4
27 1.00 1.00 1.00 3
28 1.00 1.00 1.00 3
29 1.00 1.00 1.00 5
30 1.00 1.00 1.00 2
32 1.00 1.00 1.00 6
33 1.00 1.00 1.00 4
34 1.00 1.00 1.00 4
35 1.00 1.00 1.00 4
36 1.00 1.00 1.00 1
37 1.00 1.00 1.00 1
38 0.67 1.00 0.80 2
39 1.00 1.00 1.00 4
accuracy 0.97 120
macro avg 0.98 0.97 0.97 120
weighted avg 0.98 0.97 0.97 120
In both methods when adding a SVM classifier, the validation score of the model with best hyperparameters has improved with respect to the previous scores achieved by these methods. On the other hand, the accuracy on the test sets has not experienced such significant improvement. However, the detailed classification reports shows that the classification scores has improved for some classes. For instance, for both LDA+SVM and KDA+SVM models, the accuracy for classes 0 and 15 has improved. Also for LDA with SVM, the weighted average classification accuracy has improved from 0.97 to 0.98.