Saltar al contenido

Archivos de categoría Sin categoría

Agrupación por K-medias en Python

Existen muchos modelos para clustering. En este ejemplo vamos a usar el K-medias, que se utiliza mucho en ciencia de datos, especialmente si se necesita de forma rápida descubrir nuevas conclusiones a partir de datos no etiquetados.

Vamos a realizar dos ejemplos: Uno con k-medias en un conjunto de datos generado al azar y otro usando K-medias para la segmentación del cliente

Primero, importemos las librerías necesarias:

import random 
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.cluster import KMeans 
from sklearn.datasets.samples_generator import make_blobs 
%matplotlib inline

k-Media en un dataset generado aleatoriamente

Necesitamos primero configurar una semilla aleatoria(random seed). Utilizaremos la función numpy’s random.seed(), donde la semilla se establecerá con el valor 0

np.random.seed(0)

Luego, haremos clusters aleatorios de puntos usando la clase make_blobs. La clase make_blobs puede aceptar varias entradas, pero estaremos usando concretamente estas.
Entradas:

  • n_samples: Es el número total de puntos equitativamente divididos entre los clusters. El valor será: 5000
  • centers: Es el número de centros a generar. El valor será: [[4, 4], [-2, -1], [2, -3],[1,1]]
  • cluster_std: Es el desvío estándar de los clusters. El valor será: 0.9

Salidas:

  • X: Array de la forma [n_samples, n_features]. (Matríz de Distancia). Son las muestras generadas.
  • y: Array de la forma [n_samples]. (Response Vector). Son las etiquetas de números enteros para la pertenencia de cluster en cada muestra.
X, y = make_blobs(n_samples=5000, centers=[[4,4], [-2, -1], [2, -3], [1, 1]], cluster_std=0.9)

Mostrar los puntos de los datos generados al azar.

plt.scatter(X[:, 0], X[:, 1], marker='.')
<matplotlib.collections.PathCollection at 0x7f0a31054dd8>

Estableciendo K-Medias

Ahora que tenemos nuestros datos aleatorios, configuremos nuestro Clustering K-Medias.
La clase KMedias tiene muchos parámetros que se pueden utilizar, pero estaremos usando estos tres:

  • init: Método de inicialización de los centroides.
    • El valor será: «k-means++»
    • k-means++: Elije centros de clusters iniciales eficientes para el clustering k-media de forma tal de acelerar la convergencia.
  • n_clusters: El número de clusters a formar y la cantidad de centroides a generar.
    • El valor será: 4 (tenemos 4 centros)
  • n_init: Cantidad de veces que el algoritmo k-medias se ejecutará con diferentes semillas centroides. El resultado final será la mejor salida de consecutivas ejecuciones de n_init en términos de inercia.
    • Value will be: 12

Inicializar KMedias con estos parámetros, donde el parámetro de salida se llama k_means.

k_means = KMeans(init = "k-means++", n_clusters = 4, n_init = 12)

Ahora, unamos el modelo KMedias con la matriz de distancia que creamos anteriormente, X

k_means.fit(X)
KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
    n_clusters=4, n_init=12, n_jobs=None, precompute_distances='auto',
    random_state=None, tol=0.0001, verbose=0)

Ahora juntemos las etiquetas de cada punto en el modelo usando el atributo de KMedias .labels_ y lo guardamos como k_means_labels

k_means_labels = k_means.labels_
k_means_labels
array([0, 3, 3, ..., 1, 0, 0], dtype=int32)

También obtendremos las coordenadas de los centros del cluster usando KMedias .cluster_centers_ y guardémoslo en k_means_cluster_centers

k_means_cluster_centers = k_means.cluster_centers_
k_means_cluster_centers
array([[-2.03743147, -0.99782524],
       [ 3.97334234,  3.98758687],
       [ 0.96900523,  0.98370298],
       [ 1.99741008, -3.01666822]])

Creando la Trama

En este momento, tenemos los datos generados al azar y el modelo KMedias inicializado. Podemos dibujarlos y ver de qué se trata

# Inicializar el dibujo con las dimensiones especificadas.
fig = plt.figure(figsize=(6, 4))

# Los colores usan un mapa de color, dónde produciremos un array de colores basados en
# el número de etiquetas que hay. Usaremos set(k_means_labels) para obtener
# etiquetas unívocas.
colors = plt.cm.Spectral(np.linspace(0, 1, len(set(k_means_labels))))

# Crear un dibujo
ax = fig.add_subplot(1, 1, 1)

# Loop For que dibuja los puntos de datos y los centroides.
# k tomará valores entre 0-3, los cuales coincidirán con los clusters posibles en el
# que está cada punto.
for k, col in zip(range(len([[4,4], [-2, -1], [2, -3], [1, 1]])), colors):

    # Crear una lista de todos los puntos, donde aquellos que están 
    # en el cluster (ej. cluster 0) están etiquetados como verdadero, o en su defecto
    # estarán etiquetados como falso.
    my_members = (k_means_labels == k)

    # Definir el centroide o centro del cluster.
    cluster_center = k_means_cluster_centers[k]

    # Dibujar los puntos de datos con color col.
    ax.plot(X[my_members, 0], X[my_members, 1], 'w', markerfacecolor=col, marker='.')

    # Dibujo de los centroides con un color específico pero una linea más oscura
    ax.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col,  markeredgecolor='k', markersize=6)

# Título del dibujo
ax.set_title('KMeans')

# Eliminar los ticks del eje x
ax.set_xticks(())

# Eliminar los ticks del eje y
ax.set_yticks(())

# Mostrar el dibujo
plt.show()

Agrupación en 3 clusters

Si queremos agrupar los mismos datos en 3 cluster podemos realizar el siguiente

k_means3 = KMeans(init = "k-means++", n_clusters = 3, n_init = 12)
k_means3.fit(X)
fig = plt.figure(figsize=(6, 4))
colors = plt.cm.Spectral(np.linspace(0, 1, len(set(k_means3.labels_))))
ax = fig.add_subplot(1, 1, 1)
for k, col in zip(range(len(k_means3.cluster_centers_)), colors):
    my_members = (k_means3.labels_ == k)
    cluster_center = k_means3.cluster_centers_[k]
    ax.plot(X[my_members, 0], X[my_members, 1], 'w', markerfacecolor=col, marker='.')
    ax.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col,  markeredgecolor='k', markersize=6)
plt.show()

Segmentación de Clientes con K-Medias

Imagine que tiene un conjunto de datos de clientes y tiene que aplicar la segmentación de clientes. La segmentación de clientes es la práctica de particionar una base de datos en particiones de individuos que tengan características similares. Es una estrategia significativa ya que un negocio puede poner el foco en estos grupos específicos de clientes y ubicar los recursos de marketing lo más eficientemente posible. Por ejemplo, un grupo podría tener clientes con alto ingreso y bajo riesgo, lo que quiere decir que es muy probable adquieran productos, o se suscriban para un servicio. Una de las tareas del negocio es retener este tipo de clientes. Otro grupo podría incluir clientes de organizacion sin fines de lucro y asi seguiríamos con más ejemplos.

Descargamos el conjunto de datos

!wget -O Cust_Segmentation.csv https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/Cust_Segmentation.csv
--2020-02-20 17:44:55--  https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/Cust_Segmentation.csv
Resolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196
Connecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 34276 (33K)

Saving to: ‘Cust_Segmentation.csv’

Cust_Segmentation.c 100%[===================>] 33.47K –.-KB/s in 0.02s

2020-02-20 17:44:55 (1.61 MB/s) – ‘Cust_Segmentation.csv’ saved [34276/34276]

Cargar los Datos Desde un Archivo CSV

Antes de trabajar con los datos, se deberá usar la URL para obtener el archivo Cust_Segmentation.csv.

import pandas as pd
cust_df = pd.read_csv("Cust_Segmentation.csv")
cust_df.head()
Customer Id Age Edu Years Employed Income Card Debt Other Debt Defaulted Address DebtIncomeRatio
0 1 41 2 6 19 0.124 1.073 0.0 NBA001 6.3
1 2 47 1 26 100 4.582 8.218 0.0 NBA021 12.8
2 3 33 2 10 57 6.111 5.802 1.0 NBA013 20.9
3 4 29 2 4 19 0.681 0.516 0.0 NBA009 6.3
4 5 47 1 31 253 9.308 8.908 0.0 NBA008 7.2

Pre-procesamiento

Como podrás ver, Address en este conjunto de datos es una variable categórica. El algoritmo k-medias no está directamente aplicado a variables categóricas porque la función de la distancia Euclediana no tiene sentido para variables discretas. Por lo que descartaremos esta característica y seguiremos adelante para correr el clustering.

df = cust_df.drop('Address', axis=1)
df.head()
Customer Id Age Edu Years Employed Income Card Debt Other Debt Defaulted DebtIncomeRatio
0 1 41 2 6 19 0.124 1.073 0.0 6.3
1 2 47 1 26 100 4.582 8.218 0.0 12.8
2 3 33 2 10 57 6.111 5.802 1.0 20.9
3 4 29 2 4 19 0.681 0.516 0.0 6.3
4 5 47 1 31 253 9.308 8.908 0.0 7.2

Normalizando el desvío estándar

Ahora normalicemos el set de datos. Pero, ¿por que necesitamos normalizar? La normalización es un método estadístico que ayuda a los algoritmos basados en matemática interpretar características con distintas magnitudes y distribuciones de manera igual. Usamos tandardScaler() para normalizar nuestros set de datos.

