Εισαγωγή

Σε αυτό το άρθρο κατασκευάζουμε ένα μοντέλο μηχανικής μάθησης με στόχο την πρόβλεψη των πελατών μίας τράπεζας που ενδιαφέρονται να ανοίξουν λογαριασμό προθεσμιακής κατάθεσης. Για τον σκοπό αυτό συγκρίνουμε επτά αλγορίθμους ταξινόμησης, με έμφαση στα μοντέλα Boosting (XGBoost και LightGBM). Τα δεδομένα προέρχονται από το «UCI Machine Learning Repository» και συγκεκριμένα από τη βάση Bank Marketing.

Πριν από την ανάλυση, ας ορίσουμε μερικές βασικές έννοιες.

Τι είναι η προθεσμιακή κατάθεση;

Πρόκειται για έναν τύπο τραπεζικού λογαριασμού όπου ο πελάτης δεσμεύεται να μην πραγματοποιήσει ανάληψη για ένα προκαθορισμένο χρονικό διάστημα (π.χ. ένα έτος). Σε αντάλλαγμα, η τράπεζα προσφέρει υψηλότερα επιτόκια σε σχέση με τους συνηθισμένους λογαριασμούς ταμιευτηρίου ή αποταμίευσης.

Ενδεικτικά:

  • Η Πειραιώς προσφέρει διπλάσιο επιτόκιο στους προθεσμιακούς λογαριασμούς της.
  • Η Eurobank προσφέρει μηδενικό επιτόκιο σε λογαριασμούς ταμιευτηρίου, 0,01%–0,35% σε λογαριασμούς αποταμίευσης, και 0,1%–1% σε προθεσμιακούς, ανάλογα με το πρόγραμμα και το ύψος της κατάθεσης.
  • Σύμφωνα με πρόσφατη έκθεση της Τράπεζας της Ελλάδας, τα επιτόκια των προθεσμιακών κυμαίνονται μεταξύ 1,2% και 1,4%, έναντι μόλις 0,03% για τους τυπικούς λογαριασμούς νοικοκυριών.

Το κατάλληλο προφίλ για αυτά τα προϊόντα αφορά, κατά κανόνα, άτομα με σημαντικό αποταμιευτικό υπόλοιπο και χωρίς βαριές οικονομικές υποχρεώσεις (δάνεια, ληξιπρόθεσμα χρέη).

Προαπαιτούμενα

Εισαγωγή βιβλιοθηκών

Για αυτή την ανάλυση θα χρειαστούμε τυπικές βιβλιοθήκες της R για την εισαγωγή των δεδομένων, μέσω του πακέτου {readr}, και τη μορφοποίηση αυτών με το πακέτο {dplyr}. Το πακέτο {kableExtra} αποτελεί σημαντική προσθήκη ώστε να τυπωθούν τα αποτελέσματα σε μορφή πίνακα. Ένα σημαντικό κομμάτι είναι αυτό της οπτικοποίησης των δεδομένων. Αρχικά χρησιμοποιούσα το πακέτο {ggplot2} προκειμένου να δημιουργήσω τα όποια διαγράμματα, πράγμα που είναι περιοριστικό για μία ιστοσελίδα, καθώς το ggplot2 δημιουργεί στατικά διαγράμματα. Έτσι λοιπόν για τα άρθρα μου γίνεται χρήση του πακέτου {highcharter} που δίνει τη δυνατότητα διαδραστικών διαγραμμάτων φιλικών για όλους τους τύπους οθονών. Τέλος, αυτή η ανάλυση έχει ως στόχο να κατηγοριοποιήσει τους πελάτες της τράπεζας με βάση το ενδιαφέρον τους σε κάποιο τραπεζικό προϊόν, επομένως η χρήση του πακέτου {tidymodels} κρίνεται απαραίτητη.

# Γενική επεξεργασία δεδομένων
library(readr)
library(dplyr)
library(forcats)
library(tidyr)
library(glue)

# Παρουσίαση αποτελεσμάτων
library(kableExtra)
library(reactable)
library(gt)

# Διαδραστικά διαγράμματα
library(highcharter)

# Μοντέλα μηχανικής μάθησης
library(tidymodels)
library(bonsai)    # LightGBM μέσω tidymodels
library(themis)    # SMOTE για ανισόρροπα δεδομένα
library(stacks)    # Ensemble stacking
library(probably)
library(discrim)

# Επιμέρους αλγόριθμοι
library(kknn)
library(ranger)
library(naivebayes)
library(kernlab)
library(vip)       # Σπουδαιότητα μεταβλητών

Εισαγωγή δεδομένων

Αφού φορτώσουμε τις αναγκαίες βιβλιοθήκες, θα πρέπει να εισάγουμε και τα δεδομένα μας. Υπάρχουν αρκετές εκδόσεις των ίδιων δεδομένων, μία μεγαλύτερη και μία πιο συνοπτική έκδοση, η διαφορά τους έγκειται μόνο στον αριθμό των παρατηρήσεων. Για το συγκεκριμένο άρθρο θα επιλέξω την πιο συνοπτική μορφή, μιας και η προσαρμογή Boosting μοντέλων είναι ιδιαίτερα χρονοβόρα σε σύγκριση με την κατασκευή πιο απλών μοντέλων ταξινόμησης (π.χ. Λογιστική Παλινδρόμηση, k Πλησιέστερων Γειτόνων).

bank_dataset <- read_delim("bank_dataset_files/bank.csv",
                           delim = ";",
                           escape_double = FALSE,
                           trim_ws = TRUE)

