1 Το πρόβλημα & η επιλογή μεθόδου

Dataset: creditcard.csv — 284.807 συναλλαγές, εκ των οποίων μόνο 492 απάτες (0,172%). Χαρακτηριστικά: Time, Amount και V1V28 (ανώνυμα, προϊόν PCA). H δυαδική μεταβλητή όπου στο Class 0 = κανονική, 1 = απάτη.

Η τράπεζα θέλει να ανοιχνέυει όσο πιο πολλές δόλιες και σπάνιες συναλλαγές μέσα σε πάρα πολλες εγραφφές με χιλλίαδες χαρακτιριστικά ποου δεν αλληλεπιδρανε μεταξυ τους εμφαθύνουμε στην πρόβλεψη και αποτελεσματικότηα

➜ Μεθόδος: Ensemble δέντρων αποφάσεων — Random Forest & Gradient Boosting.

όχι γραμμικό μοντέλο:

  • Μη γραμμικότητα & αλληλεπιδράσεις. Τα δέντρα «σπάνε» τον χώρο των χαρακτηριστικών και θέτουν αλληλεπιδράσεις μεταξύ των V από μόνες τους

  • Ανθεκτικότητα. Δεν χρειάζονται κανονικοποίηση/υποθέσεις κατανομής

  • Επίδοση ανισορροπίας. Με class weights ή resampling διαχειρίζονται τις σπάνιες κλάσεις καλύτερα

Ως baseline κρατάμε μια Logistic Regression, ώστε να δέιξουμε πόσο κερδίζουμε

2 Setup & Φόρτωση

library(data.table)   
library(caret)        
library(ranger)      
library(gbm)          
library(pROC)         
set.seed(42)          
df <- fread("creditcard.csv")
df$Class <- as.integer(df$Class)
dim(df)
## [1] 284807     31

3 Exploratory Data Analysis

3.1 Ισορροπία κλάσεων

table(df$Class)
## 
##      0      1 
## 284315    492
round(prop.table(table(df$Class)) * 100, 3)
## 
##      0      1 
## 99.827  0.173
barplot(table(df$Class), col = c("steelblue", "firebrick"),
        names.arg = c("Κανονική (0)", "Απάτη (1)"),
        main = "Έντονη ανισορροπία κλάσεων", ylab = "Πλήθος")

θα κρίνουμε τα μοντέλα με Precision, Recall, F1, ROC-AUC και PR-AUC, όχι με accuracy γιατί το accutacy δεν ανιχνευει τις απάτες μόνες τις επιτυχίες

3.2 Το ποσό (Amount) ανά κλάση

tapply(df$Amount, df$Class, summary)
## $`0`
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##     0.00     5.65    22.00    88.29    77.05 25691.16 
## 
## $`1`
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00    1.00    9.25  122.21  105.89 2125.87

4 Train / Test split (stratified)

Κρατάμε την αναλογία απάτης ίδια σε train και test 70/30:

set.seed(42)
idx   <- createDataPartition(factor(df$Class), p = 0.7, list = FALSE)
train <- df[idx]
test  <- df[-idx]
c(train_fraud = sum(train$Class), test_fraud = sum(test$Class))
## train_fraud  test_fraud 
##         345         147

4.1 Βοηθητικές συναρτήσεις μετρικών

# PR-AUC (εμβαδόν κάτω από την καμπύλη Precision-Recall)
pr_auc <- function(labels, scores) {
  o <- order(scores, decreasing = TRUE); l <- labels[o]
  tp <- cumsum(l); fp <- cumsum(1 - l)
  prec <- tp / (tp + fp); rec <- tp / sum(l)
  sum(diff(c(0, rec)) * prec)
}
# Μετρικές σε δεδομένο κατώφλι
get_metrics <- function(labels, scores, th = 0.5) {
  pred <- as.integer(scores > th)
  tp <- sum(pred == 1 & labels == 1); fp <- sum(pred == 1 & labels == 0)
  fn <- sum(pred == 0 & labels == 1); tn <- sum(pred == 0 & labels == 0)
  prec <- tp / (tp + fp); rec <- tp / (tp + fn)
  data.frame(
    ROC_AUC = round(as.numeric(pROC::auc(labels, scores)), 4),
    PR_AUC  = round(pr_auc(labels, scores), 4),
    Precision = round(prec, 3), Recall = round(rec, 3),
    F1 = round(2 * prec * rec / (prec + rec), 3), FN = fn, FP = fp)
}

