Info

Objective

The purpose of writing graded lab reports is to help students to stay on track and to provide summative feedback. Each lab report is just 1% of the total course mark. Please do not cheat - it is not worth it!

Your task

Solve the practical questions, knit your document into a PDF and submit to NTULearn before the deadline. The deadline is very tight because the task is simple. We are sure that everyone is capable to do it by themselves and we want to discourage taking someone else’s report and writing it with your own words.

Deadline

23 Sep 2026, midnight

Libraries

We will work with a dataset of RMS Titanic passengers. The response variable is Survived indicating whether a passenger died or survived the sinking. We will renamed Survived to Y and change it to a factor variable with possible values survived and died.

Here, we load libraries, data and set the random seed. Replace the number “1729” with the numeric part of your matric no.

library(tidyverse) # for manipulation with data
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(caret) # for machine learning
## Loading required package: lattice
## 
## Attaching package: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift
library(kernlab) # for SVM training
## 
## Attaching package: 'kernlab'
## 
## The following object is masked from 'package:purrr':
## 
##     cross
## 
## The following object is masked from 'package:ggplot2':
## 
##     alpha
library(titanic) # for the data

X <- titanic_train %>%
  select(Survived, Pclass, Sex, Age, SibSp, Parch, Fare, Embarked) %>%
  mutate(
    Survived = if_else(Survived == 1, "survived", "died"),
    Survived = factor(Survived, levels = c("died", "survived"))  
    # survived is positive class
  ) %>%
  rename(Y = Survived) %>%
  drop_na()


head(X)
set.seed(1729) # replace the number '1729' with your matric no

Question 1

Plot the data as a scatterplot on the axes Age, log(Fare) coloured according to the response variable Y. Then split the data into 70% training and 30% test sets and report dimensions of the training and the test sets.

X %>%
  ggplot(aes(x = Age, y = Fare, group = Y, colour = Y)) +
  geom_point() + scale_y_log10() +
  theme_minimal()
## Warning in scale_y_log10(): log-10 transformation introduced infinite values.

ind <- which(runif(nrow(X)) < 0.7)
train_data <- X %>% slice(ind)
test_data <- X %>% slice(-ind)

cat("Training data dimensions =", dim(train_data), "\n")
## Training data dimensions = 522 8
cat("Test data dimensions =", dim(test_data), "\n")
## Test data dimensions = 192 8

Question 2

Train a polynomial SVM to predict if a Titanic passenger survived the sinking. Tune the values of the hyperparameters by 5-fold cross-validation. Try degrees \(d=1,2,3\) and choose a reasonable grid for the other hyperparameters. Report the optimal combination of the hyperparameter values and the resulting test accuracy.

svm_poly_grid <- expand.grid(C = c(0.25, 0.5, 1, 2, 4),
                        scale = c(0.5, 1, 2, 4),
                        degree = c(1, 2, 3))

mod_svm_poly <- train(
  Y ~ . , train_data, method = 'svmPoly',
  tuneGrid = svm_poly_grid,
  trControl =  trainControl(method = "cv", number = 5)
)

The best combination of hyperparemeter values is

mod_svm_poly$bestTune

The resulting test accuracy is

### Helper function 
test_accuracy <- function(caret_model, 
                          dataset = test_data, 
                          response_var = "Y") {
  cm <- caret_model %>%
    predict(dataset, type = "raw") %>%
    confusionMatrix(dataset[[response_var]])
  
  cm$overall['Accuracy']
}

mod_svm_poly %>% test_accuracy() %>%
  round(3) %>% 
  cat("Polynomial SVM accuracy =", . ,"\n")
## Polynomial SVM accuracy = 0.792

Question 3

Train an SVM with a radial basis kernel. Use the following grid for hyperparameter tuning:

svm_gauss_grid <- expand.grid(sigma = 2^(-2:2),
                              C = 2^(-2:2))

Report the optimal combination of the hyperparameter values and the resulting test accuracy.

mod_svm_radial <- train(Y ~ . , data = train_data, method = "svmRadial",
                tuneGrid = svm_gauss_grid,
                trControl = trainControl("cv", number = 5))

cat("Best hyperparameters for Gauss kernel are\n")
mod_svm_radial$bestTune

mod_svm_radial %>% test_accuracy() %>%
  round(3) %>% 
  cat("Guass Kernel SVM accuracy =", . ,"\n")
## Best hyperparameters for Gauss kernel are
## Guass Kernel SVM accuracy = 0.792

Question 4

Is SVM a parametric or a non-parametric model? Can we use SVM for statistical inference? Explain your answer.

Answer SVM is generally considered a non-parametric model because it does not assume a fixed functional form for the relationship between predictors and the response. Instead, it uses the training data to determine decision boundaries (through support vectors and kernel functions). Unlike parametric models such as logistic regression, SVMs do not provide interpretable coefficients or standard errors. Therefore, SVMs are powerful for prediction but not suitable for statistical inference — we cannot, for example, quantify how much gender or passenger class increases or decreases the probability of survival.

Declaration of Generative AI usage

I used ChatGPT 5.0 to improve clarity of my answer to Question 4.

Type your name to confirm: Fedor Duzhin