import numpy as np
import matplotlib.pyplot as plt5 Python 9.8
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import sklearn.model_selection as skm
from sklearn import svm
from sklearn.metrics import f1_score, make_scorer
import ISLP
from ISLP.svm import plot as plot_svm
from sklearn.model_selection import GridSearchCV
import pandas as pd5.1 9.8
5.1.1 (a) Loading and preparing the data
data = ISLP.load_data("OJ")
print(data.shape)
print(data.columns)
print(data.dtypes)(1070, 18)
Index(['Purchase', 'WeekofPurchase', 'StoreID', 'PriceCH', 'PriceMM', 'DiscCH',
'DiscMM', 'SpecialCH', 'SpecialMM', 'LoyalCH', 'SalePriceMM',
'SalePriceCH', 'PriceDiff', 'Store7', 'PctDiscMM', 'PctDiscCH',
'ListPriceDiff', 'STORE'],
dtype='object')
Purchase object
WeekofPurchase int64
StoreID int64
PriceCH float64
PriceMM float64
DiscCH float64
DiscMM float64
SpecialCH int64
SpecialMM int64
LoyalCH float64
SalePriceMM float64
SalePriceCH float64
PriceDiff float64
Store7 object
PctDiscMM float64
PctDiscCH float64
ListPriceDiff float64
STORE int64
dtype: object
As you can see, there are categorial variables in the data set, so I will convert them to dummies
X = data.drop(columns="Purchase")
X = pd.get_dummies(X)
y = data["Purchase"]Train/Test separation as instructed in the assignment
X_test = X.iloc[:800]
y_test = y.iloc[:800]
X_train = X.iloc[800:]
y_train = y.iloc[800:]In the follwing \(f_1\) metrics will be used to score the performance
my_f1_scorer = make_scorer(f1_score, pos_label="CH")All the results will be saved in a data frame, here is a short function to do it
dfResults = pd.DataFrame()
def storeResults(model, comment=""):
global dfResults
df_ = pd.DataFrame({
"C":model.get_params()["C"],
"kernel":model.get_params()["kernel"],
"test_score": f1_score(y_test, model.predict(X_test), pos_label = "CH"),
"train_score": f1_score(y_train, model.predict(X_train), pos_label = "CH"),
"n_support": model.support_vectors_.shape[0],
"comment":comment
}, index = [0])
dfResults = pd.concat([dfResults, df_]).drop_duplicates()
return df_5.1.2 (b), (c) Linear Kernel witj C = 0.01
The kernel to use was not specified in the assignment, to I will use linear
kernel_ = "linear"
model = svm.SVC(C=0.01, kernel=kernel_)
model.fit(X_train, y_train)
storeResults(model)
print("C = ", model.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))C = 0.01 # of support vectors: 205
F1 score: Train: 0.700, Test: 0.614
Accuracies are reasonable
5.1.3 (d)
For some reason my system hangs when I am using CV with C>4, so only values up to 3 will be tested
grid_params = {
'C': [0.01, 0.1, 1, 1.5, 2, 2.5, 3]
}
model = svm.SVC(kernel=kernel_)
grid = GridSearchCV(model, grid_params, cv = 5, scoring=my_f1_scorer)
grid.fit(X, y)
print(grid.best_params_){'C': 1}
As you can see, C=1 was selected to give optimal score
5.1.4 (e)
model = svm.SVC(C=grid.best_params_["C"], kernel=kernel_)
model.fit(X_train, y_train)
storeResults(model, comment="bestGrid")
print("C = ", model.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))C = 1 # of support vectors: 142
F1 score: Train: 0.840, Test: 0.873
This time score is even better
5.1.5 (f) Radial Kernel
kernel_ = "rbf"
model = svm.SVC(C=0.01, kernel=kernel_)
modelR = model
model.fit(X_train, y_train)
storeResults(model)
print("C = ", model.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))C = 0.01 # of support vectors: 228
F1 score: Train: 0.732, Test: 0.766
With RBF kernel there are no problems with high \(C\), we will check some values up to 10
grid_params = {
'C': [0.01, 0.1, 1, 1.5, 2, 2.5, 3, 4, 8, 10]
}
model = svm.SVC(kernel=kernel_)
grid = GridSearchCV(model, grid_params, cv = 5, scoring=my_f1_scorer)
grid.fit(X, y)
print(grid.best_params_){'C': 0.01}
The optimal parameter is on the edge
model = svm.SVC(C=grid.best_params_["C"], kernel=kernel_)
model.fit(X_train, y_train)
storeResults(model, comment="bestGrid")
print("C = ", model.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))C = 0.01 # of support vectors: 228
F1 score: Train: 0.732, Test: 0.766
For this model optimal accuracy is better then first linear, but smaller then optimal linear
5.1.6 (g)
Let us consider polynomial with degree=2
kernel_ = "poly"
model = svm.SVC(C=0.01, kernel=kernel_, degree=2)
modelP = model
modelP.fit(X_train, y_train)
storeResults(modelP)
print("C = ", modelP.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))
del modelC = 0.01 # of support vectors: 228
F1 score: Train: 0.732, Test: 0.766
Surprisingly, results are exactly the same as in the case of RBF kernel
Again no problems with high C
grid_params = {
'C': [0.01, 0.1, 1, 1.5, 2, 2.5, 3, 4, 8, 10]
}
model = svm.SVC(kernel=kernel_, degree= 2)
grid = GridSearchCV(model, grid_params, cv = 5, scoring=my_f1_scorer)
grid.fit(X, y)
print(grid.best_params_){'C': 0.01}
As you can see, C=0.01 was selected to give optimal score
model = svm.SVC(C=grid.best_params_["C"], kernel=kernel_, degree = 2)
model.fit(X_train, y_train)
storeResults(model, comment="bestGrid")
print("C = ", model.get_params()["C"], "# of support vectors: ", model.support_vectors_.shape[0])
print(" F1 score: Train: {:.3f}, Test: {:.3f}".format(
f1_score(y_train, model.predict(X_train), pos_label="CH"),
f1_score(y_test, model.predict(X_test), pos_label="CH")
))C = 0.01 # of support vectors: 228
F1 score: Train: 0.732, Test: 0.766
5.1.7 (h)
You can see that in this case SVM with linear kernel and C=1 gives the best result
dfResults.sort_values("test_score", ascending=False)| C | kernel | test_score | train_score | n_support | comment | |
|---|---|---|---|---|---|---|
| 0 | 1.00 | linear | 0.873494 | 0.840125 | 142 | bestGrid |
| 0 | 0.01 | rbf | 0.766384 | 0.732394 | 228 | |
| 0 | 0.01 | rbf | 0.766384 | 0.732394 | 228 | bestGrid |
| 0 | 0.01 | poly | 0.766384 | 0.732394 | 228 | |
| 0 | 0.01 | poly | 0.766384 | 0.732394 | 228 | bestGrid |
| 0 | 0.01 | linear | 0.614057 | 0.699647 | 205 |
It is interesting to note that both RBF and Poly kernels give exactly the same result.
It is easy to check that their presictions are the same both on train and test subsets
(
np.all(modelR.predict(X_train) == modelP.predict(X_train)),
np.all(modelR.predict(X_test) == modelP.predict(X_test))
)(True, True)
The actual models, however, are different. For example, they are using different sets of support vectors
np.all(
np.sort(modelR.support_) == np.sort(modelP.support_)
)False