Random Forests & Gradient Boosting

Dataset: Breast Cancer (Wisconsin)

Στόχος: Πρόβλεψη κακοήθειας από κυτταρολογικά χαρακτηριστικά

ΜΕΡΟΣ Α — BASELINE με Random Forest

1. Stratified train/test split (70/30)

set.seed(42)
# Η createDataPartition διατηρεί την αναλογία των κλάσεων (Benign/Malignant)
trainIndex <- createDataPartition(bc$Class, p = 0.7, list = FALSE)
train <- bc[trainIndex, ]
test  <- bc[-trainIndex, ]

2. Εκπαίδευση Random Forest

# ntree=500: Χτίζουμε 500 δέντρα
# importance=TRUE: Για να υπολογίσουμε τη σημαντικότητα των χαρακτηριστικών
modelRF <- randomForest(Class ~ ., 
    data = train, 
    ntree = 500, 
    importance = TRUE)

3. Υπολογισμός Accuracy, Sensitivity, AUC

# Προβλέψεις κλάσης
predictRF <- predict(modelRF, newdata = test)

# Confusion Matrix
cm_rf <- confusionMatrix(predictRF, test$Class)
print(cm_rf)
## 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.9850          
##             Specificity : 0.9718          
##          Pos Pred Value : 0.9850          
##          Neg Pred Value : 0.9718          
##              Prevalence : 0.6520          
##          Detection Rate : 0.6422          
##    Detection Prevalence : 0.6520          
##       Balanced Accuracy : 0.9784          
##                                           
##        'Positive' Class : benign          
## 
# Υπολογισμός AUC (χρειάζεται τις πιθανότητες)
probRF <- predict(modelRF, newdata = test, type = "prob")
roc_rf <- roc(test$Class, probRF[, "malignant"])
## Setting levels: control = benign, case = malignant
## Setting direction: controls < cases
print(paste("AUC:", round(auc(roc_rf), 4)))
## [1] "AUC: 0.9982"

Συμπεράσματα

Η Ακρίβεια (Accuracy) του μοντέλου ανέρχεται στο 98,04%, υποδηλώνοντας ένα εξαιρετικά υψηλό ποσοστό σωστών προβλέψεων επί του συνόλου των βιοψιών. Ωστόσο, στο πλαίσιο ενός ογκολογικού κέντρου, η αποτυχία εντοπισμού έστω και λίγων κακοηθών περιπτώσεων (False Negatives) θέτει σε άμεσο κίνδυνο τη ζωή των ασθενών. Για τον λόγο αυτό, η Ευαισθησία (Sensitivity) αποτελεί την πλέον κρίσιμη μετρική για ένα εργαλείο υποστήριξης ιατρικών αποφάσεων· στην περίπτωσή μας, αυτή διαμορφώνεται στο 98,50%, γεγονός που αποδεικνύει την υψηλή αξιοπιστία του μοντέλου στον ορθό εντοπισμό των κακοηθών όγκων. Τέλος, η τιμή AUC (0.9982*), η οποία προσεγγίζει τη μονάδα, επιβεβαιώνει την κορυφαία ικανότητα του μοντέλου να διαχωρίζει επιτυχώς τις καλοήθεις από τις κακοήθεις περιπτώσεις, καθιστώντας το ένα πολύτιμο διαγνωστικό βοήθημα.

4. Variable Importance plot

#varImpPlot(modelRF, main = "Variable Importance (Random Forest)") 

#ggplot2 

# 1. Μετατροπή των importance data σε data frame
imp_data <- as.data.frame(importance(modelRF))
imp_data$Variable <- rownames(imp_data)

# 2. Αναδιοργάνωση των δεδομένων (Long Format)
# Μετατρέπουμε το table έτσι ώστε τα Accuracy και Gini να είναι σε μία στήλη
imp_long <- imp_data %>%
  pivot_longer(cols = c(MeanDecreaseAccuracy, MeanDecreaseGini), 
               names_to = "Metric", 
               values_to = "Value")

