{
  "id": 551565,
  "title": "6th Place Solution",
  "content": "<p>I am very happy to have finally achieved the goal that I set for myself a year ago when I began relatively seriously participating in Kaggle competitions: solo gold. \nAlthough I had previously reached solo gold on the public a couple of times, I did not succeed to survive on the private. This time, dedication over the last months, focusing on a generalizable solution and the relatively similar distributions of public and private data have enabled me to reach this goal.</p>\n<h1><strong>Gratitude</strong></h1>\n<p>I want to thank Kaggle and the hosts for organizing such an interesting competition, where we had to explore the reasoning abilities of different approaches to effectively rank math misconceptions by their relevance to the distractors. \nSpecial gratitude for designing public/private splits with a balanced number of seen and unseen misconceptions from the training data (I am sure that gold places that survived agree with me). That allowed to reflect the robustness of the developed approaches on the leaderboard, resulting in only a very moderate shuffle on the private. After the last competitions, I really feel that I should thank for that :) </p>\n<h1><strong>Solution</strong></h1>\n<h2>Diagram showing the general overview of the solution:</h2>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6343664%2F62b07ce60a6bfd9df65038420514f9ae%2Feedi_pipeline.jpg?generation=1734122724162162&amp;alt=media\" alt=\"\"></p>\n<h2>Validation strategy</h2>\n<p>From the beginning, as most participants, I could not find a good correlation between CV by the group split on question id and the public LB. So it became obvious that hidden data contains multiple unseen misconceptions that were not present in the train and I should make the validation split in the way that it would incorporate a part of the unseen misconceptions that train data would not have. \nDespite the group split on question id already provided a certain proportion of the unseen misconceptions, they usually took up maximum up to 20% of all misconceptions in the fold, that is why a lot of team faced the overfit to seen misconceptions that provided good general CV score even if unseen ones were very suppressed in terms of retrieving.\nFor example, if fold consists of <strong>~80% seen</strong> data and <strong>20% unseen</strong>, then with severe overfit to seen misconcepts like 65% MRR and the lack of good retrieving for unseen like 30% MRR,  the weighted average of the MRR would be\n <code>0.65*0.8 + 0.3*0.2 = 0.58</code>\nand that would be far away from what the LB would display.\nFinally, I based my validation stratagy on an assumaption that the hidden distribution is quite balanced in terms of seen/unseen misconceptions and the key for the good CV is to set equal weight to seen and unseen misconceptions.\nTo maintain a high dynamic of experiments and avoid spending too much on VMs, I stuck with using only one fold as a validation set among experiments. This strategy proved sufficient, as the results across different folds were very similar.</p>\n<h2>Synthetic data generation</h2>\n<p>Attempts to train retriever models solely on the provided train data showed bad results, so it became inevitable to  generate synthetic data for unseen misconceptions.\nWhile experimenting with LORA pretraining and few-shot generation using Qwen 32/72 instruct, I discovered that LORA pretraining seems to decrease the models' reasoning abilities (catastrophic forgetting ?). The best approach turned out to be few-shot generation. Later, I came across the great <a href=\"https://arxiv.org/pdf/2404.02124\" target=\"_blank\">EEDI paper</a>, which reached a similar conclusion that few-shot generation is more effective for this task.</p>\n<p>The main drawback of few-shot generation was that the generated samples were very similar in formulation to the few-shot examples. Any attempt at prompt engineering couldn’t change this without losing the correct connection between distractors and misconceptions.</p>\n<p><strong>Generation details:</strong></p>\n<ul>\n<li>Qwen 72B instruct (32B struggled with tricky misconcepts)</li>\n<li>3 random few-shot examples for most similar train misconcepts</li>\n<li>Full math test generation aligned with train data structure, but with only 1 distractor relevant to misconcept</li>\n<li>3 math tests per misconcept repeated for 3 different seeds. So in total 9 tests for each misconcept</li>\n<li>2xA40 took 3 hours to generate all samples</li>\n</ul>\n<h2>Retrievers</h2>\n<p>The LaTex format of the math problems contained many redundant symbols that slowed the convergence. So instead of manually filtering the text, I researched open-source libs to convert LaTex to plain text and simplify equations. Although most libs ingored most redundant symbols, I found <code>pylatexenc</code> lib, which handled the conversion quite well, mostly without losing important elements from the text or equations (there were a few cases in the training data with redundant symbols that did not follow LaTex formatting at all). However, I included two models in the final ensemble that used data without LaTex filtration to ensure no important information was lost.\nAn interesting moment is that, even though the model achieved a much higher MRR for unseen misconceptions compared to seen ones (due to synthetic oversampling) after training including the generated data, the recall remained the same between seen and unseen misconcepts. I believe the root cause is that the synthetic data allowed the model to converge well for simpler misconceptions but struggled to generate math tests for complex misconcepts. As a result, the model could retrieve most unseen misconcepts well (resulting in a high MRR) but was unable to find relevance for other complex misconcepts, leading to the same recall as for underrepresented seen misconcepts which math tests were similar between train and validation splits. </p>\n<p><strong>Retriever details:</strong></p>\n<ul>\n<li>Models: gte-Qwen2-7B-instruct, Salesforce-SFR-Embedding-2_R, bge-multilingual-gemma2, <a href=\"https://www.kaggle.com/code/anhvth226/eedi-11-21-14b\" target=\"_blank\">Qwen2.5-14B-Instruct</a> </li>\n<li>Lora parameters:<ul>\n<li>rank = 64</li>\n<li>alpha = 128</li></ul></li>\n<li>Batch size = 32</li>\n<li>Negatives per sample =  5 (from 0 to - 30 most similar negatives)</li>\n<li>Two neg mining iterations:<ul>\n<li>initial neg mining with the subsequent 1600 training steps</li>\n<li>second neg mining iteration by the pretrained model with the subsequent 400 training steps</li></ul></li>\n<li>BNB 4 bit quantization</li>\n<li>1xRTX 3090 took 9 hours to train one model</li>\n</ul>\n<h2>Rerankers</h2>\n<p>Using the <code>DataCollatorForCompletionOnlyLM</code> was crucial to exclude from training prompt tokens.\nAnother critical aspect was excluding all test misconceptions from training. I observed that training solely on the training misconceptions allowed the model to generalize effectively to unseen misconceptions. Excluding test msicocnepts gave me probably the biggest LB improvement after reaching 0.6 on the public = ~0.61 - 0.63. </p>\n<h3>Distillation</h3>\n<p>Close to the deadline, I discovered an interesting trick: first, train the model on one seed. Then, create a dataset using a different seed (which usually alters the set of negative samples and the position of the correct misconception). Use the already trained model to infer logits on this new dataset. Finally, retrain the model with KL divergence distillation combined with simple cross-entropy loss. This approach yielded a ~0.01 improvement on the public. \nMy intuition behind this trick is that, for some samples, more than one misconception may technically be correct. Overfitting the model to a single misconception reduces its ability to generalize. By incorporating soft labels through distillation, we mitigate overfitting and improve the model's generalization to unseen misconceptions.</p>\n<h3>First stage reranker</h3>\n<p>I randomly sampled 49 negative misconceptions from the 100 most similar misconceptions and placed the correct misconception at a random position. Then, I trained the model to classify which misconception was most relevant. The random order generated through sampling helped avoid overfitting to the initial order, where the retriever often placed the correct misconception among the first few options.</p>\n<h3>Misconception rephrasing</h3>\n<p>I noticed that misconceptions vary significantly, for example, some describe what knowledge is lacking, while others describe what the student believes. To address this, I rephrased the misconceptions into a general format that includes both the missing knowledge and the student’s belief that aligns with the misconception. Even when the rephrased misconceptions semantically repeated the original ones, this rephrasing served as effective regularization during model inference.\nI used the inference logits from the rephrased misconceptions and combined them with the logits from the original misconceptions, which resulted in the ~0.01 public improvement.\nTo rephrase misconceptions Qwen 32B-Instruct was used.</p>\n<h3>Second stage reranker</h3>\n<p>Analyzing the logits obtained from the first-stage reranker, I noticed that Qwen tends to preserve the initial order of misconceptions as sorted by the retriever. I found further validation for this observation in <a href=\"https://arxiv.org/pdf/2306.17563\" target=\"_blank\">this excellent paper</a>, which inspired me to retrain Retrievers with more focus on MRR and develop the second-stage reranker to refine the results of the first stage reranker.\nIn the second stage, I selected a small window of the top ranked misconceptions from the first stage reranker and applied all possible permutations to their order. I then averaged the logits across these permutations and reranked misconcepts from that window again. For my surprise, the optimal window size turned out to be 2. This means I took the top 2 misconceptions from the first stage reranker, performed inference on them, inverted their order, ran inference again, and finally used the averaged logits for the final reranking.\n<strong>Reranker details:</strong></p>\n<ul>\n<li>Qwen 32B instruct was used</li>\n<li>Lora parameters:<ul>\n<li>rank = 64</li>\n<li>alpha = 128</li></ul></li>\n<li>Batch size = 2 * 6 grad accum</li>\n<li>AWQ 4 bit quantization</li>\n<li>1xA40 took 6 hours to train one model</li>\n</ul>\n<h1>What did not work</h1>\n<ul>\n<li><strong>A lot of things</strong></li>\n<li>Training fp16 Rerankers with all available lora adapters with subsequent qunatization</li>\n<li>Training Qwen 72B as a Reranker. I got the same results as for 32B</li>\n<li>Using synthtetic data in the Reranker training</li>\n</ul>\n<h1>Hardware</h1>\n<p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable. \nI mostly used A40, and for some experiments, I rented A100. \nIn total, this cost me about $300.</p>",
  "comments": [
    {
      "id": 3073295,
      "author_name": "Ahmet Erdem",
      "author_url": "",
      "post_date": "2024-12-16T09:50:30.257000",
      "content": "<blockquote>\n  <p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable.</p>\n</blockquote>\n<p>Solo gold is already a huge success but winning it under invasion is very impressive. Congrats! I wish and hope it will all end soon.</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3071818,
      "author_name": "Oleh Kivernyk",
      "author_url": "",
      "post_date": "2024-12-14T11:12:24.197000",
      "content": "<p>Congratulations on the gold medal! Nice and solid solution : )</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3071614,
      "author_name": "Donghoon Jang",
      "author_url": "",
      "post_date": "2024-12-14T02:31:54.400000",
      "content": "<p>Congratulations on your solo gold medal, Nikita Babich! 😀 </p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3071598,
      "author_name": "James owen",
      "author_url": "",
      "post_date": "2024-12-14T01:52:49.043000",
      "content": "<p>congratulations nikita, I am delighted with your achievements. <br>\nby the way, will you release the complete code? I can't wait to learn your code!!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3071579,
      "author_name": "Digital-Munchikin",
      "author_url": "",
      "post_date": "2024-12-14T00:32:12.177000",
      "content": "<p>🇺🇦 really inspirational! </p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3071566,
      "author_name": "Nicholas Broad",
      "author_url": "",
      "post_date": "2024-12-13T23:51:45.230000",
      "content": "<h1>🇺🇦</h1>\n<p>Well done!</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3073987,
      "author_name": "Yohei Tomio",
      "author_url": "",
      "post_date": "2024-12-17T07:24:49.700000",
      "content": "<p><a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a> <br>\nCongratulations! <br>\nI always thought that you would eventually win a solo gold because you were always in a position to aim for the gold in various competitions that I participated in.</p>",
      "votes": 2,
      "replies": []
    },
    {
      "id": 3071561,
      "author_name": "Jagat Kiran",
      "author_url": "",
      "post_date": "2024-12-13T23:39:53.523000",
      "content": "<p>Considering this-</p>\n<blockquote>\n  <p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day</p>\n</blockquote>\n<p>I salute you for your dedication. Solo gold is no minor feat and you having done it with real-life challenges is absolute god-tier. <br>\nCongrats Nikita- You fully deserve it</p>",
      "votes": 2,
      "replies": [
        {
          "id": 3071562,
          "author_name": "Nikita Babych",
          "author_url": "",
          "post_date": "2024-12-13T23:42:34.623000",
          "content": "<p>Thank you! Really appreciate it. </p>",
          "votes": 2,
          "replies": []
        }
      ]
    },
    {
      "id": 3071862,
      "author_name": "Sinan Calisir",
      "author_url": "",
      "post_date": "2024-12-14T12:17:16.987000",
      "content": "<p>Congratulations on your solo gold <a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a>! It's nice to see you succeed after the ISIC competition!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 3074930,
      "author_name": "Bikram Ghuku",
      "author_url": "",
      "post_date": "2024-12-18T08:11:27.917000",
      "content": "<p>Congratulations! Winning a solo gold</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 3072883,
      "author_name": "MrSimple",
      "author_url": "",
      "post_date": "2024-12-15T18:01:11.200000",
      "content": "<p>Congratulations on your solo gold <a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a>! It's nice to see you succeed after the ISIC competition!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 3073358,
      "author_name": "Santiago Mota",
      "author_url": "",
      "post_date": "2024-12-16T11:57:51.960000",
      "content": "<p>Congrats and thanks for the explanation</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3073062,
      "author_name": "le van tao",
      "author_url": "",
      "post_date": "2024-12-16T02:13:47.110000",
      "content": "<p>Great work!</p>",
      "votes": 1,
      "replies": []
    }
  ],
  "comment_count": 14,
  "messages": [
    {
      "id": 3071558,
      "postDate": "2024-12-13T23:34:17.513Z",
      "content": "<p>I am very happy to have finally achieved the goal that I set for myself a year ago when I began relatively seriously participating in Kaggle competitions: solo gold. \nAlthough I had previously reached solo gold on the public a couple of times, I did not succeed to survive on the private. This time, dedication over the last months, focusing on a generalizable solution and the relatively similar distributions of public and private data have enabled me to reach this goal.</p>\n<h1><strong>Gratitude</strong></h1>\n<p>I want to thank Kaggle and the hosts for organizing such an interesting competition, where we had to explore the reasoning abilities of different approaches to effectively rank math misconceptions by their relevance to the distractors. \nSpecial gratitude for designing public/private splits with a balanced number of seen and unseen misconceptions from the training data (I am sure that gold places that survived agree with me). That allowed to reflect the robustness of the developed approaches on the leaderboard, resulting in only a very moderate shuffle on the private. After the last competitions, I really feel that I should thank for that :) </p>\n<h1><strong>Solution</strong></h1>\n<h2>Diagram showing the general overview of the solution:</h2>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6343664%2F62b07ce60a6bfd9df65038420514f9ae%2Feedi_pipeline.jpg?generation=1734122724162162&amp;alt=media\" alt=\"\"></p>\n<h2>Validation strategy</h2>\n<p>From the beginning, as most participants, I could not find a good correlation between CV by the group split on question id and the public LB. So it became obvious that hidden data contains multiple unseen misconceptions that were not present in the train and I should make the validation split in the way that it would incorporate a part of the unseen misconceptions that train data would not have. \nDespite the group split on question id already provided a certain proportion of the unseen misconceptions, they usually took up maximum up to 20% of all misconceptions in the fold, that is why a lot of team faced the overfit to seen misconceptions that provided good general CV score even if unseen ones were very suppressed in terms of retrieving.\nFor example, if fold consists of <strong>~80% seen</strong> data and <strong>20% unseen</strong>, then with severe overfit to seen misconcepts like 65% MRR and the lack of good retrieving for unseen like 30% MRR,  the weighted average of the MRR would be\n <code>0.65*0.8 + 0.3*0.2 = 0.58</code>\nand that would be far away from what the LB would display.\nFinally, I based my validation stratagy on an assumaption that the hidden distribution is quite balanced in terms of seen/unseen misconceptions and the key for the good CV is to set equal weight to seen and unseen misconceptions.\nTo maintain a high dynamic of experiments and avoid spending too much on VMs, I stuck with using only one fold as a validation set among experiments. This strategy proved sufficient, as the results across different folds were very similar.</p>\n<h2>Synthetic data generation</h2>\n<p>Attempts to train retriever models solely on the provided train data showed bad results, so it became inevitable to  generate synthetic data for unseen misconceptions.\nWhile experimenting with LORA pretraining and few-shot generation using Qwen 32/72 instruct, I discovered that LORA pretraining seems to decrease the models' reasoning abilities (catastrophic forgetting ?). The best approach turned out to be few-shot generation. Later, I came across the great <a href=\"https://arxiv.org/pdf/2404.02124\" target=\"_blank\">EEDI paper</a>, which reached a similar conclusion that few-shot generation is more effective for this task.</p>\n<p>The main drawback of few-shot generation was that the generated samples were very similar in formulation to the few-shot examples. Any attempt at prompt engineering couldn’t change this without losing the correct connection between distractors and misconceptions.</p>\n<p><strong>Generation details:</strong></p>\n<ul>\n<li>Qwen 72B instruct (32B struggled with tricky misconcepts)</li>\n<li>3 random few-shot examples for most similar train misconcepts</li>\n<li>Full math test generation aligned with train data structure, but with only 1 distractor relevant to misconcept</li>\n<li>3 math tests per misconcept repeated for 3 different seeds. So in total 9 tests for each misconcept</li>\n<li>2xA40 took 3 hours to generate all samples</li>\n</ul>\n<h2>Retrievers</h2>\n<p>The LaTex format of the math problems contained many redundant symbols that slowed the convergence. So instead of manually filtering the text, I researched open-source libs to convert LaTex to plain text and simplify equations. Although most libs ingored most redundant symbols, I found <code>pylatexenc</code> lib, which handled the conversion quite well, mostly without losing important elements from the text or equations (there were a few cases in the training data with redundant symbols that did not follow LaTex formatting at all). However, I included two models in the final ensemble that used data without LaTex filtration to ensure no important information was lost.\nAn interesting moment is that, even though the model achieved a much higher MRR for unseen misconceptions compared to seen ones (due to synthetic oversampling) after training including the generated data, the recall remained the same between seen and unseen misconcepts. I believe the root cause is that the synthetic data allowed the model to converge well for simpler misconceptions but struggled to generate math tests for complex misconcepts. As a result, the model could retrieve most unseen misconcepts well (resulting in a high MRR) but was unable to find relevance for other complex misconcepts, leading to the same recall as for underrepresented seen misconcepts which math tests were similar between train and validation splits. </p>\n<p><strong>Retriever details:</strong></p>\n<ul>\n<li>Models: gte-Qwen2-7B-instruct, Salesforce-SFR-Embedding-2_R, bge-multilingual-gemma2, <a href=\"https://www.kaggle.com/code/anhvth226/eedi-11-21-14b\" target=\"_blank\">Qwen2.5-14B-Instruct</a> </li>\n<li>Lora parameters:<ul>\n<li>rank = 64</li>\n<li>alpha = 128</li></ul></li>\n<li>Batch size = 32</li>\n<li>Negatives per sample =  5 (from 0 to - 30 most similar negatives)</li>\n<li>Two neg mining iterations:<ul>\n<li>initial neg mining with the subsequent 1600 training steps</li>\n<li>second neg mining iteration by the pretrained model with the subsequent 400 training steps</li></ul></li>\n<li>BNB 4 bit quantization</li>\n<li>1xRTX 3090 took 9 hours to train one model</li>\n</ul>\n<h2>Rerankers</h2>\n<p>Using the <code>DataCollatorForCompletionOnlyLM</code> was crucial to exclude from training prompt tokens.\nAnother critical aspect was excluding all test misconceptions from training. I observed that training solely on the training misconceptions allowed the model to generalize effectively to unseen misconceptions. Excluding test msicocnepts gave me probably the biggest LB improvement after reaching 0.6 on the public = ~0.61 - 0.63. </p>\n<h3>Distillation</h3>\n<p>Close to the deadline, I discovered an interesting trick: first, train the model on one seed. Then, create a dataset using a different seed (which usually alters the set of negative samples and the position of the correct misconception). Use the already trained model to infer logits on this new dataset. Finally, retrain the model with KL divergence distillation combined with simple cross-entropy loss. This approach yielded a ~0.01 improvement on the public. \nMy intuition behind this trick is that, for some samples, more than one misconception may technically be correct. Overfitting the model to a single misconception reduces its ability to generalize. By incorporating soft labels through distillation, we mitigate overfitting and improve the model's generalization to unseen misconceptions.</p>\n<h3>First stage reranker</h3>\n<p>I randomly sampled 49 negative misconceptions from the 100 most similar misconceptions and placed the correct misconception at a random position. Then, I trained the model to classify which misconception was most relevant. The random order generated through sampling helped avoid overfitting to the initial order, where the retriever often placed the correct misconception among the first few options.</p>\n<h3>Misconception rephrasing</h3>\n<p>I noticed that misconceptions vary significantly, for example, some describe what knowledge is lacking, while others describe what the student believes. To address this, I rephrased the misconceptions into a general format that includes both the missing knowledge and the student’s belief that aligns with the misconception. Even when the rephrased misconceptions semantically repeated the original ones, this rephrasing served as effective regularization during model inference.\nI used the inference logits from the rephrased misconceptions and combined them with the logits from the original misconceptions, which resulted in the ~0.01 public improvement.\nTo rephrase misconceptions Qwen 32B-Instruct was used.</p>\n<h3>Second stage reranker</h3>\n<p>Analyzing the logits obtained from the first-stage reranker, I noticed that Qwen tends to preserve the initial order of misconceptions as sorted by the retriever. I found further validation for this observation in <a href=\"https://arxiv.org/pdf/2306.17563\" target=\"_blank\">this excellent paper</a>, which inspired me to retrain Retrievers with more focus on MRR and develop the second-stage reranker to refine the results of the first stage reranker.\nIn the second stage, I selected a small window of the top ranked misconceptions from the first stage reranker and applied all possible permutations to their order. I then averaged the logits across these permutations and reranked misconcepts from that window again. For my surprise, the optimal window size turned out to be 2. This means I took the top 2 misconceptions from the first stage reranker, performed inference on them, inverted their order, ran inference again, and finally used the averaged logits for the final reranking.\n<strong>Reranker details:</strong></p>\n<ul>\n<li>Qwen 32B instruct was used</li>\n<li>Lora parameters:<ul>\n<li>rank = 64</li>\n<li>alpha = 128</li></ul></li>\n<li>Batch size = 2 * 6 grad accum</li>\n<li>AWQ 4 bit quantization</li>\n<li>1xA40 took 6 hours to train one model</li>\n</ul>\n<h1>What did not work</h1>\n<ul>\n<li><strong>A lot of things</strong></li>\n<li>Training fp16 Rerankers with all available lora adapters with subsequent qunatization</li>\n<li>Training Qwen 72B as a Reranker. I got the same results as for 32B</li>\n<li>Using synthtetic data in the Reranker training</li>\n</ul>\n<h1>Hardware</h1>\n<p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable. \nI mostly used A40, and for some experiments, I rented A100. \nIn total, this cost me about $300.</p>",
      "rawMarkdown": "I am very happy to have finally achieved the goal that I set for myself a year ago when I began relatively seriously participating in Kaggle competitions: solo gold. \nAlthough I had previously reached solo gold on the public a couple of times, I did not succeed to survive on the private. This time, dedication over the last months, focusing on a generalizable solution and the relatively similar distributions of public and private data have enabled me to reach this goal.\n\n# **Gratitude**\nI want to thank Kaggle and the hosts for organizing such an interesting competition, where we had to explore the reasoning abilities of different approaches to effectively rank math misconceptions by their relevance to the distractors. \nSpecial gratitude for designing public/private splits with a balanced number of seen and unseen misconceptions from the training data (I am sure that gold places that survived agree with me). That allowed to reflect the robustness of the developed approaches on the leaderboard, resulting in only a very moderate shuffle on the private. After the last competitions, I really feel that I should thank for that :) \n\n# **Solution**\n\n##  Diagram showing the general overview of the solution:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6343664%2F62b07ce60a6bfd9df65038420514f9ae%2Feedi_pipeline.jpg?generation=1734122724162162&alt=media)\n\n## Validation strategy\nFrom the beginning, as most participants, I could not find a good correlation between CV by the group split on question id and the public LB. So it became obvious that hidden data contains multiple unseen misconceptions that were not present in the train and I should make the validation split in the way that it would incorporate a part of the unseen misconceptions that train data would not have. \nDespite the group split on question id already provided a certain proportion of the unseen misconceptions, they usually took up maximum up to 20% of all misconceptions in the fold, that is why a lot of team faced the overfit to seen misconceptions that provided good general CV score even if unseen ones were very suppressed in terms of retrieving.\nFor example, if fold consists of **~80% seen** data and **20% unseen**, then with severe overfit to seen misconcepts like 65% MRR and the lack of good retrieving for unseen like 30% MRR,  the weighted average of the MRR would be\n `0.65*0.8 + 0.3*0.2 = 0.58`\nand that would be far away from what the LB would display.\nFinally, I based my validation stratagy on an assumaption that the hidden distribution is quite balanced in terms of seen/unseen misconceptions and the key for the good CV is to set equal weight to seen and unseen misconceptions.\nTo maintain a high dynamic of experiments and avoid spending too much on VMs, I stuck with using only one fold as a validation set among experiments. This strategy proved sufficient, as the results across different folds were very similar.\n\n## Synthetic data generation\nAttempts to train retriever models solely on the provided train data showed bad results, so it became inevitable to  generate synthetic data for unseen misconceptions.\nWhile experimenting with LORA pretraining and few-shot generation using Qwen 32/72 instruct, I discovered that LORA pretraining seems to decrease the models' reasoning abilities (catastrophic forgetting ?). The best approach turned out to be few-shot generation. Later, I came across the great [EEDI paper](https://arxiv.org/pdf/2404.02124), which reached a similar conclusion that few-shot generation is more effective for this task.\n\nThe main drawback of few-shot generation was that the generated samples were very similar in formulation to the few-shot examples. Any attempt at prompt engineering couldn’t change this without losing the correct connection between distractors and misconceptions.\n\n**Generation details:**\n- Qwen 72B instruct (32B struggled with tricky misconcepts)\n- 3 random few-shot examples for most similar train misconcepts\n- Full math test generation aligned with train data structure, but with only 1 distractor relevant to misconcept\n- 3 math tests per misconcept repeated for 3 different seeds. So in total 9 tests for each misconcept\n- 2xA40 took 3 hours to generate all samples\n\n## Retrievers\nThe LaTex format of the math problems contained many redundant symbols that slowed the convergence. So instead of manually filtering the text, I researched open-source libs to convert LaTex to plain text and simplify equations. Although most libs ingored most redundant symbols, I found `pylatexenc` lib, which handled the conversion quite well, mostly without losing important elements from the text or equations (there were a few cases in the training data with redundant symbols that did not follow LaTex formatting at all). However, I included two models in the final ensemble that used data without LaTex filtration to ensure no important information was lost.\nAn interesting moment is that, even though the model achieved a much higher MRR for unseen misconceptions compared to seen ones (due to synthetic oversampling) after training including the generated data, the recall remained the same between seen and unseen misconcepts. I believe the root cause is that the synthetic data allowed the model to converge well for simpler misconceptions but struggled to generate math tests for complex misconcepts. As a result, the model could retrieve most unseen misconcepts well (resulting in a high MRR) but was unable to find relevance for other complex misconcepts, leading to the same recall as for underrepresented seen misconcepts which math tests were similar between train and validation splits. \n\n**Retriever details:**\n- Models: gte-Qwen2-7B-instruct, Salesforce-SFR-Embedding-2_R, bge-multilingual-gemma2, [Qwen2.5-14B-Instruct](https://www.kaggle.com/code/anhvth226/eedi-11-21-14b) \n- Lora parameters:\n  - rank = 64\n  - alpha = 128\n- Batch size = 32\n- Negatives per sample =  5 (from 0 to - 30 most similar negatives)\n- Two neg mining iterations:\n  - initial neg mining with the subsequent 1600 training steps\n  - second neg mining iteration by the pretrained model with the subsequent 400 training steps\n- BNB 4 bit quantization\n- 1xRTX 3090 took 9 hours to train one model\n\n## Rerankers\nUsing the `DataCollatorForCompletionOnlyLM` was crucial to exclude from training prompt tokens.\nAnother critical aspect was excluding all test misconceptions from training. I observed that training solely on the training misconceptions allowed the model to generalize effectively to unseen misconceptions. Excluding test msicocnepts gave me probably the biggest LB improvement after reaching 0.6 on the public = ~0.61 - 0.63. \n### Distillation \nClose to the deadline, I discovered an interesting trick: first, train the model on one seed. Then, create a dataset using a different seed (which usually alters the set of negative samples and the position of the correct misconception). Use the already trained model to infer logits on this new dataset. Finally, retrain the model with KL divergence distillation combined with simple cross-entropy loss. This approach yielded a ~0.01 improvement on the public. \nMy intuition behind this trick is that, for some samples, more than one misconception may technically be correct. Overfitting the model to a single misconception reduces its ability to generalize. By incorporating soft labels through distillation, we mitigate overfitting and improve the model's generalization to unseen misconceptions.\n### First stage reranker \nI randomly sampled 49 negative misconceptions from the 100 most similar misconceptions and placed the correct misconception at a random position. Then, I trained the model to classify which misconception was most relevant. The random order generated through sampling helped avoid overfitting to the initial order, where the retriever often placed the correct misconception among the first few options.\n### Misconception rephrasing\nI noticed that misconceptions vary significantly, for example, some describe what knowledge is lacking, while others describe what the student believes. To address this, I rephrased the misconceptions into a general format that includes both the missing knowledge and the student’s belief that aligns with the misconception. Even when the rephrased misconceptions semantically repeated the original ones, this rephrasing served as effective regularization during model inference.\nI used the inference logits from the rephrased misconceptions and combined them with the logits from the original misconceptions, which resulted in the ~0.01 public improvement.\nTo rephrase misconceptions Qwen 32B-Instruct was used.\n### Second stage reranker \nAnalyzing the logits obtained from the first-stage reranker, I noticed that Qwen tends to preserve the initial order of misconceptions as sorted by the retriever. I found further validation for this observation in [this excellent paper](https://arxiv.org/pdf/2306.17563), which inspired me to retrain Retrievers with more focus on MRR and develop the second-stage reranker to refine the results of the first stage reranker.\nIn the second stage, I selected a small window of the top ranked misconceptions from the first stage reranker and applied all possible permutations to their order. I then averaged the logits across these permutations and reranked misconcepts from that window again. For my surprise, the optimal window size turned out to be 2. This means I took the top 2 misconceptions from the first stage reranker, performed inference on them, inverted their order, ran inference again, and finally used the averaged logits for the final reranking.\n**Reranker details:**\n- Qwen 32B instruct was used\n- Lora parameters:\n  - rank = 64\n  - alpha = 128\n- Batch size = 2 * 6 grad accum\n- AWQ 4 bit quantization\n- 1xA40 took 6 hours to train one model\n\n# What did not work\n- **A lot of things**\n- Training fp16 Rerankers with all available lora adapters with subsequent qunatization\n- Training Qwen 72B as a Reranker. I got the same results as for 32B\n- Using synthtetic data in the Reranker training\n\n# Hardware\nSince Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable. \nI mostly used A40, and for some experiments, I rented A100. \nIn total, this cost me about $300.",
      "votes": 40
    },
    {
      "id": 3073295,
      "postDate": "2024-12-16T09:50:30.257Z",
      "content": "<blockquote>\n  <p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable.</p>\n</blockquote>\n<p>Solo gold is already a huge success but winning it under invasion is very impressive. Congrats! I wish and hope it will all end soon.</p>",
      "rawMarkdown": ">Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day, renting VMs became inevitable.\n\nSolo gold is already a huge success but winning it under invasion is very impressive. Congrats! I wish and hope it will all end soon.",
      "votes": 1
    },
    {
      "id": 3071818,
      "postDate": "2024-12-14T11:12:24.197Z",
      "content": "<p>Congratulations on the gold medal! Nice and solid solution : )</p>",
      "rawMarkdown": "Congratulations on the gold medal! Nice and solid solution : )",
      "votes": 1
    },
    {
      "id": 3071614,
      "postDate": "2024-12-14T02:31:54.400Z",
      "content": "<p>Congratulations on your solo gold medal, Nikita Babich! 😀 </p>",
      "rawMarkdown": "Congratulations on your solo gold medal, Nikita Babich! 😀 ",
      "votes": 1
    },
    {
      "id": 3071598,
      "postDate": "2024-12-14T01:52:49.043Z",
      "content": "<p>congratulations nikita, I am delighted with your achievements. <br>\nby the way, will you release the complete code? I can't wait to learn your code!!</p>",
      "rawMarkdown": "congratulations nikita, I am delighted with your achievements. \nby the way, will you release the complete code? I can't wait to learn your code!!",
      "votes": 1
    },
    {
      "id": 3071579,
      "postDate": "2024-12-14T00:32:12.177Z",
      "content": "<p>🇺🇦 really inspirational! </p>",
      "rawMarkdown": "🇺🇦 really inspirational! ",
      "votes": 1
    },
    {
      "id": 3071566,
      "postDate": "2024-12-13T23:51:45.230Z",
      "content": "<h1>🇺🇦</h1>\n<p>Well done!</p>",
      "rawMarkdown": "# 🇺🇦                 \n\nWell done!",
      "votes": 1
    },
    {
      "id": 3073987,
      "postDate": "2024-12-17T07:24:49.700Z",
      "content": "<p><a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a> <br>\nCongratulations! <br>\nI always thought that you would eventually win a solo gold because you were always in a position to aim for the gold in various competitions that I participated in.</p>",
      "rawMarkdown": "@nikitababich \nCongratulations! \nI always thought that you would eventually win a solo gold because you were always in a position to aim for the gold in various competitions that I participated in.",
      "votes": 2
    },
    {
      "id": 3071561,
      "postDate": "2024-12-13T23:39:53.523Z",
      "content": "<p>Considering this-</p>\n<blockquote>\n  <p>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day</p>\n</blockquote>\n<p>I salute you for your dedication. Solo gold is no minor feat and you having done it with real-life challenges is absolute god-tier. <br>\nCongrats Nikita- You fully deserve it</p>",
      "rawMarkdown": "Considering this-\n>Since Ukraine's infrastructure is under constant attacks and, as a result, we have blackouts every day\n\nI salute you for your dedication. Solo gold is no minor feat and you having done it with real-life challenges is absolute god-tier. \nCongrats Nikita- You fully deserve it",
      "votes": 2,
      "replies": [
        {
          "id": 3071562,
          "postDate": "2024-12-13T23:42:34.623Z",
          "content": "<p>Thank you! Really appreciate it. </p>",
          "rawMarkdown": "Thank you! Really appreciate it. ",
          "votes": 2
        }
      ]
    },
    {
      "id": 3071862,
      "postDate": "2024-12-14T12:17:16.987Z",
      "content": "<p>Congratulations on your solo gold <a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a>! It's nice to see you succeed after the ISIC competition!</p>",
      "rawMarkdown": "Congratulations on your solo gold @nikitababich! It's nice to see you succeed after the ISIC competition!"
    },
    {
      "id": 3074930,
      "postDate": "2024-12-18T08:11:27.917Z",
      "content": "<p>Congratulations! Winning a solo gold</p>",
      "rawMarkdown": "Congratulations! Winning a solo gold"
    },
    {
      "id": 3072883,
      "postDate": "2024-12-15T18:01:11.200Z",
      "content": "<p>Congratulations on your solo gold <a href=\"https://www.kaggle.com/nikitababich\" target=\"_blank\">@nikitababich</a>! It's nice to see you succeed after the ISIC competition!</p>",
      "rawMarkdown": "Congratulations on your solo gold @nikitababich! It's nice to see you succeed after the ISIC competition!\n\n\n"
    },
    {
      "id": 3073358,
      "postDate": "2024-12-16T11:57:51.960Z",
      "content": "<p>Congrats and thanks for the explanation</p>",
      "rawMarkdown": "Congrats and thanks for the explanation",
      "votes": 1
    },
    {
      "id": 3073062,
      "postDate": "2024-12-16T02:13:47.110Z",
      "content": "<p>Great work!</p>",
      "rawMarkdown": "Great work!",
      "votes": 1
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-20T03:48:01Z",
  "competition_id": 82695,
  "competition_slug": "eedi-mining-misconceptions-in-mathematics",
  "team_id": 12739725,
  "solution_url": "/competitions/eedi-mining-misconceptions-in-mathematics/writeups/nikita-babich-6th-place-solution"
}