主成分分析PCA

本文轉自:

主成分分析PCA

降維的必要性:

  1.多重共線性–預測變量之間相互關聯。多重共線性會導致解空間的不穩定,從而可能導致結果的不連貫。

  2.高維空間本身具有稀疏性。一維正態分佈有68%的值落於正負標準差之間,而在十維空間上只有0.02%。

  3.過多的變量會妨礙查找規律的建立。

  4.僅在變量層面上分析可能會忽略變量之間的潛在聯繫。例如幾個預測變量可能落入僅反映數據某一方面特徵的一個組內。

降維的目的:

  1.減少預測變量的個數

  2.確保這些變量是相互獨立的

  3.提供一個框架來解釋結果

  降維的方法有:主成分分析、因子分析、用戶自定義複合等。

  PCA(Principal Component Analysis)不僅僅是對高維數據進行降維,更重要的是經過降維去除了噪聲,發現了數據中的模式。

  PCA把原先的n個特徵用數目更少的m個特徵取代,新特徵是舊特徵的線性組合,這些線性組合最大化樣本方差,儘量使新的m個特徵互不相關。從舊特徵到新特徵的映射捕獲數據中的固有變異性。
  PCA把原先的n個特徵用數目更少的m個特徵取代,新特徵是舊特徵的線性組合,這些線性組合最大化樣本方差,儘量使新的m個特徵互不相關。從舊特徵到新特徵的映射捕獲數據中的固有變異性。

預備知識

樣本X和樣本Y的協方差(Covariance):
       這裏寫圖片描述

  協方差爲正時說明X和Y是正相關關係,協方差爲負時X和Y是負相關關係,協方差爲0時X和Y相互獨立。
Cov(X,X)就是X的方差(Variance).

  當樣本是n維數據時,它們的協方差實際上是協方差矩陣(對稱方陣),方陣的邊長是這裏寫圖片描述。比如對於3維數據(x,y,z),計算它的協方差就是:
      這裏寫圖片描述

  若這裏寫圖片描述,則稱這裏寫圖片描述是A的特徵值,X是對應的特徵向量。實際上可以這樣理解:矩陣A作用在它的特徵向量X上,僅僅使得X的長度發生了變化,縮放比例就是相應的特徵值這裏寫圖片描述

  當A是n階可逆矩陣時,A與P1AP 相似,相似矩陣具有相同的特徵值。

特別地,當A是對稱矩陣時,A的奇異值等於A的特徵值,存在正交矩陣QQ1=QT ,使得:
         這裏寫圖片描述

對A進行奇異值分解就能求出所有特徵值和Q矩陣。

這裏寫圖片描述 , D是由特徵值組成的對角矩陣

由特徵值和特徵向量的定義知,Q的列向量就是A的特徵向量。

Jama包

  Jama包是用於基本線性代數運算的java包,提供矩陣的cholesky分解、LUD分解、QR分解、奇異值分解,以及PCA中要用到的特徵值分解,此外可以計算矩陣的乘除法、矩陣的範數和條件數、解線性方程組等。

PCA過程

  1.特徵中心化。即每一維的數據都減去該維的均值。這裏的“維”指的就是一個特徵(或屬性),變換之後每一維的均值都變成了0。
