Πρόβλεψη Καρκίνου του Μαστού με Random Forest

Στο συγκεκριμένο μέρος της ανάλυσης χρησιμοποιείται το dataset Breast Cancer Wisconsin, το οποίο περιλαμβάνει κυτταρολογικά χαρακτηριστικά από βιοψίες όγκων. Στόχος είναι η εκπαίδευση ενός Random Forest μοντέλου που προβλέπει αν ένας όγκος είναι καλοήθης ή κακοήθης. Η αξιολόγηση του μοντέλου γίνεται με βάση το accuracy, το sensitivity και το AUC, με ιδιαίτερη έμφαση στην αναγνώριση των κακοήθων περιπτώσεων.

TODO 1: Stratified Train/Test Split

Σε αυτό το βήμα, το dataset χωρίζεται σε training set και testing set με αναλογία 70/30. Η διαδικασία γίνεται με stratified split, ώστε να διατηρηθούν παρόμοιες οι αναλογίες των δύο κλάσεων, δηλαδή benign και malignant, τόσο στο training όσο και στο testing dataset.

library(tidyverse)
library(mlbench)
library(randomForest)
library(xgboost)
library(caret)
library(pROC)

set.seed(42)

# --- Φόρτωση δεδομένων ---
data("BreastCancer", package = "mlbench")
bc <- BreastCancer

# Καθαρισμός: αφαίρεση ID, χειρισμός missing, μετατροπή σε numeric
bc$Id <- NULL
bc <- na.omit(bc)
bc[, 1:9] <- lapply(bc[, 1:9], function(x) as.numeric(as.character(x)))

str(bc)
## 'data.frame':    683 obs. of  10 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 ...
##  $ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
##  - attr(*, "na.action")= 'omit' Named int [1:16] 24 41 140 146 159 165 236 250 276 293 ...
##   ..- attr(*, "names")= chr [1:16] "24" "41" "140" "146" ...
table(bc$Class)
## 
##    benign malignant 
##       444       239
set.seed(42)

train_idx <- createDataPartition(bc$Class,p = 0.7,list = FALSE)

train <- bc[train_idx, ]
test  <- bc[-train_idx, ]

# Έλεγχος ότι οι αναλογίες των κλάσεων διατηρήθηκαν
prop.table(table(train$Class)) %>% round(3)
## 
##    benign malignant 
##     0.649     0.351
prop.table(table(test$Class)) %>% round(3)
## 
##    benign malignant 
##     0.652     0.348

Το αποτέλεσμα δείχνει ότι οι αναλογίες των δύο κλάσεων είναι παρόμοιες στο training και στο testing dataset. Αυτό είναι σημαντικό, γιατί το μοντέλο πρέπει να εκπαιδευτεί και να αξιολογηθεί σε δεδομένα που έχουν παρόμοια κατανομή με το αρχικό dataset. Με αυτόν τον τρόπο, η αξιολόγηση του μοντέλου είναι πιο αξιόπιστη και δεν επηρεάζεται υπερβολικά από ανισορροπία μεταξύ των κλάσεων.

TODO 2: Εκπαίδευση Random Forest μοντέλου

Σε αυτό το βήμα εκπαιδεύεται ένα μοντέλο Random Forest με στόχο την πρόβλεψη της μεταβλητής Class, η οποία δείχνει αν ένας όγκος είναι benign ή malignant. Το μοντέλο χρησιμοποιεί όλα τα διαθέσιμα κυτταρολογικά χαρακτηριστικά του dataset ως ανεξάρτητες μεταβλητές. Ο αριθμός των δέντρων ορίζεται σε 500, ενώ η επιλογή importance = TRUE επιτρέπει αργότερα την ανάλυση της σημαντικότητας των μεταβλητών.

set.seed(42)

rf_model <- randomForest(
  Class ~ .,          # Χρήση όλων των μεταβλητών για πρόβλεψη του Class
  data = train,
  ntree = 500,        # Αριθμός δέντρων
  importance = TRUE   # Υπολογισμός variable importance
)

print(rf_model)
## 
## Call:
##  randomForest(formula = Class ~ ., data = train, ntree = 500,      importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 3.76%
## Confusion matrix:
##           benign malignant class.error
## benign       301        10  0.03215434
## malignant      8       160  0.04761905

