「Pythonによるロジスティック回帰のエラーのデバッグのベストプラクティス」
「Pythonによるロジスティック回帰のエラーデバッグのベストプラクティス」
非構造化の実世界データを使用してパフォーマンスを最適化する
ロジスティック回帰(LR)の基本については多くのことが書かれています-その多用途性、実績のあるパフォーマンス、基になる数学さえも。しかし、実際にLRを実装し、避けられないエラーをデバッグする方法を知ることは、より困難です。その情報は、QAウェブサイトや学術論文に深く埋もれているか、経験から得るものです。
現実には、アイリスデータセットのようにアイコンとなるほどのクリーンな出力ばかりではありません。大規模なデータセット(おそらく仕事で使用しているデータセット)では、LRモデルは問題に遭遇する可能性があります。信じられないほど高い係数。NaN標準誤差。収束しない。
何が原因なのでしょうか?データの問題?モデルの問題?
少し整理するために、私はいくつかの調査を行い、一般的なLRのエラー、原因、および可能な解決策のリストをまとめました。
上記の表は完全なものではありませんが、すべて1つの場所にまとめられています。以下では、作り物のデータ(不均衡でスパース、カテゴリカル…しかし典型的なデータ)を使用してこれらのエラーを人工的に再現し、修正します。
しかし、まずは。
簡単な背景
LRについて考え始めるときには、これらのモデル特有のいくつかのことを覚えておく必要があります:
- LRは厳密には確率モデルであり、分類器ではありません
- LRは線形の決定境界が必要です。機能と目的変数の間に線形関係があると仮定しています。これはビジュアル、クロスタブ、またはscipy.spatialのConvexHull分析などで確認できます
- 欠損値や重要な外れ値は許されません
- 特徴量の数が増えると、多重共線性や過学習のリスクが高くなります(それぞれVIFと正則化で修正可能)
- Pythonで最も人気のあるLRパッケージはsklearnとstatsmodelsから提供されています
では、一般的な問題について見ていきましょう。
問題1:収束の警告、最適化が失敗しました
Pythonのstatsmodelsパッケージを使用して、基本的なLRを実行すると、「ConvergenceWarning: Maximum Likelihood optimization failed to converge.」という警告が表示される場合があります。
import statsmodels.formula.api as smfmodel = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing'))", data=df_a) results = model.fit(method='bfgs')print(results.summary())
係数と標準誤差は正常に表示される場合もあります。また、sklearn.linear_modelパッケージのLogisticRegressionからも取得できます。
結果が提供された場合、ここで終わるのが誘惑されるかもしれません。しかし、そうはしません。最良(最小)のコスト関数とモデルの適合性を得るために、モデルが収束することを確認したいのです[1]。
statsmodelsまたはsklearnでsolver/methodを変更するか、maxiterパラメーターを増やすことでこれを解決できます。sklearnでは、チューニングパラメーターCを下げることでL2正則化を増やすこともできます。その際は対数スケール(100、10、1、0.1、0.01など)で反復的にテストすることができます[2]。
私の場合、maxiter=300000に増やしてモデルが収束しました。うまくいった理由は、モデル(例えばソルバー)により多くの収束試行と最適なパラメータを見つける機会を与えたからです[3]。そのような係数やp値などのパラメータは、実際により正確になります。
問題2:特徴量を追加しましたが、LRの出力が更新されませんでした
これは見落としがちですが、診断は簡単です。多くの特徴量で作業している場合やデータのクリーニングで見つけられなかった場合、LRモデルに定数に近いまたは一つのレベルしかないカテゴリカルな特徴量が誤って含まれている可能性があります…問題ですね。そのような特徴量の含まれることにより、係数と標準エラーは警告なしに計算されます。
ここでは問題のある特徴量なしのモデルと出力を示します。
model = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing'))", data=df_a) results = model.fit(method='bfgs',maxiter=30000)print(results.summary())
さあ、一つのレベルの特徴量を追加しましょう。その参照カテゴリを「Missing」とします。
model = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing')) + \ C(type,Treatment('Missing'))", data=df_a) results = model.fit(method='bfgs', maxiter=300000)print(results.summary())
そして、その余分な特徴量の影響がわかります…全く影響しませんでした。R-squaredの値は変わらず、上下に強調されています。
ロジスティック回帰は、一つのレベルまたは一定の値を持つ特徴量については有意義な推定を行うことができず、その特徴量をモデルから除外する可能性があります。これはモデル自体には決定的な影響はありませんが、ベストプラクティスとしては、ポジティブな影響を与えている特徴量のみをモデルに含めることです。
特徴量のsimple value_counts()は、一つのレベルしか持っていないことを示しており、その特徴量をモデルから削除したいことを示しています。
問題3:”Hessianの反転に失敗しました”(およびNaNの標準エラー)
これは大きな問題であり、よく起こります。新しいエラーを作成しましょう。LRモデルに4つの機能を追加します:VA、VB、VC、およびVI。それらはすべてカテゴリカルで、それぞれ3つのレベル(0、1、および「Missing」としてのリファレンス)があります。
model = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing')) + \ C(type,Treatment('Missing')) + \ C(VA,Treatment('Missing')) + \ C(VB,Treatment('Missing')) + \ C(VC,Treatment('Missing')) + \ C(VI,Treatment('Missing'))", data=df_a) results = model.fit(method='bfgs', maxiter=300000)print(results.summary())
そして、新しいエラーがこちらです:
出力を探索すると、係数が表示されますが、標準エラーやp値は表示されません。Sklearnも係数を提供しています。
しかし、なぜNaNの値が出るのでしょうか? そして、なぜヘシアンを反転する必要があるのでしょうか?
多くの最適化アルゴリズムは、尤度関数を最大化するためにヘシアンの逆行列(またはその推定値)を使用します。したがって、反転が失敗した場合、ヘシアン(厳密には対数尤度の2階微分)は正定値ではありません[4]。視覚的には、いくつかの特徴は大きな曲率を持ち、いくつかの特徴は小さな曲率を持っているため、モデルはパラメータの標準エラーを生成することができません。
より具体的には、ヘシアンが逆行列にならない場合、コンピュータ上で何らかの変更を加えても逆行列にすることはできません[5]。
これの原因は何でしょうか? 最も一般的な原因は次の3つです:
- 観測値よりも特徴変数の数が多い
- カテゴリカル特徴(s)のレベルが非常に低い
- 特徴間に多重共線性が存在する
データセットには特徴(14)に比べて行が多い(約25,000)ため、最初の可能性は無視しても安全です(ただし、この具体的な問題のための解決策が存在します[6])。第3の可能性が関与しているかもしれませんので、分散インフレーション係数(VIF)で確認できます。第2の可能性は診断しやすいですので、そこから始めましょう。
特筆すべきは、VA、VB、VC、およびVIの特徴が主に1と0で構成されていることです。
特に、VIの特徴は「Missing」のカテゴリに非常に低い(相対的な)頻度を持っており、実際には0.03%(9/24,874)程度です。
ビジネスコンテキストで1と「Missing」を組み合わせても問題ないことを確認しましょう。または、少なくとも、この方法でデータを再構築することによる潜在的な影響が既知のエラーを含むモデルよりもはるかに少ないでしょう(そして標準エラーもありません)。
したがって、VIの代わりにVI_altを作成しました。
モデルでVI_altをVIと交換すると、
モデル= smf.logit("target_satisfaction ~ C(年齢、Treatment('0-13'))+ \ C(educ、Treatment('Some High School'))+ \ C(emp、Treatment('Not Employed'))+ \ C(gender、Treatment('Male'))+ \ C(hispanic、Treatment('N'))+ \ C(dept、Treatment('Medical Oncology'))+ \ C(sign_status、Treatment('Activated'))+ \ C(mode、Treatment('Internet'))+ \ C(inf、Treatment('Missing'))+ \ C(type、Treatment('Missing'))+ \ C(VA、Treatment('Missing'))+ \ C(VB、Treatment('Missing'))+ \ C(VC、Treatment('Missing'))+ \ C(VI_alt、Treatment(0))"、データ= df_a)結果= model.fit(method ='bfgs'、maxiter = 300000)print(results.summary())
全体的なモデルにわずかな改善があります。エラーなしで収束し、係数が表示され、標準誤差も表示されるようになりました。依然として適切に適合していないモデルですが、これは目標とする動作モデルです。
Hessianの逆行列が失敗する3番目の可能性は、多重共線性です。マルチコリニアリティは、機械学習の分野で注目されています。なぜなら、(1)LR、線形回帰、KNN、Naive Bayesなど、多くの人気モデルに影響を与える(2)特徴量の除去に対するVIFヒューリスティックは科学よりも芸術であることが明らかになり、(3)結局のところ、選択バイアスの注入または重要なモデル情報の喪失を伴う場合に、最初にそのような特徴量を除去することについての専門家の意見が分かれます[5–9]。
その議論には踏み込まないでしょう。それは深いからです。
ただし、VIFを計算する方法を示しますので、ご自身で結論を出していただければと思います。
まず、VIFは実際には「他のすべての特徴量によってどの程度特徴量が共同で説明されているか」ということを問いかけます。特徴量のVIFの推定値は、他の特徴量との線形依存関係がある場合に「膨張」します。
データセットのすべての特徴量を考慮して、以下のVIFを計算しましょう。
from statsmodels.stats.outliers_influence import variance_inflation_factor
variables = results.model.exog
vif = [variance_inflation_factor(variables, i) for i in range(variables.shape[1])]
vifs = pd.DataFrame({'variables': results.model.exog_names, 'vif': ['%.2f' % elem for elem in vif]})
vifs.sort_index(ascending=False).head(14)
この一部のリストからわかるように、VA、VB、VDの特徴量はいずれもVIFが5の「経験則」のしきい値をはるかに超えています。ただし、このようなヒューリスティックの注意点が2つあります。
- 5のしきい値は他の特徴量と関連しています。例えば、ほとんどの特徴量のVIFが7未満であり、その一部の特徴量のVIFが7を超える場合、7はより妥当なしきい値となります。
- 参照カテゴリーのケース数が他のレベルと比較して小さいカテゴリー変数は、モデルの他の変数と相関していなくても高いVIFを示します[8]。
この場合、VA、VB、VCの特徴量はすべて高い共線性を示しています。交差集計で確認し、連続変数の場合はピアソンの相関行列も確認できます。
この問題の一般的な解決策は、特徴量を1つずつ系統的に削除し、すべてのVIFが選択したしきい値以下になるまでVIFを確認することです。ビジネスコンテキストと目的変数の両方に関連する特徴量からの説明力を失わないように注意する必要があります。カイ二乗検定や可視化などの検証統計テストは、2つの選択肢の間でどの特徴量を削除するかを決定するのに役立ちます。
VBを削除し、VIFの変化を確認しましょう:
モデル= smf.logit("target_satisfaction ~ C(年齢、Treatment('0-13'))+ \ C(educ、Treatment('Some High School'))+ \ C(emp、Treatment('Not Employed'))+ \ C(gender、Treatment('Male'))+ \ C(hispanic、Treatment('N'))+ \ C(dept、Treatment('Medical Oncology'))+ \ C(sign_status、Treatment('Activated'))+ \ C(mode、Treatment('Internet'))+ \ C(inf、Treatment('Missing'))+ \ C(type、Treatment('Missing'))+ \ C(VA、Treatment('Missing'))+ \ C(VC、Treatment('Missing'))+ \ C(VI_alt、Treatment(0))"、データ= df_a)結果= model.fit(method ='bfgs'、maxiter=300000)print(results.summary())
VAとVCはまだ無限遠でVIF(変数の膨張因子)を持っています。さらに悪いことに、モデルはまだNaNの標準エラーをレンダリングしています(表示されていません)。なので、VCを削除しましょう。
model = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing')) + \ C(type,Treatment('Missing')) + \ C(VA,Treatment('Missing')) + \ C(VI_alt,Treatment(0))", data=df_a) results = model.fit(method='bfgs', maxiter=300000)print(results.summary())
最後に、モデルは標準エラーを生成しますので、変数VAのVIFがまだ5を超えていても問題ありません。なぜなら、前述の第2の注意点により、参照カテゴリが他のレベルに比べて小数の場合ですから。
追加クレジット:VA、VB、およびVCが目的変数を説明するために重要であり、モデルに必要なことを絶対に知っているとしましょう。追加の特徴量がある場合、最適化は複雑な空間で動作していると想定して、「Inverting Hessian failed」を新しいソルバーやスタートパラメータ(sklearnのstart_params)を選択することで回避できるかもしれません。また、線形の境界を仮定しない新しいモデルをトレーニングすることも選択肢の一つです。
問題4:「完全な準分離」エラー(および非現実的に大きな係数と/または標準エラー)
大きな係数と高い正答率を見ると興奮するかもしれません。しかし、これらの推定値はしばしば信じられないほど大きく、別の一般的な問題、完全な分離によって引き起こされます。
完全(またはほぼ完全)分離は、1つまたは複数の特徴量が目的変数と強く関連しているときに発生します。実際、それらはほとんど同一である可能性があります。
このエラーを再現するために、目的変数であるtarget_satisfactionを取り、それに95%似ている新たな特徴量を生成することによって、このエラーを生成することができます。
df_a['new_target_satisfaction'] = pd.concat([pd.DataFrame(np.where(df_a.target_satisfaction.iloc[:1000]==1,0,df_a.target_satisfaction[:1000]),columns=["target_satisfaction"]),pd.DataFrame(df_a.target_satisfaction.iloc[1000:],columns=['target_satisfaction'])],axis=0)
そして、new_target_satisfactionをモデルに追加します。
model = smf.logit("target_satisfaction ~ C(age,Treatment('0-13')) + \ C(educ,Treatment('Some High School')) + \ C(emp,Treatment('Not Employed')) + \ C(gender,Treatment('Male')) + \ C(hispanic,Treatment('N')) + \ C(dept,Treatment('Medical Oncology')) + \ C(sign_status,Treatment('Activated')) + \ C(mode,Treatment('Internet')) + \ C(inf,Treatment('Missing')) + \ C(type,Treatment('Missing')) + \ C(VA,Treatment('Missing')) + \ C(VI_alt,Treatment(0)) + \ C(new_target_satisfaction,Treatment(0))", data=df_a) results = model.fit(method='lbfgs', maxiter=1000000)print(results.summary())
係数と標準エラーを表示しますが、準分離の警告が表示されます:
特徴はばかげて高い係数を持ち、約70,000,000のオッズ比であり、これは信じがたいものです。
その背後には、「あまりにもよく分離する」特徴が膨張した傾き、したがって大きな係数と標準誤差を作り出すということがあります。また、ロジスティック回帰モデルは収束しない可能性もあります[10]。
誤って分類されたケースを除去した場合、これら2つの赤い点は完全な分離を防ぐのに実際に役立つことができ、ロジスティック回帰モデルの収束と標準誤差の推定値を現実的にすることができました。
sklearnの完全な分離について覚えておくべきことは、この場合、近似的な正確さを持つモデルを出力することができるが、実際には新しいターゲット new_target_satisfaction がターゲット target_satisfaction のほぼ重複したものであるということです。
categorical_features = ['educ','emp','gender','hispanic','dept','sign_status','mode','inf','type','VA','VI_alt','new_target_satisfaction']df1_y = df_a['target_satisfaction']df1_x = df_a[['educ','emp','gender','hispanic','dept','sign_status','mode','inf','type','VA','VI_alt','new_target_satisfaction']]# すべてのカテゴリ変数用のパイプラインの作成categorical_transformer = Pipeline(steps=[ ('ohe', OneHotEncoder(handle_unknown='ignore'))])# 全体のカラム変換器の作成col_transformer = ColumnTransformer(transformers=[ ('ohe', OneHotEncoder(handle_unknown='ignore'), categorical_features)], # ('num', numeric_transformer, numeric_features)], remainder='passthrough')lr = Pipeline(steps = [('preprocessor', col_transformer), ('classifier', LogisticRegression(solver='lbfgs',max_iter=200000))])#['liblinear', 'newton-cg', 'lbfgs', 'sag', 'saga']X_train, X_test, y_train, y_test = train_test_split(df1_x, df1_y, test_size=0.2, random_state=101)lr_model = lr.fit(X_train, y_train)y_pred = lr_model.predict(X_test)# 閾値のリセットを活用するためのもの# THRESHOLD = 0.5# y_pred = np.where(lr_model.predict_proba(X_test)[:,1] > THRESHOLD, 1, 0)print(classification_report(y_test, y_pred))
最も一般的な解決策は、単にその特徴を削除することです。しかし、代替案の数が増えています:
- Firthの補正を適用する。これは「罰則化」された尤度関数を最大化します。現在、statsmodelsにはこの機能がありませんが、Rにはあります[11]
- 罰則付き回帰も機能する場合があります。具体的には、ソルバー、ペナルティ、および学習率の組み合わせをテストします[2]。このモデルでは new_target_satisfaction をモデルに残し、さまざまな組み合わせを試しましたが、特にこの場合にはほとんど差がありませんでした
- 問題のある特徴のいくつかのランダムに選択された観測値を手動で交換すると、それがターゲットと完全に分離されなくなります。上記の写真の赤い丸を戻すことなどです[8, 10]。この特徴をターゲットとのクロスタブで実行することで、交換するケースのパーセントを決定するのに役立ちます。これをする際に自問するかもしれません。なぜ?なぜそれを受け入れるためにこのように1つの特徴を再構築しているのか?あなたのためにより良く眠れるように、一部の研究は、完全な分離はモデルの症状であり、データではないと主張しています[8, 11]
- 最後に、一部の異議を唱える実践者は、係数が非常に高いことに何も問題がないと考えています[8]。非常に高いオッズ比は単に関連の予測を示し、ほぼ完璧に予測していることを示唆しています。結果に注意を払い、それで終わりにするべきです。この主張の基礎となるのは、高い係数は情報を評価するウォルド検定と尤度比において、代替仮説による情報の評価を必要とするという不幸な結果です。
結論
ロジスティック回帰は、現実世界のデータセットから生じる課題に取り組むことができれば、確かに多目的でパワフルな手法です。この概要が可能な解決策の良い基礎を提供できることを願っています。どのヒントが最も興味深かったですか?他にはどんなヒントがありますか?
読んでいただき、ありがとうございます。感想をコメントセクションにシェアしていただき、ロジスティック回帰に関する他の問題についても教えてください。また、LinkedInでつながって話をすることも喜んでいます。
以下は私の他の記事もチェックしてみてください。
ベイジアンネットワークを使用して病院の付帯サービスの需要予測を行う方法
引用
- Allison, Paul. Convergence Failures in Logistic Regression. University of Pennsylvania, Philadelphia, PA. https://www.people.vcu.edu/~dbandyop/BIOS625/Convergence_Logistic.pdf
- Geron, Aurelien. Hands-On Machine Learning with Scikit-Learn, Keras & Tensorflow. Second Edition. Published by: O’Reilly, 2019.
- Sckit-learn documentation, sklearn.linear_model.LogisticRegression. https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
- Google Groups discussion. “MLE error: Warning: Inverting hessian failed: Maybe I cant use ‘matrix’ containers?” 2014. https://groups.google.com/g/pystatsmodels/c/aELcgNpg5f8
- Gill, Jeff & King, Gary. What to Do When Your Hessian Is Not Invertible. Alterantives to Model Respecification in Nonlinear Estimation. SOCIOLOGICAL METHODS & RESEARCH, Vol. 33, №1, August 2004 54–87 DOI: 10.1177/0049124103262681. https://gking.harvard.edu/files/help.pdf
- “Variable Selection for a binary classification problem.” StackExchange, CrossValidated. Online at: https://stats.stackexchange.com/questions/64271/variable-selection-for-a-binary-classification-problem/64637#64637
- “In supervised learning, why is it bad to have correlated features.” StackExchange, Data Science. Online at: https://datascience.stackexchange.com/questions/24452/in-supervised-learning-why-is-it-bad-to-have-correlated-features
- “How to deal with perfect separation in logistic regression”. StackExchange, CrossValidated. Online at: https://stats.stackexchange.com/questions/11109/how-to-deal-with-perfect-separation-in-logistic-regression
- Allison, Paul. “When Can You Safely Ignore Multicollinearity”. Statistical Horizons, September 10, 2012. Online at: https://statisticalhorizons.com/multicollinearity/
- Cornell Statistical Consulting Unit. Separation and Convergence Issues in Logistic Regression. Statnews #82. Published: February, 2012. Online at: https://cscu.cornell.edu/wp-content/uploads/82_lgsbias.pdf
- Logistf: Firth’s Bias-Reduced Logistic Regression. R documentation. Published August 18, 2023. Online at: https://cran.r-project.org/web/packages/logistf/index.html
※注意:すべての画像は、特記のない限り、著者によるものです。
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles