Laboratorio 5 - Parte 2. Máquinas de Vectores de Soporte#
!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="L05.02", varname="student");
from Labs.commons.utils.lab5 import *
_, dataset = part_2()
Ejercicio 1: Limipiar base de datos y completar código#
En este ejercicio usaremos la regresión por vectores de soporte para resolver el problema de regresión de la base de datos AirQuality (https://archive.ics.uci.edu/ml/datasets/Air+Quality). Tener en cuenta que vamos a usar solo 2000 muestras.
En primera instancia vamos a transformar la matriz en un dataframe, para poderlo procesar de manera mas sencilla. Se crea una columna por cada variable que obtenemos.
dataset_df = pd.DataFrame(dataset, columns = [f'col_{c}' for c in range (1,14)])
Para esta base de datos vamos a realizar una limpieza de datos. Para ello vamos a completar la siguiente función para realizar:
Remover todos los registros cuya variable objetivo es faltante (missing Value). Estos registros están marcados como -200, es decir, donde haya un valor -200 eliminaremos el registro.
imputar los valores perdidos/faltantes en cada una de las características, vamos a usar el valor medio de la propia característica (estimado con las muestras que si cuentan con información en dicha variable) y lo usaremos para llenar los huecos. (será de utilidad conocer SimpleImputer)
Verificar si quedaron valores faltantes
retornar X (12 primeras columnas) y Y(la 13 columna).
información de utilidad:
Aca puede ser de utilidad revisar la sesión extra.
Para transformar columnas de pandas a arreglos de numpy se puede usar
.iloc
/.loc
y ..values
, por ejemplo para devolver una matriz con los valores de las primeras dos columnas es posible hacerlo asi:dataset_df.iloc[: , 0:2].values
odataset_df.loc[: , ['col_1', 'col_2']].values
Para cambiar valores faltantes, podemos usar la librería sklearn
# ejercicio de codigo
def clean_data(df):
"""función que limpia el dataset y obtiene X y Y
df: es un pandas dataframe
retorna:
X: una matriz numpy con los valores a usar como conjunto de datos
de entrada
Y una matriz numpy con los valores usados como conjunto de datos
de salida
"""
# se copia el df para evitar cambios sobre el objeto
database = df.copy()
##Verificar
pct_valores_faltantes = (database==-200).mean()
print(",".join([f"% VF en {a}: {pct_valores_faltantes[a]*100:.2f}% " for a in pct_valores_faltantes.index]))
# identificar y remover del data frame las muestras cuya salida es un valor faltante
database = ...
# Imprimir el número de muestras removidas
print(...)
print ("\nDim de la base de datos sin las muestras con variable de salida perdido "+ str(np.shape(database)))
##Imputar los valores de las características en el dataframe que tienen datos faltantes
database = ...
print ("Imputación finalizada.\n")
##Verificar
pct_valores_faltantes = (database==-200).mean()
print(",".join([f"% VF en {a}: {pct_valores_faltantes[a]*100:.2f}% " for a in pct_valores_faltantes.index]))
if(pct_valores_faltantes.max() != 0):
print ("Hay valores perdidos")
else:
print ("No hay valores perdidos en la base de datos. Ahora se puede procesar")
X = database.iloc[:,0:12].values
Y = database.iloc[:,12:13].values
return (X,Y)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T1');
Ahora usemos la función para tener nuestras variables X, Y
X,Y = clean_data(dataset_df)
Ejercicio 2: Experimentar SVM para regresión#
Ahora vamos a crear una función para experimentar con la maquina de soporte vectorial. Para ello debemos hacer lo siguiente:
Revisar la documentación de la clase de objetos que vamos a usar (ver sklearn.
Vamos a evaluar el efecto de tres hiperparámetros del SVR: kernel, gamma y el parámetro de regularización, por lo tanto debemos comprender la relación con el modelo y su efecto.
El número máximo de iteraciones debe ser ajustado a 200
Utilizar la metodología cross-validation con 4 folds.
Usar normalización de datos estandar implementada por sklearn
Extraer los vectores de soporte (observe los atributos del modelo SVR de sklearn). Recuerde que estos atributos son accesibles, una vez el modelo es entrenado
Utilizar la métrica para calcular el MAPE (usar sklearn).
#ejercicio de código
def experiementarSVR(x, y, kernels, gammas,params_reg):
"""función que realizar experimentos sobre una SVM para regresión
x: numpy.Array, con las muestras del problema
y: numpy.Array, con la variable objetivo
kernels: List[str], lista con valores a pasar
a sklearn correspondiente al kernel de la SVM
gammas: List[float], lista con los valores a pasar a
sklearn correspondientes al valor de los coeficientes para usar en el
kernel
params_reg: List[float], lista con los valores a pasar a
sklearn para ser usados como parámetro de regularización
retorna: pd.Dataframe con las siguientes columnas:
- 3 columnas con los tres parametros: kernel, gamma, param de regularizacion
- porcentaje de error absoluto medio en el cojunto test (promedio de los 4 folds)
- intervalo de confianza del porcentaje de error absoluto medio en el cojunto test
(desviacion estandar de los 4 folds)
- # de Vectores de Soporte promedio para los 4 folds
- % de Vectores de Soporte promedio para los 4 folds (0 a 100)
"""
idx = 0
kf = ...(n_splits=...)
# crear una lista con la combinaciones de los elementos de cada list
kernels_gammas_regs = list(itertools.product(kernels, gammas, params_reg))
resultados = pd.DataFrame()
for params in kernels_gammas_regs:
kernel, gamma, param_reg = params
print("parametros usados", params) # puede usar para ver los params
errores_test = []
pct_support_vectors = []
num_support_vectors = []
for train_index, test_index in kf...(...):
X_train, X_test = x[train_index], x[test_index]
y_train, y_test = y[train_index], y[test_index]
# normalizar los datos
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
#Definir y entrenar el modelo
svr = ...
# Validación del modelo
ypred = svr.predict(X=...)
# MAPE de prueba
errores_test.append(...)
# Encontrar el número de vectores de soporte y el calcular el porcentaje que representan
num_vs =
pct_vs =
pct_support_vectors.append(pct_vs)
num_support_vectors.append(num_vs)
resultados.loc[idx,'kernel'] = kernel
resultados.loc[idx,'gamma'] = gamma
resultados.loc[idx,'param_reg'] = param_reg
resultados.loc[idx,'error de prueba (promedio)'] = np.mean(...)
resultados.loc[idx,'error de prueba (intervalo de confianza)'] = np.std()
resultados.loc[idx,'# de vectores de soporte'] = np.mean(...)
resultados.loc[idx,'% de vectores de soporte'] = np.mean(...)
idx+=1
return (resultados)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T2');
Para entrenar vamos a ignorar las dos primeras variables, estas corresponden a valores de fechas.
# vamos a realizar los experimentos
resultadosSVR = experiementarSVR(x =X[:,2:],y=Y,
kernels=['linear', 'rbf'],
gammas = [0.01,0.1],
params_reg = [0.1, 1.0,10]
)
# ver los 5 primeros resultados
resultadosSVR.sort_values('error de prueba (promedio)',ascending=True).head(5)
#@title Pregunta Abierta
#@markdown ¿Cual es el objetivo de las las funciones kernel? Contestar dentro del contexto de las máquinas de sporte vectorial
respuesta = "" #@param {type:"string"}
#@title Pregunta Abierta
#@markdown Existe alguna relación entre el porcentaje de vectores de soporte y error esperado?
respuesta = "" #@param {type:"string"}
Para analizar los resultados vamos a crear dos graficas para el mejor modelo encontrado:
vamos a graficar en el eje x el valor real, en el eje y el valor predicho. El modelo ideal deberia ser una recta que recuerda la identidad
en el eje x vamos a dejar un valor incremental y con colores vamos a diferenciar entre el valor real y el valor predicho
# dividir el conjunto
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
#predicciones
# OJO: Completar!
Ypred = predict_svr(X_train,y_train,X_test,kernel = ..., )
# plots
f, ax = plt.subplots(ncols=2, sharex=False, sharey=False, figsize = (22,6))
ax[0].scatter(y_test, Ypred)
ax[0].set_xlabel('valor real', fontdict = {'fontsize': 12})
ax[0].set_ylabel('valor predicho', fontdict = {'fontsize': 12})
ax[1].plot(y_test, label = 'valor real', color = 'b', alpha = 0.7)
ax[1].plot(Ypred, label = 'valor predicho', color = 'r', alpha = 0.5)
ax[1].legend()
ax[1].set_ylabel('Humedad relativa', fontdict = {'fontsize': 12})
plt.show()
#@title Pregunta Abierta
#@markdown usando las anteriores graficas, ¿como calificaría el modelo de manera cualitativa?.
respuesta = "" #@param {type:"string"}
Ejercicio 3: Experimentar SVM para clasificación#
En este ejercicio vamos a volver a resolver el problema de clasificación de dígitos. Vamos usar solo 5 clases y realizaremos un pre-procesamiento:
Mediante PCA reduciremos la dimensionalidad de los datos
Vamos a convertir el problema en uno de cuatro clases (diferenciar entre 0, 1, 2 y el resto)
Xcl, Ycl = load_digits(n_class=5,return_X_y=True)
#--------- preprocesamiento--------------------
pca = PCA(0.99, whiten=True)
Xcl = pca.fit_transform(Xcl)
# cambiar problema de clases
unique, counts = np.unique(Ycl, return_counts=True)
print("distribución original (claves las etiquetas, valores el número de muestras): \n", dict(zip(unique, counts )))
Ycl = np.where(np.isin(Ycl, [0,1,2]), Ycl, 3)
unique, counts = np.unique(Ycl, return_counts=True)
print("Nueva distribución (claves las etiquetas, valores el número de muestras): \n", dict(zip(unique, counts )))
Ahora vamos a crear la función para experimentar con la máquina de soporte vectorial. Los parámetros de la prueba y sus requerimientos se listan a continuación:
Revisar la documentación correspondiente al modelo sklearn
Realizaremos pruebas para ajustar tres hiperparámetros del SVC: kernel, gamma y el parametro de regularización.
El número máximo de iteraciones será ajustado a 500
Seleccionar una metodología de cross-validation con 4 folds adecuada para la distribución de datos.
Usar normalización de datos estandar implementada por sklearn
Extraer los vectores de soporte (observe los atributos del modelo SVC de sklearn). Recuerde que estos atributos son accesibles una vez el modelo es entrenado
vamos a probar la dos estragegias multiclase básicas del SVC:
Utilizar como medida de desempeño el score de exactitud de sklearn.
#ejercicio de código
def experiementarSVC(x, y, kernels, gammas,params_reg, estrategia = 'ovo'):
"""función que realizar experimentos sobre un SVM para clasificación
x: numpy.Array, con los datos de entrada del problema
y: numpy.Array, con la variable objetivo
kernels: List[str], lista con valores a pasar
a sklearn correspondiente al kernel de la SVM
gammas: List[float], lista con los valores a pasar a
sklean correspondiente el valor de los coeficientes para usar en el
kernel
params_reg: List[float], lista con los valores a a pasar a
sklearn para ser usados como parametro de regularización
estrategia: str, valor que puede ser ovo (para one vs one) o ovr
(para one vs rest)
retorna: pd.Dataframe con las siguientes columnas:
- 3 columnas con los tres parametros: kernel, gamma, param de regularizacion
- estrategia de clasificación (ovo o ovr)
- Error en el conjunto de entrenamiento (promedio de los 4 folds)
- Error en el conjunto de test (promedio de los 4 folds)
- % de Vectores de Soporte promedio para los 4 folds (0 a 100)
"""
idx = 0
kf = ...(n_splits=...)
# crear una lista con la combinaciones de los elementos de cada list
kernels_gammas_regs = list(itertools.product(kernels, gammas, params_reg))
resultados = pd.DataFrame()
for params in kernels_gammas_regs:
kernel, gamma, param_reg = params
print("parametros usados", params) # puede usar para ver los params
errores_train = []
errores_test = []
pct_support_vectors = []
for train_index, test_index in kf...(...,...):
X_train, X_test = x[train_index], x[test_index]
y_train, y_test = y[train_index], y[test_index]
# normalizar los datos
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
#Instanciar y entrenar el modelo con los hiperparámetros adecuados
svm = ...
# calculo de errores
y_train_pred = ...
y_test_pred = ...
# error y pct de vectores de soporte
errores_train.append(1 - accuracy_score(y_true = y_train, y_pred = y_train_pred))
errores_test.append(1 - accuracy_score(y_true = y_test, y_pred = y_test_pred))
# Calcular el porcentaje de vectores de soporte. Tenga en cuenta que las estrategias
# OVO y OVR implican el entrenamiento de varios modelos. En este caso se debe estimar
# el promedio de porcentajes de vectores de soporte dentro de los estimadores que hacen parte de
# la estrategia multiclase y promediarlos.
pct_vs = ...
pct_support_vectors.append(pct_vs)
resultados.loc[idx,'kernel'] = kernel
resultados.loc[idx,'gamma'] = gamma
resultados.loc[idx,'param_reg'] = param_reg
resultados.loc[idx,'estrategia'] = ...
resultados.loc[idx,'error de entrenamiento'] = np.mean()
resultados.loc[idx,'error de prueba'] = np.mean()
resultados.loc[idx,'% de vectores de soporte'] = np.mean(...)
idx+=1
return (resultados)
Registra tu solución en línea
student.submit_task(namespace=globals(), task_id='T3');
Veamos la estrategia OVR
# vamos a realizar los experimentos
resultadosSVC_ovr = experiementarSVC(x = Xcl,y=Ycl,
kernels=['linear', 'rbf'],
gammas = [0.01,0.1],
params_reg = [0.001, 0.01,0.1, 1.0,10],
estrategia = 'ovr')
# ver los mejores modelos
resultadosSVC_ovr.sort_values('error de prueba', ascending=True).head(5)
Ahora vamos a ver la estrategia OVO
# vamos a realizar los experimentos
resultadosSVC_ovo = experiementarSVC(x = Xcl,y=Ycl,
kernels=['linear', 'rbf'],
gammas = [0.01,0.1],
params_reg = [0.001, 0.01,0.1, 1.0,10],
estrategia = 'ovo')
# ver los mejores modelos
resultadosSVC_ovo.sort_values('error de prueba', ascending=True).head(5)
#@title Pregunta Abierta
#@markdown Según el tipo de problema (enfocarse en la distribución de clases) ¿La métrica usada es la adecuada? ¿Cual otra métrica del modulo de sklearn podría ser usada?
respuesta = "" #@param {type:"string"}
# ver la relación de parametro de regularización y los vectores de soporte
import seaborn as sns
ax= sns.relplot(data = resultadosSVC_ovo, x = 'param_reg', y = '% de vectores de soporte', kind = 'line', hue ='kernel', aspect = 1.5)
ax.set(xscale="log")
plt.show()