Το Random Forest εκπαιδεύτηκε χρησιμοποιώντας 500 decision trees. Κάθε δέντρο δημιουργείται με τυχαίο υποσύνολο των δεδομένων και των μεταβλητών, ενώ η τελική πρόβλεψη βασίζεται στη συνδυασμένη απόφαση όλων των δέντρων. Αυτή η διαδικασία βοηθά στη μείωση του variance και κάνει το μοντέλο πιο σταθερό και λιγότερο ευαίσθητο σε τυχαίες αλλαγές των δεδομένων.

Το output του μοντέλου εμφανίζει βασικές πληροφορίες, όπως τον αριθμό των δέντρων, τον αριθμό των μεταβλητών που εξετάζονται σε κάθε split και το OOB error rate. Το OOB error αποτελεί μια εσωτερική εκτίμηση του σφάλματος του μοντέλου, χωρίς να χρησιμοποιείται ξεχωριστό validation set.

OOB Error Curve

Το παρακάτω διάγραμμα δείχνει πώς μεταβάλλεται το OOB error καθώς αυξάνεται ο αριθμός των δέντρων στο Random Forest. Αν η καμπύλη σταθεροποιείται, τότε σημαίνει ότι ο αριθμός των δέντρων είναι επαρκής και η προσθήκη περισσότερων δέντρων δεν βελτιώνει σημαντικά το μοντέλο.

plot(rf_model, main = "OOB Error vs Number of Trees")
legend(
  "topright",
  colnames(rf_model$err.rate),
  col = 1:3,
  lty = 1:3
)

TODO 3: Αξιολόγηση Random Forest στο test set

Σε αυτό το βήμα χρησιμοποιείται το εκπαιδευμένο Random Forest μοντέλο για να γίνουν προβλέψεις στο test set. Η αξιολόγηση γίνεται με Accuracy, Sensitivity και AUC. Ως positive class ορίζεται η κλάση malignant, επειδή στο συγκεκριμένο πρόβλημα μας ενδιαφέρει ιδιαίτερα η σωστή αναγνώριση των κακοήθων περιπτώσεων.

# Προβλέψεις κλάσης
rf_pred <- predict(rf_model, test)

# Προβλέψεις πιθανοτήτων για την κλάση malignant
rf_prob <- predict(rf_model, test, type = "prob")[, "malignant"]

# Confusion Matrix
rf_cm <- confusionMatrix(rf_pred,test$Class,positive = "malignant")
print(rf_cm)
## Confusion Matrix and Statistics
## 
##            Reference
## Prediction  benign malignant
##   benign       131         2
##   malignant      2        69
##                                           
##                Accuracy : 0.9804          
##                  95% CI : (0.9506, 0.9946)
##     No Information Rate : 0.652           
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9568          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9718          
##             Specificity : 0.9850          
##          Pos Pred Value : 0.9718          
##          Neg Pred Value : 0.9850          
##              Prevalence : 0.3480          
##          Detection Rate : 0.3382          
##    Detection Prevalence : 0.3480          
##       Balanced Accuracy : 0.9784          
##                                           
##        'Positive' Class : malignant       
## 
# AUC
rf_auc <- roc(
  test$Class,
  rf_prob,
  levels = c("benign", "malignant"),
  quiet = TRUE
)$auc

cat("RF AUC:", round(rf_auc, 3), "\n")
## RF AUC: 0.999
# Συγκεντρωτικός πίνακας με τα βασικά metrics
rf_results <- tibble(
  Model = "Random Forest",
  Accuracy = round(rf_cm$overall["Accuracy"], 3),
  Sensitivity = round(rf_cm$byClass["Sensitivity"], 3),
  AUC = round(as.numeric(rf_auc), 3)
)

rf_results
## # A tibble: 1 × 4
##   Model         Accuracy Sensitivity   AUC
##   <chr>            <dbl>       <dbl> <dbl>
## 1 Random Forest     0.98       0.972 0.999

Το Random Forest μοντέλο αξιολογήθηκε στο test set και πέτυχε Accuracy = 0.980, δηλαδή περίπου 98.0%. Αυτό σημαίνει ότι ταξινόμησε σωστά το 98% των περιπτώσεων, άρα είχε πολύ υψηλή συνολική απόδοση.

Ωστόσο, σε ιατρικό context το Accuracy δεν είναι από μόνο του αρκετό. Είναι σημαντικό να εξετάζεται και το Sensitivity, επειδή δείχνει πόσο καλά το μοντέλο αναγνωρίζει τις πραγματικά κακοήθεις περιπτώσεις (malignant). Ένα false negative, δηλαδή ένας κακοήθης όγκος να προβλεφθεί ως καλοήθης, μπορεί να έχει σοβαρές συνέπειες.

