{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Lab 10 Εισαγωγή στη Βαθιά Μάθηση και TF2 - Συνελικτικά δίκτυα.ipynb","provenance":[],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"},"accelerator":"GPU"},"cells":[{"cell_type":"markdown","metadata":{"collapsed":true,"id":"OOsMFzeiK9sr"},"source":["# Εισαγωγή στη Βαθιά Μάθηση\n","\n","\n","\n","Η Βαθιά Μάθηση ([Deep Learning](https://en.wikipedia.org/wiki/Deep_learning)) είναι μια υποκατηγορία της Μηχανικής Μάθησης που εστιάζει στην εκμάθηση υψηλού επιπέδου αναπαραστάσεων των δεδομένων. Το \"βάθος\" αναφέρεται στην αρχιτεκτονική των μοντέλων, τα οποία έχουν πολλαπλά κρυμμένα επίπεδα.\n","\n","![](https://cdn-images-1.medium.com/max/1600/1*5egrX--WuyrLA7gBEXdg5A.png)\n","\n","Σκοπός των πολλαπλών κρυμμένων επιπέδων είναι η εκμάθηση ολοένα και πιο αφηρημένων χαρακτηριστικών.\n","\n","![](https://s3-ap-south-1.amazonaws.com/av-blog-media/wp-content/uploads/2017/04/04085912/dl4.png)\n","\n","Η πιο διαδεδομένη βιβλιοθήκη που χρησιμοποιείται για deep learning είναι η [TensorFlow](https://www.tensorflow.org/), η οποία σχεδιάστηκε και υποστηρίζεται από τη Google. Η TensorFlow είναι ουσιαστικά μια βιβλιοθήκη συμβολικών μαθηματικών πάνω στην οποία \"κτίζονται\" deep learning frameworks όπως το [Keras](https://keras.io/)."]},{"cell_type":"markdown","metadata":{"id":"vcUG-D7qQBeM"},"source":["## Ιστορία\n","\n","Η εκπαίδευση τόσο των απλών όσο και των πολυεπίπεδων (deep) νευρωνικών δικτύων βασίζεται στον αλγόριθμο backpropagation. Στην περίπτωση των βαθιών νευρωνικών ο αριθμός των βαρών που πρέπει να υπολογίζονται σε κάθε βήμα εκπαίδευσης είναι πολύ μεγάλος και παρότι ως θεωρητικά μοντέλα υπάρχουν από τις δεκαετίες του 1980 και του 1990 δεν υπήρχε η δυνατότητα να εφαρμοστούν πρακτικά λόγω του υπολογιστικού κόστους σε σχέση με το τότε διαθέσιμο hardware. \n","\n","![alt text](http://beamandrew.github.io//images/deep_learning_101/nn_timeline.jpg)\n","\n","Τα πολυεπίπεδα νευρωνικά δίκτυα επανανακαλύφθηκαν πρώτα θεωρητικά από τον Hinton το 2006 (γένηση του όρου \"Deep Learning\") και στη συνέχεια πρακτικά από το 2012 και μετά, όταν πια έγινε δυνατή η εκπαίδευση βαθιών νευρωνικών δικτύων ακόμα και από απλούς τελικούς χρήστες, χάρη στην ανάπτυξη του hardware και συγκεκριμένα των μονάδων που αποκαλούμε επιταχυντές.\n"]},{"cell_type":"markdown","metadata":{"id":"D4b3dCQ_YWs0"},"source":["## Επιταχυντές\n","\n","Οι επιταχυντές (accelerators) στη βαθιά μηχανική μάθηση είναι οι υπολογιστικές μονάδες που μας επιτρέπουν να εκτελούμε το μαζικά παράλληλο υπολογισμό (massively parallel) που απαιτείται για την εκπαίδευση νευρωνικών δικτύων με μεγάλο αριθμό κρυφών επιπέδων και νευρώνων. \n","\n","### GPU\n","Οι πιο γνωστοί επιταχυντές είναι οι Μονάδες Επεξεργασίας Γραφικών (Graphics Processing Units - GPU). Οι μονάδες αυτές, όπως φαίνεται και από το όνομά τους, αρχικά είναι σχεδιασμένες για άλλη λειτουργία και συγκεκριμένα για την απεικόνιση γραφικών. Ωστόσο η αρχιτεκτονική τους έχει πολλά χαρακτηριστικά που τις κάνει καταλληλότερες για την εκπαίδευση βαθιών νευρωνικών δικτύων απ' ότι η CPU, με κυριότερο την ύπαρξη ενός πολύ μεγάλου αριθμού πυρήνων (cores) που μπορούν να επιτελέσουν παράλληλο υπολογισμό που συνίσταται κυρίως σε πολλαπλασιασμό πολυεπίπεδων πινάκων, δηλαδή τανυστών. \n","\n","### TPU\n","To 2016 η Google άρχισε να αναπτύσσει μονάδες ειδικά για την επεξεργασία τανυστών οι οποίες αποκαλούνται Tensor Processing Units (TPU). Τα TPU είναι πλέον εξειδικευμένες υπολογιστικές μονάδες για τη Βαθιά Μηχανική Μάθηση και δεν υποστηρίζουν δυνατότητες γραφικών. Οι TPU ανήκουν στην Google (είναι proprietary).\n","\n","Θα βρείτε μια καλή in-depth τεχνική ανάλυση [εδώ](https://www.anandtech.com/show/12673/titan-v-deep-learning-deep-dive/2).\n","\n","### GPU και cloud Jupyter notebooks\n","\n","Αυτή τη στιγμή, GPU acceleration υποστηρίζουν μόνο τα notebooks στο Colab και το Kaggle. Για το σημερινό notebook και γενικά για βαθιά μηχανική μάθηση σε notebook, μπορείτε να χρησιμοποιήσετε ένα από τα δύο. \n","\n","Για να χρησιμοποιήσετε **GPU στο Colab** πρέπει να επιλέξετε Runtime (*Χρόνος εκτέλεσης (runtime)*-> *Αλλαγή τύπου χρόνου εκτέλεσης (runtime)*) με υποστήριξη GPU.\n","\n","![Colab](https://i.ibb.co/Msv6Q92/colab.png)\n","\n","Όπως βλέπετε το Colab υποστηρίζει και (το proprietary) TPU acceleration.\n","\n","Αντίστοιχα για **GPU στο Kaggle**, εντός του notebook πηγαίνουμε στα settings και διαλέγουμε GPU on\n","\n","![Kaggle](https://i.ibb.co/swnQfHZ/kaggle.png)\n","\n","Υπενθυμίζουμε ότι στο Kaggle πρέπει να κάνουμε connect το internet για να μπορούμε να εγκαθιστούμε πακέτα (το ακριβώς από κάτω setting).\n","\n","To GPU settings αποθηκεύεται ως παράμετρος του runtime εντός του notebook και δεν χρειάζεται να το επιλέγουμε/αλλάζουμε κάθε φορά που το ανοίγουμε.\n","\n","Εφόσον έχουμε runtime με GPU τρέχουμε τις ακόλουθες εντολές για να δούμε ποια είναι η κάρτα γραφικών μας στο cloud (και το CPU μας). To Kaggle μας επιτρέπει να τρέξουμε 40 ώρες/μήνα κώδικα στις GPU και άλλες 20 ώρες/μήνα σε TPU\n","\n"]},{"cell_type":"code","metadata":{"id":"f6mMP8yNqu0L"},"source":["!nvidia-smi -L\n","!lscpu |grep 'Model name'"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"lF-Z5euNrH18"},"source":["Και τα δυο clouds έχουν μια Tesla K80 GPU και Xeon CPU."]},{"cell_type":"markdown","metadata":{"id":"zd3CaUrIqsJZ"},"source":["\n","\n","### Σύγκριση επίδοσης GPU - CPU\n","\n","Για να έχουμε μια εικόνα πόσο διαφορετική είναι η επίδοση (ταχύτητα) της εκπαίδευσης βαθιών νευρωνικών δικτύων μεταξύ CPU, GPU τόσο τοπικά, όσο και στα clouds τρέξαμε ένα πρόγραμμα σταθμομέτρησης (benchmark program) εκπαίδευσης νευρωνικού. Οι χρόνοι εκτέλεσης απεικονίζονται στο ακόλουθο γράφημα:\n","\n","![alt text](https://i.ibb.co/ZKYr7FX/benchmark.png)\n","\n","Η local CPU είναι ένας Intel i5 3rd gen και η GPU μια NVIDIA GTX 1060 6GB, δλδ και τα δύο midrange. Τα βασικά συμπεράσματα είναι ότι:\n","- Η εκπαίδευση σε GPU είναι εως και 40 φορές ταχύτερες απότι σε CPU.\n","- Οι GPU των δύο clouds έχουν περίπου τη μισή ταχύτητα από μια local midrange GPU. Γενικά το local μηχάνημα έστω και midrange είναι καλύτερο αλλά μην ξεχνάτε ότι στα clouds μοιραζόμαστε τους πόρους μας. Οριακά καλύτερη είναι η GPU του Kaggle. \n","- Η εκπαίδευση σε μια cloud GPU είναι 16 φορές ταχύτερη από την εκπαίδευση στη local CPU.\n","- Μεταξύ των cloud CPU είναι σημαντικά καλύτερη η CPU του Kaggle.\n","- Το TPU δεν φαίνεται να αποδίδει στο συγκεκριμένο test. Το πιθανότερο είναι ότι δεν χρησιμοποιούμε τις κατάλληλες εντολές βελτιστοποίησης ως προς TPU.\n","\n"]},{"cell_type":"markdown","source":["## Οι βιβλιοθήκες Tensorflow και Keras"],"metadata":{"id":"aQmRltanp8fE"}},{"cell_type":"markdown","source":["### Tensorflow\n","\n","![Tensorflow logo](https://upload.wikimedia.org/wikipedia/commons/1/11/TensorFlowLogo.svg)\n","\n","To [Tensorflow](https://www.tensorflow.org/) είναι μια ανοιχτού κώδικα πλατφόρμα και προγραμματιστική βιβλιοθήκη που χρησιμοποιείται για την εκπαίδευση μοντέλων τεχνητής νοημοσύνης και μηχανικής μάθησης. Αναπτύσσεται από την Google και η πρώτη της έκδοση δημοσιεύτηκε με άδεια ανοιχτού κώδικα στις 11 Νοεμβρίου του 2015. Διαθέτει προγραμματιστικές διεπαφές για διάφορες γλώσσες προγραμματισμού μεταξύ των οποίων συγκαταλέγονται η Python, η Javascript, η C++ και η Java, πράγμα που έχει συντελέσει στην πολύ μεγάλη δημοφιλία της. Μαζί με το [PyTorch](https://pytorch.org/), είναι τα δύο πιο διαδεδομένα περιβάλλοντα συγγραφής και ανάπτυξης μοντέλων βαθιάς μηχανικής μάθησης.\n"," "],"metadata":{"id":"lhQC5MHNAsE0"}},{"cell_type":"markdown","source":["### keras\n","\n","![keras logo](https://keras.io/img/logo.png)\n","\n","To [keras](https://keras.io/) είναι μια ανοιχτού κώδικα, υψηλού επιπέδου *διεπαφή προγραμματιστικών εφαρμογών* (API) για νευρωνικά δίκτυα, γραμμένη σε Python, η οποία μπορεί να τρέξει πάνω από το [TensorFlow](https://www.tensorflow.org/), το [Microsoft Cognitive Toolkit (CNTK)](https://www.microsoft.com/en-us/cognitive-toolkit/) ή το [Theano](http://deeplearning.net/software/theano/). Αναπτύχθηκε από τον François Chollet και η πρώτη της έκδοση δημοσιεύτηκε στις 27 Μαρτίου του 2015. Οι κύριες σχεδιαστικές της αρχές της είναι η ευκολία χρήσης, η αρθρωτή της δομή και η δυνατότητα επεκτασιμότητας, ενώ επίσης επιτρέπει τη γρήγορη σχεδίαση βαθιών δικτύων και τον πειραματισμό με αυτά. Είναι συμβατή με τις εκδόσεις της Python από 3.6 έως 3.9.\n","\n","#### Βασικά χαρακτηριστικά\n","- Εύκολη και γρήγορη κατασκευή δικτύων (μέσω της αρθρωτής της αρχιτεκτονικής και της ευχρηστίας της)\n","- Υποστηρίζει CPU και επιταχυντές γραφικών (GPU)\n","- Υποστηρίζει μεταξύ άλλων δίκτυα πρόσθιας τροφοδότησης (feedforward networks), συνελικτικά (convolutional) και αναδρομικά δίκτυα (recurrent)\n","\n","#### Λειτουργία Επιπέδων\n","\n","Το keras, **δεν είναι** αυτόνομη βιβλιοθήκη μηχανικής μάθησης, αλλά μια διεπαφή, σχεδιασμένη να τρέχει πάνω από άλλες βιβλιοθήκες. Παρέχει δομικά στοιχεία κατασκευής μοντέλων (από αρκετά απλά ως αρκετά σύνθετα), αλλά δεν χειρίζεται υπολογισμούς χαμηλού επιπέδου (λ.χ. πράξεις μεταξύ τανυστών). Αυτές πραγματοποιούνται από τις βιβλιοθήκες χαμηλότερου επιπέδου, οι όποιες θα μπορούσαμε να πούμε ότι παίζουν το ρόλο ενός *backend engine*. Η αρθρωτή αρχιτεκτονική δεν επιβάλλει τη χρήση συγκεκριμένης backend engine και αυτή τη στιγμή υποστηρίζονται οι 3 μηχανές που αναφέρθηκαν παραπάνω (Tensorflow, CNTK, Theano). Στο αμέσως κατώτερο επίπεδο υπάρχουν οι βιβλιοθήκες της γραμμικές άλγεβρας (CUDA/cuDNN για την GPU, BLAS, Eigen κλπ για τη CPU) και φυσικά το υλικό (κάρτα γραφικών/επεξεργαστής).\n","\n","![keras_stack](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSj2pFeXU2dbnb-ODl81AIg7JWOax8ejk-w4UbJU1ibC3PA-t3O7g)"],"metadata":{"id":"6R36RO75qF2I"}},{"cell_type":"markdown","source":["### Tensorflow και keras\n","\n","Από την έκδοση 2.0 του Tensorflow και έπειτα (που δημοσιεύτηκε τον Σεπτέμβριο του 2019, η παγίωση του Tensorflow ως του πιο δημοφιλούς backend του keras είχε ως συνέπεια την ενσωμάτωση του δεύτερου ως module του πρώτου (module keras του tensorflow). Εξάλλου, ο ίδιος ο François Chollet δήλωσε πως η έκδοση 2.3.0 του keras θα είναι η τελευταία που θα υποστηρίζει πολλαπλά υπολογιστικά backends και πως όλοι όσοι σκοπεύουν να χρησιμοποιήσουν το keras θα πρέπει να το κάνουμε μέσω του αντίστοιχου module του Tensorflow (tensorflow.keras) από εδώ και στο εξής.\n","\n","Κατά συνέπεια, ο τρόπος που θα χρησιμοποιήσουμε το keras για να κατασκευάσουμε βαθιά δίκτυα στο παρόν notebook θα είναι ο ενδεδειγμένος, δηλαδή μέσω του module tensorflow.keras."],"metadata":{"id":"f1gGt_dVAkZ5"}},{"cell_type":"markdown","metadata":{"id":"eHauk173wC_w"},"source":["## Εισαγωγή βιβλιοθηκών\n","\n","Εισάγουμε τις βιβλιοθήκες που πρόκειται να χρησιμοποιήσουμε σε αυτό το Notebook."]},{"cell_type":"code","metadata":{"id":"IKnJbnthK9s7"},"source":["from sklearn.preprocessing import MinMaxScaler\n","\n","import tensorflow as tf\n","import numpy as np\n","import matplotlib.pyplot as plt\n","\n","print(\"Έκδοση Tensorflow: \" + tf.__version__)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Ορισμός βοηθητικών συναρτήσεων\n","\n","Ορίζουμε βοηθητικές συναρτήσεις απεικόνισης των συναρτήσεων απώλειας και ακρίβειας κατά την εκπαίδευση των δικτύων, καθώς και συνάρτηση που οπτικοποιεί τα βάρη που συνδέουν την είσοδο με την έξοδο ενός απλού Perceptron."],"metadata":{"id":"T-h7FA_e71Nf"}},{"cell_type":"code","source":["def simple_plot(hst, epcs):\n"," plt.rcParams[\"figure.figsize\"] = (20,6)\n"," plt.subplot(1, 2, 1)\n"," plt.plot(range(1,epcs+1), hst.history['loss'], c='deepskyblue')\n"," plt.legend()\n"," plt.title('Συνάρτηση απώλειας στο σύνολο εκπαίδευσης (διαστραυρούμενη εντροπία)')\n"," plt.xlabel('Εποχή')\n"," plt.ylabel('Τιμή απώλειας')\n"," plt.subplot(1, 2, 2)\n"," plt.plot(range(1,epcs+1), hst.history['accuracy'], c='deepskyblue')\n"," plt.legend(loc='lower right')\n"," plt.title('Ακρίβεια στο σύνολο εκπαίδευσης')\n"," plt.xlabel('Εποχή')\n"," plt.ylabel('Ακρίβεια')\n","\n","\n","def plot_neuron_weights(mdl, layr):\n"," for i in range(10):\n"," plt.subplot(2, 5, i+1)\n"," weight = mdl.layers[layr].get_weights()[0][:,i].reshape([28,28])\n"," plt.title(i)\n"," plt.imshow(weight, cmap='RdBu')\n"," frame1 = plt.gca()\n"," frame1.axes.get_xaxis().set_visible(False)\n"," frame1.axes.get_yaxis().set_visible(False)"],"metadata":{"id":"rihNQtcv9apQ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Ywz4O14Oxhnq"},"source":["### Εκτέλεση Tensorflow σε CPU ή GPU\n","\n","To Tensorflow θα χρησιμοποιήσει αυτόματα την/τις GPU αν είναι διαθέσιμες. Μπορούμε να του πούμε και [ρητά](https://www.tensorflow.org/guide/using_gpu) τι να χρησιμοποιήσει."]},{"cell_type":"markdown","source":["## Το σύνολο δεδομένων MNIST"],"metadata":{"id":"uKB2WZVnrSoN"}},{"cell_type":"markdown","source":["\n","\n","Θα εργαστούμε με το [MNIST dataset](http://yann.lecun.com/exdb/mnist/), που περιέχει ασπρόμαυρες σκαναρισμένες εικόνες χειρόγραφων ψηφίων (0 ως 9), $28\\times28$ pixel η κάθε μια, μαζί με την ετικέτα τους, δηλαδή τον αριθμό που απεικονίζουν. Με άλλα λόγια, μπορούμε να το χρησιμοποιήσουμε στο πλαίσιο ενός προβλήματος επιβλεπόμενης μάθησης (και πιο συγκεκριμένα, ταξινόμησης), όπου το ζητούμενο είναι να φτιάξουμε ένα μοντέλο μηχανικής μάθησης που θα μπορεί να \"αναγνωρίζει\" σε ποιον αριθμό αντιστοιχεί το κάθε χειρόγραφο ψηφίο, δηλαδή να έχει λειτουργικότητα παρόμοια με των [προγραμμάτων οπτικής αναγνώρισης χαρακτήρων](https://en.wikipedia.org/wiki/Optical_character_recognition).\n","\n","Το MNIST dataset είναι το πιο συχνά χρησιμοποιούμενο στον τομέα της επεξεργασίας εικόνας με τεχνικές βαθιάς μάθησης, κυρίως για τη σύγκριση της απόδοσης διαφορετικών αλγορίθμων και αρχιτεκτονικών.\n","\n","![](https://ujwlkarn.files.wordpress.com/2016/08/8-gif.gif?w=192&h=192&zoom=2)\n","\n","\n","Εξαιτίας της δημοφιλίας του, δεν χρειάζεται να το κατεβάσουμε εμείς ανεξάρτητα, μιας και το Tensorflow μας δίνει τη δυνατότητα να το φορτώσουμε απευθείας καλώντας τη μέθοδο [load_dataset()](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/mnist/load_data) του submodule keras.datasets.mnist."],"metadata":{"id":"BRkYdTAxuedb"}},{"cell_type":"code","metadata":{"id":"4oI0i0qCK9tj"},"source":["(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n","print('Διαστάσεις δεδομένων εκπαίδευσης:', x_train.shape)\n","print('Διαστάσεις ετικετών εκπαίδευσης:', y_train.shape)\n","print()\n","print('Διαστάσεις δεδομένων ελέγχου:',x_test.shape)\n","print('Διαστάσεις ετικετών ελέγχου:', y_train.shape)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"fjaJGyJoK9tw"},"source":["Το dataset έχει $60.000$ εικόνες στο σύνολο δεδομένων εκπαίδευσης και $10.000$ στο σύνολο δεδομένων ελέγχου. Κάθε εικόνα είναι ασπρόμαυρη και έχει διαστάσεις $28\\times28$ pixel. \n","\n","Ας εμφανίσουμε την πρώτη εικόνα από τα δεδομένα εκπαίδευσης, μαζί με την ετικέτα της.\n"]},{"cell_type":"code","source":["ind = 0\n","print('Πρώτη εικόνα συνόλου εκπαίδευσης')\n","print('--------------------------------')\n","np.set_printoptions(linewidth=200)\n","print(x_train[ind])\n","print('--------------------------------')\n","print('Ετικέτα: {}'.format(y_train[ind]))"],"metadata":{"id":"5HOUq2IzhZ9k"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Παρατηρούμε πως η τιμή της έντασής του κάθε pixel είναι στο $[0,255]$. Ας δοκιμάσουμε να οπτικοποιήσουμε την συγκεκριμένη εικόνα"],"metadata":{"id":"GrVDhyy2h4CN"}},{"cell_type":"code","metadata":{"id":"GDwDHxxkK9ty"},"source":["plt.imshow(x_train[ind, :], cmap='gray')\n","plt.title('Παράδειγμα: {}, Ετικέτα: {}'.format(ind, y_train[ind]))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Προτού προχωρήσουμε στην κατασκευή νευρωνικών δικτύων, θα πρέπει να φέρουμε τα δεδομένα μας στην κατάλληλη μορφή. Αυτό, στη συγκεκριμένη περίπτωση σημαίνει ότι θα πρέπει να κάνουμε τις εξής μετατροπές:\n","\n","1. Θα πρέπει να μετατρέψουμε τις δισδιάστατες εικόνες σε μονοδιάστατα διανύσματα, έτσι ώστε να μπορούν να τροφοδοτηθούν στα νευρωνικά δίκτυα. Για το σκοπό αυτό θα χρησιμοποιήσουμε τη συνάρτηση [reshape()](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.reshape.html?highlight=reshape#numpy.ndarray.reshape) του NumPy.\n","2. Για το συγκεκριμένο πρόβλημα, βοηθάει στην εκπαίδευση των δικτύων η κανονικοποίηση των δεδομένων εισόδου (τιμές έντασης των pixel) στο διάστημα $[0,1]$. Για το σκοπό αυτό θα χρησιμοποιήσουμε την κλάση [MinMaxScaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html) από το preprocessing module του scikit-learn.\n","3. Το επίπεδο της εξόδου των δικτύων πρόκειται να έχει 10 νευρώνες, με τον κάθε ένα να ενεργοποιείται όταν ανιχνεύεται το αντίστοιχο ψηφίο στην είσοδο. Για το λόγο αυτό, οι ετικέτες πρέπει να μετασχηματιστούν σε μορφή διανύσματος [one hot](https://en.wikipedia.org/wiki/One-hot) 10 θέσεων, με την χρήση της μεθόδου [to_cetegorical()](https://www.tensorflow.org/api_docs/python/tf/keras/utils/to_categorical) του submodule keras.utils του Tensorflow.\n"],"metadata":{"id":"OxcMMNXZinFe"}},{"cell_type":"code","source":["# 1. Μετατροπή από πίνακα σε διάνυσμα\n","x_train=x_train.reshape((x_train.shape[0], x_train.shape[1]*x_train.shape[2]))\n","x_test=x_test.reshape((x_test.shape[0], x_test.shape[1]*x_test.shape[2]))\n","\n","# 2. Κανονικοποίηση στο [0,1]\n","scaler = MinMaxScaler()\n","x_train = scaler.fit_transform(x_train)\n","x_test = scaler.fit_transform(x_test)\n","\n","# 3. Μετατροπή ετικετών σε διανύσματα one hot\n","y_train = tf.keras.utils.to_categorical(y_train)\n","y_test = tf.keras.utils.to_categorical(y_test)\n","\n","\n","print('Διαστάσεις δεδομένων εκπαίδευσης:', x_train.shape)\n","print('Διαστάσεις ετικετών εκπαίδευσης:', y_train.shape)\n","print()\n","print('Διαστάσεις δεδομένων ελέγχου:',x_test.shape)\n","print('Διαστάσεις ετικετών ελέγχου:', y_train.shape)"],"metadata":{"id":"iRGl2L76juEX"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Ας εμφανίσουμε ξανά την πρώτη εικόνα από το σύνολο δεδομένων εκπαίδευσης, μαζί με την ετικέτα της"],"metadata":{"id":"fBleO7vu02tF"}},{"cell_type":"code","source":["print('Πρώτη εικόνα συνόλου εκπαίδευσης')\n","print('--------------------------------')\n","np.set_printoptions(linewidth=200)\n","print(x_train[ind])\n","print('--------------------------------')\n","print('Ετικέτα: {}'.format(y_train[ind]))"],"metadata":{"id":"_PdvUVN608-X"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Νευρωνικά Δίκτυα"],"metadata":{"id":"51NYFDvPyy9p"}},{"cell_type":"markdown","source":["### Διαδικασία κατασκευής και εκπαίδευσης μοντέλου\n","\n","Η διαδικασία κατασκευής και εκπαίδευσης ενός μοντέλου είναι παρόμοια με αυτή που έχουμε δει στο *scikit-learn* και περιγράφεται στα παρακάτω 4 βήματα:\n","1. Ορισμός των δεδομένων εκπαίδευσης (τανυστές εισόδου και εξόδου)\n","2. Ορισμός ενός μοντέλου, το οποίο συσχετίζει την είσοδο με την έξοδο\n","3. Παραμετροποίηση της διαδικασίας μάθησης μέσω της επιλογής συνάρτησης κόστους (cost function), του βελτιστοποιητή (optimizer) και των μετρικών απόδοσης\n","4. Τροφοδότηση των δεδομένων στο δίκτυο και εκπαίδευσή του μέσω της κλήσης της συνάρτησης *fit()* του μοντέλου\n","\n","### Ορισμός μοντέλου\n","\n","Το submodule keras του Tensorflow προσφέρει δύο διεπαφές (APIs) για τον ορισμό ενός μοντέλου νευρωνικών δικτύων:\n","- Την ακολουθιακή ([Sequential](https://www.tensorflow.org/api_docs/python/tf/keras/Sequential)), η οποία χρησιμοποιείται για την κατασκευή αρχιτεκτονικών όπου τα επίπεδα στοιβάζονται (stacked) το ένα μετά το άλλο.\n","- Την συναρτησιακή ([functional](https://www.tensorflow.org/api_docs/python/tf/keras/Model)), η οποία βασίζεται στην κατασκευή κατευθυντικών ακυκλικών γράφων (Directed Acyclic Graphs) και η οποία μπορεί να χρησιμοποιηθεί για την κατασκευή οποιασδήποτε αρχιτεκτονικής θέλουμε"],"metadata":{"id":"WixLu-Ni08fJ"}},{"cell_type":"markdown","source":["### Δημιουργία ενός Perceptron"],"metadata":{"id":"YtTmq2af1fqH"}},{"cell_type":"markdown","metadata":{"id":"ZqVyfApOK9t-"},"source":["Το πρώτο μοντέλο που θα εξετάσουμε είναι ένα Perceptron, δηλαδή ένα νευρωνικό δίκτυο ενός επιπέδου εισόδου και ενός επιπέδου εξόδου. Η είσοδος $x$ αποτελείται από τα 784 pixel, υπό τη μορφή διανύσματος και η έξοδος $y$ από 10 νευρώνες, με τον κάθε ένα που ενεργοποιείται να αντστοιχεί και σε ένα από τα 10 ψηφία. Η αρχιτεκτονική του δικτύου συνοψίζεται στο παρακάτω σχήμα.\n","\n","![](https://i.ibb.co/1fgBnRm/d5222c6e3d15770a.png)\n","\n","Σύμφωνα με τη θεωρία, ο κάθε νευρώνας του επιπέδου εξόδου εκτελεί το μετασχηματισμό $f \\left( x \\cdot W + b \\right) $, όπου $W$ και $b$ οι πίνακες των βαρών, $x$ η είσοδος του δικτύου και $f$ η μη-γραμμική συνάρτηση ενεργοποίησης. Από τη θεωρία επίσης γνωρίζουμε ότι κατά την εκπαίδευση του δικτύου, η πληροφορία διαδίδεται με 2 τρόπους: \n","1. Πρώτα το δίκτυο δέχεται την είσοδο ($Χ$) και εκτελεί τις πράξεις για να παράγει την έξοδο ($\\hat y$). Η ροή της πληροφορίας εδώ γίνεται από την είσοδο του δικτύου προς την έξοδό του. \n","\n","2. Η έξοδος του δικτύου συγκρίνεται με την αναμενόμενη έξοδο (ετικέτα $y$), με τη βοήθεια μιας συνάρτησης κόστους $L$, την οποία θέλουμε και να ελαχιστοποιήσουμε. Για να το πετύχουμε αυτό, θα χρησιμοποιήσουμε την τεχνική *κατάβασης κλίσης* (gradient descent), η οποία σε κάθε βήμα της εκπαίδευσης ενημερώνει τα βάρη του επιπέδου εισόδου του δικτύου ως εξής:\n","\n","$$\n","w \\leftarrow w - η \\cdot \\frac{dL \\left( y, \\hat y\\right)}{dw} \\\\\n","b \\leftarrow b - η \\cdot \\frac{dL \\left( y, \\hat y\\right)}{db}\n","$$"]},{"cell_type":"markdown","source":["#### Ακολουθιακή διεπαφή\n","\n","\n","Στην περίπτωση της ακολουθιακής διεπαφής του δεν χρειάζεται να ορίσουμε ούτε τα διανύσματα βαρών, ούτε τον υπολογιστικό γράφο για τις 2 ροές πληροφορίας, μιας και αυτά αναλαμβάνει να τα υλοποιήσει το ίδιο το framework. Έτσι το μόνο που έχουμε να κάνουμε εμείς είναι απλά να καθορίσουμε την αρχιτεκτονική του μοντέλου μας Με πιο απλά λόγια, το framework μας \"κρύβει\" όλες τις διαδικασίες υλοποίησης."],"metadata":{"id":"tNLZ_BPt12fr"}},{"cell_type":"code","metadata":{"id":"GZ0WMTrWK9uH"},"source":["perceptron = tf.keras.Sequential(name=\"Perceptron\")"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"it6yBRDhK9uM"},"source":["Στη συνέχεια και μέσω της χρήσης της μεθόδου *add()* προσθέτουμε το επίπεδο εξόδου, το οποίο αποτελείται από 10 πλήρους διασυνδεδεμένους (με την είσοδο) νευρώνες (κλάση [Dense](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dense) του submodule keras.layers του Tensorflow), με συνάρτηση ενεργοποίησης [softmax](https://en.wikipedia.org/wiki/Softmax_function) \n","\n","$$ \n","softmax(z_{i,j}) = \\frac{e^{z_{i,j}}}{\\sum_{k=1}^{10}{e^{z_{k, j}}}}\n","$$\n","\n","Επίσης ορίζουμε ότι το διάνυσμα εισόδου έχει μέγεθος $784$ και αρχικοποιούμε τις τιμές των βαρών και της πόλωσης στο μηδέν."]},{"cell_type":"code","metadata":{"id":"yZIjIwBNK9uO"},"source":["perceptron.add(tf.keras.layers.Dense(10, activation='softmax', kernel_initializer='zeros', bias_initializer='zeros', input_shape=(784,), name=\"Output\"))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Μπορούμε να χρησιμοποιήσουμε τη μέθοδο *summary()* για να δούμε την αρχιτεκτονική του δικτύου."],"metadata":{"id":"_HE2ZCRet4ll"}},{"cell_type":"code","source":["perceptron.summary()"],"metadata":{"id":"LiSb64sOuCLW"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"PEezY92rK9uS"},"source":["Παρατηρούμε ότι το δίκτυό μας αποτελείται από ένα επίπεδο μόνο (εξόδου) και έχει συνολικά $7.850$ εκπαιδεύσιμες παραμέτρους, οι οποίες αντιστοιχούν στα $784\\times10=7.840$ βάρη που συνδέουν τις εισόδους με τις εξόδους συν τις $10$ πολώσεις των νευρώνων του επιπέδου εξόδου.\n","\n","Ένας ακόμα πιο ευανάγνωστος τρόπος καθορισμού της αρχιτεκτονικής του μοντέλου είναι αυτή να οριστεί ως παράμετρος κατά τη δημιουργία του στιγμιοτύπου της κλάσης Sequential"]},{"cell_type":"code","metadata":{"id":"HIPSagAiK9uT"},"source":["perceptron = tf.keras.Sequential([\n"," tf.keras.layers.Dense(10, activation='softmax', kernel_initializer='zeros', bias_initializer='zeros', input_shape=(784,), name=\"Output\")\n","], name=\"Perceptron\")"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Καλώντας την *summary()* παρατηρούμε ότι τα δύο δίκτυα είναι ίδια"],"metadata":{"id":"xaSPJ1yevHpU"}},{"cell_type":"code","source":["perceptron.summary()"],"metadata":{"id":"ADH8h__CvH86"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"l44tiaFUK9ub"},"source":["Μετά τη δημιουργία της αρχιτεκτονικής, με την μέθοδο *compile()* ορίζουμε τις παραμέτρους της εκπαίδευσης:\n","1. Χρήση κατάβασης κλίσης ως αλγορίθμου βελτιστοποίησης\n","2. Χρήση εντροπίας ([crossentropy](https://ml-cheatsheet.readthedocs.io/en/latest/loss_functions.html)) ως συνάρτησης κόστους\n","3. Χρήση της μετρικής της ορθότητας για την εκτίμηση της απόδοσης του ταξινομητή"]},{"cell_type":"code","metadata":{"id":"Hno2a57HK9uc"},"source":["perceptron.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Nn_eJUzUK9um"},"source":["Τέλος, εκπαιδεύουμε το μοντέλο μας στα δεδομένα εκπαίδευσης με τη χρήση της μεθόδου *fit()* ορίζοντας πλήθος εποχών και batch_size.\n","\n","[Epochs, batch size, iterations](https://stackoverflow.com/questions/4752626/epoch-vs-iteration-when-training-neural-networks)"]},{"cell_type":"code","source":["epochs = 25\n","hist = perceptron.fit(x_train, y_train, epochs=epochs, batch_size=128)"],"metadata":{"id":"qJPzuLVpFqpO"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Μπορούμε να σχεδιάσουμε τη συνάρτηση κόστους και την ορθότητα ανά εποχή στο σύνολο εκπαίδευσης με μια βοηθητική συνάρτηση"],"metadata":{"id":"wMIf0Otq69j7"}},{"cell_type":"code","metadata":{"id":"5Xfba0N8K9un"},"source":["simple_plot(hist, epochs)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"LTuZU1-IK9uv"},"source":["Παρατηρούμε ότι το απλό Perceptron επιτυγχάνει μια ακρίβεια άνω του 90% στα δεδομένα εκπαίδευσης, ένα αποτέλεσμα αρκετά ενθαρρυντικό!\n","\n","Πριν προσπαθήσουμε να βελτιώσουμε περαιτέρω αυτή την απόδοση, εξετάζοντας πιο βαθιά μοντέλα, ας δούμε τα αποτελέσματα στο σύνολο δεδομένων ελέγχου, με τη χρήση της μεθόδου *evaluate()*"]},{"cell_type":"code","metadata":{"id":"p1w6TZGSK9ux"},"source":["perceptron_loss, perceptron_acc = perceptron.evaluate(x_test, y_test)\n","\n","print('\\nΣυνάρτηση απώλειας: \\t{:.2f}\\nΑκρίβεια:\\t\\t{:.2f}%'.format(perceptron_loss, 100*perceptron_acc))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"NmW12JOiK9u8"},"source":["Τέλος, στην περίπτωση του Perceptron, όπου υπάρχει άμεση συσχέτιση εισόδου-εξόδου, έχει ενδιαφέρουν να οπτικοποιήσουμε τα 784 βάρη που συνδέουν την είσοδο με τον κάθε νευρώνα, διατάσσοντάς τα υπό μορφή εικόνας $28\\times 28$ pixel."]},{"cell_type":"code","metadata":{"id":"AjAkZWFWK9u9"},"source":["plot_neuron_weights(perceptron, 0)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Στην παραπάνω εικόνα, τα βάρη που συμβάλλουν θετικά στην κατηγοριοποίηση εμφανίζονται με μπλε ενώ αυτά που συμβάλλουν αρνητικά με κόκκινο. Χαρακτηριστικά είναι τα πρώτα 2 ψηφία (0 και 1)."],"metadata":{"id":"vS0kUL4DBwsx"}},{"cell_type":"markdown","source":["#### Συναρτησιακή διεπαφή\n","\n","Με τη συναρτησιακή διεπαφή μπορούμε να ορίσουμε πιο σύνθετα μοντέλα (λ.χ. με πολλά επίπεδα εξόδου), κατευθυντικούς ακυκλικούς γράφους ή ακόμα και μοντέλα με διαμοιραζόμενα επίπεδα. Η λειτουργία της συναρτησιακής διεπαφής μπορούμε να ισχυριστούμε πως είναι πιο low level.\n","\n","Καταρχήν, ορίζουμε τον τανυστή των εισόδων μας (κλάση [Input](https://www.tensorflow.org/api_docs/python/tf/keras/Input) του submodule keras του Tensorflow)"],"metadata":{"id":"fqSUUkWPBy4J"}},{"cell_type":"code","source":["inputs = tf.keras.Input(shape=(784,), name=\"Input\")"],"metadata":{"id":"rZUEnZCdDTCm"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Στη συνέχεια ορίζουμε το επίπεδο εξόδου (τανυστής) 10 νευρώνων με συναρτήσεις ενεργοποίησης softmax, όπως προηγουμένως, δίνοντας ως επιπρόσθετο όρισμα τον τανυστή της εισόδου"],"metadata":{"id":"wRO9PcuADeyk"}},{"cell_type":"code","source":["predictions = tf.keras.layers.Dense(10, kernel_initializer='zeros', bias_initializer='zeros', activation='softmax', name=\"Output\")(inputs)"],"metadata":{"id":"07La9rOqDjV4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Πλέον μπορούμε να ορίσουμε το μοντέλο μας (κλάση Model), ως μια σχέση τανυστών εισόδου και εξόδου"],"metadata":{"id":"vrDBp4cDFBBV"}},{"cell_type":"code","source":["perceptron_func = tf.keras.Model(inputs=inputs, outputs=predictions, name=\"Peceptron\")"],"metadata":{"id":"fpEEWR8jFCe6"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Καλώντας τη *summary()* παρατηρούμε ότι το δίκτυο που δημιουργείται μέσω της συναρτησιακής διεπαφής είναι όμοιο με αυτό της ακολουθιακής διεπαφής"],"metadata":{"id":"PTEqCR3PvrL6"}},{"cell_type":"code","source":["perceptron_func.summary()"],"metadata":{"id":"sHQDyD9SvrnX"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Κατόπιν θέτουμε τις παραμέτρους της εκπαίδευσης (μέθοδο βελτιστοποίησης, συνάρτηση κόστους, μετρική απόδοσης), όπως και στην περίπτωση του ακολουθιακού μοντέλου"],"metadata":{"id":"Oe11sj9TFlmx"}},{"cell_type":"code","source":["perceptron_func.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])"],"metadata":{"id":"hGtGEVvdFmbA"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Είμαστε έτοιμοι να εκπαιδεύσουμε τον ταξινομητή μας, με την κλήση της μεθόδου *fit()*, ακριβώς όπως και στο ακολουθιακό μοντέλο"],"metadata":{"id":"rny6uRpkFuvF"}},{"cell_type":"code","metadata":{"id":"ev6PZeZahXRy"},"source":["hist_func = perceptron_func.fit(x_train, y_train, epochs=epochs, batch_size=128)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Σχεδιάζουμε τη συνάρτηση απώλειας και την ακρίβεια ανά εποχή εκπαίδευσης"],"metadata":{"id":"gjrC3NzkGEHO"}},{"cell_type":"code","source":["simple_plot(hist_func, epochs)"],"metadata":{"id":"baYcPgc9GHzY"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Βλέπουμε την απόδοση του μοντέλου μας στα δεδομένα ελέγχου"],"metadata":{"id":"kBTETY5vGnRk"}},{"cell_type":"code","source":["perceptron_func_loss, perceptron_func_acc = perceptron_func.evaluate(x_test, y_test)\n","\n","print('\\nΣυνάρτηση απώλειας: \\t{:.2f}\\nΑκρίβεια:\\t\\t{:.2f}%'.format(perceptron_func_loss, 100*perceptron_func_acc))"],"metadata":{"id":"yw62ErwLGoXx"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Τέλος, όπως και στην περίπτωση της ακολουθιακής διεπαφής, θα οπτικοποιήσουμε τα βάρη των νευρώνων"],"metadata":{"id":"jKYQK9QEG9Hb"}},{"cell_type":"code","source":["plot_neuron_weights(perceptron_func, 1)"],"metadata":{"id":"YpXPMaWrHDz9"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"VtX9xhY5K9vC"},"source":["### Πολυεπίπεδα Perceptron\n","\n","Τώρα που είδαμε πως λειτουργεί το ένα επίπεδο του δικτύου, μπορούμε να προσθέσουμε στο δίκτυό μας περισσότερα επίπεδα για να βελτιώσουμε την απόδοσή του. Τα επίπεδα αυτά τα αποκαλούμε κρυφά (hidden layers), επειδή δεν παρατηρούμε την έξοδό τους. Το δίκτυο που θα σχεδιάσουμε θα έχει 2 κρυφά επίπεδα και 1 επίπεδο εξόδου.\n","\n","![](http://cs231n.github.io/assets/nn1/neural_net2.jpeg)\n","\n","\n","Επειδή στα κρυφά επίπεδα δεν δεσμευόμαστε όσον αφορά τις διαστάσεις, μπορούμε να τα κάνουμε όσο μεγάλα θέλουμε! Εμείς θα επιλέξουμε αυτά να έχουν 100 και 30 νευρώνες αντίστοιχα. Επίσης θα ορίσουμε ως συνάρτηση ενεργοποίησής τους τη [σιγμοειδή](https://en.wikipedia.org/wiki/Sigmoid_function):\n","\n","$$\n","sigmoid \\left( z \\right) = \\frac{1}{1 + e^{-z}}\n","$$\n","\n","Τα υπόλοιπα (επίπεδο εξόδου, συνάρτηση κόστους, τεχνική βελτιστοποίησης) θα τα αφήσουμε ίδια με πριν."]},{"cell_type":"markdown","source":["#### Ακολουθιακή Διεπαφή"],"metadata":{"id":"k9sJmiO8OASa"}},{"cell_type":"code","source":["mlp = tf.keras.Sequential([\n"," tf.keras.layers.Dense(\n"," 100, \n"," activation='sigmoid', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1) , \n"," bias_initializer='zeros', \n"," input_shape=(784,),\n"," name=\"Hidden1\"\n"," ),\n"," tf.keras.layers.Dense(\n"," 30, \n"," activation='sigmoid', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), \n"," bias_initializer='zeros',\n"," name=\"Hidden2\"\n"," ),\n"," tf.keras.layers.Dense(\n"," 10, \n"," activation='softmax', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), \n"," bias_initializer='zeros',\n"," name=\"Output\"\n"," )\n","])"],"metadata":{"id":"iqF_LV5mOIkx"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Όταν γίνονται βαθιά τα δίκτυα, είναι σημαντικό να αρχικοποιούμε τα βάρη από τυχαίες τιμές για να μην κολλήσει η εκπαίδευση στην αρχική της θέση. Η κλάση [TruncatedNormal](https://www.tensorflow.org/api_docs/python/tf/keras/initializers/TruncatedNormal) από το submodule keras.initializers του Tensorflow επιστρέφει τιμές από μια κανονική κατανομή μόνο στο εύρος $(-2σ, +2σ)$, όπου $σ$ η τυπική απόκλιση.\n","\n","Επίσης παρατηρούμε ότι στην ακολουθιακή διεπαφή, τα επιπέπεδα του δικτύου γράφονται (στοιβάζονται - stacked) το ένα κάτω από το άλλο. Το μόνο που χρειάζεται να ορίσουμε είναι το διάνυσμα εισόδου (παράμετρος input_shape του Dense layer της εισόδου), ενώ την διαστασιολόγηση των βαρών και των πολώσεων μεταξύ των επιπέδων του δικτύου τη \"συμπεραίνει\" το ίδιο το framework.\n","\n","Μπορούμε να χρησιμοποιήσουμε τη μέθοδο *summary()* για να δούμε την αρχιτεκτονική του δικτύου."],"metadata":{"id":"Y5SxY7USPCiH"}},{"cell_type":"code","source":["mlp.summary()"],"metadata":{"id":"nuDy5PWuQsZT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Το επίπεδο της εισόδου έχει διάσταση 784 ενώ το 1ο κρυφό επίπεδο έχει διάσταση 100. Συνεπώς, τα βάρη του επιπέδου εισόδου είναι $784*100=78400$ και οι πολώσεις των νευρώνων του κρυφού επιπέδου άλλες 100, οπότε στο σύνολο έχουμε $78.500$ παραμέτρους εκπαίδευσης στο επίπεδο 1ο κρυφό επίπεδο\n","\n","Κατ' αντίστοιχό τρόπο προκύπτον και οι 3.030 παράμετροι στο 2ο κρυφό επίπεδο και οι 310 στο επίπεδο εξόδου, οπότε στο σύνολο έχουμε $78.500+3.030+310=81.840$ εκπαιδεύσιμες παραμέτρους.\n","\n","Πλέον είμαστε έτοιμοι να ορίσουμε τις παραμέτρους εκπαίδευσης του μοντέλου όπως και στην περίπτωση του απλού perceptron"],"metadata":{"id":"EK1CR_YNRePJ"}},{"cell_type":"code","source":["mlp.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])"],"metadata":{"id":"xiByS4QtSqQ8"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Και κατόπιν να εκπαιδεύσουμε το μοντέλο στο σύνολο εκπαίδευσης, καλώντας τη μέθοδο *fit()*."],"metadata":{"id":"3CUdy0C6Swhk"}},{"cell_type":"code","source":["epochs=50\n","hist_mlp = mlp.fit(x_train, y_train, epochs=epochs, batch_size=32)"],"metadata":{"id":"pMhVByQ1SvTF"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Η προσθήκη δύο κρυφών επιπέδων **βελτίωσε την απόδοση** του συστήματος. Τώρα όμως το σύστημα χρειάζεται **περισσότερο χρόνο** να εκπαιδευτεί. Αυτό συμβαίνει για 2 λόγους: Αφενός με την προσθήκη του επιπλέον επιπέδου προσθέσαμε παραπάνω παραμέτρους προς εκπαίδευση. Αφετέρου όσο πιο βαθύ είναι ένα δίκτυο τόσο πιο πολύπλοκος είναι ο υπολογισμός των παραγώγων του πρώτου επιπέδου. Αν πάμε να αυξήσουμε πολύ παραπάνω τον αριθμό των επιπέδων, θα αυξηθεί πολύ και ο χρόνος εκπαίδευσης.\n","\n","Παρατηρούμε επίσης πως η διαδικασία της σύγκλισης ξεκινάει από υψηλότερες (χαμηλότερες) τιμές για την συνάρτηση απώλειας (ακρίβεια) σε σύγκριση με την περίπτωση του Perceptron. Αυτό οφείλεται στο γεγονός πως δεν κάναμε καλή επιλογή όσον αφορά την αρχικοποίηση των παραμέτρων (αρχικοποιήσαμε το δίκτυο \"μακρυά\" από περιοχή \"καλών\" παραμέτρων).\n","\n","Ας σχεδιάσουμε, όπως προηγουμένως, τις γραφικές παραστάσεις της συνάρτησης απώλειας και της ακρίβειας"],"metadata":{"id":"WhZfO5TaTjYn"}},{"cell_type":"code","source":["mlp_loss, mlp_acc = mlp.evaluate(x_test, y_test)\n","\n","print('\\nΣυνάρτηση απώλειας: \\t{:.2f}\\nΑκρίβεια:\\t\\t{:.2f}%'.format(mlp_loss, 100*mlp_acc))"],"metadata":{"id":"M7b9jMLzZhXz"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["simple_plot(hist_mlp, epochs)"],"metadata":{"id":"w0iilc-XWXEq"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["#### Συναρτησιακή Διεπαφή\n","\n","To ίδιο δίκτυο μπορούμε να το ορίσουμε τη χρήση της συναρτησιακής διεπαφής, όπως παρακάτω"],"metadata":{"id":"X4v9eBNcWohd"}},{"cell_type":"code","source":["inputs = tf.keras.Input(shape=(784,), name=\"Input\")\n","hidden1 = tf.keras.layers.Dense(100, kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), bias_initializer='zeros', activation='sigmoid', name=\"Hidden1\")(inputs)\n","hidden2 = tf.keras.layers.Dense(30, kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), bias_initializer='zeros', activation='sigmoid', name=\"Hidden2\")(hidden1)\n","predictions = tf.keras.layers.Dense(10, kernel_initializer='zeros', bias_initializer='zeros', activation='softmax', name=\"Output\")(hidden2)\n","\n","mlp_func = tf.keras.Model(inputs=inputs, outputs=predictions)\n","mlp_func.summary()"],"metadata":{"id":"txKKhqeOWyfQ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Στη συνέχεια, ορίζουμε τις παραμέτρους εκπαίδευσης και εκπαιδεύουμε το δίκτυο"],"metadata":{"id":"EooVMJq6YGpH"}},{"cell_type":"code","source":["mlp_func.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])\n","hist_mlp_func = mlp_func.fit(x_train, y_train, epochs=epochs, batch_size=32)"],"metadata":{"id":"kBX1XeAIYX3z"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Τέλος σχεδιάζουμε, όπως προηγουμένως, τις γραφικές παραστάσεις"],"metadata":{"id":"41rwXr78Y5g1"}},{"cell_type":"code","source":["simple_plot(hist_mlp_func , epochs)"],"metadata":{"id":"3zVvkbmhY-7f"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Καθώς και την απόδοση στο σύνολο ελέγχου"],"metadata":{"id":"839VoPBSaEZJ"}},{"cell_type":"code","source":["mlp_func_loss, mlp_func_acc = mlp_func.evaluate(x_test, y_test)\n","\n","print('\\nΣυνάρτηση απώλειας: \\t{:.2f}\\nΑκρίβεια:\\t\\t{:.2f}%'.format(mlp_func_loss, 100*mlp_func_acc))"],"metadata":{"id":"eTZC7onPaH30"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Μπορεί να πετύχαμε ένα accuracy γύρω στο 96%, στο MNIST όμως αυτό δεν είναι καλό. Στη συνέχεια θα δούμε μερικές λίγο πιο εξελιγμένες τεχνικές οι οποίες μπορούν να βελτιώσουν την απόδοση. \n","\n","Τα δίκτυα που έχουμε δει τώρα τα ονομάζουμε Πλήρως Διασυνδεμένα (**Fully Connected - FC**) επειδή ο καθένας από τους νευρώνες του ενός επιπέδου έχει συνδέσεις με όλους τους νευρώνες του επόμενου."],"metadata":{"id":"T-hSTXOBO8BP"}},{"cell_type":"markdown","source":["## Συνελικτικά Δίκτυα\n","\n","Τα Συνελικτικά Νευρωνικά Δίκτυα ([Convolutional Neural Networks](https://en.wikipedia.org/wiki/Convolutional_neural_network)) είναι βαθιά νευρωνικά δίκτυα τα οποία ειδικεύονται στην ανάλυση εικόνων. Τα δίκτυα αυτά συνδυάζουν δυο στοιχεία που επιτρέπουν τη δημιουργία αρχιτεκτονικών με μικρότερο αριθμό παραμέτρων: Την αραιή σύνδεση (**sparse connectivity**) μεταξύ των επιπέδων και τη χρήση κοινών βαρών (**weight sharing**).\n","\n","Τα δύο κυριότερα είδη επιπέδων στα δίκτυα αυτά είναι τα συνελικτικά (**convolutional**) και τα επίπεδα υποδειγματοληψίας (**pooling**)."],"metadata":{"id":"V0numZW3PB8p"}},{"cell_type":"markdown","metadata":{"collapsed":true,"id":"oPmy0yylK9vX"},"source":["### Επίπεδα Συνέλιξης\n","\n","Η βασική χρήση των συνελικτικών επιπέδων είναι η εξαγωγή χαρακτηριστικών από τις εικόνες. Δεν θα ασχοληθούμε με τη [μαθηματική ερμηνεία](https://en.wikipedia.org/wiki/Convolution) της συνέλιξης, θα προσπαθήσουμε να δούμε όμως πώς αυτή εφαρμόζεται στην εικόνα. Τη λειτουργία αυτή μπορούμε να τη φανταστούμε ως ένα σταθερό \"φίλτρο\" που περνάει από μία εικόνα. Ας θεωρήσουμε ότι έχουμε μια εικόνα $5 \\times 5$.\n","\n","![](https://ujwlkarn.files.wordpress.com/2016/07/screen-shot-2016-07-24-at-11-25-13-pm.png?w=254&h=230)\n","\n","Επίσης ορίζουμε το παρακάτω φίλτρο $3 \\times 3$.\n","\n","![](https://ujwlkarn.files.wordpress.com/2016/07/screen-shot-2016-07-24-at-11-25-24-pm.png?w=148&h=128)\n","\n","Η πράξη της συνέλιξης μεταξύ των δυο φαίνεται στο παρακάτω σχήμα.\n","\n","![](https://ujwlkarn.files.wordpress.com/2016/07/convolution_schematic.gif?w=268&h=196&zoom=2)\n","\n","Σημείωση: Στην είσοδο των δικτύων αυτών δίνουμε την εικόνα 2 διαστάσεων, καθώς τα δίκτυα αυτά ψάχνουν να βρουν χαρακτηριστικά στο χώρο.\n","\n","Το αποτέλεσμα της συνέλιξης το ονομάζουμε **feature map**. Διαφορετικά φίλτρα μπορούν να έχουν πολύ [διαφορετική επίδραση][1] πάνω στην εικόνα. Ένα ωραίο παράδειγμα θα βρείτε [εδώ](http://setosa.io/ev/image-kernels/) για τη λειτουργία της συνέλιξης στις εικόνες.\n","\n","Υπάρχουν 3 παράμετροι που μπορούμε να ελέγξουμε σε ένα συνελικτικό επίπεδο:\n","\n","- Τον πίνακα με τον οποίο γίνεται η συνέλιξη (συνήθως τον ονομάζουμε φίλτρο ή **kernel**).\n","- Το βήμα που κάνει ο πίνακας (το ονομάζουμε **stride** και στο προηγούμενο παράδειγμα ήταν 1).\n","- Το αν θα βγαίνει εκτός των διαστάσεων της εικόνας, προκειμένου να διατηρήσει τις ίδιες διαστάσεις και στην έξοδο (**padding**). \n","- Τον αριθμό των διαφορετικών feature maps που θα παραχθούν στο επίπεδο αυτό (συνήθως το αποκαλούμε βάθος-**depth**).\n","\n","Τις παραμέτρους του φίλτρου το δίκτυο τις μαθαίνει κατά την εκπαίδευση.\n","\n","Η πράξη της συνέλιξης έρχεται να αντικαταστήσει τον γραμμικό μετασχηματισμό ($W \\cdot x + b$ ) των FC επιπέδων. Ως **συνάρτηση ενεργοποίησης** χρησιμοποιούμε το **Rectified Linear Unit ([ReLU][2])**, το οποίο δίνεται από τη συνάρτηση:\n","\n","$$\n","f \\left( z \\right) = max \\left( 0, z \\right)\n","$$\n","\n","Ο λόγος που επιλέγουμε αυτή είναι κυρίως η απλότητά της που μας γλιτώνει από τον επιπλέον υπολογιστικό φόρτο μίας πιο σύνθετης συνάρτησης ενεργοποίησης.\n","\n","Η συνέλιξη σε δισδιάστατα δεδομένα υλοποιείται από την κλάση [Cov2D](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2D) του submodule keras.layers του Tensorflow. Θα χρησιμοποιήσουμε αποκλειστικά την ακολουθιακή διεπαφή. Η είσοδός μας τώρα θα έχει 3 διαστάσεις: το ύψος και πλάτος της εικόνας καθώς και τον αριθμό των καναλιών της (1 για ασπρόμαυρη, 3 για έγχρωμη). Τέλος, η αρχικοποίηση των βαρών των φίλτρων γίνεται με την TruncatedNormal ενώ οι πολώσεις αρχικοποιούνται σε τιμή $0.1$ με την χρήση της κλάσης [Constant](https://www.tensorflow.org/api_docs/python/tf/keras/initializers/Constant) του submodule keras.initializers του Tensorflow.\n","\n","\n","[1]: https://en.wikipedia.org/wiki/Kernel_(image_processing)\n","[2]: https://en.wikipedia.org/wiki/Rectifier_(neural_networks)"]},{"cell_type":"code","source":["cnn = tf.keras.models.Sequential(name='ConvolutionalNetwork')\n","\n","cnn.add(\n"," tf.keras.layers.Conv2D(\n"," filters=32, # 32 φίλτρα (kernels)\n"," kernel_size=(6, 6), # 6x6 pixel μέγεθος kernel\n"," strides=(1, 1), # 1 βήμα για όλες τις διαστάσεις εισόδου\n"," padding='same', # same padding για να μην αλλάξουν οι διαστάσεις\n"," activation='relu', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1),\n"," bias_initializer=tf.keras.initializers.Constant(0.1),\n"," input_shape=(28, 28, 1), # εικόνα εισόδου 28x28 pixel, 1 κανάλι (χρώμα)\n"," name='Convolutional1'\n"," )\n",")"],"metadata":{"id":"siStvuEfQxZ6"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Ας δούμε την αρχιτεκτονική του δικτύου (πλήθος των παραμέτρων) μέχρι στιγμής"],"metadata":{"id":"8cdeI9ftS_lZ"}},{"cell_type":"code","source":["cnn.summary()"],"metadata":{"id":"38fUPQnUTaHw"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Έχουμε 32 φίλτρα διάστασης $6\\times 6$, δηλαδή ο πίνακας των βαρών αποτελείται από $32\\times 6\\times 6 = 1.152$ παραμέτρους, στις οποίες προσθέτουμε και τις 32 πολώσεις (μια για κάθε φίλτρο) και καταλήγουμε στις $1.184$ παραμέτρους εκπαίδευσης.\n","\n","Επειδή χρησιμοποιούμε same padding δεν αλλάζουν οι διαστάσεις ύψους-πλάτους από την είσοδο στην έξοδο του επιπέδου. Άρα οι διαστάσεις εξόδου του επιπέδου είναι: $28 \\times 28 \\times 32$."],"metadata":{"id":"zyODZwOBTpHE"}},{"cell_type":"markdown","metadata":{"id":"LvNNNDdMK9vd"},"source":["### Επίπεδα Υποδειγματοληψίας\n","\n","Το επίπεδο αυτό μειώνει τη διάσταση της εισόδου του μέσω κάποιου είδους χωρικής υποδειγματοληψίας. Υπάρχουν αρκετά είδη υποδειγματοληψίας (μεγίστου, αθροίσματος, μέσου όρου, κτλ). Τη λειτουργία τους μπορούμε να τη δούμε αναλυτικά στο παρακάτω παράδειγμα υποδειγματοληψίας μεγίστου:\n","\n","![](https://ujwlkarn.files.wordpress.com/2016/08/screen-shot-2016-08-10-at-3-38-39-am.png?w=500)\n","\n","Όπως και πριν, υπάρχει ένα παράθυρο το οποίο μετακινείται και εφαρμόζει μια πράξη πάνω στα στοιχεία μιας περιοχής του πίνακα. Οι παράμετροι που καλούμαστε να επιλέξουμε στο επίπεδο αυτό είναι:\n","\n","- Τον τύπο της υποδειγματοληψίας (μεγίστου, στο προηγούμενο παράδειγμα).\n","- Το μέγεθος του παραθύρου (2x2 στο παράδειγμα).\n","- Το βήμα του παραθύρου (2 στο παράδειγμα).\n","\n","Το επίπεδο αυτό **δεν** έχει εκπαιδεύσιμες παραμέτρους και υπάρχει μόνο για να μειώσει τη διάσταση της εισόδου του διατηρώντας τη χωρική πληροφορία. Πιο συγκεκριμένα βοηθάει:\n","\n","- στη μείωση των συνολικών παραμέτρων του δικτύου.\n","- στο να κάνει την αναπαράσταση αμετάβλητη σε μικροαλλαγές στην είσοδο.\n","- στο να κάνει την αναπαράσταση ανεξάρτητη της κλίμακας (**equivariance**).\n"," \n","Στο TensorFlow αυτό υλοποιείται από την κλάση [MaxPool2D](https://www.tensorflow.org/api_docs/python/tf/keras/layers/MaxPool2D) του submodule keras.layers, για δισδιάστατα δεδομένα."]},{"cell_type":"code","source":["# Είναι το 2ο επίπεδο που προσθέτουμε στο δίκτυο, οπότε δεν χρειάζεται πλέον να \n","# ορίσουμε το μέγεθος των δεδομένων εισόδου (παράμετρος input_shape), μιας και\n","# αυτό υπολογίζεται αυτόματα από το framework.\n","\n","cnn.add(\n"," tf.keras.layers.MaxPool2D(\n"," pool_size=(2, 2), # Παράθυρο μεγέθους 2x2\n"," strides=(2, 2), # Βήμα παραθύρου 2 (και στι 2 διαστάσεις)\n"," padding='same',\n"," name='Pooling1'\n"," )\n",")"],"metadata":{"id":"-VyvMtP2V1fU"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Ας δούμε ξανά την αρχιτεκτονική του δικτύου (πλήθος των παραμέτρων) μέχρι στιγμής"],"metadata":{"id":"XQa-eOmDWeHU"}},{"cell_type":"code","source":["cnn.summary()"],"metadata":{"id":"vLsyvH6pWuqp"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Παρατηρούμε ότι το επίπεδο δειγματοληψίας δεν έχει εκπαιδεύσιμες παραμέτρους!\n","\n","Η συμπεριφορά αυτή είναι αναμενόμενη, μιας και στην υποδειγματοληψία δε χρησιμοποιούνται βάρη ή πολώσεις. Επίσης, επειδή χρησιμοποιούμε παράθυρο με μέγεθος 2 και βήμα 2, οι διαστάσεις της εισόδου του επιπέδου υποδιπλασιάζονται. Άρα η έξοδος του επιπέδου αυτού θα έχει διαστάσεις $14 \\times 14 \\times 32$."],"metadata":{"id":"fZO1RYG3W4Mq"}},{"cell_type":"markdown","source":["### Επέκταση Δικτύου"],"metadata":{"id":"lhsR1d3uo_-d"}},{"cell_type":"markdown","metadata":{"id":"XlvYyPJGK9vi"},"source":["Στη συνέχεια θα προσθέσουμε άλλα 2 επίπεδα, παρόμοια με τα 2 πρώτα. Το τρίτο θα είναι συνελικτικό και θα έχει 64 φίλτρα μεγέθους $5\\times 5$ και το τέταρτο θα είναι υποδειγματοληψίας μεγίστου (όπως και το 2ο, δηλαδή)."]},{"cell_type":"code","source":["cnn.add(\n"," tf.keras.layers.Conv2D(\n"," filters=64, \n"," kernel_size=(6, 6), \n"," strides=(1, 1), \n"," padding='same', \n"," activation='relu', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1),\n"," bias_initializer=tf.keras.initializers.Constant(0.1),\n"," name='Convolutional2'\n"," )\n",")\n","\n","cnn.add(\n"," tf.keras.layers.MaxPool2D(\n"," pool_size=(2, 2), \n"," strides=(2, 2), \n"," padding='same', \n"," name='Pooling2'\n"," )\n",")"],"metadata":{"id":"jp0-5r93YAq5"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Ας δούμε την αρχιτεκτονική του δικτύου, όπως έχει διαμορφωθεί μέχρι στιγμής"],"metadata":{"id":"frjl3SYsc4kM"}},{"cell_type":"code","source":["cnn.summary()"],"metadata":{"id":"I_kC3IEGc93q"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"rCQpi792K9vo"},"source":["### Πλήρως συνδεδεμένα επίπεδα\n","\n","Τα συνελικτικά επίπεδα είναι πολύ καλά στην εξαγωγή χαρακτηριστικών από τις εικόνες. Χρειαζόμαστε ακόμα και έναν ταξινομητή. Για το λόγο αυτό θα προσθέσουμε και ένα FC component στο τέλος του δικτύου, το οποίο θα κάνει και την τελική πρόβλεψη. \n","\n","Πριν προσθέσουμε, ωστόσο, τα πλήρως συνδεδεμένα επίπεδα, θα πρέπει να μετατρέψουμε την έξοδο του συνελικτικού δικτύου, μεγέθους (7, 7, 64) σε ένα διάνυσμα $7 \\times 7 \\times 64 = 3.136$ στοιχείων. Αυτό είναι εφικτό μέσω της κλάσης [Flatten](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Flatten) του submodule keras.layers του Tensorflow. Και σε αυτή την περίπτωση, τις διαστάσεις τις υπολογίζει το ίδιο το framework.\n","\n","Για την τελική προβολή των $3.136$ χαρακτηριστικών στους $10$ νευρώνες εξόδου, θα χρησιμοποιήσουμε ένα πλήρως διασυνδεδεμένο πολυεπίπεδο Perceptron, ενός κρυφού επιπέδου 1.024 νευρώνων και ένα επίπεδου εξόδου, 10 νευρώνων.\n","\n"]},{"cell_type":"code","source":["cnn.add(tf.keras.layers.Flatten(name=\"Flatten\"))\n","\n","cnn.add(\n"," tf.keras.layers.Dense(\n"," 1024, \n"," activation='relu', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), \n"," bias_initializer=tf.keras.initializers.Constant(0.1),\n"," name=\"Hidden1\"\n"," )\n",")\n","\n","cnn.add(\n"," tf.keras.layers.Dense(\n"," 10, \n"," activation='softmax', \n"," kernel_initializer=tf.keras.initializers.TruncatedNormal(mean=0., stddev=0.1), \n"," bias_initializer=tf.keras.initializers.Constant(0.1),\n"," name=\"Output\"\n"," )\n",")"],"metadata":{"id":"l0nA4jPZdi6m"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Η αρχιτεκτονική του τελικού μοντέλου είναι η παρακάτω"],"metadata":{"id":"rKVtKT0Oofyl"}},{"cell_type":"code","source":["cnn.summary()"],"metadata":{"id":"aTP-CFmUomDr"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Τέλος το μοντέλο θα το εκπαιδεύσουμε με τη χρήση της μεθόδου βελτιστοποίησης [Adam](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Adam) με ρυθμό μάθησης $10^{-4}$ (κλάση [Adam](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam) του submodule keras.optimizers του Tensorflow)."],"metadata":{"id":"z5i886meo1cW"}},{"cell_type":"code","source":["cnn.compile(optimizer=tf.keras.optimizers.Adam(1e-4), loss='categorical_crossentropy', metrics=['accuracy'])"],"metadata":{"id":"Tb9H1h2ao1vm"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Πριν προχωρήσουμε στην εκπαίδευση του δικτύου, θα πρέπει να επαναφέρουμε τα δεδομένα εισόδου σε μορφή κατάλληλη για το δίκτυο. Θυμηθείτε πως ορίσαμε \n","\n","```\n","...\n","input_size=(28,28,1)\n","...\n","```\n","ενώ η είσοδός μας είναι αυτή τη στιγμή διάνυσμα 784 χαρακτηριστικών (από την προεπεξεργασία για τα Percpetron). Άρα θα πρέπει να την επαναφέρουμε σε διάσταση $28\\times 28$ pixel, 1 καναλιού χρώματος, πάλι με την χρήση της *reshape()*\n"],"metadata":{"id":"m4PmaN5P1U0P"}},{"cell_type":"code","source":["print('Πριν')\n","print('----')\n","print('Διαστάσεις δεδομένων εκπαίδευσης:', x_train.shape)\n","print('Διαστάσεις δεδομένων ελέγχου:',x_test.shape)\n","\n","x_train = x_train.reshape((x_train.shape[0], 28, 28, 1))\n","x_test = x_test.reshape((x_test.shape[0], 28, 28, 1))\n","\n","print('Μετά')\n","print('----')\n","print('Διαστάσεις δεδομένων εκπαίδευσης:', x_train.shape)\n","print('Διαστάσεις δεδομένων ελέγχου:',x_test.shape)"],"metadata":{"id":"0QSnkKHD1P45"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Μπορούμε να εκπαιδεύσουμε το δίκτυο που ορίσαμε, καλώντας την μέθοδο *fit()* να δούμε αν βελτιώνει όντως την απόδοση!\n","\n","**ΠΡΟΣΟΧΗ:** Μην τρέξετε τον παρακάτω κώδικα χωρίς GPU Acceleration!\n","\n","Επειδή το μοντέλο αυτό έχει μια αυξημένη πολυπλοκότητα σε σχέση με τα προηγούμενα (έχει άνω των 3 εκατομμυρίων παραμέτρων!), θέλει και περισσότερη ώρα να τρέξει. Μπορεί να κάνει εώς και 15 λεπτά ανά 10 εποχές για να τρέξει!"],"metadata":{"id":"_-_EH6TDt2K_"}},{"cell_type":"code","source":["epochs=30\n","hist_cnn = cnn.fit(x_train, y_train, epochs=epochs, batch_size=64)"],"metadata":{"id":"zBzw5unxz-Ww"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"V277qneEGLO1"},"source":["## Η επανάσταση της Βαθιάς Μάθησης στην Όραση Υπολογιστών\n","\n","Παρατηρήστε στο train και το test πόσο πιο αποτελεσματικό είναι το συνελικτικό δίκτυο σε σχέση με το MLP, επιτυγχάνοντας ορθότητα ταξινόμησης στο σύνολο εκπαίδευσης 100%!"]},{"cell_type":"code","source":["simple_plot(hist_cnn, epochs)"],"metadata":{"id":"r-oInB2x7uyP"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Ας δούμε και τις αντίστοιχες τιμές στο σύνολο ελέγχου"],"metadata":{"id":"OkJERFOA-h58"}},{"cell_type":"code","source":["cnn_loss, cnn_acc = cnn.evaluate(x_test, y_test)\n","\n","print('\\nΣυνάρτηση απώλειας: \\t{:.2f}\\Ορθότητα:\\t\\t{:.2f}%'.format(cnn_loss, 100*cnn_acc))"],"metadata":{"id":"8UsrW1Or-hDo"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Η διαπίστωση ότι τα συνελικτικά δίκτυα είναι αποτελεσματικότερα από τα πολυεπίπεδα Perceptron σε προβλήματα εικόνας (computer vision) ήταν καθοριστική για την εδραίωση της βαθιάς μάθησης."],"metadata":{"id":"JDuI843n-_fX"}},{"cell_type":"markdown","source":["## Κοιτάξτε επίσης\n"],"metadata":{"id":"DRJ9T8wlyCsC"}},{"cell_type":"markdown","metadata":{"collapsed":true,"id":"bCCs1jkIK9v1"},"source":["- [Playground](http://playground.tensorflow.org): διαδραστικό περιβάλλον στον browser στο οποίο μπορούμε να δούμε την επίδραση περισσότερων επιπέδων/νευρώνων και διαφόρων συναρτήσεων ενεργοποίησης, καθώς και να οπτικοποιήσουμε τα βάρη των νευρώνων.\n","\n","- [TensorBoard](https://www.tensorflow.org/get_started/summaries_and_tensorboard): εφαρμογή στον browser για την οπτικοποίηση των παραμέτρων εκπαίδευσης.\n","\n","- [Save and load models](https://www.tensorflow.org/tutorials/keras/save_and_load): αποθήκευση και χρήση εκπαιδευμένων δικτύων.\n","\n","- [Dropout](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dropout): αφαίρεση νευρώνων για την αποφυγή της υπερπροσαρμογής. \n","\n","- [Early Stopping](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/EarlyStopping): ομαλοποίηση για την αποφυγή της υπερπροσαρμογής.\n","\n","- [Batch Normalization](https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization): καταπολέμηση προβλημάτων αστάθειας και επιτάχυνση εκπαίδευσης των μοντέλων.\n","\n","- [Νευρωνικά Δίκτυα και Βαθιά Μάθηση](http://neuralnetworksanddeeplearning.com/chap1.html): single-page θεωρία."]}]}