{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-18T09:24:27.998941Z","iopub.execute_input":"2021-08-18T09:24:27.999548Z","iopub.status.idle":"2021-08-18T09:24:28.035267Z","shell.execute_reply.started":"2021-08-18T09:24:27.999408Z","shell.execute_reply":"2021-08-18T09:24:28.033592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.display import clear_output","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:24:32.096422Z","iopub.execute_input":"2021-08-18T09:24:32.097005Z","iopub.status.idle":"2021-08-18T09:24:32.104106Z","shell.execute_reply.started":"2021-08-18T09:24:32.096801Z","shell.execute_reply":"2021-08-18T09:24:32.102812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# unzip training data\n!unzip /kaggle/input/dogs-vs-cats/train.zip\nclear_output()\n\n# install imutils\n!pip install imutils\nclear_output()\n\n# install easy_tfrecord\n!pip install easy-tfrecord\nclear_output()","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:24:32.547564Z","iopub.execute_input":"2021-08-18T09:24:32.547995Z","iopub.status.idle":"2021-08-18T09:25:07.626361Z","shell.execute_reply.started":"2021-08-18T09:24:32.547956Z","shell.execute_reply":"2021-08-18T09:25:07.625185Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import time\nimport numpy as np\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n\nfrom imutils import paths\nfrom tfrecord import TFRecord # easy-tfrecord\nfrom sklearn.model_selection import train_test_split","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:25:09.595818Z","iopub.execute_input":"2021-08-18T09:25:09.596276Z","iopub.status.idle":"2021-08-18T09:25:16.446078Z","shell.execute_reply.started":"2021-08-18T09:25:09.596213Z","shell.execute_reply":"2021-08-18T09:25:16.444817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Get image paths and labels","metadata":{}},{"cell_type":"code","source":"# set the directory\ndata_dir = \"./train\"\n\n# get all the image paths\nimage_paths = list(paths.list_images(data_dir))\nprint(image_paths[:5])","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:25:19.236352Z","iopub.execute_input":"2021-08-18T09:25:19.236747Z","iopub.status.idle":"2021-08-18T09:25:19.340754Z","shell.execute_reply.started":"2021-08-18T09:25:19.236700Z","shell.execute_reply":"2021-08-18T09:25:19.339237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# extract the labels\nlabels = [path.split(os.path.sep)[-1].split('.')[0]\n          for path in image_paths]\nclasses = sorted(set(labels))\nprint(\"Unique Classes\", classes)\nprint(labels[:5])","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:25:35.465262Z","iopub.execute_input":"2021-08-18T09:25:35.465606Z","iopub.status.idle":"2021-08-18T09:25:35.492317Z","shell.execute_reply.started":"2021-08-18T09:25:35.465576Z","shell.execute_reply":"2021-08-18T09:25:35.491005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Encode labels","metadata":{}},{"cell_type":"code","source":"# encode the labels\nlabel2idx = {label: idx for idx, label in enumerate(classes)}\nidx2label = {idx: label for label, idx in label2idx.items()}\nlabels_enc = [label2idx[label] for label in labels]\nprint(\"Labels: \", labels[:5])\nprint(\"Labels Encoded: \",labels_enc[:5])","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:25:41.217060Z","iopub.execute_input":"2021-08-18T09:25:41.217427Z","iopub.status.idle":"2021-08-18T09:25:41.227996Z","shell.execute_reply.started":"2021-08-18T09:25:41.217398Z","shell.execute_reply":"2021-08-18T09:25:41.226684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Split into train, valid & test","metadata":{}},{"cell_type":"code","source":"# split the dataset into train, valid, test\nX_train, X_valid, y_train, y_valid = train_test_split(image_paths, labels_enc, test_size=0.2,\n                                                      random_state=42)\n\nX_valid, X_test, y_valid, y_test = train_test_split(X_valid, y_valid, test_size=0.2,\n                                                    random_state=42)\nlen(X_train), len(X_valid), len(X_test)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:25:48.645629Z","iopub.execute_input":"2021-08-18T09:25:48.646055Z","iopub.status.idle":"2021-08-18T09:25:48.682100Z","shell.execute_reply.started":"2021-08-18T09:25:48.646025Z","shell.execute_reply":"2021-08-18T09:25:48.680877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create tf.data Datasets","metadata":{}},{"cell_type":"code","source":"# create a function to load the image\ndef load_images(image_path, label, classes=classes):\n    image = tf.io.read_file(image_path)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [224, 224])\n    image = tf.cast(image, tf.uint8)\n    image = image / 255\n    \n    # onehot encode label\n    oh_label = tf.one_hot(label, depth=len(classes))\n    return image, oh_label","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:02.726490Z","iopub.execute_input":"2021-08-18T09:26:02.726866Z","iopub.status.idle":"2021-08-18T09:26:02.735276Z","shell.execute_reply.started":"2021-08-18T09:26:02.726835Z","shell.execute_reply":"2021-08-18T09:26:02.733774Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create tf dataset\ntrain_set = tf.data.Dataset.from_tensor_slices((X_train, y_train)).shuffle(len(X_train))\nvalid_set = tf.data.Dataset.from_tensor_slices((X_valid, y_valid))\ntest_set = tf.data.Dataset.from_tensor_slices((X_test, y_test))\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n# apply load_images func\ntrain_set = train_set.map(load_images, num_parallel_calls=AUTOTUNE)\ntrain_set = train_set.batch(32).prefetch(AUTOTUNE)\n\nvalid_set = valid_set.map(load_images, num_parallel_calls=AUTOTUNE)\nvalid_set = valid_set.batch(32).prefetch(AUTOTUNE)\n\ntest_set = test_set.map(load_images, num_parallel_calls=AUTOTUNE)\ntest_set = test_set.batch(32).prefetch(AUTOTUNE)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:03.187403Z","iopub.execute_input":"2021-08-18T09:26:03.187804Z","iopub.status.idle":"2021-08-18T09:26:05.839499Z","shell.execute_reply.started":"2021-08-18T09:26:03.187744Z","shell.execute_reply":"2021-08-18T09:26:05.838175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Display some images","metadata":{}},{"cell_type":"code","source":"fig = plt.figure(figsize=(10, 8))\n\nfor batch in train_set.take(1):\n    indices = np.random.randint(32, size=8)\n    for i,idx in enumerate(indices):\n        ax = fig.add_subplot(2, 4, i+1)\n        plt.imshow(batch[0][idx])\n        plt.axis(\"off\")\n        plt.title(idx2label[batch[1][idx].numpy().argmax(axis=-1)])\n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:37.006732Z","iopub.execute_input":"2021-08-18T09:26:37.007179Z","iopub.status.idle":"2021-08-18T09:26:37.966685Z","shell.execute_reply.started":"2021-08-18T09:26:37.007150Z","shell.execute_reply":"2021-08-18T09:26:37.965638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build Model","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import BatchNormalization\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D\nfrom tensorflow.keras.layers import Activation, Dropout\nfrom tensorflow.keras.layers import Flatten, Dense\nfrom tensorflow.keras.regularizers import l2\nfrom tensorflow.keras import backend as K","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:52.466795Z","iopub.execute_input":"2021-08-18T09:26:52.467227Z","iopub.status.idle":"2021-08-18T09:26:52.475566Z","shell.execute_reply.started":"2021-08-18T09:26:52.467175Z","shell.execute_reply":"2021-08-18T09:26:52.474022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AlexNet:\n    @staticmethod\n    def build(width, height, depth, classes, reg=0.0002):\n        # initialize the model \n        model = Sequential()\n        # Block 1\n        model.add(Conv2D(96, 11, strides=4, input_shape=(height, width, depth),\n                         padding=\"same\", kernel_regularizer=l2(reg), activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))\n        model.add(Dropout(0.25))\n        \n        # Block 2\n        model.add(Conv2D(256, 5, padding=\"same\", kernel_regularizer=l2(reg),\n                         activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))\n        model.add(Dropout(0.25))\n        \n        # Block 3\n        model.add(Conv2D(384, 3, padding=\"same\", kernel_regularizer=l2(reg),\n                         activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(Conv2D(384, 3, padding=\"same\", kernel_regularizer=l2(reg),\n                         activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(Conv2D(256, 3, padding=\"same\", kernel_regularizer=l2(reg),\n                         activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))\n        model.add(Dropout(0.25))\n        \n        # Block 4\n        model.add(Flatten())\n        model.add(Dense(4096, kernel_regularizer=l2(reg), activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(Dropout(0.5))\n        \n        # Block 5\n        model.add(Dense(4096, kernel_regularizer=l2(reg), activation=\"relu\"))\n        model.add(BatchNormalization())\n        model.add(Dropout(0.5))\n        \n        # Softmax Classifier\n        model.add(Dense(classes, kernel_regularizer=l2(reg), activation=\"softmax\"))\n        \n        return model","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:52.905881Z","iopub.execute_input":"2021-08-18T09:26:52.906351Z","iopub.status.idle":"2021-08-18T09:26:52.921050Z","shell.execute_reply.started":"2021-08-18T09:26:52.906317Z","shell.execute_reply":"2021-08-18T09:26:52.919688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training with TF Records (easy_tfrecord)\n**Writing TFRecords**","metadata":{}},{"cell_type":"code","source":"# instantiate object of TFRecord class\ntfrec = TFRecord(n_classes=2, image_shape=[224, 224, 3]) # image_shape: all images will be resized to this shape [224, 224]\n\n# compute n_shards for training set (you can directly pass value to n_shards)\ntrain_shards = tfrec.compute_nshards(X_train)\ntrain_tfrecords = tfrec.write_tfrecords(X_train, y_train, save_path=\"./data/train\",\n                                        n_shards=train_shards)\n\n# compute n_shards for validation set (optional)\nvalid_shards = tfrec.compute_nshards(X_valid)\ntrain_tfrecords = tfrec.write_tfrecords(X_valid, y_valid, save_path=\"./data/valid\", \n                                        n_shards=valid_shards)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:26:54.855613Z","iopub.execute_input":"2021-08-18T09:26:54.856149Z","iopub.status.idle":"2021-08-18T09:29:07.378788Z","shell.execute_reply.started":"2021-08-18T09:26:54.856114Z","shell.execute_reply":"2021-08-18T09:29:07.377589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Loading from TFRecords**","metadata":{}},{"cell_type":"code","source":"# create datasets for training and validation set\ntrain_dataset = tfrec.parse_tfrecord(\"./data/train\")\nvalid_dataset = tfrec.parse_tfrecord(\"./data/valid\")\n\n# create a function to normalize images: (any kind of preprocessing functions can be added)\ndef normalize(image, label):\n    image = image / 255\n    return (image, label)\n\n# normalize images\ntrain_dataset = train_dataset.map(normalize, num_parallel_calls=AUTOTUNE)\nvalid_dataset = valid_dataset.map(normalize, num_parallel_calls=AUTOTUNE)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:30:44.216512Z","iopub.execute_input":"2021-08-18T09:30:44.216944Z","iopub.status.idle":"2021-08-18T09:30:44.314976Z","shell.execute_reply.started":"2021-08-18T09:30:44.216887Z","shell.execute_reply":"2021-08-18T09:30:44.314019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Build and train model**","metadata":{}},{"cell_type":"code","source":"# build model\nmodel = AlexNet.build(width=227, height=227, depth=3, classes=2, reg=0.0002)\n\n# define optimizer\noptimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)\n# compile model\nmodel.compile(loss=\"binary_crossentropy\", optimizer=optimizer, metrics=[\"accuracy\"])\n\n# create early stopping callback\nearly_stop = tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True)\n\n# let's track time taken for 5 epochs\nstart = time.time()\n# fit the model\nhistory = model.fit(train_dataset, epochs=5, validation_data=valid_dataset,\n                    callbacks=[early_stop])\nend = time.time()","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:30:57.795851Z","iopub.execute_input":"2021-08-18T09:30:57.796241Z","iopub.status.idle":"2021-08-18T09:33:35.814147Z","shell.execute_reply.started":"2021-08-18T09:30:57.796213Z","shell.execute_reply":"2021-08-18T09:33:35.812809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"time_taken = round((end - start)/60, 2)\nprint(\"Time taken for 5 epochs: {} mins\".format(time_taken))","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:33:44.626837Z","iopub.execute_input":"2021-08-18T09:33:44.627305Z","iopub.status.idle":"2021-08-18T09:33:44.634240Z","shell.execute_reply.started":"2021-08-18T09:33:44.627274Z","shell.execute_reply":"2021-08-18T09:33:44.632884Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Uncomment below line if you want to plot losses and accuracies.**","metadata":{}},{"cell_type":"code","source":"# plot accuracy and loss\n# def plot_metrics(history):\n#     fig, ax = plt.subplots(1, 2, figsize=(15, 6))\n#     ax[0].plot(history.history[\"loss\"], label=\"Train Loss\")\n#     ax[0].plot(history.history[\"val_loss\"], label=\"Validation Loss\")\n#     ax[0].set_title(\"Training and Validation Loss\")\n#     ax[0].set_xlabel(\"Epoch\")\n#     ax[0].set_ylabel(\"Loss\")\n\n#     ax[1].plot(history.history[\"accuracy\"], label=\"Train Accuracy\")\n#     ax[1].plot(history.history[\"val_accuracy\"], label=\"Validation Accuracy\")\n#     ax[1].set_title(\"Training and Validation Accuracy\")\n#     ax[1].set_xlabel(\"Epoch\")\n#     ax[1].set_ylabel(\"Accuracy\")\n#     plt.suptitle(\"Trained without TF Records\")\n#     plt.show()\n\n# plot_metrics(history)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:19:53.212088Z","iopub.status.idle":"2021-08-18T09:19:53.212669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Evaluate on a test set**","metadata":{}},{"cell_type":"code","source":"# evaluate on a test set\nloss, acc = model.evaluate(test_set, verbose=0)\nprint(\"Loss: {:.4f}\\nAccuracy: {:.2f}%\".format(loss, acc*100))","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:33:51.506727Z","iopub.execute_input":"2021-08-18T09:33:51.507146Z","iopub.status.idle":"2021-08-18T09:33:53.293597Z","shell.execute_reply.started":"2021-08-18T09:33:51.507101Z","shell.execute_reply":"2021-08-18T09:33:53.292597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Print classification report**","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import classification_report\n\npredictions = model.predict(test_set)\npredictions = predictions.argmax(axis=-1)\nprint(classification_report(y_test, predictions))","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:34:04.307569Z","iopub.execute_input":"2021-08-18T09:34:04.308036Z","iopub.status.idle":"2021-08-18T09:34:06.098702Z","shell.execute_reply.started":"2021-08-18T09:34:04.307995Z","shell.execute_reply":"2021-08-18T09:34:06.097560Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Without TF Records\nWatch time taken per epoch","metadata":{}},{"cell_type":"code","source":"# build model\nmodel_1 = AlexNet.build(width=224, height=224, depth=3, classes=2, reg=0.0002)\n\n# define optimizer\noptimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)\n# compile model\nmodel_1.compile(loss=\"binary_crossentropy\", optimizer=optimizer, metrics=[\"accuracy\"])\n\n# create early stopping callback\n# early_stop = tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True)\n\nstart = time.time()\n# fit the model\nhistory = model_1.fit(train_set, epochs=5, validation_data=valid_set)\nend = time.time()","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:34:16.617512Z","iopub.execute_input":"2021-08-18T09:34:16.617942Z","iopub.status.idle":"2021-08-18T09:38:19.692175Z","shell.execute_reply.started":"2021-08-18T09:34:16.617910Z","shell.execute_reply":"2021-08-18T09:38:19.690892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"time_taken = round((end - start)/60, 2)\nprint(\"Time taken for 5 epochs: {} mins\".format(time_taken))","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:38:19.695995Z","iopub.execute_input":"2021-08-18T09:38:19.696323Z","iopub.status.idle":"2021-08-18T09:38:19.705457Z","shell.execute_reply.started":"2021-08-18T09:38:19.696291Z","shell.execute_reply":"2021-08-18T09:38:19.704399Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot_metrics(history)","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:19:53.220418Z","iopub.status.idle":"2021-08-18T09:19:53.220973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# evaluate on a test set\nloss, acc = model_1.evaluate(test_set, verbose=0)\nprint(\"Loss: {:.4f}\\nAccuracy: {:.2f}%\".format(loss, acc*100))","metadata":{"execution":{"iopub.status.busy":"2021-08-18T09:42:17.938996Z","iopub.execute_input":"2021-08-18T09:42:17.939380Z","iopub.status.idle":"2021-08-18T09:42:19.615637Z","shell.execute_reply.started":"2021-08-18T09:42:17.939349Z","shell.execute_reply":"2021-08-18T09:42:19.614388Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Time taken to finish 5 epochs:\n- **With TFRecords**: _2.63 minutes_\n- **Without TFRecords**: _4.05 minutes_","metadata":{}}]}