from sklearn.preprocessing import StandardScaler
X = df.values[:,1:]
X = np.nan_to_num(X)
Clus_dataSet = StandardScaler().fit_transform(X)
Clus_dataSet
array([[ 0.74291541,  0.31212243, -0.37878978, ..., -0.59048916,
        -0.52379654, -0.57652509],
       [ 1.48949049, -0.76634938,  2.5737211 , ...,  1.51296181,
        -0.52379654,  0.39138677],
       [-0.25251804,  0.31212243,  0.2117124 , ...,  0.80170393,
         1.90913822,  1.59755385],
       ...,
       [-1.24795149,  2.46906604, -1.26454304, ...,  0.03863257,
         1.90913822,  3.45892281],
       [-0.37694723, -0.76634938,  0.50696349, ..., -0.70147601,
        -0.52379654, -1.08281745],
       [ 2.1116364 , -0.76634938,  1.09746566, ...,  0.16463355,
        -0.52379654, -0.2340332 ]])

Modeling

En nuestro ejemplo (en caso no hayamos tenido acceso al algoritmo de k-medias), sería lo mismo que suponer que cada grupo de cliente tendría cierta edad, educación, etc., con muchas pruebas y experimentos. Sin embargo, usando k-medias clustering, podemos hacer todo este proceso mucho más facilmente.

Apliquemos k-medias en nuestro set de datos y miremos las etiquetas del cluster.

clusterNum = 3
k_means = KMeans(init = "k-means++", n_clusters = clusterNum, n_init = 12)
k_means.fit(X)
labels = k_means.labels_
print(labels)
[0 2 0 0 1 2 0 2 0 2 2 0 0 0 0 0 0 0 2 0 0 0 0 2 2 2 0 0 2 0 2 0 0 0 0 0 0
 0 0 2 0 2 0 1 0 2 0 0 0 2 2 0 0 2 2 0 0 0 2 0 2 0 2 2 0 0 2 0 0 0 2 2 2 0
 0 0 0 0 2 0 2 2 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 0 0 0 2 0
 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2 0 2 0
 0 0 0 0 0 0 2 0 2 2 0 2 0 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 0 0 0 2 0
 0 0 0 0 2 0 0 2 0 2 0 0 2 1 0 2 0 0 0 0 0 0 1 2 0 0 0 0 2 0 0 2 2 0 2 0 2
 0 0 0 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 2 0 0 0 0
 0 0 2 0 0 2 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 2 0 2 0 2 2 0 0 0 0 0 0
 0 0 0 2 2 2 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 2 0 0 0 0 0 2 0 2 2 0
 0 0 0 0 2 0 0 0 0 0 0 2 0 0 2 0 0 2 0 0 0 0 0 2 0 0 0 1 0 0 0 2 0 2 2 2 0
 0 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0
 0 2 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 2 0 0 0 0 1
 0 0 0 0 0 0 2 0 0 0 1 0 0 0 0 2 0 1 0 0 0 0 2 0 2 2 2 0 0 2 2 0 0 0 0 0 0
 0 2 0 0 0 0 2 0 0 0 2 0 2 0 0 0 2 0 0 0 0 2 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0
 0 2 2 0 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 2 0 0 0 0 2 0 0 2 0 0 1 0 1 0
 0 1 0 0 0 0 0 0 0 0 0 2 0 2 0 0 1 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 0 2
 0 0 0 0 0 0 2 0 0 0 0 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2
 2 0 0 2 0 2 0 0 2 0 2 0 0 1 0 2 0 2 0 0 0 0 0 2 2 0 0 0 0 2 0 0 0 2 2 0 0
 2 0 0 0 2 0 1 0 0 2 0 0 0 0 0 0 0 2 0 0 0 2 0 0 0 0 0 2 0 0 2 0 0 0 0 0 0
 0 0 2 0 0 2 0 2 0 2 2 0 0 0 2 0 2 0 0 0 0 0 2 0 0 0 0 2 2 0 0 2 2 0 0 0 0
 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 2 2 0 2 0 2 2 0 0 2 0 0 0 0 0 2 2
 0 0 0 0 0 0 0 2 0 0 0 0 0 0 1 2 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2]

Descubrimientos

Asignamos las etiquetas a cada fila dentro del marco de datos.

df["Clus_km"] = labels
df.head(5)
Customer Id Age Edu Years Employed Income Card Debt Other Debt Defaulted DebtIncomeRatio Clus_km
0 1 41 2 6 19 0.124 1.073 0.0 6.3 0
1 2 47 1 26 100 4.582 8.218 0.0 12.8 2
2 3 33 2 10 57 6.111 5.802 1.0 20.9 0
3 4 29 2 4 19 0.681 0.516 0.0 6.3 0
4 5 47 1 31 253 9.308 8.908 0.0 7.2 1

Podemos revisar fácilmente los valores centroides sacando el promedio de las características de cada cluster.

df.groupby('Clus_km').mean()
Customer Id Age Edu Years Employed Income Card Debt Other Debt Defaulted DebtIncomeRatio
Clus_km
0 432.006154 32.967692 1.613846 6.389231 31.204615 1.032711 2.108345 0.284658 10.095385
1 410.166667 45.388889 2.666667 19.555556 227.166667 5.678444 10.907167 0.285714 7.322222
2 403.780220 41.368132 1.961538 15.252747 84.076923 3.114412 5.770352 0.172414 10.725824

