---
title: "Tutorial MLR"
author: "Ricardo Aler"
date: "Julio de 2019"
output:
  pdf_document: default
  html_document: default
---

## Introducci�n a mlR

**mlR** significa Machine Learning with R. Por un lado, simplifica el uso de las distintas librer�as de aprendizaje autom�tico (**C50**, **Cubist**, **part**, **knn**, etc.). Por otro, automatiza algunas tareas t�picas de aprendizaje autom�tico, tales como normalizaci�n, imputaci�n, ajuste de hiper-par�metros, etc. Primero, vamos a cargar unos datos de un problema de regresi�n que nos servir�n para hacer las pruebas. Los datos son **BostonHousing** (descritos aqu�: [http://www.cs.toronto.edu/~delve/data/boston/bostonDetail.html]), cuyo objetivo es predecir el precio de la vivienda (situado en la �ltima columna, **medv**) en funci�n del resto de atributos de entrada (crime per c�pita, habitaciones por vivienda, ...).

```{r}
# Primero activamos los datos Boston Housing
library(mlbench)
data(BostonHousing)
head(BostonHousing)
```

El esquema b�sico de uso de **mlR** es el siguiente:

1. Se crea una "tarea" (task). La tarea puede ser de clasificaci�n (**makeClassifTask**), de regresi�n (**makeRegrTask**), o de clustering (entre otras). Crear una tarea consiste en crear una variable que contendr� mis datos y el identificador de la variable **target**, o sea, la variable de respuesta o dependiente, en mi problema.

2. Se crea un "learner". Esto consiste en crear una variable que contiene qu� algoritmo de aprendizaje se va a usar, y sus hiper-par�metros, en caso de que queramos usar valores de hiper-par�metros distintos a los valores por omisi�n (o por defecto).

3. Normalmente, no entrenaremos el modelo con todos los datos de la "task", sino que guardaremos algunos para entrenar el modelo y otros para evaluarlo. Una manera de hacerlo es guardar en una variable (**train_indices**) los �ndices de los datos que usaremos para entrenar (o sea, los n�meros de fila de la matriz de datos). El resto de �ndices son los que usaremos para evaluar el modelo (**test_indices**).

4. Entrenamos (**train**) el "learner" con los datos contenidos en la "task", aunque no todos, sino s�lo aquellos cuyos �ndices est�n en **train_indices**.  Esto se indica con **subset**.

5. Hacemos predicciones del modelo para los datos de test con **predict**

6. Evaluamos el modelo con **performance**. Esto �ltimo me permite obtener el error sobre los datos de test.

```{r}
# Primero activamos los datos Boston Housing
library(mlr) # Activamos la librer�a mlR. Si no est� instalada, usad install.packages("mlR")

# 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 algoritmo de aprendizaje ("learner"). Vamos a usar los �rboles de regresi�n de la librer�a "rpart"
learner_rpart <- makeLearner("regr.rpart")
  
# Tercero, decidmos qu� datos se usan para train y cuales para test (holdout)
# n es el n�mero de l�neas del data.frame. Tambi�n valdr�a n <- nrow(BostonHousing)
n = getTaskSize(task_bh)      
train_indices <- sample(1:n, round(2/3*n), replace = FALSE)
# setdiff = diferencia de conjuntos. setdiff(c(1,2,3,4), c(2,4)) == c(1,3)
test_indices <- setdiff(1:n, train_indices) # para test ponemos el resto de los �ndices

# Cuarto, entrenamos el modelo con los datos de train de la tarea
model_rpart <- train(learner_rpart, task_bh, subset = train_indices)

# Quinto, hacemos predicciones del modelo para los datos de test
preds_bh_rpart <- predict(model_rpart, task_bh, subset = test_indices)
```

Aqu� debajo podemos ver las 10 primeras predicciones del modelo

```{r}
# Las predicciones se almacenan en $data$response. Aqu� podemos ver las 10  primeras
preds_bh_rpart$data$response[1:10]
```

y tambi�n est�n almacenados las salidas reales. Veamos las 10 primeras

```{r}
# Tambi�n podemos ver las salidas reales en $data$truth
preds_bh_rpart$data$truth[1:10]
```

Si las ponemos juntas (predicciones y valores reales), podemos ver si el modelo est� aproximando bien. El error es la diferencia entre la predicci�n y el valor real. **numDato** es el identificador del dato (o sea, su n�mero de final en el data.frame original).

```{r}
comparacion <- data.frame(
  numDato = preds_bh_rpart$data$id,
  predicciones = preds_bh_rpart$data$response, 
  reales = preds_bh_rpart$data$truth, 
  error = abs(preds_bh_rpart$data$response - preds_bh_rpart$data$truth))
head(comparacion)

```
En sexto y �ltimo lugar, podemos pedirle a **mlR** que nos calcule alguna medida de error del modelo, teniendo en cuenta todos los datos de test. Podemos hacerlo con **performance**. En concreto, vamos a pedir el root mean squared error (ra�z del error cuadr�tico medio) y el mean absolute error (error absoluto medio).

```{r}
performance(preds_bh_rpart, measures = list(rmse, mae))
```
A continuaci�n vemos el c�digo completo que hemos venido viendo. N�tese que usamos **set.seed(0)** en dos ocasiones. Lo que hace **set.seed** es fijar la semilla del generador de n�meros aleatorios. V�ase que se usa antes de **sample**, el cual genera una secuencia aleatoria de �ndices. Dado que es aleatoria, cada vez que ejecutemos **sample**, obtendremos una secuencia de �ndices distinta. Si queremos que siempre se genere la misma secuencia, tenemos que fijar la semilla al mismo n�mero (cero, en este caso). Veamos un ejemplo:

```{r}
"TRES SECUENCIAS DISTINTAS"
(primera <- sample(1:5, 3, replace = FALSE))
(segunda <- sample(1:5, 3, replace = FALSE))
(tercera <- sample(1:5, 3, replace = FALSE))

"TRES SECUENCIAS IGUALES"
set.seed(0)
(primera <- sample(1:5, 3, replace = FALSE))
set.seed(0)
(segunda <- sample(1:5, 3, replace = FALSE))
set.seed(0)
(tercera <- sample(1:5, 3, replace = FALSE))


```

Y ahora ya el c�digo completo. N�tese que tambi�n usamos **set.seed(0)** antes de **train**, debido a que la construcci�n de un �rbol tiene aspectos aleatorios (ej: si dos atributos tienen la misma entrop�a mientras se construye el �rbol, se elige uno de ellos aleatoriamente). Esto no es necesario hacerlo en principio, pero es conveniente si queremos que cada vez que ejecutemos nuestro c�digo obtengamos los mismos resultados (reproducibilidad de resultados).


```{r}
# Primero activamos los datos Boston Housing
library(mlbench)
data(BostonHousing)

# Primero activamos los datos Boston Housing
library(mlr) # Activamos la librer�a mlR. Si no est� instalada, usad install.packages("mlR")

# 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 algoritmo de aprendizaje ("learner"). Vamos a usar los �rboles de regresi�n de la librer�a "rpart"
learner_rpart <- makeLearner("regr.rpart")
  
# Tercero, decidmos qu� datos se usan para train y cuales para test
# n es el n�mero de l�neas del data.frame. Tambi�n valdr�a n <- nrow(BostonHousing)
n = getTaskSize(task_bh)
set.seed(0) 
train_indices <- sample(1:n, round(2/3*n), replace = FALSE)
# setdiff = diferencia de conjuntos. setdiff(c(1,2,3,4), c(2,4)) == c(1,3)
test_indices <- setdiff(1:n, train_indices)   

# Cuarto, entrenamos el modelo con los datos de train de la tarea
set.seed(0) # De esta manera, siempre se aprender� el mismo modelo
model_rpart <- train(learner_rpart, task_bh, subset = train_indices)

# Quinto, hacemos predicciones del modelo para los datos de test
preds_bh_rpart <- predict(model_rpart, task_bh, subset = test_indices)

# Sexto, calculamos el error sobre el conjunto de test
performance(preds_bh_rpart, measures = list(rmse, mae))
```

### Ejercicio de uso b�sico de mlr

Usando la estructura que hemos visto hasta ahora, escribir c�digo mlR para aplicar otras dos t�cnicas de aprendizaje en regresi�n: **Cubist** (�rboles de modelos) y **knn** (vecino m�s cercano para regresi�n). En **mlR** reciben el nombre de **regr.cubist** y **regr.kknn** (aunque existen m�s librer�as, como **regr.km** y **regr.fnn**, ambas de k-vecinos y regresi�n). En cualquier caso, ten�is la lista completa de m�todos de aprendizaje autom�tico integrados en **mlR** aqu�: [https://mlr-org.github.io/mlr/articles/tutorial/integrated_learners.html]. Tambi�n pod�is usar la funci�n **listLearners()** para ver todos los posibles m�todos y sus propiedades. Por ejemplo, **listLearners("regr")** muestra todos los m�todos disponibles para hacer regresi�n.

Nuestro objetivo es ver cual de los tres algoritmos: **regr.rpart**, **regr.cubist** o **regr.kknn** es el mejor en t�rminos de MAE (el que tiene el menor MAE en test). Pod�is hacer copy paste de c�digo de las secciones anteriores.

```{r}
```
## Visualizaci�n de modelos

En ocasiones puede ser interesante visualizar el modelo construido. En el caso de Cubist, el modelo se puede ver e incluso interpretar. Se trata de reglas **if-then**, con condiciones en la parte del **if** y modelos lineales en la parte del **then**. Para cada condici�n, hay un modelo lineal a usar.

```{r}
learner_cubist <-  makeLearner("regr.cubist")
model_cubist <-  train(learner_cubist, task_bh, subset = train_indices)
summary(getLearnerModel(model_cubist))
```

### Ejercicio de una tarea de clasificaci�n y visualizaci�n del modelo

Antes hemos trabajado con un problema de regresi�n. Ahora probaremos con el dominio de clasificaci�n que vimos en clase (�es un buen d�a para jugar al tenis?). Se pide:

- Descargar los "datos del tenis" de Aula Global. Es un fichero .txt
- Leerlo en un data.frame con **read.csv**
- Crear una tarea de clasificaci�n con **makeClassifTask**
- Construir CON TODOS LOS DATOS, un modelo de �rboles de decisi�n con el algoritmo **C5.0** (es una versi�n mejorada del C4.5, que es el que vimos en clase para el ejemplo del tenis.). Tendr�is que buscar en [https://mlr-org.github.io/mlr/articles/tutorial/integrated_learners.html] como se llama C5.0 en mlR
- Visualizar el �rbol de decisi�n que se obtiene

```{r}

```
## Preproceso simple en mlR

En clases anteriores vimos c�mo llevar a cabo ciertas transformaciones de los datos (preprocesado) usando **lapply**, entre ellas la normalizaci�n y la imputaci�n. Con **mlR** el pre-proceso es incluso m�s sencillo, y podemos hacerlo as�, antes de crear la tarea:

```{r}
# Ejemplo de imputaci�n de variables num�ricas (enteras y reales)
# (nota: en este caso no se produce ning�n efecto dado que BostonHousing no tiene NAs)
# consultar la documentaci�n para otros ejemplos: ?impute
# bh_imputado es una lista. bh_imputado$data es quien contiene realmente el data.frame imputado
bh_imputado <- impute(BostonHousing, classes = list(numeric = imputeMean()))

# "range" normaliza al rango 0-1. target idenfitica la varible de respuesta,
# que normalmente, no queremos normalizar
bh_normalizado <- normalizeFeatures(bh_imputado$data, method="range", target="medv")

# Por �ltimo, creamos la tarea con los datos transformados
task_bh <- makeRegrTask(data= bh_normalizado, target="medv")

```

## Evaluando modelos en mlR

En el apartado anterior vimos como evaluar los modelos sobre un conjunto de test, que estaba definido con una lista de �ndices. Sabemos que se puede evaluar un modelo de otras maneras. Por ejemplo, validaci�n cruzada (crossvalidation). **mlR** permite automatizar tambi�n el proceso de evaluaci�n con **makeResampleDesc** y **makeResampleInstance**. **makeResampleDesc** permite definir qu� tipo de evaluaci�n queremos (train/test o holdout, validaci�n cruzada, etc.). **makeResampleInstance** crea un caso concreto con este tipo de evaluaci�n. Recordemos que cuando usamos evaluaci�n train/test, 2/3 de los datos originales van a parar al conjunto de train y 1/3 al de test. Pero esto se hace aleatoriamente. Es decir, los datos se pueden dividir de muchas maneras distintas en entrenamiento y test. **makeResampleDesc** crea una partici�n concreta (aleatoriamente) de los datos originales en entrenamiento y test. Si volvieramos a llamar a **makeResampleInstance**, se crear�a otra partici�n concreta (pero distinta, al ser el reparto aleatorio) de los datos originales.

A continuaci�n vamos a ver c�mo se har�an los pasos b�sicos que ya conocemos de los apartados anteriores, pero usando **makeResampleDesc** y **makeResampleInstance**. Lo �nico que cambia es el tercer paso (recordad que en apartados anteriores usabamos **sample**). N�tese que en **mlR**, el m�todo de evaluaci�n train/test se llama **holdout**. Tambi�n cambian ligeramente los pasos cuarto (train) y quinto (performance), porque ahora los uniremos en un solo paso con **resample**. **resample** directamente entrena al modelo y lo eval�a con los datos de test (en lugar de tener que hacer las predicciones y evaluarlas despu�s). As�, obtendremos directamente el error en test.

```{r}
# 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 algoritmo de aprendizaje ("learner"). Vamos a usar los �rboles de regresi�n de la librer�a "rpart"
learner_rpart <- makeLearner("regr.rpart")
  
# Tercero, creamos una partici�n de los datos en train y test para evaluar el modelo
# Este es el �nico paso distinto con respecto a lo que hemos visto
metodo_evaluacion <- makeResampleDesc("Holdout") # En este caso, tambi�n podr�amos usar metodo_evaluacion <- holdout
set.seed(0)
particion_datos <- makeResampleInstance(metodo_evaluacion, task_bh)
                         
# Cuarto, entrenamos el modelo con los datos de train
set.seed(0) 
errores_rpart <- resample(learner_rpart, task_bh, particion_datos, measures = list(rmse, mae))

# Podemos ver los errores del modelo sobre el conjunto de test aqu�:
errores_rpart$aggr

```

El c�digo anterior es interesante, porque si queremos cambiar el m�todo de evaluaci�n de train/test (o holdout) a validaci�n cruzada de 5 folds, basta que cambiemos ligeramente el tercer paso, como vemos a continuaci�n (**CV** significa CrossValidation):

```{r}
# Tercero, creamos una partici�n de los datos en train y test para evaluar el modelo
# Este es el �nico paso distinto con respecto a lo que hemos visto
metodo_evaluacion <- makeResampleDesc("CV", iters=5) # iters es el n�mero de iteraciones o folds de la validaci�n cruzada
set.seed(0)
particion_datos <- makeResampleInstance(metodo_evaluacion, task_bh)
                         
# Cuarto, entrenamos el modelo con los datos de train
# Abajo en rojo se ver� una serie de mensajes con el rmse y el mae para cada iteraci�n o fold de crossvalidation
set.seed(0) 
errores_rpart <- resample(learner_rpart, task_bh, particion_datos, measures = list(rmse, mae))

# Al final, Podemos ver la media de los errores (la media de los cinco folds)
errores_rpart$aggr

```

### Ejercicio

Vamos a aplicar validaci�n cruzada de 10 folds al problema de clasficaci�n de iris con C50. Escribid el c�digo necesario, teniendo en cuenta que la medida de error t�pica en clasificaci�n es "misclassification rate", o "mmce", que es 1-tasa de aciertos. Tened cuidado de llamar a las variables **metodo_evaluacion_iris**, **particion_datos_iris**, para que no machaquen a los valores que ya ten�an para el problema de BostonHousing.

El dataset de clasificaci�n de plantas **iris** est� accesible en R en la variable **iris**. La variable de respuesta es "Species" (la especie de la planta)

```{r}

```


## Cambiando los hiper-par�metros de los algoritmos de aprendizaje

Sabemos que el comportamiento de los distintos algoritmos de aprendizaje depende mucho de los valores que le demos a sus hiper-par�metros. De momento, hemos usado los hiper-par�metros por omisi�n (por defecto). Para saber qu� hiper-par�metros tiene un m�todo de aprendizaje, podemos usar **getParamSet** as�:

```{r}
getParamSet("regr.kknn")
```

Por ejemplo, en k-vecinos, el valor por omisi�n de k es 1. �C�mo cambiarlo?. Debajo se ve el c�digo necesario. Lo �nico que cambia es el segundo paso. Como el resto de pasos ya los hemos ejecutado m�s arriba, y no cambian, ya no los pondremos en el siguiente c�digo. Es decir, la tarea sigue siendo la de Boston Housing, y el m�todo de evaluaci�n sigue siendo la validaci�n cruzada con 5 folds.

```{r}
# Segundo, definimos el algoritmo de aprendizaje ("learner"). Vamos a usar rpart con minsplit = 40 y kknn con n�mero de vecinos = 3
learner_rpart <- makeLearner("regr.rpart", minsplit = 40)
learner_kknn <- makeLearner("regr.kknn", k=3)

# Cuarto, entrenamos el modelo con los datos de train
set.seed(0) 
errores_rpart <-  resample(learner_rpart, task_bh, particion_datos, measures = list(rmse, mae))
set.seed(0) 
errores_kknn <- resample(learner_kknn, task_bh, particion_datos, measures = list(rmse, mae))

# Podemos ver los errores del modelo sobre el conjunto de test aqu�:
errores_rpart$aggr
errores_kknn$aggr

```
### Ejercicio

En la tarea de BostonHousing, probad distintos valores de k (k=1, k=2, k=3, k=4, por ejemplo), entrenad un modelo con cada uno, y evaluadlo con el conjunto de test (usad. �Cu�l funciona mejor (en test)? �Pod�is automatizar el proceso con un bucle? �Seriais capaces de automatizarlo con **lapply** en lugar de un bucle?

```{r}
```

# Ejercicio

En el conjunto de datos **airquality** (accesible mediante data(airquality)), vamos a predecir la cantidad de **Ozone** en el aire, teniendo en cuenta la radiaci�n Solar (**Solar.R**), la temperatura (**Temp**) y el viento (**Wind**).

Se pide:

- Eliminar del data.frame las columnas **Month** y **Day**
- Eliminar las filas que contengan **NA** en la variable **Ozone** (la variable de respuesta no puede contener NA). Ojo, aqu� no podemos usar **complete.cases**, porque elimina todas las filas que contienen alg�n NA en cualquier atributo. En este caso, s�lo queremos eliminar las filas que tengan NAs en la variable Ozone. Se puede hacer as�, suponiendo que **aq** contiene el data.frame airquality: aq <- aq[!is.na(aq$Ozone), ]
- Usad **summary** para comprobar si alg�n otro atributo todav�a tiene NAs (vereis que **Solar.R** si que tiene NAs)
- Cread una tarea mlR para predecir la variable **Ozone**. Llamad a la tarea **task_aq**
- Usad mlR para imputar las variables que tengan NAs (en este caso, ya sabemos que **Solar.R** los tiene)
- Usad validaci�n cruzada de 10 folds con rpart para estimar el MAE. Llamad a las variables **metodo_evaluacion_aq** y **particion_datos_aq** para que los nombres de las variables no machaquen a las que hemos definido en apartados anteriores.



```{r}

```


### Ejercicio: ajuste de k (de KNN) a mano

Vamos a intentar determinar el mejor valor de **k** para el m�todo de k-vecinos. Aunque m�s adelante veremos como **mlR** puede automatizar este proceso, en este ejercicio lo vamos a hacer a mano, para practicar y entender conceptos. Lo haremos de esta manera: los datos disponibles los dividiremos aleatoriamente en **train_indices** y **test_indices**, con **sample**, como ya sabemos hacer (2/3 para train y 1/3 para test). Y despu�s, los **train_indices** los volvemos a dividir en otros dos conjuntos: **train_train_indices** (2/3) y **validation_indices** (1/3), tambi�n con **sample**. Habr� que usar tambi�n **setdiff**.

Para ello seguiremos los siguientes pasos:

- Definimos **kknn** como algoritmo de aprendizaje, con **makeLearner**

- Creamos dos variables **train_indices** y otra **test_indices**, que contenga 2/3 de los datos para entrenar y 1/3 para evaluar. Tenemos que usar **sample** y **setdiff** como en apartados anteriores.

- Ahora separamos aleatoriamente los �ndices de **train_indices** en otros dos conjuntos: **train_train_indices** (2/3) y **validation_indices** (1/3). Usamos tambi�n **sample** y **setdiff**.

- Entrenamos un modelo knn con **train_train_indices** y k=1, y lo evaluamos con **validation_indices**. Para entrenar usaremos la funci�n **train**. Recordar que habr� que usar **subset** para decirle que queremos usar los **train_train_indices** para entrenar. Para evaluar, tendremos que obtener las predicciones con **predict** y computar el error con **performance**.

- Hacemos lo mismo que en el punto anterior, pero con k=2

- Hacemos lo mismo que en el punto anterior, pero con k=3

- �Cu�l de los tres errores obtenidos sobre el conjunto de validaci�n es menor? Apuntamos el k para el que el error sea el menor.

- Ahora construimos un modelo usando todos los datos de entrenamiento (**train_indices**) y el k �ptimo anterior

- Y dicho modelo lo evaluamos con los datos de test (**test_indices**)


```{r}


```
k = 3 parecer ser el �ptimo.

```{r}
```

## Ajuste �ptimo de hiper-par�metros con mlR

Vamos a ajustar de manera �ptima algunos de los par�metros de **regr.rpart**, en concreto **minsplit** (cuantos datos necesita una hoja para continuar la subdivisi�n del �rbol) y **maxdepth** (m�xima profundidad del �rbol). Para ello, el primer paso es definir los posibles valores que pueden tomar los hiper-par�metros. Para **minsplit** probaremos con 10 y 20 datos. Para **maxdepth** probaremos con profundidades de 2, 4, y 6.

```{r}
# simple mini grid
ps = makeParamSet(
  makeDiscreteParam("minsplit", values = c(10, 20)),
  makeDiscreteParam("maxdepth", values = c(2, 4, 6))
)
```

Lo segundo que tenemos que hacer es definir una estrategia de b�squeda de hiper-par�metros. La m�s sencilla de entender, pero tambi�n la m�s costosa en tiempo es "gridsearch", la cual prueba todas las posibles combinaciones de hiper-par�metros. En nuestro caso, probar� con **minsplit==10** y **maxdepth==2**, con **minsplit==10** y **maxdepth==4**, y as� con todas las posibles combinaciones de valores de ambos hiper-par�metros.

```{r}
control_grid <- makeTuneControlGrid()
```

Para cada combinaci�n de valores de hiper-par�metros, se entrenar� un modelo y se evaluar�. Tenemos que definir por tanto como la "gridsearch" va a evaluar los modelos. Podemos usar una estrategia sencilla y r�pida como **Holdout**.

```{r}
evaluacion_grid <- makeResampleDesc("Holdout")
```

A continuaci�n vamos a definir una "secuencia de m�todos" (Wrapped learner), mediante **makeTuneWrapper** que hace la siguiente secuencia:

1. Entrena todos los modelos necesarios para evaluar a cada posible combinaci�n de hiper-par�metros. En nuestro caso, como hay tres posibles valores para **maxdepth** y dos para **minsplit**, se entrenar�n 6 modelos distintos.
2. Eval�a todos esos modelos seg�n el m�todo elegido en **evaluacion_grid**. Qu�date con la mejor combinaci�n de hiper-par�metros.
3. Construye el modelo final con esos hiper-par�metros �ptimos y todos los datos.

```{r}
# Aqu� estamos construyendo un nuevo "learner", el cual es una secuencia de ajuste de hiper-par�metros y entrenamiento de rpart
# learner_rpart: es el algoritmo de aprendizaje que queremos usar
# evaluacion_grid: es la manera en la que vamos a evaluar los distintos hiper-par�metros
# par.set: define todas las posibles combinaciones de hiper-par�metros
# control: define que vamos a usar la estrategia de grid-search, o sea, vamos a probar con todas las combinaciones posibles de hiper-par�metros (3*2=6 en nuestro caso).

learner_ajuste_rpart <- makeTuneWrapper(learner_rpart, resampling = evaluacion_grid, par.set = ps, control = control_grid, measures = list(mae))
```


Pero adem�s de evaluar varios modelos para elegir el mejor conjunto de hiper-par�metros, tambi�n queremos evaluar el modelo final construido con los mejores hiper-par�metros. Esta idea es complicada de entender y es conveniente haber comprendido la clase de teor�a relacionada con este tema. Pero en el fondo, lo que estamos haciendo es construir y evaluar un modelo, como hicimos en apartados anteriores. La �nica diferencia es que usamos **learner_ajuste_rpart** como algoritmo de aprendizaje, en lugar de usar **learner_rpart** (esto �ltimo es lo que hicimos en apartados anteriores).

Se har�a as�:

```{r}
metodo_evaluacion <- makeResampleDesc("CV", iters=2) 
particion_datos <- makeResampleInstance(metodo_evaluacion, task_bh)
                         
# Cuarto, entrenamos el modelo con los datos de train
errores_ajuste_rpart <- resample(learner_ajuste_rpart, task_bh, particion_datos, measures = list(rmse, mae), extract = getTuneResult)

# Podemos ver los errores del modelo sobre el conjunto de test aqu�:
errores_ajuste_rpart$aggr
```

Y aqu� podemos ver la mejor combinaci�n de hiper-par�metros que se encontr�. N�tese que como nuestro m�todo de evaluaci�n es validaci�n cruzada con dos folds, el ajuste de par�metros se ha llevado a cabo dos veces, una en cada fold. Y para cada fold sale un conjunto de valores de hiper-par�metros distinto (de manera similar a como en cada fold sale un modelo distinto).

```{r}
errores_ajuste_rpart$extract
```
Y aqu� podemos ver la media (haciendo la media de los dos errores anteriores):

```{r}
errores_ajuste_rpart$aggr
```

Una vez que tenemos la evaluaci�n, podemos construir el modelo final con **train** con todos los datos. As�, tendremos nuestro modelo final, y su estimaci�n de comportamiento futuro, que es lo que obtuvimos antes con **errores_ajuste_rpart$aggr**.

```{r}
final_model_ajuste_rpart <- train(learner_ajuste_rpart, task_bh)
```