Pendahuluan

Definisi Umum

Support Vector Machine (SVM) adalah algoritma supervised learning yang digunakan untuk klasifikasi dan regresi. Prinsip utama SVM adalah mencari hyperplane optimal yang memisahkan dua kelas dengan margin maksimum (Cortes & Vapnik, 1995). Dalam kasus data non-linear, SVM menggunakan teknik kernel trick untuk memetakan data ke ruang berdimensi lebih tinggi. SVM dibagi menjadi dua model, yaitu model linear dan model non-linear.

Tujuan Analisis

  • Menjelaskan konsep dasar Support Vector Machine (SVM) dalam konteks klasifikasi
  • Mengevaluasi kinerja model SVM linear dibandingkan dengan model nonlinear menggunakan kernel RBF
  • Mengkaji dampak dari variasi parameter C dan gamma terhadap performa model
  • Menyajikan visualisasi batas keputusan (decision boundary) yang dihasilkan oleh model

Persiapan Data

Dataset yang digunakan dalam penelitian ini adalah Breast Cancer Dataset. Dataset ini mengandung informasi tentang berbagai fitur medis yang digunakan untuk mengklasifikasikan apakah tumor yang ditemukan pada pasien adalah jinak (benign) atau ganas (malignant). ## Load Library Memuat berbagai library penting seperti e1071 untuk SVM, caret untuk ML workflow, ggplot2 untuk visualisasi, corrplot untuk korelasi antar fitur, dan mlbench untuk dataset.

# Load libraries
# Load libraries
library(e1071)           # SVM
library(caret)           # Machine learning toolkit
library(ggplot2)         # Visualisasi
library(dplyr)           # Data manipulation
library(gridExtra)       # Multiple plots
library(RColorBrewer)    # Color palettes
library(pROC)            # ROC curves
library(tidyr)           # Data tidying (untuk pivot functions)
library(knitr)           # Kable tables
library(mlbench)         # For Dataset
library(corrplot)

Eksplorasi Dataset

Eksplorasi data dan visualisasi awal ini dilakukan untuk memahami struktur dan distribusi data sebelum membangun model.

# Load dataset
data(BreastCancer)
df <- BreastCancer

# Informasi dasar dataset
cat("Dimensi dataset:", dim(df), "\n")
## Dimensi dataset: 699 11
cat("Jumlah missing values:", sum(is.na(df)), "\n")
## Jumlah missing values: 16
# Struktur data
str(df)
## 'data.frame':    699 obs. of  11 variables:
##  $ Id             : chr  "1000025" "1002945" "1015425" "1016277" ...
##  $ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 5 3 6 4 8 1 2 2 4 ...
##  $ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 1 1 2 ...
##  $ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 2 1 1 ...
##  $ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 5 1 1 3 8 1 1 1 1 ...
##  $ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 7 2 3 2 7 2 2 2 2 ...
##  $ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 10 2 4 1 10 10 1 1 1 ...
##  $ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 3 3 3 3 3 9 3 3 1 2 ...
##  $ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 1 7 1 7 1 1 1 1 ...
##  $ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ...
##  $ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
# Summary statistik
summary(df)
##       Id             Cl.thickness   Cell.size     Cell.shape  Marg.adhesion
##  Length:699         1      :145   1      :384   1      :353   1      :407  
##  Class :character   5      :130   10     : 67   2      : 59   2      : 58  
##  Mode  :character   3      :108   3      : 52   10     : 58   3      : 58  
##                     4      : 80   2      : 45   3      : 56   10     : 55  
##                     10     : 69   4      : 40   4      : 44   4      : 33  
##                     2      : 50   5      : 30   5      : 34   8      : 25  
##                     (Other):117   (Other): 81   (Other): 95   (Other): 63  
##   Epith.c.size  Bare.nuclei   Bl.cromatin  Normal.nucleoli    Mitoses   
##  2      :386   1      :402   2      :166   1      :443     1      :579  
##  3      : 72   10     :132   3      :165   10     : 61     2      : 35  
##  4      : 48   2      : 30   1      :152   3      : 44     3      : 33  
##  1      : 47   5      : 30   7      : 73   2      : 36     10     : 14  
##  6      : 41   3      : 28   4      : 40   8      : 24     4      : 12  
##  5      : 39   (Other): 61   5      : 34   6      : 22     7      :  9  
##  (Other): 66   NA's   : 16   (Other): 69   (Other): 69     (Other): 17  
##        Class    
##  benign   :458  
##  malignant:241  
##                 
##                 
##                 
##                 
## 
# Distribusi kelas
table(df$Class)
## 
##    benign malignant 
##       458       241
# Plot distribusi kelas
ggplot(df, aes(x = Class, fill = Class)) +
  geom_bar(width = 0.5) +
  scale_fill_manual(values = c("skyblue", "salmon")) +
  labs(title = "Distribusi Kelas: Benign vs Malignant",
       x = "Kelas",
       y = "Jumlah") +
  theme_minimal()

