library("parallelMap") library("ParamHelpers") library("mlr") library("mlrCPO") library("mlrMBO") library("mosmafs") library("mobafeas") library("magrittr") library("ggplot2") set.seed(8008135) options(width = 80) data.table::setDTthreads(1) cores <- parallel::detectCores() if (Sys.getenv("FASTVIGNETTE") != "true") { cores <- min(cores, 2) } if (.Platform$OS.type == "windows") { parallelStartSocket(cores, show.info = FALSE) } else { parallelStartMulticore(cores, show.info = FALSE, mc.set.seed = FALSE) } print.list <- function(x) { if (all(vlapply(x, is.atomic))) { x <- sapply(x, function(x) if (is.numeric(x)) round(x, 3) else x) catf("list(%s)", collapse(sprintf("%s = %s", names(x), vcapply(x, deparse, width.cutoff = 500, nlines = 1)), ", ")) } else { NextMethod(x) } } knitr::opts_chunk$set( cache = FALSE, collapse = TRUE, comment = "#>" )
MoBaFeaS uses Model Based Optimization (as supplied by the mlrMBO
package) to perform (wrapper-based) feature selection, either with or without simultaneous hyperparameter tuning, and with or without treating the problem as a multi-objective optimization problem.
First some related packages need to be loaded.
library("mlrMBO") library("magrittr") library("ggplot2") library("ParamHelpers") library("mlr") library("mlrCPO") library("mosmafs") library("mobafeas")
At first, an objective function needs to be defined. It is easiest to use the makeMobafeasObjective()
function for this. It takes the base learner and the task to be evaluated, and also makes it possible to perform simultaneous evaluation on a holdout dataset (that the underlying optimizer does not regard for optimization). The multi.objective
option can be either TRUE
(performing multi-objective optimization, one objective being the proportion of features being used), FALSE
(performing single-objective optimization on performance alone), or a function. If multi.objective
is a function, it is called after each evaluation with the performance value, and the feature fraction value, and its return-value is (single-objective) minimized.
It is possible to specify a parameter set to be optimized, in which case hyperparameters and selected features are tuned jointly.
lrn <- makeLearner("classif.knn", k = 5) tsk <- pid.task # single-objective objective.so <- makeMobafeasObjective(lrn, tsk, resampling = cv10, multi.objective = FALSE) # multi-objective objective.mo <- makeMobafeasObjective(lrn, tsk, resampling = cv10, multi.objective = TRUE) # scalarized: sum of misclassification rate and feature fraction objective.sc <- makeMobafeasObjective(lrn, tsk, resampling = cv10, multi.objective = function(perf, featfrac) perf + featfrac) # joint tuning of hyperparameter 'k' and feature selection objective.joint.mo <- makeMobafeasObjective(lrn, tsk, resampling = cv10, ps = pSS(k: numeric[0, 1] [[trafo = function(x) round(50 ^ x)]]), multi.objective = TRUE)
Because MoBaFeaS uses model-based optimization, it is necessary to specify the infill optimization method. The mosmafs
package's mixed integer evolutionary strategy capability is used here. One needs to specify the mutation, recombination, selection and survival operators and other mosmafs settings in a mosmafs configuration object. The mutation and recombination operators should be created using mosmafs::combine.operators()
; the selector.selection
mutation operation should usually involve something hamming-weight preserving.
Note that the parameter set on which the operators are defined is the parameter set of the objective, not the parameter set of the learner given to makeMobafeasObjective
. This is because makeMobafeasObjective
adds the selector.selection
parameter.
In the following example, all parameter sets for objective.*
happen to be identical save the objective.joint.mo
one (because it includes the k
parameter). Therefore, our examples only need two mosmafs configuration objects
ps.obj <- getParamSet(objective.so) ps.obj
ps.joint.obj <- getParamSet(objective.joint.mo) ps.joint.obj
mutator.simple <- combine.operators(ps.obj, numeric = ecr::setup(mutGaussScaled, sdev = 0.1), integer = ecr::setup(mutGaussIntScaled, sdev = 0.1), selector.selection = mutBitflipCHW) crossover.simple <- combine.operators(ps.obj, numeric = recPCrossover, integer = recPCrossover, selector.selection = recPCrossover) mosmafs.config <- MosmafsConfig(mutator.simple, crossover.simple, 10)
mutator.joint.simple <- combine.operators(ps.joint.obj, numeric = ecr::setup(mutGaussScaled, sdev = 0.1), integer = ecr::setup(mutGaussIntScaled, sdev = 0.1), selector.selection = mutBitflipCHW) crossover.joint.simple <- combine.operators(ps.joint.obj, numeric = recPCrossover, integer = recPCrossover, selector.selection = recPCrossover) mosmafs.joint.config <- MosmafsConfig(mutator.joint.simple, crossover.joint.simple, 10)
The surrogate model being optimized can, in prinziple, be any mlr
Learner
that performs "se"
prediction (i.e. predicts a value and its estimated uncertainty). It is recommended to use either the randomForest
model (recommended settings are conveniently provided by the constructRFSurrogate()
function), or a Gaussian Process. constructMBFLearner()
constructs a Gaussian Process Learner (from the kergp
package; note it is highly recommended to use the modified version provided by the MoBaFeaS package authors) with a special kernel for the feature selection part. Use the kernelMBF*()
functions to define this kernel. The hyperparameter kernel can be a Matern 5/2, Matern 3/2, Exponential or Gaussian kernel. Our Example uses the Hamming distance kernel for the feature configuration vector and the (default) Matern 3/2 kernel for the hyperparameters.
surrogate <- constructMBFLearner(ps.obj, kernelMBFHamming()) surrogate.joint <- constructMBFLearner(ps.joint.obj, kernelMBFHamming())
At last the MBO run itself needs to be configured. It is possible to choose different infill criteria (available are Expected Improvement InfillEI()
(the default), Confidence Bound InfillCB()
, and Response InfillResponse()
). In the future it may be possible to change the number of points being proposed simultaneously.
The termination criterion can be set using the mlrMBO::setMBOControlTermination()
function. This vignette performs relatively short runs; To get adequate performance for large datasets, it may be necessary to perform hundreds or even thousands of evaluations.
ctrl <- makeMBFControl(mosmafs.config) %>% setMBOControlTermination(10) ctrl.joint <- makeMBFControl(mosmafs.joint.config) %>% setMBOControlTermination(10)
The initial design matrix for which to evaluate individuals is given to mobafeasMBO()
in form of a list of individuals. These individuals can be generated the same way as in mosmafs
: Using the ParamHelpers::sampleValues()
function, aided by the mosmafs::initSelector()
function.
initials <- sampleValues(ps.obj, 10, discrete.names = TRUE) %>% initSelector() initials.joint <- sampleValues(ps.joint.obj, 10, discrete.names = TRUE) %>% initSelector()
Optimization runs are started using the mobafeasMBO()
call.
opt.so <- mobafeasMBO(objective.so, initials, surrogate, ctrl, show.info = FALSE)
opt.mo <- mobafeasMBO(objective.mo, initials, surrogate, ctrl, show.info = FALSE)
opt.sc <- mobafeasMBO(objective.sc, initials, surrogate, ctrl, show.info = FALSE)
opt.joint.mo <- mobafeasMBO(objective.joint.mo, initials.joint, surrogate.joint, ctrl.joint, show.info = FALSE)
Use the collectMBFResult()
function to collect results.
collectMBFResult(opt.so)
collectMBFResult(opt.mo)
collectMBFResult(opt.sc)
collectMBFResult(opt.joint.mo)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.