Regularización ElasticNet con Scikit-learn: Conceptos y Aplicación
ElasticNet: Una técnica intermedia:
Hasta el momento hemos podido ver dos técnicas de regularización en las cuales añadimos un componente de penalización en el proceso donde encontramos los valores de los parámetros 𝛽 minimizando la función de error.
Por ejemplo, si usamos el método de Mínimos Cuadrados Ordinarios, tenemos por definición nuestra función definida como:
Ahora bien. Si aplicamos la regularización L1 también conocida como Lasso (Least Absolute Shrinkage and Selection Operator), tenemos una ecuación de la forma:
donde tenemos un parámetro de ajuste llamado ƛ que si tiene valores altos para el problema mandará el valor de 𝛽j a 0.
Por otro lado. Si aplicamos la regularización L2 también conocida como Ridge, tendremos la siguiente ecuación:
Tendremos una penalización también pero que no tiene la posibilidad de llevar los valores de los coeficientes a cero. Sin embargo esto nos permitirá realizar el intercambio de +sesgo por -varianza.
Recordando que :
Ninguna de las dos es mejor que la otra para todos los casos.
Lasso envía algunos coeficientes a cero permitiendo así seleccionar variables significativas para el modelo.
Lasso funciona mejor si tenemos pocos predictores que influyen sobre el modelo.
Ridge funciona mejor si es el caso contrario y tenemos una gran cantidad.
Para aplicarlos y decidir cuál es el mejor en la práctica, podemos probar usando alguna técnica como cross-validation iterativamente. o bien, podemos combinarlos...
Regularización ElasticNet
Es común encontrarnos en la literatura con un camino intermedio llamado ElasticNet. Esta técnica consiste en combinar las dos penalizaciones anteriores en una sola función. Así, nuestra ecuación de optimización quedará:
Donde tenemos ahora un parámetro adicional 𝛂 que tiene un rango de valores entre 0 y 1. Si 𝛂 = 0 , ElasticNet se comportará como Ridge, y si 𝛂 = 1 , se comportará como Lasso. Por lo tanto, nos brinda todo el espectro lineal de posibles combinaciones entre estos dos extremos.
Tenemos una forma de probar ambas L1 y L2 al tiempo sin perder información.
Supera las limitaciones individuales de ellas.
Si hace falta experiencia, o el conocimiento matemático de fondo, puede ser la opción preferente para probar la regularización.
ElasticNet con Scikit-learn
Para implementar esta técnica añadimos primero el algoritmo ubicado en el módulo linear_model.
from sklearn.linear_model import ElasticNet
Y luego simplemente lo inicializamos con el constructor ElasticNet() y entrenamos con la función fit().
Al comparar con distintos valores de alpha, al parecer lo que nos indica es que es mejor idea usar Ridge que lasso.
# elasticnet model
def modelElastic(alpha=1): modelElastic=ElasticNet(random_state=0, alpha=alpha) modelElastic.fit(X_train, y_train) y_predic_elastic=modelElastic.predict(X_test) # loss function elastic_loss =mean_squared_error(y_test, y_predic_elastic)return elastic_loss
alphas = np.arange(0,1,0.01) loss_total =[]for i inalphas: res =modelElastic(i) loss_total.append(res) loss_total = np.array(loss_total) plt.plot(alphas, loss_total) plt.xlabel('alphas') plt.ylabel('Loss Elastic') plt.text(0.02,0.8,'loss min:{}'.format(np.min(loss_total)), fontsize=7) plt.show()
El modelo lineal te da incluso mucho mejor, aquí te dejo la comparación:
Error del modelo lineal:7.141203442215841e-08Error del modelo lasso:0.0318013739963466Error del modelo ridge:0.0036293863527191326
El modelo lineal se disparo, es normal eso??
Me hubiese gustado entender el alcance del modelo desde el punto de vista práctico, es decir, cómo el modelo está prediciendo la enfermedad y basado en qué parámetros, en la vida real. Ejecutar el algoritmo de manera mecánica ayuda a aprender el código y la sintaxis de las librerías, y conceptualmente se entiende que hace el modelo y que tan bueno es, pero no a ver la aplicación práctica. Es decir cuando ingreso algunos datos, que hace el modelo y qué conclusiones sacaría quién lo utilice para la vida práctica
Ciertamente falta este rigor que indicas en la explicacion, no esta mal aprender nuevos conceptos y herramientas pero si no entendemos el transfondo o la capa de abstraccion donde esta montada asi como su significado practico, es como no sabeer nada realmente.
Deja un sinsabor
Ese puede ser un buen ejercicio para tí, lo único que debes de hacer es agrgar un nuevo data frame, y apliacs la predicción a ese nuevo dataframe, luego ya tendrías las etiquetas con las que salieron, es algo muy similar a lo que se hizo. me pondré a realizarlo :)
Al agregar este algoritmo de regularización al ejercicio anterior, vemos que tiene da por resultados que los otros dos y que lo que hizo fue lleva todos los coeficientes a cero.
Es solo cuestión de cambiar el valor de alpha (por defecto 1) a un valor más bajo (si fuera cero los coeficientes tienden a 1)
Pasa lo mismo con una regresión lasso
Una forma de comparar el error cuadrático medio variando el parámetro alpha para los 4 modelos distintos, puede ser la siguiente:
# Importamos las bibliotecas
import pandas as pd
import numpy as np
import sklearn
# Importamos los modelos de sklearn
from sklearn.linear_modelimportLinearRegression,Lasso,Ridge,ElasticNet# Importamos las metricas de entrenamiento y el error medio cuadrado
from sklearn.model_selectionimporttrain_test_splitfrom sklearn.metricsimport mean_squared_error
mse_lineal =[]mse_lasso =[]mse_ridge =[]mse_ElasticNet =[]dataset = pd.read_csv('data/felicidad.csv')X= dataset[['gdp','family','lifexp','freedom','corruption','generosity','dystopia']]y = dataset[['score']]X_train,X_test, y_train, y_test =train_test_split(X,y, test_size=0.25, random_state=42)# LinealmodelLinear =LinearRegression().fit(X_train, y_train)y_predict_linear = modelLinear.predict(X_test)alphas = np.linspace(0.01,1,50)for alpha inalphas: modelLasso =Lasso(alpha=alpha).fit(X_train, y_train) y_predict_lasso = modelLasso.predict(X_test) modelRidge =Ridge(alpha=alpha).fit(X_train, y_train) y_predict_ridge = modelRidge.predict(X_test) modelElasticNet =ElasticNet(random_state=0, alpha=alpha).fit(X_train, y_train) y_predict_ElasticNet = modelElasticNet.predict(X_test) mse_lineal.append(mean_squared_error(y_test, y_predict_linear)) mse_lasso.append(mean_squared_error(y_test, y_predict_lasso)) mse_ridge.append(mean_squared_error(y_test, y_predict_ridge)) mse_ElasticNet.append(mean_squared_error(y_test, y_predict_ElasticNet))plt.plot(alphas, mse_lineal)plt.plot(alphas, mse_lasso)plt.plot(alphas, mse_ridge)plt.plot(alphas, mse_ElasticNet)plt.legend(["Linear","Lasso","Ridge","ElasticNet"])plt.title('MSE')plt.xlabel(r'$\alpha$')plt.ylabel('Accuracy')plt.axisplt.show()
Con este código usé el ElasticNet:
import pandas as pd
import sklearn
from sklearn.linear_model import LinearRegression, Lasso, Ridge, ElasticNet
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.metrics import mean_squared_error
if __name__ =="__main__":# Cargar los datos data = pd.read_csv('./data/whr2017.csv')# Seleccionar las características (features) y el objetivo (target) X = data[['gdp','family','lifexp','freedom','corruption','generosity','dystopia']] Y = data[['score']]# Dividir los datos en entrenamiento y prueba X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25)# Regresión Lineal model_linear = LinearRegression().fit(X_train, Y_train) Y_predict_linear = model_linear.predict(X_test) linear_loose = mean_squared_error(Y_test, Y_predict_linear)print("="*32)print(f'Linear MSE: {linear_loose}')# Lasso model_lasso = Lasso(alpha=0.02).fit(X_train, Y_train) Y_predict_lasso = model_lasso.predict(X_test) lasso_loose = mean_squared_error(Y_test, Y_predict_lasso)print("="*32)print(f'Lasso MSE: {lasso_loose}')# Ridge model_ridge = Ridge(alpha=1).fit(X_train, Y_train) Y_predict_ridge = model_ridge.predict(X_test) ridge_loose = mean_squared_error(Y_test, Y_predict_ridge)print("="*32)print(f'Ridge MSE: {ridge_loose}')# **ElasticNet con GridSearchCV** param_grid ={'alpha':[0.001,0.01,0.1,1,10],'l1_ratio':[0.1,0.3,0.5,0.7,1]}# Inicializar el modelo ElasticNet elastic_net = ElasticNet()# Buscar los mejores hiperparámetros con GridSearchCV grid_search = GridSearchCV(elastic_net, param_grid, cv=5, scoring='neg_mean_squared_error') grid_search.fit(X_train, Y_train)# Mejor combinación de hiperparámetros best_params = grid_search.best_params_
print("="*32)print("Mejores hiperparámetros para ElasticNet:", best_params)# Usar el mejor modelo encontrado por GridSearchCV best_model = grid_search.best_estimator_
Y_predict_best = best_model.predict(X_test) mse_best = mean_squared_error(Y_test, Y_predict_best)print(f'MSE del mejor modelo ElasticNet: {mse_best}')# Imprimir coeficientes del mejor modelo ElasticNetprint("="*32)print("Coeficientes del mejor modelo ElasticNet")print(best_model.coef_)# Imprimir coeficientes de Lassoprint("="*32)print("Coef LASSO")print(model_lasso.coef_)# Imprimir coeficientes de Ridgeprint("="*32)print("Coef RIDGE")print(model_ridge.coef_)```
Probando el modelo ElasticNet obtuve estos scores:
intenta con un alpha bajo.
ElasticNet(random_state=0, alpha = 0.02)
Implementación de ElasticNet con Scikit-learn:
Indica cómo importa la clase ElasticNetdel módulo linear_modelde Scikit-learn.
Muestra cómo inicializar y entrenar un modelo ElasticNet con ElasticNet()y fit().
Resumiendo en pocas palabras, el texto de la clase nos da una explicación detallada de cómo funciona la regularización, especialmente enfocándose en ElasticNet. También proporciona una idea clara de cuándo y por qué podríamos preferir ElasticNet en comparación con
En el ejemplo mostrado para regularización se uso un modelo cuyo target era continuo. Ahora, si quiero analizar la importancia de mis Features para un modelo de clasificación es válido tambien estos métodos?
Hola amigos,
Implementé el model ElasticNet, pero los coeficientes me salen 0, ¿cómo se interpreta ello? ¿o simplemente este modelo no es el adecuado para esta data?
Resultado:
Código
Hola, no esta el código completo, pero por defecto alpha es 1 para ElasticNet. Esto quiere decir, según la info del curso, que esta regularizando con L1 (Lasso).
¿Es normal que los coeficientes de ElasticNet salgan ceros???
Al mio no le gusto ni una columna ajajaj.
intenta con un alpha bajo.
ElasticNet(random_state=0, alpha = 0.02)
Agregen esto: intenta con un alpha bajo.
ElasticNet(random_state=0, alpha = 0.02)
from sklearn.linear_modelimportElasticNetmodelElasticNet =ElasticNet(random_state=0, alpha=0.02).fit(X_train, y_train)y_pred_elastic = modelElasticNet.predict(X_test)elastic_loss =mean_squared_error(y_test, y_pred_elastic)print('ElasticNet Loss: ', elastic_loss)import matplotlib.pyplotas plt
import numpy as np
residuals_l = np.subtract(y_test, y_predict_linear)residuals_r = np.subtract(y_test, y_predict_ridge)residuals_ls = np.subtract(y_test, y_predict_lasso.reshape((-1,1)))residuals_el = np.subtract(y_test, y_pred_elastic.reshape((-1,1)))plt.scatter(y_predict_linear, residuals_l,label='Linear Regression')plt.scatter(y_predict_ridge, residuals_r,label='Ridge Regression')plt.scatter(y_predict_lasso, residuals_ls,label='Lasso Regression')plt.scatter(y_pred_elastic, residuals_el,label='ElasticNet')plt.legend(frameon=True, fancybox=True, shadow=True, borderpad=1, loc='upper left')plt.axhline(y=0, color='r', linestyle='--') # Agregar la línea en el valor cero
plt.show()
Este es mi modelo cuando le puse un alpha = 1
❯ python elastic_net.py
/root/scikitlearnPro/elastic_net.py:2: DeprecationWarning:
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466
import pandas as pd
rank score high low ... freedom generosity corruption dystopia
count 155.000000 155.000000 155.000000 155.000000 ... 155.000000 155.000000 155.000000 155.000000
mean 78.000000 5.354019 5.452326 5.255713 ... 0.408786 0.246883 0.123120 1.850238
std 44.888751 1.131230 1.118542 1.145030 ... 0.149997 0.134780 0.101661 0.500028
min 1.000000 2.693000 2.864884 2.521116 ... 0.000000 0.000000 0.000000 0.377914
25% 39.500000 4.505500 4.608172 4.374955 ... 0.303677 0.154106 0.057271 1.591291
50% 78.000000 5.279000 5.370032 5.193152 ... 0.437454 0.231538 0.089848 1.832910
75% 116.500000 6.101500 6.194600 6.006527 ... 0.516561 0.323762 0.153296 2.144654
max 155.000000 7.537000 7.622030 7.479556 ... 0.658249 0.838075 0.464308 3.117485
[8 rows x 11 columns]
(155, 7)
(155, 1)
Linear: 8.349947304787681e-08
Elastic Net: 1.4916349037786831
================================
Coef Linear
[[1.00014406 0.99996072 0.99988617 1.00005009 0.99966092 1.0000819
0.9999592 ]]
================================
Coef Elastic
[0. 0. 0. 0. 0. 0. 0.]
```❯ python elastic\_net.py
/root/scikitlearnPro/elastic\_net.py:2: DeprecationWarning:
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at
  import pandas as pd
  rank score high low ... freedom generosity corruption dystopia
count 155.000000 155.000000 155.000000 155.000000 ... 155.000000 155.000000 155.000000 155.000000
mean 78.000000 5.354019 5.452326 5.255713 ... 0.408786 0.246883 0.123120 1.850238
std 44.888751 1.131230 1.118542 1.145030 ... 0.149997 0.134780 0.101661 0.500028
min 1.000000 2.693000 2.864884 2.521116 ... 0.000000 0.000000 0.000000 0.377914
25% 39.500000 4.505500 4.608172 4.374955 ... 0.303677 0.154106 0.057271 1.591291
50% 78.000000 5.279000 5.370032 5.193152 ... 0.437454 0.231538 0.089848 1.832910
75% 116.500000 6.101500 6.194600 6.006527 ... 0.516561 0.323762 0.153296 2.144654
max 155.000000 7.537000 7.622030 7.479556 ... 0.658249 0.838075 0.464308 3.117485
\[8 rows x 11 columns]
(155, 7)
(155, 1)
Linear: 8.349947304787681e-08
Elastic Net: 1.4916349037786831
\================================
Coef Linear
\[\[1.00014406 0.99996072 0.99988617 1.00005009 0.99966092 1.0000819
  0.9999592 ]]
\================================
Coef Elastic
\[0. 0. 0. 0. 0. 0. 0.]
Y aqui cuando lo mande con un alpha = 0
❯ python elastic_net.py
/root/scikitlearnPro/elastic_net.py:2: DeprecationWarning:
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466
import pandas as pd
rank score high low ... freedom generosity corruption dystopia
count 155.000000 155.000000 155.000000 155.000000 ... 155.000000 155.000000 155.000000 155.000000
mean 78.000000 5.354019 5.452326 5.255713 ... 0.408786 0.246883 0.123120 1.850238
std 44.888751 1.131230 1.118542 1.145030 ... 0.149997 0.134780 0.101661 0.500028
min 1.000000 2.693000 2.864884 2.521116 ... 0.000000 0.000000 0.000000 0.377914
25% 39.500000 4.505500 4.608172 4.374955 ... 0.303677 0.154106 0.057271 1.591291
50% 78.000000 5.279000 5.370032 5.193152 ... 0.437454 0.231538 0.089848 1.832910
75% 116.500000 6.101500 6.194600 6.006527 ... 0.516561 0.323762 0.153296 2.144654
max 155.000000 7.537000 7.622030 7.479556 ... 0.658249 0.838075 0.464308 3.117485
[8 rows x 11 columns]
(155, 7)
(155, 1)
/root/miniforge3/envs/scikitlearnPro/lib/python3.12/site-packages/sklearn/base.py:1351: UserWarning: With alpha=0, this algorithm does not converge well. You are advised to use the LinearRegression estimator
return fit_method(estimator, *args, **kwargs)
/root/miniforge3/envs/scikitlearnPro/lib/python3.12/site-packages/sklearn/linear_model/_coordinate_descent.py:678: UserWarning: Coordinate descent with no regularization may lead to unexpected results and is discouraged.
model = cd_fast.enet_coordinate_descent(
Linear: 9.298974919237338e-08
Elastic Net: 9.256987412040729e-08
================================
Coef Linear
[[1.00003374 0.99993444 1.0000665 1.00010215 0.99952352 1.00027709
0.99998163]]
================================
Coef Elastic
[1.00015248 0.9999915 0.999874 1.0000184 0.99948386 1.0003127
0.99998826]
```Segun lo entendi en las clases mientras el score mas se acerque a uno (siendo score nuestra variable de seguimiento) mas preciso va a ser nuestro modelo, por lo que podemos detectar que los datos se comportan mucho mejor con un modelo de Regresion "Ridge"
Aqui les dejo el codigo, se aceptan correcciones
```python
# Importamos las bibliotecas generales
import pandas as pd
import sklearn
import matplotlib.pyplot as plt
# Importamos los módulos específicos
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
if __name__ == "__main__":
dataset = pd.read_csv('./data/felicidad.csv')
print(dataset.describe())
X = dataset[['gdp', 'family', 'lifexp', 'freedom' , 'corruption' , 'generosity', 'dystopia']]
y = dataset[['score']]
print(X.shape)
print(y.shape)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
modelLinear = LinearRegression().fit(X_train, y_train)
y_predict_linear = modelLinear.predict(X_test)
regr = ElasticNet(random_state=0, alpha=0) #alpha = 0 = Ridge, alpha = 1 = Lasso
regr.fit(X_train, y_train)
y_predict_linear = modelLinear.predict(X_test)
y_predict_elastic = regr.predict(X_test)
linearLoss = mean_squared_error(y_test, y_predict_linear)
elasticnetLoss = mean_squared_error(y_test, y_predict_elastic)
print('Linear: ', linearLoss)
print("Elastic Net: ", elasticnetLoss)
print('='*32)
print("Coef Linear")
print(modelLinear.coef_)
print('='*32)
print("Coef Elastic")
print(regr.coef_)
Un humilde consejo, intenten probar los accuracy de cada uno.
En el caso de Elastic Net y utilizando el r2_score da un accuracy negativo. Esto se puede solucionar dandole un valor de aplha muy pequeño, con lo cual logramos que se aproxime a los que conseguimos Linear Regression que tuvo el mejor accuracy y menos perdida.
Ejemplo de calculo de precision:
y_predict_elastic_net = model_elastic_net.predict(X_test)elastic_net_loss =mean_squared_error(y_test, y_predict_elastic_net)elastic_net_accuracy =r2_score(y_test, y_predict_elastic_net)print(f'Elastic Net loss: {elastic_net_loss}, accuracy: {(elastic_net_accuracy*100).round(4)}% and coef: {model_elastic_net.coef_}')
llevadolo a alpha = 0.2 el score cambia y todos los coef se aproximan a 1
igual todavia me cuenta entender un poco esto.
Excelente aporte ✔📝
En este caso, las variables fueron escogidas a mano por lo que tiene sentido que estén altamente correlacionadas con la resolución del problema y por lo tanto una solución Ridge o una ElasticNet con un coeficiente cercano a 0 den buenos resultados. Sin embargo, en este caso en particular no parece haber overfitting de la predicción usando simplemente una regresión lineal ya que es la que obtuvo el mejor resultados de MSE
No se trata de overfitting, simplente la variable score es la suma de las variable escogidas, es decir, hay una relación lineal perfecta entre las variables elegidas y la variable objetivo:
X = df_happiness[['gdp','family','lifexp','freedom','generosity','corruption','dystopia']]y = df_happiness['score']print(pd.concat([X.sum(axis=1).rename('suma'), y], axis=1))
Linear loss: 9.068971727801338e-08
Linear coef:
[1.00004716 0.99992617 0.99991259 1.0001198 1.00017723 1.000054495 1.00001989]
Siempre que tengan dudas, investigar directamente en la fuente, que sería .
Resulta que lo que la información de esta clase está totalmente equivocada (Lo cual considero bastante mediocre e irresponsable de parte de una plataforma educativa). Uno puede ir variando de Lasso a Ridge cambiando el parámetro l1_ratio (donde l1_ratio=1 se refiere a un modelo full Lasso y l1_ratio=0 es un modelo full Ridge. Ojo que en la documentación dice que usar l1_ratio<0.01 no es confiable) .
Si están interesados en controlar especificamente la proporción de L1 y L2, se hace con las siguientes expresiones:
pero el profesor no los llevo a 0.01, fue 0.02 (si no es mucha la diferencia), tienes alguna idea de porque hizo esto en lasso?
gracias por compartir
La pérdida es mucho mayor y el score me da negativo, a alguien más le ocurrió?