9.13 Lab: Support Vector Classifier

library("tidymodels")
library("kernlab") # We'll use the plot method from this.
set.seed(1)
sim_data <- matrix(
  rnorm (20 * 2), 
  ncol = 2,
  dimnames = list(NULL, c("x1", "x2"))
) %>% 
  as_tibble() %>% 
  mutate(
    y = factor(c(rep(-1, 10), rep(1, 10)))
  ) %>%
  mutate(
    x1 = ifelse(y == 1, x1 + 1, x1),
    x2 = ifelse(y == 1, x2 + 1, x2)
  )

sim_data %>% 
  ggplot() +
  aes(x1, x2, color = y) +
  geom_point() +
  labs(title = "Trying to make a hyperplane classifier",
       subtitle = "simulated data",
       caption = "R4DS book club") +
  theme_minimal()

# generated this using their process then saved it to use here.
test_data <- readRDS("data/09-testdat.rds") %>% 
  rename(x1 = x.1, x2 = x.2)

test_data %>% 
  ggplot() +
  aes(x1, x2, color = y) +
  geom_point() +
  labs(title = "Trying to make a hyperplane classifier",
       subtitle = "simulated data",
       caption = "R4DS book club") +
  theme_minimal()

We create a spec for a model, which we’ll update throughout this lab with different costs.

svm_linear_spec <- svm_poly(degree = 1) %>%
  set_mode("classification") %>%
  set_engine("kernlab", scaled = FALSE)

Then we do a couple fits with manual cost.

svm_linear_fit_10 <- svm_linear_spec %>% 
  set_args(cost = 10) %>%
  fit(y ~ ., data = sim_data)
svm_linear_fit_10
## parsnip model object
## 
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 10 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  1  scale =  1  offset =  1 
## 
## Number of Support Vectors : 7 
## 
## Objective Function Value : -52.4483 
## Training error : 0.15 
## Probability model included.
svm_linear_fit_10 %>%
  extract_fit_engine() %>%
  plot()

svm_linear_fit_01 <- svm_linear_spec %>% 
  set_args(cost = 0.1) %>%
  fit(y ~ ., data = sim_data)
svm_linear_fit_01
## parsnip model object
## 
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 0.1 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  1  scale =  1  offset =  1 
## 
## Number of Support Vectors : 16 
## 
## Objective Function Value : -1.189 
## Training error : 0.05 
## Probability model included.
svm_linear_fit_01 %>%
  extract_fit_engine() %>%
  plot()

svm_linear_fit_001 <- svm_linear_spec %>% 
  set_args(cost = 0.01) %>%
  fit(y ~ ., data = sim_data)
svm_linear_fit_001
## parsnip model object
## 
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 0.01 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  1  scale =  1  offset =  1 
## 
## Number of Support Vectors : 20 
## 
## Objective Function Value : -0.1859 
## Training error : 0.25 
## Probability model included.
svm_linear_fit_001 %>%
  extract_fit_engine() %>%
  plot()

9.13.1 Tuning

Let’s find the best cost.

svm_linear_wf <- workflow() %>%
  add_model(
    svm_linear_spec %>% set_args(cost = tune())
  ) %>%
  add_formula(y ~ .)

set.seed(1234)
sim_data_fold <- vfold_cv(sim_data, strata = y)

param_grid <- grid_regular(cost(), levels = 10)

# Our grid isn't identical to the book, but it's close enough.
param_grid
## # A tibble: 10 × 1
##         cost
##        <dbl>
##  1  0.000977
##  2  0.00310 
##  3  0.00984 
##  4  0.0312  
##  5  0.0992  
##  6  0.315   
##  7  1       
##  8  3.17    
##  9 10.1     
## 10 32
tune_res <- tune_grid(
  svm_linear_wf, 
  resamples = sim_data_fold, 
  grid = param_grid
)

# We ran this locally and then saved it so everyone doesn't need to wait for
# this to process each time they build the book.

# saveRDS(tune_res, "data/09-tune_res.rds")
autoplot(tune_res)

Tune can pull out the best result for us.

best_cost <- select_best(tune_res, metric = "accuracy")

svm_linear_final <- finalize_workflow(svm_linear_wf, best_cost)

svm_linear_fit <- svm_linear_final %>% fit(sim_data)

svm_linear_fit %>% 
  augment(new_data = test_data) %>%
  conf_mat(truth = y, estimate = .pred_class)
##           Truth
## Prediction -1 1
##         -1  9 1
##         1   2 8

\[\text{accuracy} = \frac{9 + 8}{9 + 1 + 2 + 8} = 0.85\]

svm_linear_fit_001 %>% 
  augment(new_data = test_data) %>%
  conf_mat(truth = y, estimate = .pred_class)
##           Truth
## Prediction -1  1
##         -1 11  6
##         1   0  3

\[\text{accuracy} = \frac{11 + 3}{11 + 6 + 0 + 3} = 0.70\]

9.13.2 Linearly separable data

sim_data_sep <- sim_data %>% 
  mutate(
    x1 = ifelse(y == 1, x1 + 0.5, x1),
    x2 = ifelse(y == 1, x2 + 0.5, x2)
  )

sim_data_sep %>% 
  ggplot() +
  aes(x1, x2, color = y) +
  geom_point()

svm_fit_sep_1e5 <- svm_linear_spec %>% 
  set_args(cost = 1e5) %>%
  fit(y ~ ., data = sim_data_sep)

svm_fit_sep_1e5
## parsnip model object
## 
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1e+05 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  1  scale =  1  offset =  1 
## 
## Number of Support Vectors : 3 
## 
## Objective Function Value : -24.3753 
## Training error : 0 
## Probability model included.
svm_fit_sep_1e5 %>%
  extract_fit_engine() %>%
  plot()

svm_fit_sep_1 <- svm_linear_spec %>% 
  set_args(cost = 1) %>%
  fit(y ~ ., data = sim_data_sep)

svm_fit_sep_1
## parsnip model object
## 
## Support Vector Machine object of class "ksvm" 
## 
## SV type: C-svc  (classification) 
##  parameter : cost C = 1 
## 
## Polynomial kernel function. 
##  Hyperparameters : degree =  1  scale =  1  offset =  1 
## 
## Number of Support Vectors : 7 
## 
## Objective Function Value : -3.5451 
## Training error : 0.05 
## Probability model included.
svm_fit_sep_1 %>%
  extract_fit_engine() %>%
  plot()

test_data_sep <- test_data %>% 
  mutate(
    x1 = ifelse(y == 1, x1 + 0.5, x1),
    x2 = ifelse(y == 1, x2 + 0.5, x2)
  )

svm_fit_sep_1e5 %>% 
  augment(new_data = test_data_sep) %>%
  conf_mat(truth = y, estimate = .pred_class)
##           Truth
## Prediction -1 1
##         -1  9 1
##         1   2 8

\[\text{accuracy} = \frac{9 + 8}{8 + 1 + 2 + 8} = 0.85\]

svm_fit_sep_1 %>% 
  augment(new_data = test_data_sep) %>%
  conf_mat(truth = y, estimate = .pred_class)
##           Truth
## Prediction -1 1
##         -1  9 0
##         1   2 9

\[\text{accuracy} = \frac{9 + 9}{9 + 0 + 2 + 9} = 0.90\]