ΕΙΣΑΓΩΓΗ

Στην παρούσα εργασία, αναπτύσσουμε ένα μοντέλο Δέντρων Απόφασης για την πρόβλεψη του ρόλου (Role) χαρακτήρων της Marvel (Hero, Antihero, Villain) από το dataset marvel_characters_dataset.csv, το οποίο περιλαμβάνει 45 χαρακτήρες με πληροφορίες για την ομάδα (Affiliation), τις δυνάμεις (Powers) και το επίπεδο ισχύος (Power.Level). Ακολουθούμε τα τέσσερα βήματα της μηχανικής μάθησης: προετοιμασία δεδομένων, εκπαίδευση μοντέλου, αξιολόγηση και σύγκριση με το μοντέλο Λογιστικής Παλινδρόμησης της ΕΑ-006. Στόχος είναι η αξιολόγηση της απόδοσης των δύο μοντέλων στην πρόβλεψη του ρόλου.

ΦΟΡΤΩΣΗ ΒΙΒΛΙΟΘΗΚΩΝ ΚΑΙ ΔΕΔΟΜΕΝΩΝ

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

# Εγκατάσταση πακέτων αν δεν είναι ήδη εγκατεστημένα
if (!requireNamespace("dplyr", quietly = TRUE)) install.packages("dplyr", repos = "https://cran.r-project.org")
if (!requireNamespace("caTools", quietly = TRUE)) install.packages("caTools", repos = "https://cran.r-project.org")
if (!requireNamespace("rpart", quietly = TRUE)) install.packages("rpart", repos = "https://cran.r-project.org")
if (!requireNamespace("rpart.plot", quietly = TRUE)) install.packages("rpart.plot", repos = "https://cran.r-project.org")

# Φόρτωση βιβλιοθηκών
library(dplyr)        # Για χειρισμό δεδομένων
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(caTools)      # Για διαχωρισμό train/test
library(rpart)        # Για Δέντρα Απόφασης
library(rpart.plot)   # Για οπτικοποίηση Δέντρων

# Φόρτωση dataset
data <- read.csv("marvel_characters_dataset.csv")

# Επισκόπηση dataset
str(data)
## 'data.frame':    45 obs. of  6 variables:
##  $ Character  : chr  "iron man" "captain america" "thor" "black widow" ...
##  $ Real.Name  : chr  "Tony Stark" "Steve Rogers" "Thor Odinson" "Natasha Romanoff" ...
##  $ Affiliation: chr  "Avengers" "Avengers" "Avengers" "Avengers" ...
##  $ Powers     : chr  "Powered Armor, Genius-level intellect" "Super Soldier, Enhanced strength" "God of Thunder, Weather manipulation" "Superhuman strength, Espionage" ...
##  $ Role       : chr  "Hero" "Hero" "Hero" "Hero" ...
##  $ Power.Level: chr  "Low" "Low" "Low" "Low" ...
summary(data)
##   Character          Real.Name         Affiliation           Powers         
##  Length:45          Length:45          Length:45          Length:45         
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##      Role           Power.Level       
##  Length:45          Length:45         
##  Class :character   Class :character  
##  Mode  :character   Mode  :character

ΠΡΟΕΤΟΙΜΑΣΙΑ ΔΕΔΟΜΕΝΩΝ

Εδώ προετοιμάζουμε το dataset για την εκπαίδευση του μοντέλου. Μετατρέπουμε τις κατηγορικές μεταβλητές σε factors, αφαιρούμε περιττές στήλες (Character, Real.Name, Power.Level), και διαχωρίζουμε το dataset σε train (65%) και test (35%) set. Ελέγχουμε επίσης τα επίπεδα των κατηγορικών μεταβλητών για να αποφύγουμε προβλήματα.

# Ορισμός seed για αναπαραγωγιμότητα
set.seed(923)  # Αν το seed σου είναι διαφορετικό, αντικατέστησέ το

# Μετατροπή κατηγορικών μεταβλητών σε factors
data$Affiliation <- as.factor(data$Affiliation)
data$Powers <- as.factor(data$Powers)
data$Role <- as.factor(data$Role)
data$Power.Level <- as.factor(data$Power.Level)

# Αφαίρεση περιττών στηλών
data <- data %>% select(-Character, -Real.Name, -Power.Level)

# Διαχωρισμός σε train (65%) και test (35%)
split <- sample.split(data$Role, SplitRatio = 0.65)
train <- subset(data, split == TRUE)
test <- subset(data, split == FALSE)

# Ενημέρωση επιπέδων για να αποφευχθούν προβλήματα με νέα επίπεδα
train$Affiliation <- droplevels(train$Affiliation)
train$Powers <- droplevels(train$Powers)
test$Affiliation <- factor(test$Affiliation, levels = levels(train$Affiliation))
test$Powers <- factor(test$Powers, levels = levels(train$Powers))

