{
  "id": 679230,
  "title": "25th place solution",
  "url": "/competitions/vesuvius-challenge-surface-detection/writeups/25th-place-solution",
  "author_name": "",
  "post_date": "2026-02-28T02:32:34.680Z",
  "votes": 10,
  "comment_count": 3,
  "views": 0,
  "content": "<p>I would like to express my gratitude to Kaggle and the organizers for hosting this competition. The challenge of extracting text from scrolls buried in volcanic ash is both fascinating and immensely difficult. By reviewing the code shared by the community, I have gained invaluable knowledge.</p>\n<p><strong>Model Training</strong></p>\n<p>Special thanks to <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> for providing the foundational code (Surface nnUNet Training &amp; Inference with 2x T4). Building upon this, I trained nnUNet models using the 3d_fullres configuration for 2000 epochs. Experiments were conducted with two distinct patch sizes: [128, 128, 128] and [224, 224, 224].\nThe training process leveraged both a local NVIDIA RTX 4090 GPU and the dual T4 GPUs provided by Kaggle. To identify the models with the optimal cross-validation (CV) scores, I employed an iterative training strategy, resuming training from checkpoints multiple times as needed.</p>\n<p><strong>Post-processing</strong></p>\n<p>Thanks to <a href=\"https://www.kaggle.com/ipythonx\" target=\"_blank\">@ipythonx</a> for sharing the inference code (Inference Vesuvius Surface 3D Detection). I adapted the post-processing function with slight parameter adjustments. However, due to Kaggle's submission limits, the final parameters selected may not represent the absolute optimum. The configuration used was as follows:</p>\n<pre><code>final_mask = postprocess_topology(\n    surface_prob=surface_prob,\n    baseline_fg_mask=baseline_fg,\n    low_threshold=0.25,\n    high_threshold=0.8,\n    z_radius=3,\n    xy_radius=2,\n    min_component_size=200\n)\n</code></pre>\n<p><strong>Ensemble</strong></p>\n<p>The final solution involved an ensemble of two models by performing a weighted average of their probability maps. The model trained with a patch size of [128, 128, 128] was assigned a weight of 0.7, while the model with a patch size of [224, 224, 224] received a weight of 0.3.\nNotably, the [128, 128, 128] model achieved a higher public leaderboard score but underperformed on the private leaderboard. Conversely, the [224, 224, 224] model showed a lower public score but demonstrated superior performance privately. Consequently, fine-tuning these ensemble weights could potentially yield an even higher private score.</p>\n<p>I joined the competition during the final stages. Given more time, I could have trained higher-performing models and incorporated additional architectures into the ensemble. This remains a promising avenue for further improving the score.</p>",
  "messages": [
    {
      "id": "3414967",
      "postDate": "02/28/2026 02:24:50",
      "content": "<p>I would like to express my gratitude to Kaggle and the organizers for hosting this competition. The challenge of extracting text from scrolls buried in volcanic ash is both fascinating and immensely difficult. By reviewing the code shared by the community, I have gained invaluable knowledge.</p>\n<p><strong>Model Training</strong></p>\n<p>Special thanks to <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> for providing the foundational code (Surface nnUNet Training &amp; Inference with 2x T4). Building upon this, I trained nnUNet models using the 3d_fullres configuration for 2000 epochs. Experiments were conducted with two distinct patch sizes: [128, 128, 128] and [224, 224, 224].\nThe training process leveraged both a local NVIDIA RTX 4090 GPU and the dual T4 GPUs provided by Kaggle. To identify the models with the optimal cross-validation (CV) scores, I employed an iterative training strategy, resuming training from checkpoints multiple times as needed.</p>\n<p><strong>Post-processing</strong></p>\n<p>Thanks to <a href=\"https://www.kaggle.com/ipythonx\" target=\"_blank\">@ipythonx</a> for sharing the inference code (Inference Vesuvius Surface 3D Detection). I adapted the post-processing function with slight parameter adjustments. However, due to Kaggle's submission limits, the final parameters selected may not represent the absolute optimum. The configuration used was as follows:</p>\n<pre><code>final_mask = postprocess_topology(\n    surface_prob=surface_prob,\n    baseline_fg_mask=baseline_fg,\n    low_threshold=0.25,\n    high_threshold=0.8,\n    z_radius=3,\n    xy_radius=2,\n    min_component_size=200\n)\n</code></pre>\n<p><strong>Ensemble</strong></p>\n<p>The final solution involved an ensemble of two models by performing a weighted average of their probability maps. The model trained with a patch size of [128, 128, 128] was assigned a weight of 0.7, while the model with a patch size of [224, 224, 224] received a weight of 0.3.\nNotably, the [128, 128, 128] model achieved a higher public leaderboard score but underperformed on the private leaderboard. Conversely, the [224, 224, 224] model showed a lower public score but demonstrated superior performance privately. Consequently, fine-tuning these ensemble weights could potentially yield an even higher private score.</p>\n<p>I joined the competition during the final stages. Given more time, I could have trained higher-performing models and incorporated additional architectures into the ensemble. This remains a promising avenue for further improving the score.</p>",
      "rawMarkdown": "I would like to express my gratitude to Kaggle and the organizers for hosting this competition. The challenge of extracting text from scrolls buried in volcanic ash is both fascinating and immensely difficult. By reviewing the code shared by the community, I have gained invaluable knowledge.\n\n**Model Training**\n\nSpecial thanks to @jirkaborovec for providing the foundational code (Surface nnUNet Training & Inference with 2x T4). Building upon this, I trained nnUNet models using the 3d_fullres configuration for 2000 epochs. Experiments were conducted with two distinct patch sizes: [128, 128, 128] and [224, 224, 224].\nThe training process leveraged both a local NVIDIA RTX 4090 GPU and the dual T4 GPUs provided by Kaggle. To identify the models with the optimal cross-validation (CV) scores, I employed an iterative training strategy, resuming training from checkpoints multiple times as needed.\n\n**Post-processing**\n\nThanks to @ipythonx for sharing the inference code (Inference Vesuvius Surface 3D Detection). I adapted the post-processing function with slight parameter adjustments. However, due to Kaggle's submission limits, the final parameters selected may not represent the absolute optimum. The configuration used was as follows:\n\n```python\nfinal_mask = postprocess_topology(\n    surface_prob=surface_prob,\n    baseline_fg_mask=baseline_fg,\n    low_threshold=0.25,\n    high_threshold=0.8,\n    z_radius=3,\n    xy_radius=2,\n    min_component_size=200\n)\n```\n\n**Ensemble**\n\nThe final solution involved an ensemble of two models by performing a weighted average of their probability maps. The model trained with a patch size of [128, 128, 128] was assigned a weight of 0.7, while the model with a patch size of [224, 224, 224] received a weight of 0.3.\nNotably, the [128, 128, 128] model achieved a higher public leaderboard score but underperformed on the private leaderboard. Conversely, the [224, 224, 224] model showed a lower public score but demonstrated superior performance privately. Consequently, fine-tuning these ensemble weights could potentially yield an even higher private score.\n\nI joined the competition during the final stages. Given more time, I could have trained higher-performing models and incorporated additional architectures into the ensemble. This remains a promising avenue for further improving the score.",
      "votes": null
    },
    {
      "id": "3415109",
      "postDate": "02/28/2026 07:24:44",
      "content": "<p>is it cool if I can take a look at your training scripts / GitHub repository? If it's private, can I ask for invite share? </p>",
      "rawMarkdown": "is it cool if I can take a look at your training scripts / GitHub repository? If it's private, can I ask for invite share?",
      "votes": null
    },
    {
      "id": "3415117",
      "postDate": "02/28/2026 07:39:28",
      "content": "<p>Train code:\n<a href=\"https://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323\" target=\"_blank\">https://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323</a></p>\n<p>I have modified the epochs and patch_Size</p>",
      "rawMarkdown": "Train code:\nhttps://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323\n\nI have modified the epochs and patch_Size",
      "votes": null
    },
    {
      "id": "3415135",
      "postDate": "02/28/2026 08:26:16",
      "content": "<p>Thanks for clarification.</p>",
      "rawMarkdown": "Thanks for clarification.",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3415109,
      "author_name": "rajeshthevar",
      "author_url": "",
      "post_date": "02/28/2026 07:24:44",
      "content": "<p>is it cool if I can take a look at your training scripts / GitHub repository? If it's private, can I ask for invite share? </p>",
      "votes": null,
      "replies": [
        {
          "id": 3415117,
          "author_name": "jiaoyouzhang",
          "author_url": "",
          "post_date": "02/28/2026 07:39:28",
          "content": "<p>Train code:\n<a href=\"https://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323\" target=\"_blank\">https://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323</a></p>\n<p>I have modified the epochs and patch_Size</p>",
          "votes": null,
          "replies": [
            {
              "id": 3415135,
              "author_name": "rajeshthevar",
              "author_url": "",
              "post_date": "02/28/2026 08:26:16",
              "content": "<p>Thanks for clarification.</p>",
              "votes": null,
              "replies": []
            }
          ]
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "3414967": "I would like to express my gratitude to Kaggle and the organizers for hosting this competition. The challenge of extracting text from scrolls buried in volcanic ash is both fascinating and immensely difficult. By reviewing the code shared by the community, I have gained invaluable knowledge.\n\n**Model Training**\n\nSpecial thanks to @jirkaborovec for providing the foundational code (Surface nnUNet Training & Inference with 2x T4). Building upon this, I trained nnUNet models using the 3d_fullres configuration for 2000 epochs. Experiments were conducted with two distinct patch sizes: [128, 128, 128] and [224, 224, 224].\nThe training process leveraged both a local NVIDIA RTX 4090 GPU and the dual T4 GPUs provided by Kaggle. To identify the models with the optimal cross-validation (CV) scores, I employed an iterative training strategy, resuming training from checkpoints multiple times as needed.\n\n**Post-processing**\n\nThanks to @ipythonx for sharing the inference code (Inference Vesuvius Surface 3D Detection). I adapted the post-processing function with slight parameter adjustments. However, due to Kaggle's submission limits, the final parameters selected may not represent the absolute optimum. The configuration used was as follows:\n\n```python\nfinal_mask = postprocess_topology(\n    surface_prob=surface_prob,\n    baseline_fg_mask=baseline_fg,\n    low_threshold=0.25,\n    high_threshold=0.8,\n    z_radius=3,\n    xy_radius=2,\n    min_component_size=200\n)\n```\n\n**Ensemble**\n\nThe final solution involved an ensemble of two models by performing a weighted average of their probability maps. The model trained with a patch size of [128, 128, 128] was assigned a weight of 0.7, while the model with a patch size of [224, 224, 224] received a weight of 0.3.\nNotably, the [128, 128, 128] model achieved a higher public leaderboard score but underperformed on the private leaderboard. Conversely, the [224, 224, 224] model showed a lower public score but demonstrated superior performance privately. Consequently, fine-tuning these ensemble weights could potentially yield an even higher private score.\n\nI joined the competition during the final stages. Given more time, I could have trained higher-performing models and incorporated additional architectures into the ensemble. This remains a promising avenue for further improving the score.",
    "3415109": "is it cool if I can take a look at your training scripts / GitHub repository? If it's private, can I ask for invite share?",
    "3415117": "Train code:\nhttps://www.kaggle.com/code/jirkaborovec/surface-nnunet-training-inference-with-2xt4?scriptVersionId=288729323\n\nI have modified the epochs and patch_Size",
    "3415135": "Thanks for clarification."
  },
  "source": "meta"
}