LoginSignup
1
1

More than 3 years have passed since last update.

【機械学習】カーネル密度推定を使った教師あり学習 その2

Last updated at Posted at 2020-08-01

カーネル密度推定を使った教師あり学習

この記事は機械学習の初心者が書いています。
予めご了承ください。

前回の記事はこちら
次の記事はこちら

カーネル密度推定と教師あり学習の関連性

私が勝手に関連付けました。
詳しいことは(あまり詳しくありませんが)前回の記事をご参照ください。
簡単にまとめると「カーネル密度推定を教師あり学習の分類器に使ってみた!」です。

オブジェクト指向

前回の記事でまとめたスクリプトを改造して、オブジェクト指向にしてみました。
名前は「Gaussian kernel-density estimate classifier(ガウスカーネル密度推定分類器)」、略して「GKDEClassifier」です。いま勝手に名付けました。

↓スクリプト↓

import numpy as np

class GKDEClassifier(object):

    def __init__(self, bw_method="scotts_factor", weights="None"):
        # カーネルのバンド幅
        self.bw_method = bw_method
        # カーネルのウェイト
        self.weights = weights

    def fit(self, X, y):
        # yのラベル数
        self.y_num = len(np.unique(y))
        # 推定した確率密度関数を格納するリスト
        self.kernel_ = []
        # 確率密度関数を格納
        for i in range(self.y_num):
            kernel = gaussian_kde(X[y==i].T)
            self.kernel_.append(kernel)
        return self

    def predict(self, X):
        # 予測ラベルを格納するリスト
        pred = []
        # テストデータのラベル別確率を格納するリスト
        self.p_ = []
        # ラベル別確率を格納
        for i in range(self.y_num):
            self.p_.append(self.kernel_[i].evaluate(X.T).tolist())
        # ndarray化
        self.p_ = np.array(self.p_)
        # 予測ラベルの割り振り
        for j in range(self.p_.shape[1]):
            pred.append(np.argmax(self.p_.T[j]))
        return pred

ラベルは0, 1, 2...(非負整数の小さい順)に割り振ってください。
もしかして:LabelEncoder

(2020/8/5 追記:その3で修正後のコードを公開しています)

__init__メソッド

オブジェクトの初期化を行います。
ここではカーネル密度推定で必要なパラメータ、つまりSciPyのgaussian_kdeの初期化に必要な引数を指定します。
今回はgaussian_kdeのデフォルト値と同じ値を設定しました。

fitメソッド

教師データを用いて学習を行います。
gaussian_kdeでカーネル密度推定を行ったあと、ラベル0の推定密度関数、ラベル1の推定密度関数……を順に格納していきます。

predictメソッド

テストデータの予測を行います。

for i in range(self.y_num):
            self.p_.append(self.kernel_[i].evaluate(X.T).tolist())

ここでは、kernel_から推定密度関数を一つずつ取り出し、テストデータの確率密度を計算しています。

その後のスクリプトが何やらごちゃごちゃしています。もっと簡潔に書きたかったのですが、なかなか思い通りの挙動をしなくて……
コーディング初心者あるある。動けばよい。動くだけマシ。

というわけでオブジェクト指向のガウスカーネル密度推定分類器、完成です。

wineデータセット編

PCAとの組み合わせ

wineデータセットには特徴量が13個ありますが、標準化したあと4次元まで次元削減します。
次元削減後のデータで学習と分類を行ってみましょう。

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# データセットの読み込み
wine = datasets.load_wine()
X = wine.data
y = wine.target

# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=1, stratify=y)

# 標準化
sc = StandardScaler()
sc = sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

# 次元削減
pca = PCA(n_components=4)
X_train_pca = pca.fit_transform(X_train_std)
X_test_pca = pca.transform(X_test_std)

# 学習と予測
f = GKDEClassifier()
f.fit(X_train_pca, y_train)
y_pred = f.predict(X_test_pca)

結果は……?

from sklearn.metrics import accuracy_score

print(accuracy_score(y_test, y_pred))
# 0.9722222222222222

わーい。
テストデータは36個ですから、正解率は35/36。なかなかです。

次元削減なし

どうなるでしょうか。

# 学習と予測
f = GKDEClassifier()
f.fit(X_train_std, y_train)
y_pred = f.predict(X_test_std)

print(accuracy_score(y_test, y_pred))
# 0.9722222222222222

結果:同じ。

円型データセット編

円型のデータセットを作ってみました。

from sklearn.datasets import make_circles
from matplotlib import pyplot as plt

X, y = make_circles(n_samples=1000, random_state=1, noise=0.1, factor=0.2)
plt.scatter(X[y==0, 0], X[y==0, 1], c="red", marker="^", alpha=0.5)
plt.scatter(X[y==1, 0], X[y==1, 1], c="blue", marker="o", alpha=0.5)
plt.show()

gkde_c.png

中心部と外縁部でラベルが異なります。
正しく分類できるかな?

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,
random_state=1, stratify=y)

f = GKDEClassifier()
f.fit(X_train, y_train)
y_pred = f.predict(X_test)

print(accuracy_score(y_test, y_pred))
# 0.9933333333333333

結論:大勝利。

最後に

ここまでいい感じに分類できていますが、実は大事なことを忘れています。
それは、この分類方法の学術的な正しさです。次回はその議論をしようと思います。

その3につづく

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1