Autoencoder

H
{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "autoencoder.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "id": "M80SNUtte322",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Importing Packages and dataset\n",
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "from matplotlib import pyplot as plt\n",
        "from tensorflow.keras.layers import Input,Conv2D,MaxPooling2D,UpSampling2D,Conv2DTranspose\n",
        "from tensorflow.keras.models import Model\n",
        "from tensorflow.keras.optimizers import Adam,RMSprop\n",
        "from tensorflow.keras import backend as K\n",
        "import sklearn\n",
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "#Setting up the dataset\n",
        "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
        "\n",
        "#Reshaping the data as per the model requirement\n",
        "x_train = x_train.reshape(-1, 28,28, 1)/255\n",
        "x_test = x_test.reshape(-1, 28,28, 1)/255\n",
        "\n",
        "#Splitting the data for validation\n",
        "X,X_val,Y,Y_val = train_test_split(x_train,x_train,test_size=0.2)"
      ],
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "U8h6JqK3e8iz",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Define the autoencoder model\n",
        "class AutoEncoder(tf.keras.Model):\n",
        "\n",
        "    def __init__(self):\n",
        "        super(AutoEncoder, self).__init__()\n",
        "        self.encoder = tf.keras.Sequential(\n",
        "            [\n",
        "            Conv2D(256, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(128, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(64, (3, 3), activation='relu', padding='same'),\n",
        "            MaxPooling2D(pool_size=(2, 2)),\n",
        "            Conv2D(32, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(16, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(8, (1, 1), activation='relu', padding='same'),\n",
        "            MaxPooling2D(pool_size=(2, 2)),\n",
        "            Conv2D(4, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(2, (1, 1), activation='relu', padding='same'),\n",
        "            Conv2D(1, (1, 1), activation='relu', padding='same'),\n",
        "            ]\n",
        "        )\n",
        "        self.decoder = tf.keras.Sequential(\n",
        "            [\n",
        "            Conv2D(2, (1, 1), activation='relu', padding='same'),\n",
        "            Conv2D(4, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(8, (3, 3), activation='relu', padding='same'),\n",
        "            UpSampling2D(size=(2, 2)),\n",
        "            Conv2D(16, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(32, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(64, (3, 3), activation='relu', padding='same'),\n",
        "            UpSampling2D(size=(2, 2)),\n",
        "            Conv2D(128, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(256, (3, 3), activation='relu', padding='same'),\n",
        "            Conv2D(1, (3, 3), activation='tanh', padding='same'),\n",
        "            ]\n",
        "        )\n",
        "        self.train()\n",
        "\n",
        "    def call(self, inputs):\n",
        "        if self.e:\n",
        "            inputs = self.encoder(inputs)\n",
        "        if self.d:\n",
        "            inputs = self.decoder(inputs)\n",
        "        return inputs\n",
        "\n",
        "    def encode(self):\n",
        "        self.e = True\n",
        "        self.d = False\n",
        "\n",
        "    def decode(self):\n",
        "        self.e = False\n",
        "        self.d = True\n",
        "\n",
        "    def train(self):\n",
        "        self.e = True\n",
        "        self.d = True"
      ],
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7mVDGN68fRoh",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Create a object of model class\n",
        "model = AutoEncoder()\n",
        "#Compile the model with MSE loss and Adam optimizer\n",
        "model.compile(loss='mean_squared_error', optimizer = Adam())"
      ],
      "execution_count": 3,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DTr9vVCriitJ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Build the model\n",
        "model.build(input_shape = (None,28,28,1))"
      ],
      "execution_count": 4,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HT0l47VMjD6E",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "217bcdb8-f3c3-42ff-c7a3-ae9b4f1540ae"
      },
      "source": [
        "#Print Summary\n",
        "model.encoder.summary()\n",
        "model.decoder.summary()"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Model: \"sequential\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "conv2d (Conv2D)              (None, 28, 28, 256)       2560      \n",
            "_________________________________________________________________\n",
            "conv2d_1 (Conv2D)            (None, 28, 28, 128)       295040    \n",
            "_________________________________________________________________\n",
            "conv2d_2 (Conv2D)            (None, 28, 28, 64)        73792     \n",
            "_________________________________________________________________\n",
            "max_pooling2d (MaxPooling2D) (None, 14, 14, 64)        0         \n",
            "_________________________________________________________________\n",
            "conv2d_3 (Conv2D)            (None, 14, 14, 32)        18464     \n",
            "_________________________________________________________________\n",
            "conv2d_4 (Conv2D)            (None, 14, 14, 16)        4624      \n",
            "_________________________________________________________________\n",
            "conv2d_5 (Conv2D)            (None, 14, 14, 8)         136       \n",
            "_________________________________________________________________\n",
            "max_pooling2d_1 (MaxPooling2 (None, 7, 7, 8)           0         \n",
            "_________________________________________________________________\n",
            "conv2d_6 (Conv2D)            (None, 7, 7, 4)           292       \n",
            "_________________________________________________________________\n",
            "conv2d_7 (Conv2D)            (None, 7, 7, 2)           10        \n",
            "_________________________________________________________________\n",
            "conv2d_8 (Conv2D)            (None, 7, 7, 1)           3         \n",
            "=================================================================\n",
            "Total params: 394,921\n",
            "Trainable params: 394,921\n",
            "Non-trainable params: 0\n",
            "_________________________________________________________________\n",
            "Model: \"sequential_1\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "conv2d_9 (Conv2D)            (None, 7, 7, 2)           4         \n",
            "_________________________________________________________________\n",
            "conv2d_10 (Conv2D)           (None, 7, 7, 4)           76        \n",
            "_________________________________________________________________\n",
            "conv2d_11 (Conv2D)           (None, 7, 7, 8)           296       \n",
            "_________________________________________________________________\n",
            "up_sampling2d (UpSampling2D) (None, 14, 14, 8)         0         \n",
            "_________________________________________________________________\n",
            "conv2d_12 (Conv2D)           (None, 14, 14, 16)        1168      \n",
            "_________________________________________________________________\n",
            "conv2d_13 (Conv2D)           (None, 14, 14, 32)        4640      \n",
            "_________________________________________________________________\n",
            "conv2d_14 (Conv2D)           (None, 14, 14, 64)        18496     \n",
            "_________________________________________________________________\n",
            "up_sampling2d_1 (UpSampling2 (None, 28, 28, 64)        0         \n",
            "_________________________________________________________________\n",
            "conv2d_15 (Conv2D)           (None, 28, 28, 128)       73856     \n",
            "_________________________________________________________________\n",
            "conv2d_16 (Conv2D)           (None, 28, 28, 256)       295168    \n",
            "_________________________________________________________________\n",
            "conv2d_17 (Conv2D)           (None, 28, 28, 1)         2305      \n",
            "=================================================================\n",
            "Total params: 396,009\n",
            "Trainable params: 396,009\n",
            "Non-trainable params: 0\n",
            "_________________________________________________________________\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "1xsJFnFQZgds",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "model_saver = tf.keras.callbacks.ModelCheckpoint(\"model_weights.h5\", \n",
        "                                                 monitor='val_loss', verbose=1, \n",
        "                                                 save_best_only=True, \n",
        "                                                 save_weights_only=False, \n",
        "                                                 mode='auto')"
      ],
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "PqF1Qe0Fj5xM",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 272
        },
        "outputId": "801c3964-a715-474d-e752-b6e38a282f49"
      },
      "source": [
        "#Train the model\n",
        "history = model.fit(X,Y,epochs = 3,validation_data = (X_val,Y_val), batch_size = 32,callbacks = [model_saver])"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Epoch 1/3\n",
            "   2/1500 [..............................] - ETA: 41s - loss: 0.1102WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0149s vs `on_train_batch_end` time: 0.0375s). Check your callbacks.\n",
            "1500/1500 [==============================] - ETA: 0s - loss: 0.0107WARNING:tensorflow:Callbacks method `on_test_batch_end` is slow compared to the batch time (batch time: 0.0032s vs `on_test_batch_end` time: 0.0122s). Check your callbacks.\n",
            "\n",
            "Epoch 00001: val_loss improved from inf to 0.00492, saving model to model_weights.h5\n",
            "1500/1500 [==============================] - 83s 56ms/step - loss: 0.0107 - val_loss: 0.0049\n",
            "Epoch 2/3\n",
            "1500/1500 [==============================] - ETA: 0s - loss: 0.0038\n",
            "Epoch 00002: val_loss improved from 0.00492 to 0.00326, saving model to model_weights.h5\n",
            "1500/1500 [==============================] - 83s 55ms/step - loss: 0.0038 - val_loss: 0.0033\n",
            "Epoch 3/3\n",
            "1500/1500 [==============================] - ETA: 0s - loss: 0.0032\n",
            "Epoch 00003: val_loss did not improve from 0.00326\n",
            "1500/1500 [==============================] - 83s 55ms/step - loss: 0.0032 - val_loss: 0.0033\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LUeeJaHfj8SA",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Setup Encoding Mode\n",
        "model.encode()\n",
        "#Encode 10 sample images\n",
        "encodings = model.predict(x_test[:10])"
      ],
      "execution_count": 8,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "8KEgstucl9Du",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#Setup Decoding Mode\n",
        "model.decode()\n",
        "#Decode 10 sample images\n",
        "decodings = model.predict(encodings)"
      ],
      "execution_count": 9,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "eJxyVpyrouF8",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "3c6f0845-9065-4aec-e92d-3adb7025dc5c"
      },
      "source": [
        "#Plotting original and extracted images\n",
        "w=10\n",
        "h=10\n",
        "fig=plt.figure(figsize=(10, 20))\n",
        "columns = 2\n",
        "rows = 10\n",
        "j = 0\n",
        "k = 0\n",
        "for i in range(1, columns*rows +1):\n",
        "    if i%2 == 0:\n",
        "        img = decodings[j,...,0]\n",
        "        j+=1\n",
        "    else:\n",
        "        img = x_test[k,...,0]\n",
        "        k+=1\n",
        "    fig.add_subplot(rows, columns, i)\n",
        "    plt.imshow(img,cmap = 'gray')\n",
        "plt.show()"
      ],
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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f/Inf+LjE088MbPcL37xCx9fffXVzawSgA5XaRPb8NzUqTs/JXbWWWdl3m/FihU+/vKXv+zj9Os73d3dVdWhyOhxAQCiQsMFAIhK2w4Vhsvcr7zySh9X2kjyqaee8jG7YwBopkpDc+H3tK666iofp/NXuEPGX//1X/v497//vY/DpfVSctl7nsODjRyWpMcFAIgKDRcAICptO1R4xRVX+Pj9739/Zrnwe1ysJASQl3DoLD2cd+GFO7/EcuSRR2be45e//KWPw12BKm2Ym7VzRqVy6V01QmHdw+em6xDer9oVlZnPHLQEAAAFQsMFAIgKDRcAICqW58fDzCy3h23dutXHlZbAH3TQQT5etWpVU+vUCs65eF6HBwqsu7vb7b777o28n4/DHeAl6aGHHvJxOMe1ZcuWRLlPfepTPr7vvvvK3jutlpwfznGlrw/nuCo9t9o67Jjz2rx5s/r6+srmL3pcAICo0HABAKLStsvhqzVq1Cgfb9++fcjXv/XWW5nXh0OUe+21V+Y99t57bx9/6Utfquq5fX19Pv7KV76SOJceTgBQDOHS797eXh8fcMABiXIHHnhg2evT/7YPO+wwH5955plly6WnQMJzYR3CWJKGDx/u4zB/hfkqfRzm04kTJybKhccrV6708XXXXZcoF+76kYUeFwAgKjRcAICoDDpUaGbjJN0sabQkJ2muc+56Mxsl6TZJ4yW9LOkC59z65lW1OX7729/Wdf3/+3//z8fpLvno0aN9/MlPfrKu51SyevXqxPE111zTtGcBMSly/gqHDd95553EufB4jz328HE4FCdl/1sPVwGGK6wlaePGjT7evHmzj9Mr/cKVjrvttpuPwyFEKbmqMJweSa+UDMutXbvWx7fcckui3I5hxEorD6vpcfVKusI5N0HSFEmXmtkESbMlLXLOHSFpUekYAIqE/NWGBm24nHOrnHNPlOKNkpZJGitpmqR5pWLzJE1vViUBoBbkr/Y0pFWFZjZe0iRJj0ka7ZzbMTa2WgNd8XLXzJI0q/YqAkD96s1fMX3avt1VvXOGme0h6QFJ1zjn7jKzDc65vYPz651zIwe5R247Z9x1110+njZtWl6PrUp62WnWzssLFixIHC9evLhsufAte0l69NFHfczOGUBj8lejd84IG8L0vNE//MM/+Pi8884L69Cw56el24LwOMxR4as46XPh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7r//fh/ffffdPv70008j7cJSX7iqMHw/16rCUPw5W9m+Iz5mlKuMyPO4AAA1g4ELAJAqDFwAgFRJ3RxX2N98l4aG7e66667IsbfeesvH2267rY+nT5/u48mTJ2f97t13393Hf/3rXyPHqmFJPnNcQGnkmuPKNrcTzmNJ0TmqTTfdNOu5wjwX7vzz0UcfRdq9+uqrzcbhZ3bcccfIZ3beeWcfh7nxgw8+iLR78803fbx48WIft2sXvYsqnBsr5XwXc1wAgJrBwAUASJXUlQpvuukmH//sZz8r9utK6ssvv4y8Di/dww01k0SpECiNfJfDhztQtG/fPnLszjvv9PGJJ57o4w022CDSLt8yW1immzdvno+/+uorH3ft2jXymWzL8OfOnRtp9+CDD/p4xIgRPo7fepRt+oZSIQAAGQxcAIBUSV2pMLwM32effXz88MMPR9qFK1969Ojh4/hd5OUU/tleffXVPr7++uuT7AOlQqAECtk5I75xbbhyed999/XxIYccEml38MEH+zhcFRjftDcsx4Wb5y5fvtzH8d07QvGyXyj8jv/6r//y8b333htpF5ZDS/ncLkqFAICawcAFAEgVBi4AQKqkbo6rEEceuf4J3fHlqeHc03777Ve2PowbN87H4TLYcmOOCyiNfOe4wpwan1MP57zC74ovhw+PhfNiF110UaTdj370o2Y/k2sHi3DJergcPj4XFvb1n//8p4/33nvvSLtFixY124diMccFAKgZDFwAgFRp13KT9JswYULWY+Flb1gqDJeW3nfffZHP3HPPPT6++OKLfXzaaacV1U8A6Zdr2XeYV7I9BFKSli1b5uNwg9tzzz030i7MPxdccIGPww1845uRL126tNn+5Fpqv8kmm/g4Xv4My5KlXA6fC1dcAIBUYeACAKRKi6VCM+sh6X5JDZKcpBHOuf9nZl0k/UlST0kzJA1wzn1dvq6Wx4svvujj4cOH+zjceeOcc86JfGannXbycZ8+ffI6TzU8mwtoayqdv3KVzsIS3qpVq7IeC0ts8dWH4aa94W4bhx12WNY+hN+90UYb+TheAgzb/e///q+PlyxZEmkXriQMv6OcK9bzueJaI+lS59xukg6UNNTMdpM0TNIE51wvSRMyrwGgmpC/alCLA5dzbo5z7r1MvFjSVEndJfWXNDrTbLSkE8rVSQAoBPmrNrVqVaGZ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\n",
            "text/plain": [
              "<Figure size 720x1440 with 20 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "und1g6cZo84z",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 10,
      "outputs": []
    }
  ]
}
이 알고리즘에 대해
#Importing Packages and dataset
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Input,Conv2D,MaxPooling2D,UpSampling2D,Conv2DTranspose
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam,RMSprop
from tensorflow.keras import backend as K
import sklearn
from sklearn.model_selection import train_test_split

