Para muchos procesos de machine learning se requiere que la caracteristica que tenemos como diana sea codificada como factor, asi que necesitamos recodificar la variable “diagnosis”, que es nuestro objetivo: averiguar el diagnóstico. Además, aprovechamos para simplificar el nombre de la variable recodificándola a Benign o Malignant.
wbcd$diagnosis<- factor(wbcd$diagnosis, levels = c("B", "M"),
labels = c("Benign", "Malignant"))
Comprobamos la proporción de pacientes Benign y Malign con prop.table():
round(prop.table(table(wbcd$diagnosis)) * 100, digits = 1)
##
## Benign Malignant
## 62.7 37.3
Si revisamos brevemente las dimensiones de los datos de la base de datos, obsevamos que cada variable tiene una escala diferente. Esto es un problema para el cálculo de los k-NN, ya que es altamente dependiente de la escala demedida de las variables a estudiar. De esta forma, las variables con mayor magnitud pesarán más en el cálculo. Esto podría causar problemas para nuestro clasificador, asi que vamos a aplicar la normalización a rescalar las características a un rango normal de valores.
Para normalizar los valores, necesitamos crear la función “r normalize()”.
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
Probamos en un par de vectores que la función hace su papel correctamente:
normalize(c(1, 2, 3, 4, 5))
## [1] 0.00 0.25 0.50 0.75 1.00
normalize(c(10, 20, 30, 40, 50))
## [1] 0.00 0.25 0.50 0.75 1.00
En efecto, ahora todas las variables tienen un mismo rango, de 0 a 1.
Utilizamos la función lapply() que coge una lista y aplica una función específica a cada elemento de la lista. Ya que una matriz de datos es una lista de vectores de misma longitud, podemos usar lapply() para aplicar normlize() a cada característica del dataset. El último paso es convertir la lista retornada por lapply() en un dataframe, usando la función as.data.frame():
wbcd_n <- as.data.frame(lapply(wbcd[2:31], normalize))
Preparación de los datos. Crear un training y probar los datasets.
Ante la asuencia de nuevos datasets que podamos probar después de entrenar el modelo, vamos a dividir nuestros datos en dos partes: un dataset de entrenamiento que será usado para construir el modelo de k-NN y un dataset que será usado para estimar le agudeza predictiva del modelo. Usaremos las primeras 469 registros para el training del dataset y el restante 100 para simular 100 nuevos pacientes.
Cuando construimos nuestros datasets de training y de prueba, excluimos la variable diana que es diagnosis. Para el entrenamiento del modelo necesitaremos guardar esta clase de etiquetas en vector de factores, dividido entre los datasets de training y test:
wbcd_train_labels <- wbcd[1:469, 1]
wbcd_test_labels <- wbcd[470:569, 1]
Esto lo que hace es coger todas las filas de los datasets y meterlas en un vector de la longitud de la columna 1 donde guarda todas las categorías de cada fila.
Entrenamiento del modelo sobre los datos.
Para el algoritmo de k-NN, la fase de entrenamiento en realidad no implica construccion del modelo, el proceso de entrenamiento de un aprendizaje suave como el k-NN implica el guardado del dato registrado en un formato estructurado.
Para clasificar los datos que vamos a testar, usaremos la implementacion de k-NN del paquete Class, el cual proporciona un conjunto de funciones de R para la clasificación. Si este paquete no esta ya instalado en nuestro sistema, podemos instalarlo:
install.packages("class",repos= "http://cran.us.r-project.org")
##
## The downloaded binary packages are in
## /var/folders/nn/fkq3ky797116dyb4r3tynkq00000gn/T//RtmpE0KEi4/downloaded_packages
library(class)
La función knn() del paquete class proporciona una implementación estándar y clásica del agoritmo k-NN. Para cada instancia de nuestro dataset de prueba, la función identificará los k-NN, usando las distancias euclídeas, donde k es un número especificado por el usuario. El dato testado es clasificado tomando un voto entre los k-NN, que implica asignar la clase de la mayoría de k-NN. Un empate se decide azarosamente. El entrenamiento usando la función knn() se lleva a cabo en una sola funcion de llamada, usando cuatro parámetros: knn(train,test,class,k)
Ya que nuestra matriz de entrenamiento contiene 469 registros, podríamos probar con k=21, un número impar parecido a la raiz cuadrada de 469. USar un numero impar elimina la posibilidad de tener un empate.
wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test,
cl = wbcd_train_labels, k = 21)
EVALUAR LA PERFORMANCE DEL MODELO
Evaluamos cómo de bien predijo la categoría de clase en el vector wbcd_test_pred con los valores conocidos del wbcd_test_labels. Para ello podemos usar el CrossTable(), en el paquete gmodels.
install.packages("gmodels", repos="http://cran.us.r-project.org")
##
## The downloaded binary packages are in
## /var/folders/nn/fkq3ky797116dyb4r3tynkq00000gn/T//RtmpE0KEi4/downloaded_packages
library(gmodels)
CrossTable(x=wbcd_test_labels,y=wbcd_test_pred,prop.chisq=FALSE)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Row Total |
## | N / Col Total |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 100
##
##
## | wbcd_test_pred
## wbcd_test_labels | Benign | Malignant | Row Total |
## -----------------|-----------|-----------|-----------|
## Benign | 61 | 0 | 61 |
## | 1.000 | 0.000 | 0.610 |
## | 0.968 | 0.000 | |
## | 0.610 | 0.000 | |
## -----------------|-----------|-----------|-----------|
## Malignant | 2 | 37 | 39 |
## | 0.051 | 0.949 | 0.390 |
## | 0.032 | 1.000 | |
## | 0.020 | 0.370 | |
## -----------------|-----------|-----------|-----------|
## Column Total | 63 | 37 | 100 |
## | 0.630 | 0.370 | |
## -----------------|-----------|-----------|-----------|
##
##
MEJORANDO LA PERFORMANCE DEL MODELO. Para ello emplearemos un método alternativo para reescalar las características numéricas. En Segundo lugar intentaremos diferentes valores para K
1) Transformación - standarización del z score.
Para estandarizar un vector, podemos utilizar la función scale() en R, que por defecto reescala los valores usando la estandarización del z-score. La función scale() ofrece el beneficio adicional de que puede ser aplicado directamente a una base de datos o dataframe, de manera que así podemos evitar el uso de la función lappl(). Para crear la vesrsión de z-score de la base de datos wbc, usaremos el siguiente comando, que reescala todas las características, con la excepción de “diagnosis”, y guarda el resultado en un dataframe.
wbcd_z<-as.data.frame(scale(wbcd[-1]))
## confirmamos que la transformación se aplicó correctamente, usando el summary:
summary(wbcd_z$area_mean)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -1.4532 -0.6666 -0.2949 0.0000 0.3632 5.2459
##la media de una variable estandarizada en z score siempre tiene que ser 0, y el rango debería ser muy compacto. Un z-score mayor de 3 o menos de -3 indica un valor extremadamente raro. Con esto en mente, la transformación parece haber funcionado. Necesitamos ahora dividir los datos en training y datasets, y entonces clasificar los registros de test usando la función knn().
wbcd_train <- wbcd_z[1:469, ]
wbcd_test <- wbcd_z[470:569, ]
wbcd_train_labels <- wbcd[1:469, 1]
wbcd_test_labels <- wbcd[470:569, 1]
wbcd_test_pred <- knn(train = wbcd_train, test = wbcd_test,
cl = wbcd_train_labels, k = 21)
CrossTable(x = wbcd_test_labels, y = wbcd_test_pred,
prop.chisq = FALSE)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Row Total |
## | N / Col Total |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 100
##
##
## | wbcd_test_pred
## wbcd_test_labels | Benign | Malignant | Row Total |
## -----------------|-----------|-----------|-----------|
## Benign | 61 | 0 | 61 |
## | 1.000 | 0.000 | 0.610 |
## | 0.924 | 0.000 | |
## | 0.610 | 0.000 | |
## -----------------|-----------|-----------|-----------|
## Malignant | 5 | 34 | 39 |
## | 0.128 | 0.872 | 0.390 |
## | 0.076 | 1.000 | |
## | 0.050 | 0.340 | |
## -----------------|-----------|-----------|-----------|
## Column Total | 66 | 34 | 100 |
## | 0.660 | 0.340 | |
## -----------------|-----------|-----------|-----------|
##
##