PLDA源代码分析(1)-PLDA_Train

来源:互联网 发布:log4cpp linux编译 编辑:程序博客网 时间:2024/05/18 01:23


本博文转载于http://write.blog.csdn.net/postedit?ref=toolbar

说明:此处的LDA对应于Linear Discriminant Analysis,PLDA即对应于Probabilistic LDA. 该代码对应的文章为ICCV2007 paper Probabilistic Linear Discriminant Analysis for Inferences About Identity,源代码可以从 Prince Vision Lab处下载。虽然源码虽然不长结构比较清楚,但是运用到了一定的矩阵知识,所以对源码分析稍作分析。

1、PLDA 训练(Training)源码分析

2、PLDA识别(Recognition)源码分析

3、PLDA相关应用


基本问题

PLDA的基本模型如下:



Learning LIV Models给出数据集,利用EM算法求得参数
E-Step:计算两个期望:


其中:


M-Step:计算更新三个参数:



其中均值不变,Diag操作是取矩阵对角元素构成一个对角阵,B和分别如下所示:

PLDA_Train.m共有三个函数,其中PLDA_Train是EM算法的主函数,getExpectedValuePLDA是计算期望的子函数,trainPCA是初始化F的PCA过程。

PLDA_Train

输入参数:

  • Data:d*n的训练数据,d为样本维数,n为样本个数。
  • ImageID:n*m的一个稀疏矩阵,m表示有多少个人,若ImageID[i][j]=1那么表示第i个样本来自第j个人。
  • N_ITER:EM算法迭代次数
  • N_F,N_G:因子个数,即矩阵F和G的第二维(其第一维是d)

相关初始化:

  • 去均值,后面计算PCA,E-Step,M-Step就不用再减去均值
[plain] view plaincopy
  1. meanVec = mean(Data, 2);%d*1维向量,存每一维特征均值  
  2. N_DATA = size(Data, 2);  
  3. Data = double(Data) - repmat(meanVec, 1, N_DATA);  
  • EM算法初始化
[plain] view plaincopy
  1. OBS_DIM = size(Data, 1); % 样本特征维数d  
  2. G = randn(OBS_DIM, N_G); %G随即初始化  
  3. %Sigma是对角矩阵,初始化为一个列向量来表示,方便存储和提高运算效率。  
  4. Sigma = 0.01 .* ones(OBS_DIM, 1);  
F初始化为各类人样本均值的协方差矩阵的特征值,Data*ImageID是一个d*m的矩阵,存储每个人的所有样本各维特征之和,而diag(1/.(sum(ImageID)))是一个m*m的对角矩阵,存储每个人样本数目的倒数,二者相乘即是一个d*m的矩阵,存每个人的所有样本特征均值,后面两行调用trainPCA进行PCA过程。
[plain] view plaincopy
  1. clusterMeans = (Data * ImageID) * (diag(1 ./ (sum(ImageID))));  
  2. F =  trainPCA(clusterMeans);  
  3. F = F(:, 1:N_F);  

EM算法迭代过程:

[plain] view plaincopy
  1. for cIter = 1 : N_ITER  
  2.     %E-Step  
  3.     %M-Step  
  4. end  
  • 更新,这里Eh是和EhhSum是,二者均为矩阵。
[plain] view plaincopy
  1. [Eh EhhSum] = getExpectedValuesPLDA(F, G, Sigma, Data, ImageID);  
  • 更新B
[plain] view plaincopy
  1. xhSum = zeros(size(Data,1), size(Eh, 1));  
  2. for cData = 1 : N_DATA  
  3.    xhSum = xhSum + Data(:, cData) * Eh(:, cData)';  
  4. end;  
  5.    FGEst = xhSum * inv(EhhSum) ;  
  • 更新
若Data如下所示:

那么mean(Data*Data',2)如下所示:

后面一部分也类似。
[plain] view plaincopy
  1. Sigma = mean(Data .* Data - (FGEst * Eh) .* Data,2);  
  • 根据B的定义取得F和G
[plain] view plaincopy
  1. F = FGEst(:, 1 : N_F);  
  2. G = FGEst(:, N_F + 1 : end);  

getExpectedValuesPLDA

主要思想:

该子程序主要计算,二者均为矩阵。前半部分预先计算即样本个数为repeatValues(repeatValues为每个人的样本数目)的指,后半部分具体计算这两个矩阵。

预处理打表

计算表的大小,如PLDA_Train_Demo.m中repeatValues=[2,3,4]共三种情况,那么nRepeatValues=3为循环的个数,invTermsAll存储的值。
[plain] view plaincopy
  1. repeatValues = unique(sum(ImageID));  
  2. nRepeatValues = length(repeatValues);  
  3. invTermsAll = cell(nRepeatValues, 1);<pre name="code" class="plain"></pre>  
下面的循环计算不同repeatValues时的值。
[plain] view plaincopy
  1. for cRepeatVal = 1 : nRepeatValues  
  2.     thisRepVal = repeatValues(cRepeatVal);  
  3.     ...  
  4.     invTermsAll{repeatValues(cRepeatVal)} = invTerm;  
  5. end  

对于每个thisRepVal,易知是一个 (N_HID_DIM+thisRepVal*N_HID_DIM_NOISE)* (N_HID_DIM+thisRepVal*N_HID_DIM_NOISE)大小的矩阵如下:

那些下面代码段就是分块对矩阵进行赋值,其中左上角矩阵维数是H_HID_DIM*H_HID_DIM,其他矩阵维数是N_HID_DIM_NOISE*N_HID_DIM_NOISE。
[plain] view plaincopy
  1. ATISigA(1:N_HID_DIM,1:N_HID_DIM) = thisRepVal*weightedF'*F;  
  2. for cMat = 1:thisRepVal  
  3.     ATISigA(N_HID_DIM+(cMat-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cMat*N_HID_DIM_NOISE,1:N_HID_DIM) = weightedG'*F;  
  4.     ATISigA(1:N_HID_DIM,N_HID_DIM+(cMat-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cMat*N_HID_DIM_NOISE) = weightedF'*G;  
  5.     ATISigA(N_HID_DIM+(cMat-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cMat*N_HID_DIM_NOISE,...  
  6.         N_HID_DIM+(cMat-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cMat*N_HID_DIM_NOISE) = weightedG'*G;  
  7. end;  

有了剩余代码就是计算并存储

计算期望值

外循环是每个人,内循环计算特定人所有样本的期望值,更新期望和。
[plain] view plaincopy
  1. for cInd = 1 : N_INDIV  
  2.     for cFaces = 1 : nFaces  
  3.         Eh(:,thisImIndex(cFaces)) = thisEh(thisIndex);  
  4.         EhhSum=EhhSum+thisEhh(thisIndex,thisIndex);  
  5.     end  
  6. end  
对每个人,首先获得样本数量以及样本所对应的位置。
[plain] view plaincopy
  1. nFaces = full(sum(ImageID(:,cInd)));  
  2. thisImIndex = find(ImageID(:,cInd));  
下面就是计算,计算的过程和计算类似,也是对矩阵进行分块复制。这里的X指该属于某个人的所有样本。
[plain] view plaincopy
  1. dataAll = x(:,thisImIndex).*repmat(1./Sigma,1,nFaces);  
  2. ATISigX = zeros(N_HID_DIM+nFaces*N_HID_DIM_NOISE,1);  
  3. ATISigX(1:N_HID_DIM,:) = sum(F'*dataAll,2);  
  4. for cIm = 1 : nFaces  
  5.     ATISigX(N_HID_DIM+(cIm-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cIm*N_HID_DIM_NOISE,:) = G'*dataAll(:,cIm);  
  6. end;  
下面计算更新(thisEh)和(thisEhh)。
[plain] view plaincopy
  1. thisEh = invTerm*ATISigX;  
  2. thisEhh = invTerm+thisEh*thisEh';  

最后就是for循环更新,前者分块赋值,后者累加求和。
[plain] view plaincopy
  1. for cFaces = 1 : nFaces  
  2.     thisIndex = [1:N_HID_DIM  N_HID_DIM+(cFaces-1)*N_HID_DIM_NOISE+1:N_HID_DIM+cFaces*N_HID_DIM_NOISE];  
  3.     Eh(:,thisImIndex(cFaces)) = thisEh(thisIndex);  
  4.     EhhSum=EhhSum+thisEhh(thisIndex,thisIndex);  
  5. end  

trainPCA

PCA过程,详见 SVD和PCA
1 0
原创粉丝点击