{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Learn Convolutional Neural Network with Keras\n\n","metadata":{}},{"cell_type":"markdown","source":"Code in this notebook obtain from [Keras - Python Deep Learning Neural Network API Course](https://deeplizard.com/learn/playlist/PLZbbT5o_s2xrwRnXk_yCPtnqqo4_u2YGL) or you can watch that course in [Youtube ](https://www.youtube.com/watch?v=qFJeN9V1ZsI&t=2380s)","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport itertools\nimport os\nimport shutil\nimport random\nimport glob\nimport matplotlib.pyplot as plt\nimport warnings\nimport tensorflow as tf\n\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.models import Model\n\nfrom tensorflow.keras.applications import vgg16\nfrom tensorflow.keras.applications import mobilenet\nfrom tensorflow.keras.applications import imagenet_utils\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.preprocessing import image\n\nfrom tensorflow.keras.layers import Activation, Dense, Flatten, BatchNormalization, Conv2D, MaxPool2D\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.metrics import categorical_crossentropy\n\nfrom sklearn.metrics import confusion_matrix\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:03.979504Z","iopub.execute_input":"2021-08-30T13:27:03.979883Z","iopub.status.idle":"2021-08-30T13:27:03.992518Z","shell.execute_reply.started":"2021-08-30T13:27:03.979853Z","shell.execute_reply":"2021-08-30T13:27:03.991645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Set Seed to Reproducible Result","metadata":{}},{"cell_type":"code","source":"os.environ['PYTHONHASHSEED'] = '0'\nos.environ['CUDA_VISIBLE_DEVICES'] = ''\nnp.random.seed(27)\nrandom.seed(23)\ntf.random.set_seed(2723)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:03.996251Z","iopub.execute_input":"2021-08-30T13:27:03.996498Z","iopub.status.idle":"2021-08-30T13:27:04.003535Z","shell.execute_reply.started":"2021-08-30T13:27:03.996471Z","shell.execute_reply":"2021-08-30T13:27:04.002760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Extract Zip File","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport glob\n\nzip_file = glob.glob('../input/dogs-vs-cats/*.zip')  \nprint(zip_file)\n\ndef extract_zip(file):\n    with zipfile.ZipFile(file,\"r\") as zip_ref:\n        zip_ref.extractall(\"temp\")\n        \nfor files in zip_file:\n    extract_zip(files)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-30T13:27:04.021438Z","iopub.execute_input":"2021-08-30T13:27:04.021721Z","iopub.status.idle":"2021-08-30T13:27:22.455244Z","shell.execute_reply.started":"2021-08-30T13:27:04.021695Z","shell.execute_reply":"2021-08-30T13:27:22.454400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Manage Directory\nSplit data into training, validating, and testing data. I only took 1000 images for training, 200 images for validation and 100 images for testing. This is to minimize computation because I just want to learn how to make CNN in Keras","metadata":{"execution":{"iopub.status.busy":"2021-08-30T08:35:36.614708Z","iopub.execute_input":"2021-08-30T08:35:36.615056Z","iopub.status.idle":"2021-08-30T08:35:36.625084Z","shell.execute_reply.started":"2021-08-30T08:35:36.615026Z","shell.execute_reply":"2021-08-30T08:35:36.624027Z"}}},{"cell_type":"code","source":"train_path = 'train'\nvalid_path = 'valid'\ntest_path = 'test'\n\nif os.path.isdir('train/dog') is False:\n    os.makedirs(train_path + '/dog')\n    os.makedirs(train_path + '/cat')\n    os.makedirs(valid_path + '/dog')\n    os.makedirs(valid_path + '/cat')\n    os.makedirs(test_path + '/dog')\n    os.makedirs(test_path + '/cat')\n\n    for i in random.sample(glob.glob('temp/train/cat*'), 500):\n        shutil.move(i, train_path + '/cat')      \n    for i in random.sample(glob.glob('temp/train/dog*'), 500):\n        shutil.move(i, train_path + '/dog')\n    for i in random.sample(glob.glob('temp/train/cat*'), 100):\n        shutil.move(i, valid_path + '/cat')        \n    for i in random.sample(glob.glob('temp/train/dog*'), 100):\n        shutil.move(i, valid_path + '/dog')\n    for i in random.sample(glob.glob('temp/train/cat*'), 50):\n        shutil.move(i, test_path + '/cat')      \n    for i in random.sample(glob.glob('temp/train/dog*'), 50):\n        shutil.move(i, test_path + '/dog')","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:22.456636Z","iopub.execute_input":"2021-08-30T13:27:22.456978Z","iopub.status.idle":"2021-08-30T13:27:22.833125Z","shell.execute_reply.started":"2021-08-30T13:27:22.456943Z","shell.execute_reply":"2021-08-30T13:27:22.832268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Load Data\nLoad data from directory and preprocessing with VGG16. The VGG16 model perfoms preprocessing only by substracting all color channels by their average","metadata":{"execution":{"iopub.status.busy":"2021-08-30T08:24:25.326226Z","iopub.execute_input":"2021-08-30T08:24:25.326551Z","iopub.status.idle":"2021-08-30T08:24:25.330343Z","shell.execute_reply.started":"2021-08-30T08:24:25.326521Z","shell.execute_reply":"2021-08-30T08:24:25.329513Z"}}},{"cell_type":"code","source":"train_batches = (ImageDataGenerator(preprocessing_function=vgg16.preprocess_input)\n                     .flow_from_directory(directory=train_path, \n                                          target_size=(224,224), \n                                          classes=['cat', 'dog'], \n                                          batch_size=10))\n\nvalid_batches = (ImageDataGenerator(preprocessing_function=vgg16.preprocess_input)\n                 .flow_from_directory(directory=valid_path, \n                                      target_size=(224,224), \n                                      classes=['cat', 'dog'], \n                                      batch_size=10))\n\ntest_batches = (ImageDataGenerator(preprocessing_function=vgg16.preprocess_input)\n                .flow_from_directory(directory=test_path, \n                                     target_size=(224,224), \n                                     classes=['cat', 'dog'], \n                                     batch_size=10, \n                                     shuffle=False))","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:22.835036Z","iopub.execute_input":"2021-08-30T13:27:22.835384Z","iopub.status.idle":"2021-08-30T13:27:23.149370Z","shell.execute_reply.started":"2021-08-30T13:27:22.835347Z","shell.execute_reply":"2021-08-30T13:27:23.148653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Plot Images Function","metadata":{}},{"cell_type":"code","source":"def plotImages(images_arr):\n    fig, axes = plt.subplots(1, 10, figsize=(20,20))\n    axes = axes.flatten()\n    for img, ax in zip(images_arr, axes):\n        ax.imshow(img)\n        ax.axis('off')\n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:23.150624Z","iopub.execute_input":"2021-08-30T13:27:23.150908Z","iopub.status.idle":"2021-08-30T13:27:23.158520Z","shell.execute_reply.started":"2021-08-30T13:27:23.150882Z","shell.execute_reply":"2021-08-30T13:27:23.155385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imgs, labels = next(train_batches)\nplotImages(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:23.160074Z","iopub.execute_input":"2021-08-30T13:27:23.160563Z","iopub.status.idle":"2021-08-30T13:27:23.801267Z","shell.execute_reply.started":"2021-08-30T13:27:23.160437Z","shell.execute_reply":"2021-08-30T13:27:23.800477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"All images look saturated, this is the effect of VGG16 preprocessing\n\n## Make CNN Model From Scratch\nThis CNN scratch model has 2 convolutional layers and 2 max polling layers with `relu` activation fucntion. This model have 2 output nodes, this is because our data have two classes, namely dog and cat","metadata":{}},{"cell_type":"code","source":"model = Sequential([\n    Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(224,224,3)),\n    MaxPool2D(pool_size=(2, 2), strides=2),\n    Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding = 'same'),\n    MaxPool2D(pool_size=(2, 2), strides=2),\n    Flatten(),\n    Dense(units=2, activation='softmax')\n])\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:23.802332Z","iopub.execute_input":"2021-08-30T13:27:23.802667Z","iopub.status.idle":"2021-08-30T13:27:26.083416Z","shell.execute_reply.started":"2021-08-30T13:27:23.802632Z","shell.execute_reply":"2021-08-30T13:27:26.079985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=Adam(learning_rate=0.0001),\n             loss=\"categorical_crossentropy\",\n             metrics=['accuracy'])\n\nmodel.fit(x=train_batches,\n         validation_data=valid_batches,\n         epochs=10,\n         verbose=2)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:27:26.084784Z","iopub.execute_input":"2021-08-30T13:27:26.085199Z","iopub.status.idle":"2021-08-30T13:28:14.918368Z","shell.execute_reply.started":"2021-08-30T13:27:26.085155Z","shell.execute_reply":"2021-08-30T13:28:14.917518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It can be seen that the model has perfect accuracy for training data but only has an accuray 0.61 for validation data so this model is overfitting.\n\n### Try To Predict Test Data","metadata":{}},{"cell_type":"code","source":"predictions = model.predict(test_batches)\npredictions","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:14.923598Z","iopub.execute_input":"2021-08-30T13:28:14.925581Z","iopub.status.idle":"2021-08-30T13:28:15.472560Z","shell.execute_reply.started":"2021-08-30T13:28:14.925539Z","shell.execute_reply":"2021-08-30T13:28:15.471787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The `predict` method returns the probability of an image for each class","metadata":{}},{"cell_type":"markdown","source":"### Confusion Matrix","metadata":{}},{"cell_type":"code","source":"def plot_confusion_matrix(cm, classes,\n                          normalize=False,\n                          title='Confusion matrix',\n                          cmap=plt.cm.Blues):\n    \"\"\"\n    This function prints and plots the confusion matrix.\n    Normalization can be applied by setting `normalize=True`.\n    \"\"\"\n    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=45)\n    plt.yticks(tick_marks, classes)\n\n    if normalize:\n        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n        print(\"Normalized confusion matrix\")\n    else:\n        print('Confusion matrix, without normalization')\n\n    print(cm)\n\n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, cm[i, j],\n            horizontalalignment=\"center\",\n            color=\"white\" if cm[i, j] > thresh else \"black\")\n\n    plt.tight_layout()\n    plt.ylabel('True label')\n    plt.xlabel('Predicted label')","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:15.474282Z","iopub.execute_input":"2021-08-30T13:28:15.474619Z","iopub.status.idle":"2021-08-30T13:28:15.485150Z","shell.execute_reply.started":"2021-08-30T13:28:15.474584Z","shell.execute_reply":"2021-08-30T13:28:15.484356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions_labels = np.argmax(predictions, axis=1)\ncm = confusion_matrix(test_batches.classes, predictions_labels)\n\ncm_plot_labels = ['cat','dog']\nplot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:15.486553Z","iopub.execute_input":"2021-08-30T13:28:15.486950Z","iopub.status.idle":"2021-08-30T13:28:15.729049Z","shell.execute_reply.started":"2021-08-30T13:28:15.486917Z","shell.execute_reply":"2021-08-30T13:28:15.728285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Transfer Learning\n\nTransfer learning is a technique for using pre-trained model and fine-tune those parameter to perform a new task. So, we just load the model with its weights and add a new layers so that model can predict new data that don't seen before. There are so many pre-trained models, I only wanted to use two of them, VGG16 and MobileNet.\n\n### VGG16 Model\n","metadata":{}},{"cell_type":"code","source":"vgg16_model = vgg16.VGG16()\nvgg16_model.summary()","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:15.730263Z","iopub.execute_input":"2021-08-30T13:28:15.730596Z","iopub.status.idle":"2021-08-30T13:28:21.243440Z","shell.execute_reply.started":"2021-08-30T13:28:15.730562Z","shell.execute_reply":"2021-08-30T13:28:21.242641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"VGG16 has more then 138 millions parameters with output layer having 1000 nodes, this is because VGG16 is trained by ImageNet dataset with 1000 labels. I will adjust output layer so that it only has 2 nodes and I will to freeze every layers so the weights don't change while the model is trained","metadata":{}},{"cell_type":"code","source":"model = Sequential()\nfor layer in vgg16_model.layers[:-1]:\n    model.add(layer)\n\nfor layer in model.layers:\n    layer.trainable = False\n    \nmodel.add(Dense(units=2, activation='softmax'))\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:21.246382Z","iopub.execute_input":"2021-08-30T13:28:21.246646Z","iopub.status.idle":"2021-08-30T13:28:21.357831Z","shell.execute_reply.started":"2021-08-30T13:28:21.246618Z","shell.execute_reply":"2021-08-30T13:28:21.357097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=Adam(learning_rate=0.0001), \n              loss='categorical_crossentropy', \n              metrics=['accuracy'])\n\nmodel.fit(x=train_batches,\n          validation_data=valid_batches,\n          batch_size=10,\n          epochs=3,\n          verbose=2\n)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:21.359104Z","iopub.execute_input":"2021-08-30T13:28:21.359418Z","iopub.status.idle":"2021-08-30T13:28:38.649347Z","shell.execute_reply.started":"2021-08-30T13:28:21.359382Z","shell.execute_reply":"2021-08-30T13:28:38.648551Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It can be seen that with only with 3 epochs, the model has an accuracy of 0.98 for training data and validation data. This model not overfitting like our previous model.","metadata":{}},{"cell_type":"code","source":"predictions = model.predict(x=test_batches, verbose=0)\ncm = confusion_matrix(y_true=test_batches.classes, y_pred=np.argmax(predictions, axis=-1))\n\ncm_plot_labels = ['cat','dog']\nplot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:38.650658Z","iopub.execute_input":"2021-08-30T13:28:38.651017Z","iopub.status.idle":"2021-08-30T13:28:39.394782Z","shell.execute_reply.started":"2021-08-30T13:28:38.650981Z","shell.execute_reply":"2021-08-30T13:28:39.394004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### MobileNet Model\nThe difference between this model and the VGG16 model is the size of the mode. VGG16 has a size 557MB but MobileNet only has 17MB. Very difference! Like the name, this model is very suitable for use in mobile applications. This model only has 4 million parameters but the accuracy only slightly difference from VGG16","metadata":{}},{"cell_type":"code","source":"mobile = mobilenet.MobileNet()\nmobile.summary()","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:39.396051Z","iopub.execute_input":"2021-08-30T13:28:39.396424Z","iopub.status.idle":"2021-08-30T13:28:40.613644Z","shell.execute_reply.started":"2021-08-30T13:28:39.396385Z","shell.execute_reply":"2021-08-30T13:28:40.612892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I'm not refining the model and just using the specified weights to predict the new image. You can fine-tuned this model like VGG16, as I have done before","metadata":{}},{"cell_type":"code","source":"def prepareImage(file):\n    img = image.load_img(file, target_size=(224, 224))\n    img_array = image.img_to_array(img)\n    return mobilenet.preprocess_input(np.expand_dims(img_array, axis=0))\n\ndef getRandomImageFile():\n    return random.sample(glob.glob('temp/test1/*'), 1)[0]","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:40.616162Z","iopub.execute_input":"2021-08-30T13:28:40.616414Z","iopub.status.idle":"2021-08-30T13:28:40.623269Z","shell.execute_reply.started":"2021-08-30T13:28:40.616387Z","shell.execute_reply":"2021-08-30T13:28:40.622440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = getRandomImageFile()\nimg_prepared = prepareImage(img)\nplt.imshow(img_prepared[0])","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:40.624493Z","iopub.execute_input":"2021-08-30T13:28:40.624975Z","iopub.status.idle":"2021-08-30T13:28:40.827946Z","shell.execute_reply.started":"2021-08-30T13:28:40.624941Z","shell.execute_reply":"2021-08-30T13:28:40.827219Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = mobile.predict(img_prepared)\npredictions_label = imagenet_utils.decode_predictions(predictions)\npredictions_label","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:40.829149Z","iopub.execute_input":"2021-08-30T13:28:40.829483Z","iopub.status.idle":"2021-08-30T13:28:41.398050Z","shell.execute_reply.started":"2021-08-30T13:28:40.829449Z","shell.execute_reply":"2021-08-30T13:28:41.397289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image Augmentation\nThis technique often used to increase the size of training data. This technique will manipulate the original image with zooming, rotating, fliping, etc. Image augmentation is also one way to overcome overfitting","metadata":{}},{"cell_type":"code","source":"gen = ImageDataGenerator(rotation_range=10, \n                         width_shift_range=0.1, \n                         height_shift_range=0.1, \n                         shear_range=0.15, \n                         zoom_range=0.1, \n                         channel_shift_range=10.,\n                         horizontal_flip=True)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:41.399357Z","iopub.execute_input":"2021-08-30T13:28:41.399703Z","iopub.status.idle":"2021-08-30T13:28:41.405828Z","shell.execute_reply.started":"2021-08-30T13:28:41.399648Z","shell.execute_reply":"2021-08-30T13:28:41.404256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = getRandomImageFile()\nimage_random = np.expand_dims(plt.imread(image_path),0)\nplt.imshow(image_random[0])","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:41.407503Z","iopub.execute_input":"2021-08-30T13:28:41.407880Z","iopub.status.idle":"2021-08-30T13:28:41.617766Z","shell.execute_reply.started":"2021-08-30T13:28:41.407845Z","shell.execute_reply":"2021-08-30T13:28:41.616856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"aug_iter = gen.flow(image_random)\naug_images = [next(aug_iter)[0].astype(np.uint8) for i in range(10)]\nplotImages(aug_images)","metadata":{"execution":{"iopub.status.busy":"2021-08-30T13:28:41.619168Z","iopub.execute_input":"2021-08-30T13:28:41.619516Z","iopub.status.idle":"2021-08-30T13:28:42.730540Z","shell.execute_reply.started":"2021-08-30T13:28:41.619481Z","shell.execute_reply":"2021-08-30T13:28:42.729537Z"},"trusted":true},"execution_count":null,"outputs":[]}]}