web-dev-qa-db-ja.com

ロジスティック回帰の係数scikit-learnとstatsmodels

2つのAPIを使用してロジスティック回帰を実行すると、係数が異なります。この単純な例を使用しても、係数に関して同じ結果は生成されません。また、同じトピックに関する古いアドバイスからのアドバイスに従います。たとえば、sklearnでパラメーターCに大きな値を設定すると、ペナルティがほとんどなくなる(またはpenalty = "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_)

たとえば、上記のコードを実行すると、statsmodelsに1.72276655、sklearnに1.86324749が取得されます。また、複数回実行すると、常に異なる係数が得られます(他の係数よりも近い場合もあります)。

したがって、そのおもちゃの例でも、2つのAPIは異なる係数(オッズ比)を与え、実際のデータ(ここには示されていません)を使用すると、ほぼ「制御不能」になります...

何か不足していますか?カンマの後の少なくとも1つまたは2つの数値など、同様の係数をどのように生成できますか?

1
hellowolrd

コードに問題があります。

まず、ここに示す2つのモデルはnotと同等です。ただし、scikit-learn LogisticRegressionfit_intercept=True(これはデフォルトの設定)、statsmodelsの設定では行いません。 statsmodelsから docs

切片はデフォルトでは含まれていないため、ユーザーが追加する必要があります。 statsmodels.tools.add_constantを参照してください。

これは頻繁に混乱するポイントのようです-例えば scikit-learn&statsmodels-どのR-squaredが正しいですか? (そしてそこに自分の答えも)を参照してください。

もう1つの問題は、バイナリ分類設定を行っているにもかかわらず、LogisticRegressionmulti_class='multinomial'を要求することですが、これは当てはまりません。

3番目の問題は、関連する相互検証済みスレッド ロジスティック回帰:Scikit Learn vs Statsmodels で説明されているとおりです。

Scikit-learnで正則化をオフにする方法はありませんが、チューニングパラメーターCを大きな値に設定することで無効化できます。

これにより、2つのモデルは原則として比較不可能になりますが、ここではC=1e8を設定することで問題なく対処しています。実際、それ以来(2016年)、- docs に従ってpenalty='none'を設定することにより、scikit-learnは正則化をオフにする方法を実際に追加しました:

「なし」の場合(liblinearソルバーではサポートされていません)、正則化は適用されません。

これは、正則化をオフにする標準的な方法と見なす必要があります。

したがって、これらの変更をコードに組み込むと、次のようになります。

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]

配列の最初の要素は切片で、2番目の要素はxの係数です。 scikitについては、次のことを学びます。

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

これらの結果は、マシンの数値精度の範囲内で実質的に同じです。

np.random.seed()の異なる値に対して手順を繰り返しても、上記の結果の本質は変わりません。

6
desertnaut