Коэффициенты для Logisti c Регрессия scikit-learn vs statsmodels - PullRequest
3 голосов
/ 25 мая 2020

При выполнении логистической c регрессии с использованием двух API они дают разные коэффициенты. Даже в этом простом примере он не дает таких же результатов с точки зрения коэффициентов. И я следую советам из более старых советов по тому же topi c, например, установив большое значение для параметра C в sklearn, так как это делает штрафы почти vani sh (или установив штраф = "none").

import pandas as pd
import numpy as np
import sklearn as sk
from sklearn.linear_model import LogisticRegression
import statsmodels.api as sm

n = 200

x = np.random.randint(0, 2, size=n)
y = (x > (0.5 + np.random.normal(0, 0.5, n))).astype(int)

display(pd.crosstab( y, x ))


max_iter = 100

#### Statsmodels
res_sm = sm.Logit(y, x).fit(method="ncg", maxiter=max_iter)
print(res_sm.params)

#### Scikit-Learn
res_sk = LogisticRegression( solver='newton-cg', multi_class='multinomial', max_iter=max_iter, fit_intercept=True, C=1e8 )
res_sk.fit( x.reshape(n, 1), y )
print(res_sk.coef_)

Например, я просто запускаю приведенный выше код и получаю 1.72276655 для statsmodels и 1.86324749 для sklearn. И при многократном запуске он всегда дает разные коэффициенты (иногда ближе, чем другие, но в любом случае). реальные данные (здесь не показаны), они почти "выходят из-под контроля" ...

Я что-то упускаю? Как я могу получить похожие коэффициенты, например хотя бы одно или два числа после запятой?

1 Ответ

8 голосов
/ 25 мая 2020

Есть некоторые проблемы с вашим кодом.

Начнем с того, что две модели, которые вы здесь показываете, не эквивалентны: хотя вы соответствуете своему scikit-learn LogisticRegression с fit_intercept=True (это настройка по умолчанию), вы не делаете этого со своими statsmodels one; из statsmodels docs :

Перехват не включен по умолчанию и должен быть добавлен пользователем. См. statsmodels.tools.add_constant.

Кажется, это частая путаница - см., Например, scikit-learn & statsmodels - какой R-квадрат правильный? (и собственный ответьте и там).

Другая проблема заключается в том, что, хотя вы используете двоичную классификацию, вы запрашиваете multi_class='multinomial' в своем LogisticRegression, чего не должно быть.

Третья проблема заключается в том, что, как объясняется в соответствующем потоке с перекрестной проверкой Logisti c Регрессия: Scikit Learn vs Statsmodels :

Нет способа отключить регуляризацию в scikit-learn, но вы можете сделать его неэффективным, установив параметр настройки C на большое число.

, что делает две модели снова несовместимыми в принципе, но вы успешно обратились к это здесь, установив C=1e8. Фактически, с тех пор (2016 г.) scikit-learn действительно добавил способ отключения регуляризации, установив penalty='none', поскольку, согласно docs :

If 'none' (не поддерживается либлинеарным решателем), регуляризация не применяется.

который теперь следует рассматривать как канонический способ отключения регуляризации.

Итак, включение этих изменения в вашем коде, мы имеем:

np.random.seed(42) # for reproducibility

#### Statsmodels
# first artificially add intercept to x, as advised in the docs:
x_ = sm.add_constant(x)
res_sm = sm.Logit(y, x_).fit(method="ncg", maxiter=max_iter) # x_ here
print(res_sm.params)

Что дает результат:

Optimization terminated successfully.
         Current function value: 0.403297
         Iterations: 5
         Function evaluations: 6
         Gradient evaluations: 10
         Hessian evaluations: 5
[-1.65822763  3.65065752]

с первым элементом массива, являющимся перехватом, а второй - коэффициентом x . В то время как для scikit learn у нас есть:

#### Scikit-Learn

res_sk = LogisticRegression(solver='newton-cg', max_iter=max_iter, fit_intercept=True, penalty='none')
res_sk.fit( x.reshape(n, 1), y )
print(res_sk.intercept_, res_sk.coef_)

с результатом:

[-1.65822806] [[3.65065707]]

Эти результаты практически идентичны, в пределах точности c машины.

Повторение процедуры для разных значений np.random.seed() не меняет сути результатов, показанных выше.

...