{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Autoencoders.ipynb","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"accelerator":"GPU"},"cells":[{"cell_type":"markdown","metadata":{"id":"8RwDm0n8n-7s"},"source":["# Αυτοκωδικοποιητές\n","\n","\n","\n","Όπως είπαμε και στη διάλεξη, οι **αυτοκωδικοποιητές** (*autoencoders*) είναι νευρωνικά δίκτυα τα οποία εκπαιδεύονται στο να αντιγράφουν την είσοδό τους στην έξοδό τους. Εσωτερικά αποτελούνται από κρυφό επίπεδο $\\mathbf{h}$, στο οποίο αναπαρίσταται (κωδικοποιείται) η είσοδος, ενώ το δίκτυο περιλαμβάνει δύο μέρη:\n","1. τη συνάρτηση **κωδικοποίησης** (*encoder function*) $\\mathbf{h} = f(x)$\n","1. τη συνάρτηση **αποκωδικοποίησης** (*decoder function*) $\\mathbf{r} = g(\\mathbf{h})$\n","\n","\n","Η εκπαίδευση τους πραγματοποιείται είτε μέσω τεχνικών οπίσθιας διάδοσης του σφάλματος (backpropagation) είτε μέσω **επανακυκλοφορίας** (recirculation), όπου συγκρίνεται η ενεργοποίηση των νευρώνων στην αρχική είσοδο και στην αναπαράσταση. Σε αυτό το notebook, η εκπαίδευση των δικτύων που θα δούμε βασίζεται στην πρώτη τεχνική.\n","\n","Οι αυτοκωδικοποιητές παρουσιάστηκαν για πρώτη φορά στα μέσα της δεκαετίας του 1980. Αν και η λειτουργία τους προσομοιάζει αρκετά με τους **απωλεστικούς αλγορίθμους συμπίεσης** (*lossy compresion algorithms*) όπως θα δούμε στη συνέχεια, χρησιμοποιούνται κυρίως σε προβλήματα **μείωσης διαστατικότητας** (*dimensionality reduction*) και **εξαγωγής χαρακτηριστικών** (*feature extraction*)."]},{"cell_type":"markdown","metadata":{"id":"ZfTvrH68vMLk"},"source":["## Εισαγωγή\n"]},{"cell_type":"markdown","metadata":{"id":"6IpSl0RghaJ4"},"source":["### Εισαγωγή Βιβλιοθηκών"]},{"cell_type":"markdown","metadata":{"id":"AgZqMTGEpfPa"},"source":["Στη συνέχεια εισάγουμε τις κλάσεις και τις μεθόδους που πρόκειται να χρησιμοποιήσουμε. Επίσης για να μπορούμε να αναπαράξουμε τα αποτελέσματα που θα λάβουμε, αρχικοποιούμε τη γεννήτρια ψευδοτυχαίων αριθμών χρησιμοποιώντας την ίδια σπορά (εδώ τον αριθμό $2022$)."]},{"cell_type":"code","metadata":{"id":"w4tmYQGMS374"},"source":["from keras import regularizers\n","from keras.datasets import mnist\n","from keras.layers import Input, Dense\n","from keras.models import Sequential\n","from sklearn.preprocessing import MinMaxScaler\n","from sklearn.metrics.pairwise import cosine_similarity\n","\n","import matplotlib.pyplot as plt\n","import numpy as np\n","import pandas as pd\n","\n","# Seed the pseudo-random number generator with a fixed seed for reproducible\n","# results\n","np.random.seed(2022)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"5LoZ5pqrheof"},"source":["### Ορισμός συναρτήσεων"]},{"cell_type":"markdown","metadata":{"id":"r7HHOIhyp7mQ"},"source":["Ορίζουμε δύο συναρτήσεις (μεθόδους) που πρόκειται να χρησιμοποιήσουμε για την παρουσίαση των αποτελεσμάτων, την *compare_digits()* και την *display_digits()*"]},{"cell_type":"code","metadata":{"id":"tBvkPPF-fr2V"},"source":["def compare_digits(test, decoded, n=10):\n"," plt.figure(figsize=(20, 4))\n"," for i in range(n):\n"," # display original\n"," ax = plt.subplot(2, n, i + 1)\n"," plt.imshow(test[i].reshape(28, 28))\n"," plt.gray()\n"," ax.get_xaxis().set_visible(False)\n"," ax.get_yaxis().set_visible(False)\n","\n"," # display reconstruction\n"," ax = plt.subplot(2, n, i + 1 + n)\n"," plt.imshow(decoded[i].reshape(28, 28))\n"," plt.gray()\n"," ax.get_xaxis().set_visible(False)\n"," ax.get_yaxis().set_visible(False)\n"," plt.show()\n"," \n"," \n","def display_digits(data, n=10):\n"," plt.figure(figsize=(20, 2))\n"," for i in range(n):\n"," ax = plt.subplot(1, n, i + 1)\n"," plt.imshow(data[i].reshape(28, 28))\n"," plt.gray()\n"," ax.get_xaxis().set_visible(False)\n"," ax.get_yaxis().set_visible(False)\n"," plt.show()"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"N-tUpSqUhkRK"},"source":["## Δεδομένα"]},{"cell_type":"markdown","metadata":{"id":"piEA2NarqaKm"},"source":["Για να κατανοήσουμε καλύτερα τη λειτουργία των αυτοκωδικοποιητών, θα χρησιμοποιήσουμε το (γνώστό σε προβλήματα βαθιάς μηχανικής μάθησης) dataset [MNIST,](http://yann.lecun.com/exdb/mnist/), το οποίο περιέχει $70.000$ σκαναρισμένες εικόνες ψηφίων που αντιστοιχούν στους αριθμούς $0$ ως $9$, όπως αυτοί έχουν γραφτεί από διαφορετικούς ανθρώπους.\n","\n","Η κάθε εικόνα έχει μέγεθος $28\\times28$ pixels και είναι ασπρόμαυρη. Επιπλέον, οι δημιουργοί του dataset τις έχουν χωρίσει σε δύο σύνολα, στο σύνολο εκπαίδευσης (που περιέχει $60.000$ εικόνες) και στο σύνολο ελέγχου (που περιέχει $10.000$ εικόνες). \n","\n","Μιας και το MNIST dataset παρέχεται (και) μέσω του keras, το φορτώνουμε μέσω αυτής της βιβλιοθήκης. Κατόπιν μετατρέπουμε την δισδιάστατη εικόνα σε ένα διάνυσμα $784$ χαρακτηριστικών και τέλος μετασχηματίζουμε γραμμικά τις τιμές των pixels από το $[0,255]$ στο $[0,1]$ μέσω της κλάσης *MinMaxScaler* του scikit-learn."]},{"cell_type":"code","metadata":{"id":"0gg1ABRRUfwz"},"source":["(x_train, _), (x_test, _) = mnist.load_data()\n","\n","x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))\n","x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))\n","\n","scaler = MinMaxScaler()\n","x_train = scaler.fit_transform(x_train)\n","x_test = scaler.fit_transform(x_test)\n","\n","print(x_train.shape)\n","print(x_test.shape)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"VU7CeHUUhqak"},"source":["## Υποπλήρης αυτοκωδιοποιητής"]},{"cell_type":"markdown","metadata":{"id":"js2_LTwkvdVe"},"source":["Σύμφωνα με τα όσα είπαμε στη διάλεξη, κατά την εκπαίδευση των αυτοκωδικοποιητών δεν μας ενδιαφέρει τόσο η διαδικασία της αντιγραφής, όσο το να μάθει το δίκτυο να εξάγει και να αποτυπώνει στο επίπεδο κωδικοποίησης ($\\mathbf{h}$) χρήσιμες ιδιότητες του χώρου εισόδου ($\\mathbf{x}$). Ένας τρόπος για να αποτρέπουμε το δίκτυο από το να μάθει απλά να αντιγράφει την είσοδό του στην έξοδό του είναι να επιβάλλουμε τον περιορισμό η διάσταση της κωδικοποίησης να είναι μικρότερη της διάστασης της εισόδου ($\\dim(\\mathbf{h}) < \\dim(\\mathbf{x}))$, κατασκευάζοντας κατ' αυτόν τον τρόπο τους **υποπλήρεις αυτοκωδικοποιητές** (*underbomplete autoencoders*)\n","\n","Στο παράδειγμα που εξετάζουμε, η διάσταση της εισόδου είναι $28\\times28 = 784$ χαρακτηριστικά. Θα την περιορίσουμε (αυθαίρετα) στα $32$ (επιτυγχάνοντας ένα λόγο συμπίεσης $24.5$)"]},{"cell_type":"code","metadata":{"id":"M5HZ7q0YwoLe"},"source":["dim_x = 28*28\n","dim_h = 32"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"qrAfyx4nwsBs"},"source":["Επιλέγουμε για συνάρτηση κωδικοποίησης την [Rectified Linear Unit (ReLU)](https://en.wikipedia.org/wiki/Rectifier_%28neural_networks%29) και για συνάρτηση αποκωδικοποίησης τη [σιγμοειδή](https://en.wikipedia.org/wiki/Sigmoid_function). Έτσι η συνάρτηση κωδικοποίησης γίνεται $f(\\mathbf{x}) = \\max(0, \\mathbf{w}_e\\mathbf{x} + \\mathbf{b}_e)$ και η συνάρτηση απόκωδικοποίησης $g(\\mathbf{h}) = S(\\mathbf{w}_d\\mathbf{h} + \\mathbf{b}_d)$. Κατά τη διάρκεια της εκπαίδευσης (που πραγματοποιείται με τη μέθοδο Adam), το δίκτυο μαθαίνει τα βάρη ($\\mathbf{w}_e, \\mathbf{w}_d$) και τις πολώσεις ($\\mathbf{b}_e, \\mathbf{b}_d$). Τέλος, η συνάρτηση σφάλματος που βελτιστοποιείται είναι η [δυαδική διασταυρούμενη εντροπία](https://en.wikipedia.org/wiki/Cross_entropy)"]},{"cell_type":"code","metadata":{"id":"-1LBKWxYOPzg"},"source":["autoencoder = Sequential([\n"," Dense(dim_h, activation='relu', input_shape=(dim_x,)),\n"," Dense(dim_x, activation='sigmoid')\n","])\n","\n","\n","autoencoder.compile(optimizer='adam', loss='binary_crossentropy')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"giNZHkyH39Qy"},"source":["Εκπαιδεύουμε το δίκτυο που ορίσαμε προηγουμένως για 50 εποχές και με batch size 64. Προσέξτε ότι, μιας και πρόκειται για αυτοκωδικοποιητή, η είσοδος και η έξοδος ταυτίζονται (x_train)."]},{"cell_type":"code","metadata":{"id":"2FrkksFgUr__"},"source":["autoencoder.fit(x_train, x_train,\n"," epochs=50,\n"," batch_size=64,\n"," shuffle=True,\n"," validation_data=(x_test, x_test))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"q5GLHnaV4agO"},"source":["Παρατηρούμε ότι δεν παρουσιάστηκαν φαινόμενα υπερπροσαρμογής στην εκπαίδευση. Το σφάλμα εκπαίδευσης ξεκίνησε από το $0.26$, έπεσε γρήγορα μετά τις πρώτες εποχές και τέλος σταθεροποιήθηκε γύρω στο $0.09$. Παρόμοια συμπεριφορά εμφάνισε και το σφάλμα επαλήθευσης.\n","\n","\n","Για να μπορέσουμε να εκτιμήσουμε την είσοδο και την έξοδο του αυτοκωδικοποητή που ορίσαμε προηγουμένως, κατασκευάζουμε τα αντίστοιχα δίκτυα στο keras. Προσέξτε ότι στον ορισμό τους δεν χρησιμοποιούμε την κλάση *Dense* όπως πριν, αλλά αναφερόμαστε απευθείας στα επίπεδα του αυτοκωδικοποιητή. Ο λόγος που το κάνουμε αυτό είναι γιατί θέλουμε να αντιγράψουμε τις τιμές των βαρών ($\\mathbf{w}_e, \\mathbf{w}_d$) και των πολώσεων ($\\mathbf{b}_e, \\mathbf{b}_d$) που προέκυψαν από την εκπαίδευση του δικτύου."]},{"cell_type":"code","metadata":{"id":"F4jdkEKAbTeR"},"source":["encoder = Sequential([\n"," autoencoder.layers[-2]\n","])\n","\n","decoder = Sequential([\n"," autoencoder.layers[-1]\n","])\n","\n","h = encoder.predict(x_test)\n","x_test_out = decoder.predict(h)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"8j2t0o4D64__"},"source":["Μέσω της μεθόδου *compare_digits()* που ορίσαμε στην αρχή του notebook, θα εμφανίσουμε στην πρώτη σειρά τα δέκα πρώτα ψηφία του συνόλου ελέγχου και στη δεύτερη σειρά, τα ίδια ψηφία όπως προκύπτουν από την έξοδο του αυτοκωδικοποιητή"]},{"cell_type":"code","metadata":{"id":"8vHdghvOUww_"},"source":["compare_digits(x_test, x_test_out)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"F60Ql7sP7beI"},"source":["Απ' ότι φαίνεται, παρότι ο αυτοκωδικοποιητής χάνει λίγο σε λεπτομέρεια (εμφανέστερα στο 8ο και 9ο ψηφίο που είναι ο αριθμός $9$ και $5$ αντίστοιχα) κατορθώνει να μάθει σε ικανοποιητικό βαθμό τα χαρακτηριστικά της εισόδου του συγκεκριμένου dataset.\n","\n","Ας δούμε την αντίστοιχη κωδικοποίηση των $10$ πρώτων ψηφίων ($32$ χαρακτηριστικά)"]},{"cell_type":"code","metadata":{"id":"WQ1OPOdC9FJ-"},"source":["pd.set_option('display.max_columns', dim_h) #display 32 columns\n","pd.DataFrame(data=h[0:10], columns=range(1,dim_h+1), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"YIbaD6b__Law"},"source":["Οι αυτοκωδικοποιητές μαθαίνουν κατανεμημένες αναπαραστάσεις (distributed representations) της εισόδου τους. Συνεπώς αναμένουμε οι αναπαραστάσεις όμοιων ψηφίων στο επίπεδο κωδικοποίησης να είναι περισσότερο \"κοντά\". Ας υπολογίσουμε την ομοιότητα συνημιτόνου για τις αναπαραστάσεις των $10$ πρώτων ψηφίων του συνόλου ελέγχου."]},{"cell_type":"code","metadata":{"id":"vWbLbxauqDTd"},"source":["pd.DataFrame(data=cosine_similarity(h[0:10]), columns=range(1,11), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"G0kMBcPnNrrO"},"source":["Αναμένουμε το 3ο και το 6ο ψηφίο να είναι εγγύτερα (αριθμός $1$), όπως αντίστοιχα το 5ο και το 7ο (αριθμός $4$) και το 8ο και το 10ο (αριθμός $9$). Ωστόσο από τον παραπάνω πίνακα παρατηρούμε ότι το 5ο ψηφίο είναι εγγύτερα στο 9ο (αριθμός $9$) απ' ότι στο 7ο. Από την άλλη, το 7ο ψηφίο είναι εγγύτερα στο 1ο (σε σύγκριση με τα υπόλοιπα πάντα). Παρόμοιες παρατηρήσεις μπορούμε να κάνουμε για τα άλλα ζεύγη.\n","\n","Καταλήγουμε στο συμπέρασμα ότι μπορεί ναι μεν η εκπαίδευση του αυτοκωδικοποιητή να ολοκληρώθηκε με χαμηλό σφάλμα εκπαίδευσης και γενίκευσης, ωστόσο η αναπαράσταση που έμαθε χρήζει βελτίωσης. Η ζητούμενη βελτίωση μπορεί να επιτευχθεί μεταβάλλοντας τα χαρακτηριστικά του αυτοκωδικοποιητή (μέγεθος κωδικοποίησης, συναρτήσεις (από)κωδικοποίησης, αλγόριθμος σφάλματος και εκπαίδευσης) αλλά και εξετάζοντας διαφορετικές αρχιτεκτονικές αυτοκωδικοποιητών, όπως θα δούμε παρακάτω.\n","\n","Πριν προχωρήσουμε, ας δούμε τη μέση τιμή των χαρακτηριστικών του υποπλήρη αυτοκωδικοποιητή "]},{"cell_type":"code","metadata":{"id":"tNnkzN_fqust"},"source":["print(h.mean())"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"dGbZQUoKhy2M"},"source":["## Αραιός αυτοκωδικοποιητής"]},{"cell_type":"markdown","metadata":{"id":"nCNuE3oSS3WY"},"source":["Στον **αραιό αυτοκωδικοποιητή** (*sparse autoencoder*) επιβάλλουμε όρο ποινής αραιότητας $\\Omega(\\mathbf{h})$ του επιπέδου κωδικοποίησης $\\mathbf{h}$ στη διαδικασία μάθησης (παράμετρος *activity_regularizer* της κλάσης *Dense* του keras)\n","\n","Στη συγκεκριμένη περίπτωση επιλέγουμε $L_1$ κανονικοποίηση με βάρος $\\lambda=10^{-4}$, δηλαδή $\\Omega(\\mathbf{h}) = 10^{-4}||\\mathbf{h}||$"]},{"cell_type":"code","metadata":{"id":"l_ucBa5teUba"},"source":["sparse_autoencoder = Sequential([\n"," Dense(dim_h, activation='relu', \n"," activity_regularizer=regularizers.l1(10e-4), \n"," input_shape=(dim_x,)),\n"," Dense(dim_x, activation='sigmoid')\n","])\n","\n","\n","sparse_autoencoder.compile(optimizer='adam', loss='binary_crossentropy')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"lc4V1KadUYvq"},"source":["Εκπαιδεύουμε τον αυτοκωδικοποιητή, όπως προηγουμένως."]},{"cell_type":"code","metadata":{"id":"qleHxJHxexCH"},"source":["sparse_autoencoder.fit(x_train, x_train,\n"," epochs=50,\n"," batch_size=64,\n"," shuffle=True,\n"," validation_data=(x_test, x_test))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"wBspI15yWdbN"},"source":["Παρατηρούμε ότι ούτε σε αυτή την περίπτωση παρουσιάστηκαν φαινόμενα υπερπροσαρμογής στην εκπαίδευση, αν και το σφάλμα εκπαίδευσης είναι ελαφρώς μεγαλύτερο απ' ότι στην προηγούμενη περίπτωση\n","\n","Όπως και πριν, ορίζουμε τα δίκτυα του κωδικοποιητή και του αποκωδικοποιητή."]},{"cell_type":"code","metadata":{"id":"QYXKlBHXfLlf"},"source":["sparse_encoder = Sequential([\n"," sparse_autoencoder.layers[-2]\n","])\n","\n","sparse_decoder = Sequential([\n"," sparse_autoencoder.layers[-1]\n","])\n","\n","h_sparse = sparse_encoder.predict(x_test)\n","x_test_out_sparse = sparse_decoder.predict(h_sparse)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"yxjYdct0XdxW"},"source":["Εμφανίζουμε στην πρώτη σειρά τα δέκα πρώτα ψηφία του συνόλου ελέγχου και στη δεύτερη σειρά, τα ίδια ψηφία όπως προκύπτουν από την έξοδο του αραιού αυτοκωδικοποιητή"]},{"cell_type":"code","metadata":{"id":"bPCOUKhHfRuT"},"source":["compare_digits(x_test, x_test_out_sparse)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"q3z_r68rX7KS"},"source":["Απ' ότι φαίνεται, ο αραιός αυτοκωδικοποιητής εμφανίζει παρόμοια συμπεριφορά με τον υποπλήρη, κατορθώνοντας όμως να μάθει σε ικανοποιητικό βαθμό τα χαρακτηριστικά της εισόδου του συγκεκριμένου dataset.\n","\n","Ας δούμε την αντίστοιχη κωδικοποίηση των $10$ πρώτων ψηφίων ($32$ χαρακτηρισικά)"]},{"cell_type":"code","metadata":{"id":"65s7RSVjqQkB"},"source":["pd.DataFrame(data=h_sparse[0:10], columns=range(1,dim_h+1), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"oiBdMrTDY74I"},"source":["Παρατηρούμε ότι οι τιμές που λαμβάνουν τα χαρακτηριστικά είναι πιο χαμηλές σε σύγκριση με την περίπτωση του υποπλήρη αποκωδικοποιητή, πράγμα που οφείλεται στην επίδραση του όρου αραιότητας. \n","\n","Ας υπολογίσουμε την ομοιότητα συνημιτόνου για τις αναπαραστάσεις των 10 πρώτων ψηφίων του συνόλου ελέγχου."]},{"cell_type":"code","metadata":{"id":"N62az1DNrDEi"},"source":["pd.DataFrame(data=cosine_similarity(h_sparse[0:10]), columns=range(1,11), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"m6hlKn6GelyD"},"source":["Παρατηρούμε καταρχήν πως τα εύρη των ομοιοτήτων είναι πιο μεγάλα σε σύγκριση με την προηγούμενη περίπτωση. Επίσης, παρότι το σφάλμα εκπαίδευσης ήταν ελαφρώς χειρότερο, παρατηρούμε ότι η αναπαράσταση που επιτύχαμε είναι καλύτερη. Για παράδειγμα, το πιο όμοιο στο 3ο ψηφίο είναι το 6ο και το αντίστοιχο ισχύει και για το 6ο ψηφίο (και τα δύο απεικονίζουν τον αριθμό $1$). Αντίστοιχο συμπέρασμα μπορεί να βγει και για το 5ο και 7ο ψηφίο (όχι όμως για το 8ο και 10ο).\n","\n"]},{"cell_type":"code","metadata":{"id":"xpBrKcldhoHP"},"source":["print(h_sparse.mean())"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"PS3upZqxhohQ"},"source":["Τέλος, παρατηρούμε ότι η μέση τιμή των χαρακτηριστικών του αραιού αυτοκωδικοποιητή είναι χαμηλότερη σε σύγκριση με του υποπλήρη ($0.30$ και $6.54$ αντίστοιχα)"]},{"cell_type":"markdown","metadata":{"id":"gQdSBZGOh4mD"},"source":["## Αυτοκωδικοιητής απαλοιφής θορύβου"]},{"cell_type":"markdown","metadata":{"id":"ajqqcs3PwhKU"},"source":["\n","\n","Σε αυτή την περίπτωση επιβάλλουμε στον αυτοκωδικοποιητή να μάθει τα χαρακτηριστικά της εισόδου $\\mathbf{x}$ , \"αλλοιώνοντάς\" την μέσω της προσθήκης θορύβου κανονικής κατανομής με μέση τιμή $\\mu=0$ και τυπική απόκλιση $\\sigma = 1$: $\\mathcal{N}(0, 1)$. Επίσης ορίζουμε τον βαθμό συνεισφοράς του θορύβου (noise factor) στα αρχικά δεδομένα ίσο με $n_f = 0.3$. Έχουμε δηλαδή:\n","\n","$\\widetilde{\\mathbf{x}} = \\mathbf{x} + 0.3\\mathcal{N}(0, 1)$ "]},{"cell_type":"code","metadata":{"id":"VqPBlVbYiH03"},"source":["noise_factor = 0.3\n","x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, \n"," size=x_train.shape) \n","x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, \n"," size=x_test.shape) \n","\n","x_train_noisy = np.clip(x_train_noisy, 0., 1.)\n","x_test_noisy = np.clip(x_test_noisy, 0., 1.)\n","\n","print(x_train_noisy.shape)\n","print(x_test_noisy.shape)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"IIE7OrGhkqMq"},"source":["Ας δούμε τα $10$ πρώρα ψηφία του συνόλου ελέγχου μετά την προσθήκη του θορύβου"]},{"cell_type":"code","metadata":{"id":"lo-Ht040i8IJ"},"source":["display_digits(x_test_noisy)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"gqOXYgLpk7sk"},"source":["Το δίκτυο έχει την ίδια δομή με έναν υποπλήρη αυτοκωδικοποιητή"]},{"cell_type":"code","metadata":{"id":"0ajCzaOEluQD"},"source":["noisy_autoencoder = Sequential([\n"," Dense(dim_h, activation='relu', input_shape=(dim_x,)),\n"," Dense(dim_x, activation='sigmoid')\n","])\n","\n","\n","noisy_autoencoder.compile(optimizer='adam', loss='binary_crossentropy')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"GkS5baBFlqTj"},"source":["Το εκπαιδεύουμε για ίδιο αριθμό εποχών"]},{"cell_type":"code","metadata":{"id":"bfFPAVpjl50-"},"source":["noisy_autoencoder.fit(x_train_noisy, x_train,\n"," epochs=50,\n"," batch_size=64,\n"," shuffle=True,\n"," validation_data=(x_test, x_test))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"dVMG7frFlwVt"},"source":["Παρατηρούμε ότι ούτε σε αυτή την περίπτωση παρουσιάστηκαν φαινόμενα υπερπροσαρμογής στην εκπαίδευση, με το αντίστοιχο σφάλμα να είναι στα ίδια επίπεδα με τον αραιό αυτοκωδικοποιητή (και ελαφρώς χειρότερο απ' ότι στον υποπλήρη αυτοκωδικοποιητή)\n","\n","Ορίζουμε τα δίκτυα του κωδικοποιητή και του αποκωδικοποιητή."]},{"cell_type":"code","metadata":{"id":"nKgtSbaPmBn2"},"source":["noisy_encoder = Sequential([\n"," noisy_autoencoder.layers[-2]\n","])\n","\n","noisy_decoder = Sequential([\n"," noisy_autoencoder.layers[-1]\n","])\n","\n","h_noisy = noisy_encoder.predict(x_test_noisy)\n","x_test_out_noisy = noisy_decoder.predict(h_noisy)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"IyP8AB45mxd5"},"source":["Εμφανίζουμε στην πρώτη σειρά τα δέκα πρώτα ψηφία του συνόλου ελέγχου (με θόρυβο) και στη δεύτερη σειρά, τα ίδια ψηφία όπως προκύπτουν από την έξοδο του αραιού αυτοκωδικοποιητή"]},{"cell_type":"code","metadata":{"id":"WUqrololmLwZ"},"source":["compare_digits(x_test_noisy, x_test_out_noisy)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"t4uWOdSIm-EO"},"source":["Απ' ότι φαίνεται, ο αυτοκωδικοποητής απαλοιφής θορύβου κατορθώνει να γενικεύσει, μιας και μπορεί να αναπαριστά τα ψηφία χωρίς θόρυβο (ωστόσο όχι τόσο καλά όσο οι προηγούμενοι δύο).\n","\n","Ας δούμε την αντίστοιχη κωδικοποίηση των $10$ πρώτων ψηφίων ($32$ χαρακτηριστικά)"]},{"cell_type":"code","metadata":{"id":"tynTUs5amM3w"},"source":["pd.DataFrame(data=h_noisy[0:10], columns=range(1,dim_h+1), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Aub0yBDMnmV_"},"source":["Εδώ δεν υπάρχει ποινή αραιόητας, οπότε τα χαρακτηριστικά λαμβάνουν τιμές αντίστοιχες με τον υποπλήρη αυτοκωδικοποιητή. \n","\n","Ας υπολογίσουμε την ομοιότητα συνημιτόνου για τις αναπαραστάσεις των 10 πρώτων ψηφίων του συνόλου ελέγχου."]},{"cell_type":"code","metadata":{"id":"qVVEapqWmP5B"},"source":["pd.DataFrame(data=cosine_similarity(h_noisy[0:10]), columns=range(1,11), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"6cU0IZ08oQjA"},"source":["Σε αυτή την περίπτωση, οι ομοιότητες συνημιτόνου είναι ακόμα μεγαλύτερες σε σύγκριση με τις δύο προηγούμενες περιπτώσεις. Επίσης, πάλι το 3ο και το 6ο ψηφίο είναι πιο όμοια μεταξύ τους και το ίδιο ισχύει σχεδόν για όλα τα υπόλοιπα ζεύγη ψηφίων (5ο, 7ο και 8ο, 10ο - κάντε τις συγκρίσεις με τις προηγούμενες περιπτώσεις).\n"]},{"cell_type":"markdown","metadata":{"id":"AxG4EkgPnkZS"},"source":["## Βαθύς Αυτοκωδικοποιητής"]},{"cell_type":"markdown","metadata":{"id":"xwxgXuyyyN-P"},"source":["\n","\n","Στους **βαθείς αυτοκωδικοποιητές** (*deep autoencoders*) οι συναρτήσεις (από)κωδικοποίησης δεν αποτελούνται από ένα μόνο επίπεδο αλλά από περισσότερα. Στο παράδειγμα που εξετάζουμε, συνάρτηση κωδικοποίησης αποτελείται από $3$ επίπεδα με διαστάσεις $128, 64$ και $32$ αντίστοιχα και με συνάρτηση ενεργοποίησης την ReLU. Ίδιο αριθμό επίπεδων (με ανάστροφη σειρά) έχει η συνάρτηση αποκωδικοποίησης, με το τελευταίο να έχει συνάρτηση ενεργοποίησης τη σιγμοειδή (για να \"σπάσει\" τη συμμετρία). Στη γενικότερη περίπτωση ωστόσο, δεν είναι υποχρεωτικό οι συναρτήσεις (απο)κωδικοποίησης να έχουν ίδιο αριθμό επιπέδων\n"]},{"cell_type":"code","metadata":{"id":"Rm88em1hn02l"},"source":["layer1_dim = 128\n","layer2_dim = 64\n","\n","deep_autoencoder = Sequential([\n"," Dense(layer1_dim, activation='relu', input_shape=(dim_x,)),\n"," Dense(layer2_dim, activation='relu'),\n"," Dense(dim_h, activation='relu'),\n"," \n"," Dense(layer2_dim, activation='relu'),\n"," Dense(layer1_dim, activation='relu'),\n"," Dense(dim_x, activation='sigmoid')\n","])\n","\n","\n","deep_autoencoder.compile(optimizer='adam', loss='binary_crossentropy')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"_c0y9NNpvdyr"},"source":["Εκπαιδεύουμε για 100 εποχές"]},{"cell_type":"code","metadata":{"id":"fs9esJHupGJC"},"source":["deep_autoencoder.fit(x_train, x_train,\n"," epochs=100,\n"," batch_size=64,\n"," shuffle=True,\n"," validation_data=(x_test, x_test))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"4tPUROCBxG4M"},"source":["Παρατηρούμε ότι ούτε σε αυτή την περίπτωση παρουσιάστηκαν φαινόμενα υπερπροσαρμογής στην εκπαίδευση. Ωστόσο το σφάλμα εκπαίδευσης και επαλήθευσης είναι χαμηλότερο απ' ότι σε όλες τις άλλες περιπτώσεις\n","\n","Ορίζουμε τα δίκτυα του κωδικοποιητή και του αποκωδικοποιητή."]},{"cell_type":"code","metadata":{"id":"cR_WzjM6xFfz"},"source":["deep_encoder = Sequential([\n"," deep_autoencoder.layers[0],\n"," deep_autoencoder.layers[1],\n"," deep_autoencoder.layers[2]\n","])\n","\n","deep_decoder = Sequential([\n"," deep_autoencoder.layers[-3],\n"," deep_autoencoder.layers[-2],\n"," deep_autoencoder.layers[-1]\n","])\n","\n","h_deep = deep_encoder.predict(x_test)\n","x_test_out_deep = deep_decoder.predict(h_deep)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"BIsqM8x2yNja"},"source":["Εμφανίζουμε στην πρώτη σειρά τα δέκα πρώτα ψηφία του συνόλου ελέγχου και στη δεύτερη σειρά, τα ίδια ψηφία όπως προκύπτουν από την έξοδο του βαθύ αυτοκωδικοποιητή."]},{"cell_type":"code","metadata":{"id":"bFqWHHohpSkm"},"source":["compare_digits(x_test, x_test_out_deep)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"97QR9a_vydVv"},"source":["Εκ πρώτης όψεως, ο βαθύς αυτοκωδικοποιητής έχει μάθει την καλύτερη αναπαράσταση σε σχέση με τους υπόλοιπους. \n","\n","Ας δούμε την αντίστοιχη κωδικοποίηση των $10$ πρώτων ψηφίων ($32$ χαρακτηριστικά)"]},{"cell_type":"code","metadata":{"id":"OxT7bX_zyyP6"},"source":["pd.DataFrame(data=h_deep[0:10], columns=range(1,dim_h+1), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"YiR2Fu5hzC-n"},"source":["Ας υπολογίσουμε την ομοιότητα συνημιτόνου για τις αναπαραστάσεις των 10 πρώτων ψηφίων του συνόλου ελέγχου."]},{"cell_type":"code","metadata":{"id":"cY7x2EUGzEib"},"source":["pd.DataFrame(data=cosine_similarity(h_deep[0:10]), columns=range(1,11), index=range(1,11)).head(10)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"D30ptkWQy8M0"},"source":["Παρατηρούμε ότι σε αυτή την περίπτωση τα 3 ζεύγη όμοιων ψηφίων (3ο, 6ο - 5ο, 7ο - 8ο, 10ο) είναι ανάμεσα στα πιο όμοια μεταξύ τους"]},{"cell_type":"markdown","metadata":{"id":"VLTe6LM6vxAC"},"source":["## Προαιρετικές Ασκήσεις"]},{"cell_type":"markdown","metadata":{"id":"Ap-8EYn_vz1_"},"source":["### Υποπλήρης Αυτοκωδικοποιητής\n","\n","1. Αντιστρέψετε τις συναρτήσεις (απο)κωδικοποίησης. Τι παρατηρείτε;\n","2. Χρησιμοποιείστε την ίδια συνάρτηση κωδικοποίησης και αποκωδικοποίησης (λχ τη relu). Τι παρατηρείτε;\n","\n","\n","### Αραιός Αυτοκωδικοποιητής\n","\n","1. Δοκιμάστε να αυξήσετε το βάρος της ποινής αραιότητας. Τι παρατηρείτε;\n","\n","\n","### Αυτοκωδικοποιητής απαλοιφής θορύβου\n","\n","1. Δοκομάστε να μεταβάλλετε (αυξήσετε/μειώσετε) το συντελεστή θορύβου. Τι παρατηρείτε;\n","\n","\n","### Βαθύς αυτοκωδικοποιητής\n","\n","1. Δοκιμάστε να χρησιμοποιήσετε διαφορετικές συναρτήσεις στο επίπεδο αποκωδικοποίησης. Τι παρατηρείτε;"]},{"cell_type":"markdown","metadata":{"id":"Lg3RIRB2p4YJ"},"source":["## Επιπρόσθετο υλικό"]},{"cell_type":"markdown","metadata":{"id":"AxOTzELUw4ly"},"source":["1. https://blog.keras.io/building-autoencoders-in-keras.html"]}]}