1. 线性判别分析(二类情况)
判别准则函数:
J(w)=|μ1~−μ2~|2S1~2+S2~2
其中:
1.d维x空间
各类样本的均值向量:
μi=1Ni∑x∈ωix
类内离散度矩阵:
Si=∑x∈ωi(x−μi)(x−μi)T
总类内离散度矩阵
Sw=S1+S2
类间离散度矩阵:
Sb=(μ1−μ2)(μ1−μ2)T
2.一维y空间
各类样本的均值:
μi~=1Ni∑y∈ωiy=1Ni∑x∈ωiwTx
μi~=wTμi
类内离散度
Si~2=∑y∈ωi(y−μ~i)2
Si~2=∑x∈ωi(wTx−wTμi)2
Si~2=∑x∈ωiwT(x−μi)(x−μi)Tw
Si~2=wTSiw
总类内离散度
Sw~=S1~2+S2~2
Sw~=wT(S1+S2)w
Sw~=wT(Sw)w
最终:
J(w)=wTSbwwTSww
2. 线性判别分析(多类情况)
有C个类别,需要K维向量(或者叫做基向量)来做投影。
W=[w1w2...wk]T
y=WTx
1.d维x空间
类内离散度矩阵:
Si=∑x∈ωi(x−μi)(x−μi)T
总类内离散度矩阵
Sw=∑ci=1Si
类间离散度矩阵(每类均值点相对于样本中心的散列情况):
Sb=∑ci=1Ni(μi−μ)(μi−μ)T
其中
μ
为所有样本的均值:
μ=1N∑∀xx=1N∑x∈wiNiμi
2.k维y空间
μi~=1Ni∑y∈ωiy
μ~=1N∑∀yy
类内离散度矩阵:
Si~=∑y∈ωi(y−μi~)(y−μi~)T
总类内离散度
Sw~=∑ci=1Si~=∑ci=1∑y∈ωi(y−μi~)(y−μi~)T
类间离散度矩阵(每类均值点相对于样本中心的散列情况):
Sb~=∑ci=1Ni(μ~i−μ~)(μ~i−μ~)T
最终:
J(w)=|S~b||S~w|=wTSbwwTSww
3. 求解(Fisher linear discrimination)
利用Lagrange乘法算子求解:
L(w,λ)=wTSbw−λ(wTSww−c)
令
∂L∂w=Sbw−λSww=0
Sbw∗−λSww∗=0
即:
Sbw∗=λSww∗
S−1wSbw∗=λw∗
∵Sb=(μ1−μ2)(μ1−μ2)T
∴Sbw∗=(μ1−μ2)(μ1−μ2)Tw∗=(μ1−μ2)λw
其中
λw
为常数
∴S−1w(μ1−μ2)λw=λw∗
由于对w扩大缩小任何倍不影响结果,因此可以约去两边的未知常数
λw和λ
,得到
S−1w(μ1−μ2)=w∗
代码实现:
int cv_lda(void)
{
//sampledata
double sampledata[6][2]={{0,1},{0,2},{2,4},{8,0},{8,2},{9,4}};
Mat mat=Mat(6,2,CV_64FC1,sampledata);
//labels
vector<int>labels;
for(int i=0;i<mat.rows;i++)
{
if(i<mat.rows/2)
{
labels.push_back(0);
}
else
{
labels.push_back(1);
}
}
//do LDA
//初始化并计算,构造函数中带有计算
LDA lda=LDA(mat,labels,1);
//get the eigenvector
//获得特征向量
Mat eivector=lda.eigenvectors().clone();
cout<<"特征向量(double)类型:"<<endl;
for(int i=0;i<eivector.rows;i++)
{
for(int j=0;j<eivector.cols;j++)
{
cout<<eivector.ptr<double>(i)[j]<<" ";
}
cout<<endl;
}
//------------------------------计算两个类心------------
//针对两类分类问题,计算两个数据集的中心
int classNum=2;
vector<Mat> classmean(classNum);
vector<int> setNum(classNum);
for(int i=0;i<classNum;i++)
{
classmean[i]=Mat::zeros(1,mat.cols,mat.type()); //初始化类中均值为0
setNum[i]=0; //每一类中的条目数
}
Mat instance;
for(int i=0;i<mat.rows;i++)
{
instance=mat.row(i);//获取第i行
if(labels[i]==0) //如果标签为0
{
add(classmean[0], instance, classmean[0]); //矩阵相加
setNum[0]++; //数量相加
}
else if(labels[i]==1) //对于第1类的处理
{
add(classmean[1], instance, classmean[1]);
setNum[1]++;
}
else
{}
}
for(int i=0;i<classNum;i++) //计算每一类的均值
{
classmean[i].convertTo(classmean[i],CV_64FC1,1.0/static_cast<double>(setNum[i]));
}
//----------------------------------END计算类心-------------------------
vector<Mat> cluster(classNum); //一共2类
cout<<"特征向量:"<<endl;
cout<<eivector<<endl; //此时的特征向量是一个列向量
cout<<endl<<endl;
cout<<"第一种方式(手动计算):"<<endl;
//1.投影的第一种方式:Y=X*W
//有的教程写成Y=W^T*X,(此时的X是列向量看待的所以需要将w转置)
for(int i=0;i<classNum;i++)
{
cluster[i]=Mat::zeros(1,1,mat.type()); //初始化0
//特征向量的转置同类均值相乘)
cluster[i]=classmean[i]*eivector;
}
cout<<"The project cluster center is:"<<endl; //计算均值的投影
for(int i=0;i<classNum;i++) //输出两类中心的投影值
{
cout<<cluster[i].at<double>(0)<<endl;
}
//2.第二种方式使用内置函数计算
//第一个中心
cout<<endl<<"第二种方式:";
cout<<endl<<"第一个类均值的投影:"<<endl;
cout<<lda.project(classmean[0]).at<double>(0);
cout<<endl<<"第二个类均值的投影"<<endl;
cout<<lda.project(classmean[1]).at<double>(0);
system("pause");
return 0;
}
结果:
reference:
[1]. http://www.cnblogs.com/jerrylead/archive/2011/04/21/2024384.html
[2]. http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html