はじめに
線形判別分析は非線形な分布に対応できないのでだいたいイマイチなパフォーマンスになるのですが、QDA(二次判別分析)だと若干緩和されます。
二次判別分析はその名の通り分離境界が二次関数になります。ということは、非線形性はありますが、大した非線形ではないので複雑な分布には対応できません。
まあ、一応やってみます。
実験
以下のようなコードで、circles, moons, xorを試してみました。例によってこの辺を参考にしています。
Classifier comparison — scikit-learn 0.21.3 documentation
import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from sklearn.datasets import make_moons, make_circles from sklearn.discriminant_analysis import \ LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis def make_xor(): np.random.seed(0) x = np.random.uniform(-1, 1, 300) y = np.random.uniform(-1, 1, 300) target = np.logical_xor(x > 0, y > 0) return np.c_[x, y], target def main(): lda = LinearDiscriminantAnalysis() qda = QuadraticDiscriminantAnalysis() datasets = [ ("circles", make_circles(noise=0.2, factor=0.5, random_state=1)), ("moons", make_moons(noise=0.3, random_state=0)), ("xor", make_xor())] fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(16, 9)) cm_bright = ListedColormap(['#FF0000', '#0000FF']) cm = plt.cm.RdBu for i, (cname, clf) in enumerate([("LDA", lda), ("QDA", qda)]): for j, (dname, (X, y)) in enumerate(datasets): clf.fit(X, y) x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5 y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5 h = 0.1 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:,1] Z = Z.reshape(xx.shape) axes[i, j].scatter(X[:,0], X[:,1], c=y, cmap=cm_bright) axes[i, j].contourf(xx, yy, Z, cmap=cm, alpha=.2) axes[i, j].set_title(cname + " " + dname) plt.savefig("result.png") if __name__ == "__main__": main()
結果
こうなりました。
円形のデータ、XORは二次多項式で分離境界を表現できます。これはロジスティック回帰でやったときもわかっていたことです。
非線形がなんだ! ロジスティック回帰+多項式でやってやる! - 静かなる名辞
しょせん2次なので、moonsは厳しいというかこれなら線形でやったほうが良いかもな結果になっています。まあ、仕方ないですね。
実際問題として、特に高次元になれば複雑な分離境界はそこまで必要ないことが多い気がするので、二次程度で済ませるというのはいい選択です。そういう意味では役に立ちますが、SVMの多項式カーネルなどもあるので素のQDAを使うかは微妙、といったあたりでしょうか。
まとめ
使いみちが難しそうですが、面白いのでうまく活用してみたい気もします。