SMO程序
#include
#include
#include
#include
#include "matrix.h"
#include
#include
#include
using namespace std;
#define MAX_SIZE_OF_TRAINING_SET 100
#define MAX_NUMIT 100
#define MAX 1000000
#define MIN -100000
//SMO参数结构体
struct OS
{
Matrix x;
Matrix y;
double C;
double soft;
int m;
Matrix alphas;
double b;
Matrix eCache;
Matrix kernel;
bool k;
};
//核函数的结构体
struct kTup
{
int type;//0,1
double arg;
};
class SMOP
{
//非常值得注意的是svm中训练样本按列排列,即每一列是一个样本,所以导致w是行向量
public:
OS os;
public:
/**
根据参数,来生成不同的核函数
*/
Matrix kernelTran(Matrix x,Matrix xOneCol,kTup ktup)
{
Matrix K;
K.initMatrix(&K,x.col,1);
Matrix xOneColT;
xOneColT.initMatrix(&xOneColT,xOneCol.row,xOneCol.col);
xOneColT.transposematrix(xOneCol,&xOneColT);
if(ktup.type==1)
{
K.multsmatrix(&K,x,xOneColT);
}
if(ktup.type==2)
{
//高斯核
}
return K;
}
/**
结构体OS的初始化,用于保存所以SMO算法中需要用到的参数
*/
int initOs(Matrix x,Matrix y,double C,double soft,kTup ktup)
{
os.x.initMatrix(&os.x,x.col,x.row);
os.x.copy(x,&os.x);
os.y.initMatrix(&os.y,y.col,y.row);
os.y.copy(y,&os.y);
os.eCache.initMatrix(&os.eCache,x.col,2);
os.alphas.initMatrix(&os.alphas,x.col,1);
os.b=0;
os.C=C;
os.m=x.col;
os.kernel.initMatrix(&os.kernel,os.m,os.m);
os.soft=soft;
if(ktup.type!=0)
{
int i=0,j=0;
Matrix xOneCol;
xOneCol.initMatrix(&xOneCol,1,os.x.row);
Matrix kOneRow;
kOneRow.initMatrix(&kOneRow,os.m,1);
cout<<"-----------"<;
for(i=0; i
{
xOneCol=xOneCol.getOneCol(x,i);
kOneRow=kernelTran(x,xOneCol,ktup);
for(j=0; j
os.kernel.mat[j][i]=kOneRow.mat[j][0];
}
os.k=true;
return 0;
}
os.k=false;
return 0;
}
/**
上下限剪辑函数
*/
double clipAlpha(double alpha,double L,double H)
{
if(alpha>H)
return H;
if(alpha;
return alpha;
}
/**
误差值的计算也是根据数学表达式来实现的
**/
double calcEk(int k)
{
Matrix ay;
ay.initMatrix(&ay,os.x.col,1);
int i;
for(i=0; i
{
ay.mat[i][0]=os.alphas.mat[i][0]*os.y.mat[i][0];
}
Matrix ayT;
ayT.initMatrix(&ayT,ay.row,ay.col);
ayT.transposematrix(ay,&ayT);
Matrix f;
f.initMatrix(&f,1,1);
if(!os.k)
{
Matrix ayTx;
ayTx.initMatrix(&ayTx,ayT.col,os.x.row);
ayTx.multsmatrix(&ayTx,ayT,os.x);
Matrix xOneCol;
xOneCol.initMatrix(&xOneCol,1,os.x.row);
xOneCol=xOneCol.getOneCol(os.x,k);
Matrix xOneColT;
xOneColT.initMatrix(&xOneColT,xOneCol.row,xOneCol.col);
xOneColT.transposematrix(xOneCol,&xOneColT);
f.multsmatrix(&f,ayTx,xOneColT);//值得商榷
}
else
{
Matrix kOneRow;
kOneRow.initMatrix(&kOneRow,os.m,1);
kOneRow=kOneRow.getOneRow(os.kernel,k);
f.multsmatrix(&f,ayT,kOneRow);//值得商榷
}
double fXK=f.mat[0][0]+os.b-os.y.mat[k][0];
return fXK;
}
///更新误差矩阵
void updataEk(int k)
{
double Ek;
Ek=calcEk(k);//计算误差值
os.eCache.mat[k][0]=1;
os.eCache.mat[k][1]=Ek;
}
/**
对于第二个alpha的选择采用Ei-Ej的大小进行衡量,同时这里引用一个矩阵来保存不变的E,减少计算量
选择出合适第二个alpha2,对于第一次进来,保存误差值E的矩阵必然没有一个有效的,所以采用随机策略
随机选择策略其实可以任意选择,具体没有实现,而是采用等价方式实现,但需要注意数据越界问题
**/
int selectJ(int i,double Ei)
{
int maxK=-1;
double Ek;
double Ej;
double deltaE;
double maxDeltaE=0;
int k;
for(k=0; k
{
if(os.eCache.mat[k][0]==1&&k!=i)
{
Ek=calcEk(k);
deltaE=abs(Ei-Ek);
if(deltaE>maxDeltaE)
{
maxK=k;
maxDeltaE=deltaE;
Ej=Ek;
}
}
//随机选择一个j, 理论上只有第一次时所有的ek都没计算过,所以任选一个即可
if(maxK==-1)
{
maxK=(i*2+1)%100;
Ej=calcEk(maxK);
}
}
return maxK;
}
/**
内层循环实现两个alpha的优化,由外层循环函数提供alpha1的可选集合(所有样本或者是支持向量),
遍历集合上的每一个alpha,对其进行判断是否违反KKT条件,如果违反则选为第一个alpha1,
同样调用selctJ函数来启发式选择使得alpha1改变最大的alpha2,
选择出合适ij之后,还得根据当前alpha1和alpha2来确定优化的上下限。
然后通过迭代公式,来计算alpha1和alpha2,计算的时候判断是否采用核方法。
并对计算结果进行上下限剪辑,最终确定优化后的alpha1和alpha2,同时对当前优化后结果对b也进行修改
考虑到每一次选择第二个alpha2时,都需要比较一个误差值,而这个误差值在每次优化过程中都只有对应修改alpha的误差值有变化
而其他的是不变的,所以用一个矩阵来保存有效的误差值。
*/
int innerL(int i)
{
double Ei;
double Ej;
int j;
double alphaIOld;
double alphaJOld;
double L;
double H;
double eta;
int n;
double temp[3];//ii,jj,ij
double b1,b2;
if(os.y.mat[i][0]*(Ei-os.y.mat[i][0]*os.soft)<0&&os.alphas.mat[i][0].C ||os.y.mat[i][0]*(Ei-os.y.mat[i][0]*os.soft)>0&&os.alphas.mat[i][0]>0)
{
Ei=calcEk(i);
j=selectJ(i,Ei);
Ej=calcEk(j);
alphaIOld=os.alphas.mat[i][0];
alphaJOld=os.alphas.mat[j][0];
if(os.y.mat[i][0]!=os.y.mat[j][0])
{
L=max(0.0,os.alphas.mat[j][0]-os.alphas.mat[i][0]);
H=min(os.C,os.alphas.mat[j][0]-os.alphas.mat[i][0]+os.C);
}
else
{
L=max(0.0,os.alphas.mat[j][0]+os.alphas.mat[i][0]-os.C);
H=min(os.C,os.alphas.mat[j][0]+os.alphas.mat[i][0]);
}
if(L==H)
{
cout<<"l=h------------------"<;
return 0;
}
if(!os.k)
{
temp[0]=0;
temp[1]=0;
temp[2]=0;
for(n=0; n
{
temp[0]+=pow(os.x.mat[i][n],2);
temp[1]+=pow(os.x.mat[j][n],2);
temp[2]+=os.x.mat[i][n]*os.x.mat[j][n];
}
}
else
{
temp[0]=os.kernel.mat[i][i];
temp[1]=os.kernel.mat[j][j];
temp[2]=os.kernel.mat[i][j];
eta=temp[0]+temp[1]-2*temp[2];
}
eta=temp[0]+temp[1]-2*temp[2];
if(eta<0)
{
cout<<"eta<0-----------------"<;
return 0;
}
os.alphas.mat[j][0]+=os.y.mat[j][0]*(Ei-Ej)/eta;
os.alphas.mat[j][0]=clipAlpha(os.alphas.mat[j][0],L,H);//对alpha进行剪辑
updataEk(j);//更新误差值
if(fabs(os.alphas.mat[j][0]-alphaJOld)<0.000001)
{
cout<<"j not moving enough---------------"<;
return 0;
}
os.alphas.mat[i][0]+=os.y.mat[i][0]*os.y.mat[j][0]*(alphaJOld-os.alphas.mat[j][0]);
updataEk(i);//更新误差值
b1=os.b-Ei-os.y.mat[i][0]*(os.alphas.mat[i][0]-alphaIOld)*temp[0]-os.alphas.mat[j][0]*(os.alphas.mat[j][0]-alphaJOld)*temp[2];
b2=os.b-Ej-os.y.mat[i][0]*(os.alphas.mat[i][0]-alphaIOld)*temp[2]-os.alphas.mat[j][0]*(os.alphas.mat[j][0]-alphaJOld)*temp[1];
if(0.alphas.mat[i][0]&&os.C>os.alphas.mat[i][0])
os.b=b1;
else if(0.alphas.mat[j][0]&&os.C>os.alphas.mat[j][0])
os.b=b2;
else
os.b=(b1+b2)/2;
return 1;
}
cout<<"kkt--------------------------"<;
return 0;
}
/**
SMO算法的入口函数,其主要功能是初始化SMO所有的参数到结构体OS
确定迭代结束标志,并在所有样本和支持向量上循环的选择合适alpha1,调用inner确定alpha1,
外层循环每一次是迭代优化一对alpha,内层循环才是正真实现迭代优化一对alpha
最后,对smo算法的实现进行检查,通过alpha求解出w,b,并在训练样本上比较其预测值与实际值的差异。
该办法只能检查SMO算法实现的正确性,不能检查SVM的性能。
*/
int smoP(Matrix x,Matrix y,double C,double soft,int maxIter,kTup ktup)
{
int iter=0;
int i,j;
initOs(x,y,C,soft,ktup);///初始化OS结构体,OS结构体中定义了SMOP算法需要的大部分参数
bool entireSet=true;//标志用于判断当前迭代是针对所有alpha,还是针对0-C之间的部分alpha,这里其实第一个alpha的启发式选择,即选择落在支持向量上的点
int alphaPairsChanged=0;//该参数判断在确定第一个参数之后,是否能选择出符合要求的第二alpha,返回值为1表示能,0为不能
for(iter=0; iter0||entireSet); iter++)
{//循环结束标志为迭代次数已到预设值,或者是不能再继续优化(对于所有的支持向量上的点都找不到第二个alpha对第一个alpha进行优化后,重新再遍历所有的点寻找可优化的参数对)
//还是找不到则再次遍历支持向量上的点,这次必然也是找不到,才结束迭代
alphaPairsChanged=0;
if(entireSet)
{
for(i=0; i
{
alphaPairsChanged+=innerL(i);
cout<<"iter="<" i=" <" alphachanged="<;
}
iter++;
}
else
{
for(i=0; i
{
if(os.alphas.mat[i][0]>0&&os.alphas.mat[i][0].C )//只选择支持向量上的点
continue;
alphaPairsChanged+=innerL(i);
cout<<"iter="<" i="<" alphachanged="<;
}
iter++;
}
if(entireSet)
entireSet=false;
else if(alphaPairsChanged==0)
entireSet=true;
}
///对SMO算法实现的正确性进行验证,输出预测值与实际值的差别,全为0表示在训练样本上预测全对
Matrix ay;
ay.initMatrix(&ay,os.x.col,1);
for(i=0; i
{
ay.mat[i][0]=os.alphas.mat[i][0]*os.y.mat[i][0];
}
Matrix xT;
xT.initMatrix(&xT,os.x.row,os.x.col);
xT.transposematrix(x,&xT);
Matrix w;
w.initMatrix(&w,os.x.row,1);
w.multsmatrix(&w,xT,ay);
Matrix label;
label.initMatrix(&label,os.x.col,1);
label.multsmatrix(&label,os.x,w);
cout<.b <<" ";
for(i=0; i
{
cout<.mat [i][0]<<" ";
}
cout<;
///验证训练样本数据,验证SVM实现的正确性
for(i=0; i
{
if((label.mat[i][0]+os.b)>0)
cout<<1-os.y.mat[i][0]<;
else
cout<<-1-os.y.mat[i][0]<;
}
return 0;
}
};
int main()
{
///加载数据文件保存到对象dtm的矩阵元素中
dataToMatrix dtm;
char file[20]="data\\svm.txt";
dtm.loadData(&dtm,file);
///通过矩阵对象中的load函数初始化样本的特征和类别矩阵x,y
Matrix x;
x.loadMatrix(&x,dtm);
Matrix y;
y.initMatrix(&y,x.col,1);
y=y.getOneRow(x,x.row);
x.deleteOneRow(&x,x.row);
///调用SMO函数求解SVM模型
cout<<"SVM"<;
SMOP smop;
kTup ktup;//核函数的定义,其中type元素为0表示不适用核函数,非0分别对应不同的核函数
ktup.type=1;
ktup.arg=1.0;
smop.smoP(x,y,0.6,0.001,40,ktup);//
return 0;
}
loadData程序
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX_SIZE_OF_TRAINING_SET 100
#define MAX_NUMIT 100
#define ATTR_NUM 3
#define MAX 1000000
#define MIN -100000
#define MAX_MATRIX_COL 1000
#define MAX_MATRIX_ROW 100
struct Data
{
int id;
double attr_double[ATTR_NUM];//用于数值型属性
string attr_string[ATTR_NUM];//用于数值型属性
double weight;
Data *next;
};
class dataToMatrix
{
public:
Data *dataSet;
int col;
int row;
public:
int loadData(dataToMatrix *dtm,char *file)
{
int i=0,j=0;
ifstream infile;
string tmpstrline;
Data *p;
dtm->dataSet=new Data;
dtm->dataSet->next=NULL;
p=dtm->dataSet;
Data *datatmp;
dtm->col=0;
cout<while(!infile.eof()&&i'\n');//读取文件中一行的数据,保存为string类型
stringstream input(tmpstrline);
if(tmpstrline!="\0")////由于读取文件结束符同样会继续该操作
{
datatmp=new Data;
datatmp->id=i;
datatmp->next=NULL;
j=0;
while(input>>datatmp->attr_double[j])j++;
p->next=datatmp;
p=p->next;
dtm->col++;
}
}
dtm->row=j;
return 0;
}
int print(dataToMatrix dtm)
{
//检测数据加载是否正确
int i,j;
Data *p=dtm.dataSet->next;
for(i=0;ifor(j=0; jcout<attr_double[j]<<" ";
}
p=p->next;
cout<return i;
}
};
Matrix程序
#include
#include
#include
#include
#include "loadData.h"
#include
#include
#include
using namespace std;
#define MAX_SIZE_OF_TRAINING_SET 100
#define MAX_NUMIT 100
#define ATTR_NUM 3
#define MAX 1000000
#define MIN -100000
#define MAX_MATRIX_COL 1000
#define MAX_MATRIX_ROW 100
class Matrix
{
public:
double **mat;
int col,row;
public:
int loadMatrix(Matrix *matrix,dataToMatrix dtm)
{
int i,j;
Data *p;
p=dtm.dataSet->next;
matrix->mat=(double **)malloc(sizeof(double*)*dtm.col);
if(!matrix->mat)
{
cout<<"loadMatrix fail"<exit(-1);
}
for(i=0; imat[i]=(double *)malloc(sizeof(double)*dtm.row);
if(!matrix->mat[i])
{
cout<<"loadmatrix fail"<exit(-1);
}
for(j=0; jmat[i][j]=p->attr_double[j];
}
p=p->next;
}
matrix->row=dtm.row;
matrix->col=dtm.col;
return 0;
}
int initMatrix(Matrix *matrix,int col,int row)
{
initMatrix(matrix,col,row,0);
}
int initMatrix(Matrix *matrix,int col,int row,double lam)
{
if(col==0||row==0)
{
cout<<"matrix row or col no can 0"<exit(-1);
}
matrix->col=col;
matrix->row=row;
matrix->mat=(double **)malloc(sizeof(double*)*col);
if(!matrix->mat)
{
cout<<"initMatrix fail"<exit(-1);
}
int i=0,j=0;
for(i=0; i mat[i]=(double *)malloc(sizeof(double)*row);
if(!matrix->mat[i])
{
cout<<"initMatrix fail"<exit(-1);
}
for(j=0; jmat[i][j]=0;
if(i==j)
matrix->mat[i][j]=lam;
}
}
return 0;
}
int print(Matrix matrix)
{
int i,j;
for(i=0; ifor(j=0; jcout<" ";
}
cout<int copy(Matrix matrixA,Matrix *matrixB)
{
if(!matrixB)//还应该考虑a
{
cout<<"matrixB is null"<exit(-1);
}
if(matrixB->col!=matrixA.col||matrixB->row!=matrixA.row)
{
cout<<"matrixA matrixB is no "<exit(-1);
}
int i,j;
for(i=0; ifor(j=0; jmat[i][j]=matrixA.mat[i][j];
}
}
return 0;
}
Matrix getOneRow(Matrix matrix,int iRow)
{
Matrix oneRow;
oneRow.col=matrix.col;
oneRow.row=1;
initMatrix(&oneRow,oneRow.col,oneRow.row);
int i=0;
for(i=0; i0]=matrix.mat[i][iRow-1];
}
return oneRow;
}
Matrix getOneCol(Matrix matrix,int iCol)
{
Matrix oneCol;
oneCol.row=matrix.row;
oneCol.col=1;
initMatrix(&oneCol,oneCol.col,oneCol.row);
int i=0;
for(i=0; i0][i]=matrix.mat[iCol][i];
}
return oneCol;
}
int deleteOneRow(Matrix *matrix,int iRow)
{
if(matrix->row==1)
{
cout<<"matrix is vec"<exit(-1);
}
int i,j;
for(i=iRow; irow; i++)
{
for(j=0;jcol;j++)
{
matrix->mat[j][i]=matrix->mat[j][i+1];
}
}
matrix->row--;
return 0;
}
void transposematrix(Matrix matrix,Matrix *matrixT)//矩阵形式的转置
{
if(matrixT->col!=matrix.row||matrixT->row!=matrix.col)
{
cout<<"matrix matrixT is no "<exit(-1);
}
int i=0,j=0;
for(i=0; icol; i++)
{
for(j=0; jrow; j++)
{
matrixT->mat[i][j]=matrix.mat[j][i];
}
}
}
int addmatrix(Matrix *addMatrix,Matrix matrix1,Matrix matrix2)
{
if(matrix1.col!=matrix2.col||matrix1.row!=matrix2.row||addMatrix->col!=matrix1.col||addMatrix->row!=matrix1.row)
{
cout<<"addMatrix matrix1 matrix2 is no"<exit(-1);
}
int i,j;
for(i=0; ifor(j=0; jmat[i][j]=matrix1.mat[i][j]+matrix2.mat[i][j];
}
}
return 0;
}
int submatrix(Matrix *subMatrix,Matrix matrix1,Matrix matrix2)
{
if(matrix1.col!=matrix2.col||matrix1.row!=matrix2.row||subMatrix->col!=matrix1.col||subMatrix->row!=matrix1.row)
{
cout<<"subMatrix matrix1 matrix2 is no"<exit(-1);
}
int i,j;
subMatrix->col=matrix1.col;
subMatrix->row=matrix1.row;
for(i=0; ifor(j=0; jmat[i][j]=matrix1.mat[i][j]-matrix2.mat[i][j];
}
}
return 0;
}
int multsmatrix(Matrix *multsMatrix,Matrix matrix1,Matrix matrix2)//矩阵形式的相乘
{
if(matrix1.row!=matrix2.col||multsMatrix->col!=matrix1.col||multsMatrix->row!=matrix2.row)
{
cout<<"multsmatrix error"<exit(-1);
}
int i,j,k,l;
for(i=0; ifor(j=0; jmat[i][j]=0;
}
}
for(i=0; ifor(j=0; jfor(k=0; kmat[i][j]+=matrix1.mat[i][k]*matrix2.mat[k][j];
}
}
}
return 0;
}
//行列式
double detmatrix(Matrix matrix)
{
if(matrix.col!=matrix.row)
{
cout<<"matrix det is no"<exit(-1);
}
Matrix matrixCopy;
matrixCopy.initMatrix(&matrixCopy,matrix.col,matrix.row);
matrixCopy.copy(matrix,&matrixCopy);
double det=1;
int i=0,j,k;
double max=MIN;
int swap=-1;
double temp;
double aij[MAX_MATRIX_COL][MAX_MATRIX_ROW];
for(k=0; k1; k++)//k表示第k次消元,一共需要n-1次
{
for(i=0; iif(matrixCopy.mat[i][k]>max)//每一次消元都是比较第k列的元素,选出第k列中最大的一行
{
swap=i;
}
}//找到第k次列主元消去的最大行的下标
if(swap==-1||matrixCopy.mat[swap][k]==0)
return -1;//最大主元为0
for(j=0; j//第k次消元,选出最大的一行是swap行,与第k行交换
for(i=k+1; i// 第k次消元,主元素为第k行第k列,把第k行以下的行都进行消元
for(j=k; j//对于k行以下的每一行的每一列元素都减去主行与消元因子的乘积
{
matrixCopy.mat[i][j]-=aij[i][k]*matrixCopy.mat[k][j];
}
}
}
for(i=0; icout<<"det="<return det;
}
//高斯消元矩阵求逆,特别注意,LU分解不能进行行列式变换
int nimatrix(Matrix *niMatrix,Matrix matrix)
{
if(matrix.col!=matrix.row)
{
cout<<"matrix ni is no "<exit(-1);
}
if(detmatrix(matrix)==0)//这里调用求行列式进行了列主元消去改变了参数矩阵,如何传递不改变是一个问题
{
cout<<"matrix det is no so ni is no "<exit(-1);
}
int i=0,j,k;
double temp;
Matrix cpMatrix;
Matrix uMatrix;
Matrix lMatrix;
Matrix uniMatrix;
Matrix lniMatrix;
initMatrix(&uniMatrix,matrix.col,matrix.row);
initMatrix(&lniMatrix,matrix.col,matrix.row);
initMatrix(&cpMatrix,matrix.col,matrix.row);
initMatrix(&uMatrix,matrix.col,matrix.row);
initMatrix(&lMatrix,uMatrix.col,uMatrix.row);
copy(matrix,&cpMatrix);
double aij[MAX_MATRIX_COL][MAX_MATRIX_ROW];
for(k=0; k1; k++)//k表示第k次消元,一共需要n-1次
{
for(i=k+1; i// 第k次消元,主元素为第k行第k列,把第k行以下的行都进行消元
for(j=k; j//对于k行以下的每一行的每一列元素都减去主行与消元因子的乘积
{
matrix.mat[i][j]-=aij[i][k]*matrix.mat[k][j];
}
}
}
copy(matrix,&uMatrix);
cout<<"uMatrix"<for(j=0; jfor(i=j+1; i0;
for(k=0; k1/uMatrix.mat[j][j]*(cpMatrix.mat[i][j]-temp);
}
}
for(i=0; ifor(j=0; jif(i==j)
lMatrix.mat[i][j]=1;
if(j>i)
lMatrix.mat[i][j]=0;
}
}
cout<<"lMatrix"<cout<<"lu"<//计算u逆
for(j=0; jfor(i=j; i>=0; i--)
{
if(i==j)
uniMatrix.mat[i][j]=1/uMatrix.mat[i][j];
else
{
temp=0;
for(k=j; k>i; k--)
{
temp+=uMatrix.mat[i][k]*uniMatrix.mat[k][j];
}
uniMatrix.mat[i][j]=-1/uMatrix.mat[i][i]*temp;
}
}
}
cout<<"uniMatrix"<//计算l逆
for(j=0; jfor(i=0; iif(j==i)
lniMatrix.mat[i][j]=1;
else
{
temp=0;
for(k=j; kcout<<"lniMatrix"<cout<<"luni"<cout<<"luluni"<int LDL(Matrix x)//矩阵的LDL分解,不知道怎样用于矩阵特征值,特征向量求解
{
Matrix l;
l.initMatrix(&l,x.col,x.row);
Matrix d;
d.initMatrix(&d,x.col,x.row);
int i,j,k;
Matrix temp;
temp.initMatrix(&temp,x.col,x.row);
for(i=0;i1;
for(j=0;jfor(k=0;kfor(k=0;kfor(i=0;ifor(j=0;jcout<" ";
}
cout<for(i=0;ifor(j=0;jcout<" ";
}
cout<