#Setting up the dataset
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

#Reshaping the data as per the model requirement
x_train = x_train.reshape(-1, 28,28, 1)/255
x_test = x_test.reshape(-1, 28,28, 1)/255

#Splitting the data for validation
X,X_val,Y,Y_val = train_test_split(x_train,x_train,test_size=0.2)
#Define the autoencoder model
class AutoEncoder(tf.keras.Model):

    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.encoder = tf.keras.Sequential(
            [
            Conv2D(256, (3, 3), activation='relu', padding='same'),
            Conv2D(128, (3, 3), activation='relu', padding='same'),
            Conv2D(64, (3, 3), activation='relu', padding='same'),
            MaxPooling2D(pool_size=(2, 2)),
            Conv2D(32, (3, 3), activation='relu', padding='same'),
            Conv2D(16, (3, 3), activation='relu', padding='same'),
            Conv2D(8, (1, 1), activation='relu', padding='same'),
            MaxPooling2D(pool_size=(2, 2)),
            Conv2D(4, (3, 3), activation='relu', padding='same'),
            Conv2D(2, (1, 1), activation='relu', padding='same'),
            Conv2D(1, (1, 1), activation='relu', padding='same'),
            ]
        )
        self.decoder = tf.keras.Sequential(
            [
            Conv2D(2, (1, 1), activation='relu', padding='same'),
            Conv2D(4, (3, 3), activation='relu', padding='same'),
            Conv2D(8, (3, 3), activation='relu', padding='same'),
            UpSampling2D(size=(2, 2)),
            Conv2D(16, (3, 3), activation='relu', padding='same'),
            Conv2D(32, (3, 3), activation='relu', padding='same'),
            Conv2D(64, (3, 3), activation='relu', padding='same'),
            UpSampling2D(size=(2, 2)),
            Conv2D(128, (3, 3), activation='relu', padding='same'),
            Conv2D(256, (3, 3), activation='relu', padding='same'),
            Conv2D(1, (3, 3), activation='tanh', padding='same'),
            ]
        )
        self.train()

    def call(self, inputs):
        if self.e:
            inputs = self.encoder(inputs)
        if self.d:
            inputs = self.decoder(inputs)
        return inputs

    def encode(self):
        self.e = True
        self.d = False

    def decode(self):
        self.e = False
        self.d = True

    def train(self):
        self.e = True
        self.d = True
