Regressión II¶
Regressión Múltiple¶
Los modelos de regresión multiple son los más utilizados en el mundo de machine learning, puestos que se dispone de varios característica de la población objetivo. A menudo, se estará abordando estos modelos de la perspectiva de los modelos lineales, por lo cual se debetener en mente algunos supuestos antes de comenzar:
- No colinialidad o multicolinialidad: En los modelos lineales múltiples los predictores deben ser independientes, no debe de haber colinialidad entre ellos
- Parsimonia: Este término hace referencia a que el mejor modelo es aquel capaz de explicar con mayor precisión la variabilidad observada en la variable respuesta empleando el menor número de predictores, por lo tanto, con menos asunciones.
- Homocedasticidad:La varianza de los residuos debe de ser constante en todo el rango de observaciones.
- Otros Factores:
- Distribución normal de los residuos
- No autocorrelación (Independencia)
- Valores atípicos, con alto leverage o influyentes
- Tamaño de la muestra
Por otro lado, existen otros tipos de modelos de regresión, en los cuales se necesitan menos supuestos que los modelos de regresión lineal, a cambio se pierde un poco de interpretabilidad en sus parámetros y centran su atención en los resultados obtenidos de las predicciones.
# librerias
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
pd.set_option('display.max_columns', 500) # Ver más columnas de los dataframes
# Ver gráficos de matplotlib en jupyter notebook/lab
%matplotlib inline
# sklearn models
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.datasets import fetch_openml
# Load the Boston Housing dataset using fetch_openml
data = fetch_openml(data_id=531)
# Create a DataFrame from the data
boston_df = pd.DataFrame(data.data, columns=data.feature_names)
boston_df['TARGET'] = data.target
boston_df = boston_df.astype(float)
boston_df.head()
C:\Users\franc\AppData\Local\pypoetry\Cache\virtualenvs\db-connectors-RsEieBu8-py3.8\lib\site-packages\sklearn\datasets\_openml.py:968: FutureWarning: The default value of `parser` will change from `'liac-arff'` to `'auto'` in 1.4. You can set `parser='auto'` to silence this warning. Therefore, an `ImportError` will be raised from 1.4 if the dataset is dense and pandas is not installed. Note that the pandas parser may return different data types. See the Notes Section in fetch_openml's API doc for details. warn(
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | TARGET | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.00632 | 18.0 | 2.31 | 0.0 | 0.538 | 6.575 | 65.2 | 4.0900 | 1.0 | 296.0 | 15.3 | 396.90 | 4.98 | 24.0 |
1 | 0.02731 | 0.0 | 7.07 | 0.0 | 0.469 | 6.421 | 78.9 | 4.9671 | 2.0 | 242.0 | 17.8 | 396.90 | 9.14 | 21.6 |
2 | 0.02729 | 0.0 | 7.07 | 0.0 | 0.469 | 7.185 | 61.1 | 4.9671 | 2.0 | 242.0 | 17.8 | 392.83 | 4.03 | 34.7 |
3 | 0.03237 | 0.0 | 2.18 | 0.0 | 0.458 | 6.998 | 45.8 | 6.0622 | 3.0 | 222.0 | 18.7 | 394.63 | 2.94 | 33.4 |
4 | 0.06905 | 0.0 | 2.18 | 0.0 | 0.458 | 7.147 | 54.2 | 6.0622 | 3.0 | 222.0 | 18.7 | 396.90 | 5.33 | 36.2 |
# descripcion del conjunto de datos
boston_df.describe()
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | TARGET | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 |
mean | 3.613524 | 11.363636 | 11.136779 | 0.069170 | 0.554695 | 6.284634 | 68.574901 | 3.795043 | 9.549407 | 408.237154 | 18.455534 | 356.674032 | 12.653063 | 22.532806 |
std | 8.601545 | 23.322453 | 6.860353 | 0.253994 | 0.115878 | 0.702617 | 28.148861 | 2.105710 | 8.707259 | 168.537116 | 2.164946 | 91.294864 | 7.141062 | 9.197104 |
min | 0.006320 | 0.000000 | 0.460000 | 0.000000 | 0.385000 | 3.561000 | 2.900000 | 1.129600 | 1.000000 | 187.000000 | 12.600000 | 0.320000 | 1.730000 | 5.000000 |
25% | 0.082045 | 0.000000 | 5.190000 | 0.000000 | 0.449000 | 5.885500 | 45.025000 | 2.100175 | 4.000000 | 279.000000 | 17.400000 | 375.377500 | 6.950000 | 17.025000 |
50% | 0.256510 | 0.000000 | 9.690000 | 0.000000 | 0.538000 | 6.208500 | 77.500000 | 3.207450 | 5.000000 | 330.000000 | 19.050000 | 391.440000 | 11.360000 | 21.200000 |
75% | 3.677083 | 12.500000 | 18.100000 | 0.000000 | 0.624000 | 6.623500 | 94.075000 | 5.188425 | 24.000000 | 666.000000 | 20.200000 | 396.225000 | 16.955000 | 25.000000 |
max | 88.976200 | 100.000000 | 27.740000 | 1.000000 | 0.871000 | 8.780000 | 100.000000 | 12.126500 | 24.000000 | 711.000000 | 22.000000 | 396.900000 | 37.970000 | 50.000000 |
boston_df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 506 entries, 0 to 505 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CRIM 506 non-null float64 1 ZN 506 non-null float64 2 INDUS 506 non-null float64 3 CHAS 506 non-null float64 4 NOX 506 non-null float64 5 RM 506 non-null float64 6 AGE 506 non-null float64 7 DIS 506 non-null float64 8 RAD 506 non-null float64 9 TAX 506 non-null float64 10 PTRATIO 506 non-null float64 11 B 506 non-null float64 12 LSTAT 506 non-null float64 13 TARGET 506 non-null float64 dtypes: float64(14) memory usage: 55.5 KB
#matriz de correlacion
corr_mat=boston_df.corr(method='pearson')
plt.figure(figsize=(20,10))
sns.heatmap(corr_mat,vmax=1,square=True,annot=True,cmap='cubehelix',fmt='.2f')
plt.show()
Apliquemos el modelo de regresión lineal multiple con sklearn
# datos para la regresion lineal simple
X = boston_df.drop("TARGET",axis=1)
Y = boston_df["TARGET"]
# split dataset
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state = 2)
# ajustar el modelo
model_rl = LinearRegression() # Creando el modelo.
model_rl.fit(X_train, Y_train) # ajustando el modelo
# prediciones
Y_predict = model_rl.predict(X_test)
from sklearn.metrics import mean_absolute_error, mean_squared_error
def mean_absolute_percentage_error(y_true, y_pred):
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
def regression_metrics(df):
"""
Aplicar las distintas métricas definidas
:param df: DataFrame con las columnas: ['y', 'yhat']
:return: DataFrame con las métricas especificadas
"""
df_result = pd.DataFrame()
y_true = df['y']
y_pred = df['yhat']
df_result['mae'] = [round(mean_absolute_error(y_true, y_pred), 4)]
df_result['mse'] = [round(mean_squared_error(y_true, y_pred), 4)]
df_result['rmse'] = [round(np.sqrt(mean_squared_error(y_true, y_pred)), 4)]
df_result['mape'] = [round(mean_absolute_percentage_error(y_true, y_pred), 4)]
df_result['smape'] = [round(2 * mean_absolute_percentage_error(y_true, y_pred) / (mean_absolute_percentage_error(y_true, y_pred) + 100), 4)]
return df_result
from sklearn.metrics import r2_score
# ejemplo: boston df
df_temp = pd.DataFrame(
{
'y':Y_test,
'yhat': model_rl.predict(X_test)
}
)
df_metrics = regression_metrics(df_temp)
df_metrics['r2'] = round(r2_score(Y_test, model_rl.predict(X_test)),4)
print('\nMetricas para el regresor CRIM:')
df_metrics
Metricas para el regresor CRIM:
mae | mse | rmse | mape | smape | r2 | |
---|---|---|---|---|---|---|
0 | 3.113 | 18.4954 | 4.3006 | 16.036 | 0.2764 | 0.7789 |
Cuando se aplica el modelo de regresión lineal con todas las variables regresoras, las métricas disminuyen considerablemente, lo implica una mejora en el modelo
Un problema que se tiene, a diferencia de la regresión lineal simple,es que no se puede ver gráficamente la calidad del ajuste, por lo que solo se puede confiar en las métricas calculadas. Además, se dejan las siguientes preguntas:
- ¿ Entre más regresores, mejor será el modelo de regresión lineal?
- ¿ Qué se debe tener en cuenta antes de agregar otro variable regresora al modelo de regresión lineal ?
- ¿ Qué sucede si se tienen outliers ?
- ¿ Existen otros modelos mejor que la regresión lineal ?
Ya se han discutido algunos de estos puntos, por lo que la atención estará en abordar otros modelos.
Otros modelos de Regresión¶
Existen varios modelos de regresión, sin embargo, la intepretación de sus parámetros y el análisis de confiabilidad no es tan directo como los modelos de regresión lineal. Por este motivo, la atención estará centrada en la predicción más que en la confiabilidad como tal del modelo.
Modelos lineales¶
Existen varios modelos lineales que podemos trabajar en sklearn (ver referencia), los cualeas podemos utilizar e ir comparando unos con otros.
De lo modelos lineales, destacamos los siguientes:
- regresión lineal clásica: regresión clásica por mínimos cudrados. $$(P)\ \min \sum_{i=1}^n (y_{i}-f_{i}(x;\beta))^2 $$
- lasso): se ocupa cuando tenemos un gran número de regresores y queremos que disminuya el problema de colinealidad (es decir, estimar como cero los parámetros poco relevantes). $$(P)\ \min \sum_{i=1}^n (y_{i}-f_{i}(x;\beta))^2 + \lambda \sum_{i=1}^n |\beta_{i}| $$
- ridge: también sirve para disminuir el problema de colinealidad, y además trata de que los coeficientes sean más rocuesto bajo outliers. $$(P)\ \min \sum_{i=1}^n (y_{i}-f_{i}(x;\beta))^2 + \lambda \sum_{i=1}^n \beta_{i}^2 $$
Dado que en sklearn, la forma de entrenar, estimar y predecir modelos de regresión siguen una misma estructura, para fectos prácticos, definimos una rutina para estimar las distintas métricas de la siguiente manera:
Bayesian Regression¶
En estadística, la regresión lineal bayesiana es una variante de la regresión lineal que se formula dentro del marco de la inferencia bayesiana. Este enfoque no solo estima los parámetros del modelo, sino que también cuantifica la incertidumbre asociada a dichas estimaciones a través de distribuciones de probabilidad.
Cuando se asume que los errores del modelo siguen una distribución normal y se define una distribución previa adecuada para los parámetros, es posible obtener de forma explícita las distribuciones posteriores. Estas distribuciones permiten describir cómo cambian nuestras creencias sobre los parámetros después de observar los datos.
En otras palabras, la regresión lineal bayesiana integra la información previa con la evidencia empírica, ofreciendo una visión más completa y probabilística de la relación entre variables.
k-vecinos más cercanos Knn¶
El método de $k$ vecinos más cercanos (k-NN) también se aplica a la regresión supervisada. En este caso, la predicción para un nuevo dato se obtiene promediando los valores de la variable objetivo de sus $k$ vecinos más cercanos.
Es un enfoque no paramétrico y de aprendizaje perezoso, ya que no construye un modelo explícito, sino que calcula las predicciones directamente a partir de los datos. La normalización de variables es clave, pues el algoritmo depende de medidas de distancia entre observaciones.
Decision Tree Regressor¶
Un árbol de decisión para regresión es un modelo no paramétrico que predice valores numéricos continuos dividiendo el espacio de datos en regiones. En cada nodo se selecciona una variable y un umbral que mejor separan los datos, y la predicción final en cada hoja corresponde al promedio de las observaciones en esa región.
Este enfoque es fácil de interpretar, pero puede sufrir de sobreajuste, por lo que suelen aplicarse técnicas como la poda o la validación cruzada para mejorar su generalización.
SVM¶
Las máquinas de vectores de soporte (SVM) son algoritmos de aprendizaje supervisado que pueden aplicarse tanto a clasificación como a regresión.
En el caso de la regresión con SVM (Support Vector Regression, SVR), el objetivo es encontrar una función que aproxime los datos dentro de un margen de tolerancia definido, minimizando el error y manteniendo el modelo lo más simple posible. El ajuste se realiza mediante un hiperplano (o función en espacios transformados) que equilibra la precisión de la predicción con la complejidad del modelo, utilizando vectores de soporte para definir la solución.
Gracias al uso de funciones kernel, las SVM pueden manejar relaciones lineales y no lineales, lo que las convierte en una herramienta flexible para problemas de regresión.
Aplicando varios modelos al mismo tiempo¶
Veremos el performance de los distintos modelos estudiados.
from sklearn import linear_model
from sklearn import tree
from sklearn import svm
from sklearn import neighbors
class SklearnRegressionModels:
def __init__(self,model,name_model):
self.model = model
self.name_model = name_model
@staticmethod
def test_train_model(X,y,n_size):
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=n_size , random_state=42)
return X_train, X_test, y_train, y_test
def fit_model(self,X,y,test_size):
X_train, X_test, y_train, y_test = self.test_train_model(X,y,test_size )
return self.model.fit(X_train, y_train)
def df_testig(self,X,y,test_size):
X_train, X_test, y_train, y_test = self.test_train_model(X,y,test_size )
model_fit = self.model.fit(X_train, y_train)
preds = model_fit.predict(X_test)
df_temp = pd.DataFrame(
{
'y':y_test,
'yhat': model_fit.predict(X_test)
}
)
return df_temp
def metrics(self,X,y,test_size):
df_temp = self.df_testig(X,y,test_size)
df_metrics = regression_metrics(df_temp)
df_metrics['r2'] = round(r2_score(df_temp['y'],df_temp['yhat']),4)
df_metrics['model'] = self.name_model
return df_metrics
def parameters(self,X,y,test_size):
model_fit = self.fit_model(X,y,test_size)
list_betas = [
('beta_0',model_fit.intercept_)
]
betas = model_fit.coef_
for num, beta in enumerate(betas):
name_beta = f'beta_{num+1}'
list_betas.append((name_beta,round(beta,2)))
result = pd.DataFrame(
columns = ['coef','value'],
data = list_betas
)
result['model'] = self.name_model
return result
# boston dataframe
X = boston_df.drop("TARGET",axis=1)
Y = boston_df["TARGET"]
# models
reg_lineal = linear_model.LinearRegression()
reg_ridge = linear_model.Ridge(alpha=.5)
reg_lasso = linear_model.Lasso(alpha=0.1)
reg_knn = neighbors.KNeighborsRegressor(5,weights='uniform')
reg_bayesian = linear_model.BayesianRidge()
reg_tree = tree.DecisionTreeRegressor(max_depth=5)
reg_svm = svm.SVR(kernel='linear')
list_models =[
[reg_lineal,'lineal'],
[reg_ridge,'ridge'],
[reg_lasso,'lasso'],
[reg_knn,'knn'],
[reg_bayesian,'bayesian'],
[reg_tree,'decision_tree'],
[reg_svm,'svm'],
]
frames_metrics = []
frames_coef = []
for model,name_models in list_models:
fit_model = SklearnRegressionModels( model,name_models)
frames_metrics.append(fit_model.metrics(X,Y,0.2))
if name_models in ['lineal','ridge','lasso']:
frames_coef.append(fit_model.parameters(X,Y,0.2))
# juntar resultados: metricas
pd.concat(frames_metrics).sort_values('rmse')
mae | mse | rmse | mape | smape | r2 | model | |
---|---|---|---|---|---|---|---|
0 | 2.6062 | 20.3563 | 4.5118 | 15.0760 | 0.2620 | 0.7224 | decision_tree |
0 | 3.1891 | 24.2911 | 4.9286 | 16.8664 | 0.2886 | 0.6688 | lineal |
0 | 3.1493 | 24.3776 | 4.9374 | 16.6837 | 0.2860 | 0.6676 | ridge |
0 | 3.1251 | 24.6471 | 4.9646 | 16.5449 | 0.2839 | 0.6639 | bayesian |
0 | 3.1452 | 25.1556 | 5.0155 | 16.7519 | 0.2870 | 0.6570 | lasso |
0 | 3.6639 | 25.8601 | 5.0853 | 18.8859 | 0.3177 | 0.6474 | knn |
0 | 3.1404 | 29.4359 | 5.4255 | 16.7713 | 0.2873 | 0.5986 | svm |
Basados en los distintos estadísticos, el mejor modelo corresponde al modelo de decision_tree. Por otro lado, podemos analizar los coeficientes de los modelos líneales ordinarios,Ridge y Lasso.
# juntar resultados: coeficientes
pd.concat(frames_coef)
coef | value | model | |
---|---|---|---|
0 | beta_0 | 30.246751 | lineal |
1 | beta_1 | -0.110000 | lineal |
2 | beta_2 | 0.030000 | lineal |
3 | beta_3 | 0.040000 | lineal |
4 | beta_4 | 2.780000 | lineal |
5 | beta_5 | -17.200000 | lineal |
6 | beta_6 | 4.440000 | lineal |
7 | beta_7 | -0.010000 | lineal |
8 | beta_8 | -1.450000 | lineal |
9 | beta_9 | 0.260000 | lineal |
10 | beta_10 | -0.010000 | lineal |
11 | beta_11 | -0.920000 | lineal |
12 | beta_12 | 0.010000 | lineal |
13 | beta_13 | -0.510000 | lineal |
0 | beta_0 | 26.891132 | ridge |
1 | beta_1 | -0.110000 | ridge |
2 | beta_2 | 0.030000 | ridge |
3 | beta_3 | 0.020000 | ridge |
4 | beta_4 | 2.640000 | ridge |
5 | beta_5 | -12.270000 | ridge |
6 | beta_6 | 4.460000 | ridge |
7 | beta_7 | -0.010000 | ridge |
8 | beta_8 | -1.380000 | ridge |
9 | beta_9 | 0.250000 | ridge |
10 | beta_10 | -0.010000 | ridge |
11 | beta_11 | -0.860000 | ridge |
12 | beta_12 | 0.010000 | ridge |
13 | beta_13 | -0.520000 | ridge |
0 | beta_0 | 19.859769 | lasso |
1 | beta_1 | -0.100000 | lasso |
2 | beta_2 | 0.030000 | lasso |
3 | beta_3 | -0.020000 | lasso |
4 | beta_4 | 0.920000 | lasso |
5 | beta_5 | -0.000000 | lasso |
6 | beta_6 | 4.310000 | lasso |
7 | beta_7 | -0.020000 | lasso |
8 | beta_8 | -1.150000 | lasso |
9 | beta_9 | 0.240000 | lasso |
10 | beta_10 | -0.010000 | lasso |
11 | beta_11 | -0.730000 | lasso |
12 | beta_12 | 0.010000 | lasso |
13 | beta_13 | -0.560000 | lasso |
Al comparar los resultados entre ambos modelos, se observa que hay coeficientes en la regresión Lasso que se van a cero directamente, pudiendo eliminar estas variables del modelo. Por otro lado, queda como tarea para el lector, hacer una eliminación de outliers del modelo y probar estos modelos lineales para ver si existe algún tipo de diferencia.
Conclusión¶
- Existen distintos modelos de regresión lineal: normal, Ridge y Lasso. Cada uno con sus respectivs ventajas y desventajas.
- Existen otros tipos de modelos de regresión (bayesiano, knn, arboles de decisión, svm, entre otros). Por ahora, nos interesa saber como funcionan, para poder configurar los hiperparámetros de los modelos ocupados en python (principalmente de la librería sklearn).
- En el mundo del machine learning se estará interesado más en predecir con el menor error posible (siempre tomando como referencia alguna de las métricas mencionadas) que hacer un análisis exhaustivo de la confiabilidad del modelo. Siendo este el caso y si la capacidad computacional lo permite, lo ideal es probar varios modelos al mismo tiempo y poder discriminar bajo un determinado criterio (a menudo el error cuadrático medio (rmse) o el mape).