Ahora, miremos la distribuición de los clientes basados en su edad e ingreso:

area = np.pi * ( X[:, 1])**2  
plt.scatter(X[:, 0], X[:, 3], s=area, c=labels.astype(np.float), alpha=0.5)
plt.xlabel('Age', fontsize=18)
plt.ylabel('Income', fontsize=16)

plt.show()

from mpl_toolkits.mplot3d import Axes3D 
fig = plt.figure(1, figsize=(8, 6))
plt.clf()
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)

plt.cla()
# plt.ylabel('Age', fontsize=18)
# plt.xlabel('Income', fontsize=16)
# plt.zlabel('Education', fontsize=16)
ax.set_xlabel('Education')
ax.set_ylabel('Age')
ax.set_zlabel('Income')

ax.scatter(X[:, 1], X[:, 0], X[:, 3], c= labels.astype(np.float))
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7f0a271f7748>

k-medias particionará los clientes en grupos mutuamente excluyentes, por ejemplo, en 3 clusters. Los clientes de cada cluster son parecidos unos a otros en el aspecto demográfico. Ahora, podemos crear un perfil para cada grupo, teniendo en cuenta las características en común de cada cluster. Por ejemplo, los 3 clusters podrían ser:

  • AFLUENTE, EDUCADO Y TERCERA EDAD
  • EDAD MEDIA E INGRESO PROMEDIO
  • JOVEN E INGRESO BAJO
0 Seguir leyendo →

Máquinas de soporte de vectores

Máquinas de soporte de vectores

Las máquinas de soporte de vectores (SVM – Support Vector Machine) es un algoritmo supervisado que puede clasificar los casos mediante la búsqueda de un separador. SVM trabaja primero correlacionando datos con un espacio de características de alta dimensión para que los puntos de datos puedan ser categorizados, incluso cuando los datos no son de otro modo separables linealmente. A continuación, se estima un separador para los datos. Los datos deben ser transformados de tal manera que un separador pueda ser dibujado como un hiperplano.
datos linealmente no separablesLa siguiente figura muestra la distribución de un conjunto pequeño de células en función del tamaño de la unidad y del grosor de la agrupación. Puede verse que los puntos de datos caen bajo dos categorías diferentes. Representa un conjunto de datos lineal y no separable. Las dos categorías se pueden separar con una curva, pero no con una línea. Es decir, representa un conjunto de datos linealmente no separable, que es el caso para la mayoría del conjunto de datos del mundo real.
Pero podemos transferir estos datos a un espacio dimensional más alto, cómo por ejemplo a un espacio 3-dimensional. Después de la transformación, el límite entre las dos categorías se puede definir por un hiperplano. Como estamos ahora en un espacio tridimensional, el separador se muestra como un plano. Este plano se puede utilizar para clasificar casos nuevos o desconocidos. Por lo tanto, el algoritmo SVM tiene como resultado a un hiperplano óptimo que categoriza nuevos ejemplos.
datos linealmente separables

Transformación de los datos

Por simplificar vamos a imaginar que nuestro conjunto de datos esta conformado por datos uni-dimensionales, por lo que sólo tenemos una característica x y que no son linealmente separables. Podemos transferirlo a un espacio bidimensional, por ejemplo aumentando la dimensión de los datos correlacionando x en un nuevo espacio utilizando una función, con salidas x y x-cuadrado. Ahora, los datos son linealmente separables. Hay que tener en cuenta que estamos en un espacio bidimensional, por lo que el hiperplano es una línea que divide a un plano en dos partes donde cada clase se coloca en cada lado. Ahora podemos utilizar esta línea para clasificar los nuevos casos.
transformación de datos
Básicamente, la correlación de datos en un espacio dimensional más alto se denomina kernelling. La función matemática utilizada para la transformación es conocida como la función kernel, y puede ser de diferentes tipos, como por ejemplo: Lineal, Polinomal, funcion Radial (o RBF) o Sigmoid. Cada una de estas funciones tiene sus propias características, sus pros y sus contras, y su ecuación, pero la buena noticia es que no necesitas conocerlos, ya que la mayoría de ellos ya están implementada en bibliotecas de lenguajes de programación de ciencia de datos. Además, como no es una forma fácil de saber qué función funciona mejor con cualquier conjunto de datos determinado, por lo general, elegimos diferentes funciones y comparamos los resultados. Puedes ver un ejemplo en este artículo: Máquinas de soporte de vectores en Python

Cómo encontrar el mejor separador de hiperplano después de la transformación

