scikit-learnライブラリのロジスティック回帰(LogisticRegression)を使っていたときに気づいた事象です。
まあまあこの界隈ではありがちですが、「過去に動作していたコードがライブラリ(やパッケージ)のアップデートで動作しなくなる」パターンのお話です。
下記のようなコードでエラーが発生します。具体的にはLogisticRegression()
実行時にL1正規化を行うと「L1正規化はサポートしてないぜ!」っていうエラーになります。
lr_l1 = LogisticRegression(C=C, penalty='l1').fit(X_train, y_train)
エラーの内容はこんな感じ。
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
~/devp/hoge.py in
1 for C, marker in zip([0.001, 1, 100], ['o', '^', 'v']):
----> 2 lr_l1 = LogisticRegression(C=C, penalty='l1').fit(X_train, y_train)
3 print('Training accuracy of l1 logreg with C={:.f3}: {:.2f}'.format(C, lr_l1.score(X_train, y_train)))
4 print('Test accuracy of l1 logreg with C={:.f3}: {:.2f}'.format(C, lr_l1.score(X_test, y_test)))
5 plt.plot(lr_l1.coef_.T, marker, label='C={:.3f}'.format(C))
/usr/local/lib/python3.7/site-packages/sklearn/linear_model/_logistic.py in fit(self, X, y, sample_weight)
1484 The SAGA solver supports both float64 and float32 bit arrays.
1485 """
-> 1486 solver = _check_solver(self.solver, self.penalty, self.dual)
1487
1488 if not isinstance(self.C, numbers.Number) or self.C < 0:
/usr/local/lib/python3.7/site-packages/sklearn/linear_model/_logistic.py in _check_solver(solver, penalty, dual)
443 if solver not in ['liblinear', 'saga'] and penalty not in ('l2', 'none'):
444 raise ValueError("Solver %s supports only 'l2' or 'none' penalties, "
--> 445 "got %s penalty." % (solver, penalty))
446 if solver != 'liblinear' and dual:
447 raise ValueError("Solver %s supports only "
ValueError: Solver lbfgs supports only 'l2' or 'none' penalties, got l1 penalty.
solver
のlbfgs
はl2
かnone
しかサポートしてないよ、っていうエラーですね。lbfgs
なんてコードのどこにも書いてないぞ、ってところなんですが、これはsolver
のデフォルト値です。
下記のように記述するのが正解です。
lr_l1 = LogisticRegression(C=C, penalty='l1', solver='liblinear').fit(X_train, y_train)
solver
にliblinear
を指定するとエラーを解消できます。
ここに書いてあるのですが、LogisticRegression
のsolver
のデフォルト値がアップデートにより変更されたせいです。
Changed in version 0.22: The default solver changed from ‘liblinear’ to ‘lbfgs’ in 0.22.
これにより本来solver
のデフォルト値をliblinear
だと想定して、solver
の設定を記述していないソースコードが軒並み影響を受けて、実行結果が異なったりエラーを吐いたりしているわけです。
scikit-learnのバージョン0.22が公開されたのは2019年12月3日なんですが、それより前に書かれた本だとかブログなんかのソースでsolver
がliblinear
を想定しているものは注意が必要です。
そもそも「Pythonではじめる機械学習」って言う本のソースコードを、最近改めて写経しているときに気づいたんですよね。テキストに書いてあるコードをそのまま記述して実行しているにもかかわらず、実行結果が異なったり実行そのものがエラーになるケースが、このLogisticRegression
を実行しているときに頻発しました。
実行結果が異なるのは、なんかトレーニングデータでの訓練時にrandom_state
の値設定が間違っているかなー、とかプログラムのミス(というか写経ミス)を疑っていたんですが、エラーが出力された時点で「これライブラリ側のせいなんじゃ?」と考えるに至りました。調査してみたらやっぱりライブラリ側の原因じゃん、という話で。
こういう「ライブラリ側の原因で動作が変わる、あるいは動作しなくなる」ケースって、scikit-learnに限った話では全然なくて、この界隈あるあるなんですよね。自分のコーディングミスだと思って調べていた時間が若干もったいなかったなぁ、という感じ。
よく利用するライブラリのバージョンアップは、欠かさずキャッチアップしておきたいものです。