bank_dataset <- bank_dataset %>% tibble::rowid_to_column("ID")

Προεπισκόπηση δεδομένων

Παρακάτω παρουσιάζεται ένα μικρό δείγμα του συνόλου δεδομένων (οι πρώτες 6 παρατηρήσεις), ώστε να κατανοήσουμε τη δομή του και τον τύπο των μεταβλητών.

Προτού κάνουμε οποιαδήποτε ανάλυση είναι καλό να προσδιορίσουμε τον τύπο των δεδομένων που έχουμε διαθέσιμα. Γενικότερα, οι μεταβλητές μπορούν να ταξινομηθούν ως εξής:

  • Ποσοτικές: Διακριτές ή Συνεχείς
  • Ποιοτικές: Κατηγορικές ή Διατάξιμες

: Σύνοψη μεταβλητών δεδομένων

ΜεταβλητήΤύπος μεταβλητήςΠεριγραφή
Ageποσοτική (συνεχής)Ηλικία ατόμου
Jobποιοτική (κατηγορική)Κλάδος απασχόλησης ατόμου
Maritalποιοτική (κατηγορική)Οικογενειακή κατάσταση
Educationποιοτική (διατάξιμη)Υψηλότερη βαθμίδα εκπαίδευσης
Defaultποιοτική (κατηγορική)Έχει αθέτηση πιστωτικών υποχρεώσεων;
Balanceποσοτική (συνεχής)Μέσο ετήσιο υπόλοιπο λογαριασμού (σε €)
Housingποιοτική (κατηγορική)Έχει στεγαστικό δάνειο;
Loanποιοτική (κατηγορική)Έχει προσωπικό δάνειο;
Contactποιοτική (κατηγορική)Μέσο επικοινωνίας
Monthποιοτική (διατάξιμη)Μήνας πιο πρόσφατης προσέγγισης
Durationποσοτική (συνεχής)Διάρκεια (σε δευτερόλεπτα) τελευταίας επικοινωνίας
CampaignποσοτικήΑριθμός προσεγγίσεων σε ένα άτομο
pdaysποσοτικήΑριθμός ημερών που μεσολάβησαν από τελευταία ενημέρωση
ppreviousποσοτικήΑριθμός προσεγγίσεων του πελάτη
poutcomeποιοτική (nominal)Αποτέλεσμα προηγούμενης προωθητικής καμπάνιας
Depositποιοτική (nominal)Ο πελάτης άνοιξε προθεσμιακό λογαριασμό;

Το δείγμα μας αποτελείται από 17 μεταβλητές (στήλες), εκ των οποίων οι 7 είναι ποσοτικές και οι υπόλοιπες 10 ποιοτικές. Όσον αφορά τις ποιοτικές μεταβλητές, 8 από αυτές είναι κατηγορικές και μόλις δύο είναι διατάξιμες (μήνας προώθησης και επίπεδο εκπαίδευσης).

Ορισμός συναρτήσεων

Οκ, είδαμε κάποια βασικά στοιχεία των δεδομένων μου και τη δομή αυτών. Μπορώ τώρα να ξεκινήσω την ανάλυσή μου;

Εξαρτάται. Σε περίπτωση που επιθυμούμε μία γρήγορη ανάλυση προκειμένου να εκμαιεύσουμε ένα συγκεκριμένο αποτέλεσμα, πιθανότατα να είναι εντάξει. Βέβαια, τις περισσότερες φορές απαιτείται προσεκτικότερος σχεδιασμός της μελέτης. Ένα συχνό λάθος στο οποίο έχω υποπέσει και εγώ στο παρελθόν είναι ο κίνδυνος της επαναληψιμότητας ορισμένων διαδικασιών. Προκειμένου να αποτρέψουμε να γράφουμε τα ίδια πράγματα πολλές φορές, κρίνεται απαραίτητη η συγγραφή ορισμένων συναρτήσεων.

Κατά συνέπεια, ορίζουμε δύο συναρτήσεις. Αρχικά την univariateQualitativePlot η οποία χρησιμοποιείται για τη δημιουργία κυκλικών διαγραμμάτων και ραβδογραμμάτων για τις ποιοτικές μεταβλητές:

univariateQualitativePlot <- function(data, column, title, subtitle, chart_type = "bar") {

  freq_table <- data %>%
    count({{ column }}, name = "Frequency") %>%
    arrange(desc(Frequency)) %>%
    rename(Variable = {{ column }}) %>%
    mutate(pct = round((Frequency / sum(Frequency) * 100), digits = 1))

  hc <- highchart() %>%
    hc_title(text = title) %>%
    hc_subtitle(text = subtitle) %>%
    hc_tooltip(pointFormat = "{point.name}: {point.y}") %>%
    hc_legend(enabled = chart_type == "pie")

  hc <- if (chart_type == "bar") {
    hc %>%
      hc_chart(type = "bar") %>%
      hc_xAxis(categories = freq_table$Variable, title = list(text = "Κατηγορία")) %>%
      hc_yAxis(title = list(text = "Συχνότητα")) %>%
      hc_series(list(name = "Frequency", data = freq_table$Frequency))
  } else if (chart_type == "pie") {
    pie_data <- lapply(1:nrow(freq_table), function(i) {
      list(name = freq_table$Variable[i], y = freq_table$Frequency[i])
    })
    hc %>%
      hc_chart(type = "pie") %>%
      hc_series(list(name = "Frequency", data = pie_data))
  }

  return(hc)
}

