{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Decision Trees - Titanic dataset.ipynb","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"markdown","metadata":{"id":"QAbOplVzljB1"},"source":["# Δέντρα αποφάσεων\n","\n","Αν χρειάστηκε ποτέ να διαγνώσετε ένα πρόβλημα με μια συσκευή, ένα αυτοκίνητο ή έναν υπολογιστή, υπάρχει μεγάλη πιθανότητα να έχετε συναντήσει ένα διάγραμμα ροής αντιμετώπισης προβλημάτων. Ένα διάγραμμα ροής είναι μια δενδροειδής δομή ερωτήσεων ναι/όχι που σας καθοδηγεί μέσω κάποιας διαδικασίας με βάση τη συγκεκριμένη κατάσταση. \n","\n","Ένα δέντρο αποφάσεων είναι ουσιαστικά ένα διάγραμμα ροής για τη λήψη απόφασης σχετικά με τον τρόπο ταξινόμησης μιας παρατήρησης: αποτελείται από μια σειρά αποφάσεων ναι/όχι ή αν/αν, οι οποίες τελικά αντιστοιχίζουν κάθε παρατήρηση σε μια συγκεκριμένη πιθανότητα ή κλάση. Η σειρά των αποφάσεων ναι/όχι μπορεί να απεικονιστεί ως μια σειρά από κλαδιά που οδηγούν σε αποφάσεις ή \"φύλλα\" στο κάτω μέρος του δέντρου.\n","\n","## Titanic dataset\n","![](https://miro.medium.com/max/600/0*CBDzZWhzCjBY2LhS.jpeg)\n","\n","Ας δημιουργήσουμε το μοντέλο με βάση το φύλο στο [Titanic training set](https://www.kaggle.com/c/titanic/data) χρησιμοποιώντας δέντρα απόφασης στην Python. Πρώτα θα φορτώσουμε κάποιες βιβλιοθήκες και θα προεπεξεργαστούμε τα δεδομένα του Τιτανικού:"]},{"cell_type":"code","metadata":{"id":"rWXCpos7rrrT"},"source":["import numpy as np\n","import pandas as pd"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"bmAoi2WlsbMS"},"source":["# Load and prepare Titanic data\n","\n","titanic_train = pd.read_csv(\"https://pastebin.com/raw/phdcr5FY\") # Read the training data\n","\n"," \n","# Impute median Age for NA Age values\n","new_age_var = np.where(titanic_train[\"Age\"].isnull(), # Logical check\n"," 28, # Value if check is true\n"," titanic_train[\"Age\"]) # Value if check is false\n","\n","titanic_train[\"Age\"] = new_age_var "],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"gWsRRpg-sxZ3"},"source":["titanic_train.head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"3uRF3-nas0y5"},"source":["Στη συνέχεια, πρέπει να φορτώσουμε και να αρχικοποιήσουμε το μοντέλο δέντρου αποφάσεων του scikit-learn και στη συνέχεια να εκπαιδεύσουμε το μοντέλο χρησιμοποιώντας τη μεταβλητή sex:"]},{"cell_type":"markdown","metadata":{"id":"HcyeQ_WjtcbG"},"source":["## Decision Tree classifier 1"]},{"cell_type":"code","metadata":{"id":"G8fGbtbitASE"},"source":["from sklearn import tree\n","from sklearn import preprocessing"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"qs5GxazItE_g"},"source":["# Initialize label encoder\n","label_encoder = preprocessing.LabelEncoder()\n","\n","# Convert Sex variable to numeric\n","encoded_sex = label_encoder.fit_transform(titanic_train[\"Sex\"])\n","\n","# Initialize model\n","tree_model = tree.DecisionTreeClassifier()\n","\n","# Train the model\n","tree_model.fit(X = pd.DataFrame(encoded_sex), \n"," y = titanic_train[\"Survived\"])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"x-nAcYXBtR47"},"source":["Σημειώστε τις default τιμές των παραμέτρων στο παραπάνω μοντέλο. Διαβάστε περισσότερα για αυτες [εδώ](http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html).\n","\n","Τώρα ας δούμε μια οπτικοποίηση του δέντρου που δημιούργησε το μοντέλο:."]},{"cell_type":"code","metadata":{"id":"FwKXVAZZtu8Q"},"source":["# Save tree as dot file\n","with open(\"tree1.dot\", 'w') as f:\n"," f = tree.export_graphviz(tree_model, \n"," feature_names=[\"Sex\"], \n"," out_file=f)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"JOGckhYSuANJ"},"source":["!apt update && apt install -y graphviz"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"kHaslUJ01MJB"},"source":["!dot -Tpng tree1.dot -o tree1.png -Gdpi=100"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"IuKDkwvAdcd2"},"source":["!ls"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"4ZU4lryKyubZ"},"source":["# Display in jupyter notebook\n","from IPython.display import Image\n","Image(filename = 'tree1.png')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"_unRRGlo1iCk"},"source":["Το γράφημα του δέντρου μας δείχνει ότι αποτελείται από έναν μόνο κόμβο απόφασης που διαχωρίζει τα δεδομένα για τη μεταβλητή φύλο. Και τα 314 θηλυκά καταλήγουν σε έναν κόμβο φύλλων και τα 577 αρσενικά καταλήγουν σε έναν διαφορετικό κόμβο φύλλων.\n","\n","Ας κάνουμε προβλέψεις με αυτό το δέντρο και ας δούμε έναν πίνακα με τα αποτελέσματα:"]},{"cell_type":"code","metadata":{"id":"nOvgIOC61xMT"},"source":["# Get survival probability\n","preds = tree_model.predict_proba(X = pd.DataFrame(encoded_sex))\n","\n","pd.crosstab(preds[:,0], titanic_train[\"Sex\"])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"kx2lKokd1z5M"},"source":["**Αριστερό βέλος σημαίνει \"Αληθές\", δεξί βέλος σημαίνει \"Λάθος\". Στους κόμβους των φύλλων ο ταξινομητής επιλέγει την πλειοψηφική κλάση (αν υπάρχουν μη καθαρά φύλλα (gini>0), τότε υπάρχει σφάλμα ταξινόμησης).**\n","\n","Ο πίνακας δείχνει ότι το δέντρο απόφασης κατάφερε να δημιουργήσει ένα απλό μοντέλο με βάση το φύλο, όπου αν είστε γυναίκα επιβάτης θα πνιγείτε με πιθανότητα 0,257962 και αν είστε άνδρας επιβάτης θα πνιγείτε με πιθανότητα 0,811092.\n","\n","Ας δημιουργήσουμε ένα νέο δέντρο απόφασης που προσθέτει τη μεταβλητή κλάση επιβάτη και ας δούμε πώς αλλάζει τις προβλέψεις που προκύπτουν:"]},{"cell_type":"code","metadata":{"id":"7XYA6XFy3eZR"},"source":["# Make data frame of predictors\n","predictors = pd.DataFrame([encoded_sex, titanic_train[\"Pclass\"]]).T\n","\n","# Train the model\n","tree_model.fit(X = predictors, \n"," y = titanic_train[\"Survived\"])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"5U50bI_T3oIs"},"source":["Ας δούμε το νέο γράφο που δημιουργεί το νέο μοντέλο:"]},{"cell_type":"code","metadata":{"id":"WAfL5Xb34p-R"},"source":["with open(\"tree2.dot\", 'w') as f:\n"," f = tree.export_graphviz(tree_model, \n"," feature_names=[\"Sex\", \"Class\"], \n"," out_file=f)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"KW3evL9Q5CiS"},"source":["!dot -Tpng tree2.dot -o tree2.png -Gdpi=100"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"SElzKNiX5C_z"},"source":["# Display in jupyter notebook\n","from IPython.display import Image\n","Image(filename = 'tree2.png')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Qn9dOz1V5Lxx"},"source":["Παρατηρήστε ότι με την προσθήκη μιας ακόμη μεταβλητής, το δέντρο γίνεται σημαντικά πιο πολύπλοκο. Τώρα έχει 6 κόμβους απόφασης, 6 κόμβους φύλλων και μέγιστο βάθος 3.\n","\n","Ας κάνουμε προβλέψεις και ας δούμε έναν πίνακα με τα αποτελέσματα:"]},{"cell_type":"code","metadata":{"id":"FEcbWt2g5xaM"},"source":["# Get survival probability\n","preds = tree_model.predict_proba(X = predictors)\n","\n","# Create a table of predictions by sex and class\n","pd.crosstab(preds[:,0], columns = [titanic_train[\"Pclass\"], \n"," titanic_train[\"Sex\"]])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"VIiLPVC1y143"},"source":["## Εξαγωγή κανόνων\n","\n","Μπορούμε εύκολα να εξάγουμε τους κανόνες διασχίζοντας αναδρομικά το δένδρο\n"]},{"cell_type":"code","metadata":{"id":"1WvsJgTezAsK"},"source":["def tree_to_pseudo(tree, feature_names):\n","\n","\t'''\n","\tOutputs a decision tree model as if/then pseudocode\n","\t\n","\tParameters:\n","\t-----------\n","\ttree: decision tree model\n","\t\tThe decision tree to represent as pseudocode\n","\tfeature_names: list\n","\t\tThe feature names of the dataset used for building the decision tree\n","\t'''\n","\n","\tleft = tree.tree_.children_left\n","\tright = tree.tree_.children_right\n","\tthreshold = tree.tree_.threshold\n","\tfeatures = [feature_names[i] for i in tree.tree_.feature]\n","\tvalue = tree.tree_.value\n","\n","\tdef recurse(left, right, threshold, features, node, depth=0):\n","\t\tindent = \" \" * depth\n","\t\tif (threshold[node] != -2):\n","\t\t\tprint(indent,\"if ( \" + features[node] + \" <= \" + str(threshold[node]) + \" ) {\")\n","\t\t\tif left[node] != -1:\n","\t\t\t\trecurse (left, right, threshold, features, left[node], depth+1)\n","\t\t\t\tprint(indent,\"} else {\")\n","\t\t\t\tif right[node] != -1:\n","\t\t\t\t\trecurse (left, right, threshold, features, right[node], depth+1)\n","\t\t\t\tprint(indent,\"}\")\n","\t\telse:\n","\t\t\tprint(indent,\"return \" + str(value[node]))\n","\n","\trecurse(left, right, threshold, features, 0)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"1hDNOs4SzXx5"},"source":["tree_to_pseudo(tree_model, [\"Sex\", \"Class\"])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"B8zEFW062-oI"},"source":["Ο ταξινονητής στα φύλλα προβλέπει την πλειοψηφική κλάση (ή κάποια σε περίπτωση ισοπαλίας). Στην πρώτη θέση είναι survive=\"no\" και η δεύτερη survive=\"yes\". Μπορεί να έχουμε σφάλμα στο training set μπορεί και να μην έχουμε. Στη δεύτερη περίπτωση, αν και διαισθητικά το δένδρο που προκύπτει μοιάζει καλύτερο (μηδενικό εμπειρικό ρίσκο), πρέπει να θυμόμαστε ότι μπορεί να έχουμε πέσει σε μια κακή υπόθεση."]},{"cell_type":"markdown","metadata":{"id":"A9lXbEnE91C7"},"source":["## Ψηλότερα δένδρα (περισσότερες μεταβλητές)\n","\n","\n","Notice that the more complex model still predicts a higher survival rate for women than men, but women in third class only have a 50% predicted death probability while women in first class are predicted to die less than 5% of the time.\n","\n","\n","The more variables you add to a decision tree, the more yes/no decisions it can make, resulting in a deeper, more complex tree. Adding too much complexity to a decision tree, however, makes it prone to overfitting the training data, which can lead to poor generalization to unseen data. Let's investigate by creating a larger tree with a few more variables:"]},{"cell_type":"code","metadata":{"id":"clN4pbQI92q3"},"source":["predictors = pd.DataFrame([encoded_sex,\n"," titanic_train[\"Pclass\"],\n"," titanic_train[\"Age\"],\n"," titanic_train[\"Fare\"]]).T\n","\n","# Initialize model with maximum tree depth set to 8\n","tree_model = tree.DecisionTreeClassifier(max_depth = 8)\n","\n","tree_model.fit(X = predictors, \n"," y = titanic_train[\"Survived\"])"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"lB7EKCVo-DLy"},"source":["with open(\"tree3.dot\", 'w') as f:\n"," f = tree.export_graphviz(tree_model, \n"," feature_names=[\"Sex\", \"Class\",\"Age\",\"Fare\"], \n"," out_file=f)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"X-Qm-KsQ-GIg"},"source":["!dot -Tpng tree3.dot -o tree3.png -Gdpi=90"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"9LNbVcrI-MqH"},"source":["# Display in jupyter notebook\n","from IPython.display import Image\n","Image(filename = 'tree3.png')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"6IhoaeXe-P4y"},"source":["\n","Η παραπάνω εικόνα δείχνει πόσο πολύπλοκα μπορούν να γίνουν τα δέντρα αποφάσεων όταν αρχίσετε να προσθέτετε περισσότερες επεξηγηματικές μεταβλητές. Μπορείτε να ελέγξετε την πολυπλοκότητα του δέντρου τροποποιώντας ορισμένες από τις προεπιλεγμένες παραμέτρους της συνάρτησης δέντρου αποφάσεων. \n","\n","Στα φύλλο του δέντρου η απόφαση γίνεται με την πλειοψηφία: άν έχουμε δηλαδή για παράδειγμα \"1 3\" (ένας πνίγηκε - τρεις σώθηκαν, σύνολο 4) o ταξινομητής, για όσα δείγματα πληρούν τις συνθήκες του δέντρου μέχρι να φτάσουμε στο συγκεκριμένο φύλλο, θα αποφασίζει \"σώθηκε\" δλδ κλάση 1 (η κλάση 0 είναι η \"πνίγηκε\"). Αυτό σημαίνει οτι, στο training set θα κάνει και ένα σφάλμα στα συγκεκριμένο φύλλοι για τον 1 του φύλλου από τους 4 που πνίγηκε. Όταν το gini index είναι >0 στα φύλλα, σημαίνει ότι υπάρχει λάθος ταξινόμησης στην εκπαίδευση. Το ποια σφάλματα θα γίνουν στη φάση της αποτίμησης (test) δεν το γνωρίζουμε γιατί δεν έχουμε δει ακόμα τα δείγματα του test set. Ωστόσο οι αποφάσεις του δέντρου με βάση τις συνθήκες των εσωτεριών κόμβων και για το test θαείναι αυτές ακριβώς που καθορίστηκαν στην εκπαίδευση (στο train set).\n","\n","Ας ελέγξουμε την ακρίβεια αυτού του μοντέλου δέντρου αποφάσεων στα δεδομένα εκπαίδευσης:"]},{"cell_type":"code","metadata":{"id":"40xuEj4s_BfK"},"source":["tree_model.score(X = predictors, \n"," y = titanic_train[\"Survived\"])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Uj7KGmN0_D6w"},"source":["Το μοντέλο είναι σχεδόν 89% πιστό στα δεδομένα εκπαίδευσης, αλλά πώς τα καταφέρνει με άγνωστα δεδομένα; \n","\n","Το σύνολο δεδομένων Titanic είναι ακόμα ένας ανοιχτός διαγωνισμός του Kaggle, οπότε οι ετικέτες ελέγχου είναι άγνωστες. Μπορείτε να υποβάλετε τις προβλέψεις σας για να λάβετε ένα αποτέλεσμα ορθότητας και την κατάταξή σας."]},{"cell_type":"markdown","metadata":{"id":"-2gEClLg_1Ww"},"source":["## Download των γραφημάτων από το Colab\n"]},{"cell_type":"code","metadata":{"id":"cKmjPuSbdbxp"},"source":["!ls"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"jMHNIOMk2Z7W"},"source":["Για να κατεβάσουμε τοπικά τις εικόνες των δένδρων (.png) μπορούμε να κάνουμε το ακόλουθο:"]},{"cell_type":"code","metadata":{"id":"bSxJtbtXdcYd"},"source":["from google.colab import files\n","files.download('tree1.png')\n","files.download('tree2.png')\n","files.download('tree3.png')"],"execution_count":null,"outputs":[]}]}