{
  "id": 238198,
  "title": "1st place solution",
  "url": "/competitions/hubmap-kidney-segmentation/discussion/238198",
  "author_name": "Tom",
  "post_date": "2021-05-11T13:28:02.048000",
  "votes": 134,
  "comment_count": 48,
  "views": 0,
  "content": "<p>First of all, congratulations to the winners and thank you Kaggle and the host team for this tough competition! I entered this competition early and spent a lot of time until the data update. I could not keep my motivation for hand-labeling, so I left a month ago. Luckily I got the 1st place, so I would like to share my approach.</p>\n<h1>Summary of my approach</h1>\n<ul>\n<li>single model Unet se_resnext101_32x4d (4folds)</li>\n<li>some techniques from previous segmentation competitions (mainly from the cloud comp.)  </li>\n<li>balanced tile sampling for training (EDIT : masked area is balanced)</li>\n<li>pseudo-label for the public test data and external data</li>\n<li>a trick for avoiding the edge effect</li>\n<li>My pipeline was developed with the old dataset (i.e., before the data update).</li>\n</ul>\n<h1>1. Data preparation</h1>\n<p>make 1024x104 tiles + shifted 1024x1024 tiles (I shifted the tiles by (512,512))</p>\n<h1>2. Validation</h1>\n<p>I selected the validation data so that the same patient number is in the same group</p>\n<pre><code>val_patient_numbers_list = [\n    [63921], # fold0\n    [68250], # fold1\n    [65631], # fold2\n    [67177], # fold3\n ]\n</code></pre>\n<h1>3. Balanced tile sampling for training</h1>\n<p>First I binned the tile data with respect to the masked area (number of bins = 4 for masked tiles). Then I apply the following procedure for balanced sampling.</p>\n<pre><code>n_sample = trn_df['is_masked'].value_counts().min()\ntrn_df_0 = trn_df[trn_df['is_masked']==False].sample(n_sample, replace=True)\ntrn_df_1 = trn_df[trn_df['is_masked']==True].sample(n_sample, replace=True)\nn_bin = int(trn_df_1['binned'].value_counts().mean())\ntrn_df_list = []\nfor bin_size in trn_df_1['binned'].unique():\n    trn_df_list.append(trn_df_1[trn_df_1['binned']==bin_size].sample(n_bin, replace=True))\ntrn_df_1 = pd.concat(trn_df_list, axis=0)\ntrn_df_balanced = pd.concat([trn_df_1, trn_df_0], axis=0).reset_index(drop=True)\n</code></pre>\n<h1>4. Model</h1>\n<p>U-Net SeResNext101 + CBAM + hypercolumns + deepsupervision<br>\n In my case, larger model gave better CV and LB.<br>\n I resized 1024x1024 to 320x320 for input tiles.<br>\n here is the code snippet:</p>\n<pre><code>class CenterBlock(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super().__init__()\n        self.conv = conv3x3(in_channel, out_channel).apply(init_weight)\n\n    def forward(self, inputs):\n        x = self.conv(inputs)\n        return x\n\nclass DecodeBlock(nn.Module):\n    def __init__(self, in_channel, out_channel, upsample):\n        super().__init__()\n        self.bn1 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.upsample = nn.Sequential()\n        if upsample:\n            self.upsample.add_module('upsample',nn.Upsample(scale_factor=2, mode='nearest'))\n        self.conv3x3_1 = conv3x3(in_channel, in_channel).apply(init_weight)\n        self.bn2 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.conv3x3_2 = conv3x3(in_channel, out_channel).apply(init_weight)\n        self.cbam = CBAM(out_channel, reduction=16)\n        self.conv1x1   = conv1x1(in_channel, out_channel).apply(init_weight)\n\n    def forward(self, inputs):\n        x  = F.relu(self.bn1(inputs))\n        x  = self.upsample(x)\n        x  = self.conv3x3_1(x)\n        x  = self.conv3x3_2(F.relu(self.bn2(x)))\n        x  = self.cbam(x)\n        x += self.conv1x1(self.upsample(inputs)) #shortcut\n        return x\n\nclass UNET_SERESNEXT101(nn.Module):\n    def __init__(self, resolution, deepsupervision, clfhead, load_weights=True):\n        super().__init__()\n        h,w = resolution\n        self.deepsupervision = deepsupervision\n        self.clfhead = clfhead\n\n        #encoder\n        model_name = 'se_resnext101_32x4d'\n        seresnext101 = pretrainedmodels.__dict__[model_name](pretrained=None)\n        if load_weights:\n            seresnext101.load_state_dict(torch.load(f'{model_name}.pth'))\n\n        self.encoder0 = nn.Sequential(\n            seresnext101.layer0.conv1, #(*,3,h,w)-&gt;(*,64,h/2,w/2)\n            seresnext101.layer0.bn1,\n            seresnext101.layer0.relu1,\n        )\n        self.encoder1 = nn.Sequential(\n            seresnext101.layer0.pool, #-&gt;(*,64,h/4,w/4)\n            seresnext101.layer1 #-&gt;(*,256,h/4,w/4)\n        )\n        self.encoder2 = seresnext101.layer2 #-&gt;(*,512,h/8,w/8)\n        self.encoder3 = seresnext101.layer3 #-&gt;(*,1024,h/16,w/16)\n        self.encoder4 = seresnext101.layer4 #-&gt;(*,2048,h/32,w/32)\n\n        #center\n        self.center  = CenterBlock(2048,512) #-&gt;(*,512,h/32,w/32)\n\n        #decoder\n        self.decoder4 = DecodeBlock(512+2048,64,upsample=True) #-&gt;(*,64,h/16,w/16)\n        self.decoder3 = DecodeBlock(64+1024,64, upsample=True) #-&gt;(*,64,h/8,w/8)\n        self.decoder2 = DecodeBlock(64+512,64,  upsample=True) #-&gt;(*,64,h/4,w/4) \n        self.decoder1 = DecodeBlock(64+256,64,  upsample=True) #-&gt;(*,64,h/2,w/2) \n        self.decoder0 = DecodeBlock(64,64, upsample=True) #-&gt;(*,64,h,w) \n\n        #upsample\n        self.upsample4 = nn.Upsample(scale_factor=16, mode='bilinear', align_corners=True)\n        self.upsample3 = nn.Upsample(scale_factor=8, mode='bilinear', align_corners=True)\n        self.upsample2 = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=True)\n        self.upsample1 = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n\n        #deep supervision\n        self.deep4 = conv1x1(64,1).apply(init_weight)\n        self.deep3 = conv1x1(64,1).apply(init_weight)\n        self.deep2 = conv1x1(64,1).apply(init_weight)\n        self.deep1 = conv1x1(64,1).apply(init_weight)\n\n        #final conv\n        self.final_conv = nn.Sequential(\n            conv3x3(320,64).apply(init_weight),\n            nn.ELU(True),\n            conv1x1(64,1).apply(init_weight)\n        )\n\n        #clf head\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.clf = nn.Sequential(\n            nn.BatchNorm1d(2048).apply(init_weight),\n            nn.Linear(2048,512).apply(init_weight),\n            nn.ELU(True),\n            nn.BatchNorm1d(512).apply(init_weight),\n            nn.Linear(512,1).apply(init_weight)\n        )\n\n    def forward(self, inputs):\n        #encoder\n        x0 = self.encoder0(inputs) #-&gt;(*,64,h/2,w/2)\n        x1 = self.encoder1(x0) #-&gt;(*,256,h/4,w/4)\n        x2 = self.encoder2(x1) #-&gt;(*,512,h/8,w/8)\n        x3 = self.encoder3(x2) #-&gt;(*,1024,h/16,w/16)\n        x4 = self.encoder4(x3) #-&gt;(*,2048,h/32,w/32)\n\n        #center\n        y5 = self.center(x4) #-&gt;(*,320,h/32,w/32)\n\n        #decoder\n        y4 = self.decoder4(torch.cat([x4,y5], dim=1)) #-&gt;(*,64,h/16,w/16)\n        y3 = self.decoder3(torch.cat([x3,y4], dim=1)) #-&gt;(*,64,h/8,w/8)\n        y2 = self.decoder2(torch.cat([x2,y3], dim=1)) #-&gt;(*,64,h/4,w/4)\n        y1 = self.decoder1(torch.cat([x1,y2], dim=1)) #-&gt;(*,64,h/2,w/2) \n        y0 = self.decoder0(y1) #-&gt;(*,64,h,w)\n\n        #hypercolumns\n        y4 = self.upsample4(y4) #-&gt;(*,64,h,w)\n        y3 = self.upsample3(y3) #-&gt;(*,64,h,w)\n        y2 = self.upsample2(y2) #-&gt;(*,64,h,w)\n        y1 = self.upsample1(y1) #-&gt;(*,64,h,w)\n        hypercol = torch.cat([y0,y1,y2,y3,y4], dim=1)\n\n        #final conv\n        logits = self.final_conv(hypercol) #-&gt;(*,1,h,w)\n\n        #clf head\n        logits_clf = self.clf(self.avgpool(x4).squeeze(-1).squeeze(-1)) #-&gt;(*,1)\n\n        if self.clfhead:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps, logits_clf\n            else:\n                return logits, logits_clf\n        else:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps\n            else:\n                return logits\n</code></pre>\n<h1>5. Loss</h1>\n<p>I used bce loss + lovasz-hinge loss, on top oh that I used deep supervision with bce loss + lovasz-hinge loss (for only non-empty masks) multiplied by 0.1. For classification head, I used bce loss</p>\n<h1>6. External data and Pseudo-label</h1>\n<p>I generated pseudo-labels for (EDIT) train data, public test data, the hubmap-portal (<a href=\"https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset\" target=\"_blank\">https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset</a>)<br>\n and dataset_a_dib (<a href=\"https://data.mendeley.com/datasets/k7nvtgn2x6/3)\" target=\"_blank\">https://data.mendeley.com/datasets/k7nvtgn2x6/3)</a>. For the data d488c759a, I used Carno Zhao's pseudo-label (thanks <a href=\"https://www.kaggle.com/carnozhao\" target=\"_blank\">@carnozhao</a> !) for one of my final model and my model's pseudo-label for the other one. I checked my private scores and found that these models' performances are not so different. But my 1st place model is the former one with Carno Zhao's pseudo-label. I think it's not the hand-labeling but the indirectly ensemble effect that boosted my score since my model is single and diversity should contribute.</p>\n<h1>7. A trick for inference</h1>\n<p>I found that avoiding edge effect consistently boosts CV and LB. My trick is to use only the center part of tiles for prediction (I notice that 3rd place solution by <a href=\"https://www.kaggle.com/shujun717\" target=\"_blank\">@shujun717</a> used the same idea). I found that using smaller part as prediction gave better CV and LB, but it is time-consuming. So I end up with using 512x512 center part from original 1024x1024 as prediction. I needed x4 time for inference, but using classification head prediction was helpful to save inference time.</p>\n<h1>8. LB scores</h1>\n<p>public LB=0.936 / private LB=0.951</p>\n<h1>9. Some thoughts</h1>\n<p>I used what I learned from the previous competitions and I used the same model architecture for most of my experiments. This saved my time and I focused on the data preparation and how to avoid the edge effect. I think I did something different from the other competitors but I'm not sure these were the keys in the final position since I've not experimented a lot for the new dataset.</p>\n<p>EDIT<br>\ntraining code : <a href=\"https://github.com/tikutikutiku\" target=\"_blank\">https://github.com/tikutikutiku</a><br>\ninference code : <a href=\"https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269\" target=\"_blank\">https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269</a></p>",
  "messages": [
    {
      "id": 1302287,
      "postDate": "2021-05-11T13:28:02.050Z",
      "content": "<p>First of all, congratulations to the winners and thank you Kaggle and the host team for this tough competition! I entered this competition early and spent a lot of time until the data update. I could not keep my motivation for hand-labeling, so I left a month ago. Luckily I got the 1st place, so I would like to share my approach.</p>\n<h1>Summary of my approach</h1>\n<ul>\n<li>single model Unet se_resnext101_32x4d (4folds)</li>\n<li>some techniques from previous segmentation competitions (mainly from the cloud comp.)  </li>\n<li>balanced tile sampling for training (EDIT : masked area is balanced)</li>\n<li>pseudo-label for the public test data and external data</li>\n<li>a trick for avoiding the edge effect</li>\n<li>My pipeline was developed with the old dataset (i.e., before the data update).</li>\n</ul>\n<h1>1. Data preparation</h1>\n<p>make 1024x104 tiles + shifted 1024x1024 tiles (I shifted the tiles by (512,512))</p>\n<h1>2. Validation</h1>\n<p>I selected the validation data so that the same patient number is in the same group</p>\n<pre><code>val_patient_numbers_list = [\n    [63921], # fold0\n    [68250], # fold1\n    [65631], # fold2\n    [67177], # fold3\n ]\n</code></pre>\n<h1>3. Balanced tile sampling for training</h1>\n<p>First I binned the tile data with respect to the masked area (number of bins = 4 for masked tiles). Then I apply the following procedure for balanced sampling.</p>\n<pre><code>n_sample = trn_df['is_masked'].value_counts().min()\ntrn_df_0 = trn_df[trn_df['is_masked']==False].sample(n_sample, replace=True)\ntrn_df_1 = trn_df[trn_df['is_masked']==True].sample(n_sample, replace=True)\nn_bin = int(trn_df_1['binned'].value_counts().mean())\ntrn_df_list = []\nfor bin_size in trn_df_1['binned'].unique():\n    trn_df_list.append(trn_df_1[trn_df_1['binned']==bin_size].sample(n_bin, replace=True))\ntrn_df_1 = pd.concat(trn_df_list, axis=0)\ntrn_df_balanced = pd.concat([trn_df_1, trn_df_0], axis=0).reset_index(drop=True)\n</code></pre>\n<h1>4. Model</h1>\n<p>U-Net SeResNext101 + CBAM + hypercolumns + deepsupervision<br>\n In my case, larger model gave better CV and LB.<br>\n I resized 1024x1024 to 320x320 for input tiles.<br>\n here is the code snippet:</p>\n<pre><code>class CenterBlock(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super().__init__()\n        self.conv = conv3x3(in_channel, out_channel).apply(init_weight)\n\n    def forward(self, inputs):\n        x = self.conv(inputs)\n        return x\n\nclass DecodeBlock(nn.Module):\n    def __init__(self, in_channel, out_channel, upsample):\n        super().__init__()\n        self.bn1 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.upsample = nn.Sequential()\n        if upsample:\n            self.upsample.add_module('upsample',nn.Upsample(scale_factor=2, mode='nearest'))\n        self.conv3x3_1 = conv3x3(in_channel, in_channel).apply(init_weight)\n        self.bn2 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.conv3x3_2 = conv3x3(in_channel, out_channel).apply(init_weight)\n        self.cbam = CBAM(out_channel, reduction=16)\n        self.conv1x1   = conv1x1(in_channel, out_channel).apply(init_weight)\n\n    def forward(self, inputs):\n        x  = F.relu(self.bn1(inputs))\n        x  = self.upsample(x)\n        x  = self.conv3x3_1(x)\n        x  = self.conv3x3_2(F.relu(self.bn2(x)))\n        x  = self.cbam(x)\n        x += self.conv1x1(self.upsample(inputs)) #shortcut\n        return x\n\nclass UNET_SERESNEXT101(nn.Module):\n    def __init__(self, resolution, deepsupervision, clfhead, load_weights=True):\n        super().__init__()\n        h,w = resolution\n        self.deepsupervision = deepsupervision\n        self.clfhead = clfhead\n\n        #encoder\n        model_name = 'se_resnext101_32x4d'\n        seresnext101 = pretrainedmodels.__dict__[model_name](pretrained=None)\n        if load_weights:\n            seresnext101.load_state_dict(torch.load(f'{model_name}.pth'))\n\n        self.encoder0 = nn.Sequential(\n            seresnext101.layer0.conv1, #(*,3,h,w)-&gt;(*,64,h/2,w/2)\n            seresnext101.layer0.bn1,\n            seresnext101.layer0.relu1,\n        )\n        self.encoder1 = nn.Sequential(\n            seresnext101.layer0.pool, #-&gt;(*,64,h/4,w/4)\n            seresnext101.layer1 #-&gt;(*,256,h/4,w/4)\n        )\n        self.encoder2 = seresnext101.layer2 #-&gt;(*,512,h/8,w/8)\n        self.encoder3 = seresnext101.layer3 #-&gt;(*,1024,h/16,w/16)\n        self.encoder4 = seresnext101.layer4 #-&gt;(*,2048,h/32,w/32)\n\n        #center\n        self.center  = CenterBlock(2048,512) #-&gt;(*,512,h/32,w/32)\n\n        #decoder\n        self.decoder4 = DecodeBlock(512+2048,64,upsample=True) #-&gt;(*,64,h/16,w/16)\n        self.decoder3 = DecodeBlock(64+1024,64, upsample=True) #-&gt;(*,64,h/8,w/8)\n        self.decoder2 = DecodeBlock(64+512,64,  upsample=True) #-&gt;(*,64,h/4,w/4) \n        self.decoder1 = DecodeBlock(64+256,64,  upsample=True) #-&gt;(*,64,h/2,w/2) \n        self.decoder0 = DecodeBlock(64,64, upsample=True) #-&gt;(*,64,h,w) \n\n        #upsample\n        self.upsample4 = nn.Upsample(scale_factor=16, mode='bilinear', align_corners=True)\n        self.upsample3 = nn.Upsample(scale_factor=8, mode='bilinear', align_corners=True)\n        self.upsample2 = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=True)\n        self.upsample1 = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n\n        #deep supervision\n        self.deep4 = conv1x1(64,1).apply(init_weight)\n        self.deep3 = conv1x1(64,1).apply(init_weight)\n        self.deep2 = conv1x1(64,1).apply(init_weight)\n        self.deep1 = conv1x1(64,1).apply(init_weight)\n\n        #final conv\n        self.final_conv = nn.Sequential(\n            conv3x3(320,64).apply(init_weight),\n            nn.ELU(True),\n            conv1x1(64,1).apply(init_weight)\n        )\n\n        #clf head\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.clf = nn.Sequential(\n            nn.BatchNorm1d(2048).apply(init_weight),\n            nn.Linear(2048,512).apply(init_weight),\n            nn.ELU(True),\n            nn.BatchNorm1d(512).apply(init_weight),\n            nn.Linear(512,1).apply(init_weight)\n        )\n\n    def forward(self, inputs):\n        #encoder\n        x0 = self.encoder0(inputs) #-&gt;(*,64,h/2,w/2)\n        x1 = self.encoder1(x0) #-&gt;(*,256,h/4,w/4)\n        x2 = self.encoder2(x1) #-&gt;(*,512,h/8,w/8)\n        x3 = self.encoder3(x2) #-&gt;(*,1024,h/16,w/16)\n        x4 = self.encoder4(x3) #-&gt;(*,2048,h/32,w/32)\n\n        #center\n        y5 = self.center(x4) #-&gt;(*,320,h/32,w/32)\n\n        #decoder\n        y4 = self.decoder4(torch.cat([x4,y5], dim=1)) #-&gt;(*,64,h/16,w/16)\n        y3 = self.decoder3(torch.cat([x3,y4], dim=1)) #-&gt;(*,64,h/8,w/8)\n        y2 = self.decoder2(torch.cat([x2,y3], dim=1)) #-&gt;(*,64,h/4,w/4)\n        y1 = self.decoder1(torch.cat([x1,y2], dim=1)) #-&gt;(*,64,h/2,w/2) \n        y0 = self.decoder0(y1) #-&gt;(*,64,h,w)\n\n        #hypercolumns\n        y4 = self.upsample4(y4) #-&gt;(*,64,h,w)\n        y3 = self.upsample3(y3) #-&gt;(*,64,h,w)\n        y2 = self.upsample2(y2) #-&gt;(*,64,h,w)\n        y1 = self.upsample1(y1) #-&gt;(*,64,h,w)\n        hypercol = torch.cat([y0,y1,y2,y3,y4], dim=1)\n\n        #final conv\n        logits = self.final_conv(hypercol) #-&gt;(*,1,h,w)\n\n        #clf head\n        logits_clf = self.clf(self.avgpool(x4).squeeze(-1).squeeze(-1)) #-&gt;(*,1)\n\n        if self.clfhead:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps, logits_clf\n            else:\n                return logits, logits_clf\n        else:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps\n            else:\n                return logits\n</code></pre>\n<h1>5. Loss</h1>\n<p>I used bce loss + lovasz-hinge loss, on top oh that I used deep supervision with bce loss + lovasz-hinge loss (for only non-empty masks) multiplied by 0.1. For classification head, I used bce loss</p>\n<h1>6. External data and Pseudo-label</h1>\n<p>I generated pseudo-labels for (EDIT) train data, public test data, the hubmap-portal (<a href=\"https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset\" target=\"_blank\">https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset</a>)<br>\n and dataset_a_dib (<a href=\"https://data.mendeley.com/datasets/k7nvtgn2x6/3)\" target=\"_blank\">https://data.mendeley.com/datasets/k7nvtgn2x6/3)</a>. For the data d488c759a, I used Carno Zhao's pseudo-label (thanks <a href=\"https://www.kaggle.com/carnozhao\" target=\"_blank\">@carnozhao</a> !) for one of my final model and my model's pseudo-label for the other one. I checked my private scores and found that these models' performances are not so different. But my 1st place model is the former one with Carno Zhao's pseudo-label. I think it's not the hand-labeling but the indirectly ensemble effect that boosted my score since my model is single and diversity should contribute.</p>\n<h1>7. A trick for inference</h1>\n<p>I found that avoiding edge effect consistently boosts CV and LB. My trick is to use only the center part of tiles for prediction (I notice that 3rd place solution by <a href=\"https://www.kaggle.com/shujun717\" target=\"_blank\">@shujun717</a> used the same idea). I found that using smaller part as prediction gave better CV and LB, but it is time-consuming. So I end up with using 512x512 center part from original 1024x1024 as prediction. I needed x4 time for inference, but using classification head prediction was helpful to save inference time.</p>\n<h1>8. LB scores</h1>\n<p>public LB=0.936 / private LB=0.951</p>\n<h1>9. Some thoughts</h1>\n<p>I used what I learned from the previous competitions and I used the same model architecture for most of my experiments. This saved my time and I focused on the data preparation and how to avoid the edge effect. I think I did something different from the other competitors but I'm not sure these were the keys in the final position since I've not experimented a lot for the new dataset.</p>\n<p>EDIT<br>\ntraining code : <a href=\"https://github.com/tikutikutiku\" target=\"_blank\">https://github.com/tikutikutiku</a><br>\ninference code : <a href=\"https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269\" target=\"_blank\">https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269</a></p>",
      "rawMarkdown": "First of all, congratulations to the winners and thank you Kaggle and the host team for this tough competition! I entered this competition early and spent a lot of time until the data update. I could not keep my motivation for hand-labeling, so I left a month ago. Luckily I got the 1st place, so I would like to share my approach.\n\n\n# Summary of my approach\n+ single model Unet se_resnext101_32x4d (4folds)\n+ some techniques from previous segmentation competitions (mainly from the cloud comp.)  \n+ balanced tile sampling for training (EDIT : masked area is balanced)\n+ pseudo-label for the public test data and external data\n+ a trick for avoiding the edge effect\n+ My pipeline was developed with the old dataset (i.e., before the data update).\n\n\n# 1. Data preparation\n make 1024x104 tiles + shifted 1024x1024 tiles (I shifted the tiles by (512,512))\n \n# 2. Validation\n I selected the validation data so that the same patient number is in the same group\n ```\nval_patient_numbers_list = [\n    [63921], # fold0\n    [68250], # fold1\n    [65631], # fold2\n    [67177], # fold3\n ]\n```\n \n# 3. Balanced tile sampling for training\nFirst I binned the tile data with respect to the masked area (number of bins = 4 for masked tiles). Then I apply the following procedure for balanced sampling.\n\n ```\nn_sample = trn_df['is_masked'].value_counts().min()\ntrn_df_0 = trn_df[trn_df['is_masked']==False].sample(n_sample, replace=True)\ntrn_df_1 = trn_df[trn_df['is_masked']==True].sample(n_sample, replace=True)\nn_bin = int(trn_df_1['binned'].value_counts().mean())\ntrn_df_list = []\nfor bin_size in trn_df_1['binned'].unique():\n    trn_df_list.append(trn_df_1[trn_df_1['binned']==bin_size].sample(n_bin, replace=True))\ntrn_df_1 = pd.concat(trn_df_list, axis=0)\ntrn_df_balanced = pd.concat([trn_df_1, trn_df_0], axis=0).reset_index(drop=True)\n```\n \n \n# 4. Model\n U-Net SeResNext101 + CBAM + hypercolumns + deepsupervision\n In my case, larger model gave better CV and LB.\n I resized 1024x1024 to 320x320 for input tiles.\n here is the code snippet:\n \n```\nclass CenterBlock(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super().__init__()\n        self.conv = conv3x3(in_channel, out_channel).apply(init_weight)\n        \n    def forward(self, inputs):\n        x = self.conv(inputs)\n        return x\n\nclass DecodeBlock(nn.Module):\n    def __init__(self, in_channel, out_channel, upsample):\n        super().__init__()\n        self.bn1 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.upsample = nn.Sequential()\n        if upsample:\n            self.upsample.add_module('upsample',nn.Upsample(scale_factor=2, mode='nearest'))\n        self.conv3x3_1 = conv3x3(in_channel, in_channel).apply(init_weight)\n        self.bn2 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.conv3x3_2 = conv3x3(in_channel, out_channel).apply(init_weight)\n        self.cbam = CBAM(out_channel, reduction=16)\n        self.conv1x1   = conv1x1(in_channel, out_channel).apply(init_weight)\n        \n    def forward(self, inputs):\n        x  = F.relu(self.bn1(inputs))\n        x  = self.upsample(x)\n        x  = self.conv3x3_1(x)\n        x  = self.conv3x3_2(F.relu(self.bn2(x)))\n        x  = self.cbam(x)\n        x += self.conv1x1(self.upsample(inputs)) #shortcut\n        return x\n        \nclass UNET_SERESNEXT101(nn.Module):\n    def __init__(self, resolution, deepsupervision, clfhead, load_weights=True):\n        super().__init__()\n        h,w = resolution\n        self.deepsupervision = deepsupervision\n        self.clfhead = clfhead\n        \n        #encoder\n        model_name = 'se_resnext101_32x4d'\n        seresnext101 = pretrainedmodels.__dict__[model_name](pretrained=None)\n        if load_weights:\n            seresnext101.load_state_dict(torch.load(f'{model_name}.pth'))\n        \n        self.encoder0 = nn.Sequential(\n            seresnext101.layer0.conv1, #(*,3,h,w)->(*,64,h/2,w/2)\n            seresnext101.layer0.bn1,\n            seresnext101.layer0.relu1,\n        )\n        self.encoder1 = nn.Sequential(\n            seresnext101.layer0.pool, #->(*,64,h/4,w/4)\n            seresnext101.layer1 #->(*,256,h/4,w/4)\n        )\n        self.encoder2 = seresnext101.layer2 #->(*,512,h/8,w/8)\n        self.encoder3 = seresnext101.layer3 #->(*,1024,h/16,w/16)\n        self.encoder4 = seresnext101.layer4 #->(*,2048,h/32,w/32)\n        \n        #center\n        self.center  = CenterBlock(2048,512) #->(*,512,h/32,w/32)\n        \n        #decoder\n        self.decoder4 = DecodeBlock(512+2048,64,upsample=True) #->(*,64,h/16,w/16)\n        self.decoder3 = DecodeBlock(64+1024,64, upsample=True) #->(*,64,h/8,w/8)\n        self.decoder2 = DecodeBlock(64+512,64,  upsample=True) #->(*,64,h/4,w/4) \n        self.decoder1 = DecodeBlock(64+256,64,  upsample=True) #->(*,64,h/2,w/2) \n        self.decoder0 = DecodeBlock(64,64, upsample=True) #->(*,64,h,w) \n        \n        #upsample\n        self.upsample4 = nn.Upsample(scale_factor=16, mode='bilinear', align_corners=True)\n        self.upsample3 = nn.Upsample(scale_factor=8, mode='bilinear', align_corners=True)\n        self.upsample2 = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=True)\n        self.upsample1 = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        \n        #deep supervision\n        self.deep4 = conv1x1(64,1).apply(init_weight)\n        self.deep3 = conv1x1(64,1).apply(init_weight)\n        self.deep2 = conv1x1(64,1).apply(init_weight)\n        self.deep1 = conv1x1(64,1).apply(init_weight)\n        \n        #final conv\n        self.final_conv = nn.Sequential(\n            conv3x3(320,64).apply(init_weight),\n            nn.ELU(True),\n            conv1x1(64,1).apply(init_weight)\n        )\n        \n        #clf head\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.clf = nn.Sequential(\n            nn.BatchNorm1d(2048).apply(init_weight),\n            nn.Linear(2048,512).apply(init_weight),\n            nn.ELU(True),\n            nn.BatchNorm1d(512).apply(init_weight),\n            nn.Linear(512,1).apply(init_weight)\n        )\n        \n    def forward(self, inputs):\n        #encoder\n        x0 = self.encoder0(inputs) #->(*,64,h/2,w/2)\n        x1 = self.encoder1(x0) #->(*,256,h/4,w/4)\n        x2 = self.encoder2(x1) #->(*,512,h/8,w/8)\n        x3 = self.encoder3(x2) #->(*,1024,h/16,w/16)\n        x4 = self.encoder4(x3) #->(*,2048,h/32,w/32)\n        \n        #center\n        y5 = self.center(x4) #->(*,320,h/32,w/32)\n        \n        #decoder\n        y4 = self.decoder4(torch.cat([x4,y5], dim=1)) #->(*,64,h/16,w/16)\n        y3 = self.decoder3(torch.cat([x3,y4], dim=1)) #->(*,64,h/8,w/8)\n        y2 = self.decoder2(torch.cat([x2,y3], dim=1)) #->(*,64,h/4,w/4)\n        y1 = self.decoder1(torch.cat([x1,y2], dim=1)) #->(*,64,h/2,w/2) \n        y0 = self.decoder0(y1) #->(*,64,h,w)\n        \n        #hypercolumns\n        y4 = self.upsample4(y4) #->(*,64,h,w)\n        y3 = self.upsample3(y3) #->(*,64,h,w)\n        y2 = self.upsample2(y2) #->(*,64,h,w)\n        y1 = self.upsample1(y1) #->(*,64,h,w)\n        hypercol = torch.cat([y0,y1,y2,y3,y4], dim=1)\n        \n        #final conv\n        logits = self.final_conv(hypercol) #->(*,1,h,w)\n        \n        #clf head\n        logits_clf = self.clf(self.avgpool(x4).squeeze(-1).squeeze(-1)) #->(*,1)\n        \n        if self.clfhead:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps, logits_clf\n            else:\n                return logits, logits_clf\n        else:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps\n            else:\n                return logits\n```\n                \n\n# 5. Loss\n I used bce loss + lovasz-hinge loss, on top oh that I used deep supervision with bce loss + lovasz-hinge loss (for only non-empty masks) multiplied by 0.1. For classification head, I used bce loss\n \n \n# 6. External data and Pseudo-label\n I generated pseudo-labels for (EDIT) train data, public test data, the hubmap-portal (https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset)\n and dataset_a_dib (https://data.mendeley.com/datasets/k7nvtgn2x6/3). For the data d488c759a, I used Carno Zhao's pseudo-label (thanks @carnozhao !) for one of my final model and my model's pseudo-label for the other one. I checked my private scores and found that these models' performances are not so different. But my 1st place model is the former one with Carno Zhao's pseudo-label. I think it's not the hand-labeling but the indirectly ensemble effect that boosted my score since my model is single and diversity should contribute.\n \n\n# 7. A trick for inference\n I found that avoiding edge effect consistently boosts CV and LB. My trick is to use only the center part of tiles for prediction (I notice that 3rd place solution by @shujun717 used the same idea). I found that using smaller part as prediction gave better CV and LB, but it is time-consuming. So I end up with using 512x512 center part from original 1024x1024 as prediction. I needed x4 time for inference, but using classification head prediction was helpful to save inference time.\n\n# 8. LB scores\n public LB=0.936 / private LB=0.951\n \n \n# 9. Some thoughts\n I used what I learned from the previous competitions and I used the same model architecture for most of my experiments. This saved my time and I focused on the data preparation and how to avoid the edge effect. I think I did something different from the other competitors but I'm not sure these were the keys in the final position since I've not experimented a lot for the new dataset.\n\nEDIT\ntraining code : https://github.com/tikutikutiku\ninference code : https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269",
      "votes": 134
    },
    {
      "id": 1495856,
      "postDate": "2021-08-29T20:48:37.403Z",
      "content": "<p>I learn a lot from you. Great work =))</p>",
      "rawMarkdown": "I learn a lot from you. Great work =))",
      "votes": 1
    },
    {
      "id": 1305231,
      "postDate": "2021-05-13T07:03:28.003Z",
      "content": "<p>Congratulations，May I ask a small question, Dose hypercolumns or CBAM really help your CV or LB？In most of the segmentation competitions I participated in, they basically did not work, and sometimes even hurt CV and LB.  I’m not sure if it’s because of my code problem or their effect is really not obvious. Thanks for your sharing!</p>",
      "rawMarkdown": "Congratulations，May I ask a small question, Dose hypercolumns or CBAM really help your CV or LB？In most of the segmentation competitions I participated in, they basically did not work, and sometimes even hurt CV and LB.  I’m not sure if it’s because of my code problem or their effect is really not obvious. Thanks for your sharing!",
      "votes": 1,
      "replies": [
        {
          "id": 1305462,
          "postDate": "2021-05-13T09:42:37.883Z",
          "content": "<p>I used hypercolumns and CBAM for all of my experiments in this competition. So I didn't do the abbreviation study. From my experience of the cloud competition, these modules helped.<br>\nI'm not sure if these are always helpful. Maybe the devil's in the details here…</p>",
          "rawMarkdown": "I used hypercolumns and CBAM for all of my experiments in this competition. So I didn't do the abbreviation study. From my experience of the cloud competition, these modules helped.\nI'm not sure if these are always helpful. Maybe the devil's in the details here...",
          "votes": 2
        },
        {
          "id": 1305529,
          "postDate": "2021-05-13T10:42:14.727Z",
          "content": "<p>I get it, thank you for your generous sharing! 😁</p>",
          "rawMarkdown": "I get it, thank you for your generous sharing! 😁",
          "votes": 1
        }
      ]
    },
    {
      "id": 1304489,
      "postDate": "2021-05-12T16:43:02.747Z",
      "content": "<p>Very nice job, congratulations!</p>",
      "rawMarkdown": "Very nice job, congratulations!",
      "votes": 1
    },
    {
      "id": 1303803,
      "postDate": "2021-05-12T08:51:37.870Z",
      "content": "<p>Congratulation, great work <a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> , <br>\nThanks for sharing your approach </p>",
      "rawMarkdown": "Congratulation, great work @tikutiku , \nThanks for sharing your approach ",
      "votes": 1
    },
    {
      "id": 1303798,
      "postDate": "2021-05-12T08:45:07.043Z",
      "content": "<p>Congtras and thanks for sharing yoru idea, Amazing job with 25 days old submission!</p>",
      "rawMarkdown": "Congtras and thanks for sharing yoru idea, Amazing job with 25 days old submission!",
      "votes": 1
    },
    {
      "id": 1303333,
      "postDate": "2021-05-12T02:49:24.247Z",
      "content": "<p>Congratulations. <br>\nWhat hardware did you use? How long have you been developed your model?</p>",
      "rawMarkdown": "Congratulations. \nWhat hardware did you use? How long have you been developed your model?",
      "votes": 1,
      "replies": [
        {
          "id": 1844511,
          "postDate": "2022-07-05T15:39:36.373Z",
          "content": "<p><a href=\"https://github.com/tikutikutiku/kaggle-hubmap\" target=\"_blank\">this link</a> if you have not ever found the answer. <a href=\"https://www.kaggle.com/vedenev\" target=\"_blank\">@vedenev</a> </p>",
          "rawMarkdown": "[this link](https://github.com/tikutikutiku/kaggle-hubmap) if you have not ever found the answer. @vedenev "
        }
      ]
    },
    {
      "id": 1303116,
      "postDate": "2021-05-11T23:16:36.547Z",
      "content": "<p><a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> Congratulation on First Place and Thanks for sharing the approach</p>",
      "rawMarkdown": "@tikutiku Congratulation on First Place and Thanks for sharing the approach",
      "votes": 1
    },
    {
      "id": 1302708,
      "postDate": "2021-05-11T17:11:52.210Z",
      "content": "<p>Congrats! Interesting to see we used the same trick. <a href=\"https://www.kaggle.com/iafoss\" target=\"_blank\">@iafoss</a> also told me he had subs that used the same method and scored really well on private (0.950), so it really does seem to generalize well. </p>",
      "rawMarkdown": "Congrats! Interesting to see we used the same trick. @iafoss also told me he had subs that used the same method and scored really well on private (0.950), so it really does seem to generalize well. ",
      "votes": 1,
      "replies": [
        {
          "id": 1303039,
          "postDate": "2021-05-11T21:34:24.300Z",
          "content": "<p>Thanks! That's good to know. The trick can be used in any segmentation problem !</p>",
          "rawMarkdown": "Thanks! That's good to know. The trick can be used in any segmentation problem !",
          "votes": 1
        }
      ]
    },
    {
      "id": 1302607,
      "postDate": "2021-05-11T16:16:10.817Z",
      "content": "<p>Great Tom! Congratulations. Happy for you.</p>",
      "rawMarkdown": "Great Tom! Congratulations. Happy for you.",
      "votes": 1,
      "replies": [
        {
          "id": 1303032,
          "postDate": "2021-05-11T21:29:59.890Z",
          "content": "<p>Thank you <a href=\"https://www.kaggle.com/mpware\" target=\"_blank\">@mpware</a> !</p>",
          "rawMarkdown": "Thank you @mpware !",
          "votes": 1
        }
      ]
    },
    {
      "id": 1302386,
      "postDate": "2021-05-11T14:15:28.970Z",
      "content": "<p>congrats!!! You can use the prize money to buy some serious graphic cards (prices skyrocketed). Interesting you should mention public dataset. I used it to pre-train my models. I saw a minimal (i.e. 0.001 to 0.003) improvement over using the backbone directly.</p>",
      "rawMarkdown": "congrats!!! You can use the prize money to buy some serious graphic cards (prices skyrocketed). Interesting you should mention public dataset. I used it to pre-train my models. I saw a minimal (i.e. 0.001 to 0.003) improvement over using the backbone directly.",
      "votes": 1
    },
    {
      "id": 1302301,
      "postDate": "2021-05-11T13:36:03.490Z",
      "content": "<p>Congratulations on win! I saw your last submission a month ago, so I thought you forget this competition and don’t know your win :))</p>",
      "rawMarkdown": "Congratulations on win! I saw your last submission a month ago, so I thought you forget this competition and don’t know your win :))",
      "votes": 1,
      "replies": [
        {
          "id": 1302303,
          "postDate": "2021-05-11T13:38:06.760Z",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/lhagiimn\" target=\"_blank\">@lhagiimn</a> ! I almost forgot because I was busy on HPA comp. :)</p>",
          "rawMarkdown": "Thanks @lhagiimn ! I almost forgot because I was busy on HPA comp. :)",
          "votes": 1
        }
      ]
    },
    {
      "id": 1304646,
      "postDate": "2021-05-12T18:52:06.370Z",
      "content": "<p>Congratulations <a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> . Your last submission was 1 month ago. Haha, that's great!</p>",
      "rawMarkdown": "Congratulations @tikutiku . Your last submission was 1 month ago. Haha, that's great!",
      "votes": 2,
      "replies": [
        {
          "id": 1304795,
          "postDate": "2021-05-12T22:12:47.553Z",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/cdeotte\" target=\"_blank\">@cdeotte</a> ! I entered this competition early. So, in the other point of view, my last submission was made 5 months after I joined this competition.   </p>",
          "rawMarkdown": "Thanks @cdeotte ! I entered this competition early. So, in the other point of view, my last submission was made 5 months after I joined this competition.   ",
          "votes": 3
        }
      ]
    },
    {
      "id": 1302294,
      "postDate": "2021-05-11T13:32:57.257Z",
      "content": "<p>Congratz !<br>\nNeedless to say I was surprised to see you on top with a 25+ days old submission :)</p>",
      "rawMarkdown": "Congratz !\nNeedless to say I was surprised to see you on top with a 25+ days old submission :)",
      "votes": 2,
      "replies": [
        {
          "id": 1302296,
          "postDate": "2021-05-11T13:34:07.653Z",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/theoviel\" target=\"_blank\">@theoviel</a> !</p>",
          "rawMarkdown": "Thanks @theoviel !",
          "votes": 1
        },
        {
          "id": 1302398,
          "postDate": "2021-05-11T14:24:16.633Z",
          "content": "<p>Also, that's your second 1st place and every time I was one spot away from being it the money. <br>\nI'm not sure what to think about that aha</p>",
          "rawMarkdown": "Also, that's your second 1st place and every time I was one spot away from being it the money. \nI'm not sure what to think about that aha"
        },
        {
          "id": 1302406,
          "postDate": "2021-05-11T14:30:20.167Z",
          "content": "<p>my second time to have big surprise. Let's see the next time :)</p>",
          "rawMarkdown": "my second time to have big surprise. Let's see the next time :)",
          "votes": 1
        }
      ]
    },
    {
      "id": 1869804,
      "postDate": "2022-07-25T04:11:33.670Z",
      "content": "<p>Thanks for sharing this, many of these things are just blowing up my mind. I just picked an interest in coding and I love to grow up fast in it. I will appreciate if anyone here can add me to his group were I can learna nd grow fast. Thanks</p>",
      "rawMarkdown": "Thanks for sharing this, many of these things are just blowing up my mind. I just picked an interest in coding and I love to grow up fast in it. I will appreciate if anyone here can add me to his group were I can learna nd grow fast. Thanks"
    },
    {
      "id": 1582293,
      "postDate": "2021-11-14T18:18:50.247Z",
      "content": "<p>Great work and thank you for sharing your approach.</p>",
      "rawMarkdown": "Great work and thank you for sharing your approach."
    },
    {
      "id": 1544144,
      "postDate": "2021-10-14T05:25:19.500Z",
      "content": "<p>Congtras and thanks for sharing ideas and codes.<br>\nCan I ask one question about <a href=\"https://github.com/tikutikutiku/kaggle-hubmap/blob/main/src/02_train/train_02.py#L46\" target=\"_blank\">this line of code</a> that why you filtered out images with threshold <code>std = 10</code>?<br>\nThank you!</p>",
      "rawMarkdown": "Congtras and thanks for sharing ideas and codes.\nCan I ask one question about [this line of code](https://github.com/tikutikutiku/kaggle-hubmap/blob/main/src/02_train/train_02.py#L46) that why you filtered out images with threshold `std = 10`?\nThank you!",
      "replies": [
        {
          "id": 1545664,
          "postDate": "2021-10-15T12:49:44.257Z",
          "content": "<p>That line of code is used to remove background only images. I found std can be used for such purpose.</p>",
          "rawMarkdown": "That line of code is used to remove background only images. I found std can be used for such purpose.",
          "votes": 1
        }
      ]
    },
    {
      "id": 1380692,
      "postDate": "2021-07-08T08:37:05.313Z",
      "content": "<p>Congratulations.</p>",
      "rawMarkdown": "Congratulations."
    },
    {
      "id": 1334488,
      "postDate": "2021-06-03T14:40:58.597Z",
      "content": "<p>I don't understand how the hubmap dataset works, how to produce the input image and annotations, Is there any tutorial can help me ?</p>",
      "rawMarkdown": "I don't understand how the hubmap dataset works, how to produce the input image and annotations, Is there any tutorial can help me ?"
    },
    {
      "id": 1334431,
      "postDate": "2021-06-03T13:44:43.203Z",
      "content": "<p>I don't understand this lines of code,would you please explain the meaning of it?<br>\n<code>self.h, self.w = self.data.height, self.data.width</code><br>\n <code>self.input_sz = config['input_resolution']</code><br>\n     <code>self.sz = config['resolution']</code><br>\n <code>self.pad_sz = config['pad_size'] # add to each input tile</code><br>\n    <code>self.pred_sz = self.sz - 2*self.pad_sz</code><br>\n   <code>self.pad_h = self.pred_sz - self.h % self.pred_sz # add to whole slide</code><br>\n    <code>self.pad_w = self.pred_sz - self.w % self.pred_sz # add to whole slide</code><br>\n<code>self.num_h = (self.h + self.pad_h) // self.pred_sz</code><br>\n     <code>self.num_w = (self.w + self.pad_w) // self.pred_sz</code></p>",
      "rawMarkdown": "I don't understand this lines of code,would you please explain the meaning of it?\n`        self.h, self.w = self.data.height, self.data.width`\n `      self.input_sz = config['input_resolution']`\n     `   self.sz = config['resolution']`\n `       self.pad_sz = config['pad_size'] # add to each input tile`\n    `    self.pred_sz = self.sz - 2*self.pad_sz`\n   `     self.pad_h = self.pred_sz - self.h % self.pred_sz # add to whole slide`\n    `    self.pad_w = self.pred_sz - self.w % self.pred_sz # add to whole slide`\n`        self.num_h = (self.h + self.pad_h) // self.pred_sz`\n     `   self.num_w = (self.w + self.pad_w) // self.pred_sz`",
      "replies": [
        {
          "id": 1334484,
          "postDate": "2021-06-03T14:37:18.800Z",
          "content": "<p>I think this is a bit complex (sorry if it is confusing). I used it to implement the trick for avoiding the edge effect. Here I added comments to each line :</p>\n<pre><code># height and width of the slide (no resize)\nself.h, self.w = self.data.height, self.data.width\n\n# input image size for U-net\nself.input_sz = config['input_resolution']\n\n# tile size (no resize)\nself.sz = config['resolution']\n\n# I used a trick to avoid the edge effect and this pad size determines the size of the neglected part\n# (see the self.pred_sz below).\nself.pad_sz = config['pad_size']\n\n# This part is a bit tricky.\n# self.pred_sz is the size used for prediction which is cut from the output (with self.sz) of U-net\n# For example, \n# self.sz = 1024, self.pad_sz = 256, self.input_sz = 320\n# then I first resize 1024x1024 tile into 320x320 and feed it into U-net.\n# The output of U-net is 320x320 and I resize it to 1024x1024.\n# Since the self.pad_sz=256 here, I extract the center part 512x512 (512=1024-2*256) from the 1024x1024.\nself.pred_sz = self.sz - 2*self.pad_sz\n\n# pad size for the slide\n# Since the prediction size is self.pred_sz (not self.sz) here, I used the equation below\nself.pad_h = self.pred_sz - self.h % self.pred_sz\nself.pad_w = self.pred_sz - self.w % self.pred_sz\n\n# number of tiles \nself.num_h = (self.h + self.pad_h) // self.pred_sz\nself.num_w = (self.w + self.pad_w) // self.pred_sz\n</code></pre>",
          "rawMarkdown": "I think this is a bit complex (sorry if it is confusing). I used it to implement the trick for avoiding the edge effect. Here I added comments to each line :\n\n```\n# height and width of the slide (no resize)\nself.h, self.w = self.data.height, self.data.width\n\n# input image size for U-net\nself.input_sz = config['input_resolution']\n\n# tile size (no resize)\nself.sz = config['resolution']\n\n# I used a trick to avoid the edge effect and this pad size determines the size of the neglected part\n# (see the self.pred_sz below).\nself.pad_sz = config['pad_size']\n\n# This part is a bit tricky.\n# self.pred_sz is the size used for prediction which is cut from the output (with self.sz) of U-net\n# For example, \n# self.sz = 1024, self.pad_sz = 256, self.input_sz = 320\n# then I first resize 1024x1024 tile into 320x320 and feed it into U-net.\n# The output of U-net is 320x320 and I resize it to 1024x1024.\n# Since the self.pad_sz=256 here, I extract the center part 512x512 (512=1024-2*256) from the 1024x1024.\nself.pred_sz = self.sz - 2*self.pad_sz\n\n# pad size for the slide\n# Since the prediction size is self.pred_sz (not self.sz) here, I used the equation below\nself.pad_h = self.pred_sz - self.h % self.pred_sz\nself.pad_w = self.pred_sz - self.w % self.pred_sz\n\n# number of tiles \nself.num_h = (self.h + self.pad_h) // self.pred_sz\nself.num_w = (self.w + self.pad_w) // self.pred_sz\n```",
          "votes": 1
        },
        {
          "id": 1335458,
          "postDate": "2021-06-04T08:27:33.117Z",
          "content": "<p>what's the meaning of \"tile\"?</p>",
          "rawMarkdown": "what's the meaning of \"tile\"?"
        },
        {
          "id": 1335466,
          "postDate": "2021-06-04T08:32:44.170Z",
          "content": "<p>tile: a part of the whole image </p>",
          "rawMarkdown": "tile: a part of the whole image "
        },
        {
          "id": 1335482,
          "postDate": "2021-06-04T08:43:39.893Z",
          "content": "<p>Got it ,Thank you , But what's the differences between input size and tile size and pred size?why we use these different sizes for the model?</p>",
          "rawMarkdown": "Got it ,Thank you , But what's the differences between input size and tile size and pred size?why we use these different sizes for the model?"
        },
        {
          "id": 1335496,
          "postDate": "2021-06-04T08:53:45.207Z",
          "content": "<p>I am now getting stuck in the code of class HuBMAPDataset, I can't exactly know the meaning of the code and details.😂</p>",
          "rawMarkdown": "I am now getting stuck in the code of class HuBMAPDataset, I can't exactly know the meaning of the code and details.😂"
        }
      ]
    },
    {
      "id": 1315299,
      "postDate": "2021-05-19T17:38:16.970Z",
      "content": "<p>Good work! Why is ELU used on top of the network?</p>",
      "rawMarkdown": "Good work! Why is ELU used on top of the network?",
      "replies": [
        {
          "id": 1315494,
          "postDate": "2021-05-19T20:51:16.363Z",
          "content": "<p>For all of my experiments, I used the same head architecture which was based on my model in the cloud competition. I didn't do the abbreviation study, but other activation functions should also work.</p>",
          "rawMarkdown": "For all of my experiments, I used the same head architecture which was based on my model in the cloud competition. I didn't do the abbreviation study, but other activation functions should also work.",
          "votes": 1
        }
      ]
    },
    {
      "id": 1306688,
      "postDate": "2021-05-14T02:33:51.863Z",
      "content": "<p>Can you explain your balanced sampler? What is balanced? The number of tiles from each train image or the number of tiles with positive segmentation versus no segmentation?</p>",
      "rawMarkdown": "Can you explain your balanced sampler? What is balanced? The number of tiles from each train image or the number of tiles with positive segmentation versus no segmentation?",
      "replies": [
        {
          "id": 1307184,
          "postDate": "2021-05-14T09:57:02.677Z",
          "content": "<p>Masked area is balanced. Here is an example : </p>\n<pre><code>total # of tiles = 33114\n\n# of masked_tiles = 17292\n# of non_masked_tiles = 15822\n\namong masked tiles\n# of tiles in bin 1 : 7322 (tiles have very small masked area)\n# of tiles in bin 2 : 5547 (tiles have small masked area)\n# of tiles in bin 3 : 2796 (tiles have some masked area)\n# of tiles in bin 4 : 1627 (tiles have large masked area)\n</code></pre>\n<p>I sampled tiles from these bin1,2,3,4 equally.</p>\n<p>EDIT : looks like this<br>\n<img src=\"https://i.postimg.cc/XqxstSbp/balanced-tile-sampling.png\" alt=\"image\"></p>",
          "rawMarkdown": "Masked area is balanced. Here is an example : \n\n```\ntotal # of tiles = 33114\n\n# of masked_tiles = 17292\n# of non_masked_tiles = 15822\n\namong masked tiles\n# of tiles in bin 1 : 7322 (tiles have very small masked area)\n# of tiles in bin 2 : 5547 (tiles have small masked area)\n# of tiles in bin 3 : 2796 (tiles have some masked area)\n# of tiles in bin 4 : 1627 (tiles have large masked area)\n```\nI sampled tiles from these bin1,2,3,4 equally.\n\nEDIT : looks like this\n![image](https://i.postimg.cc/XqxstSbp/balanced-tile-sampling.png)",
          "votes": 8
        },
        {
          "id": 1307724,
          "postDate": "2021-05-14T16:17:11.880Z",
          "content": "<p>Great idea, i never thought to balance mask area, that's smart. </p>\n<p>Balanced samplers work well because we do not know what to expect in private test. (Even public test had a very unexpected image) For me, i balanced the random crops to take an equal number from each train image. (And required that every crop had some mask).</p>",
          "rawMarkdown": "Great idea, i never thought to balance mask area, that's smart. \n\nBalanced samplers work well because we do not know what to expect in private test. (Even public test had a very unexpected image) For me, i balanced the random crops to take an equal number from each train image. (And required that every crop had some mask).",
          "votes": 2
        }
      ]
    },
    {
      "id": 1304930,
      "postDate": "2021-05-13T02:17:14.650Z",
      "content": "<p>Congratulation🎉, and  thanks for sharing your approach.<br>\nI still don’t understand ** the trick for avoiding the edge effect** , can you explain it in detail.</p>",
      "rawMarkdown": "Congratulation🎉, and  thanks for sharing your approach.\nI still don’t understand ** the trick for avoiding the edge effect** , can you explain it in detail.",
      "replies": [
        {
          "id": 1305845,
          "postDate": "2021-05-13T14:05:10.040Z",
          "content": "<p>When we feed a tile into U-net, U-net cannot precisely predict the edge part of the tile compared with the center part of the tile. We can remove this edge part from inference by the trick like this:<br>\n<img src=\"https://i.postimg.cc/ncHpwJZY/avoiding-edge-effect.png\" alt=\"image\"></p>",
          "rawMarkdown": "When we feed a tile into U-net, U-net cannot precisely predict the edge part of the tile compared with the center part of the tile. We can remove this edge part from inference by the trick like this:\n![image](https://i.postimg.cc/ncHpwJZY/avoiding-edge-effect.png)",
          "votes": 2
        },
        {
          "id": 1305920,
          "postDate": "2021-05-13T14:46:12.117Z",
          "content": "<p>Thanks for your answer. :)</p>",
          "rawMarkdown": "Thanks for your answer. :)"
        }
      ]
    },
    {
      "id": 1304070,
      "postDate": "2021-05-12T12:05:46.747Z",
      "content": "<p>Big Congratulations, and thanks for sharing this.<br>\nAm I reading correct: Your loss function is the sum of 4 terms, 2 coming from logits and two from averaged form of logits_deeps, where the latter is weighted with .1 ? </p>",
      "rawMarkdown": "Big Congratulations, and thanks for sharing this.\nAm I reading correct: Your loss function is the sum of 4 terms, 2 coming from logits and two from averaged form of logits_deeps, where the latter is weighted with .1 ? ",
      "replies": [
        {
          "id": 1304097,
          "postDate": "2021-05-12T12:26:38.750Z",
          "content": "<p>My loss function looks like this:</p>\n<pre><code>criterion = nn.BCEWithLogitsLoss().to(device)\ncriterion_clf = nn.BCEWithLogitsLoss().to(device)\n\ndef criterion_lovasz_hinge_non_empty(criterion, logits_deep, y):\n    batch,c,h,w = y.size()\n    y2 = y.view(batch*c,-1)\n    logits_deep2 = logits_deep.view(batch*c,-1)\n\n    y_sum = torch.sum(y2, dim=1)\n    non_empty_idx = (y_sum!=0)\n\n    if non_empty_idx.sum()==0:\n        return torch.tensor(0)\n    else:\n        loss  = criterion(logits_deep2[non_empty_idx], \n                          y2[non_empty_idx])\n        loss += lovasz_hinge(logits_deep2[non_empty_idx].view(-1,h,w), \n                             y2[non_empty_idx].view(-1,h,w))\n        return loss\n\nloss = criterion(logits,y_true)                 \nloss += lovasz_hinge(logits.view(-1,h,w), y_true.view(-1,h,w))\nif deepsupervision:\n    for logits_deep in logits_deeps:\n         loss += 0.1 * criterion_lovasz_hinge_non_empty(criterion, logits_deep, y_true)\nif clfhead:                        \n    loss += criterion_clf(logits_clf.squeeze(-1),y_clf)\n</code></pre>\n<p>lovasz_hinge loss is from <a href=\"https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytorch/lovasz_losses.py\" target=\"_blank\">https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytorch/lovasz_losses.py</a></p>",
          "rawMarkdown": "My loss function looks like this:\n\n```\ncriterion = nn.BCEWithLogitsLoss().to(device)\ncriterion_clf = nn.BCEWithLogitsLoss().to(device)\n\ndef criterion_lovasz_hinge_non_empty(criterion, logits_deep, y):\n    batch,c,h,w = y.size()\n    y2 = y.view(batch*c,-1)\n    logits_deep2 = logits_deep.view(batch*c,-1)\n    \n    y_sum = torch.sum(y2, dim=1)\n    non_empty_idx = (y_sum!=0)\n    \n    if non_empty_idx.sum()==0:\n        return torch.tensor(0)\n    else:\n        loss  = criterion(logits_deep2[non_empty_idx], \n                          y2[non_empty_idx])\n        loss += lovasz_hinge(logits_deep2[non_empty_idx].view(-1,h,w), \n                             y2[non_empty_idx].view(-1,h,w))\n        return loss\n\nloss = criterion(logits,y_true)                 \nloss += lovasz_hinge(logits.view(-1,h,w), y_true.view(-1,h,w))\nif deepsupervision:\n    for logits_deep in logits_deeps:\n         loss += 0.1 * criterion_lovasz_hinge_non_empty(criterion, logits_deep, y_true)\nif clfhead:                        \n    loss += criterion_clf(logits_clf.squeeze(-1),y_clf)\n```\n\nlovasz_hinge loss is from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytorch/lovasz_losses.py",
          "votes": 2
        },
        {
          "id": 1304114,
          "postDate": "2021-05-12T12:38:16.990Z",
          "content": "<p>Thanks Again.</p>",
          "rawMarkdown": "Thanks Again."
        }
      ]
    },
    {
      "id": 1334540,
      "postDate": "2021-06-03T15:25:15.597Z",
      "rawMarkdown": "",
      "isDeleted": true
    },
    {
      "id": 1302727,
      "postDate": "2021-05-11T17:20:07.033Z",
      "rawMarkdown": "",
      "isDeleted": true
    }
  ],
  "comments": [
    {
      "id": 1495856,
      "author_name": "Fuco",
      "author_url": "",
      "post_date": "2021-08-29T20:48:37.403000",
      "content": "<p>I learn a lot from you. Great work =))</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1305231,
      "author_name": "He",
      "author_url": "",
      "post_date": "2021-05-13T07:03:28.003000",
      "content": "<p>Congratulations，May I ask a small question, Dose hypercolumns or CBAM really help your CV or LB？In most of the segmentation competitions I participated in, they basically did not work, and sometimes even hurt CV and LB.  I’m not sure if it’s because of my code problem or their effect is really not obvious. Thanks for your sharing!</p>",
      "votes": 1,
      "replies": [
        {
          "id": 1305462,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-13T09:42:37.883000",
          "content": "<p>I used hypercolumns and CBAM for all of my experiments in this competition. So I didn't do the abbreviation study. From my experience of the cloud competition, these modules helped.<br>\nI'm not sure if these are always helpful. Maybe the devil's in the details here…</p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1305529,
          "author_name": "He",
          "author_url": "",
          "post_date": "2021-05-13T10:42:14.727000",
          "content": "<p>I get it, thank you for your generous sharing! 😁</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1304489,
      "author_name": "Mikhail Kulyabin",
      "author_url": "",
      "post_date": "2021-05-12T16:43:02.747000",
      "content": "<p>Very nice job, congratulations!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1303803,
      "author_name": "Salim Khazem",
      "author_url": "",
      "post_date": "2021-05-12T08:51:37.870000",
      "content": "<p>Congratulation, great work <a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> , <br>\nThanks for sharing your approach </p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1303798,
      "author_name": "Markin",
      "author_url": "",
      "post_date": "2021-05-12T08:45:07.043000",
      "content": "<p>Congtras and thanks for sharing yoru idea, Amazing job with 25 days old submission!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1303333,
      "author_name": "Maxim Vedenev",
      "author_url": "",
      "post_date": "2021-05-12T02:49:24.247000",
      "content": "<p>Congratulations. <br>\nWhat hardware did you use? How long have you been developed your model?</p>",
      "votes": 1,
      "replies": [
        {
          "id": 1844511,
          "author_name": "Tan Phan",
          "author_url": "",
          "post_date": "2022-07-05T15:39:36.373000",
          "content": "<p><a href=\"https://github.com/tikutikutiku/kaggle-hubmap\" target=\"_blank\">this link</a> if you have not ever found the answer. <a href=\"https://www.kaggle.com/vedenev\" target=\"_blank\">@vedenev</a> </p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1303116,
      "author_name": "Tensor Girl",
      "author_url": "",
      "post_date": "2021-05-11T23:16:36.547000",
      "content": "<p><a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> Congratulation on First Place and Thanks for sharing the approach</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1302708,
      "author_name": "Shujun",
      "author_url": "",
      "post_date": "2021-05-11T17:11:52.210000",
      "content": "<p>Congrats! Interesting to see we used the same trick. <a href=\"https://www.kaggle.com/iafoss\" target=\"_blank\">@iafoss</a> also told me he had subs that used the same method and scored really well on private (0.950), so it really does seem to generalize well. </p>",
      "votes": 1,
      "replies": [
        {
          "id": 1303039,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-11T21:34:24.300000",
          "content": "<p>Thanks! That's good to know. The trick can be used in any segmentation problem !</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1302607,
      "author_name": "MPWARE",
      "author_url": "",
      "post_date": "2021-05-11T16:16:10.817000",
      "content": "<p>Great Tom! Congratulations. Happy for you.</p>",
      "votes": 1,
      "replies": [
        {
          "id": 1303032,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-11T21:29:59.890000",
          "content": "<p>Thank you <a href=\"https://www.kaggle.com/mpware\" target=\"_blank\">@mpware</a> !</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1302386,
      "author_name": "andras",
      "author_url": "",
      "post_date": "2021-05-11T14:15:28.970000",
      "content": "<p>congrats!!! You can use the prize money to buy some serious graphic cards (prices skyrocketed). Interesting you should mention public dataset. I used it to pre-train my models. I saw a minimal (i.e. 0.001 to 0.003) improvement over using the backbone directly.</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1302301,
      "author_name": "lhagiimn",
      "author_url": "",
      "post_date": "2021-05-11T13:36:03.490000",
      "content": "<p>Congratulations on win! I saw your last submission a month ago, so I thought you forget this competition and don’t know your win :))</p>",
      "votes": 1,
      "replies": [
        {
          "id": 1302303,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-11T13:38:06.760000",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/lhagiimn\" target=\"_blank\">@lhagiimn</a> ! I almost forgot because I was busy on HPA comp. :)</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1304646,
      "author_name": "Chris Deotte",
      "author_url": "",
      "post_date": "2021-05-12T18:52:06.370000",
      "content": "<p>Congratulations <a href=\"https://www.kaggle.com/tikutiku\" target=\"_blank\">@tikutiku</a> . Your last submission was 1 month ago. Haha, that's great!</p>",
      "votes": 2,
      "replies": [
        {
          "id": 1304795,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-12T22:12:47.553000",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/cdeotte\" target=\"_blank\">@cdeotte</a> ! I entered this competition early. So, in the other point of view, my last submission was made 5 months after I joined this competition.   </p>",
          "votes": 3,
          "replies": []
        }
      ]
    },
    {
      "id": 1302294,
      "author_name": "Theo Viel",
      "author_url": "",
      "post_date": "2021-05-11T13:32:57.257000",
      "content": "<p>Congratz !<br>\nNeedless to say I was surprised to see you on top with a 25+ days old submission :)</p>",
      "votes": 2,
      "replies": [
        {
          "id": 1302296,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-11T13:34:07.653000",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/theoviel\" target=\"_blank\">@theoviel</a> !</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1302398,
          "author_name": "Theo Viel",
          "author_url": "",
          "post_date": "2021-05-11T14:24:16.633000",
          "content": "<p>Also, that's your second 1st place and every time I was one spot away from being it the money. <br>\nI'm not sure what to think about that aha</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1302406,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-11T14:30:20.167000",
          "content": "<p>my second time to have big surprise. Let's see the next time :)</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1869804,
      "author_name": "Adebayo A. Adeniyi",
      "author_url": "",
      "post_date": "2022-07-25T04:11:33.670000",
      "content": "<p>Thanks for sharing this, many of these things are just blowing up my mind. I just picked an interest in coding and I love to grow up fast in it. I will appreciate if anyone here can add me to his group were I can learna nd grow fast. Thanks</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1582293,
      "author_name": "Nilupa Rupasinghe",
      "author_url": "",
      "post_date": "2021-11-14T18:18:50.247000",
      "content": "<p>Great work and thank you for sharing your approach.</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1544144,
      "author_name": "DylanHWang",
      "author_url": "",
      "post_date": "2021-10-14T05:25:19.500000",
      "content": "<p>Congtras and thanks for sharing ideas and codes.<br>\nCan I ask one question about <a href=\"https://github.com/tikutikutiku/kaggle-hubmap/blob/main/src/02_train/train_02.py#L46\" target=\"_blank\">this line of code</a> that why you filtered out images with threshold <code>std = 10</code>?<br>\nThank you!</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1545664,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-10-15T12:49:44.257000",
          "content": "<p>That line of code is used to remove background only images. I found std can be used for such purpose.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1380692,
      "author_name": "Mridul Paul",
      "author_url": "",
      "post_date": "2021-07-08T08:37:05.313000",
      "content": "<p>Congratulations.</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1334488,
      "author_name": "Crophone",
      "author_url": "",
      "post_date": "2021-06-03T14:40:58.597000",
      "content": "<p>I don't understand how the hubmap dataset works, how to produce the input image and annotations, Is there any tutorial can help me ?</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1334431,
      "author_name": "Crophone",
      "author_url": "",
      "post_date": "2021-06-03T13:44:43.203000",
      "content": "<p>I don't understand this lines of code,would you please explain the meaning of it?<br>\n<code>self.h, self.w = self.data.height, self.data.width</code><br>\n <code>self.input_sz = config['input_resolution']</code><br>\n     <code>self.sz = config['resolution']</code><br>\n <code>self.pad_sz = config['pad_size'] # add to each input tile</code><br>\n    <code>self.pred_sz = self.sz - 2*self.pad_sz</code><br>\n   <code>self.pad_h = self.pred_sz - self.h % self.pred_sz # add to whole slide</code><br>\n    <code>self.pad_w = self.pred_sz - self.w % self.pred_sz # add to whole slide</code><br>\n<code>self.num_h = (self.h + self.pad_h) // self.pred_sz</code><br>\n     <code>self.num_w = (self.w + self.pad_w) // self.pred_sz</code></p>",
      "votes": 0,
      "replies": [
        {
          "id": 1334484,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-06-03T14:37:18.800000",
          "content": "<p>I think this is a bit complex (sorry if it is confusing). I used it to implement the trick for avoiding the edge effect. Here I added comments to each line :</p>\n<pre><code># height and width of the slide (no resize)\nself.h, self.w = self.data.height, self.data.width\n\n# input image size for U-net\nself.input_sz = config['input_resolution']\n\n# tile size (no resize)\nself.sz = config['resolution']\n\n# I used a trick to avoid the edge effect and this pad size determines the size of the neglected part\n# (see the self.pred_sz below).\nself.pad_sz = config['pad_size']\n\n# This part is a bit tricky.\n# self.pred_sz is the size used for prediction which is cut from the output (with self.sz) of U-net\n# For example, \n# self.sz = 1024, self.pad_sz = 256, self.input_sz = 320\n# then I first resize 1024x1024 tile into 320x320 and feed it into U-net.\n# The output of U-net is 320x320 and I resize it to 1024x1024.\n# Since the self.pad_sz=256 here, I extract the center part 512x512 (512=1024-2*256) from the 1024x1024.\nself.pred_sz = self.sz - 2*self.pad_sz\n\n# pad size for the slide\n# Since the prediction size is self.pred_sz (not self.sz) here, I used the equation below\nself.pad_h = self.pred_sz - self.h % self.pred_sz\nself.pad_w = self.pred_sz - self.w % self.pred_sz\n\n# number of tiles \nself.num_h = (self.h + self.pad_h) // self.pred_sz\nself.num_w = (self.w + self.pad_w) // self.pred_sz\n</code></pre>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1335458,
          "author_name": "Crophone",
          "author_url": "",
          "post_date": "2021-06-04T08:27:33.117000",
          "content": "<p>what's the meaning of \"tile\"?</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1335466,
          "author_name": "Ioannis M",
          "author_url": "",
          "post_date": "2021-06-04T08:32:44.170000",
          "content": "<p>tile: a part of the whole image </p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1335482,
          "author_name": "Crophone",
          "author_url": "",
          "post_date": "2021-06-04T08:43:39.893000",
          "content": "<p>Got it ,Thank you , But what's the differences between input size and tile size and pred size?why we use these different sizes for the model?</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1335496,
          "author_name": "Crophone",
          "author_url": "",
          "post_date": "2021-06-04T08:53:45.207000",
          "content": "<p>I am now getting stuck in the code of class HuBMAPDataset, I can't exactly know the meaning of the code and details.😂</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1315299,
      "author_name": "ZavodRobotov",
      "author_url": "",
      "post_date": "2021-05-19T17:38:16.970000",
      "content": "<p>Good work! Why is ELU used on top of the network?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1315494,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-19T20:51:16.363000",
          "content": "<p>For all of my experiments, I used the same head architecture which was based on my model in the cloud competition. I didn't do the abbreviation study, but other activation functions should also work.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1306688,
      "author_name": "Chris Deotte",
      "author_url": "",
      "post_date": "2021-05-14T02:33:51.863000",
      "content": "<p>Can you explain your balanced sampler? What is balanced? The number of tiles from each train image or the number of tiles with positive segmentation versus no segmentation?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1307184,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-14T09:57:02.677000",
          "content": "<p>Masked area is balanced. Here is an example : </p>\n<pre><code>total # of tiles = 33114\n\n# of masked_tiles = 17292\n# of non_masked_tiles = 15822\n\namong masked tiles\n# of tiles in bin 1 : 7322 (tiles have very small masked area)\n# of tiles in bin 2 : 5547 (tiles have small masked area)\n# of tiles in bin 3 : 2796 (tiles have some masked area)\n# of tiles in bin 4 : 1627 (tiles have large masked area)\n</code></pre>\n<p>I sampled tiles from these bin1,2,3,4 equally.</p>\n<p>EDIT : looks like this<br>\n<img src=\"https://i.postimg.cc/XqxstSbp/balanced-tile-sampling.png\" alt=\"image\"></p>",
          "votes": 8,
          "replies": []
        },
        {
          "id": 1307724,
          "author_name": "Chris Deotte",
          "author_url": "",
          "post_date": "2021-05-14T16:17:11.880000",
          "content": "<p>Great idea, i never thought to balance mask area, that's smart. </p>\n<p>Balanced samplers work well because we do not know what to expect in private test. (Even public test had a very unexpected image) For me, i balanced the random crops to take an equal number from each train image. (And required that every crop had some mask).</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 1304930,
      "author_name": "Chunyu Wei",
      "author_url": "",
      "post_date": "2021-05-13T02:17:14.650000",
      "content": "<p>Congratulation🎉, and  thanks for sharing your approach.<br>\nI still don’t understand ** the trick for avoiding the edge effect** , can you explain it in detail.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1305845,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-13T14:05:10.040000",
          "content": "<p>When we feed a tile into U-net, U-net cannot precisely predict the edge part of the tile compared with the center part of the tile. We can remove this edge part from inference by the trick like this:<br>\n<img src=\"https://i.postimg.cc/ncHpwJZY/avoiding-edge-effect.png\" alt=\"image\"></p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1305920,
          "author_name": "Chunyu Wei",
          "author_url": "",
          "post_date": "2021-05-13T14:46:12.117000",
          "content": "<p>Thanks for your answer. :)</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1304070,
      "author_name": "GUNER",
      "author_url": "",
      "post_date": "2021-05-12T12:05:46.747000",
      "content": "<p>Big Congratulations, and thanks for sharing this.<br>\nAm I reading correct: Your loss function is the sum of 4 terms, 2 coming from logits and two from averaged form of logits_deeps, where the latter is weighted with .1 ? </p>",
      "votes": 0,
      "replies": [
        {
          "id": 1304097,
          "author_name": "Tom",
          "author_url": "",
          "post_date": "2021-05-12T12:26:38.750000",
          "content": "<p>My loss function looks like this:</p>\n<pre><code>criterion = nn.BCEWithLogitsLoss().to(device)\ncriterion_clf = nn.BCEWithLogitsLoss().to(device)\n\ndef criterion_lovasz_hinge_non_empty(criterion, logits_deep, y):\n    batch,c,h,w = y.size()\n    y2 = y.view(batch*c,-1)\n    logits_deep2 = logits_deep.view(batch*c,-1)\n\n    y_sum = torch.sum(y2, dim=1)\n    non_empty_idx = (y_sum!=0)\n\n    if non_empty_idx.sum()==0:\n        return torch.tensor(0)\n    else:\n        loss  = criterion(logits_deep2[non_empty_idx], \n                          y2[non_empty_idx])\n        loss += lovasz_hinge(logits_deep2[non_empty_idx].view(-1,h,w), \n                             y2[non_empty_idx].view(-1,h,w))\n        return loss\n\nloss = criterion(logits,y_true)                 \nloss += lovasz_hinge(logits.view(-1,h,w), y_true.view(-1,h,w))\nif deepsupervision:\n    for logits_deep in logits_deeps:\n         loss += 0.1 * criterion_lovasz_hinge_non_empty(criterion, logits_deep, y_true)\nif clfhead:                        \n    loss += criterion_clf(logits_clf.squeeze(-1),y_clf)\n</code></pre>\n<p>lovasz_hinge loss is from <a href=\"https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytorch/lovasz_losses.py\" target=\"_blank\">https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytorch/lovasz_losses.py</a></p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1304114,
          "author_name": "GUNER",
          "author_url": "",
          "post_date": "2021-05-12T12:38:16.990000",
          "content": "<p>Thanks Again.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1334540,
      "author_name": "",
      "author_url": "",
      "post_date": "2021-06-03T15:25:15.597000",
      "content": "",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1302727,
      "author_name": "",
      "author_url": "",
      "post_date": "2021-05-11T17:20:07.033000",
      "content": "",
      "votes": 0,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "1302287": "First of all, congratulations to the winners and thank you Kaggle and the host team for this tough competition! I entered this competition early and spent a lot of time until the data update. I could not keep my motivation for hand-labeling, so I left a month ago. Luckily I got the 1st place, so I would like to share my approach.\n\n\n# Summary of my approach\n+ single model Unet se_resnext101_32x4d (4folds)\n+ some techniques from previous segmentation competitions (mainly from the cloud comp.)  \n+ balanced tile sampling for training (EDIT : masked area is balanced)\n+ pseudo-label for the public test data and external data\n+ a trick for avoiding the edge effect\n+ My pipeline was developed with the old dataset (i.e., before the data update).\n\n\n# 1. Data preparation\n make 1024x104 tiles + shifted 1024x1024 tiles (I shifted the tiles by (512,512))\n \n# 2. Validation\n I selected the validation data so that the same patient number is in the same group\n ```\nval_patient_numbers_list = [\n    [63921], # fold0\n    [68250], # fold1\n    [65631], # fold2\n    [67177], # fold3\n ]\n```\n \n# 3. Balanced tile sampling for training\nFirst I binned the tile data with respect to the masked area (number of bins = 4 for masked tiles). Then I apply the following procedure for balanced sampling.\n\n ```\nn_sample = trn_df['is_masked'].value_counts().min()\ntrn_df_0 = trn_df[trn_df['is_masked']==False].sample(n_sample, replace=True)\ntrn_df_1 = trn_df[trn_df['is_masked']==True].sample(n_sample, replace=True)\nn_bin = int(trn_df_1['binned'].value_counts().mean())\ntrn_df_list = []\nfor bin_size in trn_df_1['binned'].unique():\n    trn_df_list.append(trn_df_1[trn_df_1['binned']==bin_size].sample(n_bin, replace=True))\ntrn_df_1 = pd.concat(trn_df_list, axis=0)\ntrn_df_balanced = pd.concat([trn_df_1, trn_df_0], axis=0).reset_index(drop=True)\n```\n \n \n# 4. Model\n U-Net SeResNext101 + CBAM + hypercolumns + deepsupervision\n In my case, larger model gave better CV and LB.\n I resized 1024x1024 to 320x320 for input tiles.\n here is the code snippet:\n \n```\nclass CenterBlock(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super().__init__()\n        self.conv = conv3x3(in_channel, out_channel).apply(init_weight)\n        \n    def forward(self, inputs):\n        x = self.conv(inputs)\n        return x\n\nclass DecodeBlock(nn.Module):\n    def __init__(self, in_channel, out_channel, upsample):\n        super().__init__()\n        self.bn1 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.upsample = nn.Sequential()\n        if upsample:\n            self.upsample.add_module('upsample',nn.Upsample(scale_factor=2, mode='nearest'))\n        self.conv3x3_1 = conv3x3(in_channel, in_channel).apply(init_weight)\n        self.bn2 = nn.BatchNorm2d(in_channel).apply(init_weight)\n        self.conv3x3_2 = conv3x3(in_channel, out_channel).apply(init_weight)\n        self.cbam = CBAM(out_channel, reduction=16)\n        self.conv1x1   = conv1x1(in_channel, out_channel).apply(init_weight)\n        \n    def forward(self, inputs):\n        x  = F.relu(self.bn1(inputs))\n        x  = self.upsample(x)\n        x  = self.conv3x3_1(x)\n        x  = self.conv3x3_2(F.relu(self.bn2(x)))\n        x  = self.cbam(x)\n        x += self.conv1x1(self.upsample(inputs)) #shortcut\n        return x\n        \nclass UNET_SERESNEXT101(nn.Module):\n    def __init__(self, resolution, deepsupervision, clfhead, load_weights=True):\n        super().__init__()\n        h,w = resolution\n        self.deepsupervision = deepsupervision\n        self.clfhead = clfhead\n        \n        #encoder\n        model_name = 'se_resnext101_32x4d'\n        seresnext101 = pretrainedmodels.__dict__[model_name](pretrained=None)\n        if load_weights:\n            seresnext101.load_state_dict(torch.load(f'{model_name}.pth'))\n        \n        self.encoder0 = nn.Sequential(\n            seresnext101.layer0.conv1, #(*,3,h,w)->(*,64,h/2,w/2)\n            seresnext101.layer0.bn1,\n            seresnext101.layer0.relu1,\n        )\n        self.encoder1 = nn.Sequential(\n            seresnext101.layer0.pool, #->(*,64,h/4,w/4)\n            seresnext101.layer1 #->(*,256,h/4,w/4)\n        )\n        self.encoder2 = seresnext101.layer2 #->(*,512,h/8,w/8)\n        self.encoder3 = seresnext101.layer3 #->(*,1024,h/16,w/16)\n        self.encoder4 = seresnext101.layer4 #->(*,2048,h/32,w/32)\n        \n        #center\n        self.center  = CenterBlock(2048,512) #->(*,512,h/32,w/32)\n        \n        #decoder\n        self.decoder4 = DecodeBlock(512+2048,64,upsample=True) #->(*,64,h/16,w/16)\n        self.decoder3 = DecodeBlock(64+1024,64, upsample=True) #->(*,64,h/8,w/8)\n        self.decoder2 = DecodeBlock(64+512,64,  upsample=True) #->(*,64,h/4,w/4) \n        self.decoder1 = DecodeBlock(64+256,64,  upsample=True) #->(*,64,h/2,w/2) \n        self.decoder0 = DecodeBlock(64,64, upsample=True) #->(*,64,h,w) \n        \n        #upsample\n        self.upsample4 = nn.Upsample(scale_factor=16, mode='bilinear', align_corners=True)\n        self.upsample3 = nn.Upsample(scale_factor=8, mode='bilinear', align_corners=True)\n        self.upsample2 = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=True)\n        self.upsample1 = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        \n        #deep supervision\n        self.deep4 = conv1x1(64,1).apply(init_weight)\n        self.deep3 = conv1x1(64,1).apply(init_weight)\n        self.deep2 = conv1x1(64,1).apply(init_weight)\n        self.deep1 = conv1x1(64,1).apply(init_weight)\n        \n        #final conv\n        self.final_conv = nn.Sequential(\n            conv3x3(320,64).apply(init_weight),\n            nn.ELU(True),\n            conv1x1(64,1).apply(init_weight)\n        )\n        \n        #clf head\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.clf = nn.Sequential(\n            nn.BatchNorm1d(2048).apply(init_weight),\n            nn.Linear(2048,512).apply(init_weight),\n            nn.ELU(True),\n            nn.BatchNorm1d(512).apply(init_weight),\n            nn.Linear(512,1).apply(init_weight)\n        )\n        \n    def forward(self, inputs):\n        #encoder\n        x0 = self.encoder0(inputs) #->(*,64,h/2,w/2)\n        x1 = self.encoder1(x0) #->(*,256,h/4,w/4)\n        x2 = self.encoder2(x1) #->(*,512,h/8,w/8)\n        x3 = self.encoder3(x2) #->(*,1024,h/16,w/16)\n        x4 = self.encoder4(x3) #->(*,2048,h/32,w/32)\n        \n        #center\n        y5 = self.center(x4) #->(*,320,h/32,w/32)\n        \n        #decoder\n        y4 = self.decoder4(torch.cat([x4,y5], dim=1)) #->(*,64,h/16,w/16)\n        y3 = self.decoder3(torch.cat([x3,y4], dim=1)) #->(*,64,h/8,w/8)\n        y2 = self.decoder2(torch.cat([x2,y3], dim=1)) #->(*,64,h/4,w/4)\n        y1 = self.decoder1(torch.cat([x1,y2], dim=1)) #->(*,64,h/2,w/2) \n        y0 = self.decoder0(y1) #->(*,64,h,w)\n        \n        #hypercolumns\n        y4 = self.upsample4(y4) #->(*,64,h,w)\n        y3 = self.upsample3(y3) #->(*,64,h,w)\n        y2 = self.upsample2(y2) #->(*,64,h,w)\n        y1 = self.upsample1(y1) #->(*,64,h,w)\n        hypercol = torch.cat([y0,y1,y2,y3,y4], dim=1)\n        \n        #final conv\n        logits = self.final_conv(hypercol) #->(*,1,h,w)\n        \n        #clf head\n        logits_clf = self.clf(self.avgpool(x4).squeeze(-1).squeeze(-1)) #->(*,1)\n        \n        if self.clfhead:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps, logits_clf\n            else:\n                return logits, logits_clf\n        else:\n            if self.deepsupervision:\n                s4 = self.deep4(y4)\n                s3 = self.deep3(y3)\n                s2 = self.deep2(y2)\n                s1 = self.deep1(y1)\n                logits_deeps = [s4,s3,s2,s1]\n                return logits, logits_deeps\n            else:\n                return logits\n```\n                \n\n# 5. Loss\n I used bce loss + lovasz-hinge loss, on top oh that I used deep supervision with bce loss + lovasz-hinge loss (for only non-empty masks) multiplied by 0.1. For classification head, I used bce loss\n \n \n# 6. External data and Pseudo-label\n I generated pseudo-labels for (EDIT) train data, public test data, the hubmap-portal (https://portal.hubmapconsortium.org/search?entity_type[0]=Dataset)\n and dataset_a_dib (https://data.mendeley.com/datasets/k7nvtgn2x6/3). For the data d488c759a, I used Carno Zhao's pseudo-label (thanks @carnozhao !) for one of my final model and my model's pseudo-label for the other one. I checked my private scores and found that these models' performances are not so different. But my 1st place model is the former one with Carno Zhao's pseudo-label. I think it's not the hand-labeling but the indirectly ensemble effect that boosted my score since my model is single and diversity should contribute.\n \n\n# 7. A trick for inference\n I found that avoiding edge effect consistently boosts CV and LB. My trick is to use only the center part of tiles for prediction (I notice that 3rd place solution by @shujun717 used the same idea). I found that using smaller part as prediction gave better CV and LB, but it is time-consuming. So I end up with using 512x512 center part from original 1024x1024 as prediction. I needed x4 time for inference, but using classification head prediction was helpful to save inference time.\n\n# 8. LB scores\n public LB=0.936 / private LB=0.951\n \n \n# 9. Some thoughts\n I used what I learned from the previous competitions and I used the same model architecture for most of my experiments. This saved my time and I focused on the data preparation and how to avoid the edge effect. I think I did something different from the other competitors but I'm not sure these were the keys in the final position since I've not experimented a lot for the new dataset.\n\nEDIT\ntraining code : https://github.com/tikutikutiku\ninference code : https://www.kaggle.com/tikutiku/hubmap-tilespadded-inference-v2?scriptVersionId=59475269",
    "1495856": "I learn a lot from you. Great work =))",
    "1305231": "Congratulations，May I ask a small question, Dose hypercolumns or CBAM really help your CV or LB？In most of the segmentation competitions I participated in, they basically did not work, and sometimes even hurt CV and LB.  I’m not sure if it’s because of my code problem or their effect is really not obvious. Thanks for your sharing!",
    "1304489": "Very nice job, congratulations!",
    "1303803": "Congratulation, great work @tikutiku , \nThanks for sharing your approach ",
    "1303798": "Congtras and thanks for sharing yoru idea, Amazing job with 25 days old submission!",
    "1303333": "Congratulations. \nWhat hardware did you use? How long have you been developed your model?",
    "1303116": "@tikutiku Congratulation on First Place and Thanks for sharing the approach",
    "1302708": "Congrats! Interesting to see we used the same trick. @iafoss also told me he had subs that used the same method and scored really well on private (0.950), so it really does seem to generalize well. ",
    "1302607": "Great Tom! Congratulations. Happy for you.",
    "1302386": "congrats!!! You can use the prize money to buy some serious graphic cards (prices skyrocketed). Interesting you should mention public dataset. I used it to pre-train my models. I saw a minimal (i.e. 0.001 to 0.003) improvement over using the backbone directly.",
    "1302301": "Congratulations on win! I saw your last submission a month ago, so I thought you forget this competition and don’t know your win :))",
    "1304646": "Congratulations @tikutiku . Your last submission was 1 month ago. Haha, that's great!",
    "1302294": "Congratz !\nNeedless to say I was surprised to see you on top with a 25+ days old submission :)",
    "1869804": "Thanks for sharing this, many of these things are just blowing up my mind. I just picked an interest in coding and I love to grow up fast in it. I will appreciate if anyone here can add me to his group were I can learna nd grow fast. Thanks",
    "1582293": "Great work and thank you for sharing your approach.",
    "1544144": "Congtras and thanks for sharing ideas and codes.\nCan I ask one question about [this line of code](https://github.com/tikutikutiku/kaggle-hubmap/blob/main/src/02_train/train_02.py#L46) that why you filtered out images with threshold `std = 10`?\nThank you!",
    "1380692": "Congratulations.",
    "1334488": "I don't understand how the hubmap dataset works, how to produce the input image and annotations, Is there any tutorial can help me ?",
    "1334431": "I don't understand this lines of code,would you please explain the meaning of it?\n`        self.h, self.w = self.data.height, self.data.width`\n `      self.input_sz = config['input_resolution']`\n     `   self.sz = config['resolution']`\n `       self.pad_sz = config['pad_size'] # add to each input tile`\n    `    self.pred_sz = self.sz - 2*self.pad_sz`\n   `     self.pad_h = self.pred_sz - self.h % self.pred_sz # add to whole slide`\n    `    self.pad_w = self.pred_sz - self.w % self.pred_sz # add to whole slide`\n`        self.num_h = (self.h + self.pad_h) // self.pred_sz`\n     `   self.num_w = (self.w + self.pad_w) // self.pred_sz`",
    "1315299": "Good work! Why is ELU used on top of the network?",
    "1306688": "Can you explain your balanced sampler? What is balanced? The number of tiles from each train image or the number of tiles with positive segmentation versus no segmentation?",
    "1304930": "Congratulation🎉, and  thanks for sharing your approach.\nI still don’t understand ** the trick for avoiding the edge effect** , can you explain it in detail.",
    "1304070": "Big Congratulations, and thanks for sharing this.\nAm I reading correct: Your loss function is the sum of 4 terms, 2 coming from logits and two from averaged form of logits_deeps, where the latter is weighted with .1 ? ",
    "1334540": "",
    "1302727": ""
  }
}