Αντίστοιχα, θα ορίσουμε και τη συνάρτηση univariateQuantitativePlot για την κατασκευή ραβδογραμμάτων για τις ποσοτικές μεταβλητές μας. Και οι δύο συναρτήσεις κατασκευάζουν διαγράμματα χρησιμοποιώντας το πακέτο {highcharter}.

Περιγραφική Ανάλυση

Ελλειπούσες τιμές

Στο δοσμένο σύνολο υπάρχουν συνολικά 0 ελλειπούσες τιμές. Αυτή βέβαια είναι μία σπάνια, ιδανική περίπτωση. Διαφορετικά, θα έπρεπε να γεμίσουμε τις κενές τιμές με κάποια μέθοδο εκτίμησης.

Μονομεταβλητή ανάλυση

Στη συνέχεια είναι σημαντικό να μελετήσουμε τις μεταβλητές μας, τις τιμές τους και τις κατανομές αυτών. Είναι ένα σημαντικό κομμάτι ώστε να κατανοήσουμε το δείγμα και να λάβουμε παραπάνω παραμέτρους υπόψιν μας στην κατασκευή του μοντέλου.

Όσον αφορά τον κλάδο της εργασίας, στο δείγμα παρατηρείται μία σημαντική συμμετοχή ατόμων με εργασίες που πιθανότατα συνδυάζονται με υψηλότερες σπουδές και συνεπακόλουθα υψηλότερες απολαβές, όπως τα διευθυντικά στελέχη, διοικητικοί υπάλληλοι, επιχειρηματίες κτλ. Περίπου το 40% των πελατών της τράπεζας απασχολούνται σε εργασίες «μπλε κολάρου» οι οποίες τις περισσότερες φορές συνδυάζονται με μειωμένη διάθεση δέσμευσης κεφαλαίου. Τέλος, στο πελατολόγιο της τράπεζας υπάρχει ένα ποσοστό περί το 10% που αφορά ομάδες πληθυσμού που για διάφορους λόγους δεν τους συμφέρει να δημιουργήσουν ένα τέτοιο λογαριασμό, όπως οι άνεργοι, οι σπουδαστές, καθώς και οι συνταξιούχοι που με τη σειρά τους θα χρειαστούν να καλύψουν έκτακτες ανάγκες σε παροχές υγείας.

Άλλο ένα διαθέσιμο στοιχείο είναι η οικογενειακή κατάσταση, η οποία μπορεί να συνδέεται με αυξημένες ανάγκες και έξοδα για το νοικοκυριό. Γενικά, δεν είναι ξεκάθαρη εκ των προτέρων η ερμηνεία του συγκεκριμένου δείκτη. Σε κάθε περίπτωση στο υπό εξέταση δείγμα έχουμε περί το 60% των ατόμων που είναι παντρεμένοι, το ένα τέταρτο των πελατών είναι ανύπαντροι και οι υπόλοιποι είναι διαζευγμένοι.

Ένας δείκτης που, τουλάχιστον διαισθητικά, μπορεί να είναι από τους πιο σημαντικούς είναι το ανώτατο επίπεδο εκπαίδευσης του πελάτη. Είναι λογικό κάποιος που έχει υψηλότερου επιπέδου σπουδές να έχει τη δυνατότητα να απασχοληθεί σε εργασίες που απαιτούν εξειδίκευση. Στο συγκεκριμένο σύνολο μόλις το 30% έχει πανεπιστημιακή εκπαίδευση.

Εκτός από τις σπουδές, σημαντικό ρόλο διαδραματίζουν και οι υποχρεώσεις του ατόμου. Αυτές μπορούν να διακριθούν μέσα από τρεις μεταβλητές:

  • αν ο πελάτης έχει οφειλές
  • αν ο πελάτης έχει λάβει στεγαστικό δάνειο
  • αν ο πελάτης έχει λάβει προσωπικό καταναλωτικό δάνειο

Είναι προφανές ότι αν έχει μη εξυπηρετούμενες οφειλές το τελευταίο πράγμα που θα σκεφτεί είναι να κάνει αποταμίευση. Στο δείγμα μόλις 76 άτομα, που αντιστοιχούν στο 1,6% των πελατών, εμπίπτουν σε αυτή την κατηγορία.

Επιπλέον, σημαντικό ποσοστό των πελατών της τράπεζας έχουν ήδη σημαντικές υποχρεώσεις τόσο σε βραχυπρόθεσμο επίπεδο, όσο και μακροπρόθεσμα. Πάνω από τους μισούς έχουν λάβει στεγαστικό δάνειο. Αυτό που ίσως να αποτελεί σημαντικότερο παράγοντα άρνησης είναι η λήψη καταναλωτικών δανείων, τα οποία είναι διαβόητα για τα υψηλά επιτόκια. Στη δική μας περίπτωση περίπου το 15% έχει λάβει καταναλωτικό δάνειο, στοιχείο που είναι ελαφρώς αισιόδοξο.

Ένα άλλο στοιχείο που δίνεται είναι το μέσο επικοινωνίας με τον πελάτη. Πάνω από τους μισούς (64%) έχουν δηλώσει κινητό τηλέφωνο ως μέσο επικοινωνίας.

Άλλη μία ενδιαφέρουσα μεταβλητή είναι ο τελευταίος μήνας στον οποίο προσεγγίστηκε ένας πελάτης. Οι περισσότερες τελευταίες προσεγγίσεις φαίνεται να έγιναν τους καλοκαιρινούς μήνες. Βέβαια αυτό το στοιχείο θέλει προσοχή στην ερμηνεία του.

