Laboratorio 3 - Parte 1. GMM para clasificación y clustering#
!wget -nc --no-cache -O init.py -q https://raw.githubusercontent.com/jdariasl/Intro_ML_2025/master/init.py
import init; init.init(force_download=False); init.get_weblink()
from local.lib.rlxmoocapi import submit, session
import inspect
session.LoginSequence(endpoint=init.endpoint, course_id=init.course_id, lab_id="L03.01", varname="student");
#configuración del laboratorio
# Ejecuta esta celda!
from Labs.commons.utils.lab3 import *
_ = part_1()
Ejercicio 1: Contextualización del problema#
A continuación se leen los datos de un problema de clasificación. El problema corresponde a la clasificación de dígitos escritos a mano. Los datos fueron preprocesados para reducir el número de características y solo se van usar los digitos 0 al 4. La técnica usada para realizar la reducción de dimensión es PCA, la cual ya fue usada en el curso de Modelos y Simulación I, y será nuevamente analizada en detalle más adelante en este curso. Al ejecutar la celda de código, también se podrá visualizar una muestra de los datos usados.
digits = load_digits(n_class=5)
#--------- preprocesamiento--------------------
pca = PCA(0.99, whiten=True)
data = pca.fit_transform(digits.data)
#---------- Datos a usar ----------------------
x = data[:-1]
y = digits.target[:-1]
plot_digits(digits.data)
#Echemos un vistazo a la distribución de las clases
from local.lib.util import plot_samples_per_class
plot_samples_per_class(y)
Para poder realizar la validación de nuestro algoritmo, realizaremos una partición de los datos. Como sabemos, existen dos principales alternativas:
En el siguiente ejercicio, instancie dos objetos de cada una de las clases de objetos listadas antes. En los dos casos se desea repetir el experimento 4 veces. Cuando instancie los objetos, defina los parámetros para que, aunque las técnicas de validación sean diferentes, los resultados sean estadísticamente equivalentes. Ajuste la semilla de los generadores en 0 para que los resultados sean reproducibles.
#ejercicio de código
from sklearn.model_selection import KFold
from sklearn.model_selection import ShuffleSplit
def get_cv_objects():
cv1 = KFold...
cv2 = ShuffleSplit...
return cv1, cv2
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T1');
Ejercicio 2: Mezclas de gaussinas#
En la siguiente celda vamos a crear una clase para definir un modelo de clasificación basado en el modelo GMM. Para ello vamos a usar la función GaussianMixture de sklearn. Consultar aquí: http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html
Los parámetros que deben ser tenidos en cuenta para en el entrenamiento del modelo, ya están definidos en el constructor de la clase. Con el propósito de seguir la estructura de los modelos definidos en sklearn, deben completar tres métodos:
train
predict
predict_proba
En el notebook, ya se encuentra cargada la libreria que vamos a usar:
from sklearn.mixture import GaussianMixture
#ejercicio de código
def get_GMM_classifier(n_components,covariance_type,max_iter=100,n_init=5,init_params='kmeans',random_state=0):
"""Función para instanciar un modelo de clasificación basado en Mezclas de Gaussianas
retorna: objeto de la clase GMMClassifierTrain
"""
class GMMClassifier():
def __init__(self, n_components=1, covariance_type='full', max_iter=100, n_init=1, init_params='kmeans', random_state=None):
self.n_components = n_components
self.covariance_type = covariance_type
self.max_iter = max_iter
self.n_init = n_init
self.init_params = init_params
self.random_state = random_state
self.models = {}
self.class_labels =[]
def fit(self,X,Y):
"""
retorna: almacena el diccionario de modelos entrenados y las etiquetas de
las clases. Las etiquetas de las clases se usan como keys en el diccionario
"""
...
return self
def predict(self,X):
"""
retorna: matriz de predicciones de tamaño (n_muestras,)
"""
self.models = ...
self.class_labels = ...
return self
def predict(self,X):
"""
retorna: matriz de predicciones de tamaño (n_muestras,)
"""
...
return Yest
def predict_proba(self,X):
"""
retorna: matriz de probabilidades de tamaño (n_muestras, n_clases)
"""
...
return Yproba
return GMMClassifier(
n_components = n_components,
covariance_type = covariance_type,
max_iter = max_iter,
n_init = n_init,
init_params=init_params,
random_state=random_state
)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T2');
#@title Pregunta Abierta
#@markdown En sus palabras, ¿por qué debemos entrenar un modelo por cada clase?
respuesta = "" #@param {type:"string"}
Ejercicio 3: Experimentos#
Con el código completado, vamos a realizar experimentos. Complete la siguiente función para poder obtener los resultados de los experimentos. Tenga en cuenta lo siguiente:
Retornar errores de entrenamiento y pruebas
Retornar intervalos de confianza (desviacion estandar) para cada una de las configuraciones de hiperparámetros
En el código ya se sugiere la metodologia de validación (usando 4 folds) y se incluye, como es usual, el standard scaler para normalizar los datos.
#ejercicio de código
def experimentar(X, Y, covariance_types,num_components):
"""función que realiza experimentos con el GMM
X: matriz con las muestras de entrada
Y: matriz de numpy con las etiquetas de salida
covariance_types: list[str] con las matrices de covarianza a probar
num_components: list[int] con el numero de componente a probar
retorna: dataframe con:
- matriz de covarianza
- numero de componentes
- eficiencia de entrenamiento
- desviacion de estandar eficiencia de entrenamiento
- eficiencia de prueba
- desviacion estandar eficiencia de prueba
"""
# Definir metodología de validación
skf, _ = get_cv_objects()
resultados = pd.DataFrame()
idx = 0
for cov_tipo in covariance_types:
for M in num_components:
## para almacenar los errores intermedios
EficienciaTrain = []
EficienciaVal = []
for train, test in skf.split(X, Y):
Xtrain = X[train,:]
Ytrain = Y[train]
Xtest = X[test,:]
Ytest = Y[test]
#Normalizamos los datos
scaler = StandardScaler()
scaler.fit(Xtrain)
Xtrain= scaler.transform(Xtrain)
Xtest = scaler.transform(Xtest)
#Haga el llamado a la función para crear y entrenar el modelo usando los datos de entrenamiento
gmms = ...
#Validación
Ytrain_pred = ...
Yest = ...
#Evaluamos las predicciones del modelo con los datos de test
EficienciaTrain.append(np.mean(Ytrain_pred.ravel() == Ytrain.ravel()))
EficienciaVal.append(np.mean(Yest.ravel() == Ytest.ravel()))
resultados.loc[idx,'matriz de covarianza'] = cov_tipo
resultados.loc[idx,'numero de componentes'] = M
resultados.loc[idx,'eficiencia de entrenamiento'] =
resultados.loc[idx,'desviacion estandar entrenamiento'] =
resultados.loc[idx,'eficiencia de prueba'] =
resultados.loc[idx,'desviacion estandar prueba'] =
idx= idx +1
print("termina para", cov_tipo, M)
return (resultados)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T3');
Ahora vamos a ejecutar nuestros experimentos:
matrices = ['full', 'tied', 'diag', 'spherical']
componentes = [1,2,3]
resultados = experimentar(x, y, matrices, componentes)
# para ver la tabla
resultados
#@title Pregunta Abierta
#@markdown ¿A qué corresponde el parámetro init_params de la función GaussianMixture y cuál fue el valor usado en nuestros experimentos?
respuesta = "" #@param {type:"string"}
Ejercicio 4 Kmeans y experimentos#
Como sabemos, el modelo GMM puede usarse también para resolver problemas no supervisados. Aunque el dataset que estamos usando contiene etiquetas, vamos a usarlo como si el problema fuera de tipo no supervisado. Usaremos el modelo GMM para resolver el problema y lo compararemos con el algoritmo K-means.
Consultar todo lo relacionado al llamado del método KMeans de la librería scikit-learn en el siguiente enlace: http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html.
En el notebook, ya se encuentra cargada la libreria:
from sklearn.cluster import KMeans
Por ello vamos a implementar una función que defina y entre los modelos de clustering KMeans y GMM. Los hiparámetros que se deben usar para instanciar los modelos ya están definidos en la función.
#ejercicio de código
def entrenar_clustering(numero_clusters,Xtrain,max_iter=100,n_init=5,random_state=0):
"""función que realiza experimentos no supervisados: GMM y Kmeans
numero_clusters: int número de clusters a usar en los modelos
Xtrain: matriz con las muestras de entrenamiento
"""
#Define el modelo de clustering basado en kmeans con el conjunto de hiperparámetros apropiado
kmeans =
#¿qué metodo debes llamar para entrenar kmeans?
kmeans
# Ahora usa el modelo GMM para hacer clustering (Use matriz de covarianza 'tied')
gmm = GaussianMixture(...)
#Entrena el gmm
gmm
return kmeans, gmm
Como sabemos, la evaluación de los algoritmos de agrupamiento no supervisados se hace a partir de medidas que evalúan la cohesión de los grupos encontrados, como el coeficiente Silhouette. Creemos una función que realice la experimentación para diferente número de grupos de acuerdo con dicha medida.
#ejercicio de código
from sklearn.metrics import silhouette_score
def experimentar_clustering(numero_clusters,X):
"""función que realiza experimentos no supervisados: GMM y Kmeans
numero_clusters: list[int] numero cluster para realizar experimentos
X: matriz con las caractersiticas
Y: matriz de numpy con etiquetas
retorna: dataframe con:
- numero_clusters
- el error de entrenamiento
- desviacion de estandar del error entrenamiento
- error de prueba
- desviacion estandar eror de prueba
"""
_, skf = get_cv_objects()
resultados = pd.DataFrame()
idx = 0
for n_cluster in numero_clusters:
## para almacenar los errores intermedios
DesempenoTrain_kmeans = []
DesempenoVal_kmeans = []
DesempenoTrain_gmm = []
DesempenoVal_gmm = []
EficienciaVal = []
for train, test in skf.split(X, X):
Xtrain = X[train,:]
Xtest = X[test,:]
#Normalizamos los datos
scaler = StandardScaler()
scaler.fit(Xtrain)
Xtrain= scaler.transform(Xtrain)
Xtest = scaler.transform(Xtest)
kmeans, gmm = ...
#¿Cómo determinar los grupos a los que fueron asignadas las muestras de entrenamiento?
Gtrain_pred_k = kmeans...
Gtrain_pred_g = gmm...
#¿Cómo determinar los grupos a los que fueron asignadas las muestras de prueba?
Gest_k = kmeans...
Gest_g = gmm...
#Evaluamos las predicciones del modelo con los datos de test
DesempenoTrain_kmeans.append(silhouette_score(Xtrain, Gtrain_pred_k))
DesempenoVal_kmeans.append(silhouette_score(Xtest, Gest_k))
DesempenoTrain_gmm.append(silhouette_score(Xtrain, Gtrain_pred_g))
DesempenoVal_gmm.append(silhouette_score(Xtest, Gest_g))
resultados.loc[idx,'numero de clusters'] = n_cluster
resultados.loc[idx,'Silhouette de entrenamiento kmeans'] = np.mean(np.stack(DesempenoTrain_kmeans))
resultados.loc[idx,'Silhouette de test kmeans'] = np.mean(np.stack(DesempenoVal_kmeans))
resultados.loc[idx,'Silhouette de entrenamiento gmm'] = np.mean(np.stack(DesempenoTrain_gmm))
resultados.loc[idx,'Silhouette de test gmm'] = np.mean(np.stack(DesempenoVal_gmm))
idx= idx +1
print("termina para", n_cluster)
return (resultados)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T4');
# ejecuta los experimentos y ve los resultados
resultados_clustering = experimentar_clustering([3,5,6,8],x)
resultados_clustering
#@title Pregunta Abierta
#@markdown ¿Qué método podría usar para determinar el grado de pertenencia de una muestra a cada grupo según el modelo GMM ?
respuesta = "" #@param {type:"string"}
#@title Pregunta Abierta
#@markdown ¿Es el número de grupos óptimo coherente con el número de clases en el problema?
respuesta = "" #@param {type:"string"}
Teniendo en cuenta que los métodos que hemos usados son no supervisados, el orden asignado a los grupos no tiene porqué coincidir con las etiquetas de clase. Vamos a graficar los centroides de las clases para ver con cuál de las clases coinciden.
# ejercicio de código
n_cluster = ... #Escoja un número de clusters de acuerdo con el resultado anterior
_, skf = get_cv_objects()
train, test = next(skf.split(x, x))
Xtrain = x[train,:]
Xtest = x[test,:]
#Normalizamos los datos
scaler = StandardScaler()
scaler.fit(Xtrain)
Xtrain= scaler.transform(Xtrain)
Xtest = scaler.transform(Xtest)
modelo = ....fit(Xtrain) #Seleccione uno de los dos modelos de clustering
centroids = pca.inverse_transform(....) #Extraiga los centroides del modelo y haga la transformación inversa de PCA
fig, ax = plt.subplots(1, n_cluster, figsize=(4, 8),subplot_kw=dict(xticks=[], yticks=[]))
fig.subplots_adjust(hspace=0.05, wspace=0.05)
for i, axi in enumerate(ax.flat):
im = axi.imshow(centroids[i].reshape(8, 8), cmap='binary')
im.set_clim(0, 16)
#@title Pregunta Abierta
#@markdown ¿Corresponden los centroides a las clases del problema?
respuesta = "" #@param {type:"string"}