#Create a object of model class
model = AutoEncoder()
#Compile the model with MSE loss and Adam optimizer
model.compile(loss='mean_squared_error', optimizer = Adam())
#Build the model
model.build(input_shape = (None,28,28,1))
#Print Summary
model.encoder.summary()
model.decoder.summary()
Model: &quot;sequential&quot;
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 28, 28, 256)       2560      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 28, 28, 128)       295040    
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 64)        73792     
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 14, 14, 32)        18464     
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 14, 14, 16)        4624      
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 14, 14, 8)         136       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 7, 7, 8)           0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 7, 7, 4)           292       
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 7, 7, 2)           10        
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 7, 7, 1)           3         
=================================================================
Total params: 394,921
Trainable params: 394,921
Non-trainable params: 0
_________________________________________________________________
Model: &quot;sequential_1&quot;
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_9 (Conv2D)            (None, 7, 7, 2)           4         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 7, 7, 4)           76        
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 7, 7, 8)           296       
_________________________________________________________________
up_sampling2d (UpSampling2D) (None, 14, 14, 8)         0         
_________________________________________________________________
conv2d_12 (Conv2D)           (None, 14, 14, 16)        1168      
_________________________________________________________________
conv2d_13 (Conv2D)           (None, 14, 14, 32)        4640      
_________________________________________________________________
conv2d_14 (Conv2D)           (None, 14, 14, 64)        18496     
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 28, 28, 64)        0         
_________________________________________________________________
conv2d_15 (Conv2D)           (None, 28, 28, 128)       73856     
_________________________________________________________________
conv2d_16 (Conv2D)           (None, 28, 28, 256)       295168    
_________________________________________________________________
conv2d_17 (Conv2D)           (None, 28, 28, 1)         2305      
=================================================================
Total params: 396,009
Trainable params: 396,009
Non-trainable params: 0
_________________________________________________________________
model_saver = tf.keras.callbacks.ModelCheckpoint("model_weights.h5", 
                                                 monitor='val_loss', verbose=1, 
                                                 save_best_only=True, 
                                                 save_weights_only=False, 
                                                 mode='auto')
