Easy Support Vector Machine

Dependents:   WeatherPredictor

Revision:
5:792afbb0bcf3
Parent:
4:01a20b89db32
--- a/SVM.cpp	Mon Feb 16 07:53:14 2015 +0000
+++ b/SVM.cpp	Wed Feb 18 15:01:12 2015 +0000
@@ -2,252 +2,255 @@
 
 SVM::SVM(int dim_sample, int n_sample, float* sample_data, int* sample_label)
 {
-  this->dim_signal = dim_sample;
-  this->n_sample = n_sample;
+    this->dim_signal = dim_sample;
+    this->n_sample = n_sample;
 
-  // 各配列(ベクトル)のアロケート
-  alpha   = new float[n_sample];
-  // grammat = new float[n_sample * n_sample];
-  label   = new int[n_sample];
-  sample  = new float[dim_signal * n_sample];
-  sample_max = new float[dim_signal];
-  sample_min = new float[dim_signal];
+    // 各配列(ベクトル)のアロケート
+    alpha   = new float[n_sample];
+    // grammat = new float[n_sample * n_sample];
+    label   = new int[n_sample];
+    sample  = new float[dim_signal * n_sample];
+    sample_max = new float[dim_signal];
+    sample_min = new float[dim_signal];
+
+    // サンプルのコピー
+    memcpy(this->sample, sample_data,
+           sizeof(float) * dim_signal * n_sample);
+    memcpy(this->label, sample_label,
+           sizeof(int) * n_sample);
 
-  // サンプルのコピー
-  memcpy(this->sample, sample_data,
-          sizeof(float) * dim_signal * n_sample);
-  memcpy(this->label, sample_label,
-          sizeof(int) * n_sample);
+    // 正規化のための最大最小値
+    memset(sample_max, 0, sizeof(float) * dim_sample);
+    memset(sample_min, 0, sizeof(float) * dim_sample);
+    for (int i = 0; i < dim_signal; i++) {
+        float value;
+        sample_min[i] = FLT_MAX;
+        for (int j = 0; j < n_sample; j++) {
+            value = MATRIX_AT(this->sample, dim_signal, j, i);
+            if ( value > sample_max[i] ) {
+                sample_max[i] = value;
+            } else if ( value < sample_min[i] ) {
+                //printf("min[%d] : %f -> ", i, sample_min[i]);
+                sample_min[i] = value;
+                //printf("min[%d] : %f \dim_signal", i, value);
+            }
+        }
+    }
 
-  // 正規化のための最大最小値
-  memset(sample_max, 0, sizeof(float) * dim_sample);
-  memset(sample_min, 0, sizeof(float) * dim_sample);
-  for (int i = 0; i < dim_signal; i++) {
-    float value;
-    sample_min[i] = FLT_MAX;
-    for (int j = 0; j < n_sample; j++) {
-      value = MATRIX_AT(this->sample, dim_signal, j, i);
-      if ( value > sample_max[i] ) {
-        sample_max[i] = value;
-      } else if ( value < sample_min[i] ) {
-        //printf("min[%d] : %f -> ", i, sample_min[i]);
-        sample_min[i] = value;
-        //printf("min[%d] : %f \dim_signal", i, value);
+    // 信号の正規化 : 死ぬほど大事
+    for (int i = 0; i < dim_signal; i++) {
+        float max,min;
+        max = sample_max[i];
+        min = sample_min[i];
+        for (int j = 0; j < n_sample; j++) {
+            // printf("[%d,%d] %f -> ", i, j, MATRIX_AT(this->sample, dim_signal, j, i));
+            MATRIX_AT(this->sample, dim_signal, j, i) = ( MATRIX_AT(this->sample, dim_signal, j, i) - min ) / (max - min);
+            // printf("%f \r\n", MATRIX_AT(this->sample, dim_signal, j, i));
+        }
+    }
+
+    /* // グラム行列の計算 : メモリの制約上,廃止
+    for (int i = 0; i < n_sample; i++) {
+      for (int j = i; j < n_sample; j++) {
+        MATRIX_AT(grammat,n_sample,i,j) = kernel_function(&(MATRIX_AT(this->sample,dim_signal,i,0)), &(MATRIX_AT(this->sample,dim_signal,j,0)), dim_signal);
+        // グラム行列は対称
+        if ( i != j ) {
+          MATRIX_AT(grammat,n_sample,j,i) = MATRIX_AT(grammat,n_sample,i,j);
+        }
       }
     }
-  }
-
-  // 信号の正規化 : 死ぬほど大事
-  for (int i = 0; i < dim_signal; i++) {
-    float max,min;
-    max = sample_max[i];
-    min = sample_min[i];
-    for (int j = 0; j < n_sample; j++) {
-      // printf("[%d,%d] %f -> ", i, j, MATRIX_AT(this->sample, dim_signal, j, i));
-      MATRIX_AT(this->sample, dim_signal, j, i) = ( MATRIX_AT(this->sample, dim_signal, j, i) - min ) / (max - min);
-      // printf("%f \r\n", MATRIX_AT(this->sample, dim_signal, j, i));
-    }
-  }
+    */
 
-  /* // グラム行列の計算 : メモリの制約上,廃止
-  for (int i = 0; i < n_sample; i++) {
-    for (int j = i; j < n_sample; j++) {
-      MATRIX_AT(grammat,n_sample,i,j) = kernel_function(&(MATRIX_AT(this->sample,dim_signal,i,0)), &(MATRIX_AT(this->sample,dim_signal,j,0)), dim_signal);
-      // グラム行列は対称
-      if ( i != j ) {
-        MATRIX_AT(grammat,n_sample,j,i) = MATRIX_AT(grammat,n_sample,i,j);
-      }
-    }
-  }
-  */
+    // 学習関連の設定. 例によって経験則
+    this->maxIteration = 5000;
+    this->epsilon      = float(0.00001);
+    this->eta          = float(0.05);
+    this->learn_alpha  = float(0.8) * this->eta;
+    this->status       = SVM_NOT_LEARN;
 
-  // 学習関連の設定. 例によって経験則
-  this->maxIteration = 5000;
-  this->epsilon      = float(0.00001);
-  this->eta          = float(0.05);
-  this->learn_alpha  = float(0.8) * this->eta;
-  this->status       = SVM_NOT_LEARN;
+    // ソフトマージンの係数. 両方ともFLT_MAXとすることでハードマージンと一致.
+    // また, 設定するときはどちらか一方のみにすること.
+    C1 = FLT_MAX;
+    C2 = 5;
 
-  // ソフトマージンの係数. 両方ともFLT_MAXとすることでハードマージンと一致.
-  // また, 設定するときはどちらか一方のみにすること.
-  C1 = FLT_MAX;
-  C2 = 5;
-
-  srand((unsigned int)time(NULL));
-
+    srand((unsigned int)time(NULL));
 }
 
 // 楽園追放
-SVM::~SVM(void) 
+SVM::~SVM(void)
 {
-    delete [] alpha; delete [] label;
+    delete [] alpha;
+    delete [] label;
     delete [] sample;
-    delete [] sample_max; delete [] sample_min;
+    delete [] sample_max;
+    delete [] sample_min;
 }
 
 // 再急勾配法(サーセンwww)による学習
 int SVM::learning(void)
 {
 
-  int iteration;              // 学習繰り返しカウント
-
-  float* diff_alpha;          // 双対問題の勾配値
-  float* pre_diff_alpha;      // 双対問題の前回の勾配値(慣性項に用いる)
-  float* pre_alpha;           // 前回の双対係数ベクトル(収束判定に用いる)
-  register float diff_sum;    // 勾配計算用の小計
-  register float kernel_val;  // カーネル関数とC2を含めた項
-
-  //float plus_sum, minus_sum;  // 正例と負例の係数和
+    int iteration;              // 学習繰り返しカウント
 
-  // 配列(ベクトル)のアロケート
-  diff_alpha     = new float[n_sample];
-  pre_diff_alpha = new float[n_sample];
-  pre_alpha      = new float[n_sample];
-
-  status = SVM_NOT_LEARN;       // 学習を未完了に
-  iteration  = 0;       // 繰り返し回数を初期化
+    float* diff_alpha;          // 双対問題の勾配値
+    float* pre_diff_alpha;      // 双対問題の前回の勾配値(慣性項に用いる)
+    float* pre_alpha;           // 前回の双対係数ベクトル(収束判定に用いる)
+    register float diff_sum;    // 勾配計算用の小計
+    register float kernel_val;  // カーネル関数とC2を含めた項
 
-  // 双対係数の初期化.乱択
-  for (int i = 0; i < n_sample; i++ ) {
-    // 欠損データの係数は0にして使用しない
-    if ( label[i] == 0 ) {
-      alpha[i] = 0;
-      continue;
-    }
-    alpha[i] = uniform_rand(1.0) + 1.0;
-  }
+    //float plus_sum, minus_sum;  // 正例と負例の係数和
 
-  // 学習ループ
-  while ( iteration < maxIteration ) {
+    // 配列(ベクトル)のアロケート
+    diff_alpha     = new float[n_sample];
+    pre_diff_alpha = new float[n_sample];
+    pre_alpha      = new float[n_sample];
 
-    printf("ite: %d diff_norm : %f alpha_dist : %f \r\n", iteration, two_norm(diff_alpha, n_sample), vec_dist(alpha, pre_alpha, n_sample));
-    // 前回の更新値の記録
-    memcpy(pre_alpha, alpha, sizeof(float) * n_sample);
-    if ( iteration >= 1 ) {
-      memcpy(pre_diff_alpha, diff_alpha, sizeof(float) * n_sample);
-    } else {
-      // 初回は0埋めで初期化
-      memset(diff_alpha, 0, sizeof(float) * n_sample);
-      memset(pre_diff_alpha, 0, sizeof(float) * n_sample);
-    }
+    status = SVM_NOT_LEARN;       // 学習を未完了に
+    iteration  = 0;       // 繰り返し回数を初期化
 
-    // 勾配値の計算
-    for (int i=0; i < n_sample; i++) {
-      diff_sum = 0;
-      for (int j=0; j < n_sample;j++) {
-        // C2を踏まえたカーネル関数値
-        kernel_val = kernel_function(&(MATRIX_AT(sample,dim_signal,i,0)), &(MATRIX_AT(sample,dim_signal,j,0)), dim_signal);
-        // kernel_val = MATRIX_AT(grammat,n_sample,i,j); // via Gram matrix
-        if (i == j) { 
-          kernel_val += (1/C2);
+    // 双対係数の初期化.乱択
+    for (int i = 0; i < n_sample; i++ ) {
+        // 欠損データの係数は0にして使用しない
+        if ( label[i] == 0 ) {
+            alpha[i] = 0;
+            continue;
         }
-        diff_sum += alpha[j] * label[j] * kernel_val; 
-      }
-      diff_sum *= label[i];
-      diff_alpha[i] = 1 - diff_sum;
+        alpha[i] = uniform_rand(1.0) + 1.0;
     }
 
-    // 双対変数の更新
-    for (int i=0; i < n_sample; i++) {
-      if ( label[i] == 0 ) {
-        continue;
-      }
-      //printf("alpha[%d] : %f -> ", i, alpha[i]);
-      alpha[i] = pre_alpha[i] 
-                  + eta * diff_alpha[i]
-                  + learn_alpha * pre_diff_alpha[i];
-      //printf("%f \dim_signal", alpha[i]);
+    // 学習ループ
+    while ( iteration < maxIteration ) {
+
+        printf("ite: %d diff_norm : %f alpha_dist : %f \r\n", iteration, two_norm(diff_alpha, n_sample), vec_dist(alpha, pre_alpha, n_sample));
+        // 前回の更新値の記録
+        memcpy(pre_alpha, alpha, sizeof(float) * n_sample);
+        if ( iteration >= 1 ) {
+            memcpy(pre_diff_alpha, diff_alpha, sizeof(float) * n_sample);
+        } else {
+            // 初回は0埋めで初期化
+            memset(diff_alpha, 0, sizeof(float) * n_sample);
+            memset(pre_diff_alpha, 0, sizeof(float) * n_sample);
+        }
+
+        // 勾配値の計算
+        for (int i=0; i < n_sample; i++) {
+            diff_sum = 0;
+            for (int j=0; j < n_sample; j++) {
+                // C2を踏まえたカーネル関数値
+                kernel_val = kernel_function(&(MATRIX_AT(sample,dim_signal,i,0)), &(MATRIX_AT(sample,dim_signal,j,0)), dim_signal);
+                // kernel_val = MATRIX_AT(grammat,n_sample,i,j); // via Gram matrix
+                if (i == j) {
+                    kernel_val += (1/C2);
+                }
+                diff_sum += alpha[j] * label[j] * kernel_val;
+            }
+            diff_sum *= label[i];
+            diff_alpha[i] = 1 - diff_sum;
+        }
+
+        // 双対変数の更新
+        for (int i=0; i < n_sample; i++) {
+            if ( label[i] == 0 ) {
+                continue;
+            }
+            //printf("alpha[%d] : %f -> ", i, alpha[i]);
+            alpha[i] = pre_alpha[i]
+                       + eta * diff_alpha[i]
+                       + learn_alpha * pre_diff_alpha[i];
+            //printf("%f \dim_signal", alpha[i]);
 
-      // 非数/無限チェック
-      if ( isnan(alpha[i]) || isinf(alpha[i]) ) {
-        fprintf(stderr, "Detected NaN or Inf Dual-Coffience : pre_alhpa[%d]=%f -> alpha[%d]=%f", i, pre_alpha[i], i, alpha[i]);
-        return SVM_DETECT_BAD_VAL;
-      }
+            // 非数/無限チェック
+            if ( isnan(alpha[i]) || isinf(alpha[i]) ) {
+                fprintf(stderr, "Detected NaN or Inf Dual-Coffience : pre_alhpa[%d]=%f -> alpha[%d]=%f", i, pre_alpha[i], i, alpha[i]);
+                return SVM_DETECT_BAD_VAL;
+            }
+
+        }
 
-    }
+        // 係数の制約条件1:正例と負例の双対係数和を等しくする.
+        //                 手法:標本平均に寄せる
+        float norm_sum = 0;
+        for (int i = 0; i < n_sample; i++ ) {
+            norm_sum += (label[i] * alpha[i]);
+        }
+        norm_sum /= n_sample;
 
-    // 係数の制約条件1:正例と負例の双対係数和を等しくする.
-    //                 手法:標本平均に寄せる
-    float norm_sum = 0;
-    for (int i = 0; i < n_sample; i++ ) {
-      norm_sum += (label[i] * alpha[i]);
-    }
-    norm_sum /= n_sample;
+        for (int i = 0; i < n_sample; i++ ) {
+            if ( label[i] == 0 ) {
+                continue;
+            }
+            alpha[i] -= (norm_sum / label[i]);
+        }
 
-    for (int i = 0; i < n_sample; i++ ) {
-      if ( label[i] == 0 ) {
-        continue;
-      }
-      alpha[i] -= (norm_sum / label[i]);
+        // 係数の制約条件2:双対係数は非負
+        for (int i = 0; i < n_sample; i++ ) {
+            if ( alpha[i] < 0 ) {
+                alpha[i] = 0;
+            } else if ( alpha[i] > C1 ) {
+                // C1を踏まえると,係数の上限はC1となる.
+                alpha[i] = C1;
+            }
+        }
+
+        // 収束判定 : 凸計画問題なので,収束時は大域最適が
+        //            保証されている.
+        if ( (vec_dist(alpha, pre_alpha, n_sample) < epsilon)
+                || (two_norm(diff_alpha, n_sample) < epsilon) ) {
+            // 学習の正常完了
+            status = SVM_LEARN_SUCCESS;
+            break;
+        }
+
+        // 学習繰り返し回数のインクリメント
+        iteration++;
     }
 
-    // 係数の制約条件2:双対係数は非負
-    for (int i = 0; i < n_sample; i++ ) {
-      if ( alpha[i] < 0 ) {
-        alpha[i] = 0;
-      } else if ( alpha[i] > C1 ) {
-        // C1を踏まえると,係数の上限はC1となる.
-        alpha[i] = C1;
-      }  
+    if (iteration >= maxIteration) {
+        fprintf(stderr, "Learning is not convergenced. (iteration count > maxIteration) \r\n");
+        status = SVM_NOT_CONVERGENCED;
+    } else if ( status != SVM_LEARN_SUCCESS ) {
+        status = SVM_NOT_LEARN;
     }
 
-    // 収束判定 : 凸計画問題なので,収束時は大域最適が
-    //            保証されている.
-    if ( (vec_dist(alpha, pre_alpha, n_sample) < epsilon)
-        || (two_norm(diff_alpha, n_sample) < epsilon) ) {
-      // 学習の正常完了
-      status = SVM_LEARN_SUCCESS;
-      break;
-    }
-
-    // 学習繰り返し回数のインクリメント
-    iteration++;
-  }
+    // 領域開放
+    delete [] diff_alpha;
+    delete [] pre_diff_alpha;
+    delete [] pre_alpha;
 
-  if (iteration >= maxIteration) {
-    fprintf(stderr, "Learning is not convergenced. (iteration count > maxIteration) \r\n");
-    status = SVM_NOT_CONVERGENCED;
-  } else if ( status != SVM_LEARN_SUCCESS ) {
-    status = SVM_NOT_LEARN;
-  }
-  
-  // 領域開放
-  delete [] diff_alpha;
-  delete [] pre_diff_alpha;
-  delete [] pre_alpha;
-  
-  return status;
+    return status;
 
 }
 
 // 未知データのネットワーク値を計算
 float SVM::predict_net(float* data)
 {
-  // 学習の終了を確認
-  if (status != SVM_LEARN_SUCCESS && status != SVM_SET_ALPHA) {
-    fprintf(stderr, "Learning is not completed yet.");
-    //exit(1);
-    return SVM_NOT_LEARN;
-  }
+    // 学習の終了を確認
+    if (status != SVM_LEARN_SUCCESS && status != SVM_SET_ALPHA) {
+        fprintf(stderr, "Learning is not completed yet.");
+        //exit(1);
+        return SVM_NOT_LEARN;
+    }
 
-  float* norm_data = new float[dim_signal];
+    float* norm_data = new float[dim_signal];
+
+    // 信号の正規化
+    for (int i = 0; i < dim_signal; i++) {
+        norm_data[i] = ( data[i] - sample_min[i] ) / ( sample_max[i] - sample_min[i] );
+    }
 
-  // 信号の正規化
-  for (int i = 0; i < dim_signal; i++) {
-    norm_data[i] = ( data[i] - sample_min[i] ) / ( sample_max[i] - sample_min[i] );
-  }
+    // ネットワーク値の計算
+    float net = 0;
+    for (int l=0; l < n_sample; l++) {
+        // **係数が正に相当するサンプルはサポートベクトル**
+        if(alpha[l] > 0) {
+            net += label[l] * alpha[l]
+                   * kernel_function(&(MATRIX_AT(sample,dim_signal,l,0)), norm_data, dim_signal);
+        }
+    }
+    
+    delete [] norm_data;
 
-  // ネットワーク値の計算
-  float net = 0;
-  for (int l=0; l < n_sample; l++) {
-    // **係数が正に相当するサンプルはサポートベクトル**
-    if(alpha[l] > 0) {
-      net += label[l] * alpha[l]
-              * kernel_function(&(MATRIX_AT(sample,dim_signal,l,0)), norm_data, dim_signal);
-    }
-  }
-
-  return net;
+    return net;
 
 }
 
@@ -258,9 +261,9 @@
     float* optimal_w = new float[dim_signal];   // 最適時の係数(not 双対係数)
     float sigmoid_param;                        // シグモイド関数の温度パラメタ
     float norm_w;                               // 係数の2乗ノルム
-    
+
     net = SVM::predict_net(data);
-    
+
     // 最適時の係数を計算
     for (int n = 0; n < dim_signal; n++ ) {
         optimal_w[n] = 0;
@@ -270,11 +273,9 @@
     }
     norm_w = two_norm(optimal_w, dim_signal);
     sigmoid_param = 1 / ( norm_w * logf( (1 - epsilon) / epsilon ) );
-    
+
     probability = sigmoid_func(net/sigmoid_param);
-    
-    delete [] optimal_w;
-    
+
     // 打ち切り:誤差epsilon以内ならば, 1 or 0に打ち切る.
     if ( probability > (1 - epsilon) ) {
         return float(1);
@@ -282,23 +283,27 @@
         return float(0);
     }
     
+    delete [] optimal_w;
+
     return probability;
-    
+
 }
 
 // 未知データの識別
 int SVM::predict_label(float* data)
 {
-  return (predict_net(data) >= 0) ? 1 : (-1);
+    return (predict_net(data) >= 0) ? 1 : (-1);
 }
 
 // 双対係数のゲッター
-float* SVM::get_alpha(void) {
+float* SVM::get_alpha(void)
+{
     return (float *)alpha;
 }
 
 // 双対係数のセッター
-void SVM::set_alpha(float* alpha_data, int nsample) {
+void SVM::set_alpha(float* alpha_data, int nsample)
+{
     if ( nsample != n_sample ) {
         fprintf( stderr, " set_alpha : number of sample isn't match with arg. n_samle= %d, arg= %d \r\n", n_sample, nsample);
         return;