Σύμφωνα με τα στοιχεία και την περιγραφή αυτών, η τράπεζα είχε τρέξει και σε προηγούμενα χρόνια παρόμοιες καμπάνιες. Ως αποτέλεσμα των προηγούμενων καμπανιών είχαμε 129 ενδιαφερόμενους για κλειστούς λογαριασμούς, ενώ η κατάσταση πολλών είναι άγνωστη.

Οι καταθέτες της τράπεζας είναι κυρίως νεότερης ηλικίας και η συντριπτική πλειοψηφία κάτω των 60 ετών. Το ιστόγραμμα φαίνεται να έχει ένα σχήμα καμπάνας που προσοιδιάζει στην κανονική κατανομή, αλλά υπάρχει μία ελαφριά θετική ασυμμετρία.

Άλλο ένα στοιχείο είναι η διάρκεια της κλήσης. Διαισθητικά, αν η κλήση διαρκεί πολύ λίγο εκτιμάται ότι ο πελάτης δεν ενδιαφέρεται. Σε αντίθετη περίπτωση, μία αυξημένη διάδραση μεταξύ του καλούντος εκ μέρους της εταιρείας και του πελάτη πιθανώς να δηλώνει αυξημένο ενδιαφέρον για το τραπεζικό προϊόν.

Διμεταβλητή ανάλυση

Στην προηγούμενη υπο-ενότητα εξετάστηκαν κάποια βασικά περιγραφικά στοιχεία ανά μεταβλητή. Στην προκειμένη περίπτωση θα είχε επιπλέον όφελος να συγκρίνουμε τις προηγούμενες μεταβλητές με τη μεταβλητή απόκρισης (δηλαδή με την επιθυμία ανοίγματος προθεσμιακού λογαριασμού).

Μία σημαντική σύγκριση είναι ο κλάδος εργασίας του πελάτη με την τελική του απόφαση. Τα δεδομένα δηλώνουν τη χαμηλότερη ποσοστιαία ζήτηση ατόμων με χειρονακτική εργασία, ενώ οι συνταξιούχοι είναι αυτοί με την υψηλότερη.

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

Υπάρχουν όμως και μεταβλητές υπό εξέταση στις οποίες η απάντηση δεν είναι προφανής, όπως στην οικογενειακή κατάσταση. Στο σχήμα, εξετάζεται η σημαντικά μεγαλύτερη αναλογικά συμμετοχή ατόμων που είναι μόνοι τους (είτε ως ανύπαντροι είτε ως διαζευγμένοι).

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

custom_df <-
  tibble(
    r = bank_dataset$education,
    t = bank_dataset$marital,
    m = bank_dataset$y
  )

df_sankey <- custom_df %>%
  group_by(r, t, m) %>%
  summarise(weight = n(), .groups = "drop")

links2 <- df_sankey %>%
  group_by(t, r) %>%
  summarise(weight = sum(weight), .groups = "drop") %>%
  rename(from = t, to = r)

links3 <- df_sankey %>%
  group_by(r, m) %>%
  summarise(weight = sum(weight), .groups = "drop") %>%
  rename(from = r, to = m)

links_all <- bind_rows(links2, links3)

highchart() %>%
  hc_chart(type = "sankey") %>%
  hc_title(text = "Οικογενειακή κατάσταση -> Εκπαίδευση -> Έκβαση") %>%
  hc_add_series(
    keys = c("from", "to", "weight"),
    data = list_parse(links_all),
    name = "Flow"
  ) %>%
  hc_tooltip(pointFormat = "{point.from} → {point.to}: <b>{point.weight}</b>")

Έκρινα σημαντικό να συγκρίνω τη διαφορά μεταξύ των προηγούμενων και της τωρινής καμπάνιας. Αρχικά η τράπεζα διατηρεί μεγάλο μέρος της εμπιστοσύνης των προηγούμενων χρηστών της υπηρεσίας μιας και το 64% αυτών που είχαν συμφωνήσει να ανοίξουν προθεσμιακό λογαριασμό το πράττουν και με τη νέα καμπάνια. Το κρίσιμο δεδομένο σε αυτή τη διμεταβλητή ανάλυση είναι το κατά πόσο έπεισε τους πελάτες που αρνήθηκαν την υπηρεσία. Αυτό το ποσοστό προσεγγίζει το 13%, που είναι μία αρκούντως ικανοποιητική επίδοση.

Κατασκευή μοντέλου

Στην R υπάρχουν δύο διαδεδομένοι τρόποι για τη σύνθεση μοντέλων, το caret και το tidymodels. Από τη μία μεριά, το πακέτο caret είναι αρκετά εύκολο στη χρήση. Από την άλλη μεριά, το tidymodels είναι μία «όλα σε ένα» λύση, αφού αποτελεί ένα μεταπακέτο που προσπαθεί να δώσει μια ολοκληρωμένη λύση, ωστόσο υπάρχει λιγότερη τεκμηρίωση λόγω του ότι έχει δημιουργηθεί πρόσφατα.

Διαχωρισμός συνόλου δεδομένων

Το πρώτο βήμα είναι να χωρίσουμε το αρχικό σύνολο δεδομένων. Στη συγκεκριμένη ανάλυση χρησιμοποιούμε τριμερή διαχωρισμό:

  • Training set (bank_train): χρησιμοποιείται για την εκπαίδευση όλων των μοντέλων και τη διασταυρωμένη επικύρωση.
  • Validation set (bank_val): ένα μικρό τμήμα του training set που κρατάμε κλειστό αποκλειστικά για την επιλογή του βέλτιστου ορίου ταξινόμησης (threshold) του Stack Ensemble.
  • Test set (bank_test): χρησιμοποιείται μόνο για την τελική αξιολόγηση — δεν το αγγίζουμε σε κανένα άλλο στάδιο.
set.seed(123)

# Κύριος διαχωρισμός: 75% train, 25% test
bank_dataset_split <- initial_split(bank_dataset,
                                    prop   = 0.75,
                                    strata = y)
bank_trainval <- training(bank_dataset_split)
bank_test     <- testing(bank_dataset_split)

# Δευτερεύων διαχωρισμός: από το trainval παράγουμε
# bank_train (80%) και bank_val (20%)
set.seed(123)
trainval_split <- initial_split(bank_trainval,
                                prop   = 0.80,
                                strata = y)
bank_train <- training(trainval_split)
bank_val   <- testing(trainval_split)
ΥποσύνολοΠαρατηρήσειςΣκοπός
Πλήρες σύνολο4.521
bank_trainval3.390Εκπαίδευση + επικύρωση
bank_test1.131Τελική αξιολόγηση μόνο
bank_train2.712Εκπαίδευση μοντέλων & cross-validation
bank_val678Επιλογή threshold Stack Ensemble

Το bank_val περιέχει περίπου 678 παρατηρήσεις — αρκετές ώστε να δώσουν αξιόπιστη εκτίμηση του threshold, χωρίς να αφαιρούν σημαντικό μέρος από τα δεδομένα εκπαίδευσης.

Επεξεργασία δεδομένων

Βέβαια η κατασκευή των μοντέλων δεν είναι τόσο εύκολη υπόθεση. Ανάμεσα στο διαχωρισμό του συνόλου δεδομένων και τη σύνθεση των μοντέλων παρεμβάλεται η επεξεργασία των δεδομένων. Ευτυχώς, το πακέτο {tidymodels} προσφέρει έτοιμες εντολές. Υπάρχουν και άλλα πακέτα που συμπληρώνουν συνήθη προβλήματα στο σύνολο δεδομένων μας. Για παράδειγμα, στα δεδομένα μας αναμένεται οι περισσότεροι να μην επιθυμούν να ανοίξουν προθεσμιακό λογαριασμό. Τα δεδομένα μας χαρακτηρίζονται ως ανισόρροπα (imbalanced) όταν η μεταβλητή την οποία προσπαθώ να προβλέψω έχει μεγάλη διαφορά μεταξύ κατηγοριών (90% δεν επιθυμούν / 10%). Σε αυτή την περίπτωση χρησιμοποιούμε την εντολή step_smote() από το πακέτο {themis}.

# Recipe για tree-based μοντέλα (RF, XGBoost, LightGBM)
tree_recipe <- recipe(y ~., data = bank_train) %>%
  step_rm(poutcome, ID, duration) %>%
  step_corr(all_numeric(), threshold = 0.75) %>%
  step_dummy(all_nominal(), -all_outcomes()) %>%
  step_smote(y)

# Recipe για distance/linear μοντέλα (Logistic Regression, KNN)
linear_recipe <- recipe(y ~., data = bank_train) %>%
  step_rm(poutcome, ID, duration) %>%
  step_corr(all_numeric(), threshold = 0.75) %>%
  step_dummy(all_nominal(), -all_outcomes()) %>%
  step_normalize(all_numeric_predictors()) %>%  # επιπλέον βήμα
  step_smote(y)

Αξίζει να σημειωθεί ότι η μεταβλητή duration (διάρκεια κλήσης) αφαιρέθηκε σκοπίμως από τα μοντέλα. Ο λόγος είναι ότι η τιμή αυτής της μεταβλητής γίνεται γνωστή μόνο μετά την ολοκλήρωση της κλήσης, δηλαδή μετά το γεγονός που προσπαθούμε να προβλέψουμε. Αν τη συμπεριλαμβάναμε, θα δημιουργούσαμε πρόβλημα διαρροής δεδομένων (data leakage). Επιπλέον αφαιρέθηκε και η μεταβλητή poutcome, καθώς αφορά προηγούμενες καμπάνιες για τις οποίες δεν υπάρχουν επαρκή δεδομένα για τη μεγάλη πλειοψηφία των πελατών.

Διασταυρωμένη επικύρωση

Οκ, ήρθε η ώρα να κατασκευάσουμε το μοντέλο μας;

Όχι τόσο γρήγορα. Θεωρητικά θα μπορούσαμε να συνεχίσουμε, ωστόσο η ενδεδειγμένη μέθοδος είναι να μην λαμβάνουμε απλώς δύο μέρη, καθώς η περαιτέρω αξιολόγηση βασίζεται ως επί το πλείστον στο πώς έγινε ο διαχωρισμός.

Η διασταυρωμένη επικύρωση (cross-validation) είναι μια τεχνική κατά την οποία το σύνολο εκπαίδευσης χωρίζεται σε k ίσα υποσύνολα (folds). Σε κάθε επανάληψη, ένα fold χρησιμοποιείται ως σύνολο αξιολόγησης και τα υπόλοιπα k-1 ως σύνολο εκπαίδευσης. Στη δική μας περίπτωση, χρησιμοποιήσαμε 5-fold cross-validation με στρωματοποίηση (stratification) ώστε η αναλογία ενδιαφερόμενων / μη ενδιαφερόμενων να διατηρείται σε κάθε fold.

