svm 彻底的过程

SVM样本训练步骤
转载自:http://blog.csdn.net/xw20084898/article/details/21389885
和原文:https://blog.csdn.net/keen_zuxwang/article/details/72818040
和https://www.cnblogs.com/chenzhefan/p/7667812.html
在这里再次感谢博主的分享
在这自己加一点点东西,方便大家理解,也方便自己后来看
SVM训练样本:
对于给出的训练样本,要明确每个样本的归类是0还是1,即每个样本都需要标注一个确切的类别标签,提供给SVM训练使用(因SVM是一种有监督的学习分类方法)
对于样本的特征,以及特征的维度,SVM并没有限定,可以使用如Haar、角点、Sift、Surf、直方图等各种特征作为训练样本的表述参与SVM的训练。
类型
它的重要特征是可以处理非完美分类的问题(即训练数据不可以被线性分割),包括线性可分割和不可分割。
svm_type: (CvSVMParams中成员变量 CV_PROP_RW int svm_type;)
SVM的类型:
CvSVM::C_SVC:
C类支持向量分类机,n类分组(n≥2),允许用异常值惩罚因子C进行不完全分类。
CvSVM::NU_SVC:
类支持向量分类机,n类似然不完全分类的分类器。参数为取代C(其值在区间【0,1】中,nu越大,决策边界越平滑)。
CvSVM::ONE_CLASS:
单分类器,所有的训练数据提取自同一个类,然后SVM建立了一个分界线以分割该类在特征空间中所占区域和其它类在特征空间中所占区域。
CvSVM::EPS_SVR:
类支持向量回归机,训练集中的特征向量和拟合出来的超平面的距离需要小于p。异常值惩罚因子C被采用。
CvSVM::NU_SVR:
类支持向量回归机,代替了p。
<2> kernel_type: SVM的内核类型(4种):

核函数
为了将训练样本映射到更有利于线性分割的样本集中。映射的结果是增加了样本向量的维度。
SVM核函数是SVM的关键,核函数的作用就是在保证不增加算法复杂度的情况下将完全不可分问题转化为可分或达到近似可分的状态
(低维空间向量集通常难于划分,可将它们映射到高维空间,选用适当的核函数,就可以得到高维空间的分类函数)
在SVM理论中,采用不同的核函数将导致不同的SVM算法。在确定了核函数之后,由于确定核函数的已知数据也存在一定的误差,考虑到推广性问题,
因此引入了 松弛系数(用以处理样本数据可能存在的噪声问题) 以及 惩罚系数 两个参变量来加以校正。在确定了核函数基础上,再经过大量对比实验等将这两个系数取定

常用的核函数有以下4种:
⑴ 线性核函数
⑵ 多项式核函数
⑶ 径向基(RBF)核函数(高斯核函数)
⑷ Sigmoid 核函数(二层神经网络核函数)