# Έλεγχος επιπέδων κατηγορικών μεταβλητών
cat("ΕΠΙΠΕΔΑ AFFILIATION ΣΤΟ TRAIN SET:", levels(train$Affiliation), "\n")
## ΕΠΙΠΕΔΑ AFFILIATION ΣΤΟ TRAIN SET: Avengers Defenders Fantastic Four Guardians of the Galaxy Inhumans Sinister Six Villain X-Force X-Men
cat("ΕΠΙΠΕΔΑ AFFILIATION ΣΤΟ TEST SET:", levels(test$Affiliation), "\n")
## ΕΠΙΠΕΔΑ AFFILIATION ΣΤΟ TEST SET: Avengers Defenders Fantastic Four Guardians of the Galaxy Inhumans Sinister Six Villain X-Force X-Men
cat("ΕΠΙΠΕΔΑ POWERS ΣΤΟ TRAIN SET:", levels(train$Powers), "\n")
## ΕΠΙΠΕΔΑ POWERS ΣΤΟ TRAIN SET: Aerial combat, Shield mastery Chi manipulation, Martial arts expert Cybernetic enhancements Elasticity, Superhuman strength Enhanced senses, Superhuman strength Expert marksmanship, Advanced technology Flight, Superhuman strength, Cosmic energy God of Thunder, Weather manipulation Invisibility, Force field generation Magical powers Powered Armor, Genius-level intellect Regeneration, Adamantium claws Regeneration, Skilled hand-to-hand combatant Size manipulation, Ant communication Skilled combatant, Enhanced reflexes Skilled combatant, Weapon mastery Sorcery, Magic Super Soldier, Enhanced strength Superhuman strength, Agility Superhuman strength, Durability Superhuman strength, Espionage Superhuman strength, Expert fighter Superhuman strength, Gamma Radiation Superhuman strength, Regeneration Superhuman strength, Tentacle control Superhuman strength, Vibranium suit Symbiote bonding, Enhanced strength Telekinesis, Reality warping
cat("ΕΠΙΠΕΔΑ POWERS ΣΤΟ TEST SET:", levels(test$Powers), "\n")
## ΕΠΙΠΕΔΑ POWERS ΣΤΟ TEST SET: Aerial combat, Shield mastery Chi manipulation, Martial arts expert Cybernetic enhancements Elasticity, Superhuman strength Enhanced senses, Superhuman strength Expert marksmanship, Advanced technology Flight, Superhuman strength, Cosmic energy God of Thunder, Weather manipulation Invisibility, Force field generation Magical powers Powered Armor, Genius-level intellect Regeneration, Adamantium claws Regeneration, Skilled hand-to-hand combatant Size manipulation, Ant communication Skilled combatant, Enhanced reflexes Skilled combatant, Weapon mastery Sorcery, Magic Super Soldier, Enhanced strength Superhuman strength, Agility Superhuman strength, Durability Superhuman strength, Espionage Superhuman strength, Expert fighter Superhuman strength, Gamma Radiation Superhuman strength, Regeneration Superhuman strength, Tentacle control Superhuman strength, Vibranium suit Symbiote bonding, Enhanced strength Telekinesis, Reality warping
cat("ΕΠΙΠΕΔΑ ROLE ΣΤΟ TRAIN SET:", levels(train$Role), "\n")
## ΕΠΙΠΕΔΑ ROLE ΣΤΟ TRAIN SET: Antihero Hero Villain
# Καταγραφή αριθμού καταχωρήσεων
cat("ΑΡΙΘΜΟΣ ΚΑΤΑΧΩΡΗΣΕΩΝ ΣΤΟ TRAIN SET:", nrow(train), "\n")
## ΑΡΙΘΜΟΣ ΚΑΤΑΧΩΡΗΣΕΩΝ ΣΤΟ TRAIN SET: 29
cat("ΑΡΙΘΜΟΣ ΚΑΤΑΧΩΡΗΣΕΩΝ ΣΤΟ TEST SET:", nrow(test), "\n")
## ΑΡΙΘΜΟΣ ΚΑΤΑΧΩΡΗΣΕΩΝ ΣΤΟ TEST SET: 16

ΕΚΠΑΙΔΕΥΣΗ ΜΟΝΤΕΛΟΥ ΔΕΝΤΡΩΝ ΑΠΟΦΑΣΗΣ

Σε αυτό το βήμα, εκπαιδεύουμε ένα μοντέλο Δέντρων Απόφασης χρησιμοποιώντας τη βιβλιοθήκη rpart για την πρόβλεψη της μεταβλητής Role. Οπτικοποιούμε το δέντρο για να κατανοήσουμε τη δομή του.

# Δημιουργία μοντέλου
tree_model <- rpart(Role ~ ., data = train, method = "class")

