{
  "id": 566574,
  "title": "3rd Place Solution",
  "content": "<p><a href=\"https://www.kaggle.com/code/churkinnikita/3rd-place-solution\" target=\"_blank\">Full code</a></p>\n<p><a href=\"https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb\" target=\"_blank\">Best \"solo\" model (gold medal top-11 Private LB — 0.69694, full training from scratch)</a></p>\n<h6>First of all, we are grateful to the competition organizers for the possibility to contribute to HCT therapy!</h6>\n<p>Our solution has the following steps.</p>\n<p></p><ol><br>\n<li><p>Validation is the most important one. We made a 4-fold CV (as the public test part is 25%) and evaluated the score by 20–100 random split seeds (depends on the computational complexity). During all the competition, we had excellent CV–LB correlation when measuring average fold scores (4 x NSeeds). The public \"fold\" was approximately at the 75-quantile of the score histogram in our experiments. We emulated and tracked both \"public\" and \"private\" scores in this way and created a second submission as more private-oriented. It had a lower LB score but significantly better emulated (and real) private score.</p></li><br>\n<li><p>We made a uniform target in the range [0, 1] for uncensored (<code>efs = 1</code>) observations and in the range [1.345, 1.355] for censored (<code>efs = 0</code>) cases. The target was calculated within train folds and valid folds separately for every race group.</p></li><br>\n<li><p>Then we divided the task into three parts: separate regressions within uncensored and censored data weighted on the probability of belonging to the classes <code>efs = 1</code> and <code>efs = 0</code>.</p></li><br>\n<li><p>Regression within zeros is the most insignificant part of the composition. For NN models, we just collapse all the <code>efs = 0</code> observations to the constant 1.35 (that was tuned to show maximal performance in terms of the concordance index).</p></li><br>\n<li><p>The target range [0, 1] for the regression task allowed us to train the models via binary cross-entropy loss, which is much more profitable in comparison with mean squared error.</p></li><br>\n<li><p>Thus, the prediction has the form<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F9c55f0aede5a15151742a0ee42d7b17d%2Fcomposition_resize.png?generation=1741321386636868&amp;alt=media\" alt=\"\"><br>\nand we obtain an overall scatter plot of type<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F6e95e95854da5b3a7c7c005a8f43618a%2Fscatter_resize.png?generation=1741321580825059&amp;alt=media\" alt=\"\"></p></li><br>\n<li><p>Our model zoo consists of CatBoost, LightGBM, XGBoost, MLP with ODST, and TabM models (separate exemplars of each for regression and classification). NNs were the best at classification, and GBMs are the champions for regression. Ranking models have weaker performance in the competition.</p></li><br>\n<li><p>The data is noisy, and the classic methods of noise reduction worked well. We averaged the models with fixed hyperparameters / architectures over random initialization seeds and eliminated the observations with giant regression errors from the training. But in the case of classification, outlier denoising leads to overfitting (ROC AUC becomes better, but LogLoss worsens at the same time).</p></li><br>\n<li><p>On the very final stage, we blended the regressors by convex combination and stacked the classifiers by logistic model. Stacking by logreg shows spectacular performance on the private part of the test data that we saw via out-of-fold imitation of the public / private split. All blending weights were optimized simultaneously to maximize the concordance index on 20 OOF predictions (20 random split seeds) using Bayesian optimization<br><br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fdf195be3ade190bb433e952e3bb6c12e%2Foptimization_resize.png?generation=1741321695756990&amp;alt=media\" alt=\"\"><br><br>\nThe resulting ensemble is<br><br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F13624458590e21382e28602019fb2fde%2FCIBMTR_resize.png?generation=1741321824928137&amp;alt=media\" alt=\"\"><br><br>\nOverall performance of the components:<br></p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>CV (\"Public\" estimate — folds x seeds)</th>\n<th>Public LB</th>\n<th>Private LB</th>\n<th><br></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>CatBoost</td>\n<td>0.68586</td>\n<td>0.69424</td>\n<td>0.69577</td>\n<td><br></td>\n</tr>\n<tr>\n<td>LightGBM</td>\n<td>0.68530</td>\n<td>0.69318</td>\n<td>0.69572</td>\n<td><br></td>\n</tr>\n<tr>\n<td>XGBoost</td>\n<td>0.68616</td>\n<td>0.69252</td>\n<td>0.69493</td>\n<td><br></td>\n</tr>\n<tr>\n<td>NN</td>\n<td>0.68554</td>\n<td>0.69540</td>\n<td>0.69694</td>\n<td><br></td>\n</tr>\n<tr>\n<td>Blend GBMs</td>\n<td>0.68932</td>\n<td>0.69608</td>\n<td>0.69794</td>\n<td><br></td>\n</tr>\n<tr>\n<td>Blend All + LogReg</td>\n<td>0.69139</td>\n<td>0.69692</td>\n<td>0.69937</td>\n<td><p></p></td></tr></tbody></table></li>\n\n\n</ol>\n\n\n\n\n\n\nTo sum up, the most important trick in this competition was to divide the task into separate regression and classification parts. You could see this trick, for example, by looking at the scatter plot within the first straightforward modeling attempts. The picture below shows training using all data (left), training using <code>efs = 1</code> data only (center), and training using <code>efs = 0</code> data only (right)\n\n\n\n\n\n\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fd238f2de1afcbdd22a83c9bed1bb717d%2Fnaive.jpg?generation=1742565645481169&amp;alt=media\" alt=\"\">\n\n\n\n\n\n\n\n<p>My teammates disclose the details of the solution in the comment section.</p>",
  "comments": [
    {
      "id": 3142358,
      "author_name": "Nikita Churkin",
      "author_url": "",
      "post_date": "2025-03-06T10:40:54.757000",
      "content": "<p>For <code>efs=1</code> samples, we used the following target:</p>\n<p><code>risk := rank_by_race(efs_time)</code> mapped to [0, 1].</p>\n<p>Range [0, 1] allowed us to model risk with binary cross-entropy loss, which was a much better choice than MSE/ranking losses because of the superior BCE performance in the final solution.</p>\n<p>For risk modeling, it was helpful to drop some samples (outliers, green on the picture) from train folds (not valid folds!): they were unpredictable and messed up the training process a bit. We deleted up to 1k \"noisy\" samples (samples with the highest BCE).<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F5e774990fc3e909f40aab7f3df97b907%2Foutliers.png?generation=1741257427013353&amp;alt=media\" alt=\"\"></p>",
      "votes": 11,
      "replies": [
        {
          "id": 3142363,
          "author_name": "Gunes Evitan",
          "author_url": "",
          "post_date": "2025-03-06T10:44:54.023000",
          "content": "<p>I tried mse, huber, poisson and cross-entropy losses, but mse was the best. I'm surprised cross-entropy loss worked better for you.  </p>",
          "votes": 3,
          "replies": []
        },
        {
          "id": 3142803,
          "author_name": "Robert Hatch",
          "author_url": "",
          "post_date": "2025-03-06T17:17:42.420000",
          "content": "<p>Ignorant question time: is BCE regression (didn't realize it was possible) essentially exactly identical to what I did, other than margin term? I did normal regression on logit targets (0.01-0.99)?</p>\n<p>I'm guessing no, there's different gradient and hessian(?), but I'm not sure. I didn't recall that you could just use BCE directly for regression, heh. </p>",
          "votes": 3,
          "replies": [
            {
              "id": 3143220,
              "author_name": "Dmitriy Ukrainskiy",
              "author_url": "",
              "post_date": "2025-03-07T03:01:40.923000",
              "content": "<p>MSE and BCE are just different functions:</p>\n<p><code>mse(x,y) = (x-y)^2</code>,<br>\n<code>d(mse)/dx = 2(x-y)</code>,<br>\n<code>d^2(mse)/dx^2 = 2</code></p>\n<p><code>bce(x,y) = -(y ln(x) + (1-y) ln(1-x))</code>,<br>\n<code>d(bce)/dx = -(y-x)/(x(1-x))</code>,<br>\n<code>d^2(bce)/dx^2 = y/x^2 + (1-y)/(1-x)^2</code></p>\n<p>Hence, they generate different optimization processes</p>",
              "votes": 9,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3142440,
      "author_name": "Nikita Churkin",
      "author_url": "",
      "post_date": "2025-03-06T11:50:01.947000",
      "content": "<p>Some thoughts on validation in this competition.</p>\n<p>If we try to split the train set into 4 parts (size as the public set's size) and try to fit &amp; predict with some model, we will see insane variation of scores depending on the choice of the particular split.</p>\n<p>The picture shows an experiment with 4 folds by 50 split seeds (200 emulated folds in total).<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F7812776112a5ae711c7fe35786ae3302%2Fcv.jpg?generation=1741261793039664&amp;alt=media\" alt=\"\"><br>\nSo, in my view, using only 1 CV split is dangerous and unreliable, especially if you are using leak-prone procedures as early stopping. High deviation of validation scores is enough to incorrectly evaluate almost any (at least non-major) improvement.</p>\n<p>For the final evaluation and blending, we recomputed our out-of-fold predictions corresponding to 20 new, \"unseen\" random seeds to, hopefully, decrease any \"seed-induced\" bias.</p>",
      "votes": 10,
      "replies": [
        {
          "id": 3142789,
          "author_name": "Robert Hatch",
          "author_url": "",
          "post_date": "2025-03-06T17:09:15.733000",
          "content": "<p>Not only is it a good idea… It takes a bit of time and effort! Kudos to your team for the disciplined approach and putting in the time and effort to make it happen!</p>",
          "votes": 5,
          "replies": []
        }
      ]
    },
    {
      "id": 3142609,
      "author_name": "DS_Blonde_Sofa",
      "author_url": "",
      "post_date": "2025-03-06T14:30:34.350000",
      "content": "<p>Survival analysis is a fruitful branch of mathematics, but open-source models and approaches didn’t show competitive performance. Nevertheless, the paper [<a href=\"https://arxiv.org/abs/2410.01086\" target=\"_blank\">https://arxiv.org/abs/2410.01086</a>] from the author of the Deep Kernel Survival Analysis framework helped to dive deeper into the subject details.</p>\n<p>Regarding the feature engineering stage, we tested various variables. Some of them were designed to detect anomalies and some to catch rare data patterns (e.g., preprocessing of text features: TF-IDF vectorizer, embedding with Bio NLP). We also tried complex multi-category combinations, but the simple frequency encoder was the one who improved the score for some of our models (although not drastically).</p>\n<p>We also experimented with different generative models (similar to SurvivalGAN) to improve the resulting performance with additional data. However, it didn’t lead to a substantial score increase.</p>",
      "votes": 7,
      "replies": [
        {
          "id": 3145949,
          "author_name": "Nikita Churkin",
          "author_url": "",
          "post_date": "2025-03-10T11:53:21.253000",
          "content": "<p>Some tricks that haven’t worked:</p>\n<ul>\n<li>Target encoding</li>\n<li>Autoencoders: DAE, VAE</li>\n<li>Other kinds of models: SVM, Gaussian Processes, RGF, TabNet, FTTransformer</li>\n<li>Sophisticated stacking instead of blending</li>\n</ul>",
          "votes": 4,
          "replies": []
        }
      ]
    },
    {
      "id": 3149258,
      "author_name": "Abdullah Qazi",
      "author_url": "",
      "post_date": "2025-03-14T02:08:48.493000",
      "content": "<p>Great Explaination!</p>",
      "votes": 3,
      "replies": []
    },
    {
      "id": 3147825,
      "author_name": "gourav_gujariya",
      "author_url": "",
      "post_date": "2025-03-12T12:58:47.220000",
      "content": "<p>best explaination i ever seen i will download this and try understanding this </p>",
      "votes": 3,
      "replies": []
    },
    {
      "id": 3145924,
      "author_name": "Aniruddha Wankhede",
      "author_url": "",
      "post_date": "2025-03-10T11:18:40.100000",
      "content": "<p>How did your team decide on the specific target range [1.345, 1.355] for censored cases (efs = 0), and what impact did that choice have on the model’s ability to generalize across different subsets of the data, particularly in the private test set?</p>",
      "votes": 3,
      "replies": [
        {
          "id": 3145945,
          "author_name": "Nikita Churkin",
          "author_url": "",
          "post_date": "2025-03-10T11:44:09.793000",
          "content": "<p>We picked that range because it optimized our CV score. It worked for Public LB, and for Private LB, eventually. You can find it by tweaking by hand or using more advanced methods (like Bayesian optimization).</p>",
          "votes": 4,
          "replies": []
        }
      ]
    },
    {
      "id": 3143325,
      "author_name": "M4XD",
      "author_url": "",
      "post_date": "2025-03-07T05:26:14.773000",
      "content": "<p>if not a secret - what was the TabM's C-Index on CV/LB?</p>",
      "votes": 3,
      "replies": [
        {
          "id": 3143346,
          "author_name": "Dmitriy Ukrainskiy",
          "author_url": "",
          "post_date": "2025-03-07T06:09:11.173000",
          "content": "<p>MLP-ODST classifier + TabM regressor gave CV 0.68554 (average by folds x seeds; didn't evaluate the overall OOF score), Public 0.69540, and Private 0.69694 (top-11 Private LB).</p>\n<p>It is the best \"solo\" model: <a href=\"https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb\" target=\"_blank\">https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb</a></p>",
          "votes": 6,
          "replies": []
        }
      ]
    },
    {
      "id": 3142831,
      "author_name": "Robert Hatch",
      "author_url": "",
      "post_date": "2025-03-06T17:35:57.470000",
      "content": "<p>What was your best CV score and, if different, your winning CV score?</p>",
      "votes": 3,
      "replies": [
        {
          "id": 3142988,
          "author_name": "Nikita Churkin",
          "author_url": "",
          "post_date": "2025-03-06T20:02:45.957000",
          "content": "<p>I am not sure that particular values are illustrative (aligned CV-LB movements are more important): 0.69139 \"Public\" estimate, 0.695 CV \"Private\" estimate, 0.69692 Public LB, 0.69937 Private LB.</p>",
          "votes": 5,
          "replies": []
        }
      ]
    },
    {
      "id": 3146466,
      "author_name": "",
      "author_url": "",
      "post_date": "2025-03-10T23:15:47.503000",
      "content": "<p>Solution. 0.74 open source reconquistareport blogspot com</p>",
      "votes": -6,
      "replies": []
    },
    {
      "id": 3148849,
      "author_name": "Tanishk Patil",
      "author_url": "",
      "post_date": "2025-03-13T15:22:39.910000",
      "content": "<p>Congratulations! thanks for sharing.</p>",
      "votes": 2,
      "replies": []
    }
  ],
  "comment_count": 18,
  "messages": [
    {
      "id": 3142064,
      "postDate": "2025-03-06T04:17:29.293Z",
      "content": "<p><a href=\"https://www.kaggle.com/code/churkinnikita/3rd-place-solution\" target=\"_blank\">Full code</a></p>\n<p><a href=\"https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb\" target=\"_blank\">Best \"solo\" model (gold medal top-11 Private LB — 0.69694, full training from scratch)</a></p>\n<h6>First of all, we are grateful to the competition organizers for the possibility to contribute to HCT therapy!</h6>\n<p>Our solution has the following steps.</p>\n<p></p><ol><br>\n<li><p>Validation is the most important one. We made a 4-fold CV (as the public test part is 25%) and evaluated the score by 20–100 random split seeds (depends on the computational complexity). During all the competition, we had excellent CV–LB correlation when measuring average fold scores (4 x NSeeds). The public \"fold\" was approximately at the 75-quantile of the score histogram in our experiments. We emulated and tracked both \"public\" and \"private\" scores in this way and created a second submission as more private-oriented. It had a lower LB score but significantly better emulated (and real) private score.</p></li><br>\n<li><p>We made a uniform target in the range [0, 1] for uncensored (<code>efs = 1</code>) observations and in the range [1.345, 1.355] for censored (<code>efs = 0</code>) cases. The target was calculated within train folds and valid folds separately for every race group.</p></li><br>\n<li><p>Then we divided the task into three parts: separate regressions within uncensored and censored data weighted on the probability of belonging to the classes <code>efs = 1</code> and <code>efs = 0</code>.</p></li><br>\n<li><p>Regression within zeros is the most insignificant part of the composition. For NN models, we just collapse all the <code>efs = 0</code> observations to the constant 1.35 (that was tuned to show maximal performance in terms of the concordance index).</p></li><br>\n<li><p>The target range [0, 1] for the regression task allowed us to train the models via binary cross-entropy loss, which is much more profitable in comparison with mean squared error.</p></li><br>\n<li><p>Thus, the prediction has the form<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F9c55f0aede5a15151742a0ee42d7b17d%2Fcomposition_resize.png?generation=1741321386636868&amp;alt=media\" alt=\"\"><br>\nand we obtain an overall scatter plot of type<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F6e95e95854da5b3a7c7c005a8f43618a%2Fscatter_resize.png?generation=1741321580825059&amp;alt=media\" alt=\"\"></p></li><br>\n<li><p>Our model zoo consists of CatBoost, LightGBM, XGBoost, MLP with ODST, and TabM models (separate exemplars of each for regression and classification). NNs were the best at classification, and GBMs are the champions for regression. Ranking models have weaker performance in the competition.</p></li><br>\n<li><p>The data is noisy, and the classic methods of noise reduction worked well. We averaged the models with fixed hyperparameters / architectures over random initialization seeds and eliminated the observations with giant regression errors from the training. But in the case of classification, outlier denoising leads to overfitting (ROC AUC becomes better, but LogLoss worsens at the same time).</p></li><br>\n<li><p>On the very final stage, we blended the regressors by convex combination and stacked the classifiers by logistic model. Stacking by logreg shows spectacular performance on the private part of the test data that we saw via out-of-fold imitation of the public / private split. All blending weights were optimized simultaneously to maximize the concordance index on 20 OOF predictions (20 random split seeds) using Bayesian optimization<br><br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fdf195be3ade190bb433e952e3bb6c12e%2Foptimization_resize.png?generation=1741321695756990&amp;alt=media\" alt=\"\"><br><br>\nThe resulting ensemble is<br><br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F13624458590e21382e28602019fb2fde%2FCIBMTR_resize.png?generation=1741321824928137&amp;alt=media\" alt=\"\"><br><br>\nOverall performance of the components:<br></p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>CV (\"Public\" estimate — folds x seeds)</th>\n<th>Public LB</th>\n<th>Private LB</th>\n<th><br></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>CatBoost</td>\n<td>0.68586</td>\n<td>0.69424</td>\n<td>0.69577</td>\n<td><br></td>\n</tr>\n<tr>\n<td>LightGBM</td>\n<td>0.68530</td>\n<td>0.69318</td>\n<td>0.69572</td>\n<td><br></td>\n</tr>\n<tr>\n<td>XGBoost</td>\n<td>0.68616</td>\n<td>0.69252</td>\n<td>0.69493</td>\n<td><br></td>\n</tr>\n<tr>\n<td>NN</td>\n<td>0.68554</td>\n<td>0.69540</td>\n<td>0.69694</td>\n<td><br></td>\n</tr>\n<tr>\n<td>Blend GBMs</td>\n<td>0.68932</td>\n<td>0.69608</td>\n<td>0.69794</td>\n<td><br></td>\n</tr>\n<tr>\n<td>Blend All + LogReg</td>\n<td>0.69139</td>\n<td>0.69692</td>\n<td>0.69937</td>\n<td><p></p></td></tr></tbody></table></li>\n\n\n</ol>\n\n\n\n\n\n\nTo sum up, the most important trick in this competition was to divide the task into separate regression and classification parts. You could see this trick, for example, by looking at the scatter plot within the first straightforward modeling attempts. The picture below shows training using all data (left), training using <code>efs = 1</code> data only (center), and training using <code>efs = 0</code> data only (right)\n\n\n\n\n\n\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fd238f2de1afcbdd22a83c9bed1bb717d%2Fnaive.jpg?generation=1742565645481169&amp;alt=media\" alt=\"\">\n\n\n\n\n\n\n\n<p>My teammates disclose the details of the solution in the comment section.</p>",
      "rawMarkdown": "[Full code](https://www.kaggle.com/code/churkinnikita/3rd-place-solution)\n\n[Best \"solo\" model (gold medal top-11 Private LB — 0.69694, full training from scratch)](https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb)\n\n###### First of all, we are grateful to the competition organizers for the possibility to contribute to HCT therapy!\n\nOur solution has the following steps.\n\n1. Validation is the most important one. We made a 4-fold CV (as the public test part is 25%) and evaluated the score by 20–100 random split seeds (depends on the computational complexity). During all the competition, we had excellent CV–LB correlation when measuring average fold scores (4 x NSeeds). The public \"fold\" was approximately at the 75-quantile of the score histogram in our experiments. We emulated and tracked both \"public\" and \"private\" scores in this way and created a second submission as more private-oriented. It had a lower LB score but significantly better emulated (and real) private score.\n\n2. We made a uniform target in the range [0, 1] for uncensored (`efs = 1`) observations and in the range [1.345, 1.355] for censored (`efs = 0`) cases. The target was calculated within train folds and valid folds separately for every race group.\n\n3. Then we divided the task into three parts: separate regressions within uncensored and censored data weighted on the probability of belonging to the classes `efs = 1` and `efs = 0`.\n\n4. Regression within zeros is the most insignificant part of the composition. For NN models, we just collapse all the `efs = 0` observations to the constant 1.35 (that was tuned to show maximal performance in terms of the concordance index).\n\n5. The target range [0, 1] for the regression task allowed us to train the models via binary cross-entropy loss, which is much more profitable in comparison with mean squared error.\n\n6. Thus, the prediction has the form\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F9c55f0aede5a15151742a0ee42d7b17d%2Fcomposition_resize.png?generation=1741321386636868&alt=media)\nand we obtain an overall scatter plot of type\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F6e95e95854da5b3a7c7c005a8f43618a%2Fscatter_resize.png?generation=1741321580825059&alt=media)\n7. Our model zoo consists of CatBoost, LightGBM, XGBoost, MLP with ODST, and TabM models (separate exemplars of each for regression and classification). NNs were the best at classification, and GBMs are the champions for regression. Ranking models have weaker performance in the competition.\n\n8. The data is noisy, and the classic methods of noise reduction worked well. We averaged the models with fixed hyperparameters / architectures over random initialization seeds and eliminated the observations with giant regression errors from the training. But in the case of classification, outlier denoising leads to overfitting (ROC AUC becomes better, but LogLoss worsens at the same time).\n\n9. On the very final stage, we blended the regressors by convex combination and stacked the classifiers by logistic model. Stacking by logreg shows spectacular performance on the private part of the test data that we saw via out-of-fold imitation of the public / private split. All blending weights were optimized simultaneously to maximize the concordance index on 20 OOF predictions (20 random split seeds) using Bayesian optimization\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fdf195be3ade190bb433e952e3bb6c12e%2Foptimization_resize.png?generation=1741321695756990&alt=media)\nThe resulting ensemble is\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2F13624458590e21382e28602019fb2fde%2FCIBMTR_resize.png?generation=1741321824928137&alt=media)\nOverall performance of the components:\n| Model | CV (\"Public\" estimate — folds x seeds) | Public LB | Private LB |\n| --- | --- | --- | --- |\n| CatBoost | 0.68586 | 0.69424 | 0.69577 |\n| LightGBM | 0.68530 | 0.69318 | 0.69572 |\n| XGBoost | 0.68616 | 0.69252 | 0.69493 |\n| NN | 0.68554 | 0.69540 | 0.69694 |\n| Blend GBMs | 0.68932 | 0.69608 | 0.69794 |\n| Blend All + LogReg | 0.69139 | 0.69692 | 0.69937 |\n\nTo sum up, the most important trick in this competition was to divide the task into separate regression and classification parts. You could see this trick, for example, by looking at the scatter plot within the first straightforward modeling attempts. The picture below shows training using all data (left), training using `efs = 1` data only (center), and training using `efs = 0` data only (right)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F743648%2Fd238f2de1afcbdd22a83c9bed1bb717d%2Fnaive.jpg?generation=1742565645481169&alt=media)\n\nMy teammates disclose the details of the solution in the comment section.",
      "votes": 53
    },
    {
      "id": 3142358,
      "postDate": "2025-03-06T10:40:54.757Z",
      "content": "<p>For <code>efs=1</code> samples, we used the following target:</p>\n<p><code>risk := rank_by_race(efs_time)</code> mapped to [0, 1].</p>\n<p>Range [0, 1] allowed us to model risk with binary cross-entropy loss, which was a much better choice than MSE/ranking losses because of the superior BCE performance in the final solution.</p>\n<p>For risk modeling, it was helpful to drop some samples (outliers, green on the picture) from train folds (not valid folds!): they were unpredictable and messed up the training process a bit. We deleted up to 1k \"noisy\" samples (samples with the highest BCE).<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F5e774990fc3e909f40aab7f3df97b907%2Foutliers.png?generation=1741257427013353&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "For ``efs=1`` samples, we used the following target:\n\n``risk := rank_by_race(efs_time)`` mapped to [0, 1].\n\nRange [0, 1] allowed us to model risk with binary cross-entropy loss, which was a much better choice than MSE/ranking losses because of the superior BCE performance in the final solution.\n\nFor risk modeling, it was helpful to drop some samples (outliers, green on the picture) from train folds (not valid folds!): they were unpredictable and messed up the training process a bit. We deleted up to 1k \"noisy\" samples (samples with the highest BCE).\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F5e774990fc3e909f40aab7f3df97b907%2Foutliers.png?generation=1741257427013353&alt=media)",
      "votes": 11,
      "replies": [
        {
          "id": 3142363,
          "postDate": "2025-03-06T10:44:54.023Z",
          "content": "<p>I tried mse, huber, poisson and cross-entropy losses, but mse was the best. I'm surprised cross-entropy loss worked better for you.  </p>",
          "rawMarkdown": "I tried mse, huber, poisson and cross-entropy losses, but mse was the best. I'm surprised cross-entropy loss worked better for you.  ",
          "votes": 3
        },
        {
          "id": 3142803,
          "postDate": "2025-03-06T17:17:42.420Z",
          "content": "<p>Ignorant question time: is BCE regression (didn't realize it was possible) essentially exactly identical to what I did, other than margin term? I did normal regression on logit targets (0.01-0.99)?</p>\n<p>I'm guessing no, there's different gradient and hessian(?), but I'm not sure. I didn't recall that you could just use BCE directly for regression, heh. </p>",
          "rawMarkdown": "Ignorant question time: is BCE regression (didn't realize it was possible) essentially exactly identical to what I did, other than margin term? I did normal regression on logit targets (0.01-0.99)?\n\nI'm guessing no, there's different gradient and hessian(?), but I'm not sure. I didn't recall that you could just use BCE directly for regression, heh. ",
          "votes": 3,
          "replies": [
            {
              "id": 3143220,
              "postDate": "2025-03-07T03:01:40.923Z",
              "content": "<p>MSE and BCE are just different functions:</p>\n<p><code>mse(x,y) = (x-y)^2</code>,<br>\n<code>d(mse)/dx = 2(x-y)</code>,<br>\n<code>d^2(mse)/dx^2 = 2</code></p>\n<p><code>bce(x,y) = -(y ln(x) + (1-y) ln(1-x))</code>,<br>\n<code>d(bce)/dx = -(y-x)/(x(1-x))</code>,<br>\n<code>d^2(bce)/dx^2 = y/x^2 + (1-y)/(1-x)^2</code></p>\n<p>Hence, they generate different optimization processes</p>",
              "rawMarkdown": "MSE and BCE are just different functions:\n\n`mse(x,y) = (x-y)^2`,\n`d(mse)/dx = 2(x-y)`,\n`d^2(mse)/dx^2 = 2`\n\n`bce(x,y) = -(y ln(x) + (1-y) ln(1-x))`,\n`d(bce)/dx = -(y-x)/(x(1-x))`,\n`d^2(bce)/dx^2 = y/x^2 + (1-y)/(1-x)^2`\n\nHence, they generate different optimization processes",
              "votes": 9
            }
          ]
        }
      ]
    },
    {
      "id": 3142440,
      "postDate": "2025-03-06T11:50:01.947Z",
      "content": "<p>Some thoughts on validation in this competition.</p>\n<p>If we try to split the train set into 4 parts (size as the public set's size) and try to fit &amp; predict with some model, we will see insane variation of scores depending on the choice of the particular split.</p>\n<p>The picture shows an experiment with 4 folds by 50 split seeds (200 emulated folds in total).<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F7812776112a5ae711c7fe35786ae3302%2Fcv.jpg?generation=1741261793039664&amp;alt=media\" alt=\"\"><br>\nSo, in my view, using only 1 CV split is dangerous and unreliable, especially if you are using leak-prone procedures as early stopping. High deviation of validation scores is enough to incorrectly evaluate almost any (at least non-major) improvement.</p>\n<p>For the final evaluation and blending, we recomputed our out-of-fold predictions corresponding to 20 new, \"unseen\" random seeds to, hopefully, decrease any \"seed-induced\" bias.</p>",
      "rawMarkdown": "Some thoughts on validation in this competition.\n\nIf we try to split the train set into 4 parts (size as the public set's size) and try to fit & predict with some model, we will see insane variation of scores depending on the choice of the particular split.\n\nThe picture shows an experiment with 4 folds by 50 split seeds (200 emulated folds in total).\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F578229%2F7812776112a5ae711c7fe35786ae3302%2Fcv.jpg?generation=1741261793039664&alt=media)\nSo, in my view, using only 1 CV split is dangerous and unreliable, especially if you are using leak-prone procedures as early stopping. High deviation of validation scores is enough to incorrectly evaluate almost any (at least non-major) improvement.\n\nFor the final evaluation and blending, we recomputed our out-of-fold predictions corresponding to 20 new, \"unseen\" random seeds to, hopefully, decrease any \"seed-induced\" bias.",
      "votes": 10,
      "replies": [
        {
          "id": 3142789,
          "postDate": "2025-03-06T17:09:15.733Z",
          "content": "<p>Not only is it a good idea… It takes a bit of time and effort! Kudos to your team for the disciplined approach and putting in the time and effort to make it happen!</p>",
          "rawMarkdown": "Not only is it a good idea... It takes a bit of time and effort! Kudos to your team for the disciplined approach and putting in the time and effort to make it happen!",
          "votes": 5
        }
      ]
    },
    {
      "id": 3142609,
      "postDate": "2025-03-06T14:30:34.350Z",
      "content": "<p>Survival analysis is a fruitful branch of mathematics, but open-source models and approaches didn’t show competitive performance. Nevertheless, the paper [<a href=\"https://arxiv.org/abs/2410.01086\" target=\"_blank\">https://arxiv.org/abs/2410.01086</a>] from the author of the Deep Kernel Survival Analysis framework helped to dive deeper into the subject details.</p>\n<p>Regarding the feature engineering stage, we tested various variables. Some of them were designed to detect anomalies and some to catch rare data patterns (e.g., preprocessing of text features: TF-IDF vectorizer, embedding with Bio NLP). We also tried complex multi-category combinations, but the simple frequency encoder was the one who improved the score for some of our models (although not drastically).</p>\n<p>We also experimented with different generative models (similar to SurvivalGAN) to improve the resulting performance with additional data. However, it didn’t lead to a substantial score increase.</p>",
      "rawMarkdown": "Survival analysis is a fruitful branch of mathematics, but open-source models and approaches didn’t show competitive performance. Nevertheless, the paper [https://arxiv.org/abs/2410.01086] from the author of the Deep Kernel Survival Analysis framework helped to dive deeper into the subject details.\n\nRegarding the feature engineering stage, we tested various variables. Some of them were designed to detect anomalies and some to catch rare data patterns (e.g., preprocessing of text features: TF-IDF vectorizer, embedding with Bio NLP). We also tried complex multi-category combinations, but the simple frequency encoder was the one who improved the score for some of our models (although not drastically).\n\nWe also experimented with different generative models (similar to SurvivalGAN) to improve the resulting performance with additional data. However, it didn’t lead to a substantial score increase.",
      "votes": 7,
      "replies": [
        {
          "id": 3145949,
          "postDate": "2025-03-10T11:53:21.253Z",
          "content": "<p>Some tricks that haven’t worked:</p>\n<ul>\n<li>Target encoding</li>\n<li>Autoencoders: DAE, VAE</li>\n<li>Other kinds of models: SVM, Gaussian Processes, RGF, TabNet, FTTransformer</li>\n<li>Sophisticated stacking instead of blending</li>\n</ul>",
          "rawMarkdown": "Some tricks that haven’t worked:\n\n* Target encoding\n* Autoencoders: DAE, VAE\n* Other kinds of models: SVM, Gaussian Processes, RGF, TabNet, FTTransformer\n* Sophisticated stacking instead of blending",
          "votes": 4
        }
      ]
    },
    {
      "id": 3149258,
      "postDate": "2025-03-14T02:08:48.493Z",
      "content": "<p>Great Explaination!</p>",
      "rawMarkdown": "Great Explaination!",
      "votes": 3
    },
    {
      "id": 3147825,
      "postDate": "2025-03-12T12:58:47.220Z",
      "content": "<p>best explaination i ever seen i will download this and try understanding this </p>",
      "rawMarkdown": "best explaination i ever seen i will download this and try understanding this ",
      "votes": 3
    },
    {
      "id": 3145924,
      "postDate": "2025-03-10T11:18:40.100Z",
      "content": "<p>How did your team decide on the specific target range [1.345, 1.355] for censored cases (efs = 0), and what impact did that choice have on the model’s ability to generalize across different subsets of the data, particularly in the private test set?</p>",
      "rawMarkdown": "How did your team decide on the specific target range [1.345, 1.355] for censored cases (efs = 0), and what impact did that choice have on the model’s ability to generalize across different subsets of the data, particularly in the private test set?",
      "votes": 3,
      "replies": [
        {
          "id": 3145945,
          "postDate": "2025-03-10T11:44:09.793Z",
          "content": "<p>We picked that range because it optimized our CV score. It worked for Public LB, and for Private LB, eventually. You can find it by tweaking by hand or using more advanced methods (like Bayesian optimization).</p>",
          "rawMarkdown": "We picked that range because it optimized our CV score. It worked for Public LB, and for Private LB, eventually. You can find it by tweaking by hand or using more advanced methods (like Bayesian optimization).",
          "votes": 4
        }
      ]
    },
    {
      "id": 3143325,
      "postDate": "2025-03-07T05:26:14.773Z",
      "content": "<p>if not a secret - what was the TabM's C-Index on CV/LB?</p>",
      "rawMarkdown": "if not a secret - what was the TabM's C-Index on CV/LB?",
      "votes": 3,
      "replies": [
        {
          "id": 3143346,
          "postDate": "2025-03-07T06:09:11.173Z",
          "content": "<p>MLP-ODST classifier + TabM regressor gave CV 0.68554 (average by folds x seeds; didn't evaluate the overall OOF score), Public 0.69540, and Private 0.69694 (top-11 Private LB).</p>\n<p>It is the best \"solo\" model: <a href=\"https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb\" target=\"_blank\">https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb</a></p>",
          "rawMarkdown": "MLP-ODST classifier + TabM regressor gave CV 0.68554 (average by folds x seeds; didn't evaluate the overall OOF score), Public 0.69540, and Private 0.69694 (top-11 Private LB).\n\nIt is the best \"solo\" model: https://www.kaggle.com/code/ukrainskiydv/3rd-place-solution-nn-solo-0-69694-private-lb",
          "votes": 6
        }
      ]
    },
    {
      "id": 3142831,
      "postDate": "2025-03-06T17:35:57.470Z",
      "content": "<p>What was your best CV score and, if different, your winning CV score?</p>",
      "rawMarkdown": "What was your best CV score and, if different, your winning CV score?",
      "votes": 3,
      "replies": [
        {
          "id": 3142988,
          "postDate": "2025-03-06T20:02:45.957Z",
          "content": "<p>I am not sure that particular values are illustrative (aligned CV-LB movements are more important): 0.69139 \"Public\" estimate, 0.695 CV \"Private\" estimate, 0.69692 Public LB, 0.69937 Private LB.</p>",
          "rawMarkdown": "I am not sure that particular values are illustrative (aligned CV-LB movements are more important): 0.69139 \"Public\" estimate, 0.695 CV \"Private\" estimate, 0.69692 Public LB, 0.69937 Private LB.",
          "votes": 5
        }
      ]
    },
    {
      "id": 3146466,
      "postDate": "2025-03-10T23:15:47.503Z",
      "content": "<p>Solution. 0.74 open source reconquistareport blogspot com</p>",
      "rawMarkdown": "Solution. 0.74 open source reconquistareport blogspot com",
      "votes": -6
    },
    {
      "id": 3148849,
      "postDate": "2025-03-13T15:22:39.910Z",
      "content": "<p>Congratulations! thanks for sharing.</p>",
      "rawMarkdown": "Congratulations! thanks for sharing.",
      "votes": 2
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-19T19:46:59Z",
  "competition_id": 70942,
  "competition_slug": "equity-post-HCT-survival-predictions",
  "team_id": 13054779,
  "solution_url": "/competitions/equity-post-HCT-survival-predictions/writeups/survivalhorror-3rd-place-solution"
}