Επομένως, το 98.0% Accuracy είναι πολύ καλό αποτέλεσμα, αλλά πρέπει να αξιολογείται μαζί με το Sensitivity, το AUC και το confusion matrix.

TODO 4: Variable Importance στο Random Forest

Σε αυτό το βήμα παρουσιάζεται το Variable Importance plot του Random Forest μοντέλου. Το γράφημα δείχνει ποιες μεταβλητές συνέβαλαν περισσότερο στην πρόβλεψη της μεταβλητής Class.

varImpPlot(rf_model, main = "Variable Importance — Random Forest")

importance(rf_model)
##                    benign malignant MeanDecreaseAccuracy MeanDecreaseGini
## Cl.thickness    12.702656 19.830096            19.363837        11.845927
## Cell.size       11.605774 16.330563            19.826188        60.344346
## Cell.shape      10.417048 20.502944            21.800283        53.561147
## Marg.adhesion    6.228395 12.425305            13.009741         5.977215
## Epith.c.size    10.510480  3.579421            11.128863        11.709247
## Bare.nuclei     20.271034 19.036320            26.035566        34.154820
## Bl.cromatin      6.086543 14.621620            14.691182        14.587449
## Normal.nucleoli 11.849778 11.676750            15.229482        23.815034
## Mitoses          4.984157  0.839361             4.608274         1.550928

Με βάση το Variable Importance plot, τα τρία σημαντικότερα χαρακτηριστικά του Random Forest μοντέλου ήταν τα Cell.size, Cell.shape και Bare.nuclei. Αυτές οι μεταβλητές είχαν τις μεγαλύτερες τιμές σημαντικότητας και επομένως συνέβαλαν περισσότερο στη διάκριση μεταξύ benign και malignant περιπτώσεων.

Το Cell.size και το Cell.shape σχετίζονται με το μέγεθος και το σχήμα των κυττάρων, ενώ το Bare.nuclei αφορά την παρουσία γυμνών πυρήνων. Η υψηλή σημαντικότητα αυτών των χαρακτηριστικών είναι λογική, καθώς τέτοια κυτταρολογικά χαρακτηριστικά μπορούν να βοηθήσουν στη διάκριση μεταξύ καλοήθων και κακοήθων περιπτώσεων.

TODO 5: Προετοιμασία δεδομένων για XGBoost

Σε αυτό το βήμα τα δεδομένα μετατρέπονται στην κατάλληλη μορφή για το XGBoost. Σε αντίθεση με το Random Forest, το XGBoost δεν δέχεται απευθείας data frames με factor target. Για αυτόν τον λόγο, οι ανεξάρτητες μεταβλητές μετατρέπονται σε αριθμητικό matrix, ενώ η μεταβλητή Class μετατρέπεται σε δυαδική μορφή, όπου malignant = 1 και benign = 0.

# Μετατροπή των features σε numeric matrix
train_x <- as.matrix(train[, !names(train) %in% "Class"])
test_x  <- as.matrix(test[, !names(test) %in% "Class"])

# Μετατροπή του target σε 0/1
# malignant = 1, benign = 0
train_y <- ifelse(train$Class == "malignant", 1, 0)
test_y  <- ifelse(test$Class == "malignant", 1, 0)

# Δημιουργία xgb.DMatrix, δηλαδή της μορφής που χρειάζεται το XGBoost
dtrain <- xgb.DMatrix(data = train_x, label = train_y)
dtest  <- xgb.DMatrix(data = test_x, label = test_y)

Η μετατροπή αυτή είναι απαραίτητη γιατί το XGBoost απαιτεί τα δεδομένα εισόδου να είναι σε αριθμητική μορφή. Τα features αποθηκεύονται ως matrices, ενώ η μεταβλητή-στόχος Class μετατρέπεται σε δυαδικό vector. Η κλάση malignant ορίζεται ως 1, επειδή αποτελεί τη θετική κλάση που θέλουμε να εντοπίζει το μοντέλο, ενώ η κλάση benign ορίζεται ως 0.

TODO 6: Εκπαίδευση XGBoost μοντέλου με early stopping

Σε αυτό το βήμα εκπαιδεύεται ένα XGBoost μοντέλο για την πρόβλεψη της κλάσης Class. Το μοντέλο χρησιμοποιεί objective binary:logistic, επειδή το πρόβλημα είναι binary classification, δηλαδή πρόβλεψη μεταξύ benign και malignant. Ως μετρική αξιολόγησης χρησιμοποιείται το AUC.

