{"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":"> In this Notebook we will classify images of dogs and cats using transfer learning from pre-trained network\n\n\n","metadata":{"id":"8CpA7-svcfz8"}},{"cell_type":"markdown","source":"# Genrel workFlow\n\n\n1.   Data Preprocessing\n2.   Create Base model\n3.   Feature Extraction\n4.   Model Evaluation\n\n","metadata":{"id":"ANzIvJ6vkv3K"}},{"cell_type":"markdown","source":"A **pre-trained** model is a saved network that was previously trained on a large dataset, typically on a large-scale image-classification task. You either use the pretrained model as is or use transfer learning to customize this model to a given task. ☣\n\n\n\n\n\n\n\n\n\n\n\n**The intuition** behind **transfer learning** for image classification is that if a model is trained on a large and general enough dataset, this model will effectively serve as a generic model of the visual world. You can then take advantage of these learned feature maps without having to start from scratch by training a large model on a large dataset","metadata":{"id":"7jschsVvfPQi"}},{"cell_type":"code","source":"# Setup\n\nimport os\nimport numpy as np\nimport tensorflow as tf \nimport matplotlib.pyplot as plt\n\n%matplotlib inline\n\n","metadata":{"id":"roidI4MCdwTd","execution":{"iopub.status.busy":"2022-05-04T00:03:40.757487Z","iopub.execute_input":"2022-05-04T00:03:40.75782Z","iopub.status.idle":"2022-05-04T00:03:45.030375Z","shell.execute_reply.started":"2022-05-04T00:03:40.757735Z","shell.execute_reply":"2022-05-04T00:03:45.02893Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preprocessing","metadata":{"id":"1nSxUbiKfBAj"}},{"cell_type":"markdown","source":"We will download a dataset containing several thousand images of cats and dogs , then extract a zip file and create a training and validation ","metadata":{"id":"IuaABZC-gXOd"}},{"cell_type":"code","source":"_url_ = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'\npath_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip',origin = _url_,extract=True)\nPATH = os.path.join(os.path.dirname(path_to_zip),'cats_and_dogs_filtered')\n\ntrain_dir = os.path.join(PATH,'train')\nvalidation_dir = os.path.join(PATH,'validation')\n\nBatch_Size = 32\nIMG_SIZE=(160,160)","metadata":{"id":"tl3pho0dhB7E","outputId":"d4c1006c-2406-441f-fba9-5007e6031b08","execution":{"iopub.status.busy":"2022-05-04T00:03:45.033347Z","iopub.execute_input":"2022-05-04T00:03:45.03394Z","iopub.status.idle":"2022-05-04T00:03:47.488955Z","shell.execute_reply.started":"2022-05-04T00:03:45.033903Z","shell.execute_reply":"2022-05-04T00:03:47.488196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"print directories we working wit it","metadata":{"id":"6GKNOed7y750"}},{"cell_type":"code","source":"print(os.path.dirname(path_to_zip))\nprint(train_dir)\nprint(validation_dir)","metadata":{"id":"N1I5ldoPxZFn","outputId":"3df8e3e6-b697-4966-a263-19007fb9a8ef","execution":{"iopub.status.busy":"2022-05-04T00:03:47.490416Z","iopub.execute_input":"2022-05-04T00:03:47.490654Z","iopub.status.idle":"2022-05-04T00:03:47.502273Z","shell.execute_reply.started":"2022-05-04T00:03:47.49062Z","shell.execute_reply":"2022-05-04T00:03:47.499701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"making a train and validation classes from data we extracted above","metadata":{"id":"TiptT1iwwWt4"}},{"cell_type":"code","source":"train_dataset = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    shuffle=True,\n    batch_size=Batch_Size,\n    image_size = IMG_SIZE\n)","metadata":{"id":"_8JBYoASwoay","outputId":"002a9573-2163-4c69-bb7c-1ef27674223b","execution":{"iopub.status.busy":"2022-05-04T00:03:47.505848Z","iopub.execute_input":"2022-05-04T00:03:47.506464Z","iopub.status.idle":"2022-05-04T00:03:50.015188Z","shell.execute_reply.started":"2022-05-04T00:03:47.506429Z","shell.execute_reply":"2022-05-04T00:03:50.013714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"validation_dataset = tf.keras.utils.image_dataset_from_directory(\n    validation_dir,\n    shuffle=True,\n    batch_size=Batch_Size,\n    image_size = IMG_SIZE\n)","metadata":{"id":"q-pXbmqnxkik","outputId":"5795de9e-ef1e-43de-af88-143616b66bfd","execution":{"iopub.status.busy":"2022-05-04T00:03:50.016641Z","iopub.execute_input":"2022-05-04T00:03:50.016911Z","iopub.status.idle":"2022-05-04T00:03:50.138975Z","shell.execute_reply.started":"2022-05-04T00:03:50.016877Z","shell.execute_reply":"2022-05-04T00:03:50.13828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**showing the first nine images and labels from the training set**\n\n","metadata":{"id":"CulbHeLoyGDc"}},{"cell_type":"code","source":"class_name = train_dataset.class_names\n","metadata":{"id":"4dTCCE_Mnaq4","execution":{"iopub.status.busy":"2022-05-04T00:03:50.140184Z","iopub.execute_input":"2022-05-04T00:03:50.140406Z","iopub.status.idle":"2022-05-04T00:03:50.145442Z","shell.execute_reply.started":"2022-05-04T00:03:50.140375Z","shell.execute_reply":"2022-05-04T00:03:50.144608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,10))\n\nfor image , labels in train_dataset.take(1):\n  \n  for i in range(9):\n\n    ax = plt.subplot(3,3,i+1)\n    ax.get_xaxis().set_visible(False)\n    ax.get_yaxis().set_visible(False)\n\n    plt.imshow(image[i].numpy().astype('uint8'))\n    plt.title(class_name[labels[i]])\n\n","metadata":{"id":"Qw7qyCpd2p5k","outputId":"fd7202bd-ca90-41f8-b97e-175a48fe2796","execution":{"iopub.status.busy":"2022-05-04T00:03:50.146923Z","iopub.execute_input":"2022-05-04T00:03:50.147348Z","iopub.status.idle":"2022-05-04T00:03:51.576383Z","shell.execute_reply.started":"2022-05-04T00:03:50.147315Z","shell.execute_reply":"2022-05-04T00:03:51.57572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As the orignal dataset don`t have test set , we will create one.\n**How ?**\n\nDetermine how many batchs of data available in the validation set then move 20% of them to a test set","metadata":{"id":"dqNMw4ZUAOEh"}},{"cell_type":"code","source":"val_batches = tf.data.experimental.cardinality(validation_dataset)\ntest_dataset = validation_dataset.take(val_batches//5)\nvalidation_dataset = validation_dataset.skip(val_batches//5)","metadata":{"id":"c25sEfapAnxp","execution":{"iopub.status.busy":"2022-05-04T00:03:51.577634Z","iopub.execute_input":"2022-05-04T00:03:51.578112Z","iopub.status.idle":"2022-05-04T00:03:51.604913Z","shell.execute_reply.started":"2022-05-04T00:03:51.578075Z","shell.execute_reply":"2022-05-04T00:03:51.604226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'number of validation batches = {tf.data.experimental.cardinality(validation_dataset)}')\nprint(f'number of test batches = {tf.data.experimental.cardinality(test_dataset)}')","metadata":{"id":"e7ZBYNIpFMFL","outputId":"642f8f20-267a-46a4-e8c2-da5280941a90","execution":{"iopub.status.busy":"2022-05-04T00:03:51.605812Z","iopub.execute_input":"2022-05-04T00:03:51.606068Z","iopub.status.idle":"2022-05-04T00:03:51.81317Z","shell.execute_reply.started":"2022-05-04T00:03:51.60603Z","shell.execute_reply":"2022-05-04T00:03:51.812186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**configure the dataset for performance**","metadata":{"id":"evmgBsylG7p3"}},{"cell_type":"markdown","source":"this will be better performance , you can visit [tens_api](https://www.tensorflow.org/guide/data_performance)","metadata":{"id":"orAJkLquR9ab"}},{"cell_type":"code","source":"autotune = tf.data.AUTOTUNE\ntrain_dataset = train_dataset.prefetch(buffer_size = autotune)\nvalidation_dataset = validation_dataset.prefetch(buffer_size = autotune)\ntest_dataset = test_dataset.prefetch(buffer_size = autotune)","metadata":{"id":"SDal8Oj0OLcB","execution":{"iopub.status.busy":"2022-05-04T00:03:51.816095Z","iopub.execute_input":"2022-05-04T00:03:51.816475Z","iopub.status.idle":"2022-05-04T00:03:51.82519Z","shell.execute_reply.started":"2022-05-04T00:03:51.816435Z","shell.execute_reply":"2022-05-04T00:03:51.824453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Use Data Augmentation**","metadata":{"id":"O50F6eQlR1Ke"}},{"cell_type":"markdown","source":"nice trick to reduce overfitting as your data is not large enough","metadata":{"id":"f_v6qpKqSnJz"}},{"cell_type":"code","source":"# data augmentation layers [1]\n\ndata_augmentation = tf.keras.Sequential([              \n          tf.keras.layers.RandomFlip(mode='horizontal'),\n          tf.keras.layers.RandomRotation(factor=.2)\n          ])\n\n\n\n# Note > this layers active only while training \"model.fit\" and inactive when using \"model.evaluate\"","metadata":{"id":"n2cHSTqgSiUH","execution":{"iopub.status.busy":"2022-05-04T00:03:51.826435Z","iopub.execute_input":"2022-05-04T00:03:51.827655Z","iopub.status.idle":"2022-05-04T00:03:51.856912Z","shell.execute_reply.started":"2022-05-04T00:03:51.82762Z","shell.execute_reply":"2022-05-04T00:03:51.856279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**apply these layers to sample image and see the result.**","metadata":{"id":"YrMVO1jWdHYO"}},{"cell_type":"code","source":"for image , _ in train_dataset.take(1):\n\n  plt.figure(figsize = (10,10))\n  sample_image = image[0]\n\n  for i in range(9):\n\n    ax = plt.subplot(3,3,i+1)\n    ax.get_xaxis().set_visible(False)\n    ax.get_yaxis().set_visible(False)\n\n    aug_img = data_augmentation(tf.expand_dims(sample_image,0))\n    plt.imshow(aug_img[0]/255)\n    ","metadata":{"id":"e5c-bHaYdfb1","outputId":"b5632fce-c980-4cf2-e0d1-847355efda4b","execution":{"iopub.status.busy":"2022-05-04T00:03:51.858002Z","iopub.execute_input":"2022-05-04T00:03:51.85824Z","iopub.status.idle":"2022-05-04T00:03:52.946867Z","shell.execute_reply.started":"2022-05-04T00:03:51.858208Z","shell.execute_reply":"2022-05-04T00:03:52.946243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**rescale pixels value**\n\nwe will use `tf.keras.applications.MobileNetV2` for use as base model\nbut this model expect pixel value in [1,-1] while pixel value in our images in [0,255]  , so we need to rescaling them","metadata":{"id":"EEatxnpZr7cK"}},{"cell_type":"code","source":"# rescaling layer [2]\n\npreprocess_input =  tf.keras.applications.mobilenet_v2.preprocess_input ","metadata":{"id":"V4teHGOXCWZZ","execution":{"iopub.status.busy":"2022-05-04T00:03:52.948162Z","iopub.execute_input":"2022-05-04T00:03:52.948564Z","iopub.status.idle":"2022-05-04T00:03:52.953828Z","shell.execute_reply.started":"2022-05-04T00:03:52.948528Z","shell.execute_reply":"2022-05-04T00:03:52.952651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create the base model from pre-trained ","metadata":{"id":"-cbL4H56Cs3m"}},{"cell_type":"markdown","source":"we will create base model from the **MobileNetV2** please check API [Here](https://www.tensorflow.org/api_docs/python/tf/keras/applications/mobilenet_v2)","metadata":{"id":"QoQWj66KDKjx"}},{"cell_type":"code","source":"IMG_SHAPE = IMG_SIZE + (3,)\nbase_model = tf.keras.applications.MobileNetV2(\n    input_shape = IMG_SHAPE,\n    include_top = False, # pick which layer will use and top layers not include here\n    weights = 'imagenet'\n)","metadata":{"id":"vNvkXb5tD4ek","outputId":"6b39b973-9c1f-4128-8c11-b5a3ba925824","execution":{"iopub.status.busy":"2022-05-04T00:03:52.955004Z","iopub.execute_input":"2022-05-04T00:03:52.955647Z","iopub.status.idle":"2022-05-04T00:03:54.45192Z","shell.execute_reply.started":"2022-05-04T00:03:52.955597Z","shell.execute_reply":"2022-05-04T00:03:54.451149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# base model layers [3]\n\nimage_batch, label_batch = next(iter(train_dataset))\nfeature_batch = base_model(image_batch) \nprint(feature_batch.shape)","metadata":{"id":"XxZ1ZglOHE_N","outputId":"6f164929-c45e-4a4b-ce8b-5b97bcd96e8f","execution":{"iopub.status.busy":"2022-05-04T00:03:54.453336Z","iopub.execute_input":"2022-05-04T00:03:54.453645Z","iopub.status.idle":"2022-05-04T00:04:00.79709Z","shell.execute_reply.started":"2022-05-04T00:03:54.453573Z","shell.execute_reply":"2022-05-04T00:04:00.796292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Feature Extraction","metadata":{"id":"J0ClXwH3Hd8S"}},{"cell_type":"markdown","source":"**Note** --\n\nwe use base model from `MobileNetV2` this model has many layers already, we pick some of these layers but need to freeze what we pick , it is Important step","metadata":{"id":"yCVH9KJbHq9p"}},{"cell_type":"code","source":"base_model.trainable = False","metadata":{"id":"9MAlJwNIJQTX","execution":{"iopub.status.busy":"2022-05-04T00:04:00.798337Z","iopub.execute_input":"2022-05-04T00:04:00.799Z","iopub.status.idle":"2022-05-04T00:04:00.808815Z","shell.execute_reply.started":"2022-05-04T00:04:00.798962Z","shell.execute_reply":"2022-05-04T00:04:00.807835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lets take a look about base_model architecture\nbase_model.summary()","metadata":{"id":"wmzfa1LEJmpB","execution":{"iopub.status.busy":"2022-05-04T00:04:00.810094Z","iopub.execute_input":"2022-05-04T00:04:00.810361Z","iopub.status.idle":"2022-05-04T00:04:00.886024Z","shell.execute_reply.started":"2022-05-04T00:04:00.810326Z","shell.execute_reply":"2022-05-04T00:04:00.885213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n> from summary above you can see `BatchNormalization layer`\nyou should see [API](https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization) for this layer and understand why shoud make `layer.trainable = False` \n\n> you should keep the `BatchNormalization layers` in inference mode by passing `training = False` when calling the base model. Otherwise, the updates applied to the non-trainable weights will **destroy** what the model has learned.\n\n","metadata":{"id":"44YxkNAJJ3SM"}},{"cell_type":"markdown","source":"**Add a classification head** \n","metadata":{"id":"Lgt_ddLmSx-b"}},{"cell_type":"code","source":"# feature extractor layers [4]\n\nglobal_average_layer = tf.keras.layers.GlobalAveragePooling2D()\nfeature_average_layer = global_average_layer(feature_batch)\n\nprediction_layer = tf.keras.layers.Dense(1) # note you don`t need activation here as output will be raw prediction 0 or 1 \npredication_batch = prediction_layer(feature_average_layer)\n\nprint(predication_batch.shape)\n","metadata":{"id":"QAHwdJTqluhA","outputId":"a37ad3a6-8090-4ce0-cc3b-be60eaa1a446","execution":{"iopub.status.busy":"2022-05-04T00:04:00.887221Z","iopub.execute_input":"2022-05-04T00:04:00.887455Z","iopub.status.idle":"2022-05-04T00:04:00.917208Z","shell.execute_reply.started":"2022-05-04T00:04:00.887423Z","shell.execute_reply":"2022-05-04T00:04:00.916494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Building a model by chaining all together**","metadata":{"id":"uEQTV6xTrRhN"}},{"cell_type":"code","source":"inputs = tf.keras.Input(shape = (160,160,3))\nx = data_augmentation(inputs)\nx = preprocess_input(x)\nx = base_model(x,training =False) # note batchnormalization \nx = global_average_layer(x)\nx = tf.keras.layers.Dropout(.2)(x) # reduce overfitting\noutputs = prediction_layer(x)\nmodel = tf.keras.Model(inputs,outputs)\n","metadata":{"id":"k1MmkTG1rwFq","execution":{"iopub.status.busy":"2022-05-04T00:04:00.918244Z","iopub.execute_input":"2022-05-04T00:04:00.918554Z","iopub.status.idle":"2022-05-04T00:04:01.346591Z","shell.execute_reply.started":"2022-05-04T00:04:00.918511Z","shell.execute_reply":"2022-05-04T00:04:01.345961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**compile the model**","metadata":{"id":"tbYMYtAItEl7"}},{"cell_type":"code","source":"learning_rate = 0.0001\nmodel.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),\n              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),\n              metrics=['accuracy'])","metadata":{"id":"F494jUFMuDrE","execution":{"iopub.status.busy":"2022-05-04T00:04:01.347732Z","iopub.execute_input":"2022-05-04T00:04:01.34795Z","iopub.status.idle":"2022-05-04T00:04:01.364106Z","shell.execute_reply.started":"2022-05-04T00:04:01.347919Z","shell.execute_reply":"2022-05-04T00:04:01.363378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#showing model summary\nmodel.summary()","metadata":{"id":"y-o7ixq-uKta","outputId":"398c8ae1-e8db-423c-981b-d148a3ef4634","execution":{"iopub.status.busy":"2022-05-04T00:04:01.365163Z","iopub.execute_input":"2022-05-04T00:04:01.365951Z","iopub.status.idle":"2022-05-04T00:04:01.384184Z","shell.execute_reply.started":"2022-05-04T00:04:01.365884Z","shell.execute_reply":"2022-05-04T00:04:01.383528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# finally training the model\ninitial_epochs = 30\nhistory = model.fit(train_dataset,\n                    epochs=initial_epochs,\n                    validation_data = validation_dataset\n                    )","metadata":{"id":"YJahKx_puTw7","outputId":"22b3a121-b653-43be-c8f8-b58eaaf5974a","execution":{"iopub.status.busy":"2022-05-04T00:04:01.385406Z","iopub.execute_input":"2022-05-04T00:04:01.385661Z","iopub.status.idle":"2022-05-04T00:06:17.586261Z","shell.execute_reply.started":"2022-05-04T00:04:01.38563Z","shell.execute_reply":"2022-05-04T00:06:17.585323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Evaluation\n\nlook at the learning curves of the training and validation accuracy/loss","metadata":{"id":"WHxSlRLju_lo"}},{"cell_type":"code","source":"loss, accuracy = model.evaluate(test_dataset)\n\nprint(\"Test Model loss: {:.2f}\".format(loss))\nprint(\"Test Model accuracy: {:.2f}\".format(accuracy))","metadata":{"id":"VEyVVbRcwZwX","outputId":"e694e682-6e49-4b1f-c4aa-6fcef73bfd5e","execution":{"iopub.status.busy":"2022-05-04T00:06:17.588714Z","iopub.execute_input":"2022-05-04T00:06:17.589336Z","iopub.status.idle":"2022-05-04T00:06:18.23985Z","shell.execute_reply.started":"2022-05-04T00:06:17.589295Z","shell.execute_reply":"2022-05-04T00:06:18.239067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nplt.figure(figsize=(8, 8))\nplt.subplot(2, 1, 1)\nplt.plot(acc, label='Training Accuracy')\nplt.plot(val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.ylabel('Accuracy')\nplt.ylim([min(plt.ylim()),1])\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(2, 1, 2)\nplt.plot(loss, label='Training Loss')\nplt.plot(val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.ylabel('Cross Entropy')\nplt.ylim([0,1.0])\nplt.title('Training and Validation Loss')\nplt.xlabel('epoch')\nplt.show()","metadata":{"id":"PS5V6-auuzmW","outputId":"c2b980c4-5491-4081-f170-f80b752bc185","execution":{"iopub.status.busy":"2022-05-04T00:06:18.240926Z","iopub.execute_input":"2022-05-04T00:06:18.241171Z","iopub.status.idle":"2022-05-04T00:06:18.548137Z","shell.execute_reply.started":"2022-05-04T00:06:18.241142Z","shell.execute_reply":"2022-05-04T00:06:18.54749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **UP Vote 🥺**\nThanks , love you bro 👍","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}