Hasil Eksplorasi: 1) Dataset terdiri dari 699 observasi, 11 variabel, 16 nilai NA (missing) 2) Dataset mengandung banyak fitur bertipe ordinal factor, yang perlu dikonversi ke numerik. 3) Distribusi kelas terdapat 458 observasi benign 241 obervasi malignant. Menunjukkan dataset tidak terlalu imbalanced, meski kelas benign sedikit lebih dominan (~65%).

# Mengonversi kolom ordinal menjadi numerik
cols_to_convert <- c("Cl.thickness", "Cell.size", "Cell.shape", "Marg.adhesion",
                     "Epith.c.size", "Bare.nuclei", "Bl.cromatin",
                     "Normal.nucleoli", "Mitoses")

df[cols_to_convert] <- lapply(df[cols_to_convert], as.numeric)

# Memilih variabel numerik selain 'Class' dan 'Id'
numeric_vars <- df %>%
  select(-Class, -Id) %>%
  select(where(is.numeric))

# Mengecek struktur data numerik
str(numeric_vars)
## 'data.frame':    699 obs. of  9 variables:
##  $ Cl.thickness   : num  5 5 3 6 4 8 1 2 2 4 ...
##  $ Cell.size      : num  1 4 1 8 1 10 1 1 1 2 ...
##  $ Cell.shape     : num  1 4 1 8 1 10 1 2 1 1 ...
##  $ Marg.adhesion  : num  1 5 1 1 3 8 1 1 1 1 ...
##  $ Epith.c.size   : num  2 7 2 3 2 7 2 2 2 2 ...
##  $ Bare.nuclei    : num  1 10 2 4 1 10 10 1 1 1 ...
##  $ Bl.cromatin    : num  3 3 3 3 3 9 3 3 1 2 ...
##  $ Normal.nucleoli: num  1 2 1 7 1 7 1 1 1 1 ...
##  $ Mitoses        : num  1 1 1 1 1 1 1 1 5 1 ...
# Menghitung matriks korelasi (dengan observasi lengkap saja)
correlation_matrix <- cor(numeric_vars, use = "complete.obs")

# Visualisasi korelasi antar variabel
corrplot(correlation_matrix,
         method = "color",              # bentuk visual: warna gradasi
         type = "upper",                # hanya segitiga atas
         col = colorRampPalette(c("red", "white", "blue"))(200), # warna: merah ke biru
         tl.col = "black",              # warna teks label
         tl.srt = 45,                   # rotasi label variabel
         tl.cex = 0.8,                  # ukuran teks label
         number.cex = 0.7,              # ukuran angka korelasi (jika ditampilkan)
         addCoef.col = "black",         # warna angka korelasi
         title = "Matriks Korelasi Variabel Numerik",
         mar = c(0,0,2,0),              # margin atas untuk judul
         diag = FALSE)                  # tidak menampilkan diagonal

Heatmap matriks korelasi menunjukkan korelasi kuat antara Cell.size dan Cell.shape (r = 0.91). Di sisi lain, variabel Mitoses tampak memiliki korelasi yang relatif lemah dengan sebagian besar variabel lain (umumnya di bawah 0.50), sehingga dapat dianggap sebagai variabel yang lebih independen. Korelasi yang tinggi antar variabel perlu diwaspadai karena dapat menyebabkan multikolinearitas, terutama jika digunakan dalam model-model linier. Namun, pada metode yang lebih robust seperti Support Vector Machine (SVM), hal ini cenderung tidak menjadi masalah besar meskipun tetap relevan dalam interpretasi model.

Preprocessing Data

Langkah: 1. Hapus data NA → tersisa 683 observasi 2. Konversi faktor ordinal ke numerik 3. Standarisasi fitur (menggunakan scale) 4. Split data: 80% training (548), 20% testing (135)

# Menghilangkan baris dengan nilai yang hilang
df_clean <- na.omit(df)
cat("Jumlah data setelah menghapus missing values:", nrow(df_clean), "baris\n")
## Jumlah data setelah menghapus missing values: 683 baris
# Memilih fitur numerik dan target kelas untuk analisis
df_selected <- df_clean %>%
  select(Class, Cl.thickness, Cell.size, Cell.shape, Marg.adhesion, 
         Epith.c.size, Bare.nuclei, Bl.cromatin, Normal.nucleoli, Mitoses)

# Melakukan standardisasi pada variabel numerik
df_selected[, -1] <- scale(df_selected[, -1])

# Membagi data menjadi training (80%) dan testing (20%) secara acak dengan seed tertentu
set.seed(123)
idx_train <- createDataPartition(df_selected$Class, p = 0.8, list = FALSE)
train_data <- df_selected[idx_train, ]
test_data <- df_selected[-idx_train, ]

cat("Jumlah baris data training:", nrow(train_data), "\n")
## Jumlah baris data training: 548
cat("Jumlah baris data testing:", nrow(test_data), "\n")
## Jumlah baris data testing: 135
# Menghitung dan menampilkan frekuensi kelas pada data training dan testing
train_class_freq <- table(train_data$Class)
test_class_freq <- table(test_data$Class)