5 Baseline: Logistic Regression

ερμηνεύσιμο μοντέλο πάνω στο training set:

lr <- glm(Class ~ ., data = train, family = binomial)
p_lr <- predict(lr, test, type = "response")
m_lr <- get_metrics(test$Class, p_lr); m_lr

6 Αντιμετώπιση ανισορροπίας: downsampling

Δημιουργούμε ένα training set — όλες τις απάτες + δείγμα κανονικών συναλλαγών σε αναλογία 1:10.

fraud  <- train[Class == 1]
legit  <- train[Class == 0]
set.seed(1)
balanced <- rbind(fraud, legit[sample(.N, 10 * nrow(fraud))])
table(balanced$Class)
## 
##    0    1 
## 3450  345

7 Random Forest

set.seed(1)
rf <- ranger(x = balanced[, !"Class"], y = factor(balanced$Class),
             num.trees = 400, probability = TRUE,
             importance = "impurity", num.threads = 4)
p_rf <- predict(rf, test[, !"Class"])$predictions[, "1"]
m_rf <- get_metrics(test$Class, p_rf); m_rf

8 Gradient Boosting

set.seed(1)
gb <- gbm(Class ~ ., data = balanced, distribution = "bernoulli",
          n.trees = 800, interaction.depth = 4, shrinkage = 0.05,
          bag.fraction = 0.8, verbose = FALSE)
p_gb <- predict(gb, test, n.trees = 800, type = "response")
m_gb <- get_metrics(test$Class, p_gb); m_gb

9 Διερευνητικές ερωτήσεις

9.1 Q1 — Μετρικές κατάλληλες για ανισοκατανομή

res <- rbind(Logistic = m_lr, RandomForest = m_rf, GradientBoosting = m_gb)
res

➜ Απάντηση. Με 99%“accuracy” trivial baseline, το accuracy δεν μας βοηθάει Χρησιμοποιούμε PR-AUC

κλάση), ROC-AUC, Recall (τι ποσοστό απατών πιάνουμε) και Precision . Και τα τρία μοντέλα έχουν ROC-AUC ≈ 0,97–0,99 κοιτάμε κυρίως PR-AUC (≈ 0,74–0,86) και το ζεύγος Recall/Precision.

9.2 Q2 — Επίδραση της αντιμετώπισης της ανισορροπίας

Εκπαιδεύουμε ένα Random Forest χωρίς αντιμετώπιση και το συγκρίνουμε με το ισορροπημένο :

set.seed(1)
rf_imb <- ranger(x = train[, !"Class"], y = factor(train$Class),
                 num.trees = 100, probability = TRUE, num.threads = 4)
## Growing trees.. Progress: 47%. Estimated remaining time: 34 seconds.
## Growing trees.. Progress: 98%. Estimated remaining time: 1 seconds.
p_rfi  <- predict(rf_imb, test[, !"Class"])$predictions[, "1"]
m_rfi  <- get_metrics(test$Class, p_rfi)

rbind(`RF χωρίς αντιμετώπιση` = m_rfi,
      `RF με downsampling`   = m_rf)

Απάντηση. Η αντιμετώπιση της ανισορροπίας αυξάνει το Recall : από ~0,78 σε ~0,86, δηλαδή λιγότερα false negatives Αλλά είναι χαμηλότερο Precision / περισσότερα false positives . Δηλαδή το resampling **μετακινεί το μοντέλο προς

false positives μέσω κατωφλίου .

9.3 Q3 — Feature importance

imp <- sort(rf$variable.importance, decreasing = TRUE)
barplot(rev(head(imp, 10)), horiz = TRUE, las = 1, col = "steelblue",
        main = "Random Forest — Top 10 Feature Importance", xlab = "Importance (impurity)")

head(round(imp, 1), 10)
##   V14   V17   V10   V12   V11   V16    V3    V4    V7    V9 
## 118.6  80.8  76.2  63.1  45.3  40.4  32.2  24.9  21.3  12.1
imp_gb <- summary(gb, plotit = FALSE)
head(imp_gb, 10)

