{
  "id": 127500,
  "title": "15th palce solution",
  "content": "<p>We( @yasufuminakama, @currypurin, @hidehisaarai1213 ) would like to thank Booz Allen Hamilton for the very interesting competition and to all the participants for giving us a lot of ideas.</p>\n\n<h2>Features</h2>\n\n<h3>Nakama Feature</h3>\n\n<ul>\n<li>Nunique features of ['event_id', 'game_session', ...and so on]</li>\n<li>Decayed Count features &amp; Ratio features of ['title_event_code', 'title', ...and so on] <br>\nCount features decayed by elapsed time from previous assessment and their Ratio features. \nRatio features are better than Count features. <br>\nBelow is an example of decay function.\n<code>python\ndef decaying_counter(x, days):\nreturn max(0.xx, 1-days/30) * x\n</code></li>\n<li>Misclicking features <br>\nAs mentioned <a href=\"https://www.kaggle.com/zgzjnbzl/visualizing-distraction-and-misclicking\">here</a>, event_code 4070 are clicks on invalid places on the screen. \nSo by using kmeans clustering of 4070 (x, y), we expect cluster as object or something on the screen, then calculating distance from it's own cluster, the distance can represent \n\"Operation is rougher or unfamiliar as the distance is larger?\".</li>\n<li>Past assessment features <br>\nStatistical features of past assessment of all time &amp; last 7 days for overall &amp; each assessment title. </li>\n<li>What didn't work\nTfIdf &amp; w2v feature on sequence of titles before assessment. I should've tried more...\n<h3>アライさん's features</h3></li>\n</ul>\n\n<p>Feature generation code is made public at <a href=\"https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py\">https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py</a>.\n* Features based on public kernels\nAlthough it worked well, it can also be a cause of overfitting since the distribution of count based features differs between train and test. Therefore, I also applied decaying factor to count them or get average of them, which had already been proved to be effective to avoid overfitting in Y.Nakama's experiment.\nDecaying of count features in Y.Nakama's and mine is slightly different, since Y.Nakama applied decaying to every assessments while I applied decaying to every sessions.\nNote that some of those features which appeared to be not useful so much based on LightGBM importance or showed high correlation with other features were deleted from feature generation code.\n* Past Assessment features\n  * {mean, var, last} of {success_ratio, n_failure, accuracy_group} of the same assessment type in the past\n  * time to get success\n  * {mean, var} of interval of actions (event_code <code>4020</code>, <code>4025</code>)\n* Past Game features\n  * {mean, var, last} of {correct, incorrect} (decayed) count of each Game\n  * {mean, var, last} of success ratio of each Game\n  * {mean, var} of interval of actions in each Game\n* Past Activity features\nFew handcrafted features of some of the activities.</p>\n\n<h2>data augmentation idea</h2>\n\n<p>As we apply decay function to Count features, we could augment data by using different decay functions. The aim is that oblivion curve is different from each people by age or something.</p>\n\n<h2>Feature selection</h2>\n\n<p>Feature selection using LightGBM / CatBoost importance were applied before training. About 80 - 90% of the features were deleted at this step and the resulting number of features are around 400. Feature selection was effective especially to NN model (probably because of high dropout rate in NN model) and bumped up the oof score around 0.005 for GBDT model and 0.01 for NN model.</p>\n\n<h2>Model</h2>\n\n<p>Applying certain transformation to the output of multiclass classification gives us better result\ncompared to regression. The transformation is as follows.\n<code>python\nprediction @ np.arange(4)  # the format of prediction should be (n_samples, 4)\n</code></p>\n\n<h3>Tree based models</h3>\n\n<p>My team tried several objectives. Cross entropy and multiclass worked, used it for the final model.\n* Final model used three models\n  * Lightgbm: cross entropy\n  * Lightgbm: multiclass\n  * Catboost: multiclass\n* cross entropy\n  * Divide the target by 3 and convert from 0 to 1, then learn with cross entropy (objective: xentropy). In the final model, this model's weight was the largest.\n* multiclass\n  * In multiclass, after calculating the probabilities of the target class from 0 to 3, the following calculation is performed to make continuous values.\n  * <code>preds @ np.arange(4)</code></p>\n\nTree based models didn't work\n\n<ul>\n<li>CatBoost\n<ul><li>regression, CrossEntropy</li></ul></li>\n<li>Lightgbm\n<ul><li>regression, multiclassova（One-vs-All)</li></ul></li>\n<li>Xgboost\n<ul><li>regression, reg:logistic\n<h3>NN model</h3></li></ul></li>\n</ul>\n\n<p>Our NN model is simple 3 layer MLP. The implementation is <a href=\"https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/models/neural_network/model.py\">here</a> (<code>DSBOvR</code> is the model we used).\nWe used training of one-vs-rest fashion, so the output of the model is a (n_batch, 4) shape tensor and each column represents the probability of each class. <code>torch.nn.BCELoss</code> was used for loss function and after getting the output tensor, following transformation is applied to get (pseudo-)regression value.\n<code>python\nvalid_preds = valid_preds / np.repeat(\n    valid_preds.sum(axis=1), 4).reshape(-1, 4)  # normalization\nvalid_preds = valid_preds @ np.arange(4) / 3  # conversion to get pseudo-regression value\n</code>\nthis pseudo-regression value can be used for threshold optimization. Note that we normalized this value to be in the range of (0.0, 1.0) while training.\nBefore training, feature selection using LightGBM importance (about 80-90% of the features were deleted), preprocessing (fillna, log transformation for those feature which showed high skewness, feature scaling with <code>StandardScaler</code>) was applied. When training, Adam optimizer is used with CosineAnnealing lr scheduler and for each fold we trained the model 100 epochs. At the end of each epoch we calculate QWK using threshold optimization to pseudo-regression value and saved the weights if the best score is achieved. Final oof and prediction to test data was made with the weights which achived the best QWK score at each fold.\nWe've also prepared NN only kernel <a href=\"https://www.kaggle.com/hidehisaarai1213/dsb2019-nn-ovr-reduce-90-val-60-percentile\">here</a>.</p>\n\n<h2>validation strategy</h2>\n\n<ul>\n<li>validation selected by number of Assessments\nIf validation is performed using all data, model fits strongly to the data which has many previous assessments and thus easy to predict. \nTherefore, the 95% quantile of the distribution of the Assessment number of the test that is truncated is used as a threshold, then removed the data that exceeds the threshold from validation. That one also raised all oof CV.\n<h2>Ensemble and QWK threshold</h2></li>\n</ul>\n\n<p>Ensemble using all oof is not appropriate to maximize truncated CV.\nTherefore, We sampled the training data at the same ratio as when truncate.\nIn particular, sampling weight is 1/(Assessment Count) for each installation_id.\nBlend is performed based on this sampled data. We also tried stacking by Ridge regression, but we don't think there is a big difference from blending.\nThe threshold is also determined so that the truncated cv of this sampled data is maximized.</p>\n\n<h2>Metric used for validation</h2>\n\n<p>Both public LB score and oof score was not very helpful to judge if a change in our submission is effective or not. Therefore we used truncation to train data to mimic the generation process of test data. This truncation is mostly the same as that shared in common in discussion (select 1 assessment from each installation_id). Since this score is a bit unstable we repeated the sampling &amp; scoring process 1000 times and calulated the mean of the score.</p>\n\n<h2>Final result</h2>\n\n<ol>\n<li>truncated score: 0.5818, public score: 0.565, private score: 0.557 (private 15th)</li>\n<li>truncated score: 0.5811, public score: 0.574 (public 5th), private score: 0.556</li>\n</ol>",
  "comments": [
    {
      "id": 727972,
      "author_name": "",
      "author_url": "",
      "post_date": "2020-01-24T09:23:10.760000",
      "content": "",
      "votes": 0,
      "replies": []
    }
  ],
  "comment_count": 1,
  "messages": [
    {
      "id": 727963,
      "postDate": "2020-01-24T09:10:39.153Z",
      "content": "<p>We( @yasufuminakama, @currypurin, @hidehisaarai1213 ) would like to thank Booz Allen Hamilton for the very interesting competition and to all the participants for giving us a lot of ideas.</p>\n\n<h2>Features</h2>\n\n<h3>Nakama Feature</h3>\n\n<ul>\n<li>Nunique features of ['event_id', 'game_session', ...and so on]</li>\n<li>Decayed Count features &amp; Ratio features of ['title_event_code', 'title', ...and so on] <br>\nCount features decayed by elapsed time from previous assessment and their Ratio features. \nRatio features are better than Count features. <br>\nBelow is an example of decay function.\n<code>python\ndef decaying_counter(x, days):\nreturn max(0.xx, 1-days/30) * x\n</code></li>\n<li>Misclicking features <br>\nAs mentioned <a href=\"https://www.kaggle.com/zgzjnbzl/visualizing-distraction-and-misclicking\">here</a>, event_code 4070 are clicks on invalid places on the screen. \nSo by using kmeans clustering of 4070 (x, y), we expect cluster as object or something on the screen, then calculating distance from it's own cluster, the distance can represent \n\"Operation is rougher or unfamiliar as the distance is larger?\".</li>\n<li>Past assessment features <br>\nStatistical features of past assessment of all time &amp; last 7 days for overall &amp; each assessment title. </li>\n<li>What didn't work\nTfIdf &amp; w2v feature on sequence of titles before assessment. I should've tried more...\n<h3>アライさん's features</h3></li>\n</ul>\n\n<p>Feature generation code is made public at <a href=\"https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py\">https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py</a>.\n* Features based on public kernels\nAlthough it worked well, it can also be a cause of overfitting since the distribution of count based features differs between train and test. Therefore, I also applied decaying factor to count them or get average of them, which had already been proved to be effective to avoid overfitting in Y.Nakama's experiment.\nDecaying of count features in Y.Nakama's and mine is slightly different, since Y.Nakama applied decaying to every assessments while I applied decaying to every sessions.\nNote that some of those features which appeared to be not useful so much based on LightGBM importance or showed high correlation with other features were deleted from feature generation code.\n* Past Assessment features\n  * {mean, var, last} of {success_ratio, n_failure, accuracy_group} of the same assessment type in the past\n  * time to get success\n  * {mean, var} of interval of actions (event_code <code>4020</code>, <code>4025</code>)\n* Past Game features\n  * {mean, var, last} of {correct, incorrect} (decayed) count of each Game\n  * {mean, var, last} of success ratio of each Game\n  * {mean, var} of interval of actions in each Game\n* Past Activity features\nFew handcrafted features of some of the activities.</p>\n\n<h2>data augmentation idea</h2>\n\n<p>As we apply decay function to Count features, we could augment data by using different decay functions. The aim is that oblivion curve is different from each people by age or something.</p>\n\n<h2>Feature selection</h2>\n\n<p>Feature selection using LightGBM / CatBoost importance were applied before training. About 80 - 90% of the features were deleted at this step and the resulting number of features are around 400. Feature selection was effective especially to NN model (probably because of high dropout rate in NN model) and bumped up the oof score around 0.005 for GBDT model and 0.01 for NN model.</p>\n\n<h2>Model</h2>\n\n<p>Applying certain transformation to the output of multiclass classification gives us better result\ncompared to regression. The transformation is as follows.\n<code>python\nprediction @ np.arange(4)  # the format of prediction should be (n_samples, 4)\n</code></p>\n\n<h3>Tree based models</h3>\n\n<p>My team tried several objectives. Cross entropy and multiclass worked, used it for the final model.\n* Final model used three models\n  * Lightgbm: cross entropy\n  * Lightgbm: multiclass\n  * Catboost: multiclass\n* cross entropy\n  * Divide the target by 3 and convert from 0 to 1, then learn with cross entropy (objective: xentropy). In the final model, this model's weight was the largest.\n* multiclass\n  * In multiclass, after calculating the probabilities of the target class from 0 to 3, the following calculation is performed to make continuous values.\n  * <code>preds @ np.arange(4)</code></p>\n\nTree based models didn't work\n\n<ul>\n<li>CatBoost\n<ul><li>regression, CrossEntropy</li></ul></li>\n<li>Lightgbm\n<ul><li>regression, multiclassova（One-vs-All)</li></ul></li>\n<li>Xgboost\n<ul><li>regression, reg:logistic\n<h3>NN model</h3></li></ul></li>\n</ul>\n\n<p>Our NN model is simple 3 layer MLP. The implementation is <a href=\"https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/models/neural_network/model.py\">here</a> (<code>DSBOvR</code> is the model we used).\nWe used training of one-vs-rest fashion, so the output of the model is a (n_batch, 4) shape tensor and each column represents the probability of each class. <code>torch.nn.BCELoss</code> was used for loss function and after getting the output tensor, following transformation is applied to get (pseudo-)regression value.\n<code>python\nvalid_preds = valid_preds / np.repeat(\n    valid_preds.sum(axis=1), 4).reshape(-1, 4)  # normalization\nvalid_preds = valid_preds @ np.arange(4) / 3  # conversion to get pseudo-regression value\n</code>\nthis pseudo-regression value can be used for threshold optimization. Note that we normalized this value to be in the range of (0.0, 1.0) while training.\nBefore training, feature selection using LightGBM importance (about 80-90% of the features were deleted), preprocessing (fillna, log transformation for those feature which showed high skewness, feature scaling with <code>StandardScaler</code>) was applied. When training, Adam optimizer is used with CosineAnnealing lr scheduler and for each fold we trained the model 100 epochs. At the end of each epoch we calculate QWK using threshold optimization to pseudo-regression value and saved the weights if the best score is achieved. Final oof and prediction to test data was made with the weights which achived the best QWK score at each fold.\nWe've also prepared NN only kernel <a href=\"https://www.kaggle.com/hidehisaarai1213/dsb2019-nn-ovr-reduce-90-val-60-percentile\">here</a>.</p>\n\n<h2>validation strategy</h2>\n\n<ul>\n<li>validation selected by number of Assessments\nIf validation is performed using all data, model fits strongly to the data which has many previous assessments and thus easy to predict. \nTherefore, the 95% quantile of the distribution of the Assessment number of the test that is truncated is used as a threshold, then removed the data that exceeds the threshold from validation. That one also raised all oof CV.\n<h2>Ensemble and QWK threshold</h2></li>\n</ul>\n\n<p>Ensemble using all oof is not appropriate to maximize truncated CV.\nTherefore, We sampled the training data at the same ratio as when truncate.\nIn particular, sampling weight is 1/(Assessment Count) for each installation_id.\nBlend is performed based on this sampled data. We also tried stacking by Ridge regression, but we don't think there is a big difference from blending.\nThe threshold is also determined so that the truncated cv of this sampled data is maximized.</p>\n\n<h2>Metric used for validation</h2>\n\n<p>Both public LB score and oof score was not very helpful to judge if a change in our submission is effective or not. Therefore we used truncation to train data to mimic the generation process of test data. This truncation is mostly the same as that shared in common in discussion (select 1 assessment from each installation_id). Since this score is a bit unstable we repeated the sampling &amp; scoring process 1000 times and calulated the mean of the score.</p>\n\n<h2>Final result</h2>\n\n<ol>\n<li>truncated score: 0.5818, public score: 0.565, private score: 0.557 (private 15th)</li>\n<li>truncated score: 0.5811, public score: 0.574 (public 5th), private score: 0.556</li>\n</ol>",
      "rawMarkdown": "We( @yasufuminakama, @currypurin, @hidehisaarai1213 ) would like to thank Booz Allen Hamilton for the very interesting competition and to all the participants for giving us a lot of ideas.\n## Features\n### Nakama Feature\n- Nunique features of ['event\\_id', 'game_session', ...and so on]\n- Decayed Count features &amp; Ratio features of ['title\\_event\\_code', 'title', ...and so on]  \nCount features decayed by elapsed time from previous assessment and their Ratio features. \nRatio features are better than Count features.   \nBelow is an example of decay function.\n```python\ndef decaying_counter(x, days):\n    return max(0.xx, 1-days/30) * x\n```\n- Misclicking features  \nAs mentioned [here](https://www.kaggle.com/zgzjnbzl/visualizing-distraction-and-misclicking), event_code 4070 are clicks on invalid places on the screen. \nSo by using kmeans clustering of 4070 (x, y), we expect cluster as object or something on the screen, then calculating distance from it's own cluster, the distance can represent \n\"Operation is rougher or unfamiliar as the distance is larger?\".\n- Past assessment features  \nStatistical features of past assessment of all time &amp; last 7 days for overall &amp; each assessment title. \n- What didn't work\nTfIdf &amp; w2v feature on sequence of titles before assessment. I should've tried more...\n### アライさん's features\nFeature generation code is made public at [https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py](https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/features/past_summary3_decay.py).\n* Features based on public kernels\nAlthough it worked well, it can also be a cause of overfitting since the distribution of count based features differs between train and test. Therefore, I also applied decaying factor to count them or get average of them, which had already been proved to be effective to avoid overfitting in Y.Nakama's experiment.\nDecaying of count features in Y.Nakama's and mine is slightly different, since Y.Nakama applied decaying to every assessments while I applied decaying to every sessions.\nNote that some of those features which appeared to be not useful so much based on LightGBM importance or showed high correlation with other features were deleted from feature generation code.\n* Past Assessment features\n  * {mean, var, last} of {success_ratio, n_failure, accuracy_group} of the same assessment type in the past\n  * time to get success\n  * {mean, var} of interval of actions (event_code `4020`, `4025`)\n* Past Game features\n  * {mean, var, last} of {correct, incorrect} (decayed) count of each Game\n  * {mean, var, last} of success ratio of each Game\n  * {mean, var} of interval of actions in each Game\n* Past Activity features\nFew handcrafted features of some of the activities.\n## data augmentation idea\nAs we apply decay function to Count features, we could augment data by using different decay functions. The aim is that oblivion curve is different from each people by age or something.\n## Feature selection\nFeature selection using LightGBM / CatBoost importance were applied before training. About 80 - 90% of the features were deleted at this step and the resulting number of features are around 400. Feature selection was effective especially to NN model (probably because of high dropout rate in NN model) and bumped up the oof score around 0.005 for GBDT model and 0.01 for NN model.\n## Model\nApplying certain transformation to the output of multiclass classification gives us better result\ncompared to regression. The transformation is as follows.\n```python\nprediction @ np.arange(4)  # the format of prediction should be (n_samples, 4)\n```\n### Tree based models\nMy team tried several objectives. Cross entropy and multiclass worked, used it for the final model.\n* Final model used three models\n  * Lightgbm: cross entropy\n  * Lightgbm: multiclass\n  * Catboost: multiclass\n* cross entropy\n  * Divide the target by 3 and convert from 0 to 1, then learn with cross entropy (objective: xentropy). In the final model, this model's weight was the largest.\n* multiclass\n  * In multiclass, after calculating the probabilities of the target class from 0 to 3, the following calculation is performed to make continuous values.\n  * `preds @ np.arange(4)`\n#### Tree based models didn't work\n* CatBoost\n  * regression, CrossEntropy\n* Lightgbm\n  * regression, multiclassova（One-vs-All)\n* Xgboost\n  * regression, reg:logistic\n### NN model\nOur NN model is simple 3 layer MLP. The implementation is [here](https://github.com/koukyo1994/kaggle-dsb2019/blob/master/src/models/neural_network/model.py) (`DSBOvR` is the model we used).\nWe used training of one-vs-rest fashion, so the output of the model is a (n_batch, 4) shape tensor and each column represents the probability of each class. `torch.nn.BCELoss` was used for loss function and after getting the output tensor, following transformation is applied to get (pseudo-)regression value.\n```python\nvalid_preds = valid_preds / np.repeat(\n    valid_preds.sum(axis=1), 4).reshape(-1, 4)  # normalization\nvalid_preds = valid_preds @ np.arange(4) / 3  # conversion to get pseudo-regression value\n```\nthis pseudo-regression value can be used for threshold optimization. Note that we normalized this value to be in the range of (0.0, 1.0) while training.\nBefore training, feature selection using LightGBM importance (about 80-90% of the features were deleted), preprocessing (fillna, log transformation for those feature which showed high skewness, feature scaling with `StandardScaler`) was applied. When training, Adam optimizer is used with CosineAnnealing lr scheduler and for each fold we trained the model 100 epochs. At the end of each epoch we calculate QWK using threshold optimization to pseudo-regression value and saved the weights if the best score is achieved. Final oof and prediction to test data was made with the weights which achived the best QWK score at each fold.\nWe've also prepared NN only kernel [here](https://www.kaggle.com/hidehisaarai1213/dsb2019-nn-ovr-reduce-90-val-60-percentile).\n## validation strategy\n- validation selected by number of Assessments\nIf validation is performed using all data, model fits strongly to the data which has many previous assessments and thus easy to predict. \nTherefore, the 95% quantile of the distribution of the Assessment number of the test that is truncated is used as a threshold, then removed the data that exceeds the threshold from validation. That one also raised all oof CV.\n## Ensemble and QWK threshold\nEnsemble using all oof is not appropriate to maximize truncated CV.\nTherefore, We sampled the training data at the same ratio as when truncate.\nIn particular, sampling weight is 1/(Assessment Count) for each installation_id.\nBlend is performed based on this sampled data. We also tried stacking by Ridge regression, but we don't think there is a big difference from blending.\nThe threshold is also determined so that the truncated cv of this sampled data is maximized.\n## Metric used for validation\nBoth public LB score and oof score was not very helpful to judge if a change in our submission is effective or not. Therefore we used truncation to train data to mimic the generation process of test data. This truncation is mostly the same as that shared in common in discussion (select 1 assessment from each installation\\_id). Since this score is a bit unstable we repeated the sampling &amp; scoring process 1000 times and calulated the mean of the score.\n## Final result\n1. truncated score: 0.5818, public score: 0.565, private score: 0.557 (private 15th)\n2. truncated score: 0.5811, public score: 0.574 (public 5th), private score: 0.556",
      "votes": 37
    },
    {
      "id": 727972,
      "postDate": "2020-01-24T09:23:10.760Z",
      "rawMarkdown": "",
      "isDeleted": true
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-23T23:49:18Z",
  "competition_id": 16531,
  "competition_slug": "data-science-bowl-2019",
  "team_id": 3858557,
  "solution_url": "/competitions/data-science-bowl-2019/writeups/kaggler-ja-15th-palce-solution"
}