{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":20270,"databundleVersionId":1222630,"sourceType":"competition"}],"dockerImageVersionId":29928,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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.","metadata":{}},{"cell_type":"code","source":"import tensorflow\nprint(tensorflow.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:32.985607Z","iopub.execute_input":"2025-02-15T18:08:32.985901Z","iopub.status.idle":"2025-02-15T18:08:32.990029Z","shell.execute_reply.started":"2025-02-15T18:08:32.985876Z","shell.execute_reply":"2025-02-15T18:08:32.989211Z"}},"outputs":[],"execution_count":null},{"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\nfrom keras.layers import GlobalMaxPooling2D\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 gc\nimport skimage.io\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.python.keras import backend as K","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:32.991603Z","iopub.execute_input":"2025-02-15T18:08:32.991836Z","iopub.status.idle":"2025-02-15T18:08:33.004645Z","shell.execute_reply.started":"2025-02-15T18:08:32.991813Z","shell.execute_reply":"2025-02-15T18:08:33.004036Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"I'm gonna be using the jpeg files for training and testing.","metadata":{}},{"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')","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.005817Z","iopub.execute_input":"2025-02-15T18:08:33.006162Z","iopub.status.idle":"2025-02-15T18:08:33.089327Z","shell.execute_reply.started":"2025-02-15T18:08:33.006126Z","shell.execute_reply":"2025-02-15T18:08:33.088695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.091225Z","iopub.execute_input":"2025-02-15T18:08:33.091492Z","iopub.status.idle":"2025-02-15T18:08:33.103681Z","shell.execute_reply.started":"2025-02-15T18:08:33.091467Z","shell.execute_reply":"2025-02-15T18:08:33.102741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Since this is medical data I'm expecting it to be unbalanced.","metadata":{}},{"cell_type":"code","source":"train['target'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.105267Z","iopub.execute_input":"2025-02-15T18:08:33.105661Z","iopub.status.idle":"2025-02-15T18:08:33.118134Z","shell.execute_reply.started":"2025-02-15T18:08:33.105624Z","shell.execute_reply":"2025-02-15T18:08:33.117526Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dist=train['target'].value_counts()\nprint(\"Benign cases are\",(32542/(32542+584))*100)\n","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.119353Z","iopub.execute_input":"2025-02-15T18:08:33.119615Z","iopub.status.idle":"2025-02-15T18:08:33.127118Z","shell.execute_reply.started":"2025-02-15T18:08:33.119593Z","shell.execute_reply":"2025-02-15T18:08:33.126409Z"}},"outputs":[],"execution_count":null},{"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.","metadata":{}},{"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.128184Z","iopub.execute_input":"2025-02-15T18:08:33.128525Z","iopub.status.idle":"2025-02-15T18:08:33.151900Z","shell.execute_reply.started":"2025-02-15T18:08:33.128493Z","shell.execute_reply":"2025-02-15T18:08:33.151010Z"}},"outputs":[],"execution_count":null},{"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\")","metadata":{}},{"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.152902Z","iopub.execute_input":"2025-02-15T18:08:33.153113Z","iopub.status.idle":"2025-02-15T18:08:33.172871Z","shell.execute_reply.started":"2025-02-15T18:08:33.153093Z","shell.execute_reply":"2025-02-15T18:08:33.172146Z"}},"outputs":[],"execution_count":null},{"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.","metadata":{}},{"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'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.173904Z","iopub.execute_input":"2025-02-15T18:08:33.174124Z","iopub.status.idle":"2025-02-15T18:08:33.204571Z","shell.execute_reply.started":"2025-02-15T18:08:33.174103Z","shell.execute_reply":"2025-02-15T18:08:33.203891Z"}},"outputs":[],"execution_count":null},{"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\nAs I have already said the data is very imbalanced so I'll train on only a sample of it.","metadata":{}},{"cell_type":"code","source":"df_0=train[train['target']==0].sample(2000)\ndf_1=train[train['target']==1]\ntrain=pd.concat([df_0,df_1])\ntrain=train.reset_index()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.206826Z","iopub.execute_input":"2025-02-15T18:08:33.207057Z","iopub.status.idle":"2025-02-15T18:08:33.218971Z","shell.execute_reply.started":"2025-02-15T18:08:33.207035Z","shell.execute_reply":"2025-02-15T18:08:33.218421Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Before going any further with training let's take a look at sample photos from both classes.","metadata":{}},{"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:33.220163Z","iopub.execute_input":"2025-02-15T18:08:33.220365Z","iopub.status.idle":"2025-02-15T18:08:42.869139Z","shell.execute_reply.started":"2025-02-15T18:08:33.220346Z","shell.execute_reply":"2025-02-15T18:08:42.868466Z"}},"outputs":[],"execution_count":null},{"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:42.870352Z","iopub.execute_input":"2025-02-15T18:08:42.870613Z","iopub.status.idle":"2025-02-15T18:08:48.024890Z","shell.execute_reply.started":"2025-02-15T18:08:42.870589Z","shell.execute_reply":"2025-02-15T18:08:48.024122Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preparing the Datasets","metadata":{}},{"cell_type":"code","source":"labels=[]\ndata=[]\nfor i in range(train.shape[0]):\n    data.append(train_dir + train['image_name'].iloc[i]+'.jpg')\n    labels.append(train['target'].iloc[i])\ndf=pd.DataFrame(data)\ndf.columns=['images']\ndf['target']=labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:48.026049Z","iopub.execute_input":"2025-02-15T18:08:48.026276Z","iopub.status.idle":"2025-02-15T18:08:48.079019Z","shell.execute_reply.started":"2025-02-15T18:08:48.026255Z","shell.execute_reply":"2025-02-15T18:08:48.078432Z"}},"outputs":[],"execution_count":null},{"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']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:48.080070Z","iopub.execute_input":"2025-02-15T18:08:48.080343Z","iopub.status.idle":"2025-02-15T18:08:48.183499Z","shell.execute_reply.started":"2025-02-15T18:08:48.080318Z","shell.execute_reply":"2025-02-15T18:08:48.182931Z"}},"outputs":[],"execution_count":null},{"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:48.184452Z","iopub.execute_input":"2025-02-15T18:08:48.184674Z","iopub.status.idle":"2025-02-15T18:08:48.193758Z","shell.execute_reply.started":"2025-02-15T18:08:48.184653Z","shell.execute_reply":"2025-02-15T18:08:48.192978Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"I'll do some very basic preprocessing like \n* normalizing\n* reshaping\n* augmentation(only for tarin data)","metadata":{}},{"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)\n\nval_datagen=ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow_from_dataframe(\n    train,\n    x_col='images',\n    y_col='target',\n    target_size=(224, 224),\n    batch_size=8,\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=(224, 224),\n    shuffle=False,\n    batch_size=8,\n    class_mode='raw')\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:08:48.195125Z","iopub.execute_input":"2025-02-15T18:08:48.195465Z","iopub.status.idle":"2025-02-15T18:09:17.173549Z","shell.execute_reply.started":"2025-02-15T18:08:48.195432Z","shell.execute_reply":"2025-02-15T18:09:17.172606Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Modelling\nI'm using pretrained VGG-16 and adding the last dense layer.\nThe competition is evaluated on AUC scores, so we'll use that as a metric.","metadata":{}},{"cell_type":"code","source":"def resnet50_model( num_classes=None):\n\n    model = ResNet50(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\nresnet_conv=resnet50_model(1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:27:11.239059Z","iopub.execute_input":"2025-02-15T18:27:11.239436Z","iopub.status.idle":"2025-02-15T18:27:13.883710Z","shell.execute_reply.started":"2025-02-15T18:27:11.239397Z","shell.execute_reply":"2025-02-15T18:27:13.882888Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Also, because of class imbalance it's better to use **focal loss** rather than normal **binary_crossentropy**.You can read more about it [here](https://arxiv.org/abs/1708.02002)","metadata":{}},{"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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:09:23.649356Z","iopub.status.idle":"2025-02-15T18:09:23.649751Z","shell.execute_reply":"2025-02-15T18:09:23.649581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"opt = Adam(lr=1e-5)\nresnet_conv.compile(loss=focal_loss(), metrics=[tf.keras.metrics.AUC()],optimizer=opt)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:09:23.650650Z","iopub.status.idle":"2025-02-15T18:09:23.650970Z","shell.execute_reply":"2025-02-15T18:09:23.650811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"nb_epochs = 20\nbatch_size = 64\nnb_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))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:09:23.651653Z","iopub.status.idle":"2025-02-15T18:09:23.651982Z","shell.execute_reply":"2025-02-15T18:09:23.651810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"resnet_conv.fit_generator(\n    train_generator,\n    steps_per_epoch=nb_train_steps,\n    epochs=nb_epochs,\n    validation_data=validation_generator,\n    validation_steps=nb_val_steps\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T18:09:23.652694Z","iopub.status.idle":"2025-02-15T18:09:23.653017Z","shell.execute_reply":"2025-02-15T18:09:23.652858Z"}},"outputs":[],"execution_count":null}]}