{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install efficientnet","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array\nfrom tensorflow.image import *\nfrom tensorflow.keras.models import Sequential, load_model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.models import Model\nimport tensorflow_addons as tfa \n\nimport matplotlib.pyplot as plt\nimport efficientnet.tfkeras as efn\n\nimport numpy as np\nimport pandas as pd\nimport os\nimport math\n\nimport wandb","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The idea behind this approach is that instead of jumping to directly classifying images into Melignant/ Benign, it might fruitful to check that melanoma images for different parts of the body are different in appearance. For instance, the metadata gives us different locations such as **Torso, Oral/Genital, Lower extremity, Upper extremity, Palms/Soles** for each image . Given this, we can try to make a separate classifier for images belonging to different parts of the body.","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#Some Pre-processing\nbase_path='../input/siim-isic-melanoma-classification/'\ntrain = pd.read_csv(os.path.join(base_path, 'train.csv'))\ntest = pd.read_csv(os.path.join(base_path, 'test.csv'))\nsample = pd.read_csv(os.path.join(base_path, 'sample_submission.csv'))\ntrain_img_path = base_path+'jpeg/train/'\ntest_img_path = base_path+'jpeg/test/'\n\nEPOCHS=30\nBATCH_SIZE=10\ninput_shape=(512, 512, 3)\nlr=1e-3","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Basic Helper Functions\n\ndef get_model(shape, weights):\n    \n   \n    input = Input(shape=shape)\n\n    base_model = efn.EfficientNetB3(weights=weights,include_top=False, input_shape=shape)\n    base_model.trainable = True\n    \n    output = base_model(input)\n    output = GlobalMaxPooling2D()(output)\n    output = Dense(256)(output)\n    output = LeakyReLU(alpha = 0.25)(output)\n    output = Dropout(0.25)(output)\n\n    output = Dense(16,activation=\"relu\")(output)\n    output = Dropout(0.15)(output)\n\n    output = Dense(1,activation=\"sigmoid\")(output)\n    \n    model = Model(input,output)\n    \n    return model\n    \ndef focal_loss(gamma=2., alpha=.25):\n    def focal_loss_fixed(y_true, y_pred):\n        pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))\n        pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))\n\n        pt_1 = K.clip(pt_1, 1e-3, .999)\n        pt_0 = K.clip(pt_0, 1e-3, .999)\n\n        return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1))-K.sum((1-alpha) * K.pow( pt_0, gamma) * K.log(1. - pt_0))\n\n    return focal_loss_fixed\n\ndef get_cosine_schedule_with_warmup(lr,num_warmup_steps, num_training_steps, num_cycles=0.75):\n  \n    def lrfn(epoch):\n        if epoch < num_warmup_steps:\n            return (float(epoch) / float(max(1, num_warmup_steps))) * lr\n\n        progress = float(epoch - num_warmup_steps ) / float(max(1, num_training_steps - num_warmup_steps))\n\n        return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))) * lr\n\n    return tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=True)\n\nlr_schedule= get_cosine_schedule_with_warmup(lr=0.00004,num_warmup_steps=4, num_training_steps=EPOCHS)\n\ndef lrfn2(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Imputing Missing Data","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"The metadata contains some missing values. So it is necessary to impute them.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train.columns = [ 'img_name', 'id', 'sex', 'age', 'location', 'diagnosis',\n    'benign_malignant', 'target'\n]\ntest.columns = ['img_name', 'id', 'sex', 'age', 'location']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train['location'].value_counts())\nprint(\"Number of NA values in location\", train['location'].isna().sum())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for df in [train, test]:\n    df['location'].fillna('unknown', inplace=True) #Replacing","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Filling age and sex with appropriate values.\n\ntrain['sex'].fillna(train['sex'].mode()[0], inplace=True)\ntrain['age'].fillna(train['age'].median(), inplace=True)\nprint(\n    f'Train missing value count: {train.isnull().sum().sum()}\\nTest missing value count: {train.isnull().sum().sum()}'\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train['location'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have imputed the missing locations with a new label **'unknown'**","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Selecting images by Body Parts\n\n## 1. Head/Neck","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"I will be implementing this approach for 2 body parts. Following this, you can easily extend it to all the other parts","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"location='head/neck'    #Containing images with locations as 'Head/Neck'\ntrain_neck_head = train.loc[train['location']==location]\ntrain_neck_head.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\nAppending '.jpg' in front of each image name so that it can be used by our Train Generator\n'''\n\ndef append_ext(fn):\n    return fn+\".jpg\"\n\ntrain_neck_head[\"img_name\"]=train_neck_head[\"img_name\"].apply(append_ext)\ntrain_neck_head[\"target\"] = train_neck_head['target'].astype(\"str\")\ntrain_neck_head.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = ImageDataGenerator(width_shift_range=0.5,\n    height_shift_range=0.5, shear_range=0.5, zoom_range=0.5,\n    channel_shift_range=0.5, rescale=1/255, validation_split=0.25)\n\nimage_gen = aug.flow_from_dataframe(dataframe=train_neck_head, directory=train_img_path, x_col=\"img_name\",\ny_col=\"target\", subset=\"training\",\nbatch_size=BATCH_SIZE,\nseed=42,\nshuffle=True,\nclass_mode=\"binary\",\ntarget_size=(300,300))\n\nimage_gen.class_indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_head_neck = get_model(input_shape, 'imagenet')  #Can also use noisy-student weights\nmodel_head_neck.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Compile the Model\n'''\nUsing Focal loss due to imbalance\n'''\n\nmodel_head_neck.compile(\n        optimizer='adam',\n        loss = tfa.losses.SigmoidFocalCrossEntropy(reduction=tf.keras.losses.Reduction.AUTO),\n        metrics=['accuracy',tf.keras.metrics.AUC()]\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"filepath = 'EffNetB3-Head-Neck.h5'\nmc = tf.keras.callbacks.ModelCheckpoint(filepath=filepath , monitor='loss', save_weights_only=False, save_model=True, save_best_only=True)\n#lr_callback2 = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose = True)\n\ncallbacks = [mc, lr_schedule]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model_head_neck.fit(image_gen,\n          epochs=2,    #Increase the Number of Epochs\n          batch_size=BATCH_SIZE,          \n          verbose=1,\n          steps_per_epoch=math.ceil(len(train_neck_head)//BATCH_SIZE),\n          callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Lower Extremity","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"location='lower extremity'\ntrain_lower_ex = train.loc[train['location']==location]\nNUM_SAMPLES = len(train_lower_ex)\ntrain_lower_ex.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def append_ext(fn):\n    return fn+\".jpg\"\n\ntrain_lower_ex[\"img_name\"]=train_lower_ex[\"img_name\"].apply(append_ext)\ntrain_lower_ex[\"target\"] = train_lower_ex['target'].astype(\"str\")\ntrain_lower_ex.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = ImageDataGenerator(width_shift_range=0.5,\n    height_shift_range=0.5, shear_range=0.5, zoom_range=0.5,\n    channel_shift_range=0.5, rescale=1/255, validation_split=0.25)\n\nimage_gen = aug.flow_from_dataframe(dataframe=train_lower_ex, directory=train_img_path, x_col=\"img_name\",\ny_col=\"target\", subset=\"training\",\nbatch_size=BATCH_SIZE,\nseed=42,\nshuffle=True,\nclass_mode=\"binary\",\ntarget_size=(300,300))\n\nimage_gen.class_indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_lower_ex = get_model(input_shape, 'imagenet')  #Can also use noisy-student weights\nmodel_lower_ex.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Compile the Model\nmodel_lower_ex.compile(\n        optimizer='adam',\n        loss = tfa.losses.SigmoidFocalCrossEntropy(reduction=tf.keras.losses.Reduction.AUTO),\n        metrics=['accuracy',tf.keras.metrics.AUC()]\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"filepath = 'EffNetB3-Lower-extremity.h5'\nmc = tf.keras.callbacks.ModelCheckpoint(filepath=filepath , monitor='loss', save_weights_only=False, save_model=True, save_best_only=True)\nlr_callback2 = tf.keras.callbacks.LearningRateScheduler(lrfn2, verbose = True)\n\ncallbacks = [mc, lr_schedule]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model_lower_ex.fit(image_gen,\n          epochs=2,\n          batch_size=BATCH_SIZE,          \n          verbose=1,\n          steps_per_epoch=math.ceil(NUM_SAMPLES//BATCH_SIZE),\n          callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"If you continue to repeat this process for all the parts, we will get 7 different models for 7 different body locations. After these models have been trained to satisfaction, we can test these on the test set in a similar manner i.e. 'model_head_neck' for test images having location 'head/neck'","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Predictions","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import Image\n#from tensorflow.keras.preprocessing.image import img_to_array\n\ndef prepare_img(path, name, target_size):\n    try:\n        img = Image.open(path+name)\n    except:\n        return \"File not found\"\n        \n    img_arr = img_to_array(img)\n    img_arr = resize(img_arr, target_size)\n    img_arr = img_arr/255\n    img_arr = np.expand_dims(img_arr, axis=0)\n    \n    return img_arr","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1. Head/Neck Test Images","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"location='head/neck'\ntest_head_neck = test.loc[test['location']==location]\nprint(len(test_head_neck))\ntest_head_neck.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Same Procedure\n\ndef append_ext(fn):\n    return fn+\".jpg\"\n\ntest_head_neck[\"img_name\"]=test_head_neck[\"img_name\"].apply(append_ext)\ntest_head_neck.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_head_neck = load_model('EffNetB3-Head-Neck.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_head=[]\nimg_name = []\nimport random\nfor img in list(test_head_neck['img_name']):\n    img_name.append(img.split('.')[0])\n    img = prepare_img(test_img_path, img, (512, 512))\n    preds_head.append(model_head_neck.predict(img)[0][0])\n    #preds_head.append(random.choice([0,1]))\n    \nlen(preds_head)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_preds_head = pd.DataFrame(list(zip(img_name, preds_head)), columns=['image_name', 'target'])\ndf_preds_head.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_preds_head.to_csv(sub_base_path+'preds-head-neck.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Lower Extremity","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# lower extremity\nlocation='lower extremity'\ntest_lower = test.loc[test['location']==location]\ntest_lower.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def append_ext(fn):\n    return fn+\".jpg\"\n\ntest_lower[\"img_name\"]=test_lower[\"img_name\"].apply(append_ext)\ntest_lower.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_lower = load_model('EffNetB3-Lower-extremity.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_lower=[]\nimg_name = []\nfor img in list(test_lower['img_name']):\n    img_name.append(img.split('.')[0])\n    img = prepare_img(test_img_path, img, (512, 512))\n    preds_lower.append(model_lower.predict(img)[0][0])\n    #preds_head.append(random.choice([0,1]))\n    \nlen(preds_lower)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_preds_lower = pd.DataFrame(list(zip(img_name, preds_lower)), columns=['image_name', 'target'])\ndf_preds_lower.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_preds_lower.to_csv(sub_base_path+'preds_lower.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Continuing in a similar fashion, we can get a Dataframe (and hence a CSV file) containing predictions of test images belonging to a particular location","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Combining all the Prediction","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"df_preds_combined = df_preds_upper.append([df_preds_unknown, df_preds_torso, df_preds_palms,\n                           df_preds_oral, df_preds_lower, df_preds_head], ignore_index=True)\nprint(len(df_preds_combined))\ndf_preds_combined.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del sample['target']\nsample = sample.merge(df_preds_combined, on='image_name')\nsample.to_csv('Submissions/individual1.csv', index=False)\nsample.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Thanks for reading this kernel. I would love to read your thoughts on this approach. \n\n### This is my first competition and also my first notebook on Kaggle. Please upvote if you found it useful. \n\nTHANKS :)\n","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}