Package: nested-cv
Nested-Cross-ValidationThis repository implements a general nested cross-validation function. Ready to use with ANY estimator that implements the Scikit-Learn estimator interface.Installing the pacakge:You can find the package onpypi* and install it via pip by using the following command:pipinstallnested-cvYou can also install it from the wheel file on theReleasespage.* we gradually push updates, pull this master from github if you want the absolute latest changes.UsageBe mindful of the options that are available for NestedCV. Some cross-validation options are defined in a dictionarycv_options.
This package is optimized for any estimator that implements a scikit-learn wrapper, e.g. XGBoost, LightGBM, KerasRegressor, KerasClassifier etc.Single algorithmHere is a single example using Random Forest#import the packagefromnested_cvimportNestedCV# Define a parameters gridparam_grid={'max_depth':[3,None],'n_estimators':[100,200,300,400,500,600,700,800,900,1000],'max_features':[50,100,150,200]# Note: You might not have that many features}# Define parameters for function# Default scoring: RMSEnested_CV_search=NestedCV(model=RandomForestRegressor(),params_grid=param_grid,outer_kfolds=5,inner_kfolds=5,cv_options={'sqrt_of_score':True,'randomized_search_iter':30})nested_CV_search.fit(X=X,y=y)grid_nested_cv.score_vs_variance_plot()print('\nCumulated best parameter grid was:\n{0}'.format(nested_CV_search.best_params))Multiple algorithmsHere is an example using Random Forest, XGBoost and LightGBM.models_to_run=[RandomForestRegressor(),xgb.XGBRegressor(),lgb.LGBMRegressor()]models_param_grid=[{# 1st param grid, corresponding to RandomForestRegressor'max_depth':[3,None],'n_estimators':[100,200,300,400,500,600,700,800,900,1000],'max_features':[50,100,150,200]},{# 2nd param grid, corresponding to XGBRegressor'learning_rate':[0.05],'colsample_bytree':np.linspace(0.3,0.5),'n_estimators':[100,200,300,400,500,600,700,800,900,1000],'reg_alpha':(1,1.2),'reg_lambda':(1,1.2,1.4)},{# 3rd param grid, corresponding to LGBMRegressor'learning_rate':[0.05],'n_estimators':[100,200,300,400,500,600,700,800,900,1000],'reg_alpha':(1,1.2),'reg_lambda':(1,1.2,1.4)}]fori,modelinenumerate(models_to_run):nested_CV_search=NestedCV(model=model,params_grid=models_param_grid[i],outer_kfolds=5,inner_kfolds=5,cv_options={'sqrt_of_score':True,'randomized_search_iter':30})nested_CV_search.fit(X=X,y=y)model_param_grid=nested_CV_search.best_paramsprint('\nCumulated best parameter grid was:\n{0}'.format(model_param_grid))NestedCV ParametersNametypedescriptionmodelestimatorThe estimator implements scikit-learn estimator interface.params_griddictionary "dict"The dict contains hyperparameters for model.outer_kfoldsintNumber of outer K-partitions in KFoldinner_kfoldsintNumber of inner K-partitions in KFoldcv_optionsdictionary "dict"Next sectionn_jobsintNumber of jobs for joblib to run (multiprocessing)cv_optionsvalue optionsmetric:Callable from sklearn.metrics, default = mean_squared_errorA scoring metric used to score each modelmetric_score_indicator_lower:boolean, default = TrueChoose whether lower score is better for the metric calculation or higher score is better.sqrt_of_score:boolean, default = FalseWhether or not if the square root should be taken of scorerandomized_search:boolean, default = TrueWhether to use gridsearch or randomizedsearch from sklearnrandomized_search_iter:int, default = 10Number of iterations for randomized searchrecursive_feature_elimination:boolean, default = FalseWhether to do feature eliminationpredict_proba:boolean, default = FalseIf true, predict probabilities instead for a class, instead of predicting a classmulticlass_average:string, default = 'binary'For some classification metrics with a multiclass prediction, you need to specify an
average other than 'binary'Returnsvariance:Model variance by numpy.var()outer_scores:A list of the outer scores, from the outer cross-validationbest_inner_score_list:A list of best inner scores for each outer loopbest_params:All best params from each inner loop cumulated in a dictbest_inner_params_list:Best inner params for each outer loop as an array of dictionariesHow to use the output?We suggest looking at the best hyperparameters together with the score for each outer loop. Look at how stable the model appears to be in a nested cross-validation setting. If the outer score changes a lot, then it might indicate instability in your model. In that case, start over with making a new model.After Nested Cross-Validation?If the results from nested cross-validation are stable: Run a normal cross-validation with the same procedure as in nested cross-validation, i.e. if you used feature selection in nested cross-validation, you should also do that in normal cross-validation.LimitationsXGBoostimplements aearly_stopping_rounds, which cannot be used in this implementation. Other similar parameters might not work in combination with this implementation. The function will have to be adopted to use special parameters like that.What did we learn?UsingScikit-Learnwill lead to a faster implementation, since the Scikit-Learn community has implemented many functions that does much of the work.We have learned and applied this package in our main project aboutHouse Price Prediction.Why use Nested Cross-Validation?Controlling the bias-variance tradeoff is an essential and important task in machine learning, indicated by[Cawley and Talbot, 2010]. Many articles indicate that this is possible by the use of nested cross-validation, one of them byVarma and Simon, 2006. Other interesting literature for nested cross-validation are[Varoquaox et al., 2017]and[Krstajic et al., 2014].