📈

`statsmodels`と`sklearn`でのロジスティック回帰の挙動の違いについて

2024/07/13に公開

TL;DR

  • Pythonでロジスティック回帰をする方法にはsklearnstatsmodelsがある
  • sklearn.linear_model.LogisticRegressionはデフォルトで正則化(L2=1)がついている
  • statsmodels.api.Logitは正則化がついていない
  • 備えよう

本編

問題設定

seabornに含まれるirisデータセットにおいて、versicolor種であるかどうかを判断するロジスティック回帰モデルを考える。

# %% loading data
iris_df = sns.load_dataset("iris")
iris_df = iris_df[(iris_df['species']=='versicolor') 
                  | (iris_df['species']=='virginica')].reset_index(drop=True)

# %% utility function to comparison.
def result_to_df(features, params):
    out_dict = {
        'features': features,
        'params':params
    }

    out = pd.DataFrame(out_dict)
    
    return out

# %% settings target and features
# features
X = iris_df.drop("species",axis=1)
# versicolor convert from category to binary values.
y = iris_df['species'].map({'versicolor': 0, 'virginica': 1}) 

モデル実装

statsmodels

statsmodelsは切片を定数項としてXに入れないといけないっぽいので、
切片なしでモデルを作ることにする。

clf_statsmodels = sm.Logit(y, X)
clf_statsmodels = clf_statsmodels.fit()
statsmodels_params = result_to_df(features=clf_statsmodels.params.index.values,
                                  params=clf_statsmodels.params.values)
statsmodels_params
#   features        params
# sepal_length    -6.327719
# sepal_width     -6.618187
# petal_length     8.433801
# petal_width     10.282544

sklearn

statsmodelsに合わせて切片なしモデルを組む

clf_sklearn_default = LogisticRegression(random_state=0, fit_intercept=False)
clf_sklearn_default.fit(X, y)
sklearn_default_params = result_to_df(features=X.columns.values,
                                     params=clf_sklearn_default.coef_[0])
sklearn_default_params

結果を見ると明らかに回帰係数の値が異なる。これはsklearnのロジスティック回帰モデルは、
デフォルトでL2正則化がついていることに起因する。
official document

#   features       params
# sepal_length   -1.863342
# sepal_width    -1.646021
# petal_length    2.477247
# petal_width     2.593144

回避策

あんまりstatsmodelsと並行して使うことはないと思うが、値を近くする方法はいくつかある。

1. 正則化パラメータをめっちゃ小さくする

Cという引数が、正則化の強さの逆数であるので、こいつをめっちゃでっかくする。

clf_sklearn_low_penalty = LogisticRegression(
                            random_state=0,
                            fit_intercept=False,
                            C = 1e9)
clf_sklearn_low_penalty.fit(X, y)
sklearn_low_penalty_params = result_to_df(features=X.columns.values,
                                          params=clf_sklearn_low_penalty.coef_[0])
sklearn_low_penalty_params

結果的に近くなる。

#   features        params
# sepal_length    -6.327718
# sepal_width     -6.618181
# petal_length     8.433797
# petal_width     10.282542

2. 正則化しない

penalty=Noneにすると正則化しないらしい。こっちの方が良い。

clf_sklearn_None_penalty = LogisticRegression(
                              random_state=0,
                              fit_intercept=False,
                              penalty=None)

clf_sklearn_None_penalty.fit(X, y)
sklearn_None_penalty_params = result_to_df(features=X.columns.values,
                                          params=clf_sklearn_None_penalty.coef_[0])
sklearn_None_penalty_params
#   features        params
# sepal_length    -6.327718
# sepal_width     -6.618181
# petal_length     8.433798
# petal_width     10.282542

結論

統計モデリングの上では、パラメータの一致性・不偏性を意識する場面があるので、
正則化のないモデルを組んだほうが良いことがある。
sklearnは機械学習の領域で広く使われるライブラリの1つであるから、統計モデリングをこれを使って実施する場面も多いだろうが、正則化項については気をつけた方が良い。
また今回は記述していないが、statsmodelsは回帰診断(モデル適合度など)の評価が可能であったり、パラメータの有意性検定も可能であるので、R等で回帰分析に親しんでいる場合は、こちらを援用した方が障壁は少ないと思う。

実装元

githubに記載している。
https://github.com/8-u8/learning_Python/blob/main/sklearn_logistic/logistic_regression_test.py

Discussion