kernel_type: (CvSVMParams中成员变量 CV_PROP_RW int kernel_type;)
SVM的内核类型(:
CvSVM::LINEAR:
线性内核,没有任何向量映射至高维空间(表示不需要进行高维空间映射),线性区分(或回归)在原始特征空间中被完成,这是最快的选择
CvSVM::POLY:
多项式内核:
CvSVM::RBF:
基于径向的函数,对于大多数情况都是一个较好的选择:
CvSVM::SIGMOID:
Sigmoid函数内核:

径向基(RBF)核函数(高斯核函数):
K(x, x1) = exp{-|x-x1|2/σ2}
它可将原始空间映射到无穷维空间。对于参数σ,如果选的很大,高次特征上的权重实际上衰减得非常快,如果选得很小,则可以将任意的数据映射为线性可分(过小则可能出现非常严重的过拟合问题)
径向基(RBF)核函数的参数选取
(分类器的好坏取决于参数C、σ的确定。参数选择的好坏直接影响到分类器性能的好坏)
惩罚因子C
控制着使间隔 margin 最大且错误率最小的折中,就是在确定的特征空间中调节学习机器的置信范围和经验风险的比例
参数σ
是RBF核函数参数,主要影响样本数据在高维特征空间中分布的复杂程度。
常用的C、σ参数选择方法:
1、网格法(OpenCV中SVM用到)
2、双线性法
3、梯度下降搜索法
4、遗传算法
验证核函数性能的方法(衡量泛化能力):
1、单一验证估计
2、K 折交叉验证(OpenCV中SVM用到)

1、引言
近期在做飞形体目标识别的研究,需要做SVM训练来生成识别的分类器。从网上找了大量的参考文章,但是发现很多文章都讲的比较零散。鉴于此原因,本文对SVM训练过程做一个较为系统的总结,希望对广大初学者有所帮助。
2、步骤
(1)生成SVM描述文件;
将需要训练的样本文件的路径和对应的分类类别号写入txt文档,如:
plane/飞机训练正样本Normalize/0.jpg
1
plane/飞机训练正样本Normalize/1.jpg 命名为:SVM_DATA.txt
1
(2)将描述文件读入容器中;
定义两个容器,用于保存样本路径和分类标号,如:

vector img_path;
vector img_catg;

读入数据:

     int nLine = 0;  

string buf;
ifstream svm_data( “SVM_DATA.txt” );

while( svm_data )
{
if( getline( svm_data, buf) )
/原型
  istream& getline ( istream &is , string &str , char delim );   istream& getline ( istream& , string& );
参数 is 进行读入操作的输入流   str 存储读入的内容   delim 终结符 返回值 与参数is是一样的
功能 将输入流is中读到的字符存入str中,直到遇到终结符delim才结束。
对于第一个函数delim是可以由用户自己定义的终结符;对于第二个函数delim默认为 ‘\n’(换行符)。   
函数在输入流is中遇到文件结束符(EOF)或者在读入字符的过程中遇到错误都会结束。   
在遇到终结符delim后,delim会被丢弃,不存入str中。在下次读入操作时,将在delim的下个字符开始读入。
/
{
nLine ++;
if( nLine % 2 == 0 )
{
img_catg.push_back( atoi( buf.c_str() ) );//atoi将字符串转换成整型,值为0或1 用0,1区分正负样本
//功 能: 把字符串转换成整型数。   名字来源:array to integer 的缩写。   
//原型: int atoi(const char *nptr);   
//函数说明: 参数nptr字符串,如果第一个非空格字符不存在或者不是数字也不是正负号则返回零,否则开始做类型转换,
//之后检测到非数字(包括结束符 \0) 字符时停止转换,返回整型数。
// 函数声明:const char *c_str();   c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同.
}
else
{
img_path.push_back( buf );//图像路径
}
}
}
svm_data.close();//关闭文件
(3)读入样本数量,生成样本矩阵和类型矩阵

CvMat *data_mat, *res_mat;
int nImgNum = nLine / 2; //读入样本数量
样本矩阵,nImgNum:横坐标是样本数量, WIDTH * HEIGHT:样本特征向量,即图像大小
data_mat = cvCreateMat( nImgNum, 144, CV_32FC1 );
cvSetZero( data_mat );
//类型矩阵,存储每个样本的类型标志
res_mat = cvCreateMat( nImgNum, 1, CV_32FC1 );
cvSetZero( res_mat );
(4)读入样本图像

IplImage* src;
IplImage* trainImg=cvCreateImage(cvSize(64,64),8,3);//需要分析的图片

    for( string::size_type z = 0; z != img_path.size(); z++ )    //整体循环为z
    {  
        src=cvLoadImage(img_path[z].c_str(),1); 
		// 函数声明:const char *c_str();c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同.  
            if( src == NULL )  
            {  
                cout<<" can not load the image: "<<img_path[z].c_str()<<endl;  
            continue;  
            }  
  
            cout<<" processing "<<img_path[z].c_str()<<endl;  

(5)提取HOG特征

//以下为提取Hog特征
cvResize(src,trainImg); //读取图片,归一化大小
HOGDescriptor *hog=new HOGDescriptor(cvSize(64,64),cvSize(16,16),cvSize(16,16),cvSize(16,16),9);

            vector<float>descriptors;//结果数组      
            hog->compute(trainImg, descriptors,Size(8,8), Size(0,0)); //调用计算函数开始计算      
            cout<<"HOG dims: "<<descriptors.size()<<endl;  
            //CvMat* SVMtrainMat=cvCreateMat(descriptors.size(),1,CV_32FC1);   
            n=0;  
            for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)    //迭代器
            {  
            cvmSet(data_mat,z,n,*iter);   //将HOG特征 存入data_mat矩阵中

x=cvmGet(data_mat,z,n);
cout<<“hog”<<x<<endl;
n++;
}
(6)将HOG特征写入txt文件
FILE *fp1;
int i,j;
if((fp1=fopen(“Hog.txt”,“ab”))==NULL)// 读写打开一个二进制文件,允许读或在文件末追加数据。
{
printf(“can not open the hu file\n”);
exit(0);//正常退出程序
}
for (i = 0; i <144; ++i)
{
fprintf(fp1,"%lf “,descriptors[i]);
}
//fprintf(fp1,”\r\n");
fclose(fp1);

		    cvmSet( res_mat, z, 0, img_catg[z] );   //将正负样本标记存入矩阵res_mat中
            cout<<" end processing "<<img_path[z].c_str()<<" "<<img_catg[z]<<endl;  
}  

(7)进行SVM训练
CvSVM svm = CvSVM();
CvSVMParams param;
CvTermCriteria criteria;
criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
param = CvSVMParams( CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );
/*
SVM种类:CvSVM::C_SVC
Kernel的种类:CvSVM::RBF
degree:10.0(此次不使用)
gamma:8.0
coef0:1.0(此次不使用)
C:10.0
nu:0.5(此次不使用)
p:0.1(此次不使用)
然后对训练数据正规化处理,并放在CvMat型的数组里。
*/
//SVM学习
svm.train( data_mat, res_mat, NULL, NULL, param );
//利用训练数据和确定的学习参数,进行SVM学习
svm.save( “SVM_DATA1.xml” );

	cvReleaseImage(&src);
	cvReleaseMat( &data_mat ); 
    cvReleaseMat( &res_mat );  
	 return 0;

在以上训练过程中,要特别注意的是在创建样本矩阵的时候,其矩阵大小由样本数量和样本提取的特征维数决定的。比如上面创建的样本矩阵大小为:
int nImgNum = nLine / 2; 行,144列; 144是由提取HOG特征时,由窗口大小、块大小、胞元大小和每个抱怨大小中的特征数共同决定的。

训练完后的识别
代码很清楚就直接粘上去了
void HogSVMPre()//检测样本
{
vector img_tst_path;
string buf;
unsigned long n;
int ImgWidht = 64;
int ImgHeight = 64;
Mat TestImg = Mat::zeros(ImgHeight, ImgWidht, CV_8UC3);
ifstream img_tst(“E:\vswork\car3\val\val.txt”);
while (img_tst)
{
if (getline(img_tst, buf))
{
img_tst_path.push_back(buf);
}
}
img_tst.close();
CvSVM svm;
svm.load(“SVM_DATA.xml”);
Mat test; 22 char line[512];
ofstream predict_txt(“SVM_PREDICT.txt”);
for (string::size_type j = 0; j != img_tst_path.size(); j++)
{
test = imread(img_tst_path[j].c_str(), 1);//读入图像
resize(test, TestImg, cv::Size(ImgWidht, ImgHeight), 0, 0, INTER_CUBIC);//要搞成同样的大小才可以检测到
HOGDescriptor *hog = new HOGDescriptor(cvSize(ImgWidht, ImgHeight), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9); //窗口大小,块大小,块滑动增量,cell的大小,bins的个数
vectordescriptors;//结果数组
hog->compute(TestImg, descriptors, Size(1, 1), Size(0, 0)); //调用计算函数开始计算
cout << “The Detection Result:” << endl;
cout << "HOG dims: " << descriptors.size() << endl;
Mat SVMtrainMat = Mat::zeros(1, descriptors.size(), CV_32FC1);
n = 0;
for (vector::iterator iter = descriptors.begin(); iter != descriptors.end(); iter++)
{
SVMtrainMat.at(0, n) = *iter;
n++;
}

                           int ret = svm.predict(SVMtrainMat); 
                           std::sprintf(line, "%s %d\r\n", img_tst_path[j].c_str(), ret); 
                           printf("%s %d\r\n", img_tst_path[j].c_str(), ret);//输出预测的结果,ret的值就代表类别 
                           //getchar(); 
                            predict_txt << line; 
                             } 
                  predict_txt.close(); 
                   system("PAUSE"); 
                     return; 
               }

小样本图片SVM的识别结果还是很不错的。本文的测试图片较少,也不能说明模型到底有多好,但基于opencv SVM的识别分类流程基本是这样了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值