#Train the model
history = model.fit(X,Y,epochs = 3,validation_data = (X_val,Y_val), batch_size = 32,callbacks = [model_saver])
Epoch 1/3
   2/1500 [..............................] - ETA: 41s - loss: 0.1102WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0149s vs `on_train_batch_end` time: 0.0375s). Check your callbacks.
1500/1500 [==============================] - ETA: 0s - loss: 0.0107WARNING:tensorflow:Callbacks method `on_test_batch_end` is slow compared to the batch time (batch time: 0.0032s vs `on_test_batch_end` time: 0.0122s). Check your callbacks.

Epoch 00001: val_loss improved from inf to 0.00492, saving model to model_weights.h5
1500/1500 [==============================] - 83s 56ms/step - loss: 0.0107 - val_loss: 0.0049
Epoch 2/3
1500/1500 [==============================] - ETA: 0s - loss: 0.0038
Epoch 00002: val_loss improved from 0.00492 to 0.00326, saving model to model_weights.h5
1500/1500 [==============================] - 83s 55ms/step - loss: 0.0038 - val_loss: 0.0033
Epoch 3/3
1500/1500 [==============================] - ETA: 0s - loss: 0.0032
Epoch 00003: val_loss did not improve from 0.00326
1500/1500 [==============================] - 83s 55ms/step - loss: 0.0032 - val_loss: 0.0033
#Setup Encoding Mode
model.encode()
#Encode 10 sample images
encodings = model.predict(x_test[:10])
#Setup Decoding Mode
model.decode()
#Decode 10 sample images
decodings = model.predict(encodings)
#Plotting original and extracted images
w=10
h=10
fig=plt.figure(figsize=(10, 20))
columns = 2
rows = 10
j = 0
k = 0
for i in range(1, columns*rows +1):
    if i%2 == 0:
        img = decodings[j,...,0]
        j+=1
    else:
        img = x_test[k,...,0]
        k+=1
    fig.add_subplot(rows, columns, i)
    plt.imshow(img,cmap = 'gray')
plt.show()