サポートベクターマシン(SVM)
ここでは、“使い方”という立場から、排他的論理和(XOR)例を使って説明する。
SVMネットワークの構成は次のようになっている。
ネットワークは、入力x(図のxは一寸曲者でベクトル、m入力なら(x1,x2,…,xm))が中間ユニットに送られる。各中間ユニットは各教師データに対応して作成するもので、当然中間ユニット数は教師データの数と同じである。各中間ユニットの出力はK(x,xs)はスカラーである(sはs番目の中間ユニットを表す)。中間ユニットの出力には重みwsが掛けられ出力ユニットで加算されて、最終出力yが得られる。
式で書くと、 となる。
SVMの課題は中間ユニットの出力Kの求め方とw(上の式のα*y)の求め方である。
(理論的には線形分離できない空間をK(中間ユニット)で別の超平面に写像し、写像後の空間をwで分離している)
Kはカーネル(核)関数と呼ばれ、いくつかの関数が提案されている。例えば、p次の多項式カーネルK(x,x’)=(xTx’+1)P、やガウシアンカーネルである。以下では2次多項式カーネルを用いた例を示す。
wを求めるにはαを求めて、yを掛ければよい。αを求めるにはニューラルネットの学習のような反復法と、固有値問題を解くような方法がある。以下では簡単な繰り返し法を示す。
排他的論理和(XOR)の実現
学習データはである。
2次多項式カーネルの式は次の通りである。
この式でiはi番目の中間ユニット、すなわちi番目の学習パターンに対応したユニットを示している。
XORのネットの図は以下の通りである。
この図では入力をxa,xbとした。各中間ユニットの出力は、教師データ(は教師データ1の1番目の要素(−1)を示す)を代入すると次のようになる。
KとYをまとめると次のようになる。
ここで、とは、教師データiが入力された時のj番目のユニットの出力、
は教師データの出力iとjの積を表す。
次に、中間層と出力層間の重みを反復計算で求める方法を示す。
wを求めるにはαを求めyを掛ければよい。αを反復計算で求める際は次の手順を行う。
Step1: (i=1~4)に初期値0を入れる。また、次の行列Dijを用意する。
Step2:各教師データ(i=1,..,4)に対して次の処理を行う。
Step2をαが収束するまで繰り返す
ここで、γは学習率、Cはαの上限を決める正の数。
これをプログラムで実行した結果は、α1~α4=0.125(全て同じ)であった。wは次の通りである。w1=0.125*-1=-0.25, w2=0.125, w3=0.125, w4=-0.125。
学習済みのネットでは出力yの正負で入力パターンを2つのクラスに分類する。
上の例はカーネル関数に多項式を用いた、多項式ではKのイメージがつかみにくいが、カーネルをガウシアンにするとRBFというモデルになりイメージがつかみやすくなる。RBFでは、教師データを正規分布で表すので、入力が2つの場合、平面におわんを逆さに置いていくイメージになる。排他的論理和では以下のイメージとなる。
//プログラム
#include <stdio.h>
double
x[4][2]={{-1,-1},{-1,1},{1,-1},{1,1}};
//教師入力データ
double y[4]={-1, 1, 1,-1}; //教師出力データ
double K[4][4], Y[4][4], D[4][4], u[4], xa,
xb; int i,j,k, count;
double E[4];
double C=10.0, gamma=0.01, dalpha, mi, ma,
uk;
double alpha[4]= {0,0,0,0};
double kk(int ii, int jo){ //教師データiが入力された時ユニットjの出力
return
(1+x[ii][0]*x[ii][0]*x[jo][0]*x[jo][0]+2*x[ii][0]*x[ii][1]*x[jo][0]*x[jo][1]
+x[ii][1]*x[ii][1]*x[jo][1]*x[jo][1]+2*x[ii][0]*x[jo][0]+2*x[ii][1]*x[jo][1]);
}
double yy(int ii, int jj){
return (y[ii]* y[jj]);
}
void main() {
int i,j;
printf("K\n");
for(i=0; i<4; i++){
for( j= 0; j<4; j++) {
K[i][j]=
kk(i,j);
printf("%f, ",K[i][j]);
}
printf("\n");
}
printf("Y\n");
for(i=0; i<4; i++){
for( j= 0; j<4; j++) {
Y[i][j]=
yy(i,j);
printf("%f, ",Y[i][j]);
}
printf("\n");
}
printf("D\n");
for(i=0; i<4;
i++){
for( j= 0; j<4; j++) {
D[i][j]=
Y[i][j]*K[i][j];
printf("%f, ",D[i][j]);
}
printf("\n");
}
for(count=0;
count<1000; count++) {
for(i=0; i<4; i++){
E[i]=alpha[0]*D[i][0]+alpha[1]*D[i][1]+alpha[2]*D[i][2]+alpha[3]*D[i][3];
if
((gamma*(1.0-E[i]))> -alpha[i]) ma=gamma*(1.0-E[i]);
else ma=
-alpha[i];
if( ma<
(C-alpha[i])) dalpha= ma;
else dalpha= C-
alpha[i];
//
dalpha= min{ max{ gamma*(1.0-E[i]), alpha[i]}, C-alpha[i]};
alpha[i]+=
dalpha;
}
}
printf("alpha\n");
for(i=0;
i<4; i++)
printf("%f,",alpha[i]);
printf("\n");
//report1
printf("report1\n");
for(i=0; i<4; i++){
for(j=0;
j<4; j++){
xa=x[i][0]; xb=x[i][1];
u[j]= (1+xa*xa*x[j][0]*x[j][0]+2*xa*xb*x[j][0]*x[j][1]
+xb*xb*x[j][1]*x[j][1]+2*xa*x[j][0]+2*xb*x[j][1]);
printf("%f,",u[j]);
}
printf("\n");
}
//report2
printf("report2\n");
for(i=0; i<10; i++){
for(j=0;
j<10; j++){
xa=(double)i -5.0; xb=(double)j -5.0;
uk=0.0;
for(k=0;k<4;k++){
uk= uk+ (1+xa*xa*x[k][0]*x[k][0]+2*xa*xb*x[k][0]*x[k][1]
+xb*xb*x[k][1]*x[k][1]+2*xa*x[k][0]+2*xb*x[k][1])*alpha[k]*y[k];
}
if(uk>0) printf("+");
else printf("-");
}
printf("\n");
}
}