{
  "id": 452165,
  "title": "baseline with Lightning⚡TIMM scores 0.4+ on LB become TOP 5%",
  "url": "/competitions/UBC-OCEAN/discussion/452165",
  "author_name": "Jirka",
  "post_date": "2023-11-01T04:40:35.024000",
  "votes": 86,
  "comment_count": 28,
  "views": 0,
  "content": "<p>Overall, it seems that we need to use detail from WSI (whole slice images)as shown in the illustration of this <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-eda-load-wsi-prune-bg\" target=\"_blank\">notebook</a>. My first attempts and also naive training start with using the provided thumbnails <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-baseline-with-lightning-timm\" target=\"_blank\">Cancer🔬Subtype: baseline with Lightning⚡TIMM</a> but that stored at most 0.27 on LB. With a swift switch to training on tiles, I got LB's score of 0.4 with the same model and AdamW, which at this moment is 5% submission.</p>\n<h2>data 🗃️</h2>\n<p>Competition provides WSi and thumbnails for the large images except for TMA, but by description, it seems TMA is a majority in the test dataset. So, training on provided thumbnails would miss these key images.</p>\n<p>So I created a <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-large-image-tiles\" target=\"_blank\">notebook</a> that was specialized for cutting tiles from <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-wsi-tiles-0-25x\" target=\"_blank\">all training images of size 2048px and scaled them down to 0.25</a>. I have also pruned tiles without any information, such as background or edge cases.</p>\n<p>As this does not fit to single run storage, I had to do it three times and merge all results in a single dataset: <a href=\"https://www.kaggle.com/datasets/jirkaborovec/tiles-of-cancer-2048px-scale-0-25\" target=\"_blank\">Tiles 🖽 of 🔬 Cancer - 2048px | scale 0.25</a> which can be easily added to your training notebooks.</p>\n<h2>training 💻</h2>\n<p>I have used <a href=\"https://lightning.ai/docs/pytorch/stable/\" target=\"_blank\">Lightning⚡</a>, which absorbed most of the boilerplate code and allowed me seamlessly (without any code changes) switch between debugging on CPU and training on GPU.</p>\n<p>For models, I took <a href=\"https://timm.fast.ai/\" target=\"_blank\">TIMM</a> and one of the simple vision transformers, which has a good balance between the size of its receptive field and the total number of parameters.</p>\n<p>For training, I randomly take one tile from training images and naively assume that it is fine representing the cancer subtypes.</p>\n<p>see the training notebook: <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models\" target=\"_blank\">Cancer🔬Subtype: tiles🖽 w/ Lightning⚡TIMM models</a></p>\n<h2>inference 🚀</h2>\n<p>it uses the trained model from the step above and with <code>pyVips</code> on-the-fly decomposes test images, cuts random 50 tiles, and as the final label sums probability predictions for each tile of a particular image.</p>\n<p>see the training notebook: <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-lightning-torch-inference-tiles\" target=\"_blank\">Cancer🔬Subtype: Lightning⚡Torch [inference|tiles]</a></p>\n<p>🆙 <strong>Please consider upvoting my work - notebooks/dataset if you find them useful or forking it =)</strong> ⏫</p>",
  "messages": [
    {
      "id": 2507510,
      "postDate": "2023-11-01T04:40:35.023Z",
      "content": "<p>Overall, it seems that we need to use detail from WSI (whole slice images)as shown in the illustration of this <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-eda-load-wsi-prune-bg\" target=\"_blank\">notebook</a>. My first attempts and also naive training start with using the provided thumbnails <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-baseline-with-lightning-timm\" target=\"_blank\">Cancer🔬Subtype: baseline with Lightning⚡TIMM</a> but that stored at most 0.27 on LB. With a swift switch to training on tiles, I got LB's score of 0.4 with the same model and AdamW, which at this moment is 5% submission.</p>\n<h2>data 🗃️</h2>\n<p>Competition provides WSi and thumbnails for the large images except for TMA, but by description, it seems TMA is a majority in the test dataset. So, training on provided thumbnails would miss these key images.</p>\n<p>So I created a <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-large-image-tiles\" target=\"_blank\">notebook</a> that was specialized for cutting tiles from <a href=\"https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-wsi-tiles-0-25x\" target=\"_blank\">all training images of size 2048px and scaled them down to 0.25</a>. I have also pruned tiles without any information, such as background or edge cases.</p>\n<p>As this does not fit to single run storage, I had to do it three times and merge all results in a single dataset: <a href=\"https://www.kaggle.com/datasets/jirkaborovec/tiles-of-cancer-2048px-scale-0-25\" target=\"_blank\">Tiles 🖽 of 🔬 Cancer - 2048px | scale 0.25</a> which can be easily added to your training notebooks.</p>\n<h2>training 💻</h2>\n<p>I have used <a href=\"https://lightning.ai/docs/pytorch/stable/\" target=\"_blank\">Lightning⚡</a>, which absorbed most of the boilerplate code and allowed me seamlessly (without any code changes) switch between debugging on CPU and training on GPU.</p>\n<p>For models, I took <a href=\"https://timm.fast.ai/\" target=\"_blank\">TIMM</a> and one of the simple vision transformers, which has a good balance between the size of its receptive field and the total number of parameters.</p>\n<p>For training, I randomly take one tile from training images and naively assume that it is fine representing the cancer subtypes.</p>\n<p>see the training notebook: <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models\" target=\"_blank\">Cancer🔬Subtype: tiles🖽 w/ Lightning⚡TIMM models</a></p>\n<h2>inference 🚀</h2>\n<p>it uses the trained model from the step above and with <code>pyVips</code> on-the-fly decomposes test images, cuts random 50 tiles, and as the final label sums probability predictions for each tile of a particular image.</p>\n<p>see the training notebook: <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-lightning-torch-inference-tiles\" target=\"_blank\">Cancer🔬Subtype: Lightning⚡Torch [inference|tiles]</a></p>\n<p>🆙 <strong>Please consider upvoting my work - notebooks/dataset if you find them useful or forking it =)</strong> ⏫</p>",
      "rawMarkdown": "Overall, it seems that we need to use detail from WSI (whole slice images)as shown in the illustration of this [notebook](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-eda-load-wsi-prune-bg). My first attempts and also naive training start with using the provided thumbnails [Cancer🔬Subtype: baseline with Lightning⚡TIMM](https://kaggle.com/code/jirkaborovec/cancer-subtype-baseline-with-lightning-timm) but that stored at most 0.27 on LB. With a swift switch to training on tiles, I got LB's score of 0.4 with the same model and AdamW, which at this moment is 5% submission.\n\n## data 🗃️\n\nCompetition provides WSi and thumbnails for the large images except for TMA, but by description, it seems TMA is a majority in the test dataset. So, training on provided thumbnails would miss these key images.\n\nSo I created a [notebook](https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-large-image-tiles) that was specialized for cutting tiles from [all training images of size 2048px and scaled them down to 0.25](https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-wsi-tiles-0-25x). I have also pruned tiles without any information, such as background or edge cases.\n\nAs this does not fit to single run storage, I had to do it three times and merge all results in a single dataset: [Tiles 🖽 of 🔬 Cancer - 2048px | scale 0.25](https://www.kaggle.com/datasets/jirkaborovec/tiles-of-cancer-2048px-scale-0-25) which can be easily added to your training notebooks.\n\n## training 💻\n\nI have used [Lightning⚡](https://lightning.ai/docs/pytorch/stable/), which absorbed most of the boilerplate code and allowed me seamlessly (without any code changes) switch between debugging on CPU and training on GPU.\n\nFor models, I took [TIMM](https://timm.fast.ai/) and one of the simple vision transformers, which has a good balance between the size of its receptive field and the total number of parameters.\n\nFor training, I randomly take one tile from training images and naively assume that it is fine representing the cancer subtypes.\n\nsee the training notebook: [Cancer🔬Subtype: tiles🖽 w/ Lightning⚡TIMM models](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models)\n\n## inference 🚀\n\nit uses the trained model from the step above and with `pyVips` on-the-fly decomposes test images, cuts random 50 tiles, and as the final label sums probability predictions for each tile of a particular image.\n\nsee the training notebook: [Cancer🔬Subtype: Lightning⚡Torch [inference|tiles]](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-lightning-torch-inference-tiles)\n\n🆙 **Please consider upvoting my work - notebooks/dataset if you find them useful or forking it =)** ⏫",
      "votes": 85
    },
    {
      "id": 2515290,
      "postDate": "2023-11-06T20:21:19.960Z",
      "content": "<p>Hey, <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> thanks for the awesome baseline! I think we've all learned a lot from your works here!</p>",
      "rawMarkdown": "Hey, @jirkaborovec thanks for the awesome baseline! I think we've all learned a lot from your works here!",
      "votes": 1
    },
    {
      "id": 2511205,
      "postDate": "2023-11-03T14:26:31.747Z",
      "content": "<p>Great job! Btw, what do you mean by the comment in your training code below? Also, what is your reason for using one-hot encoding? Looking forward to your reply.<br>\n<code>as validation is subsampled it may happen that some labels are missing and so created one-hot-encoding vector will be sorter)</code></p>",
      "rawMarkdown": "Great job! Btw, what do you mean by the comment in your training code below? Also, what is your reason for using one-hot encoding? Looking forward to your reply.\n`as validation is subsampled it may happen that some labels are missing and so created one-hot-encoding vector will be sorter)`",
      "votes": 1,
      "replies": [
        {
          "id": 2511215,
          "postDate": "2023-11-03T14:32:24.860Z",
          "content": "<p>in what notebook? it is possible it is an outdated comment :)</p>",
          "rawMarkdown": "in what notebook? it is possible it is an outdated comment :)",
          "votes": 1,
          "replies": [
            {
              "id": 2511225,
              "postDate": "2023-11-03T14:37:33.873Z",
              "content": "<p>Just click on <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models\" target=\"_blank\">this</a> in your discussion above…😂, in the <code>class CancerSubtypeDM(pl.LightningDataModule)</code>.</p>",
              "rawMarkdown": "Just click on [this](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models) in your discussion above...😂, in the `class CancerSubtypeDM(pl.LightningDataModule)`.",
              "votes": 1
            },
            {
              "id": 2511256,
              "postDate": "2023-11-03T14:48:48.370Z",
              "content": "<p>ok, likely the story is such that we have 5 classes and the smallest have 8% so if we take only 10% or less as a validation subset it may happen that this rare class is missing so when you try to re-create labels mapping you would have only 4 classes instead of 5… and then you have disproportion in labels between training and validation</p>",
              "rawMarkdown": "ok, likely the story is such that we have 5 classes and the smallest have 8% so if we take only 10% or less as a validation subset it may happen that this rare class is missing so when you try to re-create labels mapping you would have only 4 classes instead of 5... and then you have disproportion in labels between training and validation",
              "votes": 1
            }
          ]
        }
      ]
    },
    {
      "id": 2511109,
      "postDate": "2023-11-03T13:39:22.900Z",
      "content": "<p>Good work , Thanks for sharing valuable information.</p>",
      "rawMarkdown": "Good work , Thanks for sharing valuable information.",
      "votes": 1
    },
    {
      "id": 2510439,
      "postDate": "2023-11-03T02:08:04.210Z",
      "content": "<p>Thank you for sharing your databases and notebooks <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a>. Really nice work!</p>",
      "rawMarkdown": "Thank you for sharing your databases and notebooks @jirkaborovec. Really nice work!",
      "votes": 1,
      "replies": [
        {
          "id": 2510441,
          "postDate": "2023-11-03T02:16:08.340Z",
          "content": "<p>you are welcome, hope you find them useful</p>",
          "rawMarkdown": "you are welcome, hope you find them useful",
          "votes": 2
        }
      ]
    },
    {
      "id": 2508378,
      "postDate": "2023-11-01T16:35:13.650Z",
      "content": "<p>Did you try tiles with high proportion of background in the inference? Or you just sum the probability predictions of all tiles and assume that noise from low-information tiles will just be random, and therefore averaged to 0 when aggregating?</p>",
      "rawMarkdown": "Did you try tiles with high proportion of background in the inference? Or you just sum the probability predictions of all tiles and assume that noise from low-information tiles will just be random, and therefore averaged to 0 when aggregating?",
      "votes": 1,
      "replies": [
        {
          "id": 2508383,
          "postDate": "2023-11-01T16:42:08.930Z",
          "content": "<p>So the tiles were filtered to reduce samples with mostly BG, but if you ask if I tried to weight tiles prediction by color intensity (a proxy measure to mount of background) - not yet :)</p>",
          "rawMarkdown": "So the tiles were filtered to reduce samples with mostly BG, but if you ask if I tried to weight tiles prediction by color intensity (a proxy measure to mount of background) - not yet :)",
          "votes": 2
        }
      ]
    },
    {
      "id": 2507719,
      "postDate": "2023-11-01T07:57:15.157Z",
      "content": "<p>Really nice work, thanks for the baseline! Just one question: with this approach the inference will take a longer time as you need to decompose the images in tiles, right? In my case it took around 6h for submission</p>",
      "rawMarkdown": "Really nice work, thanks for the baseline! Just one question: with this approach the inference will take a longer time as you need to decompose the images in tiles, right? In my case it took around 6h for submission",
      "votes": 1,
      "replies": [
        {
          "id": 2507732,
          "postDate": "2023-11-01T08:07:56.567Z",
          "content": "<p>got the the one with 0.4 score took about 11h</p>",
          "rawMarkdown": "got the the one with 0.4 score took about 11h",
          "votes": 6
        }
      ]
    },
    {
      "id": 2507595,
      "postDate": "2023-11-01T05:49:11.943Z",
      "content": "<p>Valuable work! Initially, did you simply filter based on the background threshold to avoid selecting patches that are mostly meaningless background, and then randomly pick a patch with stained tissue and assign it the corresponding WSI label for training? Do you have any improved strategies or ideas for the next steps?</p>",
      "rawMarkdown": "Valuable work! Initially, did you simply filter based on the background threshold to avoid selecting patches that are mostly meaningless background, and then randomly pick a patch with stained tissue and assign it the corresponding WSI label for training? Do you have any improved strategies or ideas for the next steps?",
      "votes": 1,
      "replies": [
        {
          "id": 2507613,
          "postDate": "2023-11-01T06:05:37.250Z",
          "content": "<p>That is correct, see the code in PyTorch dataset:</p>\n<pre><code> () -&gt; :\n    random.shuffle(self.img_dirs[idx])\n     img_path  self.img_dirs[idx]:\n        \n        tile = np.array(Image.(img_path))[..., :]\n        \n        black_bg = np.(tile, axis=) == \n        tile[black_bg, :] = \n        \n        mask_bg = np.mean(tile, axis=) &gt; self.white_thr\n        \n         np.(mask_bg) &lt; (np.prod(mask_bg.shape) * self.thr_max_bg):\n            \n        \n</code></pre>",
          "rawMarkdown": "That is correct, see the code in PyTorch dataset:\n```python\ndef __getitem__(self, idx: int) -> tuple:\n    random.shuffle(self.img_dirs[idx])\n    for img_path in self.img_dirs[idx]:\n        # loading the image\n        tile = np.array(Image.open(img_path))[..., :3]\n        # replace the black background with the white one\n        black_bg = np.sum(tile, axis=2) == 0\n        tile[black_bg, :] = 255\n        # determining background pixels with simple thresholding\n        mask_bg = np.mean(tile, axis=2) > self.white_thr\n        # passing to the next one if tile has too many BG\n        if np.sum(mask_bg) < (np.prod(mask_bg.shape) * self.thr_max_bg):\n            break\n        # if nothing, you always have the last image/tile\n```",
          "votes": 4
        }
      ]
    },
    {
      "id": 2509536,
      "postDate": "2023-11-02T12:35:56.570Z",
      "content": "<p>Thank you for providing great starting point. I will use your notebook as a baseline. Great sharing and contribution.</p>",
      "rawMarkdown": "Thank you for providing great starting point. I will use your notebook as a baseline. Great sharing and contribution.",
      "votes": 2
    },
    {
      "id": 2507777,
      "postDate": "2023-11-01T08:33:46.173Z",
      "content": "<p>Thanks for providing the baseline. Just started the competition. Will surely help me.</p>",
      "rawMarkdown": "Thanks for providing the baseline. Just started the competition. Will surely help me.",
      "votes": 2,
      "replies": [
        {
          "id": 2507828,
          "postDate": "2023-11-01T09:08:01.067Z",
          "content": "<p>sure, pls check also the notebooks/implemntations</p>",
          "rawMarkdown": "sure, pls check also the notebooks/implemntations",
          "votes": 2
        }
      ]
    },
    {
      "id": 2551786,
      "postDate": "2023-12-07T01:11:41.017Z",
      "content": "<p>Hi Jirka Borovec, I'm wondering if you've tried model ensemble or tta. l've found that the inference time of just single efficientnet_b0 model based on patch has been as high as 7-8 hours 😂 (with parallel processing), do you have any good suggestions?</p>",
      "rawMarkdown": "Hi Jirka Borovec, I'm wondering if you've tried model ensemble or tta. l've found that the inference time of just single efficientnet_b0 model based on patch has been as high as 7-8 hours 😂 (with parallel processing), do you have any good suggestions?",
      "replies": [
        {
          "id": 2552042,
          "postDate": "2023-12-07T06:28:58.370Z",
          "content": "<p>From my experience, most of the time takes preprocessing (load WSI, cut and filter edge cases) so running ensambel on GPU shall be fine… </p>",
          "rawMarkdown": "From my experience, most of the time takes preprocessing (load WSI, cut and filter edge cases) so running ensambel on GPU shall be fine... ",
          "replies": [
            {
              "id": 2552064,
              "postDate": "2023-12-07T06:55:31.667Z",
              "content": "<p>Thanks, I think that's too tough for us😂. I'll give it a try.</p>",
              "rawMarkdown": "Thanks, I think that's too tough for us😂. I'll give it a try."
            }
          ]
        }
      ]
    },
    {
      "id": 2526719,
      "postDate": "2023-11-16T01:56:46.633Z",
      "content": "<p>Hey, <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a>. Are you training like the following：<br>\nfor imgs in dataloader:<br>\n        with torch.no_grad():<br>\n            pred = model(imgs.to(device))<br>\n        preds += pred.cpu().numpy().tolist()<br>\n # decide label<br>\n lb = np.argmax(np.sum(preds, axis=0))<br>\n row['label'] = labels[lb]</p>\n<p>Alternatively, are you splitting the preprocessed samples at rates of 8:2 or 9:1 to create a Training set and Validation set?</p>",
      "rawMarkdown": "Hey, @jirkaborovec. Are you training like the following：\nfor imgs in dataloader:\n        with torch.no_grad():\n            pred = model(imgs.to(device))\n        preds += pred.cpu().numpy().tolist()\n # decide label\n lb = np.argmax(np.sum(preds, axis=0))\n row['label'] = labels[lb]\n\nAlternatively, are you splitting the preprocessed samples at rates of 8:2 or 9:1 to create a Training set and Validation set?",
      "replies": [
        {
          "id": 2526789,
          "postDate": "2023-11-16T03:50:24.840Z",
          "content": "<p>Well, you refer inference code, but yes I trained with ratio 9:1</p>",
          "rawMarkdown": "Well, you refer inference code, but yes I trained with ratio 9:1"
        }
      ]
    },
    {
      "id": 2524711,
      "postDate": "2023-11-14T13:31:41.433Z",
      "content": "<p>Thank you for the excellent work! I have just a small note: The training dataset is not perfectly normally distributed (mean -0.5), because images with a lot of background are skipped. This should be taken into account in the calculation of normalization parameters, so that the training images are truly normally distributed :)</p>\n<p>Have you calculated the balanced accuracy on the validation split? I'm surprised that the accuracy is only at 0.22, but on the leaderboard, a balanced accuracy of 0.4 is achieved.</p>",
      "rawMarkdown": "Thank you for the excellent work! I have just a small note: The training dataset is not perfectly normally distributed (mean -0.5), because images with a lot of background are skipped. This should be taken into account in the calculation of normalization parameters, so that the training images are truly normally distributed :)\n\nHave you calculated the balanced accuracy on the validation split? I'm surprised that the accuracy is only at 0.22, but on the leaderboard, a balanced accuracy of 0.4 is achieved.",
      "replies": [
        {
          "id": 2524980,
          "postDate": "2023-11-14T16:51:22.907Z",
          "content": "<p>About the color normalization, not really as I do the same background replacement and skipping also for predictions… </p>",
          "rawMarkdown": "About the color normalization, not really as I do the same background replacement and skipping also for predictions... ",
          "replies": [
            {
              "id": 2524981,
              "postDate": "2023-11-14T16:54:08.873Z",
              "content": "<p>But you don't do it when you compute the normalization parameters :) Check the mean of your final dataset if you don't trust me</p>",
              "rawMarkdown": "But you don't do it when you compute the normalization parameters :) Check the mean of your final dataset if you don't trust me"
            }
          ]
        }
      ]
    },
    {
      "id": 2517679,
      "postDate": "2023-11-08T17:12:27.190Z",
      "content": "<p>Once cut you end up with some images that are 90% black, have you tried filtering a second time?<br>\nI wonder if shifting the cut by 1024 px could improve the model.</p>",
      "rawMarkdown": "Once cut you end up with some images that are 90% black, have you tried filtering a second time?\nI wonder if shifting the cut by 1024 px could improve the model.",
      "replies": [
        {
          "id": 2517680,
          "postDate": "2023-11-08T17:15:30.993Z",
          "content": "<p>Sure it is possible, or you can also consider random cut, not mosaic, but it requires more compute…..<br>\nalso r read here somewhere you may navigate the best location y intensity in red channel</p>",
          "rawMarkdown": "Sure it is possible, or you can also consider random cut, not mosaic, but it requires more compute.....\nalso r read here somewhere you may navigate the best location y intensity in red channel"
        }
      ]
    },
    {
      "id": 2510228,
      "postDate": "2023-11-02T20:01:24.213Z",
      "content": "<p>Great work! Thanks for sharing.</p>",
      "rawMarkdown": "Great work! Thanks for sharing.",
      "votes": 2
    }
  ],
  "comments": [
    {
      "id": 2515290,
      "author_name": "Brian Yip",
      "author_url": "",
      "post_date": "2023-11-06T20:21:19.960000",
      "content": "<p>Hey, <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> thanks for the awesome baseline! I think we've all learned a lot from your works here!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 2511205,
      "author_name": "阳光开朗大男孩",
      "author_url": "",
      "post_date": "2023-11-03T14:26:31.747000",
      "content": "<p>Great job! Btw, what do you mean by the comment in your training code below? Also, what is your reason for using one-hot encoding? Looking forward to your reply.<br>\n<code>as validation is subsampled it may happen that some labels are missing and so created one-hot-encoding vector will be sorter)</code></p>",
      "votes": 1,
      "replies": [
        {
          "id": 2511215,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-03T14:32:24.860000",
          "content": "<p>in what notebook? it is possible it is an outdated comment :)</p>",
          "votes": 1,
          "replies": [
            {
              "id": 2511225,
              "author_name": "阳光开朗大男孩",
              "author_url": "",
              "post_date": "2023-11-03T14:37:33.873000",
              "content": "<p>Just click on <a href=\"https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models\" target=\"_blank\">this</a> in your discussion above…😂, in the <code>class CancerSubtypeDM(pl.LightningDataModule)</code>.</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 2511256,
              "author_name": "Jirka",
              "author_url": "",
              "post_date": "2023-11-03T14:48:48.370000",
              "content": "<p>ok, likely the story is such that we have 5 classes and the smallest have 8% so if we take only 10% or less as a validation subset it may happen that this rare class is missing so when you try to re-create labels mapping you would have only 4 classes instead of 5… and then you have disproportion in labels between training and validation</p>",
              "votes": 1,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 2511109,
      "author_name": "sunil thite",
      "author_url": "",
      "post_date": "2023-11-03T13:39:22.900000",
      "content": "<p>Good work , Thanks for sharing valuable information.</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 2510439,
      "author_name": "David Roberts",
      "author_url": "",
      "post_date": "2023-11-03T02:08:04.210000",
      "content": "<p>Thank you for sharing your databases and notebooks <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a>. Really nice work!</p>",
      "votes": 1,
      "replies": [
        {
          "id": 2510441,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-03T02:16:08.340000",
          "content": "<p>you are welcome, hope you find them useful</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 2508378,
      "author_name": "Andreu Arderiu",
      "author_url": "",
      "post_date": "2023-11-01T16:35:13.650000",
      "content": "<p>Did you try tiles with high proportion of background in the inference? Or you just sum the probability predictions of all tiles and assume that noise from low-information tiles will just be random, and therefore averaged to 0 when aggregating?</p>",
      "votes": 1,
      "replies": [
        {
          "id": 2508383,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-01T16:42:08.930000",
          "content": "<p>So the tiles were filtered to reduce samples with mostly BG, but if you ask if I tried to weight tiles prediction by color intensity (a proxy measure to mount of background) - not yet :)</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 2507719,
      "author_name": "Andreu Arderiu",
      "author_url": "",
      "post_date": "2023-11-01T07:57:15.157000",
      "content": "<p>Really nice work, thanks for the baseline! Just one question: with this approach the inference will take a longer time as you need to decompose the images in tiles, right? In my case it took around 6h for submission</p>",
      "votes": 1,
      "replies": [
        {
          "id": 2507732,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-01T08:07:56.567000",
          "content": "<p>got the the one with 0.4 score took about 11h</p>",
          "votes": 6,
          "replies": []
        }
      ]
    },
    {
      "id": 2507595,
      "author_name": "Huang Jin Feng",
      "author_url": "",
      "post_date": "2023-11-01T05:49:11.943000",
      "content": "<p>Valuable work! Initially, did you simply filter based on the background threshold to avoid selecting patches that are mostly meaningless background, and then randomly pick a patch with stained tissue and assign it the corresponding WSI label for training? Do you have any improved strategies or ideas for the next steps?</p>",
      "votes": 1,
      "replies": [
        {
          "id": 2507613,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-01T06:05:37.250000",
          "content": "<p>That is correct, see the code in PyTorch dataset:</p>\n<pre><code> () -&gt; :\n    random.shuffle(self.img_dirs[idx])\n     img_path  self.img_dirs[idx]:\n        \n        tile = np.array(Image.(img_path))[..., :]\n        \n        black_bg = np.(tile, axis=) == \n        tile[black_bg, :] = \n        \n        mask_bg = np.mean(tile, axis=) &gt; self.white_thr\n        \n         np.(mask_bg) &lt; (np.prod(mask_bg.shape) * self.thr_max_bg):\n            \n        \n</code></pre>",
          "votes": 4,
          "replies": []
        }
      ]
    },
    {
      "id": 2509536,
      "author_name": "Remek Kinas",
      "author_url": "",
      "post_date": "2023-11-02T12:35:56.570000",
      "content": "<p>Thank you for providing great starting point. I will use your notebook as a baseline. Great sharing and contribution.</p>",
      "votes": 2,
      "replies": []
    },
    {
      "id": 2507777,
      "author_name": "TensorKitty",
      "author_url": "",
      "post_date": "2023-11-01T08:33:46.173000",
      "content": "<p>Thanks for providing the baseline. Just started the competition. Will surely help me.</p>",
      "votes": 2,
      "replies": [
        {
          "id": 2507828,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-01T09:08:01.067000",
          "content": "<p>sure, pls check also the notebooks/implemntations</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 2551786,
      "author_name": "阳光开朗大男孩",
      "author_url": "",
      "post_date": "2023-12-07T01:11:41.017000",
      "content": "<p>Hi Jirka Borovec, I'm wondering if you've tried model ensemble or tta. l've found that the inference time of just single efficientnet_b0 model based on patch has been as high as 7-8 hours 😂 (with parallel processing), do you have any good suggestions?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 2552042,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-12-07T06:28:58.370000",
          "content": "<p>From my experience, most of the time takes preprocessing (load WSI, cut and filter edge cases) so running ensambel on GPU shall be fine… </p>",
          "votes": 0,
          "replies": [
            {
              "id": 2552064,
              "author_name": "阳光开朗大男孩",
              "author_url": "",
              "post_date": "2023-12-07T06:55:31.667000",
              "content": "<p>Thanks, I think that's too tough for us😂. I'll give it a try.</p>",
              "votes": 0,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 2526719,
      "author_name": "woodman718",
      "author_url": "",
      "post_date": "2023-11-16T01:56:46.633000",
      "content": "<p>Hey, <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a>. Are you training like the following：<br>\nfor imgs in dataloader:<br>\n        with torch.no_grad():<br>\n            pred = model(imgs.to(device))<br>\n        preds += pred.cpu().numpy().tolist()<br>\n # decide label<br>\n lb = np.argmax(np.sum(preds, axis=0))<br>\n row['label'] = labels[lb]</p>\n<p>Alternatively, are you splitting the preprocessed samples at rates of 8:2 or 9:1 to create a Training set and Validation set?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 2526789,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-16T03:50:24.840000",
          "content": "<p>Well, you refer inference code, but yes I trained with ratio 9:1</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 2524711,
      "author_name": "Patchef",
      "author_url": "",
      "post_date": "2023-11-14T13:31:41.433000",
      "content": "<p>Thank you for the excellent work! I have just a small note: The training dataset is not perfectly normally distributed (mean -0.5), because images with a lot of background are skipped. This should be taken into account in the calculation of normalization parameters, so that the training images are truly normally distributed :)</p>\n<p>Have you calculated the balanced accuracy on the validation split? I'm surprised that the accuracy is only at 0.22, but on the leaderboard, a balanced accuracy of 0.4 is achieved.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 2524980,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-14T16:51:22.907000",
          "content": "<p>About the color normalization, not really as I do the same background replacement and skipping also for predictions… </p>",
          "votes": 0,
          "replies": [
            {
              "id": 2524981,
              "author_name": "Patchef",
              "author_url": "",
              "post_date": "2023-11-14T16:54:08.873000",
              "content": "<p>But you don't do it when you compute the normalization parameters :) Check the mean of your final dataset if you don't trust me</p>",
              "votes": 0,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 2517679,
      "author_name": "steffelf",
      "author_url": "",
      "post_date": "2023-11-08T17:12:27.190000",
      "content": "<p>Once cut you end up with some images that are 90% black, have you tried filtering a second time?<br>\nI wonder if shifting the cut by 1024 px could improve the model.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 2517680,
          "author_name": "Jirka",
          "author_url": "",
          "post_date": "2023-11-08T17:15:30.993000",
          "content": "<p>Sure it is possible, or you can also consider random cut, not mosaic, but it requires more compute…..<br>\nalso r read here somewhere you may navigate the best location y intensity in red channel</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 2510228,
      "author_name": "Martin Kovacevic Buvinic",
      "author_url": "",
      "post_date": "2023-11-02T20:01:24.213000",
      "content": "<p>Great work! Thanks for sharing.</p>",
      "votes": 2,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "2507510": "Overall, it seems that we need to use detail from WSI (whole slice images)as shown in the illustration of this [notebook](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-eda-load-wsi-prune-bg). My first attempts and also naive training start with using the provided thumbnails [Cancer🔬Subtype: baseline with Lightning⚡TIMM](https://kaggle.com/code/jirkaborovec/cancer-subtype-baseline-with-lightning-timm) but that stored at most 0.27 on LB. With a swift switch to training on tiles, I got LB's score of 0.4 with the same model and AdamW, which at this moment is 5% submission.\n\n## data 🗃️\n\nCompetition provides WSi and thumbnails for the large images except for TMA, but by description, it seems TMA is a majority in the test dataset. So, training on provided thumbnails would miss these key images.\n\nSo I created a [notebook](https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-large-image-tiles) that was specialized for cutting tiles from [all training images of size 2048px and scaled them down to 0.25](https://kaggle.com/code/jirkaborovec/cancer-subtype-decompose-wsi-tiles-0-25x). I have also pruned tiles without any information, such as background or edge cases.\n\nAs this does not fit to single run storage, I had to do it three times and merge all results in a single dataset: [Tiles 🖽 of 🔬 Cancer - 2048px | scale 0.25](https://www.kaggle.com/datasets/jirkaborovec/tiles-of-cancer-2048px-scale-0-25) which can be easily added to your training notebooks.\n\n## training 💻\n\nI have used [Lightning⚡](https://lightning.ai/docs/pytorch/stable/), which absorbed most of the boilerplate code and allowed me seamlessly (without any code changes) switch between debugging on CPU and training on GPU.\n\nFor models, I took [TIMM](https://timm.fast.ai/) and one of the simple vision transformers, which has a good balance between the size of its receptive field and the total number of parameters.\n\nFor training, I randomly take one tile from training images and naively assume that it is fine representing the cancer subtypes.\n\nsee the training notebook: [Cancer🔬Subtype: tiles🖽 w/ Lightning⚡TIMM models](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models)\n\n## inference 🚀\n\nit uses the trained model from the step above and with `pyVips` on-the-fly decomposes test images, cuts random 50 tiles, and as the final label sums probability predictions for each tile of a particular image.\n\nsee the training notebook: [Cancer🔬Subtype: Lightning⚡Torch [inference|tiles]](https://www.kaggle.com/code/jirkaborovec/cancer-subtype-lightning-torch-inference-tiles)\n\n🆙 **Please consider upvoting my work - notebooks/dataset if you find them useful or forking it =)** ⏫",
    "2515290": "Hey, @jirkaborovec thanks for the awesome baseline! I think we've all learned a lot from your works here!",
    "2511205": "Great job! Btw, what do you mean by the comment in your training code below? Also, what is your reason for using one-hot encoding? Looking forward to your reply.\n`as validation is subsampled it may happen that some labels are missing and so created one-hot-encoding vector will be sorter)`",
    "2511109": "Good work , Thanks for sharing valuable information.",
    "2510439": "Thank you for sharing your databases and notebooks @jirkaborovec. Really nice work!",
    "2508378": "Did you try tiles with high proportion of background in the inference? Or you just sum the probability predictions of all tiles and assume that noise from low-information tiles will just be random, and therefore averaged to 0 when aggregating?",
    "2507719": "Really nice work, thanks for the baseline! Just one question: with this approach the inference will take a longer time as you need to decompose the images in tiles, right? In my case it took around 6h for submission",
    "2507595": "Valuable work! Initially, did you simply filter based on the background threshold to avoid selecting patches that are mostly meaningless background, and then randomly pick a patch with stained tissue and assign it the corresponding WSI label for training? Do you have any improved strategies or ideas for the next steps?",
    "2509536": "Thank you for providing great starting point. I will use your notebook as a baseline. Great sharing and contribution.",
    "2507777": "Thanks for providing the baseline. Just started the competition. Will surely help me.",
    "2551786": "Hi Jirka Borovec, I'm wondering if you've tried model ensemble or tta. l've found that the inference time of just single efficientnet_b0 model based on patch has been as high as 7-8 hours 😂 (with parallel processing), do you have any good suggestions?",
    "2526719": "Hey, @jirkaborovec. Are you training like the following：\nfor imgs in dataloader:\n        with torch.no_grad():\n            pred = model(imgs.to(device))\n        preds += pred.cpu().numpy().tolist()\n # decide label\n lb = np.argmax(np.sum(preds, axis=0))\n row['label'] = labels[lb]\n\nAlternatively, are you splitting the preprocessed samples at rates of 8:2 or 9:1 to create a Training set and Validation set?",
    "2524711": "Thank you for the excellent work! I have just a small note: The training dataset is not perfectly normally distributed (mean -0.5), because images with a lot of background are skipped. This should be taken into account in the calculation of normalization parameters, so that the training images are truly normally distributed :)\n\nHave you calculated the balanced accuracy on the validation split? I'm surprised that the accuracy is only at 0.22, but on the leaderboard, a balanced accuracy of 0.4 is achieved.",
    "2517679": "Once cut you end up with some images that are 90% black, have you tried filtering a second time?\nI wonder if shifting the cut by 1024 px could improve the model.",
    "2510228": "Great work! Thanks for sharing."
  }
}