---
title: "AjusteHiperRF"
author: "Ricardo Aler"
date: "1/8/2019"
output:
  pdf_document: default
  html_notebook: default
  word_document: default
---

## Ajuste hiper-par�metros Random Forests

```{r}
library(mlr)
library(mlbench)
library(mlrHyperopt) # Esta librer�a ayuda en la obtenci�n de hiper-par�metros 
library(ggplot2)     # Librer�a para hacer plots
library(mlrMBO)      # Para hacer ajuste de hiper-par�metros con model-based optimization

# Esto es para que no haya demasiados mensajes de informaci�n en el documento
configureMlr(show.info = FALSE, on.learner.warning = "quiet")

data(BostonHousing)
# Primero, definimos una tarea de regresi�n, cuyos datos est�n contenidos en el data.frame BostonHousing y cuyo target es "mdev"
task_bh <- makeRegrTask(data= BostonHousing, target="medv")

# Segundo, definimos el nombre delgoritmo de aprendizaje ("learner"). Hay muchos de random forests, probemos ranger.
learner_name <- "regr.ranger"

# Tercero, vemos que hiper-par�metros ajustables tiene
filterParams(getParamSet(learner_name), tunable = TRUE)

# Nuestro learner inicial va a ser una SVM con kernel gausiano (radial). Lo definimos as�:

learner_rf <- makeLearner(learner_name)


```

