對於PCA,一直都是有個概念,沒有實際使用過,今天終於實際使用了一把,發現PCA還是挺神奇的。
在OPENCV中使用PCA非常簡單,只要幾條語句就可以了。
1、初始化數據
//每一行表示一個樣本
CvMat* pData = cvCreateMat( 總的樣本數, 每個樣本的維數, CV_32FC1 );
CvMat* pMean = cvCreateMat(1, 樣本的維數, CV_32FC1);
//pEigVals中的每個數表示一個特征值
CvMat* pEigVals = cvCreateMat(1, min(總的樣本數,樣本的維數), CV_32FC1);
//每一行表示一個特征向量
CvMat* pEigVecs = cvCreateMat( min(總的樣本數,樣本的維數), 樣本的維數, CV_32FC1);
2、PCA處理,計算出平均向量pMean,特征值pEigVals和特征向量pEigVecs
cvCalcPCA( pData, pMean, pEigVals, pEigVecs, CV_PCA_DATA_AS_ROW );
3、選出前P個特征向量(主成份),然后投影,結果保存在pResult中,pResult中包含了P個系數
CvMat* pResult = cvCreateMat( 總的樣本數, PCA變換后的樣本維數(即主成份的數目), CV_32FC1 );
cvProjectPCA( pData, pMean, pEigVecs, pResult );
4、 重構,結果保存在pRecon中
CvMat* pRecon = cvCreateMat( 總的樣本數, 每個樣本的維數, CV_32FC1 );
cvBackProjectPCA( pResult, pMean, pEigVecs, pRecon );
5、重構誤差的計算
計算pRecon和pData的"差"就可以了.
使用時如果是想用PCA判斷“是非”問題,則可以先用正樣本計算主成分,判斷時,對需要判斷得數據進行投影,然后重構,計算重構出的數據與原數據的差異,如果差異在給定范圍內,可以認為“是”。
如果相用PCA進行分類,例如對數字進行分類,則先用所有數據(0-9的所有樣本)計算主成分,然后對每一類數據進行投影,計算投影的系數,可簡單得求平均。即對每一類求出平均系數。分類時,將需要分類得數據進行投影,得到系數,與先前計算出得每一類得平均系數進行比較,可判為最接近得一類。當然這只是最簡單得使用方法
*********************************************************************************************
*********************************************************************************************
PCA是主成分分析,主要用於數據降維,對於一系列sample的feature組成的多維向量,多維向量里的某些元素本身沒有區分性,比如某個元素在所有的sample中都為1,或者與1差距不大,那么這個元素本身就沒有區分性,用它做特征來區分,貢獻會非常小。所以我們的目的是找那些變化大的元素,即方差大的那些維,而去除掉那些變化不大的維,從而使feature留下的都是“精品”,而且計算量也變小了。
對於一個k維的feature來說,相當於它的每一維feature與其他維都是正交的(相當於在多維坐標系中,坐標軸都是垂直的),那么我們可以變化這些維的坐標系,從而使這個feature在某些維上方差大,而在某些維上方差很小。例如,一個45度傾斜的橢圓,在第一坐標系,如果按照x,y坐標來投影,這些點的x和y的屬性很難用於區分他們,因為他們在x,y軸上坐標變化的方差都差不多,我們無法根據這個點的某個x屬性來判斷這個點是哪個,而如果將坐標軸旋轉,以橢圓長軸為x軸,則橢圓在長軸上的分布比較長,方差大,而在短軸上的分布短,方差小,所以可以考慮只保留這些點的長軸屬性,來區分橢圓上的點,這樣,區分性比x,y軸的方法要好!
所以我們的做法就是求得一個k維特征的投影矩陣,這個投影矩陣可以將feature從高維降到低維。投影矩陣也可以叫做變換矩陣。新的低維特征必須每個維都正交,特征向量都是正交的。通過求樣本矩陣的協方差矩陣,然后求出協方差矩陣的特征向量,這些特征向量就可以構成這個投影矩陣了。特征向量的選擇取決於協方差矩陣的特征值的大小。
舉一個例子:
對於一個訓練集,100個sample,特征是10維,那么它可以建立一個100*10的矩陣,作為樣本。求這個樣本的協方差矩陣,得到一個10*10的協方差矩陣,然后求出這個協方差矩陣的特征值和特征向量,應該有10個特征值和特征向量,我們根據特征值的大小,取前四個特征值所對應的特征向量,構成一個10*4的矩陣,這個矩陣就是我們要求的特征矩陣,100*10的樣本矩陣乘以這個10*4的特征矩陣,就得到了一個100*4的新的降維之后的樣本矩陣,每個sample的維數下降了。
當給定一個測試的特征集之后,比如1*10維的特征,乘以上面得到的10*4的特征矩陣,便可以得到一個1*4的特征,用這個特征去分類。
所以做PCA實際上是求得這個投影矩陣,用高維的特征乘以這個投影矩陣,便可以將高維特征的維數下降到指定的維數。
在opencv里面有專門的函數,可以得到這個這個投影矩陣(特征矩陣)。
void cvCalcPCA( const CvArr* data, CvArr* avg, CvArr* eigenvalues, CvArr* eigenvectors, int flags );
*********************************************************************************************
*********************************************************************************************
float* features=new float[lenOfFeatures];
...
CvMat* vector_feature=cvCreateMat(m_pm.numOfSamples,m_pm.dim,CV_32FC1);
cvSetData(vector_feature,features,vector_feature->step);
CvMat *AvgVector;
CvMat *EigenVector;
CvMat *EigenValue_Row;
CvMat* vector_pca=cvCreateMat(m_pm.numOfSamples,PCA_DIM,CV_32FC1);
AvgVector=cvCreateMat(1,m_pm.dim,CV_32FC1);
EigenValue_Row=cvCreateMat(1,min(m_pm.dim,m_pm.numOfSamples),CV_32FC1);
EigenVector=cvCreateMat(min(m_pm.dim,m_pm.numOfSamples),m_pm.dim,CV_32FC1);
// 計算特征值,對原始數據進行變換,得到主成分
cvCalcPCA(vector_feature,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
cvProjectPCA(vector_feature,AvgVector,EigenVector,vector_pca);
delete[] features;
