【原文:http://blog.csdn.net/whuheming/article/details/19809889】
(一)LDA算法
LDA的全称是Linear Discriminant Analysis(线性判别分析),是一种supervised learning。有些资料上也称为是Fisher’s Linear Discriminant,因为它被Ronald Fisher发明自1936年,Discriminant这次词我个人的理解是,一个模型,不需要去通过概率的方法来训练、预测数据,比如说各种贝叶斯方法,就需要获取数据的先验、后验概率等等。
LDA的原理是,将带上标签的数据(点),通过投影的方法,投影到维度更低的空间中,使得投影后的点,会形成按类别区分,一簇一簇的情况,相同类别的点,将会在投影后的空间中更接近。线性判别分析的基本思想是将高维的模式样本投影到最佳鉴别矢量空间,即把高维空间中的数据点投影到一条直线去,将多维降为一维,并且要求投影后各样本的类间散布距离最大,同时类内散布距离最小。即投影之后的数据点更具有线性分类性。
LDA是一种线性分类器。对于K-分类的一个分类问题,会有K个线性函数:
当满足条件:对于所有的j,都有Yk > Yj,的时候,我们就说x属于类别k。对于每一个分类,都有一个公式去算一个分值,在所有的公式得到的分值中,找一个最大的,就是所属的分类了。
假设用来区分二分类的直线(投影函数)为:
类别i的原始中心点为:
类别i投影后的中心点为:衡量类别i投影后,类别点之间的分散程度(方差)为:
最终我们可以得到一个下面的公式,表示LDA投影到w后的损失函数,也称为准则函数:
我们分类的目标是找到一个最优化的W,使得类别内的点距离越近越好(集中),类别间的点越远越好。上式准则函数的分母表示每一个类别内的方差之和,方差越大表示一个类别内的点越分散,分子为两个类别各自的中心点的距离的平方,我们最大化J(w)就可以求出最优的w。想要求出最优的w,可以使用拉格朗日乘子法,但是现在我们得到的J(w)里面,w是不能被单独提出来的,我们就得想办法将w单独提出来。
我们定义样本类内离散度矩阵和总类内离散度矩阵如下所示:
代入上式准则函数,可以简化为:
这样就可以用最喜欢的拉格朗日乘子法了,但是还有一个问题,如果分子、分母是都可以取任意值的,那就会使得有无穷解,我们将分母限制为长度为1(这是用拉格朗日乘子法一个很重要的技巧,在下面将说的PCA里面也会用到,如果忘记了,请复习一下高数),并作为拉格朗日乘子法的限制条件,代入得到:
这同样是一个求特征值的问题,我们求出的第i大的特征向量,就是对应的Wi了。
(二) PCA算法
主成分分析法(PCA)是多元统计分析中用来分析数据的一种方法,它是用一种较少数量的特征对样本进行描述以达到降低特征空间维数的方法,它的本质实际上是K-L变换。PCA是一种unsupervised learning,最著名的应用应该是在人脸识别中特征提取及数据维,我们知道输入200*200大小的人脸图像,单单提取它的灰度值作为原始特征,则这个原始特征将达到40000维,这给后面分类器的处理将带来极大的难度。著名的人脸识别Eigenface算法就是采用PCA算法,用一个低维子空间描述人脸图像,同时用保存了识别所需要的信息。
主成分分析(PCA)的原理就是将一个高维向量x,通过一个特殊的特征向量矩阵U,投影到一个低维的向量空间中,表征为一个低维向量y,并且仅仅损失了一些次要信息。也就是说,通过低维表征的向量和特征向量矩阵,可以基本重构出所对应的原始高维向量。在人脸识别中,特征向量矩阵U称为特征脸(eigenface)空间,因此其中的特征向量ui进行量化后可以看出人脸轮廓,在下面的实验中可以看出。以人脸识别为例,说明下PCA的应用。假设有N个人脸训练样本,每个样本由其像素灰度值组成一个向量xi,则样本图像的像素点数即为xi的维数,M=width*height ,由向量构成的训练样本集为:{x1,x2,.....xN}。
该样本集的平均向量(又称为平均脸)为:
样本集的协方差矩阵为:
- //***************************** Face Recognize ***********************************
- void train(){
- // load training data
- nTrainFaces = loadFaceImgArray("faceSample\\train.txt");
- if( nTrainFaces < 2){
- fprintf(
- stderr,
- "Need 2 or more training faces\n"
- "Input file contains only %d\n",
- nTrainFaces
- );
- return;
- }
- // do PCA on the training faces
- doPCA();
- // project the training images onto the PCA subspace
- projectedTrainFaceMat = cvCreateMat(nTrainFaces, nEigens, CV_32FC1);
- for(int i = 0; i < nTrainFaces; i ++){
- cvEigenDecomposite(
- faceImgArr[i],
- nEigens,
- eigenVectArr,
- 0, 0,
- pAvgTrainImg,
- projectedTrainFaceMat->data.fl + i*nEigens
- );
- }
- // store the recognition data as an xml file
- storeTrainingData();
- }
- int loadFaceImgArray(char* filename){
- FILE* imgListFile = 0;
- char imgFilename[512];
- int iFace, nFaces = 0;
- // open the input file
- imgListFile = fopen(filename, "r");
- // count the number of faces
- while( fgets(imgFilename, 512, imgListFile) ) ++ nFaces;
- rewind(imgListFile);
- // allocate the face-image array and person number matrix
- faceImgArr = (IplImage **)cvAlloc( nFaces*sizeof(IplImage *) );
- personNumTruthMat = cvCreateMat( 1, nFaces, CV_32SC1 );
- // store the face images in an array
- for(iFace=0; iFace<nFaces; iFace++){
- //read person number and name of image file
- fscanf(imgListFile, "%d %s", personNumTruthMat->data.i+iFace, imgFilename);
- // load the face image
- faceImgArr[iFace] = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE);
- }
- fclose(imgListFile);
- return nFaces;
- }
- void doPCA(){
- int i;
- CvTermCriteria calcLimit;
- CvSize faceImgSize;
- // set the number of eigenvalues to use
- nEigens = nTrainFaces - 1;
- // allocate the eigenvector images
- faceImgSize.width = faceImgArr[0]->width;
- faceImgSize.height = faceImgArr[0]->height;
- eigenVectArr = (IplImage**)cvAlloc(sizeof(IplImage*) * nEigens);
- for(i=0; i<nEigens; i++){
- eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);
- }
- // allocate the eigenvalue array
- eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 );
- // allocate the averaged image
- pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);
- // set the PCA termination criterion
- calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1);
- // compute average image, eigenvalues, and eigenvectors
- cvCalcEigenObjects(
- nTrainFaces,
- (void*)faceImgArr,
- (void*)eigenVectArr,
- CV_EIGOBJ_NO_CALLBACK,
- 0,
- 0,
- &calcLimit,
- pAvgTrainImg,
- eigenValMat->data.fl
- );
- }
- void storeTrainingData(){
- CvFileStorage* fileStorage;
- int i;
- // create a file-storage interface
- fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_WRITE);
- // store all the data
- cvWriteInt( fileStorage, "nEigens", nEigens);
- cvWriteInt( fileStorage, "nTrainFaces", nTrainFaces );
- cvWrite(fileStorage, "trainPersonNumMat", personNumTruthMat, cvAttrList(0, 0));
- cvWrite(fileStorage, "eigenValMat", eigenValMat, cvAttrList(0,0));
- cvWrite(fileStorage, "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0));
- cvWrite(fileStorage, "avgTrainImg", pAvgTrainImg, cvAttrList(0,0));
- for(i=0; i<nEigens; i++){
- char varname[200];
- sprintf( varname, "eigenVect_%d", i);
- cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0));
- }
- //release the file-storage interface
- cvReleaseFileStorage( &fileStorage );
- }
- // Face Recognize by PCA EigenFace...
- int PCA_recognize(IplImage* faceRegion)
- {
- CvMat* trainPersonNumMat = 0; // the person numbers during training
- float* projectedTestFace = 0;
- // load the saved training data
- if( !loadTrainingData( &trainPersonNumMat ) ) return -1;
- // project the test face region onto the PCA subspace
- projectedTestFace = (float*)cvAlloc( nEigens*sizeof(float) );
- cvEigenDecomposite(
- faceRegion,
- nEigens,
- eigenVectArr,
- 0, 0,
- pAvgTrainImg,
- projectedTestFace
- );
- int iNearest, nearest;
- iNearest = findNearestNeighbor(projectedTestFace);
- nearest = trainPersonNumMat->data.i[iNearest];
- return nearest;
- }