set.seed(42)

params <- list(
  objective = "binary:logistic",
  eval_metric = "auc",
  max_depth = 4,
  eta = 0.1,
  subsample = 0.8,
  colsample_bytree = 0.8
)

xgb_model <- xgb.train(
  params = params,
  data = dtrain,
  nrounds = 500,
  watchlist = list(train = dtrain, test = dtest),
  early_stopping_rounds = 20,
  print_every_n = 25,
  verbose = 1,
  maximize = TRUE
)
## Multiple eval metrics are present. Will use test_auc for early stopping.
## Will train until test_auc hasn't improved in 20 rounds.
## 
## [1]  train-auc:0.977109  test-auc:0.967489 
## [26] train-auc:0.997722  test-auc:0.995446 
## [51] train-auc:0.999541  test-auc:0.997458 
## [76] train-auc:0.999847  test-auc:0.997564 
## Stopping. Best iteration:
## [95] train-auc:0.999923  test-auc:0.997458
## 
## [95] train-auc:0.999923  test-auc:0.997458
xgb_model$best_iteration
## NULL
xgb_model$best_score
## NULL

Το XGBoost μοντέλο εκπαιδεύτηκε με μέγιστο αριθμό 500 επαναλήψεων, αλλά χρησιμοποιήθηκε early stopping ώστε η εκπαίδευση να σταματήσει αν το AUC στο test set δεν βελτιωθεί για 20 συνεχόμενους γύρους. Αυτό βοηθά στην αποφυγή overfitting, επειδή το μοντέλο δεν συνεχίζει να εκπαιδεύεται άσκοπα όταν η απόδοσή του δεν βελτιώνεται.

Η παράμετρος max_depth = 4 ελέγχει το μέγιστο βάθος κάθε δέντρου, ενώ το eta = 0.1 είναι το learning rate, δηλαδή το πόσο γρήγορα μαθαίνει το μοντέλο σε κάθε επανάληψη. Οι παράμετροι subsample = 0.8 και colsample_bytree = 0.8 σημαίνουν ότι το μοντέλο χρησιμοποιεί κάθε φορά ένα ποσοστό των παρατηρήσεων και των μεταβλητών, κάτι που μπορεί να βοηθήσει στη μείωση του overfitting.

TODO 7: Σύγκριση XGBoost και Random Forest

Σε αυτό το βήμα συγκρίνονται τα δύο μοντέλα, Random Forest και XGBoost, με βάση τις μετρικές Accuracy, Sensitivity, Specificity και AUC. Η σύγκριση γίνεται στο test set, ώστε να αξιολογηθεί η απόδοση των μοντέλων σε δεδομένα που δεν χρησιμοποιήθηκαν στην εκπαίδευση.

# ==============================
# Random Forest metrics
# ==============================

rf_pred <- predict(rf_model, test)
rf_prob <- predict(rf_model, test, type = "prob")[, "malignant"]

rf_cm <- confusionMatrix(
  rf_pred,
  test$Class,
  positive = "malignant"
)

rf_auc <- roc(
  test$Class,
  rf_prob,
  levels = c("benign", "malignant")
)$auc


# ==============================
# XGBoost metrics
# ==============================

xgb_prob <- predict(xgb_model, dtest)

xgb_pred <- factor(
  ifelse(xgb_prob > 0.5, "malignant", "benign"),
  levels = levels(test$Class)
)

xgb_cm <- confusionMatrix(
  xgb_pred,
  test$Class,
  positive = "malignant"
)

xgb_auc <- roc(
  test$Class,
  xgb_prob,
  levels = c("benign", "malignant")
)$auc


# ==============================
# Comparison table
# ==============================

comparison_results <- tibble(
  Model = c("Random Forest", "XGBoost"),
  Accuracy = c(
    rf_cm$overall["Accuracy"],
    xgb_cm$overall["Accuracy"]
  ),
  Sensitivity = c(
    rf_cm$byClass["Sensitivity"],
    xgb_cm$byClass["Sensitivity"]
  ),
  Specificity = c(
    rf_cm$byClass["Specificity"],
    xgb_cm$byClass["Specificity"]
  ),
  AUC = c(
    as.numeric(rf_auc),
    as.numeric(xgb_auc)
  )
)

comparison_results <- comparison_results %>%
  mutate(across(where(is.numeric), round, 3))

