Unsupervised Machine Learning (UML)

El objetivo del aprendizaje no supervisado es exploratorio. Encuentra estructuras y patrones ocultos en los datos.

El clustering agrupa los datos en base a similitudes entre ellos. La reducción de dimensionalidad trata de simplificar los datos para generar hipótesis o visualizarlos de forma más sencilla.

Clustering no jerárquico

Agrupa datos de la muestra en clusters en base a similitudes entre sus elementos individuales. El modelo intenta generar la menor suma de cuadrados de cada observación a su centro (total within cluster sum of squares).

k-means clustering

stats::kmeans(data,
              centers = 3,
              iter.max = 10,
              nstart = 10)
  1. center = n genera n centros, tres valores que actúan de media para cada grupo. Cada elemento es asignado a uno de los grupos de forma aleatoria

  2. El modelo asigna cada elemento al grupo que tenga su media más cerca; genera una nueva media y repite el proceso hasta que los centros dejan de moverse (o se alcanza un número máximo de iteraciones, iter.max = n)

  3. nstart = n modifica el elemento aleatorio de kmeans, que marca la posición inicial de cada centro; realiza n posiciones iniciales para mejorar el modelo

Generar el modelo

x <- subset(iris, select = c(Petal.Length, Sepal.Length))
cl <- stats::kmeans(x, centers = 3, nstart = 100)
cl
K-means clustering with 3 clusters of sizes 58, 51, 41

Cluster means:
  Petal.Length Sepal.Length
1     4.393103     5.874138
2     1.492157     5.007843
3     5.678049     6.839024

Clustering vector:
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
  2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 
 21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
  2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 
 41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
  2   2   2   2   2   2   2   2   2   2   3   1   3   1   1   1   1   1   1   1 
 61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
  1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   3   3   1   1 
 81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
  1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   2   1 
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
  3   1   3   3   3   3   1   3   3   3   3   3   3   1   1   3   3   3   3   1 
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
  3   1   3   1   3   3   1   1   3   3   3   3   3   3   3   3   3   3   1   3 
141 142 143 144 145 146 147 148 149 150 
  3   3   1   3   3   3   1   3   3   1 