Απάντηση. Και οι δύο αλγόριθμοι δείχνουν: το V14 είναι το καλύτερο χαρακτηριστικό, ακολουθούμενο από V17, V10, V12, V4, V7. Όταν δύο ανεξάρτητοι αλγόριθμοι δείχνουν τα ίδια top features, το σήμα είναι αξιόπιστο. `

9.4 Q4 — Απλό μοντέλο → ισχυρό ensemble

roc_lr <- roc(test$Class, p_lr); roc_rf <- roc(test$Class, p_rf); roc_gb <- roc(test$Class, p_gb)
plot(roc_lr, col = "darkorange", lwd = 2, main = "ROC Curves")
lines(roc_rf, col = "forestgreen", lwd = 2)
lines(roc_gb, col = "steelblue", lwd = 2)
legend("bottomright", lwd = 2, col = c("darkorange","forestgreen","steelblue"),
       legend = c(sprintf("Logistic (AUC=%.3f)", auc(roc_lr)),
                  sprintf("Random Forest (AUC=%.3f)", auc(roc_rf)),
                  sprintf("Gradient Boosting (AUC=%.3f)", auc(roc_gb))))

Απάντηση. Περνώντας από τη Logistic Regression στα ensembles το Recall ανεβαίνει (από 0,69 σε ~0,86–0,88), δηλαδή πιάνουμε σημαντικά*περισσότεραδείγματα ενώ το ROC-AUC φτάνει ~0,99. Το Random Forest δίνει το υψηλότερο PR-AUC, ενώ το Gradient Boosting το υψηλότερο Recall

9.5 Q5 — Επιλογή κατωφλίου (threshold)

Το 0,5 . Σαρώνουμε τα δεδομένα και βλέπουμε το trade-off false negatives ↔︎ false positives για το Random Forest:

ths <- seq(0.1, 0.95, by = 0.05)
tbl <- t(sapply(ths, function(th){
  pred <- as.integer(p_rf > th)
  tp <- sum(pred==1 & test$Class==1); fp <- sum(pred==1 & test$Class==0)
  fn <- sum(pred==0 & test$Class==1)
  c(Threshold=th, Recall=round(tp/(tp+fn),3), Precision=round(tp/(tp+fp),3), FN=fn, FP=fp)
}))
as.data.frame(tbl)
plot(ths, tbl[,"Recall"], type="l", lwd=2, col="forestgreen", ylim=c(0,1),
     xlab="Κατώφλι (threshold)", ylab="Τιμή", main="Recall & Precision vs Threshold")
lines(ths, tbl[,"Precision"], lwd=2, col="firebrick")
abline(v=0.7, lty=2, col="grey40")
legend("bottomleft", lwd=2, col=c("forestgreen","firebrick"),
       legend=c("Recall (ευαισθησία)","Precision"))

Απάντηση. Ζητάμε ελαχιστοποίηση των false negatives χωρίς να αυξηθούν τα false positives. Στα χαμηλά κατώφλια (0,3) πιάνουμε πολλές από τις απάτες αλλά με εκατοντάδες λάθος Στο ≈ 0,7 έχουμε Recall ≈ 0,84** (πιάνουμε ~124/147 απάτες) με Precision ≈ 0,80 και μόλις ~30 false

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

  • Μέθοδος: Για να βρίσκοθμε σπάνια δεδομένα που δεν αλληλεπιρδούν μεταξ΄θ τους τα χαρακτηριστικά τα ensembles δέντρων (RF & Gradient Boosting) είναι η πιο σωστή επιλογή

  • Μετρικές: Το accuracy είναι παραπλανητικό . Κρίνουμε με PR-AUC, Recall, Precision.

  • Ανισορροπία: downsampling/class weights ανεβάζει το Recall με αποτέλεσμα περισσότερους false positives.

  • Feature importance: V14 και V17, V10, V12, V4 δείχνουν στην ανίχνευση ότι συμφωνούν οι αλγόριθμοι

  • Κατώφλι: Επιλογή βάσει κόστους· ένα κατώφλι ≈ 0,7 δίνει ισορροπημένο Recall/Precision.

  • SMOTE, hyper-parameter tuning με cross-validation, cost-sensitive learning, και σε κλίμακα XGBoost/LightGBM πιο γρήγορα σε πολλές εγγραφές. ```