Comparación de R, statmodels, sklearn para una tarea de clasificación con regresión logística

He hecho algunos experimentos con regresión logística en R, modelos de python y sklearn. Si bien los resultados proporcionados por R y statmodels están de acuerdo, existe cierta discrepancia con lo que devuelve sklearn. Me gustaría entender por qué estos resultados son diferentes. Entiendo que probablemente no sean los mismos algoritmos de optimización que se usan debajo de la madera.

Específicamente, uso el conjunto de datos estándar Default (usado en el libro ISL ). El siguiente código de Python lee los datos en un dataframe Default .

 import pandas as pd # data is available here Default = pd.read_csv('https://d1pqsl2386xqi9.cloudfront.net/notebooks/Default.csv', index_col=0) # Default['default']=Default['default'].map({'No':0, 'Yes':1}) Default['student']=Default['student'].map({'No':0, 'Yes':1}) # I=Default['default']==0 print("Number of 'default' values :", Default[~I]['balance'].count()) 

Número de valores ‘por defecto’: 333.

Hay un total de 10000 ejemplos, con solo 333 positivos.

Regresión logística en R

Yo uso lo siguiente

 library("ISLR") data(Default,package='ISLR') #write.csv(Default,"default.csv") glm.out=glm('default~balance+income+student', family=binomial, data=Default) s=summary(glm.out) print(s) # glm.probs=predict(glm.out,type="response") glm.probs[1:5] glm.pred=ifelse(glm.probs>0.5,"Yes","No") #attach(Default) t=table(glm.pred,Default$default) print(t) score=mean(glm.pred==Default$default) print(paste("score",score)) 

El resultado es el siguiente

Llamada: glm (formula = “predeterminado ~ balance + ingreso + estudiante”, familia = binomial, datos = predeterminado)

Residuos de desviación: Mín. 1Q Mediana 3Q Máx.
-2.4691 -0.1418 -0.0557 -0.0203 3.7383

Coeficientes

 Estimate Std. Error z value Pr(>|z|) (Intercept) -1.087e+01 4.923e-01 -22.080 < 2e-16 balance 5.737e-03 2.319e-04 24.738 < 2e-16 income 3.033e-06 8.203e-06 0.370 0.71152 studentYes -6.468e-01 2.363e-01 -2.738 0.00619 

(El parámetro de dispersión para la familia binomial se toma como 1)

 Null deviance: 2920.6 on 9999 degrees of freedom Residual 

desviación: 1571.5 en 9996 grados de libertad AIC: 1579.5

Número de iteraciones de puntuación de Fisher: 8

  glm.pred No Yes No 9627 228 Yes 40 105 

1 “puntuación 0.9732”

Soy demasiado perezoso para cortar y pegar los resultados obtenidos con statmodels. Basta con decir que son extremadamente similares a los dados por R.

sklearn

Para sklearn, corrí el siguiente código.

  • Hay un parámetro class_weight para tener en cuenta las clases desequilibradas. Probé class_weight = None (sin ponderación, creo que es el valor predeterminado en R), y class_weight = ‘auto’ (ponderación con las frecuencias inversas encontradas en los datos)
  • También puse C = 10000, el inverso del parámetro de regularización, para minimizar el efecto de la regularización.

~~

 import sklearn from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix features = Default[[ 'balance', 'income' ]] target = Default['default'] # for weight in (None, 'auto'): print("*"*40+"\nweight:",weight) classifier = LogisticRegression(C=10000, class_weight=weight, random_state=42) #C=10000 ~ no regularization classifier.fit(features, target,) #fit classifier on whole base print("Intercept", classifier.intercept_) print("Coefficients", classifier.coef_) y_true=target y_pred_cls=classifier.predict_proba(features)[:,1]>0.5 C=confusion_matrix(y_true,y_pred_cls) score=(C[0,0]+C[1,1])/(C[0,0]+C[1,1]+C[0,1]+C[1,0]) precision=(C[1,1])/(C[1,1]+C[0 ,1]) recall=(C[1,1])/(C[1,1]+C[1,0]) print("\n Confusion matrix") print(C) print() print('{s:{c}<{n}}{num:2.4}'.format(s='Score',n=15,c='', num=score)) print('{s:{c}<{n}}{num:2.4}'.format(s='Precision',n=15,c='', num=precision)) print('{s:{c}<{n}}{num:2.4}'.format(s='Recall',n=15,c='', num=recall)) 

Los resultados se dan a continuación.

 > **************************************** >weight: None > >Intercept [ -1.94164126e-06] > >Coefficients [[ 0.00040756 -0.00012588]] > > Confusion matrix > > [[9664 3] > [ 333 0]] > > Score 0.9664 > Precision 0.0 > Recall 0.0 > > **************************************** >weight: auto > >Intercept [-8.15376429] > >Coefficients >[[ 5.67564834e-03 1.95253338e-05]] > > Confusion matrix > > [[8356 1311] > [ 34 299]] > > Score 0.8655 > Precision 0.1857 > Recall 0.8979 

