Сравнение R, statmodels, sklearn для задачи классификации с логистической регрессией

Я провел несколько экспериментов с логистической регрессией в R, python statmodels и sklearn. Хотя результаты, даваемые R и statmodels, согласуются, существует некоторая разница в том, что возвращается sklearn. Я хотел бы понять, почему эти результаты разные. Я понимаю, что это, вероятно, не те же алгоритмы оптимизации, которые используются под древесиной.

В частности, я использую стандартный набор данных по Default (используемый в книге ISL ). Следующий код Python считывает данные в 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()) По 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()) по 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()) 

Число значений по умолчанию: 333.

Всего насчитывается 10000 примеров, всего 333 положительных

Логистическая регрессия в R

Я использую следующие

 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)) по 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)) по 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)) по 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)) по 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)) по 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)) По 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)) по 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)) 

Результат следующий.

Call: glm (formula = "default ~ balance + income + student", family = binomial, data = Default)

Оставшиеся осадки: Мин. 1Q Медиана 3Q Макс.
-2,4691 -0,1418 -0,0557 -0,0203 3,7383

Коэффициенты:

 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 

(Параметр дисперсии для биномиального семейства, взятого за 1)

 Null deviance: 2920.6 on 9999 degrees of freedom Residual 

отклонение: 1571,5 на 9996 градусов свободы AIC: 1579,5

Число итераций по подсчету Фишера: 8

  glm.pred No Yes No 9627 228 Yes 40 105 

1 "оценка 0.9732"

Я слишком ленив, чтобы вырезать и вставить результаты, полученные с помощью statmodels. Достаточно сказать, что они очень похожи на те, что даны Р.

sklearn

Для sklearn я выполнил следующий код.

  • Существует параметр class_weight для учета несбалансированных классов. Я тестировал class_weight = None (без утяжеления – я думаю, что это значение по умолчанию в R), а class_weight = 'auto' (увеличение с обратными частотами, найденными в данных)
  • Я также положил C = 10000, обратный параметру регуляризации, чтобы свести к минимуму эффект регуляризации.

~~

 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)) , 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)) 

Результаты приведены ниже.

 > **************************************** >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 

Я наблюдаю, что для class_weight=None , показатель отлично, но положительный пример не распознается. Точность и отзыв на нуле. Найденные коэффициенты очень малы, особенно перехват. Изменение C ничего не меняет. Для class_weight='auto' вещи кажутся лучше, но у меня все еще есть точность, которая очень низкая (слишком положительная классификация). Опять же, смена C не помогает. Если я модифицирую перехват вручную, я могу восстановить результаты, данные Р. Таким образом, я подозреваю, что здесь существует расхождение между оценкой intecepts в двух случаях. Так как это имеет значение в спецификации тройки (аналог повторной дискретизации вычислений), это может объяснить различия в характеристиках.

Тем не менее, я хотел бы приветствовать любые рекомендации по выбору между этими двумя решениями и помочь понять происхождение этих различий. Благодарю.

  • TypeError: unorderable types: str () <int ()
  • Python Не отрицательная матричная факторизация, которая обрабатывает как нули, так и отсутствующие данные?
  • обновить до версии scikit-learn на Anaconda?
  • Графическая кривая Precision-Recall при использовании кросс-валидации в scikit-learn
  • Почему auc настолько отличается от логистической регрессии sklearn и R
  • sklearn - Перекрестная проверка с несколькими баллами
  • Каков самый простой способ получить tfidf с помощью pandas dataframe?
  • Scikit Learn Gaussian HMM: ValueError: startprob должен суммироваться до 1.0
  • 2 Solutions collect form web for “Сравнение R, statmodels, sklearn для задачи классификации с логистической регрессией”

    Я столкнулся с аналогичной проблемой и закончил публикацию об этом на / r / MachineLearning . Оказывается, разница может быть отнесена к стандартизации данных. Независимо от подхода scikit-learn, используемого для поиска параметров модели, результаты будут лучше, если данные будут стандартизированы. В scikit-learn есть документация, в которой обсуждаются данные предварительной обработки (включая стандартизацию), которые можно найти здесь .

    Результаты

     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 

    Код

     # 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)) 

    Хотя этот пост старый, я хотел дать вам решение. В вашем посте вы сравниваете яблоки с апельсинами. В вашем коде R вы оцениваете «баланс, доход и учащийся» по умолчанию. В вашем коде Python вы оцениваете «баланс и доход» только по умолчанию. Конечно, вы не можете получить одинаковые оценки. Также различия не могут быть отнесены к масштабированию функций, поскольку логистическая регрессия обычно не нуждается в этом по сравнению с километрами.

    Вы имеете право установить высокий уровень C, чтобы не было никакой регуляризации. Если вы хотите иметь тот же результат, что и в R, вам нужно изменить решатель на «newton-cg». Различные решатели могут давать разные результаты, но они все же дают то же объективное значение. Пока ваш решатель сходится, все будет хорошо.

    Вот код, который дает вам те же оценки, что и в R и 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_))) По 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_))) 
    Python - лучший язык программирования в мире.