静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念



【python】sklearnでQuadraticDiscriminantAnalysis(二次判別分析)を試す

はじめに

 線形判別分析は非線形な分布に対応できないのでだいたいイマイチなパフォーマンスになるのですが、QDA(二次判別分析)だと若干緩和されます。

 二次判別分析はその名の通り分離境界が二次関数になります。ということは、非線形性はありますが、大した非線形ではないので複雑な分布には対応できません。

 まあ、一応やってみます。

実験

 以下のようなコードで、circles, moons, xorを試してみました。例によってこの辺を参考にしています。

Classifier comparison — scikit-learn 0.21.2 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()

結果

 こうなりました。

result.png
result.png

 円形のデータ、XORは二次多項式で分離境界を表現できます。これはロジスティック回帰でやったときもわかっていたことです。

www.haya-programming.com

 しょせん2次なので、moonsは厳しいというかこれなら線形でやったほうが良いかもな結果になっています。まあ、仕方ないですね。

 実際問題として、特に高次元になれば複雑な分離境界はそこまで必要ないことが多い気がするので、二次程度で済ませるというのはいい選択です。そういう意味では役に立ちますが、SVMの多項式カーネルなどもあるので素のQDAを使うかは微妙、といったあたりでしょうか。

まとめ

 使いみちが難しそうですが、面白いのでうまく活用してみたい気もします。