Lo que observo es que para class_weight=None , el puntaje es excelente pero no se reconoce ningún ejemplo positivo. La precisión y el recuerdo están en cero. Los coeficientes encontrados son muy pequeños, particularmente el intercepto. Modificar C no cambia las cosas. Para class_weight='auto' cosas parecen mejores pero todavía tengo una precisión que es muy baja (demasiado positivo clasificado). De nuevo, cambiar C no ayuda. Si modifico el intercepto a mano, puedo recuperar los resultados dados por R. Entonces sospecho que aquí hay una discrepancia entre la estimación de los intecepts en los dos casos. Como esto tiene una consecuencia en la especificación de los tríos (análogo a un remuestreo de las inflaciones), esto puede explicar las diferencias en los rendimientos.

Sin embargo, agradecería cualquier consejo para la elección entre las dos soluciones y ayudar a entender el origen de estas diferencias. Gracias.

Me encontré con un problema similar y terminé publicando al respecto en / r / MachineLearning . Resulta que la diferencia puede atribuirse a la estandarización de los datos. Cualquier enfoque que scikit-learn esté utilizando para encontrar los parámetros del modelo dará mejores resultados si los datos están estandarizados. scikit-learn tiene alguna documentación sobre los datos de preprocesamiento (incluida la estandarización), que puede encontrar aquí .

Resultados

 Number of 'default' values : 333 Intercept: [-6.12556565] Coefficients: [[ 2.73145133 0.27750788]] Confusion matrix [[9629 38] [ 225 108]] Score 0.9737 Precision 0.7397 Recall 0.3243 

Código

 # scikit-learn vs. R # http://stackoverflow.com/questions/28747019/comparison-of-r-statmodels-sklearn-for-a-classification-task-with-logistic-reg import pandas as pd import sklearn from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix from sklearn import preprocessing # Data is available here. Default = pd.read_csv('https://d1pqsl2386xqi9.cloudfront.net/notebooks/Default.csv', index_col = 0) Default['default'] = Default['default'].map({'No':0, 'Yes':1}) Default['student'] = Default['student'].map({'No':0, 'Yes':1}) I = Default['default'] == 0 print("Number of 'default' values : {0}".format(Default[~I]['balance'].count())) feats = ['balance', 'income'] Default[feats] = preprocessing.scale(Default[feats]) # C = 1e6 ~ no regularization. classifier = LogisticRegression(C = 1e6, random_state = 42) classifier.fit(Default[feats], Default['default']) #fit classifier on whole base print("Intercept: {0}".format(classifier.intercept_)) print("Coefficients: {0}".format(classifier.coef_)) y_true = Default['default'] y_pred_cls = classifier.predict_proba(Default[feats])[:,1] > 0.5 confusion = confusion_matrix(y_true, y_pred_cls) score = float((confusion[0, 0] + confusion[1, 1])) / float((confusion[0, 0] + confusion[1, 1] + confusion[0, 1] + confusion[1, 0])) precision = float((confusion[1, 1])) / float((confusion[1, 1] + confusion[0, 1])) recall = float((confusion[1, 1])) / float((confusion[1, 1] + confusion[1, 0])) print("\nConfusion matrix") print(confusion) print('\n{s:{c}<{n}}{num:2.4}'.format(s = 'Score', n = 15, c = '', num = score)) print('{s:{c}<{n}}{num:2.4}'.format(s = 'Precision', n = 15, c = '', num = precision)) print('{s:{c}<{n}}{num:2.4}'.format(s = 'Recall', n = 15, c = '', num = recall)) 

Aunque esta publicación es antigua, quería darte una solución. En tu post estás comparando manzanas con naranjas. En su código R, está estimando “saldo, ingreso y estudiante” en “predeterminado”. En su código Python, solo está estimando “saldo e ingresos” en “default”. Por supuesto, no se pueden obtener las mismas estimaciones. Además, las diferencias no pueden atribuirse a la escala de características, ya que la regresión logística generalmente no la necesita en comparación con los kmeans.

Tiene derecho a establecer una C alta, de modo que no haya regularización. Si desea tener la misma salida que en R, debe cambiar el solucionador a “newton-cg”. Diferentes solucionadores pueden dar resultados diferentes pero aún así producen el mismo valor objective. Mientras su solucionador converja todo estará bien.

Aquí está el código que le da las mismas estimaciones como en R y Statsmodels:

 import pandas as pd from sklearn.linear_model import LogisticRegression from patsy import dmatrices # import numpy as np # data is available here Default = pd.read_csv('https://d1pqsl2386xqi9.cloudfront.net/notebooks/Default.csv', index_col=0) # Default['default']=Default['default'].map({'No':0, 'Yes':1}) Default['student']=Default['student'].map({'No':0, 'Yes':1}) # use dmatrices to get data frame for logistic regression y, X = dmatrices('default ~ balance+income+C(student)', Default,return_type="dataframe") y = np.ravel(y) # fit logistic regression model = LogisticRegression(C = 1e6, fit_intercept=False, solver = "newton-cg", max_iter=10000000) model = model.fit(X, y) # examine the coefficients pd.DataFrame(zip(X.columns, np.transpose(model.coef_)))