Búsqueda del mejor separadorLas SVM se basan en la idea de encontrar un hiperplano que mejor divide un conjunto de datos en dos clases. Como estamos en un espacio bidimensional, se puede pensar en el hiperplano como una línea que linealmente separa unos puntos de los otros. Una elección razonable como el mejor hiperplano es la que representa la mayor separación, o margen, entre las dos clases. Por lo tanto, el objetivo es elegir un hiperplano con un margen tan grande como sea posible. Los ejemplos más cercanos al hiperplano son los vectores de soporte. Es intuitivo que sólo los vectores de apoyo importan para lograr nuestro objetivo y, por lo tanto, otros ejemplos de entrenamiento pueden ser ignorados. Tratamos de encontrar el hiperplano de tal manera que tenga la distancia máxima con respecto a los vectores de soporte. Hay que tener en cuenta que las líneas de decisión de hiperplano y límite tienen sus propias ecuaciones. Por lo tanto, encontrar el hiperplano optimizado se puede formalizar utilizando una ecuación que involucra un poco más de matemáticas. Dicho esto, el hiperplano se aprende de los datos de entrenamiento usando un procedimiento de optimización que maximiza el margen y al igual que muchos otros problemas,e ste problema de optimización también puede ser resuelto por el descenso de gradiente.
Por lo tanto, la salida del algoritmo es los valores ‘w’ y ‘b’ para la línea. Puede realizar clasificaciones utilizando esta línea estimada. Es suficiente aplicar los valores de entrada en la ecuación de la línea, entonces, se puede calcular si un punto desconocido está por encima o por debajo de la línea. Si la ecuación devuelve un valor mayor que 0, entonces el punto pertenece a la primera clase, que está por encima de la línea, y viceversa.

Las dos principales ventajas de las máquinas de vectores de soporte son que son exactas en espacios dimensionales altos y utilizan un subconjunto de puntos de entrenamiento en la función de decisión (llamados vectores de soporte), así que también es eficiente en el manejo de la memoria. Las desventajas de las máquinas de vectores de soporte incluyen el hecho de que el algoritmo es propenso para el exceso de ajuste, si el número de características es mucho mayor que el número de muestras. Además, las SVM no proporcionan directamente estimaciones de probabilidad, que son deseables en la mayoría de problemas de clasificación. Y, por último, las SVM no son muy eficientes computacionalmente, si su conjunto de datos es muy grande, como cuando tiene más de mil filas.

Situaciones donde utilizar máquinas de soporte de vectores

SVM es bueno para las tareas de análisis de imagen, como la clasificación de imagen de reconocimiento de dígitos escritos a mano. Además, SVM es muy eficaz en tareas de minería de textos, particularmente debido a su efectividad en el tratamiento de datos de alta dimensión. Por ejemplo, se utiliza para detectar spam, asignación de categorías de texto y análisis de sentimientos. Otra aplicación de SVM se encuentra en la clasificación de datos de Expresión de Gene, de nuevo, debido a su potencia en la clasificación de datos de alta dimensión. SVM también se puede utilizar para otros tipos de problemas de machine learning, tales como regresión, detección de valores atípicos,y agrupación en clúster.

Ir al artículo anterior de la serie: Cálculo de los parámetros de la función logística

Ir al artículo siguiente de la serie: Introducción al clustering

0 Seguir leyendo →

Regresión lineal Vs. regresión logística

Regresión lineal Vs. regresión logística

regresión linealVamos a ver la diferencia entre la regresión lineal vs. la regresión logística. Revisaremos la regresión lineal y veremos por qué no se puede utilizar correctamente para algunos problemas de clasificación binaria.

El objetivo de la regresión logística consiste en construir un modelo para predecir la clase de cada observación, y también la probabilidad de que cada muestra pertenezca a una clase. Idealmente, queremos construir un modelo, \hat y, que pueda estimar que la clase de una observación sea 1, dadas sus características, x. Hay que anotar que «y» son los «vectores de etiquetas» también llamados «valores reales» que nos gustaría predecir, y que «\hat y» es el vector de los valores pronosticados por nuestro modelo.

Cómo funciona la regresión lineal

Con la regresión lineal, se puede encajar una línea o polinomio a través de los datos. Podemos encontrar esta línea usando el entrenamiento de nuestro modelo, o calculándola matemáticamente basádonos en conjuntos de ejemplo. Por simplificar, vamos a decir que es una línea recta que tiene una ecuación a + b \cdot x_1. Ahora, utilizamos esta línea para predecir un valor continuo «y«.