# Οπτικοποίηση του δέντρου
rpart.plot(tree_model, main = "ΔΕΝΤΡΟ ΑΠΟΦΑΣΗΣ ΓΙΑ ΠΡΟΒΛΕΨΗ ΡΟΛΟΥ")

ΠΡΟΒΛΕΨΕΙΣ ΚΑΙ ΑΞΙΟΛΟΓΗΣΗ

Εδώ κάνουμε προβλέψεις στο test set με το μοντέλο Δέντρων Απόφασης και αξιολογούμε την απόδοσή του χρησιμοποιώντας το confusion matrix και μετρικές όπως ακρίβεια, sensitivity και specificity.

# Προβλέψεις στο test set
predictTest <- predict(tree_model, test, type = "class")

# Δημιουργία confusion matrix
conf_matrix <- table(Predicted = predictTest, Actual = test$Role)
print(conf_matrix)
##           Actual
## Predicted  Antihero Hero Villain
##   Antihero        0    0       0
##   Hero            0   10       2
##   Villain         1    2       1
# Υπολογισμός ακρίβειας
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)

# Υπολογισμός sensitivity και specificity για κάθε κατηγορία
classes <- levels(test$Role)
sensitivity <- numeric(length(classes))
specificity <- numeric(length(classes))
for (i in seq_along(classes)) {
  true_pos <- conf_matrix[i, i]
  false_neg <- sum(conf_matrix[, i]) - true_pos
  false_pos <- sum(conf_matrix[i, ]) - true_pos
  true_neg <- sum(conf_matrix) - true_pos - false_neg - false_pos
  
  sensitivity[i] <- ifelse(true_pos + false_neg > 0, true_pos / (true_pos + false_neg), 0)
  specificity[i] <- ifelse(true_neg + false_pos > 0, true_neg / (true_neg + false_pos), 0)
}
names(sensitivity) <- classes
names(specificity) <- classes

cat("ΑΚΡΙΒΕΙΑ ΜΟΝΤΕΛΟΥ ΔΕΝΤΡΩΝ ΑΠΟΦΑΣΗΣ:", round(accuracy, 3), "\n")
## ΑΚΡΙΒΕΙΑ ΜΟΝΤΕΛΟΥ ΔΕΝΤΡΩΝ ΑΠΟΦΑΣΗΣ: 0.688
cat("SENSITIVITY ΑΝΑ ΚΑΤΗΓΟΡΙΑ:\n", round(sensitivity, 3), "\n")
## SENSITIVITY ΑΝΑ ΚΑΤΗΓΟΡΙΑ:
##  0 0.833 0.333
cat("SPECIFICITY ΑΝΑ ΚΑΤΗΓΟΡΙΑ:\n", round(specificity, 3), "\n")
## SPECIFICITY ΑΝΑ ΚΑΤΗΓΟΡΙΑ:
##  1 0.5 0.769

ΣΥΓΚΡΙΣΗ ΜΕ ΛΟΓΙΣΤΙΚΗ ΠΑΛΙΝΔΡΟΜΗΣΗ

Συγκρίνουμε το μοντέλο Δέντρων Απόφασης με τη Λογιστική Παλινδρόμηση, όπως στην ΕΑ-006. Η Λογιστική Παλινδρόμηση απαιτεί δυαδική εξαρτημένη μεταβλητή, οπότε μετατρέπουμε το Role σε Hero vs. Non-Hero. Φιλτράρουμε το test set για να αποφύγουμε προβλήματα με νέα επίπεδα (π.χ., Hydra).

# Εκπαίδευση μοντέλου Λογιστικής Παλινδρόμησης
train$Role_binary <- ifelse(train$Role == "Hero", 1, 0)
test$Role_binary <- ifelse(test$Role == "Hero", 1, 0)

# Φιλτράρισμα του test set για να περιλαμβάνει μόνο γνωστά επίπεδα Affiliation
test_filtered <- test[!is.na(test$Affiliation), ]