Within cluster sum of squares by cluster:
[1] 23.508448  9.893725 20.407805
 (between_SS / total_SS =  90.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"      

Método del codo

Para determinar el punto que mejor define las agrupaciones con este método se puede dibujar un gráfico que represente el número de grupos en el eje x y suma total de los cuadrados en el eje y. El punto en el que el gráfico se acoda indica el punto de mayor discriminación.

elbow <- c()
for (c in 1:15) {
    elbow[c] <- stats::kmeans(x,
                              centers = c)$tot.withinss
}

plot(elbow, type="b")

Resultados

ggplot(data = x, aes(x = Sepal.Length, y = Petal.Length,
       colour = factor(cl$cluster))) +
geom_point() + 
geom_point(data = cl$centers,
           aes(x = Sepal.Length, y = Petal.Length),
               color= "black", size = 7, shape = 4) +
scale_colour_discrete(name = "Species") + 
theme_classic()
ggplot(data = iris, aes(x = Sepal.Length, y = Petal.Length,
       colour = Species)) + 
geom_point() +
theme_classic()

k-means clustering

fuente original (‘iris’)

Clustering jerárquico

En el clustering jerárquico tiene dos aproximaciones; la aglomerativa y la divisva.

La agrupación aglomerativa es útil cuando no se conoce con certeza el la estructura de la muestra y se quieren determinar cuantos grupos existen; considera cada elemento como un grupo, al cuál va añadiendo elementos similares de forma progresiva (hclust, agnes).

La agrupación divisiva es útil cuando se quiere controlar el número de grupos que se van a generar - segmenta la muestra original de forma progresiva hasta obtenerlos (diana).

Agrupamiento jerárquico aglomerativo

Se define un cluster con cada uno de los \(n\) puntos de la muestra. En cada iteración se fusionan los dos cluster más próximos entre sí hasta que se consume la muestra. Las distancias utilizan el vector multidimensional de cada elemento.

Este proceso genera un dendrograma, que relaciona los grupos y las distancias entre ellos. El último definie la distancia que se considera relevante para diferenciar los grupos generados.

dist() calcula las distancias entre elementos (la distancia Euclidiana; devuelve una matriz de distancias). hclust() aplica el clustering jerárquico. Se puede representar después con plot():

iris_dist <- dist(iris[,1:4])
iris_clst <- hclust(iris_dist)
plot(iris_clst, main = "Agrupamiento jerárquico aglomerativo")

Definir los clusters

cutree corta el árbol definiendo h (la altura) o k (el número de clusters deseado) - devuelve un vector con un factor.

plot(iris_clst, main="", sub="", xlab="", ylab ="")
abline(h = 3.9, col="red")

identical(cutree(iris_clst, h = 3.9),
          cutree(iris_clst, k = 3))
[1] TRUE

Agrupamiento jerárquico divisivo

hclust también permite hacer clustering divisivo:

iris_div <- hclust(iris_dist, 
       method = "ward.D2")
plot(iris_div)

cutree(iris_div, k = 4)
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 2 3 2 3 2 3 3 2 3 2 3 2 3 3 2 3 2 2 2 2
 [75] 2 2 2 4 2 3 3 3 3 2 3 2 2 2 3 3 3 2 3 3 3 3 3 2 3 3 4 2 4 4 4 4 3 4 4 4 4
[112] 4 4 2 2 4 4 4 4 2 4 2 4 2 4 4 2 2 4 4 4 4 4 2 2 4 4 4 2 4 4 4 2 4 4 4 2 4
[149] 4 2
iris_div_clust <- data.frame(iris[1:4], 
                             Clusters = cutree(iris_div, k = 4))
iris_div_clust[sample(1:150, 5), ]
    Sepal.Length Sepal.Width Petal.Length Petal.Width Clusters
47           5.1         3.8          1.6         0.2        1
48           4.6         3.2          1.4         0.2        1
105          6.5         3.0          5.8         2.2        4
104          6.3         2.9          5.6         1.8        4
149          6.2         3.4          5.4         2.3        4

Reducción dimensional

La reducción dimensional transforma los datos y los transfiere a un nuevo espacio dimensional más reducido.

Las nuevas dimensiones resumen las características de los datos originales. Esta simplificación permite

  • visualizar los datos de forma sencilla
  • encontrar patrones y estructuras en los datos
  • realizar pre-procesamiento de los datos y las simplifica. genera componentes principales (PC) independientes entre sí y los numera(PC1, PC2, PC3…)

Principal Component Analysis (PCA)

PCA es una técnica de reducción dimensional lineal que genera nuevas dimensiones de las dimensiones originales. Las nuevas dimensiones

  • son combinaciones lineales de las dimensiones originales
  • la primera dimensión (PC1) recoge la mayor variabilidad de los datos
    • después PC2, PC3, PC4…
  • los componentes son independientes entre sí

prcomp (principal componente analysis)

prcomp(x, retx = TRUE, center = TRUE, scale. = FALSE,
       tol = NULL, rank. = NULL, ...)

scale.= aparece como FALSE por defecto; pero en general tiene sentido escalar las varianzas antes (y help(prcomp) lo recomienda).

Preparar los datos

Muchos de los modelos requieren preparar los datos previamente

  • estandarizando todas las variables
  • eliminando las que no sean numéricas

Para la estandarización, escalamos las variables para obtener una distribución normal \(X \sim N(0,1)\):

\[ z = \dfrac{x - \mu}{\sigma} \]

Aunque algunas funciones (como prcomp) permiten escalar los datos antes del análisis pasando la opción scale. = TRUE (o algo parecido), es buena idea revisarlos y prepararlos de antemano

dd <- survival::diabetic
head(dd,3)
  id laser age   eye trt risk  time status
1  5 argon  28  left   0    9 46.23      0
2  5 argon  28 right   1    9 46.23      0
3 14 xenon  12  left   1    8 42.50      0
dd_std <- scale(dd[, sapply(dd,is.numeric)])
head(dd_std,3)
            id        age        trt       risk      time     status
[1,] -1.752093  0.4873237 -0.9987302 -0.4731892 0.4987246 -0.8042944
[2,] -1.752093  0.4873237  0.9987302 -0.4731892 0.4987246 -0.8042944
[3,] -1.733930 -0.5928762  0.9987302 -1.1511404 0.3240656 -0.8042944

La presencia de un factor como variable del conjunto de datos puede alterar el análisis (se interpreta como numérico aunque no lo sea). Hay soluciones a este problema.

Los factores ordinales pueden ser aceptables.

En iris, difícil visualizar las relaciones entre grupos utilizando las cuatro dimensiones numéricas; utilizando PCA reducimos las dimensiones y facilitamos la interpretación:

pairs(iris[, -5], col = iris[,5], pch=19)

Las cuatro dimensiones numéricas de iris

Primero estandarizamos la muestra

head(iris,3)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
iris_std <- scale(iris[, sapply(iris, is.numeric)])
iris_std <- as.data.frame(iris_std)
iris_std$Species <- iris$Species
head(iris_std,3)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1   -0.8976739   1.0156020    -1.335752   -1.311052  setosa
2   -1.1392005  -0.1315388    -1.335752   -1.311052  setosa
3   -1.3807271   0.3273175    -1.392399   -1.311052  setosa

Aplicamos PCA

irispca <- prcomp(iris_std[,-5], scale.=TRUE)
summary(irispca)
Importance of components:
                          PC1    PC2     PC3     PC4
Standard deviation     1.7084 0.9560 0.38309 0.14393
Proportion of Variance 0.7296 0.2285 0.03669 0.00518
Cumulative Proportion  0.7296 0.9581 0.99482 1.00000

Proportion of Variance devuelve la cantidad de la varianza explicada por cada PC. En iris, PC1 y PC2 explican el 96% de la varianza (73% y 23% respectivamente).

plot(irispca)

pca$x contiene los vectores de cada PC para poder examinarlos posteriormente:

pcx <- irispca$x
plot(pcx[,1], pcx[,2], col = iris[,5], pch =19,
     xlab = "PC1", ylab = "PC2")
pcx <- irispca$x
plot(pcx[,3], pcx[,4], col = iris[,5], pch =19,
     xlab = "PC3", ylab = "PC4")

PC1 y PC2 explican el 96% de la varianza

PC3 y PC4 explican el 4% restante

biplot() devuelve los puntos orientados en PC1 y PC2, con unos vectores superpuestos que representan los datos originales:

biplot(irispca)

pca$rotation contiene información sobre cómo contribuyen las distintas variables a cada PC (en signo y cuantía), los cargos

irispca$rotation
                    PC1         PC2        PC3        PC4
Sepal.Length  0.5210659 -0.37741762  0.7195664  0.2612863
Sepal.Width  -0.2693474 -0.92329566 -0.2443818 -0.1235096
Petal.Length  0.5804131 -0.02449161 -0.1421264 -0.8014492
Petal.Width   0.5648565 -0.06694199 -0.6342727  0.5235971

En este ejemplo, Sepal.Width contribuye de forma muy importante en PC2, de forma negativa.

t-SNE (t-distributed Stochastic Neighbour Embedding)

t-SNE es una técnica de reducción dimensional no lineal que intenta condensar una variable multidimensional a dos o tres dimensiones. Agrupa elementos próximos e ignora elementos distantes1. Es útil para variables con un gran número de dimensiones.

Rtsne::Rtsne

Primero hay que eliminar duplicados de los datos con unique().

Los resultados de Rtsne son estocásticos - cada vez que se ejecuta devuelve resultados distintos.

uiris <- unique(iris[, 1:5])
iristsne <- Rtsne::Rtsne(uiris[,1:4])
plot(iristsne$Y, col = uiris$Species)

Escalar los datos

Al igual que en PCA, es posible escalar las dimensiones antes del análisis con pca_scale = TRUE. Los datos están centrados en 0 porque los datos están normalizados por defecto (normalize = TRUE). Ver escalar los datos.

Al igual que otros algoritmos (principalmente algoritmos de clasificación), t-SNE tiene dos parámetros que modifican sustancialmente los resultados:

Perplejidad
??? Iteraciones
el número de iteraciones hasta que el proceso de clustering finaliza
Incompleto

  1. https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding↩︎