{
  "id": 118262,
  "title": "10th place solution",
  "url": "/competitions/understanding_cloud_organization/writeups/toru-ito-10th-place-solution",
  "author_name": "",
  "post_date": "2019-11-29T14:48:15.273Z",
  "votes": 36,
  "comment_count": 15,
  "views": 0,
  "content": "<p>Congratulations to all winners!\nHere is my solution (Public 0.67376, Private 0.66765).</p>\n\n<h2>Pre processing:</h2>\n\n<ul>\n<li>resize image size to (320, 512)</li>\n<li>exclude bad images (removed 21 images)</li>\n</ul>\n\n<h2>Augmentations:</h2>\n\n<p>I used <a href=\"https://github.com/albu/albumentations\">albumentations</a>.\n- HorizontalFlip, VerticalFlip\n- ShiftScaleRotate, GridDistortion\n- Blur, MedianBlur, GaussianBlur\n- CLAHE, RandomBrightnessContrast, HueSaturationValue, IAASharpen</p>\n\n<h2>Model:</h2>\n\n<p>I used <a href=\"https://github.com/qubvel/segmentation_models.pytorch\">segmentation_models.pytorch</a>,  <a href=\"https://github.com/cadene/pretrained-models.pytorch\">pretrained-models.pytorch</a>,  <a href=\"https://github.com/lukemelas/EfficientNet-PyTorch\">EfficientNet-PyTorch</a>.</p>\n\nModel-1:\n\n<ul>\n<li>densenet169 Unet with classification</li>\n<li>image size : 320x480</li>\n</ul>\n\nModel-2:\n\n<ul>\n<li>efficientnet-b4 FPN with classification</li>\n<li>image size : 320x480</li>\n</ul>\n\n<h2>Optimizer:</h2>\n\n<ul>\n<li><a href=\"https://github.com/LiyuanLucasLiu/RAdam\">RAdam</a></li>\n</ul>\n\n<h2>Loss:</h2>\n\nPre training:\n\n<ul>\n<li>segmentation: BCE + Dice</li>\n<li>classification: FocalLoss</li>\n</ul>\n\nMain training:\n\n<ul>\n<li>(classification)( FocalLoss * 0.5 + BCEWithLogits * 0.5 ) * 0.05 + (segmentation)( BCE + Dice ) * 0.95</li>\n</ul>\n\n<h2>Ensemble:</h2>\n\n<ul>\n<li>simple average of the 2 models(x 4 = total 8 models)</li>\n</ul>\n\n<h2>Post processing:</h2>\n\n<ul>\n<li>TTA : None, h-flip, v-flip, h-flip and v-flip</li>\n<li>Threshold : I use <a href=\"https://github.com/optuna/optuna\">optuna</a> to find the optimal value from the cv score. </li>\n</ul>\n\n<h2>GPU:</h2>\n\n<ul>\n<li>RTX2080Ti x 1</li>\n</ul>",
  "messages": [
    {
      "id": "677644",
      "postDate": "11/20/2019 12:46:53",
      "content": "<p>Congratulations to all winners!\nHere is my solution (Public 0.67376, Private 0.66765).</p>\n\n<h2>Pre processing:</h2>\n\n<ul>\n<li>resize image size to (320, 512)</li>\n<li>exclude bad images (removed 21 images)</li>\n</ul>\n\n<h2>Augmentations:</h2>\n\n<p>I used <a href=\"https://github.com/albu/albumentations\">albumentations</a>.\n- HorizontalFlip, VerticalFlip\n- ShiftScaleRotate, GridDistortion\n- Blur, MedianBlur, GaussianBlur\n- CLAHE, RandomBrightnessContrast, HueSaturationValue, IAASharpen</p>\n\n<h2>Model:</h2>\n\n<p>I used <a href=\"https://github.com/qubvel/segmentation_models.pytorch\">segmentation_models.pytorch</a>,  <a href=\"https://github.com/cadene/pretrained-models.pytorch\">pretrained-models.pytorch</a>,  <a href=\"https://github.com/lukemelas/EfficientNet-PyTorch\">EfficientNet-PyTorch</a>.</p>\n\nModel-1:\n\n<ul>\n<li>densenet169 Unet with classification</li>\n<li>image size : 320x480</li>\n</ul>\n\nModel-2:\n\n<ul>\n<li>efficientnet-b4 FPN with classification</li>\n<li>image size : 320x480</li>\n</ul>\n\n<h2>Optimizer:</h2>\n\n<ul>\n<li><a href=\"https://github.com/LiyuanLucasLiu/RAdam\">RAdam</a></li>\n</ul>\n\n<h2>Loss:</h2>\n\nPre training:\n\n<ul>\n<li>segmentation: BCE + Dice</li>\n<li>classification: FocalLoss</li>\n</ul>\n\nMain training:\n\n<ul>\n<li>(classification)( FocalLoss * 0.5 + BCEWithLogits * 0.5 ) * 0.05 + (segmentation)( BCE + Dice ) * 0.95</li>\n</ul>\n\n<h2>Ensemble:</h2>\n\n<ul>\n<li>simple average of the 2 models(x 4 = total 8 models)</li>\n</ul>\n\n<h2>Post processing:</h2>\n\n<ul>\n<li>TTA : None, h-flip, v-flip, h-flip and v-flip</li>\n<li>Threshold : I use <a href=\"https://github.com/optuna/optuna\">optuna</a> to find the optimal value from the cv score. </li>\n</ul>\n\n<h2>GPU:</h2>\n\n<ul>\n<li>RTX2080Ti x 1</li>\n</ul>",
      "rawMarkdown": "Congratulations to all winners!\nHere is my solution (Public 0.67376, Private 0.66765).\n\n## Pre processing:\n- resize image size to (320, 512)\n- exclude bad images (removed 21 images)\n\n## Augmentations:\nI used [albumentations](https://github.com/albu/albumentations).\n- HorizontalFlip, VerticalFlip\n- ShiftScaleRotate, GridDistortion\n- Blur, MedianBlur, GaussianBlur\n- CLAHE, RandomBrightnessContrast, HueSaturationValue, IAASharpen\n   \n## Model:\nI used [segmentation_models.pytorch](https://github.com/qubvel/segmentation_models.pytorch),  [pretrained-models.pytorch](https://github.com/cadene/pretrained-models.pytorch),  [EfficientNet-PyTorch](https://github.com/lukemelas/EfficientNet-PyTorch).\n\n#### Model-1:\n- densenet169 Unet with classification\n- image size : 320x480\n\n#### Model-2:\n- efficientnet-b4 FPN with classification\n- image size : 320x480\n   \n## Optimizer:\n- [RAdam](https://github.com/LiyuanLucasLiu/RAdam)\n   \n## Loss:\n#### Pre training:\n- segmentation: BCE + Dice\n- classification: FocalLoss\n\n####  Main training:\n- (classification)( FocalLoss * 0.5 + BCEWithLogits * 0.5 ) * 0.05 + (segmentation)( BCE + Dice ) * 0.95\n   \n## Ensemble:\n- simple average of the 2 models(x 4 = total 8 models)\n   \n## Post processing:\n- TTA : None, h-flip, v-flip, h-flip and v-flip\n- Threshold : I use [optuna](https://github.com/optuna/optuna) to find the optimal value from the cv score. \n   \n## GPU:\n- RTX2080Ti x 1",
      "votes": null
    },
    {
      "id": "677710",
      "postDate": "11/20/2019 14:15:14",
      "content": "<p>Congratulations\nThanks for Sharing your Approach &amp; Insights! <a href=\"/iru538\">@iru538</a> </p>",
      "rawMarkdown": "Congratulations\nThanks for Sharing your Approach &amp; Insights! @iru538",
      "votes": null
    },
    {
      "id": "677849",
      "postDate": "11/20/2019 16:49:22",
      "content": "<p>Congrats on solo gold. Your model is simple and effective, nice work. </p>\n\n<p>Your models say \"with classification\". Can you describe your classification head? Was it just <code>GlobalAveragePooling2D()</code> then <code>Dense(4)</code> after the encoder and before the decoder?</p>",
      "rawMarkdown": "Congrats on solo gold. Your model is simple and effective, nice work. \n\nYour models say \"with classification\". Can you describe your classification head? Was it just `GlobalAveragePooling2D()` then `Dense(4)` after the encoder and before the decoder?",
      "votes": null
    },
    {
      "id": "678445",
      "postDate": "11/21/2019 12:12:12",
      "content": "<p>Thanks for your congratulations!</p>",
      "rawMarkdown": "Thanks for your congratulations!",
      "votes": null
    },
    {
      "id": "678466",
      "postDate": "11/21/2019 12:34:10",
      "content": "<p>Thanks for your congratulations!</p>\n\n<p>I added classification after the encoder and AdaptiveAvgPool2d.\nHere is my model.</p>\n\n<p>```\nimport segmentation_models_pytorch as smp</p>\n\n<p>class model_segmentation_classification(nn.Module):</p>\n\n<pre><code>def __init__(self, encoder_name, decoder_name, classes):\n    super(model_segmentation_classification, self).__init__()\n\n    if decoder_name == 'Unet':\n\n        self.model = smp.Unet(\n                            encoder_name=encoder_name, \n                            encoder_weights='imagenet', \n                            classes=classes, \n                            activation=None\n                         )\n\n    else:\n\n        self.model = smp.FPN(\n                            encoder_name=encoder_name, \n                            encoder_weights='imagenet', \n                            classes=classes, \n                            activation=None\n                         )\n\n    out_shapes = {\n        'se_resnext101_32x4d': 2048,\n        'se_resnext50_32x4d':  2048,\n        'densenet169':         1664,\n        'efficientnet-b3':     384,\n        'efficientnet-b4':     448,\n        'efficientnet-b5':     512,\n    }\n\n    self.linear_size = out_shapes[encoder_name]\n    self.avgpool = nn.AdaptiveAvgPool2d((1,1))\n\n    self.classification = nn.Sequential(\n                                nn.Linear( self.linear_size, 128 ),\n                                nn.BatchNorm1d( 128 ),\n                                nn.ReLU( inplace=True ),\n                                nn.Linear( 128, classes ),\n                            )\n\ndef forward(self, x):\n    global_features = self.model.encoder(x)\n\n    cls_feature = global_features[0]\n    cls_feature = self.avgpool(cls_feature)\n    cls_feature = cls_feature.view(cls_feature.size(0), -1)\n    cls_feature = self.classification(cls_feature)\n\n    seg_feature = self.model.decoder(global_features)\n\n    return cls_feature, seg_feature\n</code></pre>\n\n<p>model = model_segmentation_classification( \n                                encoder_name='densenet169', <br>\n                                decoder_name='Unet', \n                                classes=4 \n                             )</p>\n\n<h1>model = model_segmentation_classification(</h1>\n\n<h1>encoder_name='efficientnet-b4',</h1>\n\n<h1>decoder_name='FPN',</h1>\n\n<h1>classes=4</h1>\n\n<h1>)</h1>\n\n<p>```</p>",
      "rawMarkdown": "Thanks for your congratulations!\n\nI added classification after the encoder and AdaptiveAvgPool2d.\nHere is my model.\n\n```\nimport segmentation_models_pytorch as smp\n\n\nclass model_segmentation_classification(nn.Module):\n\n    def __init__(self, encoder_name, decoder_name, classes):\n        super(model_segmentation_classification, self).__init__()\n        \n        if decoder_name == 'Unet':\n\n            self.model = smp.Unet(\n                                encoder_name=encoder_name, \n                                encoder_weights='imagenet', \n                                classes=classes, \n                                activation=None\n                             )\n\n        else:\n\n            self.model = smp.FPN(\n                                encoder_name=encoder_name, \n                                encoder_weights='imagenet', \n                                classes=classes, \n                                activation=None\n                             )\n\n        out_shapes = {\n            'se_resnext101_32x4d': 2048,\n            'se_resnext50_32x4d':  2048,\n            'densenet169':         1664,\n            'efficientnet-b3':     384,\n            'efficientnet-b4':     448,\n            'efficientnet-b5':     512,\n        }\n        \n        self.linear_size = out_shapes[encoder_name]\n        self.avgpool = nn.AdaptiveAvgPool2d((1,1))\n        \n        self.classification = nn.Sequential(\n                                    nn.Linear( self.linear_size, 128 ),\n                                    nn.BatchNorm1d( 128 ),\n                                    nn.ReLU( inplace=True ),\n                                    nn.Linear( 128, classes ),\n                                )\n\n    def forward(self, x):\n        global_features = self.model.encoder(x)\n        \n        cls_feature = global_features[0]\n        cls_feature = self.avgpool(cls_feature)\n        cls_feature = cls_feature.view(cls_feature.size(0), -1)\n        cls_feature = self.classification(cls_feature)\n        \n        seg_feature = self.model.decoder(global_features)\n        \n        return cls_feature, seg_feature\n\n\n\nmodel = model_segmentation_classification( \n                                encoder_name='densenet169',  \n                                decoder_name='Unet', \n                                classes=4 \n                             )\n\n#model = model_segmentation_classification( \n#                                encoder_name='efficientnet-b4', \n#                                decoder_name='FPN', \n#                                classes=4 \n#                             )\n```",
      "votes": null
    },
    {
      "id": "678698",
      "postDate": "11/21/2019 18:28:09",
      "content": "<p>Awesome. I see you effectively do <code>GlobalAveragePooling2D()</code>, then <code>Dense(128)</code>, then <code>Dense(4)</code> after encoder.</p>\n\n<p>Two more questions. You rescaled your images to <code>320x512</code> and your models' input size is <code>320x480</code>, did you then use random crop augmentation? How did you ensemble your models, simple average classification output and average pixel probability output? </p>\n\n<p>I like how your model is so simple. There is a lot to learn from here.</p>",
      "rawMarkdown": "Awesome. I see you effectively do `GlobalAveragePooling2D()`, then `Dense(128)`, then `Dense(4)` after encoder.\n\nTwo more questions. You rescaled your images to `320x512` and your models' input size is `320x480`, did you then use random crop augmentation? How did you ensemble your models, simple average classification output and average pixel probability output? \n\nI like how your model is so simple. There is a lot to learn from here.",
      "votes": null
    },
    {
      "id": "679094",
      "postDate": "11/22/2019 09:23:32",
      "content": "<p>Congratulations . This is simple and elegant .\nLo and Behold , <a href=\"/pavel92\">@pavel92</a>  's latest pytorch repo has it implemented already :)  !! </p>",
      "rawMarkdown": "Congratulations . This is simple and elegant .\nLo and Behold , @pavel92  's latest pytorch repo has it implemented already :)  !!",
      "votes": null
    },
    {
      "id": "679202",
      "postDate": "11/22/2019 12:42:06",
      "content": "<p>Thanks for your congratulations!\nThank you for telling me <a href=\"/pavel92\">@pavel92</a> 's latest pytorch repo.</p>",
      "rawMarkdown": "Thanks for your congratulations!\nThank you for telling me @pavel92 's latest pytorch repo.",
      "votes": null
    },
    {
      "id": "679228",
      "postDate": "11/22/2019 13:21:33",
      "content": "<p>I am not using random crop augmentation.</p>\n\n<p>Here is a simple average classification output and average pixel probability code.</p>\n\n<p>```\nmodel_list = [ <br>\n    # densenet169\n    [ 'model1_1.pth', 'densenet169', 'Unet' ],\n    [ 'model1_2.pth', 'densenet169', 'Unet' ],\n    [ 'model1_3.pth', 'densenet169', 'Unet' ],\n    [ 'model1_4.pth', 'densenet169', 'Unet' ],</p>\n\n<pre><code># efficientnet-b4\n[ 'model2_1.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_2.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_3.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_4.pth', 'efficientnet-b4', 'FPN' ],  \n</code></pre>\n\n<p>]</p>\n\n<p>models = []</p>\n\n<p>for info in model_list:\n    m = model_segmentation_classification(\n                                           encoder_name=info[1],\n                                           decoder_name=info[2],\n                                           classes=4\n                                         )</p>\n\n<pre><code>m.load_state_dict( torch.load( info[0] ) )\nm.to('cuda')\nm.eval()\n\nmodels.append( m )  \n</code></pre>\n\n<p>class Model:\n    def <strong>init</strong>(self, models):\n        self.models = models</p>\n\n<pre><code>def __call__(self, x):\n    preds_cls = []\n    preds = []\n    x = x.cuda()\n\n    with torch.no_grad():\n        for m in self.models:\n            pred_cls, pred = m(x)\n            preds_cls.append( pred_cls )\n            preds.append( pred )\n\n    preds_cls = torch.stack(preds_cls)\n    preds_cls = torch.mean(preds_cls, dim=0)\n\n    preds = torch.stack(preds)\n    preds = torch.mean(preds, dim=0)\n\n    return preds_cls, preds\n</code></pre>\n\n<p>model = Model( models )\n```</p>",
      "rawMarkdown": "I am not using random crop augmentation.\n\nHere is a simple average classification output and average pixel probability code.\n\n```\nmodel_list = [    \n    # densenet169\n    [ 'model1_1.pth', 'densenet169', 'Unet' ],\n    [ 'model1_2.pth', 'densenet169', 'Unet' ],\n    [ 'model1_3.pth', 'densenet169', 'Unet' ],\n    [ 'model1_4.pth', 'densenet169', 'Unet' ],\n    \n    # efficientnet-b4\n    [ 'model2_1.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_2.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_3.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_4.pth', 'efficientnet-b4', 'FPN' ],  \n]\n\n\nmodels = []\n\nfor info in model_list:\n    m = model_segmentation_classification(\n                                           encoder_name=info[1],\n                                           decoder_name=info[2],\n                                           classes=4\n                                         )\n\n    m.load_state_dict( torch.load( info[0] ) )\n    m.to('cuda')\n    m.eval()\n    \n    models.append( m )  \n\n\nclass Model:\n    def __init__(self, models):\n        self.models = models\n    \n    def __call__(self, x):\n        preds_cls = []\n        preds = []\n        x = x.cuda()\n        \n        with torch.no_grad():\n            for m in self.models:\n                pred_cls, pred = m(x)\n                preds_cls.append( pred_cls )\n                preds.append( pred )\n       \n        preds_cls = torch.stack(preds_cls)\n        preds_cls = torch.mean(preds_cls, dim=0)\n        \n        preds = torch.stack(preds)\n        preds = torch.mean(preds, dim=0)\n        \n        return preds_cls, preds\n\n\nmodel = Model( models )\n```",
      "votes": null
    },
    {
      "id": "680988",
      "postDate": "11/25/2019 13:38:49",
      "content": "<p>Congratulations. I am new to segmentation tasks, just wondering how do you combine the classification loss and segmentation loss. From my understanding, the model could only optimizer 1 loss. Or did you do model in 2 stage?</p>\n\n<p>Thank you very much for your sharing </p>",
      "rawMarkdown": "Congratulations. I am new to segmentation tasks, just wondering how do you combine the classification loss and segmentation loss. From my understanding, the model could only optimizer 1 loss. Or did you do model in 2 stage?\n\nThank you very much for your sharing",
      "votes": null
    },
    {
      "id": "681780",
      "postDate": "11/26/2019 14:39:14",
      "content": "<p>Thanks for your congratulations!</p>\n\n<p>My model training is in 2 stages. Training a model with separate segmentation and classification.</p>\n\n<h3>stage-1:</h3>\n\n<ul>\n<li>segmentation training</li>\n</ul>\n\n<p>```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = True</p>\n\n<p>for param in model.model.decoder.parameters():\n    param.requires_grad = True</p>\n\n<p>for param in model.classification.parameters():\n    param.requires_grad = False <br>\n```</p>\n\n<h3>stage-2:</h3>\n\n<ul>\n<li>classification training</li>\n</ul>\n\n<p>```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = False</p>\n\n<p>for param in model.model.decoder.parameters():\n    param.requires_grad = False</p>\n\n<p>for param in model.classification.parameters():\n    param.requires_grad = True <br>\n```</p>",
      "rawMarkdown": "Thanks for your congratulations!\n\nMy model training is in 2 stages. Training a model with separate segmentation and classification.\n\n### stage-1:\n- segmentation training\n\n```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = True\n    \nfor param in model.model.decoder.parameters():\n    param.requires_grad = True\n    \nfor param in model.classification.parameters():\n    param.requires_grad = False    \n```\n\n### stage-2:\n- classification training\n\n```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = False\n    \nfor param in model.model.decoder.parameters():\n    param.requires_grad = False\n    \nfor param in model.classification.parameters():\n    param.requires_grad = True    \n```",
      "votes": null
    },
    {
      "id": "681879",
      "postDate": "11/26/2019 16:28:52",
      "content": "<p>I have another alternative solution for your reference.\nin my experiments, this well well:\n```\n1. start training using loss = 0.1*loss_label + 0.9*loss_mask\n2. when there can be no more improvement to kaggle metric (not loss), try \n     loss = 0.2*loss_label + 0.8*loss_mask</p>\n\n<p>...</p>\n\n<p>repeat reweighing  the loss to strengthen classifier or segmentation until your  kaggle metric maximizes\n```</p>\n\n<p>there is one related paper on learnable loss function but i forget the title.</p>",
      "rawMarkdown": "I have another alternative solution for your reference.\nin my experiments, this well well:\n```\n1. start training using loss = 0.1*loss_label + 0.9*loss_mask\n2. when there can be no more improvement to kaggle metric (not loss), try \n     loss = 0.2*loss_label + 0.8*loss_mask\n\n...\n\nrepeat reweighing  the loss to strengthen classifier or segmentation until your  kaggle metric maximizes\n```\n\nthere is one related paper on learnable loss function but i forget the title.",
      "votes": null
    },
    {
      "id": "685329",
      "postDate": "12/01/2019 12:26:48",
      "content": "<p>Thank you very much.</p>",
      "rawMarkdown": "Thank you very much.",
      "votes": null
    },
    {
      "id": "685340",
      "postDate": "12/01/2019 13:03:28",
      "content": "<p>Thank you for the information.</p>",
      "rawMarkdown": "Thank you for the information.",
      "votes": null
    },
    {
      "id": "753271",
      "postDate": "02/22/2020 00:54:43",
      "content": "<p>Hello, could you please provide the epochs and learning rates of each stage of training? My training effect in the segmentation stage is not very good, I wonder if the epochs are too few to cause the lack of training time. Or the setting of learning rate is not reasonable enough</p>",
      "rawMarkdown": "Hello, could you please provide the epochs and learning rates of each stage of training? My training effect in the segmentation stage is not very good, I wonder if the epochs are too few to cause the lack of training time. Or the setting of learning rate is not reasonable enough",
      "votes": null
    },
    {
      "id": "778607",
      "postDate": "03/18/2020 15:33:01",
      "content": "<p>I used <a href=\"https://github.com/bckenstler/CLR\">CyclicScheduler2</a>.</p>\n\n<h3>segmentation training</h3>\n\n<ul>\n<li>encoder: CyclicScheduler2( min_lr=0.00005, max_lr=0.0001, period=17, warm_start=3, max_decay=0.9 )</li>\n<li>decoder: CyclicScheduler2( min_lr=0.0005, max_lr=0.001, period=17, warm_start=3, max_decay=0.9 )</li>\n<li>epoch: 20</li>\n</ul>\n\n<h3>classification training</h3>\n\n<ul>\n<li>classification: CyclicScheduler2( min_lr=0.00008, max_lr=0.001, period=14, warm_start=1, max_decay=0.9 )</li>\n<li>epoch: 15</li>\n</ul>",
      "rawMarkdown": "I used [CyclicScheduler2](https://github.com/bckenstler/CLR).\n\n### segmentation training\n- encoder: CyclicScheduler2( min_lr=0.00005, max_lr=0.0001, period=17, warm_start=3, max_decay=0.9 )\n- decoder: CyclicScheduler2( min_lr=0.0005, max_lr=0.001, period=17, warm_start=3, max_decay=0.9 )\n- epoch: 20\n\n### classification training\n- classification: CyclicScheduler2( min_lr=0.00008, max_lr=0.001, period=14, warm_start=1, max_decay=0.9 )\n- epoch: 15",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 677710,
      "author_name": "veeralakrishna",
      "author_url": "",
      "post_date": "11/20/2019 14:15:14",
      "content": "<p>Congratulations\nThanks for Sharing your Approach &amp; Insights! <a href=\"/iru538\">@iru538</a> </p>",
      "votes": null,
      "replies": [
        {
          "id": 678445,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "11/21/2019 12:12:12",
          "content": "<p>Thanks for your congratulations!</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 677849,
      "author_name": "cdeotte",
      "author_url": "",
      "post_date": "11/20/2019 16:49:22",
      "content": "<p>Congrats on solo gold. Your model is simple and effective, nice work. </p>\n\n<p>Your models say \"with classification\". Can you describe your classification head? Was it just <code>GlobalAveragePooling2D()</code> then <code>Dense(4)</code> after the encoder and before the decoder?</p>",
      "votes": null,
      "replies": [
        {
          "id": 678466,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "11/21/2019 12:34:10",
          "content": "<p>Thanks for your congratulations!</p>\n\n<p>I added classification after the encoder and AdaptiveAvgPool2d.\nHere is my model.</p>\n\n<p>```\nimport segmentation_models_pytorch as smp</p>\n\n<p>class model_segmentation_classification(nn.Module):</p>\n\n<pre><code>def __init__(self, encoder_name, decoder_name, classes):\n    super(model_segmentation_classification, self).__init__()\n\n    if decoder_name == 'Unet':\n\n        self.model = smp.Unet(\n                            encoder_name=encoder_name, \n                            encoder_weights='imagenet', \n                            classes=classes, \n                            activation=None\n                         )\n\n    else:\n\n        self.model = smp.FPN(\n                            encoder_name=encoder_name, \n                            encoder_weights='imagenet', \n                            classes=classes, \n                            activation=None\n                         )\n\n    out_shapes = {\n        'se_resnext101_32x4d': 2048,\n        'se_resnext50_32x4d':  2048,\n        'densenet169':         1664,\n        'efficientnet-b3':     384,\n        'efficientnet-b4':     448,\n        'efficientnet-b5':     512,\n    }\n\n    self.linear_size = out_shapes[encoder_name]\n    self.avgpool = nn.AdaptiveAvgPool2d((1,1))\n\n    self.classification = nn.Sequential(\n                                nn.Linear( self.linear_size, 128 ),\n                                nn.BatchNorm1d( 128 ),\n                                nn.ReLU( inplace=True ),\n                                nn.Linear( 128, classes ),\n                            )\n\ndef forward(self, x):\n    global_features = self.model.encoder(x)\n\n    cls_feature = global_features[0]\n    cls_feature = self.avgpool(cls_feature)\n    cls_feature = cls_feature.view(cls_feature.size(0), -1)\n    cls_feature = self.classification(cls_feature)\n\n    seg_feature = self.model.decoder(global_features)\n\n    return cls_feature, seg_feature\n</code></pre>\n\n<p>model = model_segmentation_classification( \n                                encoder_name='densenet169', <br>\n                                decoder_name='Unet', \n                                classes=4 \n                             )</p>\n\n<h1>model = model_segmentation_classification(</h1>\n\n<h1>encoder_name='efficientnet-b4',</h1>\n\n<h1>decoder_name='FPN',</h1>\n\n<h1>classes=4</h1>\n\n<h1>)</h1>\n\n<p>```</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 678698,
          "author_name": "cdeotte",
          "author_url": "",
          "post_date": "11/21/2019 18:28:09",
          "content": "<p>Awesome. I see you effectively do <code>GlobalAveragePooling2D()</code>, then <code>Dense(128)</code>, then <code>Dense(4)</code> after encoder.</p>\n\n<p>Two more questions. You rescaled your images to <code>320x512</code> and your models' input size is <code>320x480</code>, did you then use random crop augmentation? How did you ensemble your models, simple average classification output and average pixel probability output? </p>\n\n<p>I like how your model is so simple. There is a lot to learn from here.</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 679228,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "11/22/2019 13:21:33",
          "content": "<p>I am not using random crop augmentation.</p>\n\n<p>Here is a simple average classification output and average pixel probability code.</p>\n\n<p>```\nmodel_list = [ <br>\n    # densenet169\n    [ 'model1_1.pth', 'densenet169', 'Unet' ],\n    [ 'model1_2.pth', 'densenet169', 'Unet' ],\n    [ 'model1_3.pth', 'densenet169', 'Unet' ],\n    [ 'model1_4.pth', 'densenet169', 'Unet' ],</p>\n\n<pre><code># efficientnet-b4\n[ 'model2_1.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_2.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_3.pth', 'efficientnet-b4', 'FPN' ],\n[ 'model2_4.pth', 'efficientnet-b4', 'FPN' ],  \n</code></pre>\n\n<p>]</p>\n\n<p>models = []</p>\n\n<p>for info in model_list:\n    m = model_segmentation_classification(\n                                           encoder_name=info[1],\n                                           decoder_name=info[2],\n                                           classes=4\n                                         )</p>\n\n<pre><code>m.load_state_dict( torch.load( info[0] ) )\nm.to('cuda')\nm.eval()\n\nmodels.append( m )  \n</code></pre>\n\n<p>class Model:\n    def <strong>init</strong>(self, models):\n        self.models = models</p>\n\n<pre><code>def __call__(self, x):\n    preds_cls = []\n    preds = []\n    x = x.cuda()\n\n    with torch.no_grad():\n        for m in self.models:\n            pred_cls, pred = m(x)\n            preds_cls.append( pred_cls )\n            preds.append( pred )\n\n    preds_cls = torch.stack(preds_cls)\n    preds_cls = torch.mean(preds_cls, dim=0)\n\n    preds = torch.stack(preds)\n    preds = torch.mean(preds, dim=0)\n\n    return preds_cls, preds\n</code></pre>\n\n<p>model = Model( models )\n```</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 679094,
      "author_name": "phoenix9032",
      "author_url": "",
      "post_date": "11/22/2019 09:23:32",
      "content": "<p>Congratulations . This is simple and elegant .\nLo and Behold , <a href=\"/pavel92\">@pavel92</a>  's latest pytorch repo has it implemented already :)  !! </p>",
      "votes": null,
      "replies": [
        {
          "id": 679202,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "11/22/2019 12:42:06",
          "content": "<p>Thanks for your congratulations!\nThank you for telling me <a href=\"/pavel92\">@pavel92</a> 's latest pytorch repo.</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 680988,
      "author_name": "xubujie",
      "author_url": "",
      "post_date": "11/25/2019 13:38:49",
      "content": "<p>Congratulations. I am new to segmentation tasks, just wondering how do you combine the classification loss and segmentation loss. From my understanding, the model could only optimizer 1 loss. Or did you do model in 2 stage?</p>\n\n<p>Thank you very much for your sharing </p>",
      "votes": null,
      "replies": [
        {
          "id": 681780,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "11/26/2019 14:39:14",
          "content": "<p>Thanks for your congratulations!</p>\n\n<p>My model training is in 2 stages. Training a model with separate segmentation and classification.</p>\n\n<h3>stage-1:</h3>\n\n<ul>\n<li>segmentation training</li>\n</ul>\n\n<p>```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = True</p>\n\n<p>for param in model.model.decoder.parameters():\n    param.requires_grad = True</p>\n\n<p>for param in model.classification.parameters():\n    param.requires_grad = False <br>\n```</p>\n\n<h3>stage-2:</h3>\n\n<ul>\n<li>classification training</li>\n</ul>\n\n<p>```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = False</p>\n\n<p>for param in model.model.decoder.parameters():\n    param.requires_grad = False</p>\n\n<p>for param in model.classification.parameters():\n    param.requires_grad = True <br>\n```</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 681879,
          "author_name": "hengck23",
          "author_url": "",
          "post_date": "11/26/2019 16:28:52",
          "content": "<p>I have another alternative solution for your reference.\nin my experiments, this well well:\n```\n1. start training using loss = 0.1*loss_label + 0.9*loss_mask\n2. when there can be no more improvement to kaggle metric (not loss), try \n     loss = 0.2*loss_label + 0.8*loss_mask</p>\n\n<p>...</p>\n\n<p>repeat reweighing  the loss to strengthen classifier or segmentation until your  kaggle metric maximizes\n```</p>\n\n<p>there is one related paper on learnable loss function but i forget the title.</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 685329,
          "author_name": "xubujie",
          "author_url": "",
          "post_date": "12/01/2019 12:26:48",
          "content": "<p>Thank you very much.</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 685340,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "12/01/2019 13:03:28",
          "content": "<p>Thank you for the information.</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 753271,
      "author_name": "ssfailearning",
      "author_url": "",
      "post_date": "02/22/2020 00:54:43",
      "content": "<p>Hello, could you please provide the epochs and learning rates of each stage of training? My training effect in the segmentation stage is not very good, I wonder if the epochs are too few to cause the lack of training time. Or the setting of learning rate is not reasonable enough</p>",
      "votes": null,
      "replies": [
        {
          "id": 778607,
          "author_name": "iru538",
          "author_url": "",
          "post_date": "03/18/2020 15:33:01",
          "content": "<p>I used <a href=\"https://github.com/bckenstler/CLR\">CyclicScheduler2</a>.</p>\n\n<h3>segmentation training</h3>\n\n<ul>\n<li>encoder: CyclicScheduler2( min_lr=0.00005, max_lr=0.0001, period=17, warm_start=3, max_decay=0.9 )</li>\n<li>decoder: CyclicScheduler2( min_lr=0.0005, max_lr=0.001, period=17, warm_start=3, max_decay=0.9 )</li>\n<li>epoch: 20</li>\n</ul>\n\n<h3>classification training</h3>\n\n<ul>\n<li>classification: CyclicScheduler2( min_lr=0.00008, max_lr=0.001, period=14, warm_start=1, max_decay=0.9 )</li>\n<li>epoch: 15</li>\n</ul>",
          "votes": null,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "677644": "Congratulations to all winners!\nHere is my solution (Public 0.67376, Private 0.66765).\n\n## Pre processing:\n- resize image size to (320, 512)\n- exclude bad images (removed 21 images)\n\n## Augmentations:\nI used [albumentations](https://github.com/albu/albumentations).\n- HorizontalFlip, VerticalFlip\n- ShiftScaleRotate, GridDistortion\n- Blur, MedianBlur, GaussianBlur\n- CLAHE, RandomBrightnessContrast, HueSaturationValue, IAASharpen\n   \n## Model:\nI used [segmentation_models.pytorch](https://github.com/qubvel/segmentation_models.pytorch),  [pretrained-models.pytorch](https://github.com/cadene/pretrained-models.pytorch),  [EfficientNet-PyTorch](https://github.com/lukemelas/EfficientNet-PyTorch).\n\n#### Model-1:\n- densenet169 Unet with classification\n- image size : 320x480\n\n#### Model-2:\n- efficientnet-b4 FPN with classification\n- image size : 320x480\n   \n## Optimizer:\n- [RAdam](https://github.com/LiyuanLucasLiu/RAdam)\n   \n## Loss:\n#### Pre training:\n- segmentation: BCE + Dice\n- classification: FocalLoss\n\n####  Main training:\n- (classification)( FocalLoss * 0.5 + BCEWithLogits * 0.5 ) * 0.05 + (segmentation)( BCE + Dice ) * 0.95\n   \n## Ensemble:\n- simple average of the 2 models(x 4 = total 8 models)\n   \n## Post processing:\n- TTA : None, h-flip, v-flip, h-flip and v-flip\n- Threshold : I use [optuna](https://github.com/optuna/optuna) to find the optimal value from the cv score. \n   \n## GPU:\n- RTX2080Ti x 1",
    "677710": "Congratulations\nThanks for Sharing your Approach &amp; Insights! @iru538",
    "677849": "Congrats on solo gold. Your model is simple and effective, nice work. \n\nYour models say \"with classification\". Can you describe your classification head? Was it just `GlobalAveragePooling2D()` then `Dense(4)` after the encoder and before the decoder?",
    "678445": "Thanks for your congratulations!",
    "678466": "Thanks for your congratulations!\n\nI added classification after the encoder and AdaptiveAvgPool2d.\nHere is my model.\n\n```\nimport segmentation_models_pytorch as smp\n\n\nclass model_segmentation_classification(nn.Module):\n\n    def __init__(self, encoder_name, decoder_name, classes):\n        super(model_segmentation_classification, self).__init__()\n        \n        if decoder_name == 'Unet':\n\n            self.model = smp.Unet(\n                                encoder_name=encoder_name, \n                                encoder_weights='imagenet', \n                                classes=classes, \n                                activation=None\n                             )\n\n        else:\n\n            self.model = smp.FPN(\n                                encoder_name=encoder_name, \n                                encoder_weights='imagenet', \n                                classes=classes, \n                                activation=None\n                             )\n\n        out_shapes = {\n            'se_resnext101_32x4d': 2048,\n            'se_resnext50_32x4d':  2048,\n            'densenet169':         1664,\n            'efficientnet-b3':     384,\n            'efficientnet-b4':     448,\n            'efficientnet-b5':     512,\n        }\n        \n        self.linear_size = out_shapes[encoder_name]\n        self.avgpool = nn.AdaptiveAvgPool2d((1,1))\n        \n        self.classification = nn.Sequential(\n                                    nn.Linear( self.linear_size, 128 ),\n                                    nn.BatchNorm1d( 128 ),\n                                    nn.ReLU( inplace=True ),\n                                    nn.Linear( 128, classes ),\n                                )\n\n    def forward(self, x):\n        global_features = self.model.encoder(x)\n        \n        cls_feature = global_features[0]\n        cls_feature = self.avgpool(cls_feature)\n        cls_feature = cls_feature.view(cls_feature.size(0), -1)\n        cls_feature = self.classification(cls_feature)\n        \n        seg_feature = self.model.decoder(global_features)\n        \n        return cls_feature, seg_feature\n\n\n\nmodel = model_segmentation_classification( \n                                encoder_name='densenet169',  \n                                decoder_name='Unet', \n                                classes=4 \n                             )\n\n#model = model_segmentation_classification( \n#                                encoder_name='efficientnet-b4', \n#                                decoder_name='FPN', \n#                                classes=4 \n#                             )\n```",
    "678698": "Awesome. I see you effectively do `GlobalAveragePooling2D()`, then `Dense(128)`, then `Dense(4)` after encoder.\n\nTwo more questions. You rescaled your images to `320x512` and your models' input size is `320x480`, did you then use random crop augmentation? How did you ensemble your models, simple average classification output and average pixel probability output? \n\nI like how your model is so simple. There is a lot to learn from here.",
    "679094": "Congratulations . This is simple and elegant .\nLo and Behold , @pavel92  's latest pytorch repo has it implemented already :)  !!",
    "679202": "Thanks for your congratulations!\nThank you for telling me @pavel92 's latest pytorch repo.",
    "679228": "I am not using random crop augmentation.\n\nHere is a simple average classification output and average pixel probability code.\n\n```\nmodel_list = [    \n    # densenet169\n    [ 'model1_1.pth', 'densenet169', 'Unet' ],\n    [ 'model1_2.pth', 'densenet169', 'Unet' ],\n    [ 'model1_3.pth', 'densenet169', 'Unet' ],\n    [ 'model1_4.pth', 'densenet169', 'Unet' ],\n    \n    # efficientnet-b4\n    [ 'model2_1.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_2.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_3.pth', 'efficientnet-b4', 'FPN' ],\n    [ 'model2_4.pth', 'efficientnet-b4', 'FPN' ],  \n]\n\n\nmodels = []\n\nfor info in model_list:\n    m = model_segmentation_classification(\n                                           encoder_name=info[1],\n                                           decoder_name=info[2],\n                                           classes=4\n                                         )\n\n    m.load_state_dict( torch.load( info[0] ) )\n    m.to('cuda')\n    m.eval()\n    \n    models.append( m )  \n\n\nclass Model:\n    def __init__(self, models):\n        self.models = models\n    \n    def __call__(self, x):\n        preds_cls = []\n        preds = []\n        x = x.cuda()\n        \n        with torch.no_grad():\n            for m in self.models:\n                pred_cls, pred = m(x)\n                preds_cls.append( pred_cls )\n                preds.append( pred )\n       \n        preds_cls = torch.stack(preds_cls)\n        preds_cls = torch.mean(preds_cls, dim=0)\n        \n        preds = torch.stack(preds)\n        preds = torch.mean(preds, dim=0)\n        \n        return preds_cls, preds\n\n\nmodel = Model( models )\n```",
    "680988": "Congratulations. I am new to segmentation tasks, just wondering how do you combine the classification loss and segmentation loss. From my understanding, the model could only optimizer 1 loss. Or did you do model in 2 stage?\n\nThank you very much for your sharing",
    "681780": "Thanks for your congratulations!\n\nMy model training is in 2 stages. Training a model with separate segmentation and classification.\n\n### stage-1:\n- segmentation training\n\n```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = True\n    \nfor param in model.model.decoder.parameters():\n    param.requires_grad = True\n    \nfor param in model.classification.parameters():\n    param.requires_grad = False    \n```\n\n### stage-2:\n- classification training\n\n```\nfor param in model.model.encoder.parameters():\n    param.requires_grad = False\n    \nfor param in model.model.decoder.parameters():\n    param.requires_grad = False\n    \nfor param in model.classification.parameters():\n    param.requires_grad = True    \n```",
    "681879": "I have another alternative solution for your reference.\nin my experiments, this well well:\n```\n1. start training using loss = 0.1*loss_label + 0.9*loss_mask\n2. when there can be no more improvement to kaggle metric (not loss), try \n     loss = 0.2*loss_label + 0.8*loss_mask\n\n...\n\nrepeat reweighing  the loss to strengthen classifier or segmentation until your  kaggle metric maximizes\n```\n\nthere is one related paper on learnable loss function but i forget the title.",
    "685329": "Thank you very much.",
    "685340": "Thank you for the information.",
    "753271": "Hello, could you please provide the epochs and learning rates of each stage of training? My training effect in the segmentation stage is not very good, I wonder if the epochs are too few to cause the lack of training time. Or the setting of learning rate is not reasonable enough",
    "778607": "I used [CyclicScheduler2](https://github.com/bckenstler/CLR).\n\n### segmentation training\n- encoder: CyclicScheduler2( min_lr=0.00005, max_lr=0.0001, period=17, warm_start=3, max_decay=0.9 )\n- decoder: CyclicScheduler2( min_lr=0.0005, max_lr=0.001, period=17, warm_start=3, max_decay=0.9 )\n- epoch: 20\n\n### classification training\n- classification: CyclicScheduler2( min_lr=0.00008, max_lr=0.001, period=14, warm_start=1, max_decay=0.9 )\n- epoch: 15"
  },
  "source": "meta"
}