{"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":"# Imports\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom glob import glob # finds all the pathnames matching a specified pattern according to the rules used by the Unix shell\nimport cv2 # computer vision library for reading images\nfrom IPython.display import Image\nimport os, warnings\nimport matplotlib.pyplot as plt\nfrom matplotlib import gridspec\n\n# Tensorflow\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.layers.experimental import preprocessing\n\n#Keras\nfrom keras import applications\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom keras.applications import InceptionResNetV2\nfrom tensorflow.keras.applications.inception_resnet_v2 import preprocess_input\nfrom tensorflow.keras.preprocessing import image\n\nfrom sklearn.model_selection import train_test_split\n\n# Albumentations\n\nfrom albumentations import (\n    Compose, RandomBrightness, JpegCompression, HueSaturationValue, RandomContrast, HorizontalFlip,\n    Rotate\n)\nimport tensorflow_datasets as tfds\nfrom functools import partial\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\n# Reproducability\ndef set_seed(seed=31415):\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    os.environ['TF_DETERMINISTIC_OPS'] = '1'\nset_seed()\n\n# Set Matplotlib defaults\nplt.rc('figure', autolayout=True)\nplt.rc('axes', labelweight='bold', labelsize='large',\n       titleweight='bold', titlesize=18, titlepad=10)\nplt.rc('image', cmap='magma')\nwarnings.filterwarnings(\"ignore\") # to clean up output cells","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-17T08:15:27.251041Z","iopub.execute_input":"2021-06-17T08:15:27.25148Z","iopub.status.idle":"2021-06-17T08:15:36.063863Z","shell.execute_reply.started":"2021-06-17T08:15:27.251381Z","shell.execute_reply":"2021-06-17T08:15:36.062746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Path to the data\nPATH = \"../input/dog-breed-identification\"\nTRAIN_PATH = os.path.join(PATH, 'train/*')\nTEST_PATH = os.path.join(PATH, 'test/*')\nLABELS_PATH = os.path.join(PATH, 'labels.csv')\nDOG_IMAGES_PATH = os.path.join('../input/dog-images/*')\n\n# Set the parameters for the Keras model\nSIZE = 299\nNUM_CLASSES = 120\nBATCH_SIZE = 128\nEPOCHS = 50","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.065716Z","iopub.execute_input":"2021-06-17T08:15:36.066196Z","iopub.status.idle":"2021-06-17T08:15:36.075918Z","shell.execute_reply.started":"2021-06-17T08:15:36.066152Z","shell.execute_reply":"2021-06-17T08:15:36.074382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_model():\n    '''\n    Builds a pretrained model on the imagenet dataset\n    Model InceptionRestNetV2 with freezed weights\n    Sets the input shape to (SIZE, SIZE, 3)\n    Sets the output shape to 120 classes\n    '''\n    base_model = InceptionResNetV2(\n    include_top=False,\n    weights=\"imagenet\",\n    input_tensor=None,\n    input_shape=(SIZE, SIZE, 3),\n    pooling=None)\n    base_model.trainable = False\n\n    model = keras.models.Sequential([\n        base_model,\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dropout(0.2),\n        keras.layers.Dense(120, activation='softmax')\n    ])\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.078938Z","iopub.execute_input":"2021-06-17T08:15:36.079657Z","iopub.status.idle":"2021-06-17T08:15:36.088276Z","shell.execute_reply.started":"2021-06-17T08:15:36.079613Z","shell.execute_reply":"2021-06-17T08:15:36.086742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#pretrained_base.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.090781Z","iopub.execute_input":"2021-06-17T08:15:36.091315Z","iopub.status.idle":"2021-06-17T08:15:36.106462Z","shell.execute_reply.started":"2021-06-17T08:15:36.091262Z","shell.execute_reply":"2021-06-17T08:15:36.105237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef read_image(path):\n\n    img = image.load_img(path)\n\n    return img\n\n#\ndef parse_data(x, label):\n    image = read_image(x)\n    image = np.array(image)\n    label = np.array(label)\n    image = image.astype(np.float32)\n    label = label.astype(np.int32)\n    return image, label\n\ndef tf_parse(x, y):\n    x, y = tf.numpy_function(parse_data, [x, y], [tf.float32, tf.int32])\n    x.set_shape((SIZE, SIZE, 3))\n    #y.set_shape((NUM_CLASSES))\n    return x, y\n\n# \n\ndef tf_dataset(x, y):\n\n    dataset = tf.data.Dataset.from_tensor_slices((x, y))\n    dataset = dataset.map(tf_parse)\n    '''\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.repeat()\n    '''\n    return dataset\n","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.109132Z","iopub.execute_input":"2021-06-17T08:15:36.110124Z","iopub.status.idle":"2021-06-17T08:15:36.121145Z","shell.execute_reply.started":"2021-06-17T08:15:36.110077Z","shell.execute_reply":"2021-06-17T08:15:36.119823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the number of breeds\nlabels_df = pd.read_csv(LABELS_PATH)\nbreed = labels_df['breed'].unique()\nprint('Number of breeds: ', len(breed))","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.123029Z","iopub.execute_input":"2021-06-17T08:15:36.123612Z","iopub.status.idle":"2021-06-17T08:15:36.175894Z","shell.execute_reply.started":"2021-06-17T08:15:36.123499Z","shell.execute_reply":"2021-06-17T08:15:36.174811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set a unique id for each breed\nbreed_to_id = {name: i for i, name in enumerate(breed)}\nid_to_breed = {i: name for i, name in enumerate(breed)}","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.179232Z","iopub.execute_input":"2021-06-17T08:15:36.179555Z","iopub.status.idle":"2021-06-17T08:15:36.185354Z","shell.execute_reply.started":"2021-06-17T08:15:36.17951Z","shell.execute_reply":"2021-06-17T08:15:36.183727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a list of labels ids from each image\nids = glob(TRAIN_PATH)\nlabels = []\n\nfor image_id in ids:\n    image_id = image_id.split('/')[-1].split('.')[0]\n    breed_name = list(labels_df[labels_df.id == image_id]['breed'])[0]\n    breed_idx = breed_to_id[breed_name]\n    labels.append(breed_idx)","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:36.189724Z","iopub.execute_input":"2021-06-17T08:15:36.190541Z","iopub.status.idle":"2021-06-17T08:15:55.654268Z","shell.execute_reply.started":"2021-06-17T08:15:36.190487Z","shell.execute_reply":"2021-06-17T08:15:55.652745Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split the dataset\nx_train, x_valid, y_train, y_valid = train_test_split(ids, labels, test_size=0.2, random_state=42)","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:55.657069Z","iopub.execute_input":"2021-06-17T08:15:55.657723Z","iopub.status.idle":"2021-06-17T08:15:55.678131Z","shell.execute_reply.started":"2021-06-17T08:15:55.657672Z","shell.execute_reply":"2021-06-17T08:15:55.676661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Albumentations","metadata":{}},{"cell_type":"code","source":"# Create the datasets\ntrain_dataset = tf_dataset(x_train, y_train)\nvalid_dataset = tf_dataset(x_valid, y_valid)","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:55.67959Z","iopub.execute_input":"2021-06-17T08:15:55.680192Z","iopub.status.idle":"2021-06-17T08:15:58.195933Z","shell.execute_reply.started":"2021-06-17T08:15:55.680148Z","shell.execute_reply":"2021-06-17T08:15:58.194904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:58.197448Z","iopub.execute_input":"2021-06-17T08:15:58.198023Z","iopub.status.idle":"2021-06-17T08:15:58.209568Z","shell.execute_reply.started":"2021-06-17T08:15:58.19798Z","shell.execute_reply":"2021-06-17T08:15:58.208259Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef process_image(image, label, img_size):\n\n    # cast and normalize image\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    # apply simple augmentations\n    #image = tf.image.random_flip_left_right(image)\n    image = tf.image.resize(image,[img_size, img_size])\n    return image, label\n","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:58.211785Z","iopub.execute_input":"2021-06-17T08:15:58.212886Z","iopub.status.idle":"2021-06-17T08:15:58.222714Z","shell.execute_reply.started":"2021-06-17T08:15:58.212808Z","shell.execute_reply":"2021-06-17T08:15:58.221537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = train_dataset.map(partial(process_image, img_size=SIZE), num_parallel_calls=AUTOTUNE).batch(30).prefetch(AUTOTUNE)\ntrain_dataset","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:58.223775Z","iopub.execute_input":"2021-06-17T08:15:58.22411Z","iopub.status.idle":"2021-06-17T08:15:58.303227Z","shell.execute_reply.started":"2021-06-17T08:15:58.22408Z","shell.execute_reply":"2021-06-17T08:15:58.302194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def view_image(ds):\n    image, label = next(iter(ds)) # extract 1 batch from the dataset\n    image = image.numpy()\n    label = label.numpy()\n\n    fig = plt.figure(figsize=(22, 22))\n    for i in range(20):\n        ax = fig.add_subplot(4, 5, i+1, xticks=[], yticks=[])\n        ax.imshow(image[i])\n    \n        ax.set_title(f\"Label: {label[i]}\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:58.304607Z","iopub.execute_input":"2021-06-17T08:15:58.30502Z","iopub.status.idle":"2021-06-17T08:15:58.312279Z","shell.execute_reply.started":"2021-06-17T08:15:58.304981Z","shell.execute_reply":"2021-06-17T08:15:58.3105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_image(train_dataset)","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:15:58.314357Z","iopub.execute_input":"2021-06-17T08:15:58.315148Z","iopub.status.idle":"2021-06-17T08:16:00.880091Z","shell.execute_reply.started":"2021-06-17T08:15:58.315106Z","shell.execute_reply":"2021-06-17T08:16:00.877606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Instantiate augments\n# we can apply as many augments we want and adjust the values accordingly\n# here I have chosen the augments and their arguments at random\ntransforms = Compose([\n            Rotate(limit=40),\n            RandomBrightness(limit=0.1),\n            JpegCompression(quality_lower=85, quality_upper=100, p=0.5),\n            HueSaturationValue(hue_shift_limit=20, sat_shift_limit=30, val_shift_limit=20, p=0.5),\n            RandomContrast(limit=0.2, p=0.5),\n            HorizontalFlip(),\n        ])","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:00.881652Z","iopub.execute_input":"2021-06-17T08:16:00.882123Z","iopub.status.idle":"2021-06-17T08:16:00.898429Z","shell.execute_reply.started":"2021-06-17T08:16:00.882065Z","shell.execute_reply":"2021-06-17T08:16:00.896685Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def aug_fn(image, img_size):\n    data = {\"image\":image}\n    aug_data = transforms(**data)\n    aug_img = aug_data[\"image\"]\n    aug_img = tf.cast(aug_img/255.0, tf.float32)\n    aug_img = tf.image.resize(aug_img, size=[img_size, img_size])\n    return aug_img","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:00.900628Z","iopub.execute_input":"2021-06-17T08:16:00.901794Z","iopub.status.idle":"2021-06-17T08:16:00.912789Z","shell.execute_reply.started":"2021-06-17T08:16:00.90173Z","shell.execute_reply":"2021-06-17T08:16:00.911702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def process_data(image, label, img_size):\n    aug_img = tf.numpy_function(func=aug_fn, inp=[image, img_size], Tout=tf.float32)\n    return aug_img, label","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:00.916111Z","iopub.execute_input":"2021-06-17T08:16:00.918093Z","iopub.status.idle":"2021-06-17T08:16:00.928334Z","shell.execute_reply.started":"2021-06-17T08:16:00.918039Z","shell.execute_reply":"2021-06-17T08:16:00.927081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create dataset\ntrain = train_dataset.map(partial(process_data, img_size=SIZE),\n                  num_parallel_calls=AUTOTUNE).prefetch(AUTOTUNE)\ntrain","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:00.929959Z","iopub.execute_input":"2021-06-17T08:16:00.931711Z","iopub.status.idle":"2021-06-17T08:16:01.038583Z","shell.execute_reply.started":"2021-06-17T08:16:00.931665Z","shell.execute_reply":"2021-06-17T08:16:01.037584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_shapes(img, label, img_shape=(SIZE,SIZE,3)):\n    img.set_shape(img_shape)\n    label.set_shape([])\n    return img, label","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:01.043757Z","iopub.execute_input":"2021-06-17T08:16:01.04655Z","iopub.status.idle":"2021-06-17T08:16:01.054874Z","shell.execute_reply.started":"2021-06-17T08:16:01.046468Z","shell.execute_reply":"2021-06-17T08:16:01.053399Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.map(set_shapes, num_parallel_calls=AUTOTUNE).batch(32).prefetch(AUTOTUNE)\ntrain","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:01.060166Z","iopub.execute_input":"2021-06-17T08:16:01.063443Z","iopub.status.idle":"2021-06-17T08:16:01.15276Z","shell.execute_reply.started":"2021-06-17T08:16:01.060743Z","shell.execute_reply":"2021-06-17T08:16:01.151802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_image(train)","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:01.157888Z","iopub.execute_input":"2021-06-17T08:16:01.160715Z","iopub.status.idle":"2021-06-17T08:16:02.316325Z","shell.execute_reply.started":"2021-06-17T08:16:01.160638Z","shell.execute_reply":"2021-06-17T08:16:02.312314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Instanciate the model\nmodel = build_model()\nmodel.compile(loss='categorical_crossentropy', optimizer='Adam', metrics=['acc'])\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:02.317276Z","iopub.status.idle":"2021-06-17T08:16:02.318006Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set callbacks\ncallbacks = [\n    EarlyStopping(\n    patience=5,\n    min_delta=0.001,\n    restore_best_weights=True,\n    ),\n    ModelCheckpoint('model.h5', verbose=1, save_best_only=True),\n    ReduceLROnPlateau(factor=0.1, patience=5, min_lr=1e-6)\n]\ntrain_steps = (len(x_train)//BATCH_SIZE) + 1\nvalid_steps = (len(x_valid)//BATCH_SIZE) + 1\n\n# Training\nhistory = model.fit(train_dataset,\n          steps_per_epoch=train_steps,\n          validation_steps=valid_steps,\n          validation_data=(valid_dataset),\n          epochs=EPOCHS,\n          callbacks=callbacks\n         )","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:02.323186Z","iopub.status.idle":"2021-06-17T08:16:02.324374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history_frame = pd.DataFrame(history.history)\nhistory_frame.loc[:, ['loss', 'val_loss']].plot()\nhistory_frame.loc[:, ['acc', 'val_acc']].plot();","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:02.326229Z","iopub.status.idle":"2021-06-17T08:16:02.327252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_predictions(preds, top=5, class_list_path=None):\n    results = []\n    top_indices = (-preds).argsort()[0]\n    for i in range(top):\n        breed = id_to_breed.get(top_indices[i])\n        prob = preds[0][top_indices[i]]\n        results.append((breed, prob))\n    return results","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:02.329689Z","iopub.status.idle":"2021-06-17T08:16:02.33103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Testing with friends and family dogs","metadata":{}},{"cell_type":"code","source":"images = glob(DOG_IMAGES_PATH)\n\nfor i in range(len(images)):\n    img_path = images[i]\n    display(Image(filename=img_path, width=224, height=224))\n    x = read_image(img_path)\n    x = np.expand_dims(x, axis=0)\n\n    preds = model.predict(x)\n    print('Predicted:', decode_predictions(preds, top=3))","metadata":{"execution":{"iopub.status.busy":"2021-06-17T08:16:02.333225Z","iopub.status.idle":"2021-06-17T08:16:02.334662Z"},"trusted":true},"execution_count":null,"outputs":[]}]}