# 3. Δημιουργία του Faceted Plot
ggplot(imp_long, aes(x = Value, y = reorder(Variable, Value))) +
  geom_segment(aes(x = 0, xend = Value, y = Variable, yend = Variable), color = "black") +
  geom_point(aes(color = Metric), size = 3) +
  # Διαχωρισμός σε δύο panels
  facet_wrap(~Metric, scales = "free_x") + 
  theme_minimal() +
  labs(
    title = "Variable Importance Analysis",
    subtitle = "Comparison of Accuracy and Gini Importance",
    x = "Importance Score",
    y = NULL
  ) +
  theme(
    legend.position = "none",
    strip.text = element_text(face = "bold", size = 11),
    panel.grid.major.y = element_blank()
  ) +
  scale_color_manual(values = c("MeanDecreaseAccuracy" = "#90D5FF", "MeanDecreaseGini" = "#FFC5D3"))

### Συμπεράσματα

Σύμφωνα με το κριτήριο MeanDecreaseGini, το οποίο μετρά πόσο συμβάλλει κάθε μεταβλητή στην ομοιογένεια των κόμβων σε όλα τα δέντρα του δάσους, τα τρία σημαντικότερα κυτταρολογικά χαρακτηριστικά για τη διάγνωση είναι:
Cell.size (Μέγεθος Κυττάρου), Cell.shape (Σχήμα Κυττάρου) και Bare.nuclei(Γυμνοί Πυρήνες),που στην κυτταρολογία αναφέρεται σε πυρήνες κυττάρων που έχουν χάσει το κυτταρόπλασμά τους.

ΜΕΡΟΣ Β — BOOSTING & TUNING

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

# Το XGBoost απαιτεί αριθμητικούς πίνακες (matrices) και labels 0/1 
train_x <- as.matrix(train[, 1:9])
test_x  <- as.matrix(test[, 1:9])

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

# Δημιουργία DMatrix για βελτιστοποίηση ταχύτητας και early stopping
dtrain <- xgb.DMatrix(data = train_x, label = train_y)
dtest  <- xgb.DMatrix(data = test_x, label = test_y)

6. Εκπαίδευση XGBoost με Early Stopping —

set.seed(42)
params <- list(
  objective = "binary:logistic", # Για δυαδική κατηγοριοποίηση 
  max_depth = 4,                 # Βάθος δέντρου 
  eta = 0.1,                     # Learning rate (ρυθμός μάθησης)
  eval_metric = "error"
)

model_xgb <- xgb.train(
  params = params,
  data = dtrain,
  nrounds = 500,
  evals = list(val = dtest, train = dtrain),
  early_stopping_rounds = 20,    # Σταματά αν δεν βελτιωθεί για 20 γύρους
  verbose = 0
)

# Προβλέψεις (μετατροπή πιθανοτήτων σε κλάσεις 0/1)
pred_probs <- predict(model_xgb, test_x)
pred_xgb <- ifelse(pred_probs > 0.5, "malignant", "benign")
pred_xgb <- factor(pred_xgb, levels = c("benign", "malignant"))

7. Σύγκριση XGBoost vs Random Forest —

cm_xgb <- confusionMatrix(pred_xgb, test$Class)

# Υπολογισμός AUC για XGBoost
roc_xgb <- roc(test$Class, pred_probs)
## Setting levels: control = benign, case = malignant
## Setting direction: controls < cases
# Δημιουργία συγκριτικού πίνακα
comparison_table <- data.frame(
  Metric = c("Accuracy", "Sensitivity", "Specificity", "AUC"),
  Random_Forest = c(cm_rf$overall["Accuracy"], cm_rf$byClass["Sensitivity"], 
                    cm_rf$byClass["Specificity"], as.numeric(auc(roc_rf))),
  XGBoost = c(cm_xgb$overall["Accuracy"], cm_xgb$byClass["Sensitivity"], 
              cm_xgb$byClass["Specificity"], as.numeric(auc(roc_xgb)))
)