set.seed(123)
cv_folds <- vfold_cv(bank_train, v = 5, strata = y)

ctrl_grid  <- control_stack_grid()
ctrl_bayes <- control_stack_bayes()

Κατασκευάζοντας το μοντέλο

Στη συνέχεια, με το πακέτο {parsnip} έχουμε τη δυνατότητα να ορίσουμε τα χαρακτηριστικά των διάφορων μοντέλων. Αναπτύχθηκαν και συγκρίθηκαν επτά διαφορετικά μοντέλα ταξινόμησης: Λογιστική Παλινδρόμηση, Κοντινότεροι Γείτονες (KNN), Τυχαίο Δάσος (Random Forest), Naive Bayes, SVM, XGBoost και LightGBM.

# --- Logistic Regression ---
log_reg_model <- parsnip::logistic_reg(
  penalty = tune(),
  mixture = tune()
) %>%
  set_engine("glmnet") %>%
  set_mode("classification")

# --- K-Nearest Neighbors ---
knn_model <- parsnip::nearest_neighbor(
  neighbors   = tune(),
  weight_func = tune()
) %>%
  set_engine("kknn") %>%
  set_mode("classification")

# --- Random Forest ---
rf_model <- parsnip::rand_forest(
  trees = 200,
  mtry  = tune(),
  min_n = tune()
) %>%
  set_engine("ranger") %>%
  set_mode("classification")

# --- Naive Bayes ---
nb_model <- naive_Bayes(
  smoothness = tune(),
  Laplace    = tune()
) %>%
  set_engine("naivebayes") %>%
  set_mode("classification")

# --- SVM ---
svm_model <- parsnip::svm_linear(
  cost = tune()
) %>%
  set_engine("kernlab") %>%
  set_mode("classification")

# --- XGBoost ---
xgb_model <- parsnip::boost_tree(
  trees      = 200,
  min_n      = tune(),
  learn_rate = tune(),
  tree_depth = tune()
) %>%
  set_engine("xgboost") %>%
  set_mode("classification")

# --- LightGBM ---
lgbm_model <- parsnip::boost_tree(
  trees      = 200,
  min_n      = tune(),
  learn_rate = tune(),
  tree_depth = tune()
) %>%
  set_engine("lightgbm") %>%
  set_mode("classification")

log_wf  <- workflow() %>% add_recipe(linear_recipe) %>% add_model(log_reg_model)
knn_wf  <- workflow() %>% add_recipe(linear_recipe) %>% add_model(knn_model)
rf_wf   <- workflow() %>% add_recipe(tree_recipe)   %>% add_model(rf_model)
nb_wf   <- workflow() %>% add_recipe(linear_recipe) %>% add_model(nb_model)
svm_wf  <- workflow() %>% add_recipe(linear_recipe) %>% add_model(svm_model)
xgb_wf  <- workflow() %>% add_recipe(tree_recipe)   %>% add_model(xgb_model)
lgbm_wf <- workflow() %>% add_recipe(tree_recipe)   %>% add_model(lgbm_model)

Εφαρμόζοντας τα μοντέλα

Αφού ορίσαμε τα μοντέλα και τις αντίστοιχες ροές εργασίας, προχωρούμε στη βελτιστοποίηση των υπερπαραμέτρων τους. Για τα απλούστερα μοντέλα χρησιμοποιούμε αναζήτηση πλέγματος (tune_grid()). Για τα μοντέλα gradient boosting (XGBoost και LightGBM), επιλέγω Μπεϋζιανή βελτιστοποίηση (tune_bayes()), η οποία εξοικονομεί χρόνο σε σχέση με την εξαντλητική αναζήτηση.

set.seed(123)

log_results <- tune_grid(
  log_wf, resamples = cv_folds, grid = 10,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_grid
)

knn_results <- tune_grid(
  knn_wf, resamples = cv_folds, grid = 10,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_grid
)

rf_results <- tune_grid(
  rf_wf, resamples = cv_folds, grid = 15,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_grid
)

nb_results <- tune_grid(
  nb_wf, resamples = cv_folds, grid = 10,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_grid
)

svm_results <- tune_grid(
  svm_wf, resamples = cv_folds, grid = 10,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_grid
)

# XGBoost & LightGBM — tune_bayes
xgb_results <- tune_bayes(
  xgb_wf, resamples = cv_folds, initial = 5, iter = 25,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_bayes
)

lgbm_results <- tune_bayes(
  lgbm_wf, resamples = cv_folds, initial = 5, iter = 25,
  metrics = metric_set(roc_auc, accuracy), control = ctrl_bayes
)

Πέρα από τη μεμονωμένη αξιολόγηση κάθε μοντέλου, εφαρμόζουμε και μια τεχνική που ονομάζεται stacking. Η ιδέα πίσω από το stacking είναι ότι αντί να επιλέξουμε ένα μόνο μοντέλο ως τελικό, συνδυάζουμε τις προβλέψεις πολλών μοντέλων σε ένα μετα-μοντέλο. Χρησιμοποιώ το πακέτο {stacks}:

bank_stack_v1 <- stacks() %>%
  add_candidates(log_results)  %>%
  add_candidates(knn_results)  %>%
  add_candidates(rf_results)   %>%
  add_candidates(xgb_results)  %>%
  add_candidates(lgbm_results)