舉例
(Ps:因爲不知道如何將代碼段摺疊,所以原始數據就不貼了,詳細的可以參考原始博文
主要步驟爲:

  1. 原始數據是150×4的矩陣A。
  2. 每一列減去該列均值後,得到矩陣B。
  3. 2.計算B的協方差矩陣C。
  4. 計算協方差矩陣C的特徵值和特徵向量。
    C=V*S*V-1
    S=
    4.2248414     0       0       0
    0          0.24224437  0        0
    0          0       0.078524387 0
    0          0       0        0.023681839

    V=
    
    0.36158919   0.65654382   -0.58100304   0.3172364 
    -0.082268924    0.72970845    0.596429220       -0.3240827 
    0.85657212  -0.17576972 0.  072535217    -0.47971643 
    0.35884438    -0.074704743    0.54904125    0.75113489
    
  5. 選取大的特徵值對應的特徵向量,得到新的數據集
      特徵值是由大到小排列的,前兩個特徵值的和已經超過了所有特徵值之和的97%。我們取前兩個特徵值對應的特徵向量,得到一個4×2的矩陣M。令A150×2=A150×4×M4×2 ,這樣我們就把150×4的數據A集映射成了150×2的數據集A’,特徵由4個減到了2個。
      
      每個樣本正好是二維的,畫在平面座標系中如圖:
      這裏寫圖片描述
      鷲尾花數據集共分爲3類花(前50個樣本爲一類,中間50個樣本爲一類,後50個樣本爲一類),從上圖可以看到把數據集映射到2維後分類會更容易進行,直觀上看已經是線性可分的了,下面我們用自組織映射網絡對其進行聚類。

      當然我們已知了有3類,所以在設計SOFM網絡時,我把競爭層節點數設爲3,此時的聚類結果是前50個樣本聚爲一類,後100個樣本聚爲一類。當把競爭層節點數改爲4時,僅第2類中的3個樣本被誤分到了第3類中,整體精度達98%!

#include<iostream>
#include<fstream>
#include<set>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<ctime>

using namespace std;

const int sample_num=150;      //鷲尾花樣本個數
const int class_num=4;      //指定聚類的數目
int iteration_ceil;      //迭代的上限
vector<pair<double,double> > flowers(sample_num);      //樣本數據
vector<vector<double> > weight(class_num);   //權向量
const double prime_eta=0.7;     //初始學習率

/*向量模長歸一化*/
void normalize(vector<double> &vec){
    double sum=0.0;
    for(int i=0;i<vec.size();++i)
        sum+=pow(vec[i],2);
    sum=sqrt(sum);
    for(int i=0;i<vec.size();++i)
        vec[i]/=sum;
}

/*從文件讀入鷲尾花樣本數據*/
void init_sample(string filename){
    ifstream ifs(filename.c_str());
    if(!ifs){
        cerr<<"open data file failed."<<endl;
        exit(1);
    }
    for(int i=0;i<sample_num;++i){
        vector<double> X(2);
        ifs>>X[0]>>X[1];
        normalize(X);       //輸入向量模長歸一化
        flowers[i]=make_pair(X[0],X[1]);
    }
    ifs.close();
}

/*初始化權值*/
void init_weight(){
    srand(time(0));
    for(int i=0;i<weight.size();++i){
        vector<double> ele(2);
        ele[0]=rand()/(double)RAND_MAX;
        ele[1]=rand()/(double)RAND_MAX;
        normalize(ele);     //權值向量模長歸一化
        weight[i]=ele;
    }
}

/*根據輸入,選擇獲勝者*/
int pick_winner(double x1,double x2){
    int rect=-1;
    double max=0.0;
    for(int i=0;i<weight.size();++i){
        double product=x1*weight[i][0]+x2*weight[i][1];
        if(product>max){
            max=product;
            rect=i;
        }
    }
    return rect;
}

int main(int argc,char *argv[]){
    cout<<"input iteration count"<<endl;
    int count;      //每個樣本迭代的次數
    cin>>count;
    cout<<"input data file name"<<endl;
    string filename;
    cin>>filename;
    iteration_ceil=count*sample_num;
    init_sample(filename);
    init_weight();

    double eta=prime_eta;
    double gradient1=-1*9*prime_eta/iteration_ceil;
    double gradient2=-1*prime_eta/(9*iteration_ceil);
    double b1=prime_eta;
    double b2=prime_eta/9;
    for(int iteration=0;iteration<iteration_ceil;++iteration){
        int flower_index=iteration%sample_num;
        double x1=flowers[flower_index].first;
        double x2=flowers[flower_index].second;
        int winner=pick_winner(x1,x2);
        /*更改獲勝者的權值*/
        weight[winner][0]+=eta*(x1-weight[winner][0]);
        weight[winner][1]+=eta*(x2-weight[winner][1]);
        /*權向量歸一化*/
        for(int i=0;i<weight.size();++i){
            vector<double> W(2);
            W[0]=weight[i][0];
            W[1]=weight[i][1];
            normalize(W);
            weight[i][0]=W[0];
            weight[i][1]=W[1];
        }
        /*更新學習率*/
        if(iteration<0.1*iteration_ceil){   //在前10%的迭代中,學習率線性下降到原來的10%
            eta=gradient1*iteration+b1;
        }
        else{       //後90%的迭代中線性降低到0
            eta=gradient2*iteration+b2;
        }
    }

    for(int i=0;i<sample_num;++i){
        double x1=flowers[i].first;
        double x2=flowers[i].second;
        int winner=pick_winner(x1,x2);
        cout<<i+1<<"\t"<<winner+1<<endl;
    }
    return 0;
}

  輸出聚類結果:(原始數據,中間歸一化數據,及結果輸出參見原始博文)
原文來自:博客園(華夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun

發佈了9 篇原創文章 · 獲贊 15 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章