scikit.learn手法徹底比較! ナイーブベイズ編
scikit.learnの分類手法を比較するこの企画. 今回はナイーブベイズを検証する.
ナイーブベイズはi番目の入力ベクトルの各次元が, クラスラベル
が与えられると互いに独立, すなわち
となると仮定する分類手法である. ただし, は
の次元数,
はi番目の入力ベクトルのj個目の要素を指す.
学習段階では訓練データから各クラスjごとに確率分布を学習し, 分類する際には
が最大になるようなクラスに分類する.
scikit.learnが提供するナイーブベイズのクラスは
- Gaussian Naive Bayes
- Bernoulli Naive Bayes
- Multinomial Naive Bayes
の3つがある.
Gaussian Naive Bayesは分布をガウス分布で表現するクラスである. 訓練データからこのガウス分布のパラメーターを学習する.
Bernoulli Naive Bayesは入力ベクトルの要素が[0,1]の2値を取る場合のナイーブベイズである. 分布はベルヌーイ分布で表現される.
Multinomial Naive Bayesでは, 入力ベクトルの要素がi番目のデータにおいてj個目の要素が何回出現するか(例えば, ある記事において単語soupが何回出現しているか)を表す. なんかテキスト分類とかに使われているらしい.
今回は入力ベクトルとして各次元が画素の値(0-255)なので使えそうなのはGaussianとBernoulliである. Bernoulliの場合はオプションの閾値を設定すると, 入力ベクトルの値が閾値以上の場合は1, 以下なら0として扱う.
なお入力データは正規化している.
Gaussian Naive Bayes
コンストラクタに引数がないシンプルなクラスnaive_bayes.GaussianNBを用いる.
簡単に使えるかと思いきや
/usr/lib/pymodules/python2.7/sklearn/naive_bayes.py:174: RuntimeWarning: divide by zero encountered in log n_ij = - 0.5 * np.sum(np.log(np.pi * self.sigma_[i, :]))
というエラーメッセージが発生し, 真っ当に動作しなかった.
エラーメッセージから察するに, おそらく分散の無い要素(特定のクラスで常に一定の値を取る要素)が存在する場合, 0割りが発生しうまく動作しないのだろう.
そこで, とりあえずの解決策として, 全要素である程度の分散を持つために元のデータにガウス分布から生成したノイズを足しあわせた. その結果正常に動作するようになったが, データ数1万のケースでも分類精度が75%程度でしかもバッドノウハウにも程がある感じだったので, 詳細な検証は取りやめた.
Bernoulli Naive Bayes
調整するパラメーターは次の3つである.
- fit_prior : 事前分布(クラスごとのデータ数の偏り)を考慮するかどうか
- alpha : 各次元において1を取る確率に関する事前分布を制御する
- binarize : 元のデータを2値化するときに用いる閾値
alphaに関しては詳細な説明が必要だろう. alphaを用いることで, クラスcに属するデータのj番目の要素が1となる確率は
と学習される. ここでは学習データにおいてクラスがcである要素の数,
は学習データにおいてクラスがcでかつj番目の要素が1となる要素の数である. alphaが小さい程, この確率は純粋なデータ内の比率に近づく.
元のデータのラベル比率が均等に近いためか, fit_priorはTrueにしてもFalseにしてもあまり結果が変わらなかったため, 以下fit_priorはTrueと設定する.
クロスバリデーションによってalphaをnumpy.logspace(-5, 0, 6), binarizeをnp.linspace(-0.2,1.2,6)から選んだ. その選んだパラメーターに対して得られた結果は次の通り.
訓練データ数 | 正答率 | 学習時間(sec) | 平均予測時間(msec) |
---|---|---|---|
1000 | 0.8134 | 0.0377149582 | 0.0484575987 |
3000 | 0.8166 | 0.1247229576 | 0.0482898951 |
5000 | 0.8152 | 0.221326828 | 0.0484192133 |
10000 | 0.8157 | 0.4449100494 | 0.047160697 |
20000 | 0.8179 | 0.919823885 | 0.0482817888 |
正答率が見事に上がらないw. やはりモデルの仮定に無理があるためか, 2値化の際の情報の損失が激しいのか. 一方, 学習時間はデータ数に対して線形である. また平均予測時間はデータ数に影響を受けない. これらはアルゴリズムを想像すればまあ納得の行く結果である.
クロスバリデーションをして得た感じとしては, 学習時間は全くパラメーターの影響を受けない. また正答率もalphaなら0から1, binarizeなら0から1という常識的な値から選んでいればそこまで変化しなかった.