bank_stack_v2 <- bank_stack_v1 %>%
  add_candidates(nb_results) %>%
  add_candidates(svm_results)

set.seed(123)
bank_stack_model_v1 <- bank_stack_v1 %>%
  blend_predictions(penalty = 10^(-2:0), metric = metric_set(roc_auc))

bank_stack_model_v2 <- bank_stack_v2 %>%
  blend_predictions(penalty = 10^(-2:0), metric = metric_set(roc_auc))

bank_stack_fit_v1 <- bank_stack_model_v1 %>% fit_members()
bank_stack_fit_v2 <- bank_stack_model_v2 %>% fit_members()

Σημείωση για το threshold του Stack Ensemble

Σε αντίθεση με τα μεμονωμένα μοντέλα (LightGBM, XGBoost, Λογιστική Παλινδρόμηση) για τα οποία μπορούμε να αντλήσουμε out-of-fold προβλέψεις από τη διασταυρωμένη επικύρωση, το Stack Ensemble δεν διαθέτει αντίστοιχες προβλέψεις. Για τον λόγο αυτό δημιουργήσαμε νωρίτερα το bank_val που αποτελεί ένα υποσύνολο του train set, που το μοντέλο δεν έχει δει κατά την εκπαίδευση, και χρησιμοποιείται αποκλειστικά για την επιλογή του threshold.

Επιλογή threshold

Κάθε μοντέλο παράγει για κάθε πελάτη μία πιθανότητα ενδιαφέροντος — όχι απευθείας μία απόφαση. Για να περάσουμε από την πιθανότητα στην ταξινόμηση («ναι» / «όχι») χρειαζόμαστε ένα όριο (threshold): αν η πιθανότητα ξεπεράσει αυτό το όριο, ο πελάτης κατατάσσεται ως ενδιαφερόμενος.

Η προεπιλεγμένη τιμή 0.5 σπάνια είναι η βέλτιστη επιλογή σε ανισόρροπα δεδομένα — στη δική μας περίπτωση μόλις το 11% των πελατών ανήκει στην κατηγορία «ναι». Αντί αυτού, επιλέγουμε το threshold που μεγιστοποιεί το F1, το οποίο ισορροπεί την ικανότητα εντοπισμού ενδιαφερόμενων (recall) με την αξιοπιστία των θετικών προβλέψεων (precision).

Ένα συνηθισμένο λάθος είναι να βρίσκει κανείς το threshold πάνω στο ίδιο test set που χρησιμοποιεί για την τελική αξιολόγηση. Για να αποφύγουμε αυτό ακολουθούμε διαφορετική προσέγγιση ανάλογα με το μοντέλο:

  • Για τα individual μοντέλα χρησιμοποιούμε τις out-of-fold (OOF) προβλέψεις από τη διασταυρωμένη επικύρωση.
  • Για το Stack Ensemble χρησιμοποιούμε το bank_val.

Αξίζει να σημειωθεί ότι τα thresholds διαφέρουν σημαντικά μεταξύ των μοντέλων. Αυτό δεν σημαίνει ότι κάποιο μοντέλο είναι «λάθος» — απλώς κάθε μοντέλο βαθμονομεί διαφορετικά τις πιθανότητές του. Το σημαντικό είναι ότι κάθε threshold βρέθηκε σε δεδομένα που το αντίστοιχο μοντέλο δεν είχε χρησιμοποιήσει κατά την εκπαίδευση.

Αποτελέσματα

Σπουδαιότητα μεταβλητών

Από την ανάλυση σπουδαιότητας μεταβλητών του μοντέλου LightGBM προκύπτει ότι οι πιο καθοριστικοί παράγοντες για την πρόβλεψη ενδιαφέροντος σε προθεσμιακό λογαριασμό είναι η ύπαρξη στεγαστικού δανείου (15,3%) και ο άγνωστος τρόπος επικοινωνίας (14,0%), με την οικογενειακή κατάσταση «παντρεμένος» να ακολουθεί (11,7%). Αξιοσημείωτη είναι και η συμβολή του μήνα επικοινωνίας, με τον Μάιο να εμφανίζεται ως ο πιο σημαντικός μήνας (8,2%), ενώ το επίπεδο εκπαίδευσης συμμετέχει επίσης με αξιόλογο βάρος. Τα αποτελέσματα αυτά συνάδουν σε μεγάλο βαθμό με τα ευρήματα της περιγραφικής ανάλυσης.

ΜεταβλητήΣπουδαιότητα (%)
Στεγαστικό δάνειο15,3
Άγνωστος τρόπος επικοινωνίας14,0
Έγγαμος11,7
Μήνας: Μάιος8,2
Τριτοβάθμια εκπαίδευση7,1
Δευτεροβάθμια εκπαίδευση6,4
Μήνας: Ιούλιος5,8
Μήνας: Αύγουστος5,3
Επάγγελμα: Χειρονακτική εργασία4,9
Μήνας: Νοέμβριος4,6

Σύγκριση μοντέλων

Πριν προχωρήσουμε στην τελική αξιολόγηση, αξίζει να συγκρίνουμε γραφικά την προβλεπτική ικανότητα των κορυφαίων μοντέλων με τη βοήθεια των ROC καμπυλών (Receiver Operating Characteristic curves). Η ROC καμπύλη απεικονίζει την ευαισθησία (sensitivity) έναντι του ποσοστού ψευδών θετικών, σε όλα τα δυνατά threshold. Όσο πιο κοντά στην επάνω αριστερή γωνία βρίσκεται η καμπύλη, τόσο καλύτερη η συνολική προβλεπτική ικανότητα. Από το διάγραμμα φαίνεται καθαρά η υπεροχή των συνδυαστικών μοντέλων (Stack Ensemble) έναντι των άλλων.