# Έλεγχος αν το test_filtered είναι κενό
if (nrow(test_filtered) == 0) {
  cat("ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Το test set δεν περιέχει έγκυρα επίπεδα για το Affiliation. Παραλείπεται η Λογιστική Παλινδρόμηση.\n")
  log_accuracy <- NA
  log_sensitivity <- NA
  log_specificity <- NA
} else {
  # Έλεγχος επιπέδων στο train set
  if (nlevels(droplevels(train$Affiliation)) < 2) {
    stop("Η μεταβλητή Affiliation έχει λιγότερα από 2 επίπεδα στο train set.")
  }
  if (nlevels(droplevels(train$Powers)) < 2) {
    stop("Η μεταβλητή Powers έχει λιγότερα από 2 επίπεδα στο train set.")
  }

  log_model <- glm(Role_binary ~ Affiliation + Powers, 
                   data = train, family = binomial())

  # Προβλέψεις
  log_predict <- predict(log_model, test_filtered, type = "response")
  log_predict_class <- ifelse(log_predict > 0.5, 1, 0)

  # Confusion Matrix για Λογιστική Παλινδρόμηση
  log_conf_matrix <- table(Predicted = log_predict_class, Actual = test_filtered$Role_binary)
  print(log_conf_matrix)

  # Υπολογισμός ακρίβειας
  log_accuracy <- sum(diag(log_conf_matrix)) / sum(log_conf_matrix)

  # Υπολογισμός sensitivity και specificity
  true_pos <- log_conf_matrix[2, 2]
  false_neg <- sum(log_conf_matrix[, 2]) - true_pos
  false_pos <- sum(log_conf_matrix[2, ]) - true_pos
  true_neg <- sum(log_conf_matrix) - true_pos - false_neg - false_pos

  log_sensitivity <- ifelse(true_pos + false_neg > 0, true_pos / (true_pos + false_neg), 0)
  log_specificity <- ifelse(true_neg + false_pos > 0, true_neg / (true_neg + false_pos), 0)
}
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
##          Actual
## Predicted 0 1
##         0 1 1
##         1 1 1
cat("ΑΚΡΙΒΕΙΑ ΜΟΝΤΕΛΟΥ ΛΟΓΙΣΤΙΚΗΣ ΠΑΛΙΝΔΡΟΜΗΣΗΣ:", round(log_accuracy, 3), "\n")
## ΑΚΡΙΒΕΙΑ ΜΟΝΤΕΛΟΥ ΛΟΓΙΣΤΙΚΗΣ ΠΑΛΙΝΔΡΟΜΗΣΗΣ: 0.5
cat("SENSITIVITY (HERO):", round(log_sensitivity, 3), "\n")
## SENSITIVITY (HERO): 0.5
cat("SPECIFICITY (HERO):", round(log_specificity, 3), "\n")
## SPECIFICITY (HERO): 0.5

ΔΙΑΓΡΑΜΜΑ ΣΥΓΚΡΙΣΗΣ ΑΚΡΙΒΕΙΑΣ

Δημιουργούμε ένα διάγραμμα ράβδων για να συγκρίνουμε την ακρίβεια των δύο μοντέλων (Δέντρα Απόφασης και Λογιστική Παλινδρόμηση).

# Δημιουργία πλαισίου δεδομένων για το διάγραμμα
accuracy_data <- data.frame(
  Model = c("Δέντρο Απόφασης", "Λογιστική Παλινδρόμηση"),
  Accuracy = c(accuracy, log_accuracy)
)

# Αν η Λογιστική Παλινδρόμηση απέτυχε, αντικατάσταση NA με 0 για το διάγραμμα
accuracy_data$Accuracy[is.na(accuracy_data$Accuracy)] <- 0

# Δημιουργία bar chart
barplot(accuracy_data$Accuracy, 
        names.arg = accuracy_data$Model, 
        main = "ΣΥΓΚΡΙΣΗ ΑΚΡΙΒΕΙΑΣ ΜΟΝΤΕΛΩΝ",
        xlab = "ΜΟΝΤΕΛΟ", 
        ylab = "ΑΚΡΙΒΕΙΑ",
        col = c("#1b9e77", "#d95f02"),
        ylim = c(0, 1))

ΣΥΜΠΕΡΑΣΜΑΤΑ

Το μοντέλο Δέντρων Απόφασης πέτυχε ακρίβεια [εισαγωγή τιμής από αποτελέσματα], ενώ το μοντέλο Λογιστικής Παλινδρόμησης πέτυχε ακρίβεια [εισαγωγή τιμής από αποτελέσματα ή NA αν απέτυχε]. Το Δέντρο Απόφασης αποδίδει καλύτερα, καθώς μπορεί να χειριστεί την πολυκατηγορική μεταβλητή Role (Hero, Antihero, Villain) και να μοντελοποιήσει μη γραμμικές σχέσεις, σε αντίθεση με τη Λογιστική Παλινδρόμηση που περιορίζεται σε δυαδική ταξινόμηση (Hero vs. Non-Hero). Η στήλη Power.Level αφαιρέθηκε, καθώς περιείχε μόνο μία τιμή (Low), προσφέροντας μηδενική πληροφορία. Το μικρό μέγεθος του dataset (45 καταχωρήσεις) και η παρουσία κατηγοριών όπως το Hydra μόνο στο test set περιόρισαν την απόδοση της Λογιστικής Παλινδρόμησης, καθώς ορισμένες προβλέψεις δεν ήταν δυνατές. Προτείνεται η χρήση μεγαλύτερου dataset ή η εφαρμογή τεχνικών όπως το pruning για τη βελτίωση του Δέντρου Απόφασης. ```