funcion categorizadaPero, ¿podemos utilizar la misma técnica para predecir un campo categórico? Tenemos una característica, x_1, y una característica categórica con dos clases: por ejemplo «si» y «no». Podemos correlacionar «si» y «no» con valores enteros, 0 y 1. Gráficamente, podríamos representar nuestro conjunto de datos con un gráfico de dispersión. Pero esta vez sólo tenemos 2 valores para el eje y. Con la regresión lineal se puede encajar de nuevo un polinomio a través de los datos, que se muestra tradicionalmente como a + b \cdto x. Este polinomio también se puede mostrar tradicionalmente como \theta_0 + \theta_1 \cdot x_1. Esta línea tiene 2 parámetros, que se muestran con el vector \theta, donde los valores del vector son \theta_0 y \theta_1. También podemos mostrar la ecuación de esta línea formalmente como \theta^T \cdot X = \theta_0 + \theta_1 \cdot x_1. Y, en general, podemos mostrar la ecuación de un espacio multidimensional como \theta^T \cdot X = \theta_0 + \theta_1 \cdot x_1 + \theta_2 \cdot x_2 + \cdots, donde θ son los parámetros de la línea en un espacio bidimensional, o parámetros de un plano en un espacio tri-dimensional, y así sucesivamente. Como \theta^T=\left [ \theta_0, \theta_1, \theta_2, \cdots \right ] es un vector de parámetros, y se supone que para ser multiplicado por X, se muestra convencionalmente como \theta^T. θ también se llama el «vector de peso» o «confianzas de la ecuación», siendo estos dos términos usados indistintamente. Y X=\begin{bmatrix}{1}\\{x_1}\\{x_2}\\{\cdots}\end{bmatrix} es el conjunto de características, que representa una observación. De todos modos, dado un conjunto de datos, todos los conjuntos de características X, \theta parámetros, se pueden calcular a través de un algoritmo de optimización o matemáticamente, que resulta en la ecuación de la línea adecuada.
Por ejemplo, si los parámetros de esta línea son \theta^T=\left [ -1, 0.1 \right ], entonces la ecuación de la línea es \theta^T \cdot X = -1 + 0.1 \cdot x_1. Ahora, podemos utilizar esta línea de regresión para predecir. Por ejemplo, para un valor de 13 tenemos: \theta^T \cdot X = -1 + 0.1 \cdot x_1 = -1 + 0.1 \cdot 13 = 0.3. Ahora podemos definir un umbral, por ejemplo, en 0.5, para definir la clase. Así que escribimos una regla para nuestro modelo, \hat y, que nos permite separar la clase 0 de la clase 1. Si el valor de \theta^T \cdot X es menor que 0.5, entonces la clase es 0, de lo contrario, si el valor de \theta^T \cdot X es más de 0.5, entonces la clase es 1. Y debido a que el valor «y» de nuestro ejemplo es menor que el umbral, podemos decir que pertenece a la clase 0, basada en nuestro modelo.

Pero aquí hay un problema, ya que no es el mejor modelo para conocer la probabilidad de pertenencia a la clase. Además, si utilizamos la línea de regresión para calcular la clase de un punto, siempre devuelve un número independientemente de lo grande o pequeño, positivo o negativo que es la entrada. Así utilizando el umbral, podemos encontrar la clase de un registro sin importar lo grande que sea el valor, siempre que sea mayor que 0.5, simplemente es igual a 1. Y viceversa, independientemente de lo pequeño que sea el valor, la salida sería cero si es menor que 0.5. En otras palabras, no hay diferencia entre un valor de uno o de 1000; el resultado sería 1. Este método no nos da realmente la probabilidad de que una observación pertenezca a una clase, lo cual es muy deseable. Así que necesitamos un método que también nos pueda dar la probabilidad de caer en una clase.

Función sigmoide

Funcion sigmoideSi en vez de usar \theta^T \cdot X usamos una función específica llamada sigmoide (\sigma), entonces, \sigma(\theta^T \cdot X) = \sigma(\theta_0 + \theta_1 \cdot x_1 + \cdots) nos da la probabilidad de que un punto pertenezca a una clase, en lugar del valor de y directamente. En lugar de calcular el valor de \theta^T \cdot X directamente, devuelve la probabilidad de que un \theta^T \cdot X sea muy grande o muy pequeño. Siempre devuelve un valor entre 0 y 1 dependiendo de que tan grande es \thta^T \cdot Xrealmente.
Ahora, nuestro modelo es \sigma(\theta^T \cdot X), que representa la probabilidad de que la salida sea 1, dado x. La función sigmoide, también llamada la función logística, se asemeja a la función de paso y se utiliza la siguiente expresión en la regresión logística:

\sigma(\theta^T X) = \frac{1}{1+e^{-\theta^T X}}

Fíjese en que en la ecuación sigmoide, cuando \theta^T X se hace muy grande, el e^{-\theta^T X} en el denominador de la fracción se convierte en casi cero, y el valor de la función sigmoid se acerca más a 1. Si \theta^T X es muy pequeño, la función sigmoide se acerca a cero. Representando en el trazado sigmoide, cuando \theta^T X, se hace más grande, el valor de la función sigmoide se acerca a 1, y también, si el \theta^T X es muy pequeño, la función sigmoide se acerca a cero. Por lo tanto, el valor de la función sigmoide está entre 0 y 1, lo que hace que sea apropiado para interpretar los resultados como probabilidades. Es obvio que cuando el resultado de la función sigmoide se acerca más a 1, la probabilidad de y, dado x, sube, y en contraste, cuando el valor sigmoide está más cerca de cero, la probabilidad de y, dado x, es muy pequeña.

Regresión logística