Συνολικά, παρατηρούμε ότι η ακρίβεια (accuracy) από μόνη της παραπλανά. Το XGBoost που εμφανίζει μία από τις κορυφαίες τιμές ακρίβειας, έχει σχεδόν μηδενικό F1. Αυτό συμβαίνει γιατί η ακρίβεια «ανταμείβει» κυρίως τις σωστές αρνητικές προβλέψεις και στα ανισόρροπα δεδομένα μας, το να λέμε «όχι» στη συντριπτική πλειοψηφία είναι εύκολο. Το F1 είναι το πιο έντιμο κριτήριο εδώ, γιατί τιμωρεί εξίσου τα ψευδώς θετικά και τα ψευδώς αρνητικά. Αξίζει επίσης να σημειώσουμε την ευαισθησία (sensitivity) του LightGBM: εντοπίζει το 51% των πραγματικά ενδιαφερόμενων πελατών, ποσοστό που παραδόξως δεν προσεγγίζεται ικανοποιητικά από τα συνδυαστικά μοντέλα.

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

Ο δεύτερος αυτός πίνακας μεταφράζει τα στατιστικά μεγέθη σε επιχειρησιακή πραγματικότητα και αναδεικνύει μια διαφορετική ιεράρχηση. Το Stack Ensemble v1 εμφανίζεται ως η πιο αποδοτική επιλογή: με μόλις 127 κλήσεις επιτυγχάνει επιτυχία ανά κλήση 40,2%, δηλαδή 4 στις 10 επαφές αποδίδουν. Αν ο πόρος που δαπανάται είναι ο χρόνος του call center, αυτό το μοντέλο σέβεται περισσότερο αυτόν τον πόρο. Το τίμημα όμως είναι 80 χαμένοι ενδιαφερόμενοι. Από τους 131 εντοπίσαμε τους 51.

Παραδόξως, το δεύτερο συνδυαστικό μοντέλο (Stack Ensemble v2) δεν έχει ξεπεράσει σε απόδοση το πρώτο, παρά το γεγονός ότι είναι συνδυασμός επτά μεμονωμένων μοντέλων, έναντι πέντε του v1. Καταφέρνει ωστόσο να μας δώσει έναν μικρότερο αριθμό προσεγγίσεων. Το ατού του v2 είναι ότι κάνει τις λιγότερες λάθος προσεγγίσεις από τα κορυφαία μοντέλα.

Το LightGBM, από την άλλη, πιάνει τους 67 ενδιαφερόμενους — τους περισσότερους από όλα τα μοντέλα — αλλά απαιτεί 204 κλήσεις για να το κάνει, με ποσοστό επιτυχίας 32,8%. Χρειάζεται δηλαδή σχεδόν 60% περισσότερες κλήσεις από το Stack Ensemble v1 για να κερδίσει 16 επιπλέον θετικές περιπτώσεις. Επίσης, ενδιαφέρον παρουσιάζουν τα αποτελέσματα του Naive Bayes, που είναι αρκετά κοντά στην απόδοση των συνδυαστικών μοντέλων. Τέλος, η απόλυτη έκπληξη ήταν τα αποτελέσματα του XGBoost με μόλις 3 σωστές προβλέψεις σε 131 πραγματικά θετικές περιπτώσεις.

ΜοντέλοΣωστές θετικές (TP)Λάθος θετικές (FP)Χαμένοι ενδ. (FN)Σύνολο κλήσεωνΕπιτυχία ανά κλήση (%)
Stack Ensemble (v1)51768012740,2
Stack Ensemble (v2)
LightGBM671376420432,8
Naive Bayes
Random Forest
Logistic Regression
SVM
XGBoost3

Από την παραπάνω ανάλυση γίνεται ξεκάθαρο ότι δεν υπάρχει το «σωστό» ή το καλύτερο μοντέλο, αλλά αυτό που καλύπτει τον σκοπό μας. Αν ο στόχος της τράπεζας ήταν να μεγιστοποιήσει τα κέρδη της προωθώντας ένα νέο προϊόν, τότε η απάντηση είναι ξεκάθαρη: LightGBM, αφού εντοπίζει τους περισσότερους ενδιαφερόμενους. Θα μπορούσαν όμως οι στόχοι να είναι πιο συντηρητικοί — για παράδειγμα, να επεκτείνουμε τις υπηρεσίες στους υφιστάμενους πελάτες μας αλλά να οχλήσουμε όσο το δυνατόν λιγότερους που δεν ενδιαφέρονται. Σε μία τέτοια περίπτωση το Stack Ensemble v2 είναι ιδανικό, αφού οι λάθος οχλήσεις είναι 21 λιγότερες. Βέβαια, αν θέλαμε μία πιο ισορροπημένη πρακτική ανάμεσα στις απαιτούμενες προσεγγίσεις και στα αναμενόμενα αποτελέσματα της καμπάνιας, ο νικητής είναι ξεκάθαρα το Stack Ensemble v1.

Εν κατακλείδι, το ιδανικό μοντέλο δεν καθορίζεται αποκλειστικά από τις ιδανικές παραμέτρους, αλλά από την ίδια την ερώτηση και τον σκοπό του οργανισμού μας.