{
  "id": 313424,
  "title": "6th place solution. A YOLO-like text span detector.",
  "content": "<p>Thank you for hosting this awesome competition and congrats to the winners.</p>\n<p>This problem is interesting and challenging. The data and annotations are high quality. It's a great learning and practicing experience working on this competition.</p>\n<p>I started this competition compeletely ignoring those shared NER baselines. I thought in an \"object detection\" task, segmentation + posprocess approaches can not do better than object detection approaches. I'm surprised by the postprocess ideas in top solutions.</p>\n<p>Overall, what I made is a YOLO-like text span detector. I share my code in <a href=\"https://www.kaggle.com/tascj0/a-text-span-detector\" target=\"_blank\">this notebook</a>.</p>\n<p>Checkpoints are shared <a href=\"https://www.kaggle.com/datasets/tascj0/feedback-checkpoints\" target=\"_blank\">here</a>.</p>\n<p>To reproduce the checkpoints, check the code and configs <a href=\"https://github.com/tascj/kaggle-feedback-prize-2021\" target=\"_blank\">here</a>.</p>\n<h2>Modeling</h2>\n<h3>Network</h3>\n<pre><code>AutoModelForTokenClassification.from_pretrained(num_labels=1 + 2 + num_classes, ...)\n</code></pre>\n<p>1 for objectness (or span-ness?. fg/bg classification)</p>\n<p>2 for regression (distance from fg location to first and last index of corresponding span)</p>\n<p>num_classes for discourse type classification</p>\n<h3>Aggregate tokens to words</h3>\n<p>Network logits is in shape (num_tokens, 10). This is inconvenient (decoding output, ensembling models with different tokenizers). So I aggregate the logits to (num_words, 10) using RoIAlign.</p>\n<h3>Training target</h3>\n<p>The problem of this formulation is how to define positive for objectness training. In object detection, center of object is a natural choice of positve. In text span detection, I found the first word of span a good choice of positive.</p>\n<p>In addition, I assign lowest cost word in each span as positive during training. This is inspired by YOLOX.</p>\n<h3>Augmentation, loss, decoding outputs, etc.</h3>\n<p>I randomly replace tokens with mask token during training.</p>\n<p>For other details, please check the code.</p>\n<h3>Post process</h3>\n<p>The only post process is nms.</p>\n<h3>Ensemble</h3>\n<p>I used one cycle policy in training and averaged weights of the last few epochs.<br>\nTo ensemble different models/folds, I simply averaged outputs of models.<br>\nWBF ensemble does not work in local validation, and I didn't figure out why. I think I did something wrong here.</p>\n<h2>Results</h2>\n<table>\n<thead>\n<tr>\n<th>backbone</th>\n<th>Validation</th>\n<th>Public LB</th>\n<th>Private LB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>google/bigbird-roberta-base</td>\n<td>0.685~0.69</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>allenai/longformer-base-4096</td>\n<td>0.685~0.69</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>allenai/longformer-large-4096</td>\n<td>0.70~0.71</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-base</td>\n<td>0.70~0.705</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-large</td>\n<td>0.715~0.72</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-xlarge</td>\n<td>0.715~0.72</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-large + microsoft/deberta-xlarge</td>\n<td>0.723</td>\n<td>0.714</td>\n<td>0.732</td>\n</tr>\n</tbody>\n</table>\n<p>The best combination is <code>deberta-large</code> + <code>deberta-xlarge</code>. Ensembling more does not help.<br>\nIn the final submission, I used 2 weights (2/5 folds) each model. Submission time is around 2 hours.</p>",
  "comments": [
    {
      "id": 1726035,
      "author_name": "Theo Viel",
      "author_url": "",
      "post_date": "2022-03-17T15:41:59.203000",
      "content": "<p>This solution should be #1</p>\n<p>More seriously, it is very elegant and I'm pretty sure that there is room for improvement - perhaps it would outperform other approaches if combined with a features+boosting model :)</p>",
      "votes": 15,
      "replies": [
        {
          "id": 1726052,
          "author_name": "CroDoc",
          "author_url": "",
          "post_date": "2022-03-17T15:59:03.777000",
          "content": "<p>Submission time 2h ha ha … crazy…</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1726247,
          "author_name": "Ken Krige",
          "author_url": "",
          "post_date": "2022-03-17T19:41:31.397000",
          "content": "<blockquote>\n  <p>This solution should be #1</p>\n</blockquote>\n<p>Agree</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1727404,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-18T00:34:41.197000",
          "content": "<p>Thank you.</p>\n<p>Yes there is room for improvement. I didn't try ensemble until the last day because I thought WBF would just work, however it did not work as expected. There are TPs in bigbird model output that deberta models does not catch, but I haven't found a proper merging method without adding too many FPs.</p>\n<p>The post process ideas in top solutions are something I would never came up with. My model using low threshold can't achieve the recall shared in the 1st solution. There's a lot to learn and try.</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 1725409,
      "author_name": "hxcc",
      "author_url": "",
      "post_date": "2022-03-17T04:33:18.033000",
      "content": "<p>love how you tackle an NLP problem with CV methods and made it work. Simple, elegant, very little ensembling, fast inference time. My favourite solution so far! Thanks for sharing!</p>",
      "votes": 9,
      "replies": []
    },
    {
      "id": 1725867,
      "author_name": "CroDoc",
      "author_url": "",
      "post_date": "2022-03-17T13:41:43.177000",
      "content": "<p>Your work on this competition is just WOW! Well done!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1725383,
      "author_name": "gezi",
      "author_url": "",
      "post_date": "2022-03-17T03:44:16.553000",
      "content": "<p>Really elegant and creative work!  Want to have a try😃</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1725362,
      "author_name": "KhanhVD",
      "author_url": "",
      "post_date": "2022-03-17T03:15:23.437000",
      "content": "<p>Interesting solution! Congrats on 6th place and thanks for sharing your approach.</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1848146,
      "author_name": "Mr Nagrom",
      "author_url": "",
      "post_date": "2022-07-08T11:55:03.963000",
      "content": "<p>Great model !!<br>\nI had thought of the same segmentation approach with YOLO but I failed to make my model as efficient as yours and I finally find myself at the 1937 position of the competition. But this is my first real competition on Kaggle.</p>\n<p>Your code is a real inspiration to me. Thanks for sharing !!!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1746097,
      "author_name": "gagaa",
      "author_url": "",
      "post_date": "2022-04-05T13:55:11.767000",
      "content": "<p>thanks for sharing! you mention \"In text span detection, I found the first word of span a good choice of positive.\",in this situation   is there no possible to predict firest location of span?so num_labels=1 + 1 + num_classes</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1748013,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-04-07T08:49:57.353000",
          "content": "<p>Yes. 1 is enough for single positive assignment, 2 is used for the extra dynamically assigned positive.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1731304,
      "author_name": "GUNER",
      "author_url": "",
      "post_date": "2022-03-22T08:15:12.473000",
      "content": "<p>One (more) tiny :-) question on 'dynamic_pozitive' targets :<br>\nAs the first word is always the target and the 'dynamic_pozitive' is an add on without a fixed grid-location (it is calculated based on minimum loss-rule) ,, wouldn't the contribution of 'dynamic_pozitive' targets diminish as you train? Since, the first word is always getting trained on and it dominates the network in time (,so: soon, 'first word' also dominates the 'min-loss' calculations (as the word with minimum loss)? </p>",
      "votes": 0,
      "replies": [
        {
          "id": 1732059,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-23T00:52:58.100000",
          "content": "<p>I had the same concern. The cost is not equal to loss, and there are random masking augmentation so in practice it does not happen and dynamic positive improves score. Actually in practice there could be multiple words with lower cost compared to the first word of span.</p>\n<p>Anyway, in the worst case it just falls back to single positive. I think it's fine.</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1732120,
          "author_name": "GUNER",
          "author_url": "",
          "post_date": "2022-03-23T03:18:48.940000",
          "content": "<p>Thanks again.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1731257,
      "author_name": "tyaba",
      "author_url": "",
      "post_date": "2022-03-22T06:48:21.077000",
      "content": "<p>Thank you for sharing your wonderful solution!<br>\nI would like to know what fg and bg refer to.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1732052,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-23T00:44:23.757000",
          "content": "<p>foreground and background</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1732100,
          "author_name": "tyaba",
          "author_url": "",
          "post_date": "2022-03-23T02:09:34.597000",
          "content": "<p>Thank you for your replying!</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1730428,
      "author_name": "Mohammad Al-Azawi",
      "author_url": "",
      "post_date": "2022-03-21T08:36:51.313000",
      "content": "<p>interesting </p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1729931,
      "author_name": "Aruna S",
      "author_url": "",
      "post_date": "2022-03-20T17:36:26.533000",
      "content": "<p>Well done !! Congratulations <a href=\"https://www.kaggle.com/tascj0\" target=\"_blank\">@tascj0</a> !!🤩🤩</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1729167,
      "author_name": "akshay kumar sahu",
      "author_url": "",
      "post_date": "2022-03-19T17:30:48.297000",
      "content": "<p>well done, get this to top</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1728624,
      "author_name": "Niek van der Zwaag",
      "author_url": "",
      "post_date": "2022-03-19T05:06:44.323000",
      "content": "<p>Creative solution, thanks for sharing your thoughts and process 🙌</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1727980,
      "author_name": "GUNER",
      "author_url": "",
      "post_date": "2022-03-18T13:53:44.310000",
      "content": "<p><em>'''Network logits is in shape (num_tokens, 10)'''</em><br>\nBecause the model creates output for each token (CV-pixel equivalent), isn't it more like a segmentation rathen than a detection job, thus falling not very far from NER approaches?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1728492,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-19T00:35:00.993000",
          "content": "<blockquote>\n  <p>isn't it more like a segmentation rathen than a detection job</p>\n</blockquote>\n<p>No. obj_loss is computed using all words output, reg_loss and cls_loss are computed using output at positive locations, others ignored. I suggest reading YOLO papers and source code to understand the training target design.</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1728571,
          "author_name": "GUNER",
          "author_url": "",
          "post_date": "2022-03-19T03:35:48.377000",
          "content": "<p>Got it, thanks. </p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1727963,
      "author_name": "GUNER",
      "author_url": "",
      "post_date": "2022-03-18T13:32:11.773000",
      "content": "<p><em>'''In addition, I assign lowest cost word in each span as positive during training.'''</em></p>\n<p>We are talking about the ground truth span and creating 'an additional positive' (in addition to the first word of the span) within the ground truth span, so that an additional loss will be calculated, right?<br>\nWhat is the reference, when you calculate 'lowest cost word'? in other words what is this other word 'lowest cost' of/to? <br>\nthank you. </p>",
      "votes": 0,
      "replies": [
        {
          "id": 1728488,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-19T00:32:07.833000",
          "content": "<blockquote>\n  <p>additional loss will be calculated, right?</p>\n</blockquote>\n<p>The more positive you have, the more \"samples\" you have to compute cls_loss and reg_loss. So yes.</p>\n<blockquote>\n  <p>what is this other word 'lowest cost' of/to?</p>\n</blockquote>\n<p>The lowest cost word of the span. It might be the first or not. The cost is computed using network output of current sample&amp;iteration. Please check the code for details.</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1728568,
          "author_name": "GUNER",
          "author_url": "",
          "post_date": "2022-03-19T03:34:44.717000",
          "content": "<p>Thank you.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1727874,
      "author_name": "patriot",
      "author_url": "",
      "post_date": "2022-03-18T12:05:21.700000",
      "content": "<p>Thank you for the original solution! There is much to learn from you.</p>\n<blockquote>\n  <p>I used one cycle policy in training and averaged weights of the last few epochs</p>\n</blockquote>\n<p>Is this something like SWA / EMA that averages weights in the middle of learning? Or did you average after learning like <a href=\"https://arxiv.org/abs/2203.05482\" target=\"_blank\">model soups</a>?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1728481,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-19T00:27:44.167000",
          "content": "<p>It's SWA, average of weights of different epochs in single hyperparameters setting. Model soups seems to be average of different weights from same pretrain using different hyperparameters.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 1727724,
      "author_name": "Patrick Chan",
      "author_url": "",
      "post_date": "2022-03-18T07:55:39.700000",
      "content": "<p>MasterStroke… or should I say Grandmaster strokes!  Well done and very innovative indeed crafting CV techniques into the NLP world! Thks very much for the share.  Much room for improvement with ensembling and post processing… Should get much higher ranking… Any idea why the WBF did not work in your case?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1728495,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-19T00:38:34.090000",
          "content": "<blockquote>\n  <p>Any idea why the WBF did not work in your case?</p>\n</blockquote>\n<p>Unfortunately no.</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1728895,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-19T11:50:41.033000",
          "content": "<p><a href=\"https://www.kaggle.com/drpatrickchan\" target=\"_blank\">@drpatrickchan</a> It seems to be just wrong hyperparameters. A low iou_thr (0.2) is necessary to ensemble my models. Didn't try iou_thr lower than 0.5.</p>\n<p>Thanks to the post processing code shared by <a href=\"https://www.kaggle.com/cdeotte\" target=\"_blank\">@cdeotte</a>, I checked the difference and soon found the problem. Different score thresholds for different classes also helps a bit after WBF, while it does not work for my models before WBF.</p>\n<p>The ensemble of 4models (longformer large + 3 deberta) x 2folds could get 0.720 public and 0.737 private (iou_thr=0.2, score_thr=0.3 for all classes).</p>",
          "votes": 5,
          "replies": []
        }
      ]
    },
    {
      "id": 1727582,
      "author_name": "kwchen",
      "author_url": "",
      "post_date": "2022-03-18T04:59:49.853000",
      "content": "<p>Congratulations and wow this solution is amazing!<br>\nFrom the YOLO paper:</p>\n<blockquote>\n  <p>It weights localization er\u0002ror equally with classification error which may not be ideal.</p>\n</blockquote>\n<p>Did you try tuning the weights between classification loss and regression loss? I feel like you could boost performance a lot with a bit of tuning.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1727681,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-18T07:14:59.430000",
          "content": "<p>Thank you.</p>\n<p>I tried a few settings, the loss weights between classification and regression does not matter much.</p>\n<p>The loss weight of objectness is important.<br>\nFollowing YOLOX, I use</p>\n<pre><code>        num_total_samples = obj_target.sum()\n        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='sum') / num_total_samples\n</code></pre>\n<p>If I haven't read the source code of YOLOX before, I may use</p>\n<pre><code>        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='mean')\n</code></pre>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 1726246,
      "author_name": "Ken Krige",
      "author_url": "",
      "post_date": "2022-03-17T19:40:44.330000",
      "content": "<p>So interesting - well done. I wish I could have spent more time on this competition, but definitely going to experiment with your code.</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1725662,
      "author_name": "SchenbergZ",
      "author_url": "",
      "post_date": "2022-03-17T09:49:02.183000",
      "content": "<p>Congrats! 2 questions: what is your first output head <em>objectness</em> meaning? Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1725700,
          "author_name": "tascj",
          "author_url": "",
          "post_date": "2022-03-17T10:50:09.603000",
          "content": "<p>Thank you.</p>\n<blockquote>\n  <p>what is your first output head objectness meaning? </p>\n</blockquote>\n<p>Objectness target is a vector of shape (num_words, ). For each ground truth span, you have at least one positive word (1 in the objectness) as representative of the span. Others words in the span are negative (0 in the objectness vector).</p>\n<blockquote>\n  <p>Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?</p>\n</blockquote>\n<p>All three heads' output are used in inference.</p>\n<p>I suggest reading YOLO papers and source code to understand the training target design and how to decode raw prediction to target in inference.</p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 1725611,
      "author_name": "GUNER",
      "author_url": "",
      "post_date": "2022-03-17T08:55:40.917000",
      "content": "<p>You nailed it without hitting the nail a thousand times :-)<br>\nYou should be the one!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1731317,
      "author_name": "",
      "author_url": "",
      "post_date": "2022-03-22T08:29:12.100000",
      "content": "",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1731539,
      "author_name": "xiaofei007-",
      "author_url": "",
      "post_date": "2022-03-22T13:17:20.577000",
      "content": "<p>this tip help me a lot,thank you</p>",
      "votes": 1,
      "replies": []
    }
  ],
  "comment_count": 40,
  "messages": [
    {
      "id": 1725353,
      "postDate": "2022-03-17T03:02:29.593Z",
      "content": "<p>Thank you for hosting this awesome competition and congrats to the winners.</p>\n<p>This problem is interesting and challenging. The data and annotations are high quality. It's a great learning and practicing experience working on this competition.</p>\n<p>I started this competition compeletely ignoring those shared NER baselines. I thought in an \"object detection\" task, segmentation + posprocess approaches can not do better than object detection approaches. I'm surprised by the postprocess ideas in top solutions.</p>\n<p>Overall, what I made is a YOLO-like text span detector. I share my code in <a href=\"https://www.kaggle.com/tascj0/a-text-span-detector\" target=\"_blank\">this notebook</a>.</p>\n<p>Checkpoints are shared <a href=\"https://www.kaggle.com/datasets/tascj0/feedback-checkpoints\" target=\"_blank\">here</a>.</p>\n<p>To reproduce the checkpoints, check the code and configs <a href=\"https://github.com/tascj/kaggle-feedback-prize-2021\" target=\"_blank\">here</a>.</p>\n<h2>Modeling</h2>\n<h3>Network</h3>\n<pre><code>AutoModelForTokenClassification.from_pretrained(num_labels=1 + 2 + num_classes, ...)\n</code></pre>\n<p>1 for objectness (or span-ness?. fg/bg classification)</p>\n<p>2 for regression (distance from fg location to first and last index of corresponding span)</p>\n<p>num_classes for discourse type classification</p>\n<h3>Aggregate tokens to words</h3>\n<p>Network logits is in shape (num_tokens, 10). This is inconvenient (decoding output, ensembling models with different tokenizers). So I aggregate the logits to (num_words, 10) using RoIAlign.</p>\n<h3>Training target</h3>\n<p>The problem of this formulation is how to define positive for objectness training. In object detection, center of object is a natural choice of positve. In text span detection, I found the first word of span a good choice of positive.</p>\n<p>In addition, I assign lowest cost word in each span as positive during training. This is inspired by YOLOX.</p>\n<h3>Augmentation, loss, decoding outputs, etc.</h3>\n<p>I randomly replace tokens with mask token during training.</p>\n<p>For other details, please check the code.</p>\n<h3>Post process</h3>\n<p>The only post process is nms.</p>\n<h3>Ensemble</h3>\n<p>I used one cycle policy in training and averaged weights of the last few epochs.<br>\nTo ensemble different models/folds, I simply averaged outputs of models.<br>\nWBF ensemble does not work in local validation, and I didn't figure out why. I think I did something wrong here.</p>\n<h2>Results</h2>\n<table>\n<thead>\n<tr>\n<th>backbone</th>\n<th>Validation</th>\n<th>Public LB</th>\n<th>Private LB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>google/bigbird-roberta-base</td>\n<td>0.685~0.69</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>allenai/longformer-base-4096</td>\n<td>0.685~0.69</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>allenai/longformer-large-4096</td>\n<td>0.70~0.71</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-base</td>\n<td>0.70~0.705</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-large</td>\n<td>0.715~0.72</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-xlarge</td>\n<td>0.715~0.72</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>microsoft/deberta-large + microsoft/deberta-xlarge</td>\n<td>0.723</td>\n<td>0.714</td>\n<td>0.732</td>\n</tr>\n</tbody>\n</table>\n<p>The best combination is <code>deberta-large</code> + <code>deberta-xlarge</code>. Ensembling more does not help.<br>\nIn the final submission, I used 2 weights (2/5 folds) each model. Submission time is around 2 hours.</p>",
      "rawMarkdown": "Thank you for hosting this awesome competition and congrats to the winners.\n\nThis problem is interesting and challenging. The data and annotations are high quality. It's a great learning and practicing experience working on this competition.\n\nI started this competition compeletely ignoring those shared NER baselines. I thought in an \"object detection\" task, segmentation + posprocess approaches can not do better than object detection approaches. I'm surprised by the postprocess ideas in top solutions.\n\nOverall, what I made is a YOLO-like text span detector. I share my code in [this notebook](https://www.kaggle.com/tascj0/a-text-span-detector).\n\nCheckpoints are shared [here](https://www.kaggle.com/datasets/tascj0/feedback-checkpoints).\n\nTo reproduce the checkpoints, check the code and configs [here](https://github.com/tascj/kaggle-feedback-prize-2021).\n\n\n## Modeling\n### Network\n\n```\nAutoModelForTokenClassification.from_pretrained(num_labels=1 + 2 + num_classes, ...)\n```\n\n1 for objectness (or span-ness?. fg/bg classification)\n\n2 for regression (distance from fg location to first and last index of corresponding span)\n\nnum_classes for discourse type classification\n\n### Aggregate tokens to words\n\nNetwork logits is in shape (num_tokens, 10). This is inconvenient (decoding output, ensembling models with different tokenizers). So I aggregate the logits to (num_words, 10) using RoIAlign.\n\n### Training target\n\nThe problem of this formulation is how to define positive for objectness training. In object detection, center of object is a natural choice of positve. In text span detection, I found the first word of span a good choice of positive.\n\nIn addition, I assign lowest cost word in each span as positive during training. This is inspired by YOLOX.\n\n### Augmentation, loss, decoding outputs, etc.\n\nI randomly replace tokens with mask token during training.\n\nFor other details, please check the code.\n\n### Post process\n\nThe only post process is nms.\n\n### Ensemble\n\nI used one cycle policy in training and averaged weights of the last few epochs.\nTo ensemble different models/folds, I simply averaged outputs of models.\nWBF ensemble does not work in local validation, and I didn't figure out why. I think I did something wrong here.\n\n## Results\n| backbone                                           | Validation | Public LB | Private LB |\n|----------------------------------------------------|------------|-----------|------------|\n| google/bigbird-roberta-base                        | 0.685~0.69 |           |            |\n| allenai/longformer-base-4096                       | 0.685~0.69 |           |            |\n| allenai/longformer-large-4096                      | 0.70~0.71  |           |            |\n| microsoft/deberta-base                             | 0.70~0.705 |           |            |\n| microsoft/deberta-large                            | 0.715~0.72 |           |            |\n| microsoft/deberta-xlarge                           | 0.715~0.72 |           |            |\n| microsoft/deberta-large + microsoft/deberta-xlarge | 0.723      | 0.714     | 0.732      |\n\nThe best combination is `deberta-large` + `deberta-xlarge`. Ensembling more does not help.\nIn the final submission, I used 2 weights (2/5 folds) each model. Submission time is around 2 hours.\n",
      "votes": 165
    },
    {
      "id": 1726035,
      "postDate": "2022-03-17T15:41:59.203Z",
      "content": "<p>This solution should be #1</p>\n<p>More seriously, it is very elegant and I'm pretty sure that there is room for improvement - perhaps it would outperform other approaches if combined with a features+boosting model :)</p>",
      "rawMarkdown": "This solution should be #1\n\nMore seriously, it is very elegant and I'm pretty sure that there is room for improvement - perhaps it would outperform other approaches if combined with a features+boosting model :)",
      "votes": 15,
      "replies": [
        {
          "id": 1726052,
          "postDate": "2022-03-17T15:59:03.777Z",
          "content": "<p>Submission time 2h ha ha … crazy…</p>",
          "rawMarkdown": "Submission time 2h ha ha ... crazy...",
          "votes": 1
        },
        {
          "id": 1726247,
          "postDate": "2022-03-17T19:41:31.397Z",
          "content": "<blockquote>\n  <p>This solution should be #1</p>\n</blockquote>\n<p>Agree</p>",
          "rawMarkdown": "> This solution should be #1\n\nAgree"
        },
        {
          "id": 1727404,
          "postDate": "2022-03-18T00:34:41.197Z",
          "content": "<p>Thank you.</p>\n<p>Yes there is room for improvement. I didn't try ensemble until the last day because I thought WBF would just work, however it did not work as expected. There are TPs in bigbird model output that deberta models does not catch, but I haven't found a proper merging method without adding too many FPs.</p>\n<p>The post process ideas in top solutions are something I would never came up with. My model using low threshold can't achieve the recall shared in the 1st solution. There's a lot to learn and try.</p>",
          "rawMarkdown": "Thank you.\n\nYes there is room for improvement. I didn't try ensemble until the last day because I thought WBF would just work, however it did not work as expected. There are TPs in bigbird model output that deberta models does not catch, but I haven't found a proper merging method without adding too many FPs.\n\nThe post process ideas in top solutions are something I would never came up with. My model using low threshold can't achieve the recall shared in the 1st solution. There's a lot to learn and try.",
          "votes": 2
        }
      ]
    },
    {
      "id": 1725409,
      "postDate": "2022-03-17T04:33:18.033Z",
      "content": "<p>love how you tackle an NLP problem with CV methods and made it work. Simple, elegant, very little ensembling, fast inference time. My favourite solution so far! Thanks for sharing!</p>",
      "rawMarkdown": "love how you tackle an NLP problem with CV methods and made it work. Simple, elegant, very little ensembling, fast inference time. My favourite solution so far! Thanks for sharing!",
      "votes": 9
    },
    {
      "id": 1725867,
      "postDate": "2022-03-17T13:41:43.177Z",
      "content": "<p>Your work on this competition is just WOW! Well done!</p>",
      "rawMarkdown": "Your work on this competition is just WOW! Well done!",
      "votes": 1
    },
    {
      "id": 1725383,
      "postDate": "2022-03-17T03:44:16.553Z",
      "content": "<p>Really elegant and creative work!  Want to have a try😃</p>",
      "rawMarkdown": "Really elegant and creative work!  Want to have a try😃",
      "votes": 1
    },
    {
      "id": 1725362,
      "postDate": "2022-03-17T03:15:23.437Z",
      "content": "<p>Interesting solution! Congrats on 6th place and thanks for sharing your approach.</p>",
      "rawMarkdown": "Interesting solution! Congrats on 6th place and thanks for sharing your approach.",
      "votes": 1
    },
    {
      "id": 1848146,
      "postDate": "2022-07-08T11:55:03.963Z",
      "content": "<p>Great model !!<br>\nI had thought of the same segmentation approach with YOLO but I failed to make my model as efficient as yours and I finally find myself at the 1937 position of the competition. But this is my first real competition on Kaggle.</p>\n<p>Your code is a real inspiration to me. Thanks for sharing !!!</p>",
      "rawMarkdown": "Great model !!\nI had thought of the same segmentation approach with YOLO but I failed to make my model as efficient as yours and I finally find myself at the 1937 position of the competition. But this is my first real competition on Kaggle.\n\nYour code is a real inspiration to me. Thanks for sharing !!!"
    },
    {
      "id": 1746097,
      "postDate": "2022-04-05T13:55:11.767Z",
      "content": "<p>thanks for sharing! you mention \"In text span detection, I found the first word of span a good choice of positive.\",in this situation   is there no possible to predict firest location of span?so num_labels=1 + 1 + num_classes</p>",
      "rawMarkdown": "thanks for sharing! you mention \"In text span detection, I found the first word of span a good choice of positive.\",in this situation   is there no possible to predict firest location of span?so num_labels=1 + 1 + num_classes",
      "replies": [
        {
          "id": 1748013,
          "postDate": "2022-04-07T08:49:57.353Z",
          "content": "<p>Yes. 1 is enough for single positive assignment, 2 is used for the extra dynamically assigned positive.</p>",
          "rawMarkdown": "Yes. 1 is enough for single positive assignment, 2 is used for the extra dynamically assigned positive."
        }
      ]
    },
    {
      "id": 1731304,
      "postDate": "2022-03-22T08:15:12.473Z",
      "content": "<p>One (more) tiny :-) question on 'dynamic_pozitive' targets :<br>\nAs the first word is always the target and the 'dynamic_pozitive' is an add on without a fixed grid-location (it is calculated based on minimum loss-rule) ,, wouldn't the contribution of 'dynamic_pozitive' targets diminish as you train? Since, the first word is always getting trained on and it dominates the network in time (,so: soon, 'first word' also dominates the 'min-loss' calculations (as the word with minimum loss)? </p>",
      "rawMarkdown": "One (more) tiny :-) question on 'dynamic_pozitive' targets :\nAs the first word is always the target and the 'dynamic_pozitive' is an add on without a fixed grid-location (it is calculated based on minimum loss-rule) ,, wouldn't the contribution of 'dynamic_pozitive' targets diminish as you train? Since, the first word is always getting trained on and it dominates the network in time (,so: soon, 'first word' also dominates the 'min-loss' calculations (as the word with minimum loss)? ",
      "replies": [
        {
          "id": 1732059,
          "postDate": "2022-03-23T00:52:58.100Z",
          "content": "<p>I had the same concern. The cost is not equal to loss, and there are random masking augmentation so in practice it does not happen and dynamic positive improves score. Actually in practice there could be multiple words with lower cost compared to the first word of span.</p>\n<p>Anyway, in the worst case it just falls back to single positive. I think it's fine.</p>",
          "rawMarkdown": "I had the same concern. The cost is not equal to loss, and there are random masking augmentation so in practice it does not happen and dynamic positive improves score. Actually in practice there could be multiple words with lower cost compared to the first word of span.\n\nAnyway, in the worst case it just falls back to single positive. I think it's fine.",
          "votes": 1
        },
        {
          "id": 1732120,
          "postDate": "2022-03-23T03:18:48.940Z",
          "content": "<p>Thanks again.</p>",
          "rawMarkdown": "Thanks again."
        }
      ]
    },
    {
      "id": 1731257,
      "postDate": "2022-03-22T06:48:21.077Z",
      "content": "<p>Thank you for sharing your wonderful solution!<br>\nI would like to know what fg and bg refer to.</p>",
      "rawMarkdown": "Thank you for sharing your wonderful solution!\nI would like to know what fg and bg refer to.",
      "replies": [
        {
          "id": 1732052,
          "postDate": "2022-03-23T00:44:23.757Z",
          "content": "<p>foreground and background</p>",
          "rawMarkdown": "foreground and background",
          "votes": 1
        },
        {
          "id": 1732100,
          "postDate": "2022-03-23T02:09:34.597Z",
          "content": "<p>Thank you for your replying!</p>",
          "rawMarkdown": "Thank you for your replying!"
        }
      ]
    },
    {
      "id": 1730428,
      "postDate": "2022-03-21T08:36:51.313Z",
      "content": "<p>interesting </p>",
      "rawMarkdown": "interesting "
    },
    {
      "id": 1729931,
      "postDate": "2022-03-20T17:36:26.533Z",
      "content": "<p>Well done !! Congratulations <a href=\"https://www.kaggle.com/tascj0\" target=\"_blank\">@tascj0</a> !!🤩🤩</p>",
      "rawMarkdown": "Well done !! Congratulations @tascj0 !!🤩🤩"
    },
    {
      "id": 1729167,
      "postDate": "2022-03-19T17:30:48.297Z",
      "content": "<p>well done, get this to top</p>",
      "rawMarkdown": "well done, get this to top"
    },
    {
      "id": 1728624,
      "postDate": "2022-03-19T05:06:44.323Z",
      "content": "<p>Creative solution, thanks for sharing your thoughts and process 🙌</p>",
      "rawMarkdown": "Creative solution, thanks for sharing your thoughts and process 🙌"
    },
    {
      "id": 1727980,
      "postDate": "2022-03-18T13:53:44.310Z",
      "content": "<p><em>'''Network logits is in shape (num_tokens, 10)'''</em><br>\nBecause the model creates output for each token (CV-pixel equivalent), isn't it more like a segmentation rathen than a detection job, thus falling not very far from NER approaches?</p>",
      "rawMarkdown": "*'''Network logits is in shape (num_tokens, 10)'''*\nBecause the model creates output for each token (CV-pixel equivalent), isn't it more like a segmentation rathen than a detection job, thus falling not very far from NER approaches?",
      "replies": [
        {
          "id": 1728492,
          "postDate": "2022-03-19T00:35:00.993Z",
          "content": "<blockquote>\n  <p>isn't it more like a segmentation rathen than a detection job</p>\n</blockquote>\n<p>No. obj_loss is computed using all words output, reg_loss and cls_loss are computed using output at positive locations, others ignored. I suggest reading YOLO papers and source code to understand the training target design.</p>",
          "rawMarkdown": "> isn't it more like a segmentation rathen than a detection job\n\nNo. obj_loss is computed using all words output, reg_loss and cls_loss are computed using output at positive locations, others ignored. I suggest reading YOLO papers and source code to understand the training target design.",
          "votes": 1
        },
        {
          "id": 1728571,
          "postDate": "2022-03-19T03:35:48.377Z",
          "content": "<p>Got it, thanks. </p>",
          "rawMarkdown": "Got it, thanks. "
        }
      ]
    },
    {
      "id": 1727963,
      "postDate": "2022-03-18T13:32:11.773Z",
      "content": "<p><em>'''In addition, I assign lowest cost word in each span as positive during training.'''</em></p>\n<p>We are talking about the ground truth span and creating 'an additional positive' (in addition to the first word of the span) within the ground truth span, so that an additional loss will be calculated, right?<br>\nWhat is the reference, when you calculate 'lowest cost word'? in other words what is this other word 'lowest cost' of/to? <br>\nthank you. </p>",
      "rawMarkdown": "*'''In addition, I assign lowest cost word in each span as positive during training.'''*\n\nWe are talking about the ground truth span and creating 'an additional positive' (in addition to the first word of the span) within the ground truth span, so that an additional loss will be calculated, right?\nWhat is the reference, when you calculate 'lowest cost word'? in other words what is this other word 'lowest cost' of/to? \nthank you. ",
      "replies": [
        {
          "id": 1728488,
          "postDate": "2022-03-19T00:32:07.833Z",
          "content": "<blockquote>\n  <p>additional loss will be calculated, right?</p>\n</blockquote>\n<p>The more positive you have, the more \"samples\" you have to compute cls_loss and reg_loss. So yes.</p>\n<blockquote>\n  <p>what is this other word 'lowest cost' of/to?</p>\n</blockquote>\n<p>The lowest cost word of the span. It might be the first or not. The cost is computed using network output of current sample&amp;iteration. Please check the code for details.</p>",
          "rawMarkdown": "> additional loss will be calculated, right?\n\nThe more positive you have, the more \"samples\" you have to compute cls_loss and reg_loss. So yes.\n\n> what is this other word 'lowest cost' of/to?\n\nThe lowest cost word of the span. It might be the first or not. The cost is computed using network output of current sample&iteration. Please check the code for details.",
          "votes": 1
        },
        {
          "id": 1728568,
          "postDate": "2022-03-19T03:34:44.717Z",
          "content": "<p>Thank you.</p>",
          "rawMarkdown": "Thank you."
        }
      ]
    },
    {
      "id": 1727874,
      "postDate": "2022-03-18T12:05:21.700Z",
      "content": "<p>Thank you for the original solution! There is much to learn from you.</p>\n<blockquote>\n  <p>I used one cycle policy in training and averaged weights of the last few epochs</p>\n</blockquote>\n<p>Is this something like SWA / EMA that averages weights in the middle of learning? Or did you average after learning like <a href=\"https://arxiv.org/abs/2203.05482\" target=\"_blank\">model soups</a>?</p>",
      "rawMarkdown": "Thank you for the original solution! There is much to learn from you.\n> I used one cycle policy in training and averaged weights of the last few epochs\n\nIs this something like SWA / EMA that averages weights in the middle of learning? Or did you average after learning like [model soups](https://arxiv.org/abs/2203.05482)?",
      "replies": [
        {
          "id": 1728481,
          "postDate": "2022-03-19T00:27:44.167Z",
          "content": "<p>It's SWA, average of weights of different epochs in single hyperparameters setting. Model soups seems to be average of different weights from same pretrain using different hyperparameters.</p>",
          "rawMarkdown": "It's SWA, average of weights of different epochs in single hyperparameters setting. Model soups seems to be average of different weights from same pretrain using different hyperparameters.",
          "votes": 1
        }
      ]
    },
    {
      "id": 1727724,
      "postDate": "2022-03-18T07:55:39.700Z",
      "content": "<p>MasterStroke… or should I say Grandmaster strokes!  Well done and very innovative indeed crafting CV techniques into the NLP world! Thks very much for the share.  Much room for improvement with ensembling and post processing… Should get much higher ranking… Any idea why the WBF did not work in your case?</p>",
      "rawMarkdown": "MasterStroke... or should I say Grandmaster strokes!  Well done and very innovative indeed crafting CV techniques into the NLP world! Thks very much for the share.  Much room for improvement with ensembling and post processing... Should get much higher ranking... Any idea why the WBF did not work in your case?",
      "replies": [
        {
          "id": 1728495,
          "postDate": "2022-03-19T00:38:34.090Z",
          "content": "<blockquote>\n  <p>Any idea why the WBF did not work in your case?</p>\n</blockquote>\n<p>Unfortunately no.</p>",
          "rawMarkdown": "> Any idea why the WBF did not work in your case?\n\nUnfortunately no."
        },
        {
          "id": 1728895,
          "postDate": "2022-03-19T11:50:41.033Z",
          "content": "<p><a href=\"https://www.kaggle.com/drpatrickchan\" target=\"_blank\">@drpatrickchan</a> It seems to be just wrong hyperparameters. A low iou_thr (0.2) is necessary to ensemble my models. Didn't try iou_thr lower than 0.5.</p>\n<p>Thanks to the post processing code shared by <a href=\"https://www.kaggle.com/cdeotte\" target=\"_blank\">@cdeotte</a>, I checked the difference and soon found the problem. Different score thresholds for different classes also helps a bit after WBF, while it does not work for my models before WBF.</p>\n<p>The ensemble of 4models (longformer large + 3 deberta) x 2folds could get 0.720 public and 0.737 private (iou_thr=0.2, score_thr=0.3 for all classes).</p>",
          "rawMarkdown": "@drpatrickchan It seems to be just wrong hyperparameters. A low iou_thr (0.2) is necessary to ensemble my models. Didn't try iou_thr lower than 0.5.\n\nThanks to the post processing code shared by @cdeotte, I checked the difference and soon found the problem. Different score thresholds for different classes also helps a bit after WBF, while it does not work for my models before WBF.\n\nThe ensemble of 4models (longformer large + 3 deberta) x 2folds could get 0.720 public and 0.737 private (iou_thr=0.2, score_thr=0.3 for all classes).",
          "votes": 5
        }
      ]
    },
    {
      "id": 1727582,
      "postDate": "2022-03-18T04:59:49.853Z",
      "content": "<p>Congratulations and wow this solution is amazing!<br>\nFrom the YOLO paper:</p>\n<blockquote>\n  <p>It weights localization er\u0002ror equally with classification error which may not be ideal.</p>\n</blockquote>\n<p>Did you try tuning the weights between classification loss and regression loss? I feel like you could boost performance a lot with a bit of tuning.</p>",
      "rawMarkdown": "Congratulations and wow this solution is amazing!\nFrom the YOLO paper:\n> It weights localization er\u0002ror equally with classification error which may not be ideal.\n\nDid you try tuning the weights between classification loss and regression loss? I feel like you could boost performance a lot with a bit of tuning.",
      "replies": [
        {
          "id": 1727681,
          "postDate": "2022-03-18T07:14:59.430Z",
          "content": "<p>Thank you.</p>\n<p>I tried a few settings, the loss weights between classification and regression does not matter much.</p>\n<p>The loss weight of objectness is important.<br>\nFollowing YOLOX, I use</p>\n<pre><code>        num_total_samples = obj_target.sum()\n        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='sum') / num_total_samples\n</code></pre>\n<p>If I haven't read the source code of YOLOX before, I may use</p>\n<pre><code>        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='mean')\n</code></pre>",
          "rawMarkdown": "Thank you.\n\nI tried a few settings, the loss weights between classification and regression does not matter much.\n\nThe loss weight of objectness is important.\nFollowing YOLOX, I use\n```\n        num_total_samples = obj_target.sum()\n        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='sum') / num_total_samples\n\n```\nIf I haven't read the source code of YOLOX before, I may use\n\n```\n        obj_loss = F.binary_cross_entropy_with_logits(\n            obj_pred, obj_target, reduction='mean')\n\n```",
          "votes": 2
        }
      ]
    },
    {
      "id": 1726246,
      "postDate": "2022-03-17T19:40:44.330Z",
      "content": "<p>So interesting - well done. I wish I could have spent more time on this competition, but definitely going to experiment with your code.</p>",
      "rawMarkdown": "So interesting - well done. I wish I could have spent more time on this competition, but definitely going to experiment with your code."
    },
    {
      "id": 1725662,
      "postDate": "2022-03-17T09:49:02.183Z",
      "content": "<p>Congrats! 2 questions: what is your first output head <em>objectness</em> meaning? Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?</p>",
      "rawMarkdown": "Congrats! 2 questions: what is your first output head *objectness* meaning? Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?",
      "replies": [
        {
          "id": 1725700,
          "postDate": "2022-03-17T10:50:09.603Z",
          "content": "<p>Thank you.</p>\n<blockquote>\n  <p>what is your first output head objectness meaning? </p>\n</blockquote>\n<p>Objectness target is a vector of shape (num_words, ). For each ground truth span, you have at least one positive word (1 in the objectness) as representative of the span. Others words in the span are negative (0 in the objectness vector).</p>\n<blockquote>\n  <p>Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?</p>\n</blockquote>\n<p>All three heads' output are used in inference.</p>\n<p>I suggest reading YOLO papers and source code to understand the training target design and how to decode raw prediction to target in inference.</p>",
          "rawMarkdown": "Thank you.\n\n> what is your first output head objectness meaning? \n\nObjectness target is a vector of shape (num_words, ). For each ground truth span, you have at least one positive word (1 in the objectness) as representative of the span. Others words in the span are negative (0 in the objectness vector).\n\n> Is your inference result only based on the 2nd output-head calculation and the discource-type based on 3rd output head?\n\nAll three heads' output are used in inference.\n\nI suggest reading YOLO papers and source code to understand the training target design and how to decode raw prediction to target in inference.",
          "votes": 2
        }
      ]
    },
    {
      "id": 1725611,
      "postDate": "2022-03-17T08:55:40.917Z",
      "content": "<p>You nailed it without hitting the nail a thousand times :-)<br>\nYou should be the one!</p>",
      "rawMarkdown": "You nailed it without hitting the nail a thousand times :-)\nYou should be the one!"
    },
    {
      "id": 1731317,
      "postDate": "2022-03-22T08:29:12.100Z",
      "rawMarkdown": "",
      "isDeleted": true
    },
    {
      "id": 1731539,
      "postDate": "2022-03-22T13:17:20.577Z",
      "content": "<p>this tip help me a lot,thank you</p>",
      "rawMarkdown": "this tip help me a lot,thank you",
      "votes": 1
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-19T12:35:58Z",
  "competition_id": 31779,
  "competition_slug": "feedback-prize-2021",
  "team_id": 8169219,
  "solution_url": "/competitions/feedback-prize-2021/writeups/rist-tascj-6th-place-solution-a-yolo-like-text-spa"
}