{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Load required libraries"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Loading of important libraries that are used throughout\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O\nimport cv2 # computer vision library\nimport keras # Python simplified interface to tensorflow\nimport matplotlib.pyplot as plt # data visualization tool\nfrom tensorflow.python.keras import backend as K # to utilize more of keras' functionality\nfrom keras.models import Model # the neural network model\nfrom keras.layers import Input, Lambda, Dense, Flatten # neural network layers\nfrom keras.applications.vgg16 import VGG16 # the transfer learning model VGG16","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# path to the training and test set\ntrain_dir='/kaggle/input/siim-isic-melanoma-classification/jpeg/train/'\ntest_dir='/kaggle/input/siim-isic-melanoma-classification/jpeg/test/'\n\n# loading the training and test set\ntrain=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/train.csv')\ntest=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# shows the first five rows of the training set\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Remove duplicate images from the training dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"# as per an ongoing discussion, there are some duplicate images in the training data, these images might adversely impact our model, \n# so, lets remove these images\ndup = pd.read_csv(\"/kaggle/input/siim-list-of-duplicates/2020_Challenge_duplicates.csv\")\n\ndrop_idx_list = []\nfor dup_image in dup.ISIC_id_paired:\n    for idx,image in enumerate(train.image_name):\n        if image == dup_image:\n            drop_idx_list.append(idx)\n\nprint(\"no. of duplicates in training dataset:\",len(drop_idx_list))\n\ntrain.drop(drop_idx_list,inplace=True)\n\nprint(\"updated dimensions of the training dataset:\",train.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# shows how many images are benign (target 0) and malignant (target 1), and as we can see, the training set is quite imbalanced\ntrain.target.value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modelling - VGG16 (Transfer Learning)"},{"metadata":{},"cell_type":"markdown","source":"## Data Preparation"},{"metadata":{},"cell_type":"markdown","source":"* ### Take Sample Images for training"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Since this is a huge dataset, we would take a sample of it for training purpose\n# In addition, to have a more balanced dataset, we create a new dataframe with more balanced amounts of benign and malignant images\ndf_0=train[train['target']==0].sample(3000)\ndf_1=train[train['target']==1]\ntrain=pd.concat([df_0,df_1])\ntrain=train.reset_index()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* ### Update Image Names"},{"metadata":{"trusted":true},"cell_type":"code","source":"# update image names with the whole path\ndef append_ext(fn):\n    return train_dir+fn+\".jpg\"\ntrain[\"image_name\"]=train[\"image_name\"].apply(append_ext)\n\ndef append_ext(fn):\n    return test_dir+fn+\".jpg\"\ntest[\"image_name\"]=test[\"image_name\"].apply(append_ext)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* ### Split into train and validate dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 20% of the training data is set aside for the validation purpose\nfrom sklearn.model_selection import train_test_split\n\nX_train, X_val, y_train, y_val = train_test_split(train['image_name'],train['target'], test_size=0.2, random_state=42)\n\n# training set\ntrain=pd.DataFrame(X_train)\ntrain.columns=['image_name']\ntrain['target']=y_train\n\n# validation set\nvalidation=pd.DataFrame(X_val)\nvalidation.columns=['image_name']\nvalidation['target']=y_val","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* ### Resize Images"},{"metadata":{"trusted":true},"cell_type":"code","source":"# import keras' image preprocessing libraries for images\nfrom keras.preprocessing.image import load_img, img_to_array, array_to_img\n\n# resizing the images to 128x128 for faster processing\nIMG_DIM = (128, 128)\n\n# load images using load_img function from keras preprocessing \n# target_size is used to load the images with smaller size\n# img_to_array will tranform the loaded image to an array\ntrain_imgs = [img_to_array(load_img(img, target_size=IMG_DIM)) for img in train.image_name]\nvalidation_imgs = [img_to_array(load_img(img, target_size=IMG_DIM)) for img in validation.image_name]\n\n# convert the list of arrays to array\ntrain_imgs = np.array(train_imgs)\nvalidation_imgs = np.array(validation_imgs)\n\nprint('Train dataset shape:', train_imgs.shape, \n      '\\tValidation dataset shape:', validation_imgs.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# define parameters for model training\nbatch_size = 32 # the total number of images processed per iteration\nnum_classes = 2 # we have two classes; benign and malignant\nepochs = 100 # the number of iteration over the entire training set\ninput_shape = (128, 128, 3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Define loss function"},{"metadata":{"trusted":true},"cell_type":"code","source":"# focal loss as we have an imbalanced data set\ndef focal_loss(alpha=0.25,gamma=2.0):\n    def focal_crossentropy(y_true, y_pred):\n        bce = K.binary_crossentropy(y_true, y_pred)\n        \n        y_pred = K.clip(y_pred, K.epsilon(), 1.- K.epsilon())\n        p_t = (y_true*y_pred) + ((1-y_true)*(1-y_pred))\n        \n        alpha_factor = 1\n        modulating_factor = 1\n\n        alpha_factor = y_true*alpha + ((1-alpha)*(1-y_true))\n        modulating_factor = K.pow((1-p_t), gamma)\n\n        # compute the final loss and return\n        return K.mean(alpha_factor*modulating_factor*bce, axis=-1)\n    return focal_crossentropy","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Optimizer & No. of Iterations"},{"metadata":{"trusted":true},"cell_type":"code","source":"# import optimizers from keras\nfrom keras.optimizers import Adam, SGD, RMSprop\n\n# use Adam optimizer\nopt = Adam(lr=1e-5)\n\n#total number of iterations is always equal to the total number of training samples divided by the batch_size.\nnb_train_steps = train.shape[0]//batch_size\nnb_val_steps=validation.shape[0]//batch_size\n\nprint(\"Number of training and validation steps: {} and {}\".format(nb_train_steps,nb_val_steps))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# pixel normalization and data augmentation\nfrom keras.preprocessing.image import ImageDataGenerator # ImageDataGenerator from keras can be used to both pixel normalization (rescale) and data augmentation (e.g. zoom, rotation, width, height, shear and flip the images)\n\n# rescaling and augmenting the training set images\ntrain_datagen = ImageDataGenerator(rescale=1./255, zoom_range=0.3, rotation_range=50,\n                                   width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, \n                                   horizontal_flip=True, fill_mode='nearest')\n\n# only rescaling the pixels in the validation set images\nval_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow(train_imgs, y_train, batch_size=batch_size)\nval_generator = val_datagen.flow(validation_imgs, y_val, batch_size=batch_size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# data augmentation example\nimg_id = 100\ngenerator_100 = train_datagen.flow(train_imgs[img_id:img_id+1], train.target[img_id:img_id+1],\n                                   batch_size=1)\naug_img = [next(generator_100) for i in range(0,5)]\nfig, ax = plt.subplots(1,5, figsize=(16, 6))\nprint('Labels:', [item[1][0] for item in aug_img])\nl = [ax[i].imshow(aug_img[i][0][0]) for i in range(0,5)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# import python garbage collector for memory mangagement\nimport gc\ndel train\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Define VGG16 Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import Model\nfrom keras.applications import vgg16\n\n# initializing the VGG16 model with pre-trained weights which was trained on ImageNet. \nvgg = vgg16.VGG16(include_top=False, weights='imagenet', \n                                     input_shape=input_shape)\n\n# flatten the output layer\noutput = vgg.layers[-1].output\noutput = keras.layers.Flatten()(output)\nvgg_model = Model(vgg.input, output)\n\n# set all layers to not be trained\nvgg_model.trainable = False\nfor layer in vgg_model.layers:\n    layer.trainable = False\n    \npd.set_option('max_colwidth', -1)\nlayers = [(layer, layer.name, layer.trainable) for layer in vgg_model.layers]\npd.DataFrame(layers, columns=['Layer Type', 'Layer Name', 'Layer Trainable']) \n\nvgg_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train the convolution layers from block4_conv1 to output layer in the model\nvgg_model.trainable = True\n\nset_trainable = False\nfor layer in vgg_model.layers:\n    if layer.name in ['block5_conv1', 'block4_conv1']:\n        set_trainable = True\n    if set_trainable:\n        layer.trainable = True\n    else:\n        layer.trainable = False\n        \nlayers = [(layer, layer.name, layer.trainable) for layer in vgg_model.layers]\npd.DataFrame(layers, columns=['Layer Type', 'Layer Name', 'Layer Trainable'])    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, InputLayer\nfrom keras.models import Sequential\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential, save_model, load_model\nfrom keras.callbacks import ModelCheckpoint\n\n# creating an instance of Sequential model\nmodel = Sequential()\n\n# add the VGG16 model\nmodel.add(vgg_model)\n\n# add dense and dropout layers\nmodel.add(Dense(512, activation='relu', input_dim=input_shape))\nmodel.add(Dropout(0.4))\nmodel.add(Dense(512, activation='relu'))\nmodel.add(Dropout(0.4))\nmodel.add(Dense(1, activation='sigmoid'))\n\n# compiling the model\nmodel.compile(loss=focal_loss(), metrics=[tf.keras.metrics.BinaryAccuracy(),tf.keras.metrics.FalsePositives(),tf.keras.metrics.FalseNegatives()],optimizer=opt)\n\n#we want to save the best model for our test predictions\ncheckpointer = ModelCheckpoint(filepath=\"weights.hdf5\", verbose=1, save_best_only=True)\n\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# implementing early stopping\n#from keras.callbacks import EarlyStopping\n#es = EarlyStopping(monitor='val_loss', patience=10, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# training of the model\nhistory = model.fit_generator(train_generator, steps_per_epoch=nb_train_steps, epochs=epochs,callbacks=[checkpointer],\n                           validation_data=val_generator, validation_steps=nb_val_steps, \n                              verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#checking model performance\nf, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))\nt = f.suptitle('VGG16 Performance', fontsize=12)\nf.subplots_adjust(top=0.85, wspace=0.3)\n\nepoch_list = list(range(1,101))\nax1.plot(epoch_list, history.history['binary_accuracy'], label='Train Accuracy')\nax1.plot(epoch_list, history.history['val_binary_accuracy'], label='Validation Accuracy')\nax1.set_xticks(np.arange(0, 101, 5))\nax1.set_ylabel('Accuracy Value')\nax1.set_xlabel('Epoch')\nax1.set_title('Accuracy')\nl1 = ax1.legend(loc=\"best\")\n\nax2.plot(epoch_list, history.history['loss'], label='Train Loss')\nax2.plot(epoch_list, history.history['val_loss'], label='Validation Loss')\nax2.set_xticks(np.arange(0, 101, 5))\nax2.set_ylabel('Loss Value')\nax2.set_xlabel('Epoch')\nax2.set_title('Loss')\nl2 = ax2.legend(loc=\"best\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_test = np.load('../input/siimisic-melanoma-resized-images/x_test_128.npy')\nx_test = x_test.astype('float16')\ntest_imgs_scaled = x_test / 255\ndel x_test\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#load our best saved model\nmodel.load_weights('weights.hdf5')\n\ntarget=[]\ni = 0\nfor img in test_imgs_scaled:\n    img1=np.reshape(img,(1,128,128,3))\n    prediction=model.predict(img1)\n    i = i + 1\n    print(\"predicted image no.\",i)\n    target.append(prediction[0][0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# submission file\nsub=pd.read_csv(\"../input/siim-isic-melanoma-classification/sample_submission.csv\")\nsub['target']=target\nsub.to_csv('submission.csv', index=False)\nsub.head()","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}