2013年10月19日土曜日

【C言語】カーネル回帰分析の実装と検証

非線形な関係を持つデータ間をカーネル法をつかって回帰分析してみます。 また、後半で本手法の問題点も示します。
このようなデータを当てはめてみることにします。
このようになります。高い表現力があることがわかります。

コードはこちら。行列計算にはmeschというライブラリを使いました。
// kernelRegression
// x:data
// dimention: x's dimention
// y: label of x 
// num: number of data
// result: coeffience
double *kernelRegression(double **x,int dimention,double *y,int num,double gamma,double regilarization){
 VEC *label;
 MAT *gram,*ident,*inv;
 int i,j;
 double *result;

 ident = m_get(num,num);
 label = v_get(num);
 memcpy(label->ve,y,sizeof(double)*num);
 gram = m_get(num,num);
 ident = m_get(num,num);
 for(i=0;i<num;i++){
  for(j=0;j<num;j++){
   gram->me[i][j]=kernel(x[i],x[j],dimention,gamma);
  }
 }
 inv=m_add(gram,sm_mlt(regilarization,m_ident(ident),NULL),NULL);
 inv=m_inverse(inv,NULL); 
 VEC *coefficient=v_get(num);
 mv_mlt(inv,label,coefficient);
 result=malloc(sizeof(double)*num);
 memcpy(result,coefficient->ve,sizeof(double)*num);

 m_free(ident);
 m_free(gram);
 m_free(inv);
 v_free(label);
 v_free(coefficient);
 return result;
}}
カーネルがこちら。
double kernel(double *x1,double *x2,int num,double gamma){
 VEC *xVector,*yVector,*diffVector;
 int i;

 xVector=v_get(num);
 memcpy(xVector->ve,x1,sizeof(double)*num);
 yVector=v_get(num);
 memcpy(yVector->ve,x2,sizeof(double)*num);

 diffVector=v_sub(xVector,yVector,NULL);

 double sum=0.0;
 for(i=0;i<num;i++){
  sum+=diffVector->ve[i]*diffVector->ve[i];
 }

 v_free(diffVector);
 v_free(xVector);
 v_free(yVector);
 
 return exp(-gamma*sum);
}
全ソースはgithubに上げています。
https://github.com/y-mitsui/kernelRegression
このように高い表現力を持つカーネル回帰ですが、多項式の次数がサンプル数と同じなのでサンプル数が増えると計算量が増えます。
また、線形の回帰分析と同様、誤差関数に二乗誤差を用いているので外れ値の影響を受けやすく、外れ値に対して積極的に当てはめようと するので、他のデータの当てはまりが悪ることがあります。
これらの問題を抑制させる回帰分析がサポートベクトル回帰で、そちらのほうがよく使われるように思います。

0 件のコメント:

コメントを投稿