{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Melanoma(skin cancer) Classification\n\nSkin cancer is the most prevalent type of cancer. Melanoma, specifically, is responsible for 75% of skin cancer deaths, despite being the least common skin cancer.It has an ability to spread to other organs more rapidly if it is not treated at an early stage.\nThe American Cancer Society estimates over 100,000 new melanoma cases will be diagnosed in 2020. It's also expected that almost 7,000 people will die from the disease. As with other cancers, early and accurate detection—potentially aided by data science—can make treatment more effective.\n\nIn this competition given an image of the cancer we are asked to predict whether it's beingn or malignant.\n\nSo let's get started.","execution_count":null},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install livelossplot","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"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)\nimport cv2\nimport PIL\nfrom IPython.display import Image, display\nfrom keras.applications.vgg16 import VGG16,preprocess_input\n# Plotly for the interactive viewer (see last section)\nimport plotly.graph_objs as go\nimport plotly.graph_objects as go\nfrom sklearn.metrics import cohen_kappa_score\nfrom sklearn.model_selection import train_test_split\nfrom keras.models import Sequential, Model,load_model\nfrom keras.applications.vgg16 import VGG16,preprocess_input\nfrom keras.applications.resnet50 import ResNet50\nfrom keras.preprocessing.image import ImageDataGenerator,load_img, img_to_array\nfrom keras.models import Sequential\nfrom keras.layers import Conv2D, MaxPooling2D, Dense, Dropout, Input, Flatten,BatchNormalization,Activation,AveragePooling2D\nfrom keras.layers import GlobalMaxPooling2D\nfrom keras.layers import UpSampling2D\nfrom keras.layers import Activation\nfrom keras.layers import MaxPool2D\nfrom keras.layers import Add\nfrom keras.layers import Multiply\nfrom keras.layers import Lambda\nfrom keras.regularizers import l2\nfrom keras.models import Model\nfrom keras.optimizers import Adam, SGD, RMSprop\nfrom keras.callbacks import ModelCheckpoint, Callback, EarlyStopping\nfrom keras.utils import to_categorical\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport skimage.io\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.python.keras import backend as K\nfrom livelossplot import PlotLossesKeras","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I'm gonna be using the jpeg files for training and testing.","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train_dir='/kaggle/input/siim-isic-melanoma-classification/jpeg/train/'\ntest_dir='/kaggle/input/siim-isic-melanoma-classification/jpeg/test/'\ntrain=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/train.csv')\ntest=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/test.csv')\nsubmission=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Since this is medical data I'm expecting it to be unbalanced.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train['target'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"dist=train['target'].value_counts()\nprint(\"Benign cases are\",(32542/(32542+584))*100)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The difference is huge and only 1.7% patients in our data have malignant cancer.\n\n**anatom_site_general_challenge** in the dataset refers to the location of the skin cancer given in the image.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"labels=train['anatom_site_general_challenge'].value_counts().index\nvalues=train['anatom_site_general_challenge'].value_counts().values\nfig = go.Figure(data=[go.Pie(labels=labels, values=values, textinfo='label+percent',\n                             insidetextorientation='radial'\n                            )])\nfig.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In more than half of the patients in our dataset, the cancer is found on the torso.\n\nNow if we look at the diagnosis provided by Dermatologists.(I have removed cases marked \"unknown\")","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"labels=train['diagnosis'].value_counts().index[1:]\nvalues=train['diagnosis'].value_counts().values[1:]\nfig = go.Figure(data=[go.Pie(labels=labels, values=values, textinfo='label+percent',\n                             insidetextorientation='radial'\n                            )])\nfig.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A \"nevus\" is basically a visible, circumscribed, chronic lesion of the skin. Since they are also called moles and also cover majority of the data, I think this diagnosis is for benign cases.\n\nLet's check it out.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"new=train.drop(labels=['image_name','patient_id','sex','age_approx','anatom_site_general_challenge','target'],axis=1)\npd.crosstab(new['diagnosis'].values,new['benign_malignant'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> So my presumption was true and most benign cases are diagnosed as **nevus**. \n\n> All patients diagnosed as \"melanoma\" have malignant cancers. I think this term is only reserved for severe cases.\n\n","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Before going any further with training let's take a look at sample photos from both classes.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"df_0=train[train['target']==0]\ndf_1=train[train['target']==1]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Benign Cases')\nbenign=[]\ndf_benign=df_0.sample(40)\ndf_benign=df_benign.reset_index()\nfor i in range(40):\n    img=cv2.imread(str(train_dir + df_benign['image_name'].iloc[i]+'.jpg'))\n    img = cv2.resize(img, (224,224))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = img.astype(np.float32)/255.\n    benign.append(img)\nf, ax = plt.subplots(5,8, figsize=(10,8))\nfor i, img in enumerate(benign):\n        ax[i//8, i%8].imshow(img)\n        ax[i//8, i%8].axis('off')\n        \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Malignant Cases')\nmalignant=[]\ndf_malignant=df_1.sample(40)\ndf_malignant=df_malignant.reset_index()\nfor i in range(40):\n    img=cv2.imread(str(train_dir + df_malignant['image_name'].iloc[i]+'.jpg'))\n    img = cv2.resize(img, (224,224))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = img.astype(np.float32)/255.\n    malignant.append(img)\nf, ax = plt.subplots(5,8, figsize=(10,8))\nfor i, img in enumerate(malignant):\n        ax[i//8, i%8].imshow(img)\n        ax[i//8, i%8].axis('off')\n        \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Preparing the Datasets","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"For training I'm going to use external [dataset](https://www.kaggle.com/shonenkov/melanoma-merged-external-data-512x512-jpeg) with duplicates removed from [Alex Shonenkov](https://www.kaggle.com/shonenkov). This dataset provides a boost from the original dataset in the competition. \nIt has more images from previous melanoma competitions and as shown in this [discussion](https://www.kaggle.com/c/siim-isic-melanoma-classification/discussion/157701) it has removed duplicate images found in the train set.\n\nI'm also balancing the classes a bit.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dir='../input/melanoma-merged-external-data-512x512-jpeg/512x512-dataset-melanoma/512x512-dataset-melanoma/'\nmarking=pd.read_csv('../input/melanoma-merged-external-data-512x512-jpeg/marking.csv')\ndf_1=marking[marking['target']==1]#5479 images\ndf_0=marking[marking['target']==0].sample(6000)\ntrain=pd.concat([df_0,df_1])\ntrain=train.reset_index()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels=[]\ndata=[]\nfor i in range(train.shape[0]):\n    data.append(train_dir + train['image_id'].iloc[i]+'.jpg')\n    labels.append(train['target'].iloc[i])\ndf=pd.DataFrame(data)\ndf.columns=['images']\ndf['target']=labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_data=[]\nfor i in range(test.shape[0]):\n    test_data.append(test_dir + test['image_name'].iloc[i]+'.jpg')\ndf_test=pd.DataFrame(test_data)\ndf_test.columns=['images']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(df['images'],df['target'], test_size=0.2, random_state=1234)\n\ntrain=pd.DataFrame(X_train)\ntrain.columns=['images']\ntrain['target']=y_train\n\nvalidation=pd.DataFrame(X_val)\nvalidation.columns=['images']\nvalidation['target']=y_val","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#let's initialize some things\nIMG_SIZE=(224,224)\nBATCH_SIZE=64\nEPOCHS=2","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I'll do some very basic preprocessing like \n* normalizing\n* reshaping\n* augmentation(only for tarin data)","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator(rescale=1./255,rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,horizontal_flip=True)\nval_datagen=ImageDataGenerator(rescale=1./255)\ntrain_generator = train_datagen.flow_from_dataframe(\n    train,\n    x_col='images',\n    y_col='target',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    class_mode='raw')\n\nvalidation_generator = val_datagen.flow_from_dataframe(\n    validation,\n    x_col='images',\n    y_col='target',\n    target_size=IMG_SIZE,\n    shuffle=False,\n    batch_size=BATCH_SIZE,\n    class_mode='raw')\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Modelling\nI'm using pretrained VGG-16 and adding the last dense layer.\n> **I know VGG is not a common choice for these competitions but it's a fairly simple architecture to start with compared to a Resnet or EfficientNet, also it takes less time to train and gives a decent baseline score on the Leaderboard.**\n\nThe competition is evaluated on AUC scores, so we'll use that as a metric. Focal loss is a better when it comes to class imbalance so I 'll be using it instead of Binary CrossEntropy.You can read more about it [here](https://arxiv.org/abs/1708.02002)","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def vgg16_model( num_classes=None):\n\n    model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\n    x=Flatten()(model.output)\n    output=Dense(1,activation='sigmoid')(x) # because we have to predict the AUC\n    model=Model(model.input,output)\n    \n    return model\n\nvgg_conv=vgg16_model(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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":{"trusted":true},"cell_type":"code","source":"opt = Adam(lr=1e-4)\nvgg_conv.compile(loss=focal_loss(), metrics=[tf.keras.metrics.AUC()],optimizer=opt)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nb_train_steps = train.shape[0]//BATCH_SIZE\nnb_val_steps=validation.shape[0]//BATCH_SIZE\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":"cb=[PlotLossesKeras()]\nvgg_conv.fit_generator(\n    train_generator,\n    steps_per_epoch=nb_train_steps,\n    epochs=EPOCHS,\n    validation_data=validation_generator,\n    callbacks=cb,\n    validation_steps=nb_val_steps)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Submission","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"target=[]\nfor path in df_test['images']:\n    img=cv2.imread(str(path))\n    img = cv2.resize(img, IMG_SIZE)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = img.astype(np.float32)/255.\n    img=np.reshape(img,(1,IMG_SIZE[0],IMG_SIZE[1],3))\n    prediction=vgg_conv.predict(img)\n    target.append(prediction[0][0])\n\nsubmission['target']=target\n    \n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I'll keep on updating this kernel with new experiments.\n\nIf you liked it please upvote the kernel.","execution_count":null}],"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}