We have seen that we can fit an SVM with a non-linear kernel in order to perform classification using a non-linear decision boundary. We will now see that we can also obtain a non-linear decision boundary by performing logistic regression using non-linear transformations of the features.
Generate a data set with n = 500 and p = 2, such that the observations belong to two classes with a quadratic decision boundary between them.
set.seed(12345)
x1 <- runif(500) - 0.5
x2 <- runif(500) - 0.5
y <- 1 * (x1^2 - x2^2 > 0)
Plot the observations, colored according to their class labels. Your plot should display X1 on the x-axis, and X2 on the y- axis.
plot(x1,x2,col=ifelse(y,'red','blue'))
Fit a logistic regression model to the data, using X1 and X2 as predictors.
logModel = glm(y ~ . , family='binomial', data=data.frame(x1,x2,y))
logModel
##
## Call: glm(formula = y ~ ., family = "binomial", data = data.frame(x1,
## x2, y))
##
## Coefficients:
## (Intercept) x1 x2
## 0.07563 0.46494 0.41377
##
## Degrees of Freedom: 499 Total (i.e. Null); 497 Residual
## Null Deviance: 692.2
## Residual Deviance: 688 AIC: 694
Apply this model to the training data in order to obtain a predicted class label for each training observation. Plot the observations, colored according to the predicted class labels. The decision boundary should be linear.
logModel.pred = predict(logModel, data.frame(x1,x2))
plot(x1, x2, col=ifelse(logModel.pred > 0, 'red', 'blue'), pch = ifelse(as.integer(logModel.pred > 0) == y, 1, 3))
Now fit a logistic regression model to the data using non-linear functions of X1 and X2 as predictors (e.g. X12, X1 ×X2, log(X2), and so forth).
logModel = glm(y ~ poly(x1, 2) + poly(x2, 2) , family='binomial', data=data.frame(x1, x2, y))
## Warning: glm.fit: algorithm did not converge
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
Apply this model to the training data in order to obtain a predicted class label for each training observation. Plot the observations, colored according to the predicted class labels. The decision boundary should be obviously non-linear. If it is not, then repeat (a)-(e) until you come up with an example in which the predicted class labels are obviously non-linear.
logModel.pred = predict(logModel, data.frame(x1,x2))
plot(x1, x2, col=ifelse(logModel.pred > 0, 'red', 'blue'), pch = ifelse(as.integer(logModel.pred > 0) == y, 1, 3))
Fit a support vector classifier to the data with X1 and X2 as predictors. Obtain a class prediction for each training observation. Plot the observations, colored according to the predicted class labels.
svmModel = svm(y ~ ., data = data.frame(x1, x2, y=as.factor(y)), kernel='linear')
svmModel.pred = predict(svmModel, data.frame(x1,x2), type='response')
plot(x1, x2, col = ifelse(svmModel.pred != 0, 'red', 'blue'), pch = ifelse(svmModel.pred == y, 1, 3))
Fit a SVM using a non-linear kernel to the data. Obtain a class prediction for each training observation. Plot the observations, colored according to the predicted class labels.
svmModel = svm(y ~ ., data = data.frame(x1, x2, y=as.factor(y)), kernel='radial')
svmModel.pred = predict(svmModel, data.frame(x1,x2), type='response')
plot(x1, x2, col = ifelse(svmModel.pred != 0, 'red', 'blue'), pch = ifelse(svmModel.pred == y, 1, 3))
Comment on your results.
Looking at our results, we can see that logistic regression with interactions and SVM’s with non-linear kernels can be equally as powerful when attempting to identify non-linear decision boundaries. Like expected, when attempting to use SVM’s with linear kernels and logistic regression without interactions they do not perform as well when attempting to identify the same non-linear decisions boundaries. From that, we can conclude that SVM’s can be useful in both linear and non-linear situations, however further tuning is necessary to achieve the best performance.
In this problem, you will use support vector approaches in order to predict whether a given car gets high or low gas mileage based on the Auto data set.
Create a binary variable that takes on a 1 for cars with gas mileage above the median, and a 0 for cars with gas mileage below the median.
Auto$gas_med <- ifelse(Auto$mpg > median(Auto$mpg), 1, 0)
Fit a support vector classifier to the data with various values of cost, in order to predict whether a car gets high or low gas mileage. Report the cross-validation errors associated with different values of this parameter. Comment on your results. Note you will need to fit the classifier without the gas mileage variable to produce sensible results.
svmTuned0 = tune(svm, gas_med ~ . - mpg, data = Auto, ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 100)), kernel = 'linear')
svmTuned0$performances
## cost error dispersion
## 1 1e-03 0.10845689 0.02413298
## 2 1e-02 0.10376012 0.03443415
## 3 1e-01 0.10020081 0.03789915
## 4 1e+00 0.09412209 0.03266744
## 5 5e+00 0.09934967 0.03193312
## 6 1e+01 0.10536253 0.03197532
## 7 1e+02 0.11756741 0.03342222
plot(svmTuned0$performance[, c(1, 2)], type = 'l')
We can see that in the beginning, as cost increased, the error decreased significantly. However after a certain point, the error stops decreasing and begins to increase, this valley in the graph with the lowest error indicates the best cost value. In our case, a cost of 1 performs the best.
Now repeat (b), this time using SVMs with radial and polynomial basis kernels, with different values of gamma and degree and cost. Comment on your results.
svmTuned1 = tune(svm, gas_med ~ . - mpg, data = Auto, ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 100), degree = 2:10), kernel = 'polynomial')
svmTuned1$performances
## cost degree error dispersion
## 1 1e-03 2 0.4984479 0.04871973
## 2 1e-02 2 0.4982038 0.04870642
## 3 1e-01 2 0.4957401 0.04855367
## 4 1e+00 2 0.4715967 0.04846382
## 5 5e+00 2 0.3822803 0.05960291
## 6 1e+01 2 0.3115269 0.06261205
## 7 1e+02 2 0.2527266 0.03343528
## 8 1e-03 3 0.4984684 0.04872129
## 9 1e-02 3 0.4984089 0.04872194
## 10 1e-01 3 0.4978145 0.04872887
## 11 1e+00 3 0.4918093 0.04882159
## 12 5e+00 3 0.4659938 0.05016607
## 13 1e+01 3 0.4356513 0.05324926
## 14 1e+02 3 0.2457496 0.03933104
## 15 1e-03 4 0.4984749 0.04872121
## 16 1e-02 4 0.4984735 0.04872115
## 17 1e-01 4 0.4984600 0.04872057
## 18 1e+00 4 0.4983246 0.04871479
## 19 5e+00 4 0.4977238 0.04869147
## 20 1e+01 4 0.4969557 0.04866300
## 21 1e+02 4 0.4834570 0.04914540
## 22 1e-03 5 0.4984750 0.04872122
## 23 1e-02 5 0.4984749 0.04872123
## 24 1e-01 5 0.4984734 0.04872127
## 25 1e+00 5 0.4984583 0.04872177
## 26 5e+00 5 0.4983912 0.04872400
## 27 1e+01 5 0.4983075 0.04872682
## 28 1e+02 5 0.4967803 0.04878020
## 29 1e-03 6 0.4984750 0.04872122
## 30 1e-02 6 0.4984750 0.04872122
## 31 1e-01 6 0.4984750 0.04872122
## 32 1e+00 6 0.4984743 0.04872120
## 33 5e+00 6 0.4984715 0.04872114
## 34 1e+01 6 0.4984680 0.04872105
## 35 1e+02 6 0.4984046 0.04871960
## 36 1e-03 7 0.4984750 0.04872122
## 37 1e-02 7 0.4984750 0.04872122
## 38 1e-01 7 0.4984750 0.04872122
## 39 1e+00 7 0.4984750 0.04872122
## 40 5e+00 7 0.4984747 0.04872123
## 41 1e+01 7 0.4984744 0.04872124
## 42 1e+02 7 0.4984688 0.04872142
## 43 1e-03 8 0.4984750 0.04872122
## 44 1e-02 8 0.4984750 0.04872122
## 45 1e-01 8 0.4984750 0.04872122
## 46 1e+00 8 0.4984750 0.04872122
## 47 5e+00 8 0.4984750 0.04872122
## 48 1e+01 8 0.4984750 0.04872122
## 49 1e+02 8 0.4984747 0.04872121
## 50 1e-03 9 0.4984750 0.04872122
## 51 1e-02 9 0.4984750 0.04872122
## 52 1e-01 9 0.4984750 0.04872122
## 53 1e+00 9 0.4984750 0.04872122
## 54 5e+00 9 0.4984750 0.04872122
## 55 1e+01 9 0.4984750 0.04872122
## 56 1e+02 9 0.4984750 0.04872122
## 57 1e-03 10 0.4984750 0.04872122
## 58 1e-02 10 0.4984750 0.04872122
## 59 1e-01 10 0.4984750 0.04872122
## 60 1e+00 10 0.4984750 0.04872122
## 61 5e+00 10 0.4984750 0.04872122
## 62 1e+01 10 0.4984750 0.04872122
## 63 1e+02 10 0.4984750 0.04872122
The best results for the model with a polynomial kernel occurred when using a cost of 100 and a degree of 3, this model yielded an error of 0.2439237
svmTuned2 = tune(svm, gas_med ~ . - mpg, data = Auto, ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 100), gamma = c(0.001, 0.01, 0.1, 1, 10, 100, 1000)), kernel = 'radial')
svmTuned2$performances
## cost gamma error dispersion
## 1 1e-03 1e-03 0.49987970 0.033043741
## 2 1e-02 1e-03 0.47450477 0.030401893
## 3 1e-01 1e-03 0.26742465 0.024866900
## 4 1e+00 1e-03 0.10374379 0.026033188
## 5 5e+00 1e-03 0.10082409 0.031233736
## 6 1e+01 1e-03 0.09914611 0.032816234
## 7 1e+02 1e-03 0.08857806 0.034985013
## 8 1e-03 1e-02 0.47999750 0.031033907
## 9 1e-02 1e-02 0.30529702 0.023258644
## 10 1e-01 1e-02 0.09907724 0.025818379
## 11 1e+00 1e-02 0.08646731 0.030627393
## 12 5e+00 1e-02 0.08264774 0.034260036
## 13 1e+01 1e-02 0.08168629 0.033894211
## 14 1e+02 1e-02 0.07324683 0.026329553
## 15 1e-03 1e-01 0.45425520 0.029338571
## 16 1e-02 1e-01 0.15296418 0.023143738
## 17 1e-01 1e-01 0.07217802 0.029517882
## 18 1e+00 1e-01 0.07065579 0.032907684
## 19 5e+00 1e-01 0.06139390 0.025255693
## 20 1e+01 1e-01 0.06329588 0.024254859
## 21 1e+02 1e-01 0.08234786 0.029041124
## 22 1e-03 1e+00 0.50010684 0.033371894
## 23 1e-02 1e+00 0.47661249 0.033418131
## 24 1e-01 1e+00 0.28231852 0.031571397
## 25 1e+00 1e+00 0.09706563 0.013597495
## 26 5e+00 1e+00 0.10035315 0.014152582
## 27 1e+01 1e+00 0.10046556 0.014389877
## 28 1e+02 1e+00 0.10046399 0.014391592
## 29 1e-03 1e+01 0.50238280 0.033413848
## 30 1e-02 1e+01 0.49754685 0.033494420
## 31 1e-01 1e+01 0.45170575 0.033576071
## 32 1e+00 1e+01 0.24266165 0.003994669
## 33 5e+00 1e+01 0.24265728 0.003991108
## 34 1e+01 1e+01 0.24265728 0.003991108
## 35 1e+02 1e+01 0.24265728 0.003991108
## 36 1e-03 1e+02 0.50248854 0.033333373
## 37 1e-02 1e+02 0.49801525 0.033004631
## 38 1e-01 1e+02 0.45580106 0.030467064
## 39 1e+00 1e+02 0.25091922 0.001020318
## 40 5e+00 1e+02 0.25091922 0.001020318
## 41 1e+01 1e+02 0.25091922 0.001020318
## 42 1e+02 1e+02 0.25091922 0.001020318
## 43 1e-03 1e+03 0.50248859 0.033333315
## 44 1e-02 1e+03 0.49801545 0.033004493
## 45 1e-01 1e+03 0.45580182 0.030466880
## 46 1e+00 1e+03 0.25092340 0.001019278
## 47 5e+00 1e+03 0.25092340 0.001019278
## 48 1e+01 1e+03 0.25092340 0.001019278
## 49 1e+02 1e+03 0.25092340 0.001019278
The best results for the radial model occurred when using a cost of 5 and a gamma of 0.1, this model yielded an error of 0.06322822.
Make some plots to back up your assertions in (b) and (c).
# Function to plot performance metrics
plot_performance <- function(tuned_model, parameter_name) {
# Extract performance metrics
performances <- tuned_model$performances
# Plot performance metrics
plot(performances[[parameter_name]], performances$error, type = "b",
xlab = parameter_name, ylab = "Cross-validation error",
main = paste("Cross-validation error vs", parameter_name))
}
plot_performance(svmTuned0, "cost")
plot_performance(svmTuned1, "degree")
plot_performance(svmTuned2, "gamma")
I tried using the following function “plot(svmTuned0, Auto, weight ~ horsepower)” to try and create classifications plots, however for some reason my R studio does nothing after running the R chunk. No errors occur when running the chunk so I am having trouble debugging the issue. In light of this issue, I decided to at least attempt to graph the cross validation error against cost (Linear Model - svmTuned0), degree (Radial Model - svmTuned1), and gamma (Polynomial Model - svmTuned2).
This problem involves the OJ data set which is part of the ISLR2 package.
Create a training set containing a random sample of 800 observations, and a test set containing the remaining observations.
set.seed(12345)
train <- sample(nrow(OJ), 800)
OJ_train <- OJ[train, ]
OJ_test <- OJ[-train, ]
Fit a support vector classifier to the training data using cost = 0.01, with Purchase as the response and the other variables as predictors. Use the summary() function to produce summary statistics, and describe the results obtained.
svm_Linear <- svm(Purchase ~ ., data = OJ_train, kernel = "linear", cost = 0.01)
summary(svm_Linear)
##
## Call:
## svm(formula = Purchase ~ ., data = OJ_train, kernel = "linear", cost = 0.01)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: linear
## cost: 0.01
##
## Number of Support Vectors: 440
##
## ( 220 220 )
##
##
## Number of Classes: 2
##
## Levels:
## CH MM
SVM-Type:
SVM-Kernel:
Cost:
Number of support vectors:
Number of Classes:
Level:
What are the training and test error rates?
predTrain <- predict(svm_Linear, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 439 61
## MM 77 223
predTest <- predict(svm_Linear, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 144 9
## MM 35 82
train_Error <- (77 + 61) / (439 + 223 + 77 + 61)
test_Error <- (35 + 9) / (144 + 82 + 35 + 9)
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.1725"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.162962962962963"
Use the tune() function to select an optimal cost. Consider values in the range 0.01 to 10.
set.seed(12345)
SVMTuned <- tune(svm, Purchase ~ ., data = OJ_train, kernel = "linear", ranges = list(cost = 10^seq(-2, 1, by = 0.25)))
summary(SVMTuned)
##
## Parameter tuning of 'svm':
##
## - sampling method: 10-fold cross validation
##
## - best parameters:
## cost
## 1.778279
##
## - best performance: 0.17125
##
## - Detailed performance results:
## cost error dispersion
## 1 0.01000000 0.18000 0.04174992
## 2 0.01778279 0.18125 0.04177070
## 3 0.03162278 0.18125 0.04093101
## 4 0.05623413 0.18000 0.04297932
## 5 0.10000000 0.17875 0.03866254
## 6 0.17782794 0.17625 0.03884174
## 7 0.31622777 0.17500 0.03584302
## 8 0.56234133 0.17375 0.03458584
## 9 1.00000000 0.17250 0.03622844
## 10 1.77827941 0.17125 0.03634805
## 11 3.16227766 0.17125 0.03120831
## 12 5.62341325 0.17500 0.03584302
## 13 10.00000000 0.17500 0.03726780
Compute the training and test error rates using this new value for cost.
SVM_cost <- svm(Purchase ~ ., kernel = "linear", data = OJ_train, cost = SVMTuned$best.parameter$cost)
predTrain <- predict(SVM_cost, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 438 62
## MM 72 228
predTest <- predict(SVM_cost, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 143 10
## MM 33 84
train_Error <- (72 + 62) / (438 + 228 + 72 + 62)
test_Error <- (33 + 10) / (143 + 84 + 33 + 10)
print("- Using Best Cost - ")
## [1] "- Using Best Cost - "
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.1675"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.159259259259259"
Repeat parts (b) through (e) using a support vector machine with a radial kernel. Use the default value for gamma.
SVM_Radial <- svm(Purchase ~ ., kernel = "radial", data = OJ_train)
summary(SVM_Radial)
##
## Call:
## svm(formula = Purchase ~ ., data = OJ_train, kernel = "radial")
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: radial
## cost: 1
##
## Number of Support Vectors: 375
##
## ( 188 187 )
##
##
## Number of Classes: 2
##
## Levels:
## CH MM
predTrain <- predict(SVM_Radial, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 457 43
## MM 79 221
predTest <- predict(SVM_Radial, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 145 8
## MM 37 80
train_Error <- (79 + 43) / (457 + 221 + 79 + 43)
test_Error <- (37 + 8) / (145 + 80 + 37 + 8)
print("- Using Radial - ")
## [1] "- Using Radial - "
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.1525"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.166666666666667"
set.seed(12345)
Radial_tuned <- tune(svm, Purchase ~ ., data = OJ_train, kernel = "radial", ranges = list(cost = 10^seq(-2,
1, by = 0.25)))
SVM_cost <- svm(Purchase ~ ., kernel = "radial", data = OJ_train, cost = Radial_tuned$best.parameter$cost)
predTrain <- predict(SVM_cost, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 457 43
## MM 79 221
predTest <- predict(SVM_cost, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 145 8
## MM 37 80
train_Error <- (79 + 43) / (457 + 221 + 79 + 43)
test_Error <- (37 + 8) / (145 + 80 + 37 + 8)
print("- Using Radial and Best Cost - ")
## [1] "- Using Radial and Best Cost - "
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.1525"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.166666666666667"
Repeat parts (b) through (e) using a support vector machine with a polynomial kernel. Set degree = 2.
SVM_Poly <- svm(Purchase ~ ., kernel = "polynomial", data = OJ_train, degree = 2)
summary(SVM_Poly)
##
## Call:
## svm(formula = Purchase ~ ., data = OJ_train, kernel = "polynomial",
## degree = 2)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: polynomial
## cost: 1
## degree: 2
## coef.0: 0
##
## Number of Support Vectors: 455
##
## ( 233 222 )
##
##
## Number of Classes: 2
##
## Levels:
## CH MM
predTrain <- predict(SVM_Poly, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 462 38
## MM 108 192
predTest <- predict(SVM_Poly, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 148 5
## MM 47 70
train_Error <- (108 + 38) / (462 + 192 + 108 + 38)
test_Error <- (47 + 5) / (148 + 70 + 47 + 5)
print("- Using Polynomial - ")
## [1] "- Using Polynomial - "
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.1825"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.192592592592593"
set.seed(12345)
Poly_tuned <- tune(svm, Purchase ~ ., data = OJ_train, kernel = "polynomial", degree = 2, ranges = list(cost = 10^seq(-2,
1, by = 0.25)))
SVM_cost <- svm(Purchase ~ ., kernel = "polynomial", data = OJ_train, cost = Poly_tuned$best.parameter$cost)
predTrain <- predict(SVM_cost, OJ_train)
table(OJ_train$Purchase, predTrain)
## predTrain
## CH MM
## CH 462 38
## MM 79 221
predTest <- predict(SVM_cost, OJ_test)
table(OJ_test$Purchase, predTest)
## predTest
## CH MM
## CH 146 7
## MM 44 73
train_Error <- (79 + 38) / (462 + 221 + 79 + 38)
test_Error <- (44 + 7) / (146 + 73 + 44 + 7)
print("- Using Polynomial and Best Cost - ")
## [1] "- Using Polynomial and Best Cost - "
paste("Train Error Rate: ", train_Error)
## [1] "Train Error Rate: 0.14625"
paste("Test Error Rate: ", test_Error)
## [1] "Test Error Rate: 0.188888888888889"
Overall, which approach seems to give the best results on this data?
The SVM model with a linear kernel using a cost of 1.778279 seems to perform the best on the data as it has the smallest test error rate. While this model does have a better test error rate, the SVM model with a polynomial kernel and a cost of 10 had a train error rate of 0.14625 which could possibly suggest that further tuning could yield better test results.