print(comparison_table)
##                  Metric Random_Forest   XGBoost
## Accuracy       Accuracy     0.9803922 0.9705882
## Sensitivity Sensitivity     0.9849624 0.9849624
## Specificity Specificity     0.9718310 0.9436620
##                     AUC     0.9981997 0.9972466

Συμπεράσματα

  1. Ποιο μοντέλο νίκησε; Με πόση διαφορά;

Στη συγκεκριμένη υλοποίηση, το μοντέλο Random Forest αναδεικνύεται νικητής έναντι του XGBoost στις περισσότερες μετρικές: Accuracy (Ακρίβεια): Το Random Forest υπερέχει με 0.9804 έναντι 0.9706 του XGBoost (διαφορά ~1%). Sensitivity (Ευαισθησία): Και τα δύο μοντέλα πέτυχαν την ίδια κορυφαία επίδοση (0.9850), γεγονός εξαιρετικά σημαντικό για την ανίχνευση κακοήθειας.
Specificity (Ειδικότητα): Το Random Forest παρουσιάζει καλύτερη ικανότητα στον εντοπισμό των καλοήθων όγκων (0.9718) σε σχέση με το XGBoost (0.9437). AUC: Το Random Forest προηγείται οριακά με 0.9982 έναντι 0.9972.

  1. Σας εξέπληξε κάτι στα αποτελέσματα;

Συνήθως, το XGBoost (Gradient Boosting) αναμένεται να έχει ελαφρώς καλύτερες επιδόσεις από το Random Forest, καθώς διορθώνει σειριακά τα σφάλματα των προηγούμενων δέντρων. Ωστόσο, στην περίπτωση αυτή το Random Forest αποδείχθηκε πιο ισχυρό. Αυτό μπορεί να οφείλεται στους εξής παράγοντες:
Tuning (Παραμετροποίηση): Το XGBoost είναι πολύ ευαίσθητο στις παραμέτρους του.
Μέγεθος Δεδομένων: Το Random Forest είναι εξαιρετικά σταθερό και αξιόπιστο σε datasets μεσαίου μεγέθους, όπως οι 699 βιοψίες του BreastCancer.
Σταθερότητα (Stability): Η τυχαιότητα του Random Forest στην επιλογή χαρακτηριστικών και δειγμάτων το καθιστά ιδιαίτερα ανθεκτικό, αποφεύγοντας την υπερεκπαίδευση (overfitting) που μερικές φορές επηρεάζει το Boosting.

(# 8. Δοκιμή eta 0.01 και 0.3)

9. ROC plot με 2 καμπύλες

# Σχεδίαση της πρώτης καμπύλης (Random Forest)
plot(roc_rf, col = "#90D5FF", lwd = 3, main = "Σύγκριση ROC: Random Forest vs XGBoost")

# Προσθήκη της δεύτερης καμπύλης (XGBoost)
plot(roc_xgb, col = "#FFC5D3", lwd = 3, add = TRUE)

# Προσθήκη υπομνήματος
legend("bottomright", legend = c(
  paste("Random Forest (AUC =", round(auc(roc_rf), 3), ")"),
  paste("XGBoost (AUC =", round(auc(roc_xgb), 3), ")")
), col = c("#90D5FF", "#FFC5D3"), lwd = 3)

# Συμπεράσματα

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

Επίσης,η περιοχή κάτω από την καμπύλη (AUC) είναι τόσο υψηλή, που επιβεβαιώνει ότι το μοντέλο είναι έτοιμο να χρησιμοποιηθεί ως Decision Support Tool για τους γιατρούς, ελαχιστοποιώντας το ρίσκο λανθασμένης διάγνωσης.