comparison_results
## # A tibble: 2 × 5
##   Model         Accuracy Sensitivity Specificity   AUC
##   <chr>            <dbl>       <dbl>       <dbl> <dbl>
## 1 Random Forest    0.98        0.972       0.985 0.999
## 2 XGBoost          0.975       0.958       0.985 0.998

Από τη σύγκριση των δύο μοντέλων φαίνεται ότι τόσο το Random Forest όσο και το XGBoost πέτυχαν πολύ υψηλή απόδοση στην ταξινόμηση των όγκων σε καλοήθεις και κακοήθεις. Το Random Forest είχε Accuracy 0.980, δηλαδή περίπου 98.0%, Sensitivity 0.972, Specificity 0.985 και AUC 0.999. Αυτό δείχνει ότι το μοντέλο ταξινόμησε σωστά τη μεγάλη πλειοψηφία των περιπτώσεων του test set και είχε πολύ υψηλή ικανότητα αναγνώρισης των κακοήθων περιπτώσεων.

Το XGBoost παρουσίασε επίσης εξαιρετική απόδοση, με Accuracy 0.975, Sensitivity 0.958, Specificity 0.985 και AUC 0.998. Η τιμή AUC δείχνει ότι και αυτό το μοντέλο έχει πολύ ισχυρή ικανότητα διαχωρισμού μεταξύ των δύο κλάσεων, δηλαδή benign και malignant.

Συγκριτικά, το Random Forest ήταν το μοντέλο που νίκησε, αλλά με πολύ μικρή διαφορά. Συγκεκριμένα, είχε υψηλότερο Accuracy κατά 0.005, δηλαδή περίπου 0.5 ποσοστιαίες μονάδες, υψηλότερο Sensitivity κατά 0.014, δηλαδή περίπου 1.4 ποσοστιαίες μονάδες, και ελαφρώς υψηλότερο AUC κατά 0.001. Το Specificity ήταν ίδιο και για τα δύο μοντέλα, με τιμή 0.985.

Αυτό που προκαλεί ενδιαφέρον είναι ότι το XGBoost, παρόλο που είναι ένα πολύ ισχυρό boosting μοντέλο, δεν ξεπέρασε το Random Forest στο συγκεκριμένο dataset. Και τα δύο μοντέλα είχαν σχεδόν τέλεια απόδοση, με AUC πολύ κοντά στο 1, όμως το Random Forest είχε ελαφρώς καλύτερη συνολική απόδοση και υψηλότερο Sensitivity.

Σε ιατρικό context, το Sensitivity είναι ιδιαίτερα σημαντικό, επειδή δείχνει πόσο καλά το μοντέλο εντοπίζει τις πραγματικά κακοήθεις περιπτώσεις. Επομένως, με βάση τα συγκεκριμένα αποτελέσματα, το Random Forest μπορεί να θεωρηθεί ελαφρώς καλύτερη επιλογή, καθώς είχε την υψηλότερη ευαισθησία στην ανίχνευση κακοήθειας.

TODO 8 Bonus: Σύγκριση διαφορετικών τιμών eta στο XGBoost

train_xgb_eta <- function(eta_value) {
  
  params_eta <- list(
    objective = "binary:logistic",
    eval_metric = "auc",
    max_depth = 4,
    eta = eta_value
  )
  
  model_eta <- xgb.train(
    params = params_eta,
    data = dtrain,
    nrounds = 500,
    watchlist = list(
      train = dtrain,
      test = dtest
    ),
    early_stopping_rounds = 20,
    maximize = TRUE,
    verbose = 0
  )
  
  # Παίρνουμε τα καλύτερα αποτελέσματα από τα attributes
  best_iteration <- as.numeric(xgb.attr(model_eta, "best_iteration")) + 1
  best_score <- as.numeric(xgb.attr(model_eta, "best_score"))
  
  # Πόσοι γύροι έτρεξαν συνολικά μέχρι να σταματήσει
  stopped_at <- nrow(model_eta$evaluation_log)
  
  tibble(
    eta = eta_value,
    Best_Iteration = best_iteration,
    Stopped_At = stopped_at,
    Best_Test_AUC = best_score
  )
}

eta_results <- bind_rows(
  train_xgb_eta(0.01),
  train_xgb_eta(0.10),
  train_xgb_eta(0.3)
)

eta_results <- eta_results %>%
  mutate(Best_Test_AUC = round(Best_Test_AUC, 4))

