{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1 stype=\"color:red\">Prostate cANcer graDe Assessment (PANDA) Challenge</h1>\n**Prostate cancer diagnosis using the Gleason grading system**"},{"metadata":{},"cell_type":"markdown","source":"## Introduction\n\n<img src=\"https://i.imgur.com/M9GEGja.jpg\"> \n\n\n\nWelcome to the \"Prostate cANcer graDe Assessment (PANDA) Challenge\" competition! In this competition, contestants are challenge to classify the severity of prostate cancer from microscopy scans of prostate biopsy samples. There are two unusual twists to this problem relative to most competitions:\n- Each individual image is quite large. We're excited to see what strategies you come up with for efficiently locating areas of concern to zoom in on.\n\n- This labels are imperfect. This is a challenging area of pathology and even experts in the field with years of experience do not always agree on how to interpret a slide. This will make training models more difficult, but increases the potential medical value of having a strong model to provide consistent ratings. All of the private test set images and most of the public test set images were graded by multiple pathologists, but this was not feasible for the training set. You can find additional details about how consistently the pathologist's labels matched [here](https://zenodo.org/record/3715938#.Xp_UC3UzZTa).\n\nIn this kernel, I will briefly explain the structure of dataset. Then, I will visualize the dataset using Plotly and Matplotlib. And finaly, I will demonstrate how this problem can be approched with a variety of image classification models.\n\n<h4> Please upvote this kernel if you like it. It motivates me </h4>"},{"metadata":{},"cell_type":"markdown","source":"To get started, we need to understand Prostate Cancer, hear is an excellent video about Prostate Cancer"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from IPython.display import HTML\nHTML('<center><iframe width=\"700\" height=\"400\" src=\"https://www.youtube.com/embed/e6h7BxOZuCU?rel=0&amp;controls=0&amp;showinfo=0\" frameborder=\"0\" allowfullscreen></iframe></center>')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Acknowledgements\n\n1. [Getting started with the PANDA dataset](https://www.kaggle.com/wouterbulten/getting-started-with-the-panda-dataset) ~ by Wouter Bulten and team\n2. [PANDA: EDA All you need to know](https://www.kaggle.com/dhananjay3/panda-eda-all-you-need-to-know) ~by Dhananjay Raut\n3. [Development and validation of a deep learning algorithm for improving Gleason scoring of prostate cancer](https://www.nature.com/articles/s41746-019-0112-2)\n4. [Prostate Cancer Research Institute](https://www.youtube.com/watch?v=eTN2vXpSHd8)"},{"metadata":{},"cell_type":"markdown","source":"# The Dataset\n\n- `image_id`: ID code for the image.\n\n- `data_provider`: The name of the institution that provided the data. Both the Karolinska Institute and Radboud University Medical Center contributed data. They used different scanners with slightly different maximum microscope resolutions and worked with different pathologists for labeling their images.\n\n- `isup_grade`: Train only. The target variable. The severity of the cancer on a 0-5 scale.\n\n- `gleason_score`: Train only. An alternate cancer severity rating system with more levels than the ISUP scale. For details on how the gleason and ISUP systems compare, see the [Additional Resources tab](https://admin.kaggle.com/c/prostate-cancer-grade-assessment/overview/additional-resources).\n\n- **[train/test]_images**: The images. Each is a large multi-level tiff file. You can expect roughly 1,000 images in the hidden test set. Note that slightly different procedures were in place for the images used in the test set than the training set. Some of the training set images have stray pen marks on them, but the test set slides are free of pen marks.\n\n- **train_label_masks**: Segmentation masks showing which parts of the image led to the ISUP grade. Not all training images have label masks, and there may be false positives or false negatives in the label masks for a variety of reasons. These masks are provided to assist with the development of strategies for selecting the most useful subsamples of the images. The mask values depend on the data provider:\n\nRadboud: Prostate glands are individually labelled. Valid values are:\n- 0: background (non tissue) or unknown\n- 1: stroma (connective tissue, non-epithelium tissue)\n- 2: healthy (benign) epithelium\n- 3: cancerous epithelium (Gleason 3)\n- 4: cancerous epithelium (Gleason 4)\n- 5: cancerous epithelium (Gleason 5)\n\nKarolinska: Regions are labelled. Valid values are:\n- 1: background (non tissue) or unknown\n- 2: benign tissue (stroma and epithelium combined)\n- 3: cancerous tissue (stroma and epithelium combined)\n\n**sample_submission.csv**: A valid submission file. This is a notebooks-only competition; the downloadable test.csv and sample_submission.csv have been truncated. The full versions will be available to your submitted notebooks."},{"metadata":{},"cell_type":"markdown","source":"# EDA\n\nNow, I will try to visualize the sales data and gain some insights from it."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import os\nimport gc\nimport time\nimport math\nimport datetime\nfrom math import log, floor\nfrom sklearn.neighbors import KDTree\n\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom sklearn.utils import shuffle\nfrom tqdm.notebook import tqdm as tqdm\n\nimport seaborn as sns\nfrom matplotlib import colors\nimport matplotlib.pyplot as plt\nfrom matplotlib.colors import Normalize\nimport matplotlib\n\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.subplots import make_subplots\n\nimport sklearn\n\nimport skimage.io\nimport openslide\nimport glob\nimport cv2\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Load the data"},{"metadata":{"trusted":true},"cell_type":"code","source":"INPUT_DIR = \"../input/prostate-cancer-grade-assessment/\"\nTRAIN_IMG_DIR = \"../input/prostate-cancer-grade-assessment/train_images/\"\nTRAIN_MASK_DIR = \"../input/prostate-cancer-grade-assessment/train_label_masks/\"\nTEST_IMG_DIR = \"../input/prostate-cancer-grade-assessment/test_images)\"\n\ntrain = pd.read_csv(INPUT_DIR+\"train.csv\").set_index(\"image_id\")\ntest = pd.read_csv(INPUT_DIR+\"test.csv\")\nsample_submission = pd.read_csv(INPUT_DIR+\"sample_submission.csv\")","execution_count":null,"outputs":[]},{"metadata":{"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":"## Train data"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"isup_grade_count = train.isup_grade.value_counts().reset_index()\nisup_grade_count.columns = [\"isup_grade\", \"count\"]\n\n\nfig = make_subplots(1,2, specs=[[{\"type\": \"bar\"}, {\"type\": \"pie\"}]])\n\ncolors=px.colors.sequential.Plasma[:6]\n\nfig.add_trace(go.Bar(\n        x=isup_grade_count[\"isup_grade\"].values, \n        y=isup_grade_count[\"count\"].values,\n        marker=dict(color=colors)\n          \n), row=1, col=1)\n\nfig.add_trace(go.Pie(\n        labels = isup_grade_count[\"isup_grade\"].values,\n        values = isup_grade_count[\"count\"].values,\n        marker=dict(colors=colors)\n), row=1, col=2)\n\nfig.update_layout(title=\"Isup_grade - Count plots\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"karolinska = train.groupby([\"data_provider\", \"isup_grade\"])[\"data_provider\"].count().loc[\"karolinska\"].reset_index()\nradboud = train.groupby([\"data_provider\", \"isup_grade\"])[\"data_provider\"].count().loc[\"radboud\"].reset_index()\n\nfig = go.Figure()\n\nfig.add_trace(go.Bar(\n    x=karolinska.isup_grade,\n    y=karolinska.data_provider,\n    name='karolinska',\n    marker_color='indianred'\n))\nfig.add_trace(go.Bar(\n    x=radboud.isup_grade,\n    y=radboud.data_provider,\n    name='rodboud',\n    marker_color='lightsalmon'\n))\n\nfig.update_layout(title=\"targets count based on data provider\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Observation :** Different providers have different target distributions."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"gleason_score_count = train.gleason_score.value_counts().reset_index()\ngleason_score_count.columns = [\"gleason_score\", \"count\"]\n\n\nfig = make_subplots(1,2, specs=[[{\"type\": \"bar\"}, {\"type\": \"pie\"}]])\n\ncolors=px.colors.sequential.Plotly3\n\nfig.add_trace(go.Bar(\n        x=gleason_score_count[\"gleason_score\"].values, \n        y=gleason_score_count[\"count\"].values,\n        marker=dict(color=colors)\n          \n), row=1, col=1)\n\nfig.add_trace(go.Pie(\n        labels = gleason_score_count[\"gleason_score\"].values,\n        values = gleason_score_count[\"count\"].values,\n        marker=dict(colors=colors)\n), row=1, col=2)\n\nfig.update_layout(title=\"Gleason_score - Count plots\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"karolinska = train.groupby([\"data_provider\", \"gleason_score\"])[\"data_provider\"].count().loc[\"karolinska\"].reset_index()\nradboud = train.groupby([\"data_provider\", \"gleason_score\"])[\"data_provider\"].count().loc[\"radboud\"].reset_index()\n\nfig = go.Figure()\n\nfig.add_trace(go.Bar(\n    x=karolinska.gleason_score,\n    y=karolinska.data_provider,\n    name='karolinska',\n    marker_color=px.colors.sequential.Blackbody[1]\n))\nfig.add_trace(go.Bar(\n    x=radboud.gleason_score,\n    y=radboud.data_provider,\n    name='rodboud',\n    marker_color=px.colors.sequential.Blackbody[2]\n))\n\nfig.update_layout(title=\"gleason_score count based on data provider\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Using OpenSlide to load the data\n\nIn the following section we will load data from the slides with [OpenSlide](https://openslide.org/api/python/). \nThe benefit of OpenSlide is that we can load arbitrary regions of the slide, with out loading the whole image in memory.\n\nYou can read more about the OpenSlide python bindings in the documentation: https://openslide.org/api/python/"},{"metadata":{},"cell_type":"markdown","source":"### Loading a slide\n\nBefore we can load from a slide, we need to open it. After a file in open we can retrieve data from it at arbitratry positions and levels."},{"metadata":{"trusted":true},"cell_type":"code","source":"path = f\"{TRAIN_IMG_DIR}005e66f06bce9c2e49142536caf2f6ee.tiff\"\nbiopsy = openslide.OpenSlide(path)\n# do somethiing with the slide hear\nbiopsy.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"we created a small function to show some basic information about a slide. Additionally, this function display a small thumbnail of the slide. All images in the dataset contain this metadata and you can use this in your data pipeline."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def print_slide_details(slide, show_thumbnail=True, max_size=(600, 400)):\n    \"\"\"Print some basic information about a slide\"\"\"\n    # Generate a small image thumbnail\n    if show_thumbnail:\n        #fig = px.imshow(slide.get_thumbnail(size=max_size))\n        #fig.show()\n        display(slide.get_thumbnail(size=max_size))\n    \n    \n    # Here we compute the \"Pixel spacing\" : the physical size of a pixel in the image.\n    # OpenSlide gives the resolution in centimeters so we convert this to microns.\n    \n    spacing = 1 / (float(slide.properties['tiff.XResolution']) / 10000)\n    \n    print(f\"File id: {slide}\")\n    print(f\"Dimensions: {slide.dimensions}\")\n    print(f\"Microns per pixel / pixel spacing: {spacing:.3f}\")\n    print(f\"Number of levels in the image: {slide.level_count}\")\n    print(f\"Downsample factor per level: {slide.level_downsamples}\")\n    print(f\"Dimensions of levels: {slide.level_dimensions}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Running the cell below loads four example biopsies using OpenSlide. Some things you can notice:\n\n- The image dimensions are quite large (typically between 5.000 and 40.000 pixels in both x and y).\n- Each slide has 3 levels you can load, corresponding to a downsampling of 1, 4 and 16. Intermediate levels can be created by downsampling a higher resolution level.\n- The dimensions of each level differ based on the dimensions of the original image.\n- Biopsies can be in different rotations. This rotation has no clinical value, and is only dependent on how the biopsy was collected in the lab.\n- There are noticable color differences between the biopsies, this is very common within pathology and is caused by different laboratory procedures.\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"example_slides = train.index.values[-4:]\n\nfor case_id in example_slides:\n    biopsy = openslide.OpenSlide(os.path.join(TRAIN_IMG_DIR, f'{case_id}.tiff'))\n    print_slide_details(biopsy)\n    biopsy.close()\n    \n    # Print the case-level label\n    print(f\"ISUP grade: {train.loc[case_id, 'isup_grade']}\")\n    print(f\"Gleason score: {train.loc[case_id, 'gleason_score']}\\n\\n\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Observation :** we can observe those are in different dimensions with different levels with down sampling"},{"metadata":{},"cell_type":"markdown","source":"## Loading image regions/patches\nWith OpenSlide we can easily extract patches from the slide from arbitrary locations. Loading a specific region is done using the read_region function.\n\nAfter opening the slide we can, for example, load a 512x512 patch from the lowest level (level 0) at a specific coordinate."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"biopsy = openslide.OpenSlide(os.path.join(TRAIN_IMG_DIR, '00928370e2dfeb8a507667ef1d4efcbb.tiff'))\n\nx = 5150\ny = 21000\nlevel = 0\nwidth = 512\nheight = 512\n\nregion = biopsy.read_region((x,y), level, (width, height))\n#fig = px.imshow(region)\n#fig.show()\ndisplay(region)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Using the level argument we can easily load in data from any level that is present in the slide. Coordinates passed to read_region are always relative to level 0 (the highest resolution)."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"x = 5140\ny = 21000\nlevel = 1\nwidth = 512\nheight = 512\n\nregion = biopsy.read_region((x,y), level, (width, height))\n#fig = px.imshow(region)\n#fig.show()\ndisplay(region)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"biopsy.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loading label masks\n\nApart from the slide-level label (present in the csv file), almost all slides in the training set have an associated mask with additional label information. These masks directly indicate which parts of the tissue are healthy and which are cancerous. The information in the masks differ from the two centers:\n\n- **Radboudumc**: Prostate glands are individually labelled. Valid values are:\n    - 0: background (non tissue) or unknown\n    - 1: stroma (connective tissue, non-epithelium tissue)\n    - 2: healthy (benign) epithelium\n    - 3: cancerous epithelium (Gleason 3)\n    - 4: cancerous epithelium (Gleason 4)\n    - 5: cancerous epithelium (Gleason 5)\n- **Karolinska**: Regions are labelled. Valid values:\n    - 0: background (non tissue) or unknown\n    - 1: benign tissue (stroma and epithelium combined)\n    - 2: cancerous tissue (stroma and epithelium combined)\n\nThe label masks of Radboudumc were semi-automatically generated by several deep learning algorithms, contain noise, and can be considered as weakly-supervised labels. The label masks of Karolinska were semi-autotomatically generated based on annotations by a pathologist.\n\nThe label masks are stored in an RGB format so that they can be easily opened by image readers. The label information is stored in the red (R) channel, the other channels are set to zero and can be ignored. As with the slides itself, the label masks can be opened using OpenSlide."},{"metadata":{},"cell_type":"markdown","source":"## Visualizing the masks (using PIL)\nUsing a small helper function we can display some basic information about a mask. To more easily inspect the masks, we map the int labels to RGB colors using a color palette. If you prefer something like matplotlib you can also use plt.imshow() to directly show a mask (without converting it to an RGB image)."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def print_mask_details(slide, center='radboud', show_thumbnail=True, max_size=(400,400)):\n    \"\"\"Print some basic information about a slide\"\"\"\n    \n    if center not in ['radboud', 'karolinska']:\n        raise Exception(\"Unsupported palette, should be one of [radboud, karolinska].\")\n        \n    \n    # Generate a small image thumbnail\n    if show_thumbnail:\n        # Read in the mask data from the highest level\n        # We cannot use thumbnail() here because we need to load the raw label data.\n        mask_data = slide.read_region((0,0), slide.level_count - 1, slide.level_dimensions[-1])\n        # Mask data is present in the R channel\n        mask_data = mask_data.split()[0]\n        \n        # To show the masks we map the raw label values to RGB values\n        \n        preview_palette = np.zeros(shape=768, dtype=int)\n        if center == \"radboud\":\n            # Mapping : {0: background, 1: stroma, 2: benign epithelium, 3: Gleason 3, 4: Gleason 4, 5: Gleason 5}\n            \n            preview_palette[0:18] = (np.array([0, 0, 0, 0.5, 0.5, 0.5, 0, 1, 0, 1, 1, 0.7, 1, 0.5, 0, 1, 0, 0]) * 255).astype(int)\n            \n        elif center == \"karolinska\":\n            \n            # Mapping: {0: background, 1: benign, 2: cancer}\n            preview_palette[0:9] = (np.array([0, 0, 0, 0.5, 0.5, 0.5, 1, 0, 0]) * 255).astype(int)\n            \n        mask_data.putpalette(data=preview_palette.tolist())\n        mask_data = mask_data.convert(mode='RGB')\n        mask_data.thumbnail(size=max_size, resample=0)\n        \n        #fig = px.imshow(mask_data)\n        #fig.show()\n        display(mask_data)\n        \n        # Compute microns per pixel (openslide gives resolution in centimeters)\n        spacing = 1 / (float(slide.properties['tiff.XResolution']) / 10000)\n\n        print(f\"File id: {slide}\")\n        print(f\"Dimensions: {slide.dimensions}\")\n        print(f\"Microns per pixel / pixel spacing: {spacing:.3f}\")\n        print(f\"Number of levels in the image: {slide.level_count}\")\n        print(f\"Downsample factor per level: {slide.level_downsamples}\")\n        print(f\"Dimensions of levels: {slide.level_dimensions}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The cells below shows two example masks from the dataset. The first mask is from Radboudumc and shows two different grades of cancer (shown in yellow and orange). The second mask is from Karolinska, the region that contains cancer is higlighted in red.\n\nNote that, eventhough a biopsy contains cancer, not all epithelial tissue has to be cancerous. Biopsies can contain a mix of cancerous and healthy tissue."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"mask = openslide.OpenSlide(os.path.join(TRAIN_MASK_DIR, '08ab45297bfe652cc0397f4b37719ba1_mask.tiff'))\nprint_mask_details(mask, center='radboud')\nmask.close()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"mask = openslide.OpenSlide(os.path.join(TRAIN_MASK_DIR, '090a77c517a7a2caa23e443a77a78bc7_mask.tiff'))\nprint_mask_details(mask, center='karolinska')\nmask.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualizing masks (using matplotlib)\nGiven that the masks are just integer matrices, you can also use other packages to display the masks. For example, using matplotlib and a custom color map we can quickly visualize the different cancer regions:"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"mask = openslide.OpenSlide(os.path.join(TRAIN_MASK_DIR , '08ab45297bfe652cc0397f4b37719ba1_mask.tiff'))\nmask_data = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n\nplt.figure()\nplt.title(\"Mask with default cmap\")\nplt.imshow(np.asarray(mask_data)[:,:,0], interpolation='nearest')\nplt.show()\n\nplt.figure()\nplt.title(\"Mask with custom cmap\")\n# Optional: create a custom color map\ncmap = matplotlib.colors.ListedColormap(['black', 'gray', 'green', 'yellow', 'orange', 'red'])\nplt.imshow(np.asarray(mask_data)[:,:,0], cmap=cmap, interpolation='nearest', vmin=0, vmax=5)\nplt.show()\n\nmask.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modeling"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install efficientnet_pytorch\nfrom efficientnet_pytorch import EfficientNet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_IMG_DIR = \"../input/panda2/train_images/\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"##%%writefile dataset.py\n\nimport pandas as pd\nimport albumentations\nimport joblib\nimport numpy as np\nimport torch\n\nfrom PIL import Image\n\nclass PANDADatasetTrain:\n    def __init__(self, df, img_height, img_width, mean, std, train=True):\n        \n        self.image_ids = df.index.values\n        self.isup_grade = df.isup_grade.values\n        self.img_height = img_height\n        self.img_width = img_width\n\n        if train:\n            self.aug = albumentations.Compose([\n                albumentations.Resize(img_height, img_width, always_apply=True),\n                albumentations.ShiftScaleRotate(shift_limit=0.0625,\n                                                scale_limit=0.1, \n                                                rotate_limit=5,\n                                                p=0.9),\n                albumentations.Normalize(mean, std, always_apply=True)\n            ])\n        else:\n            self.aug = albumentations.Compose([\n                albumentations.Resize(img_height, img_width, always_apply=True),\n                albumentations.Normalize(mean, std, always_apply=True)\n            ])\n            \n\n\n    def __len__(self):\n        return len(self.image_ids)\n    \n    def __getitem__(self, item):\n        image = Image.open(f\"{TRAIN_IMG_DIR}{self.image_ids[item]}.png\")\n        #image = image.get_thumbnail(size=(600, 400))\n        #print(image.size)\n        image = self.aug(image=np.array(image))[\"image\"]\n        image = np.transpose(image, (2, 0, 1)).astype(np.float32)\n\n        return {\n            \"image\": torch.tensor(image, dtype=torch.float),\n            \"target\": torch.tensor(self.isup_grade[item], dtype=torch.float)\n        }","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"d = PANDADatasetTrain(train, 600, 400, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))\nout = d.__getitem__(1)\nout[\"image\"].shape, out[\"target\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"##%%writefile efficientnet_model.py\n\nfrom efficientnet_pytorch import EfficientNet\nimport torch.nn as nn\nfrom torch.nn import functional as F\n\nclass EfficientNetB1(nn.Module):\n    def __init__(self, pretrained):\n        super(EfficientNetB1, self).__init__()\n\n        if pretrained is True:\n            self.model = EfficientNet.from_pretrained(\"efficientnet-b1\")\n        \n        self.l0 = nn.Linear(1280, 1)\n\n    def forward(self, x):\n        bs, _, _, _ = x.shape\n        x = self.model.extract_features(x)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(bs, -1)\n        out = self.l0(x)\n\n        return out","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def criterion(target, output):\n    mse_loss = nn.MSELoss()(target, output)\n    return torch.sqrt(mse_loss)\n\ndef train_model(model, train_loader, epoch, optimizer, scheduler, DEVICE):\n    model.train()\n\n    total_loss = 0\n\n    t = tqdm(train_loader)\n    for i, d in enumerate(t):\n\n        image = d[\"image\"].float().to(DEVICE)\n        target = d[\"target\"].float().to(DEVICE)\n\n        optimizer.zero_grad()\n\n        output = model(image)\n        \n        #print(output.shape, target.shape)\n\n        loss = criterion(target, output)\n\n        total_loss += loss\n\n        loss.backward()\n        optimizer.step()\n\n        #print(total_loss/i+1)\n        \n        t.set_description(f'Epoch {epoch+1} : Loss: %.4f'%(total_loss/(i+1)))\n\n        #if i % int(t/10) == 0:\n        #    print(f'Epoch {epoch+1|i} : Loss: %.4f'%(total_loss/(i+1)))\n\n\ndef valid_model(model, valid_loader, epoch, scheduler, DEVICE):\n    model.eval()\n\n    total_loss = 0\n    \n    output_list = []\n    target_list = []\n\n    #t = tqdm(valid_loader)\n    with torch.no_grad():\n        for i, d in enumerate(valid_loader):\n\n            image = d[\"image\"].float().to(DEVICE)\n            target = d[\"target\"].float().to(DEVICE)\n            \n            output = model(image)\n\n            loss = criterion(target, output)\n\n            total_loss += loss\n            \n            output = output.squeeze(1)\n            output = output.cpu().numpy().tolist()\n            target = target.cpu().numpy().tolist()\n            \n            output_list.extend(output)\n            target_list.extend(target)\n            \n\n            #if i == 1:\n            #    break\n        #print(total_loss/i+1)\n\n    RMSE = sklearn.metrics.mean_squared_error(target_list, output_list)\n    print(f\" Valid RMSE : %.4f\"%(RMSE))\n\n    return RMSE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_BATCH_SIZE = 16\nVALID_BATCH_SIZE = 16\nIMG_HEIGHT = 244\nIMG_WIDHT = 244\nMODEL_MEAN = (0.485, 0.456, 0.406)\nMODEL_STD = (0.229, 0.224, 0.225)\n\ntrain_df = train.iloc[:-500]\nvalid_df = train.iloc[-500:]\n\n\n\ntrain_dataset = PANDADatasetTrain(df=train_df, \n                                  img_height=IMG_HEIGHT,\n                                  img_width=IMG_WIDHT,\n                                  mean=MODEL_MEAN,\n                                  std=MODEL_STD, \n                                  train=True )\n    \ntrain_loader = torch.utils.data.DataLoader(\n        dataset=train_dataset,\n        batch_size= TRAIN_BATCH_SIZE,\n        shuffle=False,\n        num_workers=4,\n        drop_last=False\n    )\n\nvalid_dataset = PANDADatasetTrain(df=valid_df, \n                                  img_height=IMG_HEIGHT,\n                                  img_width=IMG_WIDHT,\n                                  mean=MODEL_MEAN,\n                                  std=MODEL_STD, \n                                  train=False)\n\nvalid_loader = torch.utils.data.DataLoader(\n        dataset=valid_dataset,\n        batch_size= VALID_BATCH_SIZE,\n        shuffle=False,\n        num_workers=4,\n        drop_last=False\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DEVICE = \"cuda\"\nEPOCHS = 2\nstart_e = 0\n\nmodel = EfficientNetB1(pretrained=True)\nmodel.to(DEVICE)\n\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n\nfor epoch in range(start_e, EPOCHS):\n    \n    train_model(model, train_loader, epoch, optimizer, scheduler=None, DEVICE=DEVICE)\n    rmse = valid_model(model, valid_loader, epoch, scheduler=None, DEVICE=DEVICE)\n    torch.save(model.state_dict(), f\"model_{epoch}_rmse_{rmse}.pth\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### quadratic_weighted_kappa"},{"metadata":{"trusted":true},"cell_type":"code","source":"def qwk(a1, a2):\n    \"\"\"\n    Source: https://www.kaggle.com/c/data-science-bowl-2019/discussion/114133#latest-660168\n\n    :param a1:\n    :param a2:\n    :param max_rat:\n    :return:\n    \"\"\"\n    max_rat = 3\n    a1 = np.asarray(a1, dtype=int)\n    a2 = np.asarray(a2, dtype=int)\n\n    hist1 = np.zeros((max_rat + 1, ))\n    hist2 = np.zeros((max_rat + 1, ))\n\n    o = 0\n    for k in range(a1.shape[0]):\n        i, j = a1[k], a2[k]\n        hist1[i] += 1\n        hist2[j] += 1\n        o +=  (i - j) * (i - j)\n\n    e = 0\n    for i in range(max_rat + 1):\n        for j in range(max_rat + 1):\n            e += hist1[i] * hist2[j] * (i - j) * (i - j)\n\n    e = e / a1.shape[0]\n\n    return 1 - o / e\n\ndef quadratic_weighted_kappa(y, y_pred):\n    \"\"\"\n    Calculates the quadratic weighted kappa\n    axquadratic_weighted_kappa calculates the quadratic weighted kappa\n    value, which is a measure of inter-rater agreement between two raters\n    that provide discrete numeric ratings.  Potential values range from -1\n    (representing complete disagreement) to 1 (representing complete\n    agreement).  A kappa value of 0 is expected if all agreement is due to\n    chance.\n    quadratic_weighted_kappa(rater_a, rater_b), where rater_a and rater_b\n    each correspond to a list of integer ratings.  These lists must have the\n    same length.\n    The ratings should be integers, and it is assumed that they contain\n    the complete range of possible ratings.\n    quadratic_weighted_kappa(X, min_rating, max_rating), where min_rating\n    is the minimum possible rating, and max_rating is the maximum possible\n    rating\n    \"\"\"\n    rater_a = y\n    rater_b = y_pred\n    min_rating=None\n    max_rating=None\n    rater_a = np.array(rater_a, dtype=int)\n    rater_b = np.array(rater_b, dtype=int)\n    assert(len(rater_a) == len(rater_b))\n    if min_rating is None:\n        min_rating = min(min(rater_a), min(rater_b))\n    if max_rating is None:\n        max_rating = max(max(rater_a), max(rater_b))\n        \n    conf_mat = confusion_matrix1(rater_a, rater_b,\n                                min_rating, max_rating)\n    num_ratings = len(conf_mat)\n    num_scored_items = float(len(rater_a))\n\n    hist_rater_a = histogram(rater_a, min_rating, max_rating)\n    hist_rater_b = histogram(rater_b, min_rating, max_rating)\n\n    numerator = 0.0\n    denominator = 0.0\n\n    for i in range(num_ratings):\n        for j in range(num_ratings):\n            expected_count = (hist_rater_a[i] * hist_rater_b[j]\n                              / num_scored_items)\n            d = pow(i - j, 2.0) / pow(num_ratings - 1, 2.0)\n            numerator += d * conf_mat[i][j] / num_scored_items\n            denominator += d * expected_count / num_scored_items\n\n    return (1.0 - numerator / denominator)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# qwk optimize coefficients\n\nclass OptimizedRounder(object):\n    def __init__(self, init_coef):\n        self.init_coef_ = init_coef\n        self.coef_ = 0\n    \n    def _kappa_loss(self, coef, X, y):\n        X_p = X.copy()\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            elif pred >= coef[3] and pred < coef[4]:\n                X_p[i] = 4\n            else:\n                X_p[i] = 5\n        \n        ll = quadratic_weighted_kappa(y, X_p)\n        \n        return -ll\n    \n    def fit(self, X, y):\n        loss_partial = partial(self._kappa_loss, X=X, y=y)\n        \n        initial_coef = self.init_coef_\n        \n        self.coef_ = spoptimize.minimize(loss_partial, initial_coef, method=\"nelder-mead\")\n        \n    def predict(self, X, coef):\n        X_p = X.copy()\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            elif pred >= coef[3] and pred < coef[4]:\n                X_p[i] = 4\n            else:\n                X_p[i] = 5\n                \n        return X_p\n    \n    def coefficients(self):\n        return self.coef_['x']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"color:red\"> Please upvote if you like it. It motivates me. Thank you ☺️ .</h3>"},{"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}