En la regresión logística, modelamos la probabilidad de que una entrada X pertenezca a la clase por defecto Y=1, y podemos escribirlo formalmente como, P(Y=1 \vert X). También podemos escribir P(y=0 \vert x) = 1 - P(y=1 \vert x). Por lo tanto, nuestro trabajo es entrenar al modelo para que establezca sus valores de parámetros de tal manera que nuestro modelo es una buena estimación de P(y=1 \vert x). De hecho, esto es lo que un buen modelo clasificador construido por regresión logística se supone que debe hacer por nosotros. Además, debe ser una buena estimación de P(y=0 \vert x) que se puede mostrar como 1 - \sigma(\theta^T X).
Vamos a ver cuál es el proceso para lograr el cálculo:

  1. Inicializar el vector con valores aleatorios, como se hace con la mayoría de los algoritmos de aprendizaje automático.
  2. Calcular la salida del modelo, que es \sigma(\theta^T X), para una observación de la muestra. La salida de esta ecuación es el valor de la predicción, la probabilidad que el cliente pertenece a la clase 1.
  3. Comparar la salida de nuestro modelo, \hat y, con la etiqueta real de la observación. Luego, registre la diferencia como el error de nuestro modelo para esta observación. Este es el error para una sóla observación dentro del conjunto de datos.
  4. Calcular el error para todas las observaciones tal como lo hicimos en los pasos anteriores, y añadimos estos errores. El error total es el coste del modelo. La función de coste representa la diferencia entre el real y los valores pronosticados del modelo. Por lo tanto, el costo muestra lo mal que se encuentra el modelo estimando las etiquetas del cliente. Por lo tanto, cuanto más bajo es el costo, mejor es el modelo estimando correctamente las etiquetas del cliente. Por lo tanto, lo que queremos hacer es tratar de minimizar este costo.
  5. Pero, debido a que los valores iniciales para \theta fueron elegidos al azar, es muy probable que la función de coste sea muy alta. Por lo tanto, cambiamos la forma de ser de tal manera que esperemos reducir el coste total.
  6. Después de cambiar los valores de \theta, volvemos al paso 2.

A continuación, empezamos otra iteración y calculamos de nuevo el coste del modelo. Y seguimos haciendo esos pasos una y otra vez, cambiando los valores de \theta cada vez, hasta que el coste es lo suficientemente bajo. Por lo tanto, esto plantea dos preguntas: ¿Cómo podemos cambiar los valores de \theta de modo que el coste se reduzca en las iteraciones? ¿Cuándo debemos detener las iteraciones?
Hay diferentes maneras de cambiar los valores \theta, pero una de las formas más populares es la ascendencia gradiente. Además, hay varias formas de detener las iteraciones, pero esencialmente se detiene la formación mediante el cálculo de la precisión de su modelo, y detenerlo cuando sea satisfactorio.

Ir al artículo anterior de la serie: Introducción a la Regresión Logística

Ir al artículo siguiente de la serie: Cálculo de los parámetros de la función logística

0 Seguir leyendo →

Árboles de decisión en Python

En este ejercicio se utiliza un algoritmo de clasificación para construir un modelo basado en datos históricos de pacientes y sus respectivos medicamentos. Luego, se usa el árbol de decisión para predecir la clase de paciente desconocido o para encontrar la droga adecuada para el mismo.

Importación de los paquetes básicos

import numpy as np 
import pandas as pd
from sklearn.tree import DecisionTreeClassifier

Un investigador médico ha recolectado datos de un grupo de pacientes con la misma enfermedad. Durante su tratamiento, cada paciente respondio a uno de 5 medicaciones: Droga A, Droga B, Droga c, Droga x e y. Construir un modelo para encontrar la droga apropiada para pacientes con la misma enfermedad. Las características son Edad, Sexo, Presión Sanguínea y Colesterol.

Es un ejemplo de un clasificador binario para construir un árbol de decisión para predecir la clase de pacientes desconocidos o para prescribirle a un nuevo paciente.

Descargando los Datos

Para descagar los datos, utilizaremos !wget desde IBM Object Storage.

!wget -O drug200.csv https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/drug200.csv
--2020-01-20 16:12:03--  https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/drug200.csv
Resolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196
Connecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6027 (5.9K)

Saving to: ‘drug200.csv’

drug200.csv 100%[===================>] 5.89K –.-KB/s in 0s

2020-01-20 16:12:03 (12.4 MB/s) – ‘drug200.csv’ saved [6027/6027]

my_data = pd.read_csv("drug200.csv", delimiter=",")
my_data[0:5]
Age Sex BP Cholesterol Na_to_K Drug
0 23 F HIGH HIGH 25.355 drugY
1 47 M LOW HIGH 13.093 drugC
2 47 M LOW HIGH 10.114 drugC
3 28 F NORMAL HIGH 7.798 drugX
4 61 F LOW HIGH 18.043 drugY
my_data['Drug'].value_counts()

drugY    91
drugX    54
drugA    23
drugC    16
drugB    16
Name: Drug, dtype: int64

Pre-procesamiento

