Regresión logística: Scikit Learn vs glmnet

15

Estoy tratando de duplicar los resultados de sklearnla biblioteca de regresión logística usando el glmnetpaquete en R.

A partir de la documentación desklearn regresión logística , está tratando de minimizar la función de costo bajo penalización l2

minw,c12wTw+Ci=1norteIniciar sesión(Exp(-yyo(XyoTw+C))+1)

De las viñetas de glmnet, su implementación minimiza una función de costo ligeramente diferente

minβ,β0 0-[1norteyo=1norteyyo(β0 0+XyoTβ)-Iniciar sesión(1+mi(β0 0+XyoTβ))]+λ[(α-1)El |El |βEl |El |22/ /2+αEl |El |βEl |El |1]

Con algunos ajustes en la segunda ecuación, y al configurar α=0 0 ,

λminβ,β0 01norteλyo=1norte[-yyo(β0 0+XyoTβ)+Iniciar sesión(1+mi(β0 0+XyoTβ))]+El |El |βEl |El |22/ /2

que difiere de la sklearnfunción de costo solo por un factor de λ si se establece 1norteλ=C , por lo que esperaba la misma estimación de coeficiente de los dos paquetes. Pero son diferentes. Estoy usando el conjunto de datos del tutorial idre de UCLA , prediciendo en admitbase a gre, gpay rank. Hay 400 observaciones, entonces con C=1 , λ=0.0025 .

#python sklearn
df = pd.read_csv("https://stats.idre.ucla.edu/stat/data/binary.csv")
y, X = dmatrices('admit ~ gre + gpa + C(rank)', df, return_type = 'dataframe')
X.head()
>  Intercept  C(rank)[T.2]  C(rank)[T.3]  C(rank)[T.4]  gre   gpa
0          1             0             1             0  380  3.61
1          1             0             1             0  660  3.67
2          1             0             0             0  800  4.00
3          1             0             0             1  640  3.19
4          1             0             0             1  520  2.93

model = LogisticRegression(fit_intercept = False, C = 1)
mdl = model.fit(X, y)
model.coef_
> array([[-1.35417783, -0.71628751, -1.26038726, -1.49762706,  0.00169198,
     0.13992661]]) 
# corresponding to predictors [Intercept, rank_2, rank_3, rank_4, gre, gpa]


> # R glmnet
> df = fread("https://stats.idre.ucla.edu/stat/data/binary.csv")
> X = as.matrix(model.matrix(admit~gre+gpa+as.factor(rank), data=df))[,2:6]
> y = df[, admit]
> mylogit <- glmnet(X, y, family = "binomial", alpha = 0)
> coef(mylogit, s = 0.0025)
6 x 1 sparse Matrix of class "dgCMatrix"
                    1
(Intercept)      -3.984226893
gre               0.002216795
gpa               0.772048342
as.factor(rank)2 -0.530731081
as.factor(rank)3 -1.164306231
as.factor(rank)4 -1.354160642

El Rresultado está de alguna manera cerca de la regresión logística sin regularización, como se puede ver aquí . ¿Me estoy perdiendo algo o estoy haciendo algo obviamente mal?

Actualización: también intenté usar el LiblineaRpaquete Rpara llevar a cabo el mismo proceso, pero obtuve otro conjunto diferente de estimaciones ( liblineartambién es el solucionador sklearn):

> fit = LiblineaR(X, y, type = 0, cost = 1)
> print(fit)
$TypeDetail
[1] "L2-regularized logistic regression primal (L2R_LR)"
$Type
[1] 0
$W
            gre          gpa as.factor(rank)2 as.factor(rank)3 as.factor(rank)4         Bias
[1,] 0.00113215 7.321421e-06     5.354841e-07     1.353818e-06      9.59564e-07 2.395513e-06

Actualización 2: desactivar la estandarización en glmnetdado:

> mylogit <- glmnet(X, y, family = "binomial", alpha = 0, standardize = F)
> coef(mylogit, s = 0.0025)
6 x 1 sparse Matrix of class "dgCMatrix"
                     1
(Intercept)      -2.8180677693
gre               0.0034434192
gpa               0.0001882333
as.factor(rank)2  0.0001268816
as.factor(rank)3 -0.0002259491
as.factor(rank)4 -0.0002028832
hurrikale
fuente
¿Alguna vez resolviste esto?
Huey

Respuestas:

8

la regresión logística de sklearn no estandariza las entradas por defecto, lo que cambia el significado del término de regularización ; probablemente glmnet lo hace.L2

Especialmente porque su gretérmino está en una escala tan grande como las otras variables, esto cambiará los costos relativos de usar las diferentes variables para los pesos.

Tenga en cuenta también que al incluir un término de intercepción explícito en las características, está regularizando la intercepción del modelo. Esto generalmente no se hace, ya que significa que su modelo ya no es covariante para cambiar todas las etiquetas por una constante.

Dougal
fuente
glmnetpermite desactivar la estandarización de las entradas, pero los coeficientes estimados son aún más diferentes, ver arriba. Además, incluí explícitamente el término de intercepción sklearnporque glmnetincluye uno automáticamente, por lo que esto es para asegurarse de que la entrada a ambos modelos sea la misma.
hurrikale
2
@hurrikale Creo que glmnet probablemente no está regularizando la intercepción, pero sklearn sí. Suelte la columna de intercepción Xy pase fit_intercept=True(el valor predeterminado) a LogisticRegression. Sin embargo, probablemente también esté sucediendo algo más.
Dougal
Intenté lo que sugirió y, sin embargo, obtuve diferentes conjuntos de coeficientes: [-1.873, -0.0606, -1.175, -1.378, 0.00182, 0.2435]por sklearny [-2.8181, 0.0001269, -0.0002259, -0.00020288, 0.00344, 0.000188]para glmneten orden de [Intercept, rank_2, rank_3, rank_4, gre, gpa]. Mi preocupación es que difieren tanto en magnitud como en el impacto positivo / negativo de la probabilidad, por lo que sin saber por qué difieren, es difícil elegir uno para interpretar. Y si por casualidad hay un error en una de las implementaciones, es particularmente importante que sepa en cuál confiar.
hurrikale
7

La respuesta de Dougal es correcta, usted regulariza la intersección sklearnpero no en R. Asegúrese de usarla solver='newton-cg'ya que el solucionador predeterminado ( 'liblinear') siempre regulariza la intersección.

cf https://github.com/scikit-learn/scikit-learn/issues/6595

TomDLT
fuente
Ajuste solver='newton-cg'realizado a partir de los resultados sklearny statsmodelsconsistente. Muchas gracias.
irene
0

También debe usar el L1_wt=0argumento junto con alphain fit_regularized()call.

Este código en statsmodels:

import statsmodels.api as sm
res = sm.GLM(y, X, family=sm.families.Binomial()).fit_regularized(alpha=1/(y.shape[0]*C), L1_wt=0)

es equivalente al siguiente código de sklearn:

from sklearn import linear_model
clf = linear_model.LogisticRegression(C = C)
clf.fit(X, y)

¡Espero eso ayude!

Praful Gupta
fuente