[C++] PLS2 Algorithm

PLS2 Algorithm

PLS2 Algorithm

由於工作上需要,所以參考了這裡把這個演算法寫成 code。演算法流程如上圖所示

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <sstream>
using namespace std;

int main(){
  const int m=24 ,n=28, h=2;
  // INPUT : 資料 n 筆, 控制變因 m 個, 結果變因 h 個
  // Ex : 28筆轉速 30 的資料, 控制變因 24 個 : 0.5x ~ 12X, 結果變因 2 個 : z1, z2
  const int r = min(m,n); // r 是 m,n 中取較小的值, 一般來說, r = m
  const double sx = 0.999, sy = 0.999, s = 0.00001;
  //sx 是第 29 張投影片中左邊那堆跟 X 有關的 threshold.
  //sy 是第 29 張投影片中左邊那堆跟 Y 有關的 threshold.
  //s  是第 28 張投影片中間的 s
  const double K = 1000000; //避免反矩陣錯誤的參數 

/***************************reading data******************************************/  

  ifstream inFile("data.txt"); //讀取 data.txt 內的值
  ofstream fout;
  fout.open ("PLS2.txt"); //把結果存在 NIPALS.txt 

  string (*arr)[m+h] = new string[n][m+h];

  string line;
  int y = 0;
  while(getline(inFile,line)) {
    istringstream ss(line);
    string word;
    int x = 0;
    while(ss >> word) {
      arr[y][x] = word;
      ++x;
    }
    ++y;
  }
  inFile.close();

//這時讀取的資料是字串, 下面只是字串轉數字。 

  double data[n][m];
  for (int i=0; i < n; i++)
    for (int j=0; j < m; j++)
      data[i][j] = atof(arr[i][j].c_str()); 

  double out[n][h];
  for (int i=0; i < n; i++)
    for (int j=0; j < h; j++)
      out[i][j] = atof(arr[i][j+m].c_str());

  delete []arr;

//這段只是因為我的行列設定跟你們給的相反, 所以我把資料矩陣轉置。
     double X[m][n], Y[h][n];
     for (int i=0; i < m; i++) for (int j=0; j < n; j++) X[i][j] = data[j][i];
     for (int i=0; i < h; i++) for (int j=0; j < n; j++) Y[i][j] = out[j][i];

//設定該用的向量

  double oldu[r][n], newu[r][n], oldmu[r], newmu[r];
  double t[r][n], w[r][m], q[r][h], c[r], p[r][m]; 

  double xbar[m], ybar[h], b[h][m];
  double chckx = 0, chcky, diffmu; 

  int g = 0; 

  //把 X 和 Y 的資料 normalize

  for(int i=0; i < m; i++)
  {
     xbar[i]=0;
     for(int j=0; j < n; j++) xbar[i] = X[i][j] + xbar[i];
     xbar[i] = xbar[i]/n;
  }  

  for(int i=0; i < m; i++) for(int j=0; j < n; j++) X[i][j] = X[i][j]-xbar[i];    

  for(int i=0; i < h; i++)
  {
    ybar[i]=0;
    for(int j=0; j < n; j++) ybar[i] = Y[i][j] + ybar[i];
    ybar[i] = ybar[i]/n;
  }

  for(int i=0; i < h; i++) for(int j=0; j < n; j++) Y[i][j] = Y[i][j]-ybar[i];    

  //find tr([X^T]X)
  double trX = 0;
  for(int i=0; i < m; i++)  for(int j=0; j < n; j++) trX = pow(X[i][j],2) + trX;

  //find tr([Y^T]Y)
  double trY = 0;
  for(int i=0; i < h; i++)  for(int j=0; j < n; j++) trY = pow(Y[i][j],2) + trY;

/*==============================PLS2演算法開始==============================*/
//流程可參考 server 3 \ 統計與演算法 \ 回歸分析預測 \ Method for Calibration.ppt 第 28 張投影片
do
{

  for(int i=0; i < n; i++) oldu[g][i] = Y[0][i]; //選 Y[0][i] 當成 oldu 

  do{
      for(int i=0; i < m; i++)
      {
        w[g][i]=0; //初始 w
        for(int j=0; j < n; j++) w[g][i] = oldu[g][j]*X[i][j] + w[g][i]; //計算 [X^T](oldu)
      }

      double length_w = 0; //length_w = ∥[X^T](oldu)∥
      for(int i=0; i < m; i++) length_w = pow(w[g][i],2) + length_w;
      length_w = sqrt(length_w);
      for(int i=0; i < m; i++) w[g][i] = w[g][i]/length_w; //算出我們要的 w 

      for(int i=0; i < n; i++)
      {
        t[g][i] = 0 ; //初始 t
        for (int j=0; j < m; j++) t[g][i] = X[j][i]*w[g][j]+ t[g][i]; //算出 t
      } 

      for(int i=0; i < h; i++)
      {
        q[g][i] = 0;  //初始 q
        for(int j=0; j < n; j++) q[g][i] = t[g][j]*Y[i][j] + q[g][i]; //算出 [Y^T]*t
      } 

      double length_q =  0; //length_w = ∥[Y^T]t∥
      for(int i=0; i < h; i++) length_q = pow(q[g][i],2) + length_q;
      length_q = sqrt(length_q);
      for(int i=0; i < h; i++) q[g][i] = q[g][i]/length_q; //算出我們要的 q 

      for(int i=0; i < n; i++)
      {
         newu[g][i] = 0 ;  //初始 newu
         for(int j=0; j < h; j++) newu[g][i] = Y[j][i]*q[g][j]+ newu[g][i]; //算出 newu
      } 

      oldmu[g] = 0, newmu[g] = 0; //初始 oldmu, newmu
      for(int i=0; i < n; i++)
      {
        oldmu[g] = pow(oldu[g][i],2) + oldmu[g];
        newmu[g] = pow(newu[g][i],2) + newmu[g];
      }

      diffmu = sqrt(newmu[g])-sqrt(oldmu[g]); //算出 diffmu
      if (diffmu < 0) diffmu = -1*diffmu;  

      if( diffmu > s)
       for(int i=0;i < n ;i++) oldu[g][i] = newu[g][i];  

    }while(diffmu > s);  

  double length_t = 0; //length_t = [t^T]t
  for(int j=0; j < n; j++) length_t = pow(t[g][j],2)+length_t;

  for(int i=0; i < m; i++)
  {
     p[g][i] = 0; //初始 p
     for (int j=0; j < n; j++) p[g][i] = t[g][j]*X[i][j] + p[g][i];
     p[g][i] = p[g][i]/length_t; //算出 p
  } 

  c[g] = 0; //初始 c
  for(int j=0; j < n; j++) c[g] = newu[g][j]*t[g][j] + c[g];
  c[g] = c[g]/length_t; //算出 c 

  //calculate X and Y
  for(int j=0; j < n; j++) for (int i=0; i < m; i++) X[i][j] = X[i][j] - t[g][j]*p[g][i];
  for(int j=0; j < n; j++) for (int i=0; i < h; i++) Y[i][j] = Y[i][j] - c[g]*t[g][j]*q[g][i];

  double length_p = 0; //length_p = [p^T]p
  for (int i=0; i < m; i++) length_p = pow(p[g][i],2)+length_p;

  chckx =  length_t*length_p/trX + chckx; 

   double trckY = 0;
   for(int i=0; i < h; i++)
     for(int j=0; j < n; j++) trckY = pow(Y[i][j],2) + trckY;
     //這時的 Y 其實就是一開始的 Y 去減 TC[Q^T] 

   chcky = 1-trckY/trY; 

    g++;
  if (g==24) {chckx = sx ; chcky = sy; }
  //因為 trcky 最後不可能會完全變成 0, 所以最後 chcky 不會到 1
  //因此做到最後 chcky 可能不會超過 sy, 所以設這行來停止

}while(chckx < sx || chcky < sy ); 

/*==============================PLS2演算法結束==============================*/

/*=====================以下只是代公式算最後的 prediction model================*/
//公式為 server 3 \ 統計與演算法 \ 回歸分析預測 \ Method for Calibration.ppt 第 30 張投影片 

/***********************以下是算出 ([P^T]W)^{-1}*******************************/

double A[r][r]; //A = [P^T]W 

for(int i=0; i < g; i++)
  for(int j=0; j < g; j++)
  {
    A[i][j]=0;
    for(int k=0; k < m; k++) A[i][j] = A[i][j] + p[j][k]*w[i][k];
    A[i][j] = int(A[i][j]*K);  //這兩行只是防止因小數位數太多, 造成反矩陣運算錯誤。
    A[i][j] = A[i][j]/K;       //如果不影響可以 mark 掉。
  }   

double temp[r][r], I[r][r];
double determinant=1;

for(int i=0;i<g;i++)
  for(int j=0;j<g;j++)
  {
    temp[i][j] = A[i][j];
    if (i==j) I[i][j]=1;
    else I[i][j]=0;
  }

int d = 0;
do{
   if(A[d][d]==0)
   {
     if(d == g-1) determinant=0;
     else{
           int t = d+1;
           do{
               if(A[d][t] !=0)
               {
                  determinant = 1;
                  for (int k=d; k<g; k++) A[k][d] = A[k][t]+A[k][d];
               }
               else t++; determinant=0;
             }while(determinant==0 && t<g);
         }
   }

   if(determinant==1)
   {
     for(int j=0; j<g; j++)
       for(int k=0; k<g; k++)
         if(k != d)
         {
           A[j][k] = A[j][k]-A[j][d]*temp[d][k]/temp[d][d];
           I[j][k] = I[j][k]-I[j][d]*temp[d][k]/temp[d][d];
         }

     for(int i=0; i<g; i++) for(int j=0; j<g; j++) temp[i][j] = A[i][j];

     d++;
   }
  }while(d < g && determinant==1); 

for(int i=0; i<g; i++)
  if (A[i][i] !=1)
    for(int j=0; j<g; j++)
      I[j][i] = I[j][i]/A[i][i]; //I =([P^T]W)^{-1} 

/************************ ([P^T]W)^{-1} 計算結束********************************/

double TMP[r][m];
for (int i=0; i < g; i++)
 for (int j=0 ; j < m; j++)
 {
   TMP[i][j] = 0;
   for (int k=0; k<g; k++) TMP[i][j] = TMP[i][j] + w[k][j]*I[i][k];
 } 

for(int i=0; i < h; i++)
 for(int j=0; j < m; j++)
 {
   b[i][j]=0;
   for(int k=0; k < g; k++) b[i][j] = b[i][j] + TMP[k][j]*q[k][i]*c[k];
 } 

  for(int i=0; i < h; i++)
   for(int j=0; j < m; j++)
    ybar[i] = ybar[i]-xbar[j]*b[i][j];

  for(int j=0; j < h; j++)
  {
     fout<<"y["<<j<<"]="<<ybar[j];
     for (int i=0; i < m; i++)
     {
        if (i>=0 && b[j][i]>=0) fout< <"+";
        fout<<b[j][i]<<"*X["<<i<<"]";
     }
     fout<<endl;
  }
  fout.close();
  system("pause");
return(0);}

留言