{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#### 0.0.1 - 2021 - 01 - 01\n",
"#### Dr. Marco Aceves \n",
"#### rev en Jupyter Notebook\n",
"#### Código como ejemplo como parte del libro:\n",
"#### Inteligencia Artificial para Programadores con Prisa\n",
"#### 8.14_K-means.ipynb\n",
"# Implementación de Algoritmo K-medias (K-means)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# kmeans"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"En este apartado revisaremos uno de los algoritmos de aprendizaje no supervisado más utilizados, kmeans. \n",
"Este algoritmo nos permite la agrupación de un conjunto de entrada en diversos subgrupos que cuentan con características similares. Ésto tiene una gran utilidad en proyectos donde no se tiene información etiquetada y se quiere realizar una clasificación basada en características de cada instancia del conjunto de datos.
\n",
"Comenzemos a revisar la implementación del algoritmo, inicialmente importamos algunas librerías de utilidad. \n",
"### Importación de librerias"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Algoritmo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El algoritmo de kmeans se basa en la medición del centroide de cada subgrupo por medio de la media de los integrantes de dicho grupo. Asignando cada instancia al grupo más cercano. \n",
"Revisemos el algoritmo por pasos:
\n",
"1. Definir la cantidad de clases.\n",
"2. Inicializar centroides de manera aleatoria.\n",
"3. Medición de la distancia euclidiana de cada punto a cada centroide.\n",
"4. Asignación de cada punto a cada clase según la distancia mínima calculada.\n",
"5. Actualización de centroides con la media de los puntos de cada grupo.\n",
"6. Repetir 3-5 hasta alcanzar algún criterio de paro."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"El criterio de paro no está establecido, comúnmente se realiza por: \n",
"- Número de iteraciones.\n",
"- Ausencia de cambios en los grupos.\n",
"- Poca cantidad de cambios en la asignación de cada punto.\n",
"- Otros."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Recolectar\n",
"\n",
"En este ejercicio usaremos una base de datos muy popular dentro del área de clasificación, Iris. Realizaremos la clasificación de 3 distintos tipos de flores según sus características y sin conocimiento previo de las clases.
\n",
"A continuación crearemos nuestra clase **myKmeans**, la cual será un clasificador no supervisado. Dentro de las métodos que tendrá esta clase están:\n",
"- **\\__init__**: Se inicializan las 5 variables que nos servirán para realizar este algoritmo.\n",
"- **set_random_centroids**: Inicializar los centroides de manera aleatoria al inicio del algoritmo.\n",
"- **set_new_centroids**: Actualizar los centroides en cada iteración.\n",
"- **normalizeX**: Normalizamos nuestros datos para que no existan sesgos por las magnitudes de los datos.\n",
"- **get_distance**: Obtener la distancia de cada punto a cada centroide.\n",
"- **assign_clusters**: Se basa en las distancias para asignar cada punto al grupo que corresponde según la distancia mínima.\n",
"- **fit**: Aquí se unen la mayor parte de los métodos para seguir el algoritmo.\n",
"- **predict**: Regresa la clasificación de cierto punto o grupo de puntos basado en el entrenamiento del algoritmo.\n",
"- **plot_iter**: Nos permite observar de manera gráfica el comportamiento de nuestro algoritmo."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class myKmeans:\n",
" \"\"\"\n",
" Este es nuestro propio clasificador no supervisado kmeans.\n",
" \"\"\"\n",
" import numpy as np\n",
" import random\n",
" import matplotlib.pyplot as plt\n",
" \n",
" def __init__(self): \n",
" self.k=0\n",
" self.dimensions=0\n",
" self.centroids=np.array([])\n",
" self.clusters=np.array([], dtype=int)\n",
" self.colors=['red','blue','green','orange','cyan','black', 'magenta']\n",
" \n",
" def set_k(self, k):\n",
" self.k=k\n",
" \n",
" def get_k(self):\n",
" return self.k\n",
" \n",
" def set_dimensions(self, X):\n",
" self.dimensions=X.shape[1]\n",
" \n",
" def get_dimensions(self):\n",
" return self.dimensions\n",
" \n",
" def set_random_centroids(self):\n",
" c=np.zeros([self.k, self.dimensions])\n",
" for rows in range(self.k):\n",
" for cols in range(self.dimensions):\n",
" c[rows, cols]= self.random.random()\n",
" self.centroids=c\n",
" \n",
" def set_new_centroids(self, X):\n",
" c=np.zeros([self.k, self.dimensions])\n",
" for rows in range(self.k):\n",
" for cols in range(self.dimensions):\n",
" c[rows, cols]= self.np.mean(X[self.clusters==rows,cols])\n",
" self.centroids=c\n",
" \n",
" def get_centroids(self):\n",
" return self.centroids\n",
" \n",
" def normalize(self, feature):\n",
" return (feature-min(feature))/(max(feature)-min(feature))\n",
" \n",
" def normalizeX(self, X):\n",
" X=self.np.array(X)\n",
" for index in range(self.dimensions):\n",
" X[:,index]=self.normalize(X[:,index])\n",
" return X\n",
"\n",
" def get_distance(self, X):\n",
" dist=np.zeros([X.shape[0], self.centroids.shape[0]])\n",
" for c in range(self.centroids.shape[0]):\n",
" for p in range(X.shape[0]):\n",
" dist[p,c]=self.euclidean_distance(X[p,:],self.centroids[c,:])\n",
" return dist\n",
" \n",
" def euclidean_distance(self, point, centroid):\n",
" euclideanDistance=0\n",
" for dim in range(self.dimensions):\n",
" euclideanDistance+=(point[dim]-centroid[dim])**2\n",
" return euclideanDistance**(1/2)\n",
" \n",
" def assign_clusters(self, distances):\n",
" prev_clusters=self.clusters.copy()\n",
" self.clusters=np.zeros(distances.shape[0], dtype=int)\n",
" for index in range(distances.shape[0]):\n",
" self.clusters[index]=np.where(min(distances[index,:])==distances[index,:])[0][0]\n",
" return self.np.array_equal(self.clusters, prev_clusters)\n",
" \n",
" def get_clusters(self):\n",
" return self.clusters\n",
" \n",
" def fit(self, X, k=2, iterations=20, verbose=False):\n",
" self.set_k(k)\n",
" self.set_dimensions(np.array(X))\n",
" self.set_random_centroids()\n",
" X=self.normalizeX(X)\n",
" for iteration in range(iterations):\n",
" print('Iteración', iteration)\n",
" if iteration!=0:\n",
" self.set_new_centroids(X)\n",
" distances=self.get_distance(X)\n",
" same=self.assign_clusters(distances)\n",
" if verbose:\n",
" self.plot_iter(X)\n",
" if same==True:\n",
" print('Los centroides no han cambiado')\n",
" return None\n",
" print('Máximo de iteraciones alcanzado')\n",
" return None\n",
" \n",
" def predict_clusters(self, distances):\n",
" predicted_clusters=np.zeros(distances.shape[0], dtype=int)\n",
" for index in range(distances.shape[0]):\n",
" predicted_clusters[index]=np.where(min(distances[index,:])==distances[index,:])[0][0]\n",
" return predicted_clusters\n",
" \n",
" def predict(self, X):\n",
" distances=self.get_distance(self.normalizeX(X))\n",
" return self.predict_clusters(distances)\n",
" \n",
" def set_colors(self):\n",
" while self.get_k()>len(self.get_colors()):\n",
" allColors=[k for k, i in mcolors.cnames.items()]\n",
" selected_color=allColors[random.randint(0,len(allColors))]\n",
" if selected_color not in np.array(self.get_colors()):\n",
" self.colors.append(selected_color)\n",
" \n",
" def get_colors(self):\n",
" return self.colors\n",
" \n",
" def plot_iter(self, X):\n",
" self.set_colors()\n",
" self.plt.figure()\n",
" for index in range(self.k):\n",
" self.plt.scatter(X[index==self.clusters,0],X[index==self.clusters,1], color=self.colors[index])\n",
" self.plt.scatter(self.centroids[index,0], self.centroids[index,1], marker='s', color='yellow')\n",
" self.plt.pause(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ya que tenemos nuestro clasificador procederemos a crear una función que nos permita observar, de forma gráfica, la manera en que están organizados los grupos separando las clases por colores."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.colors as mcolors\n",
"import random\n",
"\n",
"def plot_classes(X, y):\n",
" plt.figure()\n",
" classes=np.unique(y)\n",
" colors=['red','blue','green','orange','cyan'] # Si necesitas más colores puedes agregarlos.\n",
" #allColors=[k for k, i in mcolors.cnames.items()] # Si ejecutas esta línea de código puedes \n",
" # obtener todos los colores disponibles.\n",
" for index in np.unique(y):\n",
" plt.scatter(X[index==y,0], X[index==y,1], color=colors[np.where(index==np.unique(y))[0][0]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Preparar\n",
"Antes de leer nuestros datos para iniciar con la clasificación, crearemos una función que nos entregue 4 subconjuntos que llamaremos **X_train**, **y_train**, **X_test** y **y_test**. Para entrenar y probar nuestro clasificador.
\n",
"**Nota**: El subconjunto **y_train** no se utilizará debido a que es un algoritmo no supervisado, por lo cual no requiere de los valores etiquetados para clasificar el subconjunto **X_train**. Sin embargo, **y_test** se usará con propósitos de evaluación del desempeño del algoritmo."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def split_train_test(X, y, division=0.8):\n",
" index=np.random.permutation(np.arange(0,X.shape[0]))\n",
" split=int(division*X.shape[0])\n",
" X_train=X[index[:split],:]\n",
" X_test=X[index[split:]]\n",
" y_train=y[index[:split]]\n",
" y_test=y[index[split:]]\n",
" return X_train, X_test, y_train, y_test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Analizar\n",
"A continuación leemos el archivo y guardamos nuestro valor real de clasificación en la variable **y**. De igual manera, guardamos nuestros datos en la variable **X**. \n",
"Dado que no podemos observar graficamente los puntos en dimensiones muy grandes, en este ejercicio lo simplificaremos a 2 dimensiones para observarlo de una mejor manera.
\n",
"**Nota**: Puede realizarse el cálculo de clases a dimensiones más grandes pero la visualización siempre será en 2D.\n",
"
\n",
"Desplegamos una vista previa de los datos."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
" 0 1 2 3\n",
"count 150.000000 150.000000 150.000000 150.000000\n",
"mean 5.843333 3.054000 3.758667 1.198667\n",
"std 0.828066 0.433594 1.764420 0.763161\n",
"min 4.300000 2.000000 1.000000 0.100000\n",
"25% 5.100000 2.800000 1.600000 0.300000\n",
"50% 5.800000 3.000000 4.350000 1.300000\n",
"75% 6.400000 3.300000 5.100000 1.800000\n",
"max 7.900000 4.400000 6.900000 2.500000"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.describe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Procedemos por dividir **X** y **y** en nuestros subconjuntos de entrenamiento y prueba."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dimensión de X_train: (120, 2)\n",
"Dimensión de X_test: (30, 2)\n",
"Dimensión de y_train: (120,)\n",
"Dimensión de y_test: (30,)\n"
]
}
],
"source": [
"X_train, X_test, y_train, y_test = split_train_test(X, y, division=0.8)\n",
"print(\"Dimensión de X_train:\", X_train.shape)\n",
"print(\"Dimensión de X_test:\", X_test.shape)\n",
"print(\"Dimensión de y_train:\", y_train.shape)\n",
"print(\"Dimensión de y_test:\", y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Es importante mencionar que en nuestra función **split_train_test** hicimos la división en un 80 - 20. \n",
"80% de los datos son para el entrenamiento (120 datos) y 20% son para prueba (30 datos).
\n",
"**Nota**: Se utilizó una permutación aleatoria de los índices para que siempre se obtengan distintos subconjuntos de entrenamiento y prueba. Esto quiere decir que apartir de este punto tus resultados pueden variar un poco de los que aquí se muestran."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Entrenar\n",
"Ahora creamos y entrenamos el clasificador. \n",
"Como argumentos colocamos el conjunto de datos **X**, un número de grupos **k** igual a 3, 20 iteraciones como máximo y ponemos el valor *True* en **verbose** para permitir que nuestra función grafique los resultados de cada iteración."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Iteración 0\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Los centroides no han cambiado\n"
]
}
],
"source": [
"clf=myKmeans()\n",
"clf.fit(X_train, k=3, iterations=20, verbose=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Se puede observar que los centroides, en color amarillo, se van moviendo en cada iteración junto con el resto de los grupos. Al final se muestra por cuál de los dos criterios de paro se detuvo el algoritmo (este proceso se puede encontrar en el método **fit**).
\n",
"## Probar\n",
"Después, realizamos la predicción de nuestro conjunto de prueba para evaluar la capacidad de clasificación de nuestro algoritmo."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"predictions=clf.predict(X_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Para tener una visualización del conjunto real y el conjunto evaluado utilizamos la función **plot_classes** dos veces; la primera con nuestro valor conocido en **y**, la segunda con nuestros valores predichos por el clasificador."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plot_classes(X_test,predictions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"De forma gráfica puede verse que los grupos se asemejan, pero para observar de forma numérica los resultados creamos una función que nos entregue la precisión de la clasificación. Llamaremos **get_accuracy** a dicha función.
\n",
"**Nota**: Uno de los inconvenientes de los métodos de aprendizaje no supervisado es que etiquetan los datos de manera aleatoria, así que se ejecutas el algoritmo varias veces es probable que cada clase sea nombrada de manera distinta.
\n",
"Para resolver el problema anterior, dentro de la función **get_accuracy** haremos que se determine cuál es la clasificación que mejor se acopla a nuestro valor conocido **y_test** al realizar permutaciones en las etiquetas que se asignan a cada clase y determinando cuál nos entrega un mejor valor de precisión. Ésto no se realizaría en un problema en el que no tenemos datos etiquetados ya que es indistinto el nombre de las clases si no se tiene un etiquetado previo."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"from itertools import permutations \n",
"\n",
"def get_accuracy(y, predictions):\n",
" allPermutations=np.array(list(permutations(np.unique(y))))\n",
" acc=[]\n",
" for perm in allPermutations:\n",
" classes=np.arange(0,y.shape[0])\n",
" for index in range(classes.shape[0]):\n",
" classes[index]=np.where(y[index]==perm)[0][0]\n",
" acc.append(np.sum(classes==predictions))\n",
" acc=np.array(acc)\n",
" bestAccIndex=np.where(max(acc)==acc)[0][0]\n",
" return dict(zip(np.arange(0,allPermutations.shape[1]),allPermutations[bestAccIndex])), acc[bestAccIndex]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Guardamos las clases y precisión que entrega la función **get_accuracy**. Posteriormente imprimimos en pantalla el orden de los grupos y la precisión obtenida."
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Orden de los grupos: {0: 'Iris-versicolor', 1: 'Iris-setosa', 2: 'Iris-virginica'}\n",
"Aciertos: 23\n"
]
}
],
"source": [
"classes_dict, acc = get_accuracy(y_test, predictions)\n",
"print('Orden de los grupos:', classes_dict)\n",
"print('Aciertos:', acc)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Por último, creamos nuesta variable **predicted_classes**, la cuál, es una traducción de los valores numéricos de **predictions** al nombre real de nuestras clases. \n",
"Desplegamos los resultados y el porcentaje de precisión. \n",
"Adicionalmente, creamos un _DataFrame_ de la librería **pandas** para visualizar nuestros datos de una mejor manera. Colocamos el valor esperado **y_test** en la columna _Resultado esperado_, el valor predicho **predicted_classes** en la columna _Resutaldo obtenido_ y si la predicción fue acertada en la columna _Correcto_."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Aciertos: 23 / 30 - Porcentaje: 76.66666666666667 %\n"
]
},
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Resultado esperado
\n",
"
Resultado obtenido
\n",
"
Correcto
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Iris-versicolor
\n",
"
Iris-versicolor
\n",
"
True
\n",
"
\n",
"
\n",
"
1
\n",
"
Iris-setosa
\n",
"
Iris-setosa
\n",
"
True
\n",
"
\n",
"
\n",
"
2
\n",
"
Iris-setosa
\n",
"
Iris-setosa
\n",
"
True
\n",
"
\n",
"
\n",
"
3
\n",
"
Iris-versicolor
\n",
"
Iris-versicolor
\n",
"
True
\n",
"
\n",
"
\n",
"
4
\n",
"
Iris-virginica
\n",
"
Iris-versicolor
\n",
"
False
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Resultado esperado Resultado obtenido Correcto\n",
"0 Iris-versicolor Iris-versicolor True\n",
"1 Iris-setosa Iris-setosa True\n",
"2 Iris-setosa Iris-setosa True\n",
"3 Iris-versicolor Iris-versicolor True\n",
"4 Iris-virginica Iris-versicolor False"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"predicted_classes=[classes_dict[_] for _ in predictions]\n",
"results=pd.DataFrame(data=np.transpose([y_test, predicted_classes, y_test==predicted_classes]),\n",
" columns=['Resultado esperado','Resultado obtenido','Correcto'])\n",
"print('Aciertos:',acc,'/',y_test.shape[0],'- Porcentaje:',100*acc/y_test.shape[0],'%')\n",
"results.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como podemos ver, se tiene un valor aceptable de exactitud, y los primeros valores de predicción son idénticos a los que esperábamos. En caso de que un valor no corresponda, en la columna **Correcto** aparecería el valor **False**.
\n",
"**Nota**: Es probable que cuando implementes el algoritmo por tu cuenta encuentres valores similares a los que aquí se muestran, aunque podrían existir variaciones debido a que creamos grupos de entrenamiento y prueba aleatorios al inicio del algoritmo."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Usar\n",
"Una vez que revisamos el proceso completo para el uso de nuestra librería kmeans, podemos utilizarla en cualquier conjunto de datos deseado.
\n",
"Para usar otra base de datos, elige una dentro de tu carpeta **database** e importalo por medio del comando **read_csv** de la librería **pandas** como lo hemos hecho hasta ahora. \n",
"A partir de ahí sigue los mismos pasos desde el inicio de este capítulo ya que realizamos el algoritmo de manera general para que pueda ser usado con cualquier conjunto de datos.
\n",
"Como consideraciones generales, utiliza conjuntos de datos que no tengan una cantidad muy alta de grupos, debido a que será dificil visualizar las clases. \n",
"\n",
"Por último, intenta crear un conjunto de datos que sea visiblemente separable para que compruebes la efectividad del algoritmo. Puedes crear un conjunto de datos sintético con dos, tres o más grupos visiblemente separables y probar el algoritmo que acabas de implementar."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}