# PostProcess Trick - 21st place Partial Solution Hi all! First congrats to all competitors and especially solo gold winners. They just did an amazing job. I especially thank to [@ogrellier](/ogrellier) for his great kernels. First time I’ve used chunk method and multi-threading. Our final submission was a blend of our single models and I had a single LGBM model with a CV of 0.493 and LB of 0.915. For me, most important boost came from using ratio of different passband features and using different sample weights instead of direct inverse ratio of class counts. Also using top 55 features as a feature selection method, gave another important boost. My teammates will explain details of models and features in another thread. In this thread I want to mention about postprocessing trick I’ve used and till now did not see in another thread. First I validated postprocessing on my OOF predictions and it also improved my LB score. After I was invited by the team, same process improved their models' both CV and LB. Of course, as the model itself improves, gain from postprocess decreases. But, I think it will improve most of your models as well. Before this method, my best model had a CV of 0.52 and LB of 0.968 (Private: 0.99101). With this method CV moved to 0.493 and LB to 0.915 (Private: 0.93077) :) I think what lies behind this trick to work is the competition metric. For each row, the correct class’s probability is multiplied with 1 and the rest with zero. That is, it does not matter how do you distribute probabilities among wrong classes. So with this operation, you can compensate the lack of your model by simply changing class preds with weights. For example, my model was giving higher probability for class 42 and class 90 most of the time and lowering their predictions always improved my CV. Here is the code for your oof predictions. You can give a try: def change(preds): oof_df = pd.DataFrame(preds).copy() oof_df[3] = oof_df[3]* 1 oof_df[11] = oof_df[11]* 1 oof_df[0] = oof_df[0]* 1 oof_df[1] = oof_df[1]* 1 oof_df[2] = oof_df[2]* 1 oof_df[4] = oof_df[4]* 1 oof_df[5] = oof_df[5]* 1 oof_df[6] = oof_df[6]* 1 oof_df[7] = oof_df[7]* 1 oof_df[8] = oof_df[8]* 1 oof_df[12] = oof_df[12]* 1 oof_df[10] = oof_df[10]* 1 oof_df[13] = oof_df[13]* 1 #This part scales them so their sum makes 1 back again. sum = oof_df[[0,1,2,3,4,5,6,7,8,9,10,11,12,13]].sum(axis=1) for col in [0,1,2,3,4,5,6,7,8,9,10,11,12,13]: oof_df[col] = oof_df[col]/sum return oof_df.values oof_preds_modfy = change(oof_preds) #Give oof preds array to the function print("Scores OOF Processed: ",multi_weighted_logloss(y_true=y, y_preds=oof_preds_modfy)) print("Scores OOF Original: ",multi_weighted_logloss(y_true=y, y_preds=oof_preds)) It’s quite simple. Try different weights for each class one by one and check your multi weighted logloss if it improved or not. For each class it has one direction for improvement, so it takes a little time to find correct combination. Each model has different characteristics, so you have to adjust them based on your oof. Here is my weights. (Some look weird, doesn’t it? But improved) oof_df[3] = oof_df[3]*0.55 oof_df[11] = oof_df[11]*0.5 oof_df[0] = oof_df[0]*2 oof_df[1] = oof_df[1]*0.85 oof_df[2] = oof_df[2]*0.5 oof_df[4] = oof_df[4]*1 oof_df[5] = oof_df[5]*4 oof_df[6] = oof_df[6]*0.65 oof_df[7] = oof_df[7]*2.9 oof_df[8] = oof_df[8]*0.35 oof_df[12] = oof_df[12]*3.7 oof_df[10] = oof_df[10]*1.9 oof_df[13] = oof_df[13]*0.9 Moreover, yesterday we had one left submission and I wanted to try another postprocessing. It was simply this code: pred[i] = pred[i].apply(lambda x: 1.1 if x>0.76 else x ) #After changing preds, make sure that sum of preds make 1 again. Of course, first I tried on oof predictions and it was getting improvement in third decimals. So our final submission moved from 0.854 to 0.851 (Private: 0.87021 to 0.86777) and it helped us to move one place up in Private LB :P. I would be very happy indeed if you also try and share with us if it worked for you as well or not. See you in next competition!