{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"},{"sourceId":9900,"sourceType":"datasetVersion","datasetId":6209},{"sourceId":110819,"sourceType":"datasetVersion","datasetId":57539},{"sourceId":110930,"sourceType":"datasetVersion","datasetId":57578}],"dockerImageVersionId":11543,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Image Classification on Cats & Dogs dataset Using: CNN & Transfer Learning with ResNet50\n\n### Abduallah Abuhassan ","metadata":{"_uuid":"40733c39647bac5056ba0dada8409d6184fefb68"}},{"cell_type":"code","source":"# basic libraries\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\n%matplotlib inline \n\nimport cv2\n\nimport os\n\nimport tensorflow as tf","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-12-08T09:20:45.24411Z","iopub.status.idle":"2024-12-08T09:20:45.24534Z","shell.execute_reply":"2024-12-08T09:20:45.244967Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Global Constants","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false,"jupyter":{"outputs_hidden":true}}},{"cell_type":"code","source":"# Fixed for our Cats & Dogs classes\nNUM_CLASSES = 2\n\n# Fixed for Cats & Dogs color images\nCHANNELS = 1 # gray\n\nIMAGE_RESIZE = 224\nRESNET50_POOLING = 'avg'\nDENSE_LAYER_ACTIVATION = 'softmax' # multi-classification\nOBJECTIVE_FUNCTION = 'categorical_crossentropy' # multi-classification\n\n# Common accuracy metric for all outputs, but can use different metrics for different output\nLOSS_METRICS = ['accuracy']\n\n# EARLY_STOP_PATIENCE must be < NUM_EPOCHS\nNUM_EPOCHS = 10\nEARLY_STOP_PATIENCE = 3\n\n# These steps value should be proper FACTOR of no.-of-images in train & valid folders respectively\n# Training images processed in each step would be no.-of-train-images / STEPS_PER_EPOCH_TRAINING\nSTEPS_PER_EPOCH_TRAINING = 10\nSTEPS_PER_EPOCH_VALIDATION = 10\n\n# These steps value should be proper FACTOR of no.-of-images in train & valid folders respectively\n# NOTE that these BATCH* are for Keras ImageDataGenerator batching to fill epoch step input\nBATCH_SIZE_TRAINING = 100\nBATCH_SIZE_VALIDATION = 100\n\n# Using 1 to easily manage mapping between test_generator & prediction for submission preparation\nBATCH_SIZE_TESTING = 1","metadata":{"trusted":true,"_uuid":"d157dc1311928d1ada13ed2ef4a34c4ea5c0b538","execution":{"iopub.status.busy":"2024-12-08T09:20:45.2493Z","iopub.status.idle":"2024-12-08T09:20:45.249865Z","shell.execute_reply":"2024-12-08T09:20:45.249572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# model libraries\nfrom tensorflow.python.keras.applications import ResNet50\nfrom tensorflow.python.keras.models import Sequential\nfrom tensorflow.python.keras.layers import Dense","metadata":{"trusted":true,"_uuid":"ee0da10690e2848537102cb0c74f9fe19a50431b","execution":{"iopub.status.busy":"2024-12-08T09:20:45.250638Z","iopub.status.idle":"2024-12-08T09:20:45.251142Z","shell.execute_reply":"2024-12-08T09:20:45.250867Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### ResNet50\n* Notice that resnet50 folder has 2 pre-trained weights files... xyz_tf_kernels.h5 & xyz_tf_kernels_NOTOP.h5\n* The xyz_tf_kernels.h5 weights is useful for pure prediction of test image and this prediction will rely completely on ResNet50 pre-trained weights, i.e., it does not expected any training from our side\n* Out intention in this kernel is Transfer Learning by using ResNet50 pre-trained weights except its TOP layer, i.e., the xyz_tf_kernels_NOTOP.h5 weights... Use this weights as initial weight for training new layer using train images","metadata":{"_uuid":"b651539464d36c7443a601e914479f0d2153f435"}},{"cell_type":"code","source":"resnet_weights_path = '../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'","metadata":{"trusted":true,"_uuid":"2fc47c3f887e6713b5a2be38c02b38a8ec80bf97","execution":{"iopub.status.busy":"2024-12-08T09:20:45.254971Z","iopub.status.idle":"2024-12-08T09:20:45.255538Z","shell.execute_reply":"2024-12-08T09:20:45.255225Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Define Our Transfer Learning Network Model Consisting of 2 Layers\n\nHere, we are preparing specification or blueprint of the TensorFlow DAG (directed acyclcic graph) for just the MODEL part.","metadata":{"_uuid":"4ebad39fff164624f5616a83cd8831308431da7d"}},{"cell_type":"code","source":"# model init.\n\nmodel = Sequential()\n\n# 1st layer as the lumpsum weights from resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\nmodel.add(ResNet50(include_top = False, pooling = RESNET50_POOLING, weights = resnet_weights_path,))\n\n# 2nd layer as Dense for 2-class classification, i.e., dog or cat using SoftMax activation\nmodel.add(Dense(NUM_CLASSES, activation = DENSE_LAYER_ACTIVATION))\n\n# Say not to train first layer (ResNet) model as it is already trained\nmodel.layers[0].trainable = False","metadata":{"trusted":true,"_uuid":"0bb4a8eccd70874ef21f0809f478f993fa127fb2","execution":{"iopub.status.busy":"2024-12-08T09:20:45.256371Z","iopub.status.idle":"2024-12-08T09:20:45.256886Z","shell.execute_reply":"2024-12-08T09:20:45.256603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T09:20:45.2607Z","iopub.status.idle":"2024-12-08T09:20:45.261231Z","shell.execute_reply":"2024-12-08T09:20:45.260954Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Compile Our Transfer Learning Model","metadata":{"_uuid":"3587981a5b6b1d6ff8221738d51d78cafb2dd02c"}},{"cell_type":"code","source":"from tensorflow.python.keras import optimizers\n\nsgd = optimizers.SGD(lr = 0.001, decay = 1e-6, momentum = 0.9, nesterov = True)\n\nmodel.compile(optimizer = sgd, loss = OBJECTIVE_FUNCTION, metrics = LOSS_METRICS)","metadata":{"trusted":true,"_uuid":"670238611770f43a056332cc06efff44e20ad124","execution":{"iopub.status.busy":"2024-12-08T09:20:45.261995Z","iopub.status.idle":"2024-12-08T09:20:45.262498Z","shell.execute_reply":"2024-12-08T09:20:45.262218Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Prepare Keras Data Generators","metadata":{"_uuid":"8d003a99fe4ff58b8905c7f6b5286d97a852cb7a"}},{"cell_type":"code","source":"from keras.applications.resnet50 import preprocess_input\nfrom keras.preprocessing.image import ImageDataGenerator\n\nimage_size = IMAGE_RESIZE\n\n# preprocessing_function is applied on each image but only after re-sizing & augmentation (resize => augment => pre-process)\n# Each of the keras.application.resnet* preprocess_input MOSTLY mean BATCH NORMALIZATION (applied on each batch) stabilize the inputs to nonlinear activation functions\n# Batch Normalization helps in faster convergence\ndata_generator = ImageDataGenerator(\n        preprocessing_function=preprocess_input,   \n)\n\n# flow_From_directory generates batches of augmented data (where augmentation can be color conversion, etc)\ntrain_generator = data_generator.flow_from_directory(\n        '../input/catsdogs-trainvalid-80pc-prepd/trainvalidfull4keras/trainvalidfull4keras/train',\n        target_size = (image_size, image_size),\n        batch_size = BATCH_SIZE_TRAINING,\n        class_mode = 'categorical',\n)\n\nvalidation_generator = data_generator.flow_from_directory(\n        '../input/catsdogs-trainvalid-80pc-prepd/trainvalidfull4keras/trainvalidfull4keras/valid',\n        target_size = (image_size, image_size),\n        batch_size = BATCH_SIZE_VALIDATION,\n        class_mode = 'categorical',\n)","metadata":{"trusted":true,"_uuid":"ac9ebd909fee81c8c8d9b9fccb6590944d8106eb","execution":{"iopub.status.busy":"2024-12-08T09:20:45.263294Z","iopub.status.idle":"2024-12-08T09:20:45.265794Z","shell.execute_reply":"2024-12-08T09:20:45.263507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# len(train_generator) = no. of train images / train batch size, \n# len(valid_generator) = no. of validation images / validation batch size,\n(len(train_generator), len(validation_generator)) ","metadata":{"trusted":true,"_uuid":"5014f91b6aeae661f0bc5db5d546089a07ca5b9d","execution":{"iopub.status.busy":"2024-12-08T09:20:45.266645Z","iopub.status.idle":"2024-12-08T09:20:45.267362Z","shell.execute_reply":"2024-12-08T09:20:45.26711Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train Model With Cats & Dogs (splitted) Data Set","metadata":{"_uuid":"f56e7cf9eae4c86c72468e322e7f00859a2ce9cd"}},{"cell_type":"code","source":"# Early stopping & checkpointing the best model in ../working dir & restoring that as our model for prediction\nfrom tensorflow.python.keras.callbacks import EarlyStopping, ModelCheckpoint\n\ncb_early_stopper = EarlyStopping(monitor = 'val_loss', patience = EARLY_STOP_PATIENCE)\ncb_checkpointer = ModelCheckpoint(filepath = '../working/best.hdf5', monitor = 'val_loss', save_best_only = True, mode = 'auto')\n","metadata":{"trusted":true,"_uuid":"2dfad78129d725f42110cde0270c32d7373d6d1d","execution":{"iopub.status.busy":"2024-12-08T09:20:45.268205Z","iopub.status.idle":"2024-12-08T09:20:45.268998Z","shell.execute_reply":"2024-12-08T09:20:45.268761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fit_history = model.fit_generator(\n    train_generator,\n    steps_per_epoch = STEPS_PER_EPOCH_TRAINING,\n    epochs = NUM_EPOCHS,\n    validation_data = validation_generator,\n    validation_steps = STEPS_PER_EPOCH_VALIDATION,\n    callbacks = [cb_checkpointer, cb_early_stopper]\n)\n\nmodel.load_weights(\"../working/best.hdf5\")","metadata":{"trusted":true,"_uuid":"cf85fe3c0653aa56503b7da058ea8acf445eec6e","execution":{"iopub.status.busy":"2024-12-08T09:20:45.269914Z","iopub.status.idle":"2024-12-08T09:20:45.270528Z","shell.execute_reply":"2024-12-08T09:20:45.270293Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Training Metrics\n\nOne of the default callbacks that is registered when training all deep learning models is the History callback. It records training metrics (training accuracy, training loss, validation loss & validation accuracy) for each epoch. Note that training accuracy & loss during epoch steps are somewhat incomplete information and they are not recorded in history.\n\nObserve that training uses early stopping, hence metrics is available for epochs run, not for NUM_EPOCHS.","metadata":{"_uuid":"1ef4dd95f6d12f9277255576645e8bfce5270b81"}},{"cell_type":"code","source":"print(fit_history.history.keys())","metadata":{"trusted":true,"_uuid":"383d7a0aa87e1508a46fcebfcd5a80d7aafb840f","execution":{"iopub.status.busy":"2024-12-08T09:20:45.271086Z","iopub.status.idle":"2024-12-08T09:20:45.271506Z","shell.execute_reply":"2024-12-08T09:20:45.271282Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(1, figsize = (15,8))\n\nplt.subplot(221)\nplt.plot(fit_history.history['acc'])\nplt.plot(fit_history.history['val_acc'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'valid'])\n\nplt.subplot(222)\nplt.plot(fit_history.history['loss'])\nplt.plot(fit_history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train','validation'])\n\nplt.show()","metadata":{"trusted":true,"_uuid":"00e51568f7e6022c6738c0e1a41080bc7152c257","execution":{"iopub.status.busy":"2024-12-08T09:20:45.272055Z","iopub.status.idle":"2024-12-08T09:20:45.272475Z","shell.execute_reply":"2024-12-08T09:20:45.272233Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# test_generator\ntest_generator = data_generator.flow_from_directory(\n    directory = '../input/test-files-prepd/test4keras/test4keras',\n    target_size = (image_size, image_size),\n    batch_size = BATCH_SIZE_TESTING,\n    class_mode = None,\n    shuffle = False,\n    seed = 123\n)","metadata":{"trusted":true,"_uuid":"3941e7c5d983e2ad6ae6d6da0a1415ea27e1db7e","execution":{"iopub.status.busy":"2024-12-08T09:20:45.274022Z","iopub.status.idle":"2024-12-08T09:20:45.27478Z","shell.execute_reply":"2024-12-08T09:20:45.274524Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### See the Result","metadata":{"trusted":true,"_uuid":"8c6e7f35552f87b522aa32397ffb71ea732ce80b"}},{"cell_type":"code","source":"TEST_DIR = '../input/test-files-prepd/test4keras/test4keras/'\nf, ax = plt.subplots(5, 5, figsize = (15, 15))\n\nfor i in range(0,25):\n    imgBGR = cv2.imread(TEST_DIR + test_generator.filenames[i])\n    imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB)\n    \n    # a if condition else b\n    predicted_class = \"Dog\" if predicted_class_indices[i] else \"Cat\"\n\n    ax[i//5, i%5].imshow(imgRGB)\n    ax[i//5, i%5].axis('off')\n    ax[i//5, i%5].set_title(\"Predicted:{}\".format(predicted_class))    \n\nplt.show()","metadata":{"trusted":true,"_uuid":"e54bc22bd1a2af7d704f9164b624b79b8cbd4f56","execution":{"iopub.status.busy":"2024-12-08T09:20:45.275723Z","iopub.status.idle":"2024-12-08T09:20:45.276489Z","shell.execute_reply":"2024-12-08T09:20:45.276236Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### References\n\n1. [Image Classification Tutorial](https://www.kaggle.com/code/suniliitb96/tutorial-keras-transfer-learning-with-resnet50)\n2. [Dataset](https://www.kaggle.com/competitions/dogs-vs-cats-redux-kernels-edition/code)\n   ","metadata":{"_uuid":"422beefc8f408582e85292cec3fe8225228f2ab5"}}]}