{"cells":[{"metadata":{},"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/aEnbuWy.jpg\" width=\"800\">","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"**Melanoma** is a serious form of skin cancer that begins in cells known as melanocytes. While it is less common than **Basal cell carcinoma (BCC)** and **Squamous cell carcinoma (SCC)**, melanoma is more dangerous because of its ability to spread to other organs more rapidly if it is not treated at an early stage. The early diagnosis of melanoma can improve the prognosis and chance of survival significantly, as it can promote timely clinical treatment to patients. Further accurate classification of benign tumors can prevent patients from undergoing unnecessary treatments. Thus, the correct diagnosis of melanoma and classification of patients into malignant or benign groups is the subject of much research.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"**The goal** of this project is to classify whether the melanoma is **benign** or **malignant** ","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nimport warnings\nwarnings.filterwarnings('ignore')\nplt.style.use('ggplot')\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\nimport tensorflow as tf\nfrom tqdm.notebook import tqdm\nfrom kaggle_datasets import KaggleDatasets\nfrom collections import Counter\nfrom tensorflow.keras import layers as L\nimport sklearn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -q efficientnet","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# EDA and Visualization","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/train.csv')\ntest = pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/test.csv')\nsample_submission = pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Visualization of data is an imperative aspect of data science. It helps to understand data and also to explain it to another person. Python has several interesting visualization libraries such as Matplotlib, Seaborn etc","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting overall number of benign and malignant tumors\nplt.style.use('fivethirtyeight')\nsns.countplot(x='target', data=train, color='red')\nprint('Benign: {}%'.format(round(train.target.value_counts()[0]/len(train)*100.0,2)))\nprint('Malignant: {}%'.format(round(train.target.value_counts()[1]/len(train)*100.0,2)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In our example, we have images with 98.24% belonging to the \"yes\" class and 1.76% belonging to the \"no\" class. We encounter a problem known as data imbalance, where the number of observations per class is not equally distributed. There are many instances of data imbalance in medicine, since usually there would be very less number of unhealthy patients than the number of healthy patients in most cases ","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting distribution of ages by benign vs malignant tumors\nsns.kdeplot(train.loc[train['target'] == 0, 'age_approx'], label = 'Benign',shade=True, color='purple')\nsns.kdeplot(train.loc[train['target'] == 1, 'age_approx'], label = 'Malignant',shade=True, color='yellow')\nplt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting distribution of ages by male vs female persons\nsns.kdeplot(train.loc[train['sex'] == 'male', 'age_approx'], label = 'Male',shade=True, color='black')\nsns.kdeplot(train.loc[train['sex'] == 'female', 'age_approx'], label = 'Female',shade=True, color='cyan')\nplt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting number of tumors by affected body parts\nfig= plt.figure(figsize=(10,6))\nax = sns.countplot(x=\"anatom_site_general_challenge\", data=train, hue='sex', palette='seismic',\n                  order=train['anatom_site_general_challenge'].value_counts().index)\nplt.title(\"Affected Body Parts\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting a random image\nimg = plt.imread('/kaggle/input/siim-isic-melanoma-classification/jpeg/train/ISIC_5766923.jpg')\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting benign images\nw = 10\nh = 10\nfig = plt.figure(figsize=(15, 15))\ncolumns = 4\nrows = 4\n\n#ax enables access to manipulate each of subplots\nax = []\n\nfor i in range(columns*rows):\n    img = plt.imread('/kaggle/input/siim-isic-melanoma-classification/jpeg/train/'+train['image_name'][i]+'.jpg')\n    #creating a subplot and appending it to ax\n    ax.append( fig.add_subplot(rows, columns, i+1) )\n    #hiding grid lines\n    ax[-1].grid(False)\n\n    #hiding axes ticks\n    ax[-1].set_xticks([])\n    ax[-1].set_yticks([])\n    ax[-1].set_title(train['benign_malignant'][i])\n    plt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plotting malignant images\nw = 10\nh = 10\nfig = plt.figure(figsize=(15, 15))\ncolumns = 4\nrows = 4\n\nax = []\n\nfor i in range(columns*rows):\n    img = plt.imread('/kaggle/input/siim-isic-melanoma-classification/jpeg/train/'+train.loc[train['target'] == 1]['image_name'].values[i]+'.jpg')\n    ax.append( fig.add_subplot(rows, columns, i+1) )\n    ax[-1].grid(False)\n    ax[-1].set_xticks([])\n    ax[-1].set_yticks([])\n    ax[-1].set_title(train.loc[train['target'] == 1]['benign_malignant'].values[i])\n    plt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Checking for missing values in the train set\ntotal = train.isnull().sum().sort_values(ascending=False)\npercent = train.isnull().mean().sort_values(ascending=False)\nmissing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])\nmissing_data.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Checking for missing values in the test set\ntotal = test.isnull().sum().sort_values(ascending=False)\npercent = test.isnull().mean().sort_values(ascending=False)\nmissing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])\nmissing_data.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preparing Data for the Model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Imputing missing values\ntrain['sex'] = train['sex'].fillna('male')\ntrain['age_approx'] = train['age_approx'].fillna(train['age_approx'].mean())\ntrain['anatom_site_general_challenge'] = train['anatom_site_general_challenge'].fillna('head/neck')\ntest['anatom_site_general_challenge'] = test['anatom_site_general_challenge'].fillna('head/neck')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Transforming the values into categorical\nfrom sklearn.preprocessing import LabelEncoder\nenc1 = LabelEncoder()\nenc2 = LabelEncoder()\n\ntrain['sex'] = enc1.fit_transform(train['sex'])\ntrain['anatom_site_general_challenge'] = enc2.fit_transform(train['anatom_site_general_challenge'])\ntest['anatom_site_general_challenge'] = enc2.fit_transform(test['anatom_site_general_challenge'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Detecting TPU\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Running on TPU ', tpu.master())\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy()\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#For tf.dataset\nAUTO = tf.data.experimental.AUTOTUNE\n\n#Accessing the data\nGCS_PATH = KaggleDatasets().get_gcs_path('siim-isic-melanoma-classification')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAINING_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/tfrecords/train*.tfrec')\nTEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/tfrecords/test*.tfrec')\n\nCLASSES = [0,1]   \nIMAGE_SIZE = [1024, 1024]\nBATCH_SIZE = 8 * strategy.num_replicas_in_sync","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import re\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0  #converting image to floats in [0, 1] range\n    image = tf.reshape(image, [*IMAGE_SIZE, 3]) #explicit size needed for TPU\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), #tf.string means bytestring\n       \n        \"target\": tf.io.FixedLenFeature([], tf.int64),  #shape [] means single element\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    label = tf.cast(example['target'], tf.int32)\n    \n    return image, label #returns a dataset of (image, label) pairs\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['image_name']\n    return image, idnum #returns a dataset of image(s)\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    #Reading from TFRecords. For optimal performance, reading from multiple files at once and\n    #disregarding data order. Order does not matter since we will be shuffling the data anyway.\n\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False #disabling order, increasing speed\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO) #automatically interleaving reads from multiple files\n    dataset = dataset.with_options(ignore_order) #using data as soon as it streams in, rather than in its original order\n    dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    #returns a dataset of (image, label) pairs if labeled=True or (image, id) pairs if labeled=False\n    return dataset\n\ndef data_augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_brightness(image, 0.1)\n    image = tf.image.random_flip_up_down(image)\n    #image = tf.image.random_saturation(image, 0, 2)\n    return image, label   \ndef get_training_dataset():\n    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n    dataset = dataset.map(data_augment, num_parallel_calls=AUTO)\n    dataset = dataset.repeat() #the training dataset must repeat for several epochs\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO) #prefetching next batch while training (autotune prefetch buffer size)\n    return dataset\n\ndef get_validation_dataset(ordered=False):\n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nNUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)\nNUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\nSTEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE\nprint('Dataset: {} training images and {} unlabeled test images'.format(NUM_TRAINING_IMAGES,NUM_TEST_IMAGES))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we're performing **Data Augmentation** to solve the problem of data imbalance, which we detected in our analysis. **Data Augmentation** creates modified versions of the images in our dataset. It allows us to add images to the dataset without us having to collect new ones by taking a particular image and performing various sorts of image enhancements such as rotate, mirror and flip","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Model","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"We are going to use **Convolutional Neural Network (CNN)**. **CNN** is a class of Deep Learning, mostly applied to analyze visual images. There is a variety of convolutional neural networks and all have their own advantage","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/cakzqc6r.jpg\" width=\"800\">","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"In this kernel we will implement **EfficientNetB7** - a framework that was released by Google AI in 2019, which achieves much better accuracy and efficiency as compared to the previous Convolutional Neural Networks","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#Defining the parameters\nEPOCHS = 4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_lrfn(lr_start=0.00001, lr_max=0.0001, \n               lr_min=0.000001, lr_rampup_epochs=20, \n               lr_sustain_epochs=0, lr_exp_decay=.8):\n    lr_max = lr_max * strategy.num_replicas_in_sync\n\n    def lrfn(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\n    \n    return lrfn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.layers import Dense\nimport efficientnet.tfkeras as efn\n#Defining the model\nwith strategy.scope():\n    efficientnetb7_model = tf.keras.Sequential([\n        efn.EfficientNetB7(\n            input_shape=(*IMAGE_SIZE, 3),\n            #weights='imagenet',\n            weights='imagenet',\n            include_top=False\n        ),\n        L.GlobalAveragePooling2D(),\n        L.Dense(1024, activation = 'relu'), \n        L.Dropout(0.3), \n        L.Dense(512, activation= 'relu'), \n        L.Dropout(0.2), \n        L.Dense(256, activation='relu'), \n        L.Dropout(0.2), \n        L.Dense(128, activation='relu'), \n        L.Dropout(0.1), \n        L.Dense(1, activation='sigmoid')\n    ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras import backend as K\n\ndef focal_loss(gamma=2., alpha=.25):\n\tdef focal_loss_fixed(y_true, y_pred):\n\t\tpt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))\n\t\tpt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))\n\t\treturn -K.mean(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1)) - K.mean((1 - alpha) * K.pow(pt_0, gamma) * K.log(1. - pt_0))\n\treturn focal_loss_fixed","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Compiling the model\nefficientnetb7_model.compile(\n    optimizer='Adam',\n    loss = focal_loss(gamma=2., alpha=.25),\n    #loss = tf.keras.losses.BinaryCrossentropy(label_smoothing = 0.1),\n    metrics=['binary_crossentropy', 'accuracy']\n)\nefficientnetb7_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"lrfn = build_lrfn()\nlr_schedule = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#model.load_weights('../input/melanoma/model_weights.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Training the model\nhistory = efficientnetb7_model.fit(\n    get_training_dataset(), \n    epochs=EPOCHS, \n    steps_per_epoch=STEPS_PER_EPOCH,\n    callbacks=[lr_schedule],\n    class_weight = {0:0.50899675,1: 28.28782609}\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist = pd.DataFrame(history.history)\nhist['epoch'] = history.epoch\nhist.tail()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Visualizing model loss\nplt.plot(history.history['loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Visualizing model crossentropy\nplt.plot(history.history['binary_crossentropy'])\nplt.title('model crossentropy')\nplt.ylabel('crossentropy')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"efficientnetb7_model.save('complete_data_efficient_model.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"efficientnetb7_model.save_weights('complete_data_efficient_weights.h5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Predictions","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"test_ds = get_test_dataset(ordered=True)\ntest_images_ds = test_ds.map(lambda image, idnum: image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"probabilities = efficientnetb7_model.predict(test_images_ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Generating submission.csv file...')\ntest_ids_ds = test_ds.map(lambda image, idnum: idnum).unbatch()\ntest_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES))).numpy().astype('U') #all in one batch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred_df = pd.DataFrame({'image_name': test_ids, 'target': np.concatenate(probabilities)})\npred_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = pd.read_csv(\"/kaggle/input/siim-isic-melanoma-classification/sample_submission.csv\")\nsub","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del sub['target']\nsub = sub.merge(pred_df, on='image_name')\n#sub.to_csv('submission_label_smoothing.csv', index=False)\nsub.to_csv('complete_data.csv', index=False)\nsub.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**References:** [https://www.kaggle.com/shubhamai/melanoma-classification](http://) ","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}