{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h3 class=\"list-group-item list-group-item-action active\" data-toggle=\"list\"  role=\"tab\" aria-controls=\"panel\" style=\"background-color:#5EAFF2; color:#FFE100\">Table of Contents</h3>\n<ul class=\"nav flex-column\">\n  <li class=\"nav-item\">\n    <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#Introduction\"style=\"color:#ff4f00\"><b>Introduction</b><span class=\"badge badge-primary badge-pill\">1</span></a>\n  </li>\n  <li class=\"nav-item\">\n    <a class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#task\"style=\"color:#ff4f00\"><b>Task</b><span class=\"badge badge-primary badge-pill\">2</span></a>\n  </li>\n  <li class=\"nav-item\">\n    <a class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#libraries\"style=\"color:#ff4f00\"><b>Importing Libraries</b><span class=\"badge badge-primary badge-pill\">3</span></a></a>\n  </li>\n  <li class=\"nav-item\">\n    <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#eda\"style=\"color:#ff4f00\"><b>EDA</b><span class=\"badge badge-primary badge-pill\">4</span></a>\n  </li>\n   <li class=\"nav-item\">\n    <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#preprop\"style=\"color:#ff4f00\"><b>Data preprocessing and Augmentation</b><span class=\"badge badge-primary badge-pill\">5</span></a>\n  </li>\n    <li class=\"nav-item\">\n    <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#model\"style=\"color:#ff4f00\"><b>Model</b><span class=\"badge badge-primary badge-pill\">6</span></a>\n  </li>\n  <li class=\"nav-item\">\n    <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#Train\"style=\"color:#ff4f00\"><b>Train</b><span class=\"badge badge-primary badge-pill\">7</span></a>\n  </li>\n   <a  class=\"list-group-item list-group-item-action\" data-toggle=\"list\" href=\"#inference\"style=\"color:#ff4f00\"><b>Inference</b><span class=\"badge badge-primary badge-pill\">8</span></a>\n  </li>\n</ul>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# <a id='Introduction'>Introduction📜</a>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"![](http://www.brandpointcontent.com/InfoGraphics/8085100101.jpg)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"1. Melanoma is a type of skin cancer developed from mutation of pigmant producing cells called melanocytes.\n2. It is mainly caused due to exposure to UV Light with low levels of skin melanin.\n3. It is primarily diagonised by biopsy \n4. For further details you can read from the below mentioned sources","execution_count":null},{"metadata":{},"cell_type":"markdown","source":" <button type=\"button\" class=\"btn btn-success\"><a href=\"https://en.wikipedia.org/wiki/Melanoma\"><p style=\"color:yellow\"><b>Wikipedia</b></p></a> <span class=\"badge\">1</span></button>\n<button type=\"button\" class=\"btn btn-warning\"><a href=\"https://www.mayoclinic.org/diseases-conditions/melanoma/symptoms-causes/syc-20374884\"><p style=\"color:purple\"><b>Mayo Clinic</b></p></a> <span class=\"badge\">2</span></button>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Overall it is a pretty interesting problem to solve using deep learning algorithms, and moreover developments in this field can see to a reduction in the testing prices and time.If you want to know further about this type cancer, I have linked below an informative video","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from IPython.display import HTML\nHTML('<center><iframe width=\"700\" height=\"400\" src=\"https://www.youtube.com/embed/mkYBxfKDyv0?rel=0&amp;controls=0&amp;showinfo=0\" frameborder=\"0\" allowfullscreen></iframe></center>')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='task'>Task🔎</a>\n<p>In this competition the participants are given csv files which contain the image IDs and further data about the patient such as diagnosis,condition and body part where the symptoms have manifested, also there is a folder which contains images with corresponding labels(IDs).The aim is to build a model and train it using the aforementioned data and run the inference on the test data and predict whether the cancer is malignant or benign in form of Target Ids.</p>","execution_count":null},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"pip install --upgrade efficientnet-pytorch\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"!pip install efficientnet_pytorch torchtoolbox","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='libraries'>Importing the Libraries📙📗</a>\n* <p>importing the libraries required for the task </p>","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os \nimport gc\nimport re\nimport time\nimport datetime\n\n\nimport math\nimport random\n\n#Data importing libraries\nimport cv2\nfrom scipy import ndimage\nimport numpy as np\nimport pandas as pd\nfrom kaggle_datasets import KaggleDatasets\nimport missingno as msno\n\n\n#Ploting libraries\n\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.offline import iplot\nfrom plotly.subplots import make_subplots\nfrom colorama import Fore, Back, Style\n\n#Data Preprocessing Libraries\nfrom tqdm import tqdm\ntqdm.pandas()\nfrom sklearn import metrics\nfrom sklearn.utils import shuffle\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.model_selection import StratifiedKFold, GroupKFold\nfrom sklearn.metrics import accuracy_score, roc_auc_score\nfrom sklearn import preprocessing\n\n# PyTorch\nimport torch\nimport torchvision\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import FloatTensor, LongTensor\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nimport torchtoolbox.transform as transforms\n\n# Data Augmentation for Image Preprocessing\n\n\nfrom efficientnet_pytorch import EfficientNet\n\n\n\n\n\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Loading Data**","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"IMAGE_PATH = \"../input/siim-isic-melanoma-classification/jpeg/\"\nTEST_PATH = \"../input/siim-isic-melanoma-classification/test.csv\"\nTRAIN_PATH = \"../input/siim-isic-melanoma-classification/train.csv\"\nSUB_PATH = \"../input/siim-isic-melanoma-classification/sample_submission.csv\"\n\n\nsub = pd.read_csv(SUB_PATH)\ntest_df = pd.read_csv(TEST_PATH)\ntrain_df = pd.read_csv(TRAIN_PATH)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Unique Values in the training Dataframe**","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print(Fore.MAGENTA +\"Sex:\",Style.RESET_ALL,train_df[\"sex\"].unique())\nprint(Fore.GREEN +\"-----------------------\",Style.RESET_ALL)\nprint(Fore.CYAN +\"Anatomy Site:\",Style.RESET_ALL,train_df[\"anatom_site_general_challenge\"].unique())\nprint(Fore.GREEN +\"-----------------------\",Style.RESET_ALL)\nprint(Fore.YELLOW +\"Target:\",Style.RESET_ALL,train_df[\"target\"].unique())\nprint(Fore.GREEN +\"-----------------------\",Style.RESET_ALL)\nprint(Fore.BLUE +\"Diagnosis:\",Style.RESET_ALL,train_df[\"diagnosis\"].unique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Loading Images**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_image(img_name,df=\"train\"):\n    \n    file_path = img_name+\".jpg\"\n    image=IMAGE_PATH+\"train/\"+file_path\n    \n    image_disp = plt.imread(image)\n    return image_disp\ntrain_imgs = train_df[\"image_name\"][:100].progress_apply(load_image)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='eda'>EDA🧪</a>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<img src=\"https://media.giphy.com/media/NS7gPxeumewkWDOIxi/giphy.gif\">","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"EDA will give me an insight into the given data and help me in processing the data and and as wellas help in finding an approach to solve the problem.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<p>I am dividing the EDA into two components:\n    <li><a href=\"#edaimg\">Exploratory data analysis of images </a></li>\n    <li><a href=\"#edatext\">Exploratory data analysis of CSV file</a></li>\n   ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":" **<a id='edaimg' style=\"background-color:#6610f2; color:#ffc107\"><h3>1.EDA of images</h3></a>**","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<b>analysing the color channel distribution on the training images any anomaly in this distribution can help us determining which distribution will influence the model</b>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"red_values = [np.mean(train_imgs[idx][:, :, 0]) for idx in range(len(train_imgs))]\ngreen_values = [np.mean(train_imgs[idx][:, :, 1]) for idx in range(len(train_imgs))]\nblue_values = [np.mean(train_imgs[idx][:, :, 2]) for idx in range(len(train_imgs))]\nvalues = [np.mean(train_imgs[idx]) for idx in range(len(train_imgs))]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It can be observed that although this is does not look like a normal distribution but the distribution is pretty uniform","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([values], group_labels=[\"Channels\"], colors=[\"purple\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([red_values], group_labels=[\"R\"], colors=[\"red\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of red channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([green_values], group_labels=[\"G\"], colors=[\"green\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of green channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The uniform Distribution is more evident in Blue channel than other channels","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([blue_values], group_labels=[\"B\"], colors=[\"blue\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of blue channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = go.Figure()\n\nfor idx, values in enumerate([red_values, green_values, blue_values]):\n    if idx == 0:\n        color = \"Red\"\n    if idx == 1:\n        color = \"Green\"\n    if idx == 2:\n        color = \"Blue\"\n    fig.add_trace(go.Box(x=[color]*len(values), y=values, name=color, marker=dict(color=color.lower())))\n    \nfig.update_layout(yaxis_title=\"Mean value\", xaxis_title=\"Color channel\",\n                  title=\"Mean value vs. Color channel\", template=\"plotly_white\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del train_imgs","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"malignant_df=pd.DataFrame(data=train_df[train_df[\"target\"]==1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"benign_df=pd.DataFrame(data=train_df[train_df[\"target\"]==0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_images(df):\n    df_name=df[\"image_name\"].values\n    df_imgs = [np.random.choice(df_name+'.jpg') for i in range(9)]\n    img_dir = IMAGE_PATH+'train'\n    return df_imgs,img_dir","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def disp_imgs(df_imgs,img_dir):\n    plt.figure(figsize=(10,8))\n    for i in range(9):\n        plt.subplot(6, 3, i + 1)\n        img = plt.imread(os.path.join(img_dir, df_imgs[i]))\n        plt.imshow(img, cmap='gray')\n        plt.axis('off')\n    return plt.tight_layout()   \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"maldf_imgs,malimg_dir=get_images(malignant_df)\nbedf_imgs,bedimg_dir=get_images(benign_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"disp_imgs(maldf_imgs,malimg_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"disp_imgs(bedf_imgs,bedimg_dir)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h2>Edge Detection<h2>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Edge detection is a major application for convolution.\nWhat is an edge:\n <ul><li>A location in the image where is a sudden change in the\nintensity/colour of pixels.</li>\n    <li> A transition between objects or object and background.</li>\n    <li> From a human visual perception perspective it attracts attention</li></ul>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<p>for further insight into edge detection go through the source given below</p>\n<button type=\"button\" class=\"btn btn-info\"><a href=\"https://www.cs.auckland.ac.nz/compsci373s1c/PatricesLectures/Edge%20detection-Sobel_2up.pdf\"><p style=\"color:purple\"><b>Edge detection</b></p></a> <span class=\"badge\"></span></button>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<h3><p>1.Sobel filter</p></h3>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<p>The Sobel operator, sometimes called the Sobel–Feldman operator or Sobel filter, is used in image processing and computer vision, particularly within edge detection algorithms where it creates an image emphasising edges.</p>\n<p>\nThe operator uses two 3×3 kernels which are convolved with the original image to calculate approximations of the derivatives – one for horizontal changes, and one for vertical. If we define A as the source image, and Gx and Gy are two images which at each point contain the vertical and horizontal derivative approximations respectively, the computations are as follows:</p>\n\n![](http://wikimedia.org/api/rest_v1/media/math/render/svg/848abd56e0e33cf402f01183bfe1f68a93fb34a9)\n\n<p>where * here denotes the 2-dimensional signal processing convolution operation.\n\nSince the Sobel kernels can be decomposed as the products of an averaging and a differentiation kernel, they compute the gradient with smoothing. For example, Gx can be written as</p>\n\n![](http://wikimedia.org/api/rest_v1/media/math/render/svg/24ed0b82d39c9544ffc4a20fb2d3a9664c13224d)\n\n<p>The x-coordinate is defined here as increasing in the \"right\"-direction, and the y-coordinate is defined as increasing in the \"down\"-direction. At each point in the image, the resulting gradient approximations can be combined to give the gradient magnitude, using:</p>\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/23ae6772c5f58751fc6014b71d6adafb30a31c79)\n\n<p>Using this information, we can also calculate the gradient's direction:</p>\n\n![](http://wikimedia.org/api/rest_v1/media/math/render/svg/17d387c370ca3a77740f6e6c889d286e586a640e)\n\nwhere, for example, Θ is 0 for a vertical edge which is lighter on the right side.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def sobel_filter(df_imgs,img_dir):\n    plt.figure(figsize=(10,8))\n    for i in range(9):\n        \n        plt.subplot(6, 3, i + 1)\n        img = plt.imread(os.path.join(img_dir, df_imgs[i]))\n        \n        img =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n         \n        img = cv2.GaussianBlur(img,(99,99),0)\n        sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)\n        sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)\n        theta = np.sqrt((sobely**2)+(sobelx**2))\n        plt.imshow(theta,cmap='gray')\n        plt.axis('off')\n    return plt.tight_layout()\n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Malignant**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"sobel_filter(maldf_imgs,malimg_dir)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Benign**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"sobel_filter(bedf_imgs,bedimg_dir)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<p>This process generated a two dimensional binary map indicating the location of edges although the edges obtained through this process are not fine.To overcome this issue <b>Canny filter</b> can be used</p>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<h3><p>2.Canny filter</p></h3>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"The Process of Canny edge detection algorithm can be broken down to 5 different steps:\n\n<li>Apply Gaussian filter to smooth the image in order to remove the noise</li>\n<li>Find the intensity gradients of the image</li>\n<li>Apply non-maximum suppression to get rid of spurious response to edge detection</li>\n<li>Apply double threshold to determine potential edges</li>\n<li>Track edge by hysteresis: Finalize the detection of edges by suppressing all the other edges that are weak and not connected to strong edges.</li>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def canny_filter(df_imgs,img_dir,sigma=0.99):\n    plt.figure(figsize=(10,8))\n    for i in range(9):\n        \n        plt.subplot(6, 3, i + 1)\n        img = plt.imread(os.path.join(img_dir, df_imgs[i]))\n        img =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n        v=np.median(img)\n        lower = int(max(0, (1.0 - sigma) * v))\n        upper = int(min(255, (1.0 + sigma) * v))\n        \n        img = cv2.Canny(img,lower,upper)\n        plt.imshow(img,cmap='gray')\n        plt.axis('off')\n    return plt.tight_layout()\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"canny_filter(maldf_imgs,malimg_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"canny_filter(bedf_imgs,bedimg_dir)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"After going through the images generated after applying Canny filter to the original much more finer edges can be seen but presence of hair in certain samples is hindering the actual edges.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"del maldf_imgs,malimg_dir,bedf_imgs,bedimg_dir,benign_df,malignant_df","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**<a id='edatext' style=\"background-color:#6610f2; color:#ffc107\"><h3>2.EDA of CSV file</h3></a>**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"cmap = sns.diverging_palette(220, 20, sep=20, as_cmap=True)\nax = sns.heatmap(train_df.corr(), annot=True,cmap=cmap)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f, (ax1, ax2) = plt.subplots(1, 2, figsize = (16, 5))\n\nmsno.matrix(train_df, ax = ax1, color=(189/255, 240/255, 250/255), fontsize=10)\nmsno.matrix(test_df, ax = ax2, color=(238/255, 189/255, 250/255), fontsize=10)\n\nax1.set_title('Train Missing Values Map', fontsize = 13)\nax2.set_title('Test Missing Values Map', fontsize = 13);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = sns.countplot(x=train_df['sex'], data=train_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"anat=sns.countplot(x=train_df['anatom_site_general_challenge'],data = train_df,palette=sns.cubehelix_palette(8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([train_df.loc[train_df['target'] == 1,'age_approx'].dropna()], group_labels=[\"Age\"], colors=[\"magenta\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of age for malignant cases\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 1\nfig","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([train_df.loc[train_df['target'] == 0,'age_approx'].dropna()], group_labels=[\"Age\"], colors=[\"orange\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of age form benign cases\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 1\nfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='preprop'>Data Preprocessing and Augmentation📋</a>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"**setting seeds initially for consistency of the model**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_everything(seed_value):\n    random.seed(seed_value)\n    np.random.seed(seed_value)\n    torch.manual_seed(seed_value)\n    os.environ['PYTHONHASHSEED'] = str(seed_value)\n    \n    if torch.cuda.is_available(): \n        torch.cuda.manual_seed(seed_value)\n        torch.cuda.manual_seed_all(seed_value)\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = True\n\nseed = 1234\nseed_everything(seed)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div class=\"alert alert-info\" role=\"alert\"><b><p class=\"lead\">Here I got a cleaned data set with resolution of the images reduced to 256x256, although this scales down the resolution from original and reduces data present in the image but at the same time most of the features are preserved this reduced the memory requirement and speeds up the training process.</p> </b></div>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df=pd.read_csv('../input/melanoma-external-malignant-256/train_concat.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"***encoding 'sex' values to 0s and 1s***","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['sex'] = train_df['sex'].map({'male': 1, 'female': 0})\ntest_df['sex'] = test_df['sex'].map({'male': 1, 'female': 0})\ntrain_df['sex'] = train_df['sex'].fillna(-1)\ntest_df['sex'] = test_df['sex'].fillna(-1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"***imputing missing age values to the average age value of the data***","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"imp_mean=(train_df[\"age_approx\"].sum())/(train_df[\"age_approx\"].count()-train_df[\"age_approx\"].isna().sum())\ntrain_df['age_approx']=train_df['age_approx'].fillna(imp_mean)\ntrain_df['age_approx'].head()\nimp_mean_test=(test_df[\"age_approx\"].sum())/(test_df[\"age_approx\"].count())\ntest_df['age_approx']=test_df['age_approx'].fillna(imp_mean_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['patient_id'] = train_df['patient_id'].fillna(0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"***Generating dummy columns for anatomy site general values and onehot encoding them***","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"concat = pd.concat([train_df['anatom_site_general_challenge'], test_df['anatom_site_general_challenge']], ignore_index=True)\ndummies = pd.get_dummies(concat, dummy_na=True, dtype=np.uint8, prefix='site')\ntrain_df = pd.concat([train_df, dummies.iloc[:train_df.shape[0]]], axis=1)\ntest_df = pd.concat([test_df, dummies.iloc[train_df.shape[0]:].reset_index(drop=True)], axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"***Extracting meta features from the table***","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"meta_features = ['sex', 'age_approx'] + [col for col in train_df.columns if 'site_' in col]\nmeta_features.remove('anatom_site_general_challenge')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df=test_df.drop([\"anatom_site_general_challenge\"],axis=1)\ntrain_df=train_df.drop([\"anatom_site_general_challenge\"],axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(Fore.YELLOW,meta_features)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3>Hair Augmentation</h3>\n<img src=\"https://media.giphy.com/media/p2kIfDxpVQ6dO/giphy.gif\">\n","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<div class=\"alert alert-info\" role=\"alert\"><b><p class=\"lead\">During the EDA I came to realise that the presence of hair in the images can effect the features extracted by the model and this might cause drop in accuracy of the model so I started to do little bit of research online to find the solution to this problem and then I came across this notebook which used a concept called hair augmentation on the images.</p> </b></div>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<button type=\"button\" class=\"btn btn-success\"><a href=\"https://www.kaggle.com/nroman/melanoma-pytorch-starter-efficientnet\"><p style=\"color:yellow\"><b>Roman's Notebook</b></p></a> <span class=\"badge\">1</span></button>\n<button type=\"button\" class=\"btn btn-warning\"><a href=\"https://arxiv.org/pdf/1809.02568.pdf\"><p style=\"color:blue\"><b>Paper</b></p></a> <span class=\"badge\">2</span></button>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"\n     \nclass HairGrowth:\n    \n\n\n    def __init__(self, hairs , hairs_folder):\n        self.hairs = hairs\n        self.hairs_folder = hairs_folder\n\n    def __call__(self, img):\n    \n        \n        n_hairs = random.randint(0, self.hairs)\n        \n        if not n_hairs:\n            return img\n        \n        height, width, _ = img.shape  # target image width and height\n        hair_images = [im for im in os.listdir(self.hairs_folder) if 'png' in im]\n        \n        for _ in range(n_hairs):\n            hair = cv2.imread(os.path.join(self.hairs_folder, random.choice(hair_images)))\n            hair = cv2.flip(hair, random.choice([-1, 0, 1]))\n            hair = cv2.rotate(hair, random.choice([0, 1, 2]))\n\n            h_height, h_width, _ = hair.shape  # hair image width and height\n            roi_ho = random.randint(0, img.shape[0] - hair.shape[0])\n            roi_wo = random.randint(0, img.shape[1] - hair.shape[1])\n            roi = img[roi_ho:roi_ho + h_height, roi_wo:roi_wo + h_width]\n\n            # Creating a mask and inverse mask\n            img2gray = cv2.cvtColor(hair, cv2.COLOR_BGR2GRAY)\n            ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)\n            mask_inv = cv2.bitwise_not(mask)\n\n            # Now black-out the area of hair in ROI\n            img_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)\n\n            # Take only region of hair from hair image.\n            hair_fg = cv2.bitwise_and(hair, hair, mask=mask)\n\n            # Put hair in ROI and modify the target image\n            dst = cv2.add(img_bg, hair_fg)\n\n            img[roi_ho:roi_ho + h_height, roi_wo:roi_wo + h_width] = dst\n                \n        return img\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}(hairs={self.hairs}, hairs_folder=\"{self.hairs_folder}\")'\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3><b>Image Transforms</b></h3>\n<img src=\"http://i.imgur.com/fNQ9fua.jpg\"  style=\"width:720px;height:418px;\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_transform = transforms.Compose([\n    HairGrowth(hairs = 5,hairs_folder='/kaggle/input/melanoma-hairs/'),\n    transforms.RandomResizedCrop(size=256, scale=(0.7, 1.0)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.ColorJitter(brightness=32. / 255.,saturation=0.5,hue=0.01),\n    \n\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])\n])\ntest_transform = transforms.Compose([HairGrowth(hairs = 5,hairs_folder='/kaggle/input/melanoma-hairs/'),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])\n])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class MelanomaDataset(Dataset):\n    def __init__(self, df: pd.DataFrame, imfolder: str, train: bool = True, transforms = None, meta_features = None):\n        \n        self.df = df\n        self.imfolder = imfolder\n        self.transforms = transforms\n        self.train = train\n        self.meta_features = meta_features\n        \n    def __getitem__(self, index):\n        im_path = os.path.join(self.imfolder, self.df.iloc[index]['image_name'] + '.jpg')\n        image = cv2.imread(im_path)\n        metadata = np.array(self.df.iloc[index][self.meta_features].values, dtype=np.float32)\n\n        if self.transforms:\n            image = self.transforms(image)\n            \n        if self.train:\n            y = self.df.iloc[index]['target']\n            image = image.cuda()\n            return (image, metadata), y\n        else:\n            return (image, metadata)\n    \n    def __len__(self):\n        return len(self.df)\n    \n    \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3>Splitting the Data</h3>\n<img src=\"https://media.giphy.com/media/b5Hcaz7EPz26I/giphy.gif\">\n    ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<b><p style=\"text-align:left;\">In this section I will be intialize k-fold to spliting the data into 5 parts for cross validation purposes.</p></b>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<h4><b>•Group K-Fold</b></h4>\n<img src=\"https://i.imgur.com/awJeDOr.jpg\"  style=\"width:480px;height:383px;\">\n<br> Group k-fold is a type of k-fold algorithm with non overlapping groups i.e. same group will not appear twice in two different folds</br>\n<br> In this case We are taking `patient_id` as grouping column","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"skf = GroupKFold(n_splits=5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test = MelanomaDataset(df=test_df,\n                       imfolder='/kaggle/input/melanoma-external-malignant-256/test/test/', \n                       train=False,\n                       transforms=test_transform,\n                       meta_features=meta_features)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.multiprocessing import Pool, Process, set_start_method\ntry:\n     set_start_method('spawn')\nexcept RuntimeError:\n    pass","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='model'>Model🧠</a>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"![](http://i.imgur.com/TMLjo5p.jpg)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"**Models Parameters**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Config\n\nepochs = 10  # no of times till the loop will iterate over the model\nESpatience = 3 # no of times the model will wait if the loss is not decreased\nTTA = 3      # test time augmentation, random augmantation like mirror image performed on thhe input image \nnum_workers = 6 # tells DataLoader the number of subprocess to use while data loading\nlearning_rate = 0.001 # Learning Rate\nweight_decay = 0.0  # Decay Factor\nlr_patience = 1     # patience for learning rate      \nlr_factor = 0.4     \noutput_size=1    # statics\nbatch_size1 = 32\nbatch_size2 = 16\n\ntrain_len = len(train_df)\ntest_len = len(test_df)\noof = np.zeros(shape = (train_len, 1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3>Efficientnet b2</h3>\n<img src=\"https://raw.githubusercontent.com/tensorflow/tpu/master/models/official/efficientnet/g3doc/params.png\" style=\"width:480px;height:383px;\" >\n<img src=\"https://1.bp.blogspot.com/-DjZT_TLYZok/XO3BYqpxCJI/AAAAAAAAEKM/BvV53klXaTUuQHCkOXZZGywRMdU9v9T_wCLcBGAs/s640/image2.png\" >","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class EfficientNetwork(nn.Module):\n    def __init__(self, output_size, no_columns, b4=False, b2=False):\n        super().__init__()\n        self.b4, self.b2, self.no_columns = b4, b2, no_columns\n        \n        # Define Feature part (IMAGE)\n        if b4:\n            self.features = EfficientNet.from_pretrained('efficientnet-b4')\n        elif b2:\n            self.features = EfficientNet.from_pretrained('efficientnet-b2')\n        else:\n            self.features = EfficientNet.from_pretrained('efficientnet-b7')\n        \n        # (CSV) or Meta Features\n        self.csv = nn.Sequential(nn.Linear(self.no_columns, 250),\n                                 nn.BatchNorm1d(250),\n                                 nn.ReLU(),\n                                 nn.Dropout(p=0.3),\n                                 \n                                 nn.Linear(250, 250),\n                                 nn.BatchNorm1d(250),\n                                 nn.ReLU(),\n                                 nn.Dropout(p=0.3),\n                                 \n                                 nn.Linear(250, 250),\n                                 nn.BatchNorm1d(250),\n                                 nn.ReLU(),\n                                 nn.Dropout(p=0.3))\n        \n        # Define Classification part\n        if b4:\n            self.classification = nn.Sequential(nn.Linear(1792 + 250, 250),\n                                                nn.Linear(250, output_size))\n        elif b2:\n            self.classification = nn.Sequential(nn.Linear(1408 + 250, 250),\n                                                nn.Linear(250, output_size))\n        else:\n            self.classification = nn.Sequential(nn.Linear(2560 + 250, 250),\n                                                nn.Linear(250, output_size))\n        \n        \n    def forward(self, image, csv_data, prints=False):    \n        \n        if prints: print('Input Image shape:', image.shape, '\\n'+\n                         'Input csv_data shape:', csv_data.shape)\n        \n        # IMAGE CNN\n        image = self.features.extract_features(image)\n        if prints: print('Features Image shape:', image.shape)\n            \n        if self.b4:\n            image = F.avg_pool2d(image, image.size()[2:]).reshape(-1, 1792)\n        elif self.b2:\n            image = F.avg_pool2d(image, image.size()[2:]).reshape(-1, 1408)\n        else:\n            image = F.avg_pool2d(image, image.size()[2:]).reshape(-1, 2560)\n        if prints: print('Image Reshaped shape:', image.shape)\n            \n        # CSV FNN\n        csv_data = self.csv(csv_data)\n        if prints: print('CSV Data:', csv_data.shape)\n            \n        # Concatenate\n        image_csv_data = torch.cat((image, csv_data), dim=1)\n        \n        # CLASSIF\n        out = self.classification(image_csv_data)\n        if prints: print('Out shape:', out.shape)\n        \n        return out","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='Train'>Train🚴‍</a>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/GN9NB9J.jpg\"  style=\"width:480px;height:442px;\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#comment out in you don't want to Train\n\nfor fold, (train_idx, val_idx) in enumerate(skf.split(X=np.zeros(len(train_df)), y=train_df['target'], groups=train_df['patient_id'].tolist()), 1):\n    print(Fore.CYAN,'-' * 20,Style.RESET_ALL,Fore.MAGENTA, 'Fold', fold,Style.RESET_ALL,Fore.CYAN, '-' * 20,Style.RESET_ALL)\n    best_val = None\n    patience=ESpatience# Best validation score within this fold\n    model_path = 'model{Fold}.pth'.format(Fold=fold)  \n    train = MelanomaDataset(df=train_df.iloc[train_idx].reset_index(drop=True), \n                            imfolder='/kaggle/input/melanoma-external-malignant-256/train/train/', \n                            train=True, \n                            transforms=train_transform,\n                            meta_features=meta_features)\n    val = MelanomaDataset(df=train_df.iloc[val_idx].reset_index(drop=True), \n                            imfolder='/kaggle/input/melanoma-external-malignant-256/train/train/', \n                            train=True, \n                            transforms=test_transform,\n                            meta_features=meta_features)\n    train_loader = DataLoader(dataset=train, batch_size=batch_size1, shuffle=True, num_workers=0)\n    val_loader = DataLoader(dataset=val, batch_size=batch_size2, shuffle=False, num_workers=0)\n    test_loader = DataLoader(dataset=test, batch_size=batch_size2, shuffle=False, num_workers=0)\n    \n    model = EfficientNetwork(output_size=output_size, no_columns=len(meta_features),b2=True)\n    model = model.to(device)\n    \n    optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate, weight_decay=weight_decay)\n    scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='max', \n                                      patience=lr_patience, verbose=True, factor=lr_factor)\n    for epoch in range(epochs):\n        start_time = time.time()\n        correct = 0\n        train_losses = 0\n\n        model.train() #Set the model in train mode\n        \n        for data, labels in train_loader:\n                # Save them to device\n                data[0] = torch.tensor(data[0], device=device, dtype=torch.float32)\n                data[1] = torch.tensor(data[1], device=device, dtype=torch.float32)\n                labels = torch.tensor(labels, device=device, dtype=torch.float32)\n                \n                criterion = nn.BCEWithLogitsLoss()\n\n                # Clear gradients first; very important, usually done BEFORE prediction\n                optimizer.zero_grad()\n\n                # Log Probabilities & Backpropagation\n                out = model(data[0], data[1])\n                loss = criterion(out, labels.unsqueeze(1))\n                loss.backward()\n                optimizer.step()\n\n                # --- Save information after this batch ---\n                # Save loss\n                # From log probabilities to actual probabilities\n                 # 0 and 1\n                train_preds = torch.round(torch.sigmoid(out))\n                train_losses += loss.item()\n                \n                # Number of correct predictions\n                correct += (train_preds.cpu() == labels.cpu().unsqueeze(1)).sum().item()\n\n            # Compute Train Accuracy\n        train_acc = correct / len(train_idx)\n        model.eval()  # switch model to the evaluation mode\n        val_preds = torch.zeros((len(val_idx), 1), dtype=torch.float32, device=device)\n        with torch.no_grad():  # Do not calculate gradient since we are only predicting\n            \n            for j,(data_val, label_val) in enumerate(val_loader):\n                data_val[0] = torch.tensor(data_val[0], device=device, dtype=torch.float32)\n                data_val[1] = torch.tensor(data_val[1], device=device, dtype=torch.float32)\n                label_val = torch.tensor(label_val, device=device, dtype=torch.float32)\n                z_val = model(data_val[0],data_val[1])\n                val_pred = torch.sigmoid(z_val)\n                val_preds[j*data_val[0].shape[0]:j*data_val[0].shape[0] + data_val[0].shape[0]] = val_pred\n            val_acc = accuracy_score(train_df.iloc[val_idx]['target'].values, torch.round(val_preds.cpu()))\n            val_roc = roc_auc_score(train_df.iloc[val_idx]['target'].values, val_preds.cpu())\n                \n            epochval=epoch + 1\n            \n            print(Fore.YELLOW,'Epoch: ',Style.RESET_ALL,epochval,'|',Fore.CYAN,'Loss: ',Style.RESET_ALL,train_losses,'|',Fore.GREEN,'Train acc:',Style.RESET_ALL,train_acc,'|',Fore.BLUE,' Val acc: ',Style.RESET_ALL,val_acc,'|',Fore.RED,' Val roc_auc:',Style.RESET_ALL,val_roc,'|',Fore.YELLOW,' Training time:',Style.RESET_ALL,str(datetime.timedelta(seconds=time.time() - start_time)))\n                 \n                \n                \n                 \n                \n            \n            scheduler.step(val_roc)\n            # During the first iteration (first epoch) best validation is set to None\n            if not best_val:\n                best_val = val_roc  # So any validation roc_auc we have is the best one for now\n                torch.save(model, model_path)  # Saving the model\n                continue\n                \n            if val_roc >= best_val:\n                best_val = val_roc\n                patience = patience  # Resetting patience since we have new best validation accuracy\n                torch.save(model, model_path)  # Saving current best model\n            else:\n                patience -= 1\n                if patience == 0:\n                    print(Fore.BLUE,'Early stopping. Best Val roc_auc: {:.3f}'.format(best_val),Style.RESET_ALL)\n                    break\n                        \n    model = torch.load(model_path)  # Loading best model of this fold\n    model.eval()  # switch model to the evaluation mode\n    val_preds = torch.zeros((len(val_idx), 1), dtype=torch.float32, device=device)\n    with torch.no_grad():\n        # Predicting on validation set once again to obtain data for OOF\n        for j, (x_val, y_val) in enumerate(val_loader):\n            x_val[0] = torch.tensor(x_val[0], device=device, dtype=torch.float32)\n            x_val[1] = torch.tensor(x_val[1], device=device, dtype=torch.float32)\n            y_val = torch.tensor(y_val, device=device, dtype=torch.float32)\n            z_val = model(x_val[0],x_val[1])\n            val_pred = torch.sigmoid(z_val)\n            val_preds[j*x_val[0].shape[0]:j*x_val[0].shape[0] + x_val[0].shape[0]] = val_pred\n        oof[val_idx] = val_preds.cpu().numpy()\n        \n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# <a id='inference'>Inference✅</a>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"test_loader = DataLoader(dataset=test, batch_size=batch_size2, shuffle=False, num_workers=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Out of the Folds Score:',roc_auc_score(train_df['target'], oof))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = torch.load('/kaggle/input/melanoma/model3.pth')\nmodel.eval()  # switch model to the evaluation mode\npreds = torch.zeros((len(test), 1), dtype=torch.float32, device=device)\nwith torch.no_grad():\n    for _ in range(TTA):  \n            for i, x_test in enumerate(test_loader):  \n                x_test[0] = torch.tensor(x_test[0], device=device, dtype=torch.float32)\n                x_test[1] = torch.tensor(x_test[1], device=device, dtype=torch.float32) \n                z_test = model(x_test[0],x_test[1])\n                z_test = torch.sigmoid(z_test)\n                preds[i*x_test[0].shape[0]:i*x_test[0].shape[0] + x_test[0].shape[0]] += z_test\n    preds /= TTA\n            \n             \n    \n    gc.collect()   \n           \npreds /= skf.n_splits ","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['target'] = preds.cpu().numpy().reshape(-1,)\nsub.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![](http://media.giphy.com/media/5ArJanyCfxgiY/giphy.gif)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<b><i>Please do upvote if you liked the kernel</i></b>","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}