eta_results
## # A tibble: 3 × 3
##     eta Best_Iteration Best_Test_AUC
##   <dbl>          <dbl>         <dbl>
## 1  0.01             35         0.995
## 2  0.1              25         0.997
## 3  0.3              30         0.997

Από τη δοκιμή διαφορετικών τιμών του learning rate (eta) παρατηρείται ότι τα XGBoost μοντέλα πέτυχαν πολύ υψηλή απόδοση. Στο αρχικό μοντέλο χρησιμοποιήθηκε eta = 0.10, ενώ μετέπειτα δοκιμάστηκαν επιπλέον οι τιμές eta = 0.01 και eta = 0.30.

Για eta = 0.01, το καλύτερο αποτέλεσμα εμφανίστηκε στο 35ο iteration, με AUC 0.9950. Για eta = 0.30, το καλύτερο αποτέλεσμα εμφανίστηκε στο 30ο iteration, με AUC 0.9972. Τα αποτελέσματα δείχνουν ότι το μεγαλύτερο learning rate, δηλαδή eta = 0.30, χρειάστηκε λιγότερους γύρους εκπαίδευσης για να φτάσει στην καλύτερη απόδοσή του και πέτυχε ελαφρώς υψηλότερο AUC.

Αυτό είναι αναμενόμενο, καθώς ένα μεγαλύτερο eta επιτρέπει στο μοντέλο να μαθαίνει πιο γρήγορα σε κάθε iteration. Αντίθετα, ένα μικρότερο eta, όπως το 0.01, οδηγεί σε πιο αργή και σταδιακή μάθηση, άρα συνήθως χρειάζεται περισσότερους γύρους για να πετύχει την καλύτερη απόδοση.

Συνολικά, στο συγκεκριμένο dataset, το eta = 0.30 φαίνεται να ήταν ελαφρώς πιο αποδοτικό, καθώς πέτυχε καλύτερο AUC με λιγότερα iterations. Ωστόσο, η διαφορά μεταξύ των τιμών είναι πολύ μικρή, αφού τα AUC είναι πολύ κοντά στο 1. Αυτό σημαίνει ότι τα μοντέλα είχαν εξαιρετική ικανότητα διάκρισης μεταξύ benign και malignant περιπτώσεων.

TODO 9 Bonus: ROC plot για Random Forest και XGBoost

Σε αυτό το βήμα δημιουργείται ένα ROC plot για τη σύγκριση του Random Forest και του XGBoost. Το ROC curve δείχνει την ικανότητα κάθε μοντέλου να διαχωρίζει τις δύο κλάσεις, δηλαδή benign και malignant.

# ROC curves
roc_rf <- roc(
  test$Class,
  rf_prob,
  levels = c("benign", "malignant")
)

roc_xgb <- roc(
  test$Class,
  xgb_prob,
  levels = c("benign", "malignant")
)

# AUC values
rf_auc <- auc(roc_rf)
xgb_auc <- auc(roc_xgb)

# ROC plot
plot(
  roc_rf,
  col = "forestgreen",
  lwd = 2,
  main = "ROC Curves Comparison"
)

lines(
  roc_xgb,
  col = "steelblue",
  lwd = 2
)

legend(
  "bottomright",
  legend = c(
    paste0("RF (AUC = ", round(rf_auc, 3), ")"),
    paste0("XGBoost (AUC = ", round(xgb_auc, 3), ")")
  ),
  col = c("forestgreen", "steelblue"),
  lwd = 2
)

Το ROC plot συγκρίνει την ικανότητα των δύο μοντέλων να διαχωρίζουν τις benign από τις malignant περιπτώσεις. Όσο πιο κοντά βρίσκεται η καμπύλη στην πάνω αριστερή γωνία, τόσο καλύτερη είναι η διαχωριστική ικανότητα του μοντέλου.

Με βάση τα προηγούμενα αποτελέσματα, και τα δύο μοντέλα έχουν πολύ υψηλό AUC, γεγονός που δείχνει ότι μπορούν να ξεχωρίσουν με μεγάλη ακρίβεια τις καλοήθεις από τις κακοήθεις περιπτώσεις. Το Random Forest είχε AUC περίπου 0.999, ενώ το XGBoost είχε AUC περίπου 0.998. Επομένως, οι δύο καμπύλες αναμένεται να είναι πολύ κοντά μεταξύ τους, με το Random Forest να παρουσιάζει ελαφρώς υψηλότερη διαχωριστική ικανότητα.