Utilizando my_data como los datos de panda el archivo Drug.csv, declara las siguientes variables:

  • X como la matriz de características (datos de my_data)
  • y como el vector de respuesta (target)

Elimina la columna que contiene el target ya que no posee valores numéricos.

X = my_data[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
X[0:5]
array([[23, 'F', 'HIGH', 'HIGH', 25.355],
       [47, 'M', 'LOW', 'HIGH', 13.093],
       [47, 'M', 'LOW', 'HIGH', 10.113999999999999],
       [28, 'F', 'NORMAL', 'HIGH', 7.797999999999999],
       [61, 'F', 'LOW', 'HIGH', 18.043]], dtype=object)

Algunas características son de categoría, tales como __Sex__ o__BP__. Desafortunadamente, los árboles de Decisión Sklearn no manejan variables categóricas. Pero las podemos convertir en valores numéricos.

from sklearn import preprocessing
le_sex = preprocessing.LabelEncoder()
le_sex.fit(['F','M'])
X[:,1] = le_sex.transform(X[:,1]) 

le_BP = preprocessing.LabelEncoder()
le_BP.fit([ 'LOW', 'NORMAL', 'HIGH'])
X[:,2] = le_BP.transform(X[:,2])

le_Chol = preprocessing.LabelEncoder()
le_Chol.fit([ 'NORMAL', 'HIGH'])
X[:,3] = le_Chol.transform(X[:,3]) 

X[0:5]

array([[23, 0, 0, 0, 25.355],
       [47, 1, 1, 0, 13.093],
       [47, 1, 1, 0, 10.113999999999999],
       [28, 0, 2, 0, 7.797999999999999],
       [61, 0, 1, 0, 18.043]], dtype=object)

Ahora, podemos completar la variable objetivo (target).

y = my_data["Drug"]
y[0:5]
0    drugY
1    drugC
2    drugC
3    drugX
4    drugY
Name: Drug, dtype: object

Configurando el Arbol de Decisión

Importamos train_test_split de sklearn.cross_validation.

from sklearn.model_selection import train_test_split

Con la función train_test_split obtenemos parámetros diferentes. Los nombraremos: X_trainset, X_testset, y_trainset, y_testset
El train_test_split necesitará los parámetros: X, y, test_size=0.3, and random_state=3.
La X e y son los arrays necesarios antes de la operación dividir/separar, test_size representa el grado del dataset de pruebas, y el random_state asegura que obtendremos las mismas divisiones.

X_trainset, X_testset, y_trainset, y_testset = train_test_split(X, y, test_size=0.3, random_state=3)

Modelando

Primero crearemos una instancia del DecisionTreeClassifier llamada drugTree. Dentro del clasificador, especificaremos criterion=»entropy» para que podamos ver la nueva información de cada nodo.

drugTree = DecisionTreeClassifier(criterion="entropy", max_depth = 4)
drugTree # muestra los parámetros por omisión
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=4,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

Luego, adaptaremos los datos con la matriz de entrenamiento X_trainset y el vector de respuesta y_trainset

drugTree.fit(X_trainset,y_trainset)
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=4,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

Predicción

Ahora hagamos algunas predicciones en el dataset de pruebas y guardémoslas en una variable llamada predTree.

predTree = drugTree.predict(X_testset)

Puedes imprimir predTree y y_testset si quieres comparar visualmente la predicción con los valores actuales.

print (predTree [0:5])
print (y_testset [0:5])

['drugY' 'drugX' 'drugX' 'drugX' 'drugX']
40     drugY
51     drugX
139    drugX
197    drugX
170    drugX
Name: Drug, dtype: object

Evaluación

Luego, importemos __metrics__ de sklearn y revisemos la precisión de nuestro modelo.

from sklearn import metrics
import matplotlib.pyplot as plt
print("Precisión de los Arboles de Decisión: ", metrics.accuracy_score(y_testset, predTree))
Precisión de los Arboles de Decisión:  0.9833333333333333

Accuracy classification score calcula la precisión del subconjunto: las etiquetas predichas para una muestra deben coincidir con las correspondientes etiquetas en y_true.

En la clasificación multietiqueta, la función devuelve un subconjunto de precisión. Si el conjunto de etiquetas predichas para una muestra coincide totalmente con el conjunto de etiquetas, entonces la precisión del subconjunto es 1.0; de no ser así, es 0.0.

Visualización

Observemos el árbol

from sklearn.externals.six import StringIO
import pydotplus
import matplotlib.image as mpimg
from sklearn import tree
%matplotlib inline 
dot_data = StringIO()
filename = "drugtree.png"
featureNames = my_data.columns[0:5]
targetNames = my_data["Drug"].unique().tolist()
out=tree.export_graphviz(drugTree,feature_names=featureNames, out_file=dot_data, class_names= np.unique(y_trainset), filled=True,  special_characters=True,rotate=False)  
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())  
graph.write_png(filename)
img = mpimg.imread(filename)
plt.figure(figsize=(100, 200))
plt.imshow(img,interpolation='nearest')

0 Seguir leyendo →