ãã®èšäºã¯ Enigmo Advent Calendar 2018ã®10æ¥ç® ã§ãã ã¯ããã« Optunaã¯PFN瀟ãå
¬éãããã€ããŒãã©ã¡ãŒã¿èªåæé©å ãã¬ãŒã ã¯ãŒã¯ ã§ãã https://research.preferred.jp/2018/12/optuna-release/ ç®ç颿°ããæ±ºããã°ãçŽæçã«æé©åãèµ°ãããããšãå¯èœã®ããã§ãã ä»åãæé©åèªäœã®èª¬æã¯å²æãããŠããã ããŸããã æ©æ¢°åŠç¿ ã®å
¥éãšããããšãèãããšãã€ããŒãã©ã¡ãŒã¿ã®èª¿æŽãšããŠã¯ãgridsearchãRandomizedSearchCVã§è¡ãæ©äŒãå€ããšæããŸãã ã¹ãã«ããããã¯ãªãœãŒã¹ã§ãªããšããããšããããšã«ãªãããšæããŸãããç¹ã«ãkaggleã®ãããª0.X%ã®ç²ŸåºŠãåäžãéèŠã«ãªããããªç¶æ³ã§ã¯ããã€ããŒãã©ã¡ãŒã¿ã®ãã¥ãŒãã³ã°ã倧ããªããŒãã«ã®äžã€ã«ãªããŸãã ããã§ãtitanicã§ã®submitã¯ãããã®ã®ãKaggleã®çµéšãã»ãŒãŒããªçè
ã§ããOptunaã§ç°¡åã«ãã¥ãŒãã³ã°ãã§ãããã©ããã詊ããŠã¿ãããšæããŸãã ä»åã®å¯Ÿè±¡ã³ã³ã æ¢ã«closeããŠããã³ã³ãã®äžã§ãäžèšã®Porto Seguroâs Safe Driver Predictionãéžã³ãŸããã https://www.kaggle.com/c/porto-seguro-safe-driver-prediction éžå®çç±ã¯ä»¥äžã®éãã§ãã ããŒã¿ãããã»ã©å€§ãããªã æå
ïŒèªå®
ïŒã®ã©ãããããã®RAMã¯8GBãšå€§ãããªãã®ã§ãXGboostã§ã¯ãªãã¡ã¢ãªæ¶è²»ãæããããLightGBMã§ãã£ãŠã¿ãã è§£æ³ãã·ã³ãã«ãã€ãLightGBMã§äžäœã®ã¹ã³ã¢ãè§£æ³ãå
¬éããŠãã ã«ãŒãã« ãããã«èŠã€ãã£ã å
¬éè§£æ³ã®åçŸ https://www.kaggle.com/xiaozhouwang/2nd-place-lightgbm-solution äžèšããã®ãŸãŸã³ããããŠäžåsubmitããŸãã Python2察å¿ã®ãããªã®ã§ãäžèšã®ããã«Python3ã§åãããã«ä¿®æ£ããŸããã # part of 2nd place solution: lightgbm model with private score 0.29124 and public lb score 0.28555 import lightgbm as lgbm from scipy import sparse as ssp from sklearn.model_selection import StratifiedKFold import numpy as np import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder def Gini (y_true, y_pred): # check and get number of samples assert y_true.shape == y_pred.shape n_samples = y_true.shape[ 0 ] # sort rows on prediction column # (from largest to smallest) arr = np.array([y_true, y_pred]).transpose() true_order = arr[arr[:, 0 ].argsort()][::- 1 , 0 ] pred_order = arr[arr[:, 1 ].argsort()][::- 1 , 0 ] # get Lorenz curves L_true = np.cumsum(true_order) * 1. / np.sum(true_order) L_pred = np.cumsum(pred_order) * 1. / np.sum(pred_order) L_ones = np.linspace( 1 / n_samples, 1 , n_samples) # get Gini coefficients (area between curves) G_true = np.sum(L_ones - L_true) G_pred = np.sum(L_ones - L_pred) # normalize to true Gini coefficient return G_pred * 1. / G_true cv_only = True save_cv = True full_train = False def evalerror (preds, dtrain): labels = dtrain.get_label() return 'gini' , Gini(labels, preds), True path = "input/" train = pd.read_csv(path+ 'train.csv' ) train_label = train[ 'target' ] train_id = train[ 'id' ] test = pd.read_csv(path+ 'test.csv' ) test_id = test[ 'id' ] NFOLDS = 5 kfold = StratifiedKFold(n_splits=NFOLDS, shuffle= True , random_state= 218 ) y = train[ 'target' ].values drop_feature = [ 'id' , 'target' ] X = train.drop(drop_feature,axis= 1 ) feature_names = X.columns.tolist() cat_features = [c for c in feature_names if ( 'cat' in c and 'count' not in c)] num_features = [c for c in feature_names if ( 'cat' not in c and 'calc' not in c)] train[ 'missing' ] = (train==- 1 ).sum(axis= 1 ).astype( float ) test[ 'missing' ] = (test==- 1 ).sum(axis= 1 ).astype( float ) num_features.append( 'missing' ) for c in cat_features: le = LabelEncoder() le.fit(train[c]) train[c] = le.transform(train[c]) test[c] = le.transform(test[c]) enc = OneHotEncoder(categories= 'auto' ) enc.fit(train[cat_features]) X_cat = enc.transform(train[cat_features]) X_t_cat = enc.transform(test[cat_features]) ind_features = [c for c in feature_names if 'ind' in c] count= 0 for c in ind_features: if count== 0 : train[ 'new_ind' ] = train[c].astype( str )+ '_' test[ 'new_ind' ] = test[c].astype( str )+ '_' count+= 1 else : train[ 'new_ind' ] += train[c].astype( str )+ '_' test[ 'new_ind' ] += test[c].astype( str )+ '_' cat_count_features = [] for c in cat_features+[ 'new_ind' ]: d = pd.concat([train[c],test[c]]).value_counts().to_dict() train[ '%s_count' %c] = train[c].apply( lambda x:d.get(x, 0 )) test[ '%s_count' %c] = test[c].apply( lambda x:d.get(x, 0 )) cat_count_features.append( '%s_count' %c) train_list = [train[num_features+cat_count_features].values,X_cat,] test_list = [test[num_features+cat_count_features].values,X_t_cat,] X = ssp.hstack(train_list).tocsr() X_test = ssp.hstack(test_list).tocsr() learning_rate = 0.1 num_leaves = 15 min_data_in_leaf = 2000 feature_fraction = 0.6 num_boost_round = 10000 params = { "objective" : "binary" , "boosting_type" : "gbdt" , "learning_rate" : learning_rate, "num_leaves" : num_leaves, "max_bin" : 256 , "feature_fraction" : feature_fraction, "verbosity" : 0 , "drop_rate" : 0.1 , "is_unbalance" : False , "max_drop" : 50 , "min_child_samples" : 10 , "min_child_weight" : 150 , "min_split_gain" : 0 , "subsample" : 0.9 } x_score = [] final_cv_train = np.zeros( len (train_label)) final_cv_pred = np.zeros( len (test_id)) for s in range ( 16 ): cv_train = np.zeros( len (train_label)) cv_pred = np.zeros( len (test_id)) params[ 'seed' ] = s if cv_only: kf = kfold.split(X, train_label) best_trees = [] fold_scores = [] for i, (train_fold, validate) in enumerate (kf): X_train, X_validate, label_train, label_validate = \ X[train_fold, :], X[validate, :], train_label[train_fold], train_label[validate] dtrain = lgbm.Dataset(X_train, label_train) dvalid = lgbm.Dataset(X_validate, label_validate, reference=dtrain) bst = lgbm.train(params, dtrain, num_boost_round, valid_sets=dvalid, feval=evalerror, verbose_eval= 100 , early_stopping_rounds= 100 , ) best_trees.append(bst.best_iteration) cv_pred += bst.predict(X_test, num_iteration=bst.best_iteration) cv_train[validate] += bst.predict(X_validate) score = Gini(label_validate, cv_train[validate]) print (score) fold_scores.append(score) cv_pred /= NFOLDS final_cv_train += cv_train final_cv_pred += cv_pred print ( "cv score:" ) print (Gini(train_label, cv_train)) print ( "current score:" , Gini(train_label, final_cv_train / (s + 1. )), s+ 1 ) print (fold_scores) print (best_trees, np.mean(best_trees)) x_score.append(Gini(train_label, cv_train)) print (x_score) pd.DataFrame({ 'id' : test_id, 'target' : final_cv_pred / 16. }).to_csv( 'model/lgbm3_pred_avg.csv' , index= False ) pd.DataFrame({ 'id' : train_id, 'target' : final_cv_train / 16. }).to_csv( 'model/lgbm3_cv_avg.csv' , index= False ) å
¬éè§£æ³ã§ã®submit Private Scoreã§0.29097ã5169ããŒã äž46äœã®ã¹ã³ã¢ãšãªããã·ã«ããŒã¡ãã«åå
ã«å
¥ããŸããã ã³ã³ãã¯çµäºããŠããã®ã§ããã¡ããã¹ã³ã¢ããŒãã®æ¬äœã¯æŽæ°ã¯ãããŸããã ãªããå®éã®ã³ã³ãã§ã¯ã ã«ãŒãã« ã®èæžããä»ã®Neral Networkã§ã®äºæž¬å€ã®å¹³åãšèšèŒãããã®ã§ã2äœã®submitã®åçŸãšããããã«ãªããªãããã§ãã ãããããã®ãããªã·ã³ãã«ãªæ¹æ³ã§ã·ã«ããŒã¡ãã«ã®ã¹ã³ã¢ãåããã®ã¯ãå人çã«ãKaggleã«ç©æ¥µããŠã¿ãããšããå±ã¿ã«ãªã£ããšæããŠããŸãã ãã€ããŒãã©ã¡ãŒã¿ã®ãã¥ãŒãã³ã° ããŠããã€ããŒãã©ã¡ãŒã¿ã®ãã¥ãŒãã³ã°ã ãã¬ãŒã ã¯ãŒã¯ ã®åãåããŠãããŒãã«ããã£ãšäžããããšãããæ¬é¡ã«ç§»ããŸãã ä»ã®Kaggleã®ã³ã³ãããStack over flowã§éã«èª¿æ»ãããã©ã¡ãŒã¿ã®ç¯å²ã決ããŸããã ããããŠã§ããä¿®æ£ãã ãœãŒã¹ã³ãŒã ãã以äžã®ããã«ãªããŸãã import lightgbm as lgbm import optuna from scipy import sparse as ssp from sklearn.model_selection import StratifiedKFold import numpy as np import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder def Gini (y_true, y_pred): # check and get number of samples assert y_true.shape == y_pred.shape n_samples = y_true.shape[ 0 ] # sort rows on prediction column # (from largest to smallest) arr = np.array([y_true, y_pred]).transpose() true_order = arr[arr[:, 0 ].argsort()][::- 1 , 0 ] pred_order = arr[arr[:, 1 ].argsort()][::- 1 , 0 ] # get Lorenz curves L_true = np.cumsum(true_order) * 1. / np.sum(true_order) L_pred = np.cumsum(pred_order) * 1. / np.sum(pred_order) L_ones = np.linspace( 1 / n_samples, 1 , n_samples) # get Gini coefficients (area between curves) G_true = np.sum(L_ones - L_true) G_pred = np.sum(L_ones - L_pred) # normalize to true Gini coefficient return G_pred * 1. / G_true cv_only = True save_cv = True full_train = False def evalerror (preds, dtrain): labels = dtrain.get_label() return 'gini' , Gini(labels, preds), True path = "input/" train = pd.read_csv(path+ 'train.csv' ) #train = train.sample(frac=0.1, random_state=0).reset_index(drop=True) train_label = train[ 'target' ] train_id = train[ 'id' ] test = pd.read_csv(path+ 'test.csv' ) #test = test.sample(frac=0.1, random_state=0).reset_index(drop=True) test_id = test[ 'id' ] NFOLDS = 4 kfold = StratifiedKFold(n_splits=NFOLDS, shuffle= True , random_state= 218 ) y = train[ 'target' ].values drop_feature = [ 'id' , 'target' ] X = train.drop(drop_feature,axis= 1 ) feature_names = X.columns.tolist() cat_features = [c for c in feature_names if ( 'cat' in c and 'count' not in c)] num_features = [c for c in feature_names if ( 'cat' not in c and 'calc' not in c)] train[ 'missing' ] = (train==- 1 ).sum(axis= 1 ).astype( float ) test[ 'missing' ] = (test==- 1 ).sum(axis= 1 ).astype( float ) num_features.append( 'missing' ) train.shape for c in cat_features: le = LabelEncoder() le.fit(train[c]) train[c] = le.transform(train[c]) test[c] = le.transform(test[c]) # äºåã«labelEncoderãè¡ã£ãŠããããããã®äœ¿ãæ¹ã§ãŠããŒã¯ãªå€ã§å²ãåœãŠããããåŒæ°categories = 'auto'ã§èŠåãæ¶ã enc = OneHotEncoder(categories= 'auto' ) enc.fit(train[cat_features]) X_cat = enc.transform(train[cat_features]) X_t_cat = enc.transform(test[cat_features]) ind_features = [c for c in feature_names if 'ind' in c] count= 0 for c in ind_features: if count == 0 : train[ 'new_ind' ] = train[c].astype( str )+ '_' test[ 'new_ind' ] = test[c].astype( str )+ '_' count += 1 else : train[ 'new_ind' ] += train[c].astype( str )+ '_' test[ 'new_ind' ] += test[c].astype( str )+ '_' cat_count_features = [] for c in cat_features+[ 'new_ind' ]: d = pd.concat([train[c],test[c]]).value_counts().to_dict() train[ '%s_count' %c] = train[c].apply( lambda x:d.get(x, 0 )) test[ '%s_count' %c] = test[c].apply( lambda x:d.get(x, 0 )) cat_count_features.append( '%s_count' %c) train_list = [train[num_features+cat_count_features].values, X_cat] test_list = [test[num_features+cat_count_features].values, X_t_cat] X = ssp.hstack(train_list).tocsr() X_test = ssp.hstack(test_list).tocsr() def objective (trial): drop_rate = trial.suggest_uniform( 'drop_rate' , 0 , 1.0 ) feature_fraction = trial.suggest_uniform( 'feature_fraction' , 0 , 1.0 ) learning_rate = trial.suggest_uniform( 'learning_rate' , 0 , 1.0 ) subsample = trial.suggest_uniform( 'subsample' , 0.8 , 1.0 ) num_leaves = trial.suggest_int( 'num_leaves' , 5 , 1000 ) verbosity = trial.suggest_int( 'verbosity' , - 1 , 1 ) num_boost_round = trial.suggest_int( 'num_boost_round' , 10 , 100000 ) min_data_in_leaf = trial.suggest_int( 'min_data_in_leaf' , 10 , 100000 ) min_child_samples = trial.suggest_int( 'min_child_samples' , 5 , 500 ) min_child_weight = trial.suggest_int( 'min_child_weight' , 5 , 500 ) params = { "objective" : "binary" , "boosting_type" : "gbdt" , "learning_rate" : learning_rate, "num_leaves" : num_leaves, "max_bin" : 256 , "feature_fraction" : feature_fraction, "verbosity" : verbosity, "drop_rate" : drop_rate, "is_unbalance" : False , "max_drop" : 50 , "min_child_samples" : min_child_samples, "min_child_weight" : min_child_weight, "min_split_gain" : 0 , "min_data_in_leaf" : min_data_in_leaf, "subsample" : subsample } x_score = [] final_cv_train = np.zeros( len (train_label)) final_cv_pred = np.zeros( len (test_id)) cv_train = np.zeros( len (train_label)) cv_pred = np.zeros( len (test_id)) params[ 'seed' ] = 0 kf = kfold.split(X, train_label) best_trees = [] fold_scores = [] for i, (train_fold, validate) in enumerate (kf): print ( 'kfold_index:' , i) X_train, X_validate, label_train, label_validate = \ X[train_fold, :], X[validate, :], train_label[train_fold], train_label[validate] dtrain = lgbm.Dataset(X_train, label_train) dvalid = lgbm.Dataset(X_validate, label_validate, reference=dtrain) bst = lgbm.train(params, dtrain, num_boost_round, valid_sets=dvalid, feval=evalerror, verbose_eval= 100 , early_stopping_rounds= 100 ) best_trees.append(bst.best_iteration) cv_pred += bst.predict(X_test, num_iteration=bst.best_iteration) cv_train[validate] += bst.predict(X_validate) score = Gini(label_validate, cv_train[validate]) print (score) fold_scores.append(score) cv_pred /= NFOLDS final_cv_train += cv_train final_cv_pred += cv_pred print ( "cv score:" ) print (Gini(train_label, cv_train)) print ( "current score:" , Gini(train_label, final_cv_train / (s + 1. )), s+ 1 ) print (fold_scores) print (best_trees, np.mean(best_trees)) x_score.append(Gini(train_label, cv_train)) print (x_score) pd.DataFrame({ 'id' : test_id, 'target' : final_cv_pred / 16. }).to_csv( 'model/lgbm3_pred_avg_2.csv' , index= False ) pd.DataFrame({ 'id' : train_id, 'target' : final_cv_train / 16. }).to_csv( 'model/lgbm3_cv_avg_2.csv' , index= False ) return ( 1 - x_score[ 0 ]) study = optuna.create_study() study.optimize(objective, n_trials= 150 ) ãã©ã¡ãŒã¿ã®èšå®ã®ç¯å²ãæç²ãããšä»¥äžã®ããã«ãªããŸãã drop_rate = trial.suggest_uniform( 'drop_rate' , 0 , 1.0 ) feature_fraction = trial.suggest_uniform( 'feature_fraction' , 0 , 1.0 ) learning_rate = trial.suggest_uniform( 'learning_rate' , 0 , 1.0 ) subsample = trial.suggest_uniform( 'subsample' , 0.8 , 1.0 ) num_leaves = trial.suggest_int( 'num_leaves' , 5 , 1000 ) verbosity = trial.suggest_int( 'verbosity' , - 1 , 1 ) num_boost_round = trial.suggest_int( 'num_boost_round' , 10 , 100000 ) min_data_in_leaf = trial.suggest_int( 'min_data_in_leaf' , 10 , 100000 ) min_child_samples = trial.suggest_int( 'min_child_samples' , 5 , 500 ) min_child_weight = trial.suggest_int( 'min_child_weight' , 5 , 500 ) ãªããOptunaèªäœã®äœ¿ç𿹿³ã¯ãäžèšã®èšäºãšå
¬åŒãªãã¡ã¬ã³ã¹ãåèãããŠããã ãããã https://qiita.com/ryota717/items/28e2167ea69bee7e250d https://optuna.readthedocs.io/en/stable/index.html ïŒ18/12/11 19:41远èšïŒ ã³ã¡ã³ãããã ããéãã'verbosity'ã¯ãèŠåã¬ãã«ã®è¡šç€ºãå¶åŸ¡ãããã©ã¡ãŒã¿ã§ãããäºæž¬æ§èœã®æé©åãšããŠã¯æå³ã®ç¡ããã©ã¡ãŒã¿ã§ãããã§ãã®ã§ããã¥ãŒãã³ã°ã®å¯Ÿè±¡ã«ã¯ãã¹ãã§ã¯ãããŸããã§ããã 以äžã®ããã«è©Šè¡åæ°ãå®ããŠããŸããã n_trials= 150 æéãè¶³ããªããªã£ãé¢ä¿ã§ããã®æç¹ã§èšç®ããããã©ã¡ãŒã¿ã§æé©åãäžæããŠãããŸãã 20æéã»ã©åãåããŸãããããã€ããŒãã©ã¡ãŒã¿ã«ãã£ãŠæ€èšŒã®æéã¯1åãã60åçšåºŠãšãªãã 100åãããã®è©Šè¡æ°ã ã£ãããã§ãã ããããŠã§ããŠãã©ã¡ãŒã¿ãã以äžã®ããã«ã2äœã®è§£æ³ãšæ¯èŒãããšä»¥äžã®ããã«ãªããŸãã ãã€ããŒãã©ã¡ãŒã¿ ä»åã®ãã¥ãŒãã³ã°çµæ 2äœã®è§£æ³ drop _rate 0.3015600134599976 0.1 feature_fraction 0.46650703511665226 0.6 learning_rate 0.004772377676601769 0.1 subsample 0.8080720420805803 0.9 num_leaves 718 15 verbosity -1 0 num_boost_round 1942 10000 min_data_in_ leaf 212 150 min_child_samples 68 10 min_child_weight 151 150 2äœã³ã³ããšã®è§£æ³ãšã¯ãé°å²æ°ãç°ãªãã»ãããšãªããå
¬éè§£æ³ã®åçŸãšããããšã«ã¯ãªããªãããã§ãã K_fold=4ãã§ãã£ãŠããããšãç°ãªãèŠå ã«ãªããšæããŸãã ç®åºã§ãããã€ããŒãã©ã¡ãŒã¿ã§submit æåã®python3ã® ã¹ã¯ãªãã ãããã©ã¡ãŒã¿ãå
¥ãæ¿ããäºæž¬å€ãç®åºããŸããã K_fold =4, ãŸããã©ã³ãã ã·ãŒãã®æ°ã16ãã4ã«æžãããŠãããŸãã çµæ ã¹ã³ã¢ã¯äžãã£ãŠãŸãã 1176äœçžåœãããã€ããŒãã©ã¡ãŒã¿æ¬¡ç¬¬ã§ã·ã«ããŒã¡ãã«åå
ãšããããšãèãããšã埮åŠãªçµæã§ãã ææ çµæãšããŠã¯æ®å¿µã§ãããgrid searchã ãã«é Œããªãããã€ããŒãã©ã¡ãŒã¿ã®æé©åæ¹æ³ã®å°å
¥ã®ãã£ããã«ãªããŸããã ãŸããéåžžã«æè»œã«äœ¿ãããšããã®ããããä»åŸããã¥ãŒãã³ã°ã®å Žé¢ã§OptunaãæŽ»çšããŠã¿ãããšæããŸãã åçãšããŠã¯ãæ¢çŽ¢ãããã€ããŒãã©ã¡ãŒã¿ã®èšå®ãæªããèšç®ã®å¹çåãèããæªããªã£ãæãããããŸãã validationã®éã«ãfoldæ°ã®å
šãŠèšç®ããã®ã§ã¯ãªããã¹ã³ã¢ãäžãããªãããªãããã®ãã€ããŒãã©ã¡ãŒã¿ã®èšç®ãããããšããäžå®æé以äžããã£ãŠããŸã£ãããŸããæ¬¡ã«è©Šè¡ã«ç§»ããšãã§ããã°å¹çåã§ããããã«æããŸãã ãã¬ãŒã ã¯ãŒã¯ 㯠ãã©ãã¯ããã¯ã¹ ã§ãããçšåºŠã¯åããããšãã§ããŸããããã¯ãäžèº«ãããçšåºŠçè§£ããªããšé åãããŠããŸããšããã®ã¯ãåœç¶ã®çµæãšèšããŸãã ãã£ãšäœ¿ãããªãããã粟é²ããªããã°ãšæããŸããã å
¬åŒãªãã¡ã¬ã³ã¹ã§ããOptunaã§LightGBMããã¥ãŒãã³ã°ããäŸãåºãŠããããã¡ãã®äŸãåèã«ããªãããªãã³ãžããããšæããŸãã github.com æåŸã«ã§ããããã®èšäºãäœãã®åœ¹ã«çµãŠã°å¹žãã§ãã