Vemos que tiene muchos hiper-par�metros, pero sabemos que los m�s importantes son el n�mero de �rboles **num.trees** y el **mtry**. Tambi�n **min.nodesize**,  ue controla la profundidad, aunque de manera indirecta. M�s informaci�n aqu� [https://www.rdocumentation.org/packages/ranger/versions/0.10.1]. M�s adelante tomaremos una decisi�n acerca de cuales ajustar.

Es interesante saber que podemos conseguir ayuda sobre el m�todo y de sus par�metros as�:

```{r, eval=FALSE}
helpLearner(learner_name)
helpLearnerParam(learner_name, "num.random.splits")
```


```{r}

# Ahora habr�a que definir un espacio de b�squeda para los hiper-par�metros
# Una opci�n es ir a la p�gina web # http://mlrhyperopt.jakob-r.de/parconfigs

# Una segunda opci�n es descargar directamente el espacio de b�squeda desde la web as�:

pc = downloadParConfigs(learner.name=learner_name) # http://mlrhyperopt.jakob-r.de/parconfigs
print(pc)
if(length(pc)>0) {
  ps = getParConfigParSet(pc[[1]], task=task_bh)
  print(ps)
}

# Desgraciadamente, en este caso no hay ninguno exactamente para ese learner
# Tendr�amos que ir a la p�gina web http://mlrhyperopt.jakob-r.de/parconfigs
# y buscarlo a mano, para randomForest.

```

En este caso, usaremos una tercera opci�n, m�s sencilla: la funci�n **generateParConfig** nos da un espacio de b�squeda b�sico para un learner concreto.

```{r}
pc_rf <- generateParConfig(learner=learner_name, task=task_bh)
ps_rf <- getParConfigParSet(pc_rf, task=task_bh)
print(ps_rf)
```

Vemos que aparecen s�lo 2 hiper-par�metro. La raz�n de que no aparezca **num.trees** es que su valor por defecto es de 500 y el autor ha considerado que son m�s que suficientes. Tambi�n hay que recordar RandomForest no entra en sobreaprendizaje por m�s �rboles que introduzcamos en el ensemble (a diferencia de Boosting). 

Ahora definimos c�mo se b�sca en el espacio de hiper-par�metros (con **randomsearch** en este caso y un **budget** de 50 evaluaciones), y c�mo se eval�an los distintos hiper-par�metros (validaci�n cruzada de tres folds).

```{r}
# Ojo, he puesto 1000 evaluaciones para hacer una buena visualizaci�n despu�s, pero puede tardar mucho. 
# Posiblemente con budget = 50 sea suficiente

control_grid <- makeTuneControlRandom(budget=1000)
inner_desc <- makeResampleDesc("CV", iter=3)

# Aqu� construimos una secuencia de ajuste seguida de construcci�n de modelo
learner_tune_rf <- makeTuneWrapper(learner_rf, resampling = inner_desc, par.set = ps_rf, control = control_grid, measures = list(rmse), show.info = FALSE)

# Tambi�n tenemos que definir c�mo se va a evaluar el modelo final. En este caso con train/test (holdout)
outer_desc <-  makeResampleDesc("Holdout")
set.seed(0)
outer_inst <- makeResampleInstance(outer_desc, task_bh)

# Por �ltimo, usamos resample para entrenar y evaluar learner_tune_rf
set.seed(0)
error_tune_rf <- resample(learner_tune_rf, task_bh, outer_inst, measures = list(rmse), extract = getTuneResult)
```

Vemos que los hiper-par�metros que se eligieron en la partici�n de entrenamiento son los siguientes (tambi�n se muestra el error que producen dichos hiper-par�metros en la validaci�n cruzada de 3 folds que se us� para seleccionarlos).

```{r}
error_tune_rf$extract
```

y el error del modelo final es:

```{r}
error_tune_rf$aggr
```


```{r}

data <- generateHyperParsEffectData(error_tune_rf, include.diagnostics = FALSE, trafo=TRUE)

# Las siguientes dos l�neas son para solventar un bug de MLR
names(data$data)[names(data$data)=="rmse.test.rmse"] <- "rmse.test.mean"
data$measures[data$measures=="rmse.test.rmse"] <- "rmse.test.mean"
```


Aqu� vemos el espacio de b�squeda que hemos explorado:

```{r}
plt = plotHyperParsEffect(data, x = "mtry", y = "min.node.size", z = "rmse.test.mean",
  plot.type = "contour", interpolate = "regr.earth", show.experiments = TRUE)

plot(plt + geom_point(x=error_tune_rf$extract[[1]]$x$mtry, y=error_tune_rf$extract[[1]]$x$min.node.size, size=5))
```


A continuaci�n podemos ver como se ha ido reduciendo el error con las iteraciones.

```{r}
plt = plotHyperParsEffect(data, x = "iteration", y = "rmse.test.mean",  plot.type = "line")
plt
```


## Vamos a hacer el ajuste de hiper-par�metros con Model Based Optimization, del p�quete **mbo**

```{r}

# Dedica 80 iteraciones al ajuste
control = makeMBOControl()
control = setMBOControlTermination(control, iters = 80)
control = setMBOControlInfill(control, crit = makeMBOInfillCritEI())

control_grid <-makeTuneControlMBO(mbo.control = control)

inner_desc <- makeResampleDesc("CV", iter=3)

# Aqu� construimos una secuencia de ajuste seguida de construcci�n de modelo
learner_tune_rf <- makeTuneWrapper(learner_rf, resampling = inner_desc, par.set = ps_rf, control = control_grid, measures = list(rmse))

# Tambi�n tenemos que definir c�mo se va a evaluar el modelo final. En este caso con train/test (holdout)
outer_desc <-  makeResampleDesc("Holdout")
set.seed(0)
outer_inst <- makeResampleInstance(outer_desc, task_bh)

# Por �ltimo, usamos resample para entrenar y evaluar learner_tune_rf
set.seed(0)
error_tune_rf <- resample(learner_tune_rf, task_bh, outer_inst, measures = list(rmse), extract = getTuneResult )
```

Vemos que los hiper-par�metros que se eligieron en la partici�n de entrenamiento son los siguientes (tambi�n se muestra el error que producen dichos hiper-par�metros en la validaci�n cruzada de 3 folds que se us� para seleccionarlos).

```{r}
error_tune_rf$extract
```

y el error del modelo final es muy parecido tambi�n al que obtuvimos con Random Search:

```{r}
error_tune_rf$aggr
```

Lo que si parece claro que este tipo de b�squeda se centra mucho mejor en los valores adecuados de n.trees y 
Por �ltimo, podemos ver un plot que nos muestra c�mo cambia el error con los distintos valores de los hiper-par�metros. 

```{r}
data <- generateHyperParsEffectData(error_tune_rf, include.diagnostics = FALSE)

# Las siguientes dos l�neas son para solventar un bug de MLR
names(data$data)[names(data$data)=="rmse.test.rmse"] <- "rmse.test.mean"
data$measures[data$measures=="rmse.test.rmse"] <- "rmse.test.mean"
```


Aqu� vemos el espacio de b�squeda que hemos explorado:

```{r}
plt = plotHyperParsEffect(data, x = "mtry", y = "min.node.size", z = "rmse.test.mean",
  plot.type = "contour", interpolate = "regr.earth", show.experiments = TRUE)

plot(plt + geom_point(x=error_tune_rf$extract[[1]]$x$mtry, y=error_tune_rf$extract[[1]]$x$min.node.size, size=5))
```


A continuaci�n podemos ver como se ha ido reduciendo el error con las iteraciones. Parece que con pocas iteraciones se alcanzan buenos m�nimos.

```{r}
plt = plotHyperParsEffect(data, x = "iteration", y = "rmse.test.mean",
  plot.type = "line")
plt
```


Por �ltimo, puede ser interesante saber qu� hubiera pasado de haber utilizado un n�mero de �rboles diferente a 500. Definimos un learner con los mejores hiper-par�metros encontrados hasta el momento y un nuevo espacio de b�squeda donde s�lo cambie **num.trees**. Ahora utilizaremos la b�squeda sistem�tica de grid-search para hacerlo.


```{r}

learner_rf <- setHyperPars(makeLearner(learner_name), par.vals = error_tune_rf$extract[[1]]$x)

ps_rf <- makeParamSet(
    makeDiscreteParam("num.trees", values = c(10, seq(50, 2000, by=100)))
)

control_grid <- makeTuneControlGrid()
inner_desc <- makeResampleDesc("CV", iter=3)

# Aqu� construimos una secuencia de ajuste seguida de construcci�n de modelo
# N�tese que inner_Desc, control_grid, outer_inst, etc no cambi�n, con lo que usamos los mismos que antes.
learner_tune_rf <- makeTuneWrapper(learner_rf, resampling = inner_desc, par.set = ps_rf, control = control_grid, measures = list(rmse))

# Por �ltimo, usamos resample para entrenar y evaluar learner_tune_rf
set.seed(0)
error_tune_rf <- resample(learner_tune_rf, task_bh, outer_inst, measures = list(rmse), extract = getTuneResult )




```

```{r}
data <- generateHyperParsEffectData(error_tune_rf, include.diagnostics = FALSE)

# Las siguientes dos l�neas son para solventar un bug de MLR
names(data$data)[names(data$data)=="rmse.test.rmse"] <- "rmse.test.mean"
data$measures[data$measures=="rmse.test.rmse"] <- "rmse.test.mean"
```


Aqu� vemos el espacio de b�squeda que hemos explorado. Aunque la dependencia del error respecto al n�mero de �rboles es algo ruidosa, parece que 500 �rboles era un valor razonable, aunque tal vez nos hubieramos podido beneficiar con algo mas de 500 �rboles.

```{r}
plt = plotHyperParsEffect(data, x = "num.trees", y = "rmse.test.mean",
  plot.type = "line", show.experiments = TRUE)

plot(plt + geom_point(x=error_tune_rf$extract[[1]]$x$num.trees, y=error_tune_rf$extract[[1]]$y, size=5))
```

```{r}
plt = plotHyperParsEffect(data, x = "iteration", y = "rmse.test.mean",
  plot.type = "line")
plt
```
```{r}

```