kable(rbind(train_class_freq, test_class_freq), 
      caption = "Frekuensi Kelas pada Data Training dan Testing")
Frekuensi Kelas pada Data Training dan Testing
benign malignant
train_class_freq 356 192
test_class_freq 88 47

Data telah dibersihkan dari missing values dan dibagi menjadi set training (80%) dan testing (20%). Dari hasil pembagian data, terlihat bahwa kelas benign lebih dominan dibandingkan kelas malignant di kedua set. Pada data training terdapat 356 sampel benign dan 192 sampel malignant, sedangkan pada data testing terdapat 88 sampel benign dan 47 sampel malignant.

Distribusi kelas ini relatif seimbang antar set training dan testing, sehingga model yang dibangun diharapkan dapat belajar dari representasi kedua kelas yang proporsional dan menguji performa secara adil pada data baru.

Model SVM Linear

SVM Linear merupakan pendekatan yang digunakan ketika data bersifat linearly separable, yaitu data dapat dipisahkan oleh sebuah garis lurus (atau hyperplane dalam dimensi tinggi). Tujuan utama model ini adalah untuk mencari hyperplane optimal yang memaksimalkan margin (jarak terdekat antara kelas positif dan negatif terhadap hyperplane). Model ini bekerja dengan mengoptimasi: \[ \min \frac{1}{2} \|w\|^2 + C \sum_{i=1}^n \xi_i \] dengan kendala: \[ y_i (w^T x_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0 \]

Parameter C (cost) mengatur trade-off antara margin maksimum dan kesalahan klasifikasi: - C kecil → toleran terhadap kesalahan → margin lebih besar - C besar → memaksa klasifikasi benar → margin kecil namun berisiko overfitting

Training Model SVM Linear

Langkah ini melakukan pelatihan model SVM dengan kernel linear menggunakan data training yang telah disiapkan sebelumnya. Model dilatih untuk memisahkan kelas benign dan malignant berdasarkan fitur-fitur yang tersedia. Setelah model dilatih, dilakukan prediksi terhadap data testing untuk menguji generalisasi model. Hasil prediksi dievaluasi menggunakan confusion matrix untuk mengetahui akurasi dan distribusi kesalahan klasifikasi antar kelas.

# Training SVM Linear
svm_linear <- svm(Class ~ ., data = train_data, 
                  kernel = "linear", cost = 1, scale = FALSE)

# Summary model
summary(svm_linear)
## 
## Call:
## svm(formula = Class ~ ., data = train_data, kernel = "linear", cost = 1, 
##     scale = FALSE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  linear 
##        cost:  1 
## 
## Number of Support Vectors:  43
## 
##  ( 21 22 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  benign malignant
# Prediksi pada data testing
pred_linear <- predict(svm_linear, test_data)

# Confusion Matrix
cm_linear <- confusionMatrix(pred_linear, test_data$Class)
print(cm_linear)
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  benign malignant
##   benign        87         2
##   malignant      1        45
##                                           
##                Accuracy : 0.9778          
##                  95% CI : (0.9364, 0.9954)
##     No Information Rate : 0.6519          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9508          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9886          
##             Specificity : 0.9574          
##          Pos Pred Value : 0.9775          
##          Neg Pred Value : 0.9783          
##              Prevalence : 0.6519          
##          Detection Rate : 0.6444          
##    Detection Prevalence : 0.6593          
##       Balanced Accuracy : 0.9730          
##                                           
##        'Positive' Class : benign          
## 

Model SVM dengan kernel linear dan parameter cost = 1 dilatih pada data training. Model menggunakan 43 support vector (21 dari kelas benign, 22 dari kelas malignant). Evaluasi performa model yang diperoleh dari Confusion Matrix adalah sebagai berikut. - Akurasi tinggi, yaitu 97.78%, dengan confidence interval 93.64%–99.54%. - Sensitivitas (recall) kelas benign sebesar 98.86%, menunjukkan model sangat baik dalam mendeteksi kelas benign. - Spesifisitas kelas malignant sebesar 95.74%, menandakan kemampuan model mengenali kelas malignant dengan baik.

Evaluasi Model Linear

Evaluasi model dilakukan dengan menghitung metrik performa utama seperti akurasi, precision, recall, dan F1-score. Karena struktur output confusion matrix bisa berbeda antara binary dan multiclass, kode ini juga menangani fleksibilitas tersebut. Nilai-nilai yang hilang (NA) diatasi agar evaluasi tetap akurat. Semua hasil dirangkum dalam bentuk tabel agar mudah dibaca dan dibandingkan.

# Ambil nilai akurasi dari confusion matrix
acc_linear <- cm_linear$overall['Accuracy']

# Cek struktur byClass (berlaku untuk binary dan multiclass)
if (!is.null(cm_linear$byClass) && is.matrix(cm_linear$byClass)) {
  prec_linear <- cm_linear$byClass[, 'Pos Pred Value']
  rec_linear <- cm_linear$byClass[, 'Sensitivity']
  f1_linear <- cm_linear$byClass[, 'F1']
} else {
  # Penanganan jika hanya ada dua kelas
  prec_linear <- c(cm_linear$byClass['Pos Pred Value'])
  rec_linear <- c(cm_linear$byClass['Sensitivity'])
  f1_linear <- c(cm_linear$byClass['F1'])
}

# Ganti NA dengan nol agar tidak mengganggu perhitungan
prec_linear[is.na(prec_linear)] <- 0
rec_linear[is.na(rec_linear)] <- 0
f1_linear[is.na(f1_linear)] <- 0

# Susun metrik evaluasi ke dalam data frame
svm_linear_metrics <- data.frame(
  Metrics = c("Accuracy",
              paste0("Precision_", names(prec_linear)),
              paste0("Recall_", names(rec_linear)),
              paste0("F1_", names(f1_linear))),
  Values = c(acc_linear, prec_linear, rec_linear, f1_linear)
)

# Tampilkan sebagai tabel
kable(svm_linear_metrics, digits = 4, caption = "Ringkasan Evaluasi SVM Linear")
Ringkasan Evaluasi SVM Linear
Metrics Values
Accuracy Accuracy 0.9778
Pos Pred Value Precision_Pos Pred Value 0.9775
Sensitivity Recall_Sensitivity 0.9886
F1 F1_F1 0.9831

Model SVM Nonlinear (RBF Kernel)

Radial Basis Function (RBF) adalah kernel populer untuk menangani data tidak linear. Formula RBF kernel: \[ K(x_i, x_j) = \exp(-\gamma \|x_i - x_j\|^2) \] Gamma mengontrol jangkauan pengaruh titik data: - Gamma kecil → boundary halus - Gamma besar → boundary tajam (overfitting)

Training Model SVM RBF

Pada bagian ini, model SVM dilatih kembali tetapi dengan kernel Radial Basis Function (RBF) yang lebih cocok untuk data non-linear. Parameter cost dan gamma digunakan untuk mengontrol margin dan kompleksitas model. Setelah pelatihan, prediksi dilakukan terhadap data testing dan hasilnya dievaluasi menggunakan confusion matrix.

# Training SVM dengan RBF kernel
svm_rbf <- svm(Class ~ ., data = train_data, 
               kernel = "radial", cost = 1, gamma = 0.25, scale = FALSE)

# Summary model
summary(svm_rbf)
## 
## Call:
## svm(formula = Class ~ ., data = train_data, kernel = "radial", cost = 1, 
##     gamma = 0.25, scale = FALSE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  radial 
##        cost:  1 
## 
## Number of Support Vectors:  149
## 
##  ( 33 116 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  benign malignant
# Prediksi pada data testing
pred_rbf <- predict(svm_rbf, test_data)

# Confusion Matrix
cm_rbf <- confusionMatrix(pred_rbf, test_data$Class)
print(cm_rbf)
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  benign malignant
##   benign        85         1
##   malignant      3        46
##                                           
##                Accuracy : 0.9704          
##                  95% CI : (0.9259, 0.9919)
##     No Information Rate : 0.6519          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9354          
##                                           
##  Mcnemar's Test P-Value : 0.6171          
##                                           
##             Sensitivity : 0.9659          
##             Specificity : 0.9787          
##          Pos Pred Value : 0.9884          
##          Neg Pred Value : 0.9388          
##              Prevalence : 0.6519          
##          Detection Rate : 0.6296          
##    Detection Prevalence : 0.6370          
##       Balanced Accuracy : 0.9723          
##                                           
##        'Positive' Class : benign          
## 

Evaluasi Model RBF

Tahapan evaluasi untuk model SVM RBF dilakukan dengan cara yang sama seperti sebelumnya. Metrik seperti akurasi, precision, recall, dan F1-score dihitung dari confusion matrix, dan dirangkum dalam sebuah tabel. Ini memungkinkan perbandingan langsung dengan model linear.

# Menghitung akurasi model SVM Non-Linear (RBF)
accuracy_rbf <- cm_rbf$overall['Accuracy']

# Mengekstrak nilai Precision, Recall, dan F1-score dari confusion matrix
# Menangani kemungkinan struktur byClass berbeda (multi-class vs binary)
if (!is.null(cm_rbf$byClass) && is.matrix(cm_rbf$byClass)) {
  precision_rbf <- cm_rbf$byClass[, 'Pos Pred Value']
  recall_rbf <- cm_rbf$byClass[, 'Sensitivity']
  f1_rbf <- cm_rbf$byClass[, 'F1']
} else {
  # Jika struktur byClass bukan matriks (misal untuk binary classification)
  precision_rbf <- c(cm_rbf$byClass['Pos Pred Value'])
  recall_rbf <- c(cm_rbf$byClass['Sensitivity'])
  f1_rbf <- c(cm_rbf$byClass['F1'])
}

# Mengganti nilai NA dengan 0 pada hasil evaluasi
precision_rbf[is.na(precision_rbf)] <- 0
recall_rbf[is.na(recall_rbf)] <- 0
f1_rbf[is.na(f1_rbf)] <- 0

# Membuat data frame berisi metrik evaluasi model
eval_rbf <- data.frame(
  Metrics = c("Accuracy", 
              paste0("Precision_", names(precision_rbf)),
              paste0("Recall_", names(recall_rbf)),
              paste0("F1_", names(f1_rbf))),
  Values = c(accuracy_rbf, precision_rbf, recall_rbf, f1_rbf)
)

# Menampilkan hasil evaluasi dalam bentuk tabel
kable(eval_rbf, digits = 4, caption = "Ringkasan Evaluasi SVM Non-Linear dengan RBF")
Ringkasan Evaluasi SVM Non-Linear dengan RBF
Metrics Values
Accuracy Accuracy 0.9704
Pos Pred Value Precision_Pos Pred Value 0.9884
Sensitivity Recall_Sensitivity 0.9659
F1 F1_F1 0.9770

Perbandingan Model

Tabel Perbandingan Performa

Langkah ini menggabungkan hasil evaluasi dari kedua model (SVM linear dan RBF) ke dalam satu tabel untuk memudahkan perbandingan. Selain itu, dibuat visualisasi berupa grafik batang untuk membandingkan akurasi kedua model secara visual.

# Pastikan kedua data frame evaluasi punya jumlah baris yang sama
min_rows <- min(nrow(svm_linear_metrics), nrow(eval_rbf))
linear_eval_trim <- svm_linear_metrics[1:min_rows, ]
rbf_eval_trim <- eval_rbf[1:min_rows, ]

# Satukan metrik dan nilai dari kedua model dalam satu tabel
perf_comparison <- data.frame(
  Metric = linear_eval_trim$Metrics,
  Linear_SVM = linear_eval_trim$Values,
  RBF_SVM = rbf_eval_trim$Values
)

kable(perf_comparison, digits = 4,
      caption = "Tabel Perbandingan Kinerja SVM Linear dan RBF")
Tabel Perbandingan Kinerja SVM Linear dan RBF
Metric Linear_SVM RBF_SVM
Accuracy 0.9778 0.9704
Precision_Pos Pred Value 0.9775 0.9884
Recall_Sensitivity 0.9886 0.9659
F1_F1 0.9831 0.9770
# Data frame untuk membandingkan akurasi kedua model
acc_df <- data.frame(
  Model = c("Linear SVM", "RBF SVM"),
  Accuracy = c(acc_linear, accuracy_rbf)
)

# Grafik batang perbandingan akurasi dengan palet warna berbeda dan style yang lebih clean
ggplot(acc_df, aes(x = Model, y = Accuracy, fill = Model)) +
  geom_bar(stat = "identity", color = "black", width = 0.6) +
  geom_text(aes(label = sprintf("%.4f", Accuracy)), vjust = -0.3, size = 4, fontface = "bold") +
  labs(title = "Visualisasi Akurasi SVM: Linear vs RBF",
       y = "Nilai Akurasi",
       x = "Jenis Model") +
  theme_light() +
  scale_fill_manual(values = c("#1b9e77", "#d95f02")) +
  coord_cartesian(ylim = c(0, 1))

Tuning Parameter

Tuning bertujuan mencari kombinasi optimal C dan gamma melalui grid search, dilakukan dengan cross-validation untuk menghindari overfitting.

Grid Search untuk Parameter Optimal

Bagian ini bertujuan menemukan kombinasi parameter cost dan gamma terbaik untuk SVM RBF menggunakan teknik grid search dengan validasi silang. Proses ini membantu menghindari overfitting dan memastikan model memiliki performa optimal pada data testing. Model terbaik disimpan dan digunakan untuk prediksi serta evaluasi lanjutan.

# Melakukan pencarian parameter terbaik untuk SVM dengan kernel RBF menggunakan grid search
grid_search <- tune(svm, Class ~ ., data = train_data,
                    kernel = "radial",
                    ranges = list(cost = c(0.1, 1, 10, 100),
                                  gamma = c(0.01, 0.1, 1, 10)))

# Menampilkan ringkasan hasil tuning parameter
summary(grid_search)
## 
## Parameter tuning of 'svm':
## 
## - sampling method: 10-fold cross validation 
## 
## - best parameters:
##  cost gamma
##     1  0.01
## 
## - best performance: 0.02912458 
## 
## - Detailed performance results:
##     cost gamma      error dispersion
## 1    0.1  0.01 0.03646465 0.02421542
## 2    1.0  0.01 0.02912458 0.03228551
## 3   10.0  0.01 0.03276094 0.03405321
## 4  100.0  0.01 0.03094276 0.03094732
## 5    0.1  0.10 0.03461279 0.02900786
## 6    1.0  0.10 0.03276094 0.03405321
## 7   10.0  0.10 0.04373737 0.02731977
## 8  100.0  0.10 0.06387205 0.03356664
## 9    0.1  1.00 0.06377104 0.03334984
## 10   1.0  1.00 0.05282828 0.03015989
## 11  10.0  1.00 0.04737374 0.02730483
## 12 100.0  1.00 0.04737374 0.02730483
## 13   0.1 10.00 0.35050505 0.06434433
## 14   1.0 10.00 0.14589226 0.03988949
## 15  10.0 10.00 0.13127946 0.03582566
## 16 100.0 10.00 0.13127946 0.03582566

#Mengambil parameter terbaik hasil tuning

optimal_params <- grid_search$best.parameters
message(sprintf("Parameter terbaik ditemukan: Cost = %.2f, Gamma = %.2f", 
                optimal_params$cost, optimal_params$gamma))

# Membuat model SVM dengan parameter optimal tersebut
svm_optimized <- grid_search$best.model

# Melakukan prediksi menggunakan model hasil tuning
predictions_optimized <- predict(svm_optimized, test_data)

# Membuat confusion matrix dari prediksi tersebut
cm_optimized <- confusionMatrix(predictions_optimized, test_data$Class)

# Menampilkan akurasi model yang sudah dituning
message(sprintf("Akurasi model hasil tuning: %.4f", cm_optimized$overall['Accuracy']))

Visualisasi Parameter Tuning

Untuk memberikan gambaran visual dari hasil tuning, heatmap error rate ditampilkan berdasarkan kombinasi nilai cost dan gamma. Area dengan warna lebih terang menunjukkan performa yang lebih baik (error lebih rendah). # Visualisasi heatmap dari hasil tuning parameter untuk melihat tingkat error

svm_tuned <- grid_search$performances

ggplot(svm_tuned, aes(x = factor(cost), y = factor(gamma), fill = error)) +
  geom_tile(color = "gray70") +  # memberi border tipis biar kotak lebih jelas
  scale_fill_gradient(low = "#f0f0f0", high = "#b2182b") +  # warna dari abu muda ke merah tua
  labs(title = "Heat Map Tingkat Kesalahan pada Parameter Tuning",
       x = "Nilai Cost", y = "Nilai Gamma", fill = "Tingkat Error") +
  theme_light() +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

Visualisasi Decision Boundary

Visualisasi decision boundary membantu memahami cara model memisahkan kelas. Biasanya menggunakan 2 fitur penting untuk memproyeksikan ke 2D.

Decision Boundary 2D

Visualisasi ini digunakan untuk melihat secara intuitif bagaimana model memisahkan kelas benign dan malignant di ruang dua dimensi. Dua fitur dipilih untuk mempermudah pemetaan ke bidang 2D. Visualisasi mencakup decision boundary dari model linear dan RBF agar dapat dibandingkan secara langsung.

# Memilih dua fitur utama untuk visualisasi 2D: Cl.thickness dan Cell.size
train_vis <- train_data %>% select(Class, Cl.thickness, Cell.size)
test_vis <- test_data %>% select(Class, Cl.thickness, Cell.size)

# Membangun model SVM dengan kernel linear untuk data 2D
svm_linear_2d <- svm(Class ~ ., data = train_vis, kernel = "linear", cost = 1)

# Membangun model SVM dengan kernel RBF menggunakan parameter terbaik hasil tuning
svm_rbf_2d <- svm(Class ~ ., data = train_vis, kernel = "radial", 
                  cost = optimal_params$cost, gamma = optimal_params$gamma)

# Fungsi untuk menggambar batas keputusan model pada ruang 2 dimensi
plot_svm_boundary <- function(model, dataset, plot_title) {
  # Membuat grid titik prediksi untuk menggambarkan decision boundary
  x_vals <- seq(min(dataset$Cl.thickness) - 0.5, max(dataset$Cl.thickness) + 0.5, length.out = 100)
  y_vals <- seq(min(dataset$Cell.size) - 0.5, max(dataset$Cell.size) + 0.5, length.out = 100)
  grid_points <- expand.grid(Cl.thickness = x_vals, Cell.size = y_vals)
  
  # Melakukan prediksi kelas pada grid titik
  grid_points$Class <- predict(model, grid_points)
  
  # Membuat plot dengan titik data dan latar warna sesuai prediksi
  ggplot() +
    geom_point(data = grid_points, aes(x = Cl.thickness, y = Cell.size, color = Class), 
               alpha = 0.25, size = 0.6) +
    geom_point(data = dataset, aes(x = Cl.thickness, y = Cell.size, color = Class), size = 2) +
    labs(title = plot_title,
         x = "Cl.thickness (Standarisasi)",
         y = "Cell.size (Standarisasi)") +
    theme_classic() +
    scale_color_brewer(type = "qual", palette = "Dark2")
}

# Menampilkan decision boundary untuk SVM linear dan RBF secara berdampingan
plot1 <- plot_svm_boundary(svm_linear_2d, train_vis, "Batas Keputusan SVM Linear")
plot2 <- plot_svm_boundary(svm_rbf_2d, train_vis, "Batas Keputusan SVM RBF")

grid.arrange(plot1, plot2, ncol = 2)

Analisis Parameter C dan Gamma

Pengaruh Parameter C

Analisis ini mengeksplorasi pengaruh nilai parameter C terhadap akurasi model SVM. Model dilatih dengan beberapa nilai C tetap menggunakan gamma yang sama, dan akurasinya dicatat. Hasil kemudian divisualisasikan untuk melihat tren hubungan antara C dan performa model.

# Mengevaluasi performa SVM dengan kernel RBF pada beberapa nilai parameter C, gamma tetap di 0.25
parameter_c <- c(0.01, 0.1, 1, 10, 100)
results_c <- data.frame(Cost = parameter_c, Accuracy = numeric(length(parameter_c)))

for (idx in seq_along(parameter_c)) {
  model_c <- svm(Class ~ ., data = train_data, kernel = "radial",
                 cost = parameter_c[idx], gamma = 0.25, scale = FALSE)
  predictions_c <- predict(model_c, test_data)
  cm_c <- confusionMatrix(predictions_c, test_data$Class)
  results_c$Accuracy[idx] <- cm_c$overall['Accuracy']
}

# Grafik perubahan akurasi terhadap nilai logaritma basis 10 dari parameter C
ggplot(results_c, aes(x = log10(Cost), y = Accuracy)) +
  geom_line(color = "#0072B2", size = 1.2) +
  geom_point(color = "#D55E00", size = 3.5) +
  labs(title = "Dampak Parameter C pada Akurasi Model SVM",
       x = expression(log[10](C)),
       y = "Akurasi (%)") +
  theme_light()

# Tabel hasil akurasi untuk tiap nilai parameter C
kable(results_c, digits = 4, caption = "Perbandingan Akurasi Model pada Berbagai Nilai C")
Perbandingan Akurasi Model pada Berbagai Nilai C
Cost Accuracy
1e-02 0.9630
1e-01 0.9556
1e+00 0.9704
1e+01 0.9704
1e+02 0.9778

Pengaruh Parameter Gamma

Serupa dengan sebelumnya, bagian ini mengevaluasi pengaruh parameter gamma terhadap akurasi model, dengan C tetap. Eksperimen dilakukan dengan beberapa nilai gamma, dan hasilnya divisualisasikan untuk memudahkan interpretasi.

# Menguji performa SVM RBF pada berbagai nilai gamma dengan cost tetap (C = 1)
gamma_set <- c(0.001, 0.01, 0.1, 1, 10)
results_gamma <- data.frame(Gamma = gamma_set, Accuracy = numeric(length(gamma_set)))

for (j in seq_along(gamma_set)) {
  model_gamma <- svm(Class ~ ., data = train_data, kernel = "radial",
                     cost = 1, gamma = gamma_set[j], scale = FALSE)
  preds_gamma <- predict(model_gamma, test_data)
  cm_gamma <- confusionMatrix(preds_gamma, test_data$Class)
  results_gamma$Accuracy[j] <- cm_gamma$overall['Accuracy']
}

# Visualisasi perubahan akurasi terhadap logaritma basis 10 dari nilai gamma
ggplot(results_gamma, aes(x = log10(Gamma), y = Accuracy)) +
  geom_line(color = "#009E73", size = 1.2) +    # hijau yang agak tegas
  geom_point(color = "#E69F00", size = 3.5) +   # oranye sebagai titik
  labs(title = "Dampak Nilai Gamma pada Akurasi Model SVM",
       x = expression(log[10](Gamma)),
       y = "Akurasi (%)") +
  theme_light()

# Tabel hasil akurasi per nilai gamma yang diuji
kable(results_gamma, digits = 4, caption = "Hasil Akurasi untuk Berbagai Nilai Gamma")
Hasil Akurasi untuk Berbagai Nilai Gamma
Gamma Accuracy
1e-03 0.9778
1e-02 0.9704
1e-01 0.9778
1e+00 0.9556
1e+01 0.8444

Interpretasi dan Analisis

Support Vectors

# Menampilkan total jumlah support vectors untuk masing-masing model SVM
cat("Total Support Vectors pada SVM Linear   :", svm_linear$tot.nSV, "\n")
## Total Support Vectors pada SVM Linear   : 43
cat("Total Support Vectors pada SVM RBF      :", svm_rbf$tot.nSV, "\n")
## Total Support Vectors pada SVM RBF      : 149
# Jumlah support vectors per kelas untuk tiap model
sv_linear_counts <- svm_linear$nSV
sv_rbf_counts <- svm_rbf$nSV
sv_tuned_counts <- svm_tuned$nSV

# Validasi panjang support vector (harus 2: benign dan malignant)
if (length(sv_linear_counts) == 2 &&
    length(sv_rbf_counts) == 2 &&
    length(sv_tuned_counts) == 2) {
  
  # Menyiapkan data frame perbandingan jumlah support vectors
  sv_df <- data.frame(
    Class = c("benign", "malignant"),
    Linear_SVM = sv_linear_counts,
    RBF_SVM = sv_rbf_counts,
    Tuned_SVM = sv_tuned_counts
  )
  
  # Mengubah format data menjadi long untuk mempermudah plotting
  sv_long_format <- sv_df %>%
    tidyr::pivot_longer(cols = -Class, names_to = "Model", values_to = "SupportVectorCount")
  
  # Visualisasi perbandingan jumlah support vectors
  ggplot(sv_long_format, aes(x = Class, y = SupportVectorCount, fill = Model)) +
    geom_col(position = position_dodge(width = 0.8), width = 0.7) +
    labs(title = "Perbandingan Jumlah Support Vectors per Kelas",
         x = "Kategori Kelas",
         y = "Jumlah Support Vectors") +
    theme_light() +
    scale_fill_brewer(type = "qual", palette = "Pastel1") +
    theme(plot.title = element_text(hjust = 0.5, face = "bold"))
  
} else {
  message(" ")
}

Feature Importance (untuk SVM Linear)

Untuk model linear, bobot atau koefisien fitur dapat digunakan untuk mengukur pentingnya fitur terhadap keputusan klasifikasi. Pada bagian ini, dilakukan perhitungan dan visualisasi kontribusi absolut masing-masing fitur untuk membantu memahami fitur mana yang paling berpengaruh.

# Koefisien untuk SVM linear (weight vector)
weights <- t(svm_linear$coefs) %*% svm_linear$SV
feature_names <- colnames(train_data)[-1]

importance_df <- data.frame(
  Feature = feature_names,
  Importance = abs(as.numeric(weights))
)
importance_df <- importance_df[order(importance_df$Importance, decreasing = TRUE), ]

ggplot(importance_df, aes(x = reorder(Feature, Importance), y = Importance)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(title = "Feature Importance - SVM Linear",
       x = "Fitur", y = "Absolute Weight") +
  theme_minimal()

kable(importance_df, digits = 4, caption = "Feature Importance SVM Linear")
Feature Importance SVM Linear
Feature Importance
3 Cell.shape 0.6650
6 Bare.nuclei 0.6199
1 Cl.thickness 0.5299
7 Bl.cromatin 0.4316
9 Mitoses 0.3811
8 Normal.nucleoli 0.3423
4 Marg.adhesion 0.1675
5 Epith.c.size 0.1167
2 Cell.size 0.0107

Ringkasan dan Evaluasi Akhir

Kinerja Model

  • SVM dengan Kernel Linear: Tingkat akurasi mencapai 0.9778
  • SVM dengan Kernel RBF (Non-Linear): Memperoleh akurasi sebesar 0.9704
  • SVM RBF setelah Penalaan Parameter: Akurasi tertinggi 0.9704

Insight Utama

  1. Performa Berdasarkan Kernel
    • SVM dengan kernel linear memberikan performa yang lebih baik daripada SVM dengan kernel non-linear.
  2. Optimasi Parameter
    • Kombinasi parameter terbaik untuk SVM Linear dan Non-Linear adalah:
      • C = 1
      • Gamma = 0.01
    • Parameter C mengatur keseimbangan antara margin yang lebar dan penalti kesalahan klasifikasi.
    • Gamma menentukan seberapa jauh pengaruh data pelatihan menjangkau dalam pembentukan batas keputusan.
  3. Kontribusi Fitur
    • Analisis bobot pada model SVM linear menampilkan fitur utama: Cell.shape
    • Seluruh fitur pengukuran fisik berkontribusi terhadap performa klasifikasi.
  4. Jumlah Support Vector
    • Model RBF (termasuk versi tunning) menggunakan lebih banyak support vectors dibanding linear, mengindikasikan kompleksitas model yang lebih tinggi.

Penjelasan Parameter C dan Gamma

  • C (Cost): Mengontrol penalti kesalahan klasifikasi. Nilai C yang tinggi menyebabkan model berusaha keras untuk mengklasifikasikan semua data dengan benar, berisiko overfitting.
  • Gamma: Mengatur seberapa jauh pengaruh setiap titik data terhadap pembentukan boundary. Nilai gamma besar menyebabkan model lebih fokus pada titik-titik sekitar, membuat decision boundary lebih rumit.

Saran Praktis

  • Gunakan kernel RBF untuk dataset yang memiliki pola non-linear.
  • Lakukan penalaan parameter secara menyeluruh untuk mencapai hasil terbaik.
  • Pertimbangkan trade-off antara performa dan kompleksitas model guna menghindari overfitting.
  • Validasi hasil dengan metode cross-validation agar performa dapat dipertanggungjawabkan.

SVM terbukti efektif dalam klasifikasi dataset Breast Cancer.