多項式カーネルに対応する特徴ベクトルを可視化する
多項式カーネルによって写像される特徴空間を可視化してカーネル法の挙動を把握します.
線形分離不可能なデータ
以下のように半径が異なる円周上に存在する2クラスのデータを生成します.
import seaborn as sns import pandas as pd import matplotlib.pyplot as plt import numpy as np def toy_dataset(n1, n2): # class 1 data set rs = np.random.random(n1) r = 1.0 X1 = np.c_[r * np.cos(2*np.pi*rs), r * np.sin(2*np.pi*rs)] Y1 = ['class1' for _ in range(n1)] # class 2 data set rs = np.random.random(n2) r = 2.0 X2 = np.c_[r * np.cos(2*np.pi*rs), r * np.sin(2*np.pi*rs)] Y2 = ['class2' for _ in range(n2)] # concat X = np.r_[X1, X2] Y = np.r_[Y1, Y2] x_df = pd.DataFrame(data = X, columns = ['x', 'y']) y_series = pd.Series(Y, name = 'class') df = pd.concat([x_df, y_series], axis=1) return df # seed np.random.seed(0) # dataset num = 100 df = toy_dataset(n1=num, n2=num) # dataset scatter c1_df = df[df['class'] == 'class1'] c2_df = df[df['class'] == 'class2'] sns.scatterplot(c1_df['x'], c1_df['y'], label='class1') sns.scatterplot(c2_df['x'], c2_df['y'], label='class2') plt.show()
上のような2クラスのデータは直線を描いて分離することができない線形分離不可能なデータです.このようなデータを識別するためには直線を描いて分離できるような空間に変換したほうが都合がよいです.次のカーネル法でその問題を解決します.
カーネル法
先程のデータを線形分離可能な特徴空間に写像するための写像関数をとします.カーネル法では明示的にを定義せずに特徴空間の内積をカーネル関数によって定義することで写像関数の特徴空間で分離して識別することが可能となる手法の総称です.今回はそのカーネル関数によってどのような特徴空間に写像されて識別しているのかを可視化します.
多項式カーネル
(式が揃っていないのは許して...分からなかった...)
多項式カーネルはパラメータによって次の定義されるカーネル関数です.
]として,のときの特徴ベクトルは次のようになります.
よって特徴ベクトル ]となる.
同様に,のときの特徴ベクトルは
よって特徴ベクトル ]となる.
が3以上の場合は同じように計算すればいいので省略します.
SVMによる識別
さきほどの線形分離不可能なデータをSVMによって識別してみます.その際に多項式カーネルのd(次数)を変えて識別性能を比較します.
トレーニング,テストデータの準備
from sklearn.svm import SVC from sklearn.metrics import accuracy_score np.random.seed(0) # train dataset train_num = 100 df = toy_dataset(n1=train_num, n2=train_num) # test dataset test_num = 30 test_df = toy_dataset(n1=test_num, n2=test_num)
d(次数)が偶数の場合
print('even degree') for d in range(2, 11, 2): clf = SVC(gamma = 'auto', kernel='poly', degree=d) clf.fit(df[['x', 'y']], df['class']) y_pred = clf.predict(test_df[['x', 'y']]) print('degree =', d, 'acc:', accuracy_score(test_df['class'], y_pred))
出力
even degree degree = 2 acc: 1.0 degree = 4 acc: 1.0 degree = 6 acc: 1.0 degree = 8 acc: 1.0 degree = 10 acc: 1.0
d(次数)が奇数の場合
print('odd degree') for d in range(1, 10, 2): clf = SVC(gamma = 'auto', kernel='poly', degree=d) clf.fit(df[['x', 'y']], df['class']) y_pred = clf.predict(test_df[['x', 'y']]) print('degree =', d, 'acc:', accuracy_score(test_df['class'], y_pred))
出力
odd degree degree = 1 acc: 0.5166666666666667 degree = 3 acc: 0.6833333333333333 degree = 5 acc: 0.6833333333333333 degree = 7 acc: 0.6666666666666666 degree = 9 acc: 0.7
まとめ
横軸をd(次数),縦軸をAccuracyとしてプロットしてみます.
dが偶数の場合は識別可能に対して,奇数の場合は識別不可能であることが分かります.なぜこのような結果になったのかを次に特徴ベクトルを可視化して調べてみます.
特徴ベクトルの可視化
特徴ベクトル
多項式カーネルの特徴ベクトルは次のように二項定理から計算することが可能です.
def poly_feature(x, d): n = x.shape[0] Z = np.zeros((n, d+1)) for i in range(d+1): # 二項定理 a = np.sqrt(comb(d, i, exact=True)) Z[:,i] = a * (x[:,0]**(d-i)) * (x[:,1]**(i)) return Z
これによって計算できた特徴ベクトルをseabornのペアプロット図を使って可視化してみます.
from scipy.special import comb from itertools import combinations for d in range(1, 11): z = poly_feature(df[['x', 'y']].values, d) columns = [ 'feature' + str(i) for i in range(d+1)] feature_df = pd.DataFrame(data = z, columns = columns) feature_df = pd.concat([feature_df, df['class']], axis=1) g = sns.pairplot(feature_df, hue='class', vars=feature_df.columns[:-1]) g.fig.suptitle('degree = ' + str(d)) plt.show()
d(次数)が偶数の場合
偶数の場合は一部(右上,左下)の特徴空間を見ると線形分離可能な空間に写像されていることがよくわかります.
d(次数)が奇数の場合
奇数の場合はどの写像した空間を見ても線形分離できないことが確認できます.