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);//需要分析的圖片
(5)提取HOG特征
//以下為提取Hog特征
cvResize(src,trainImg); //讀取圖片,歸一化大小
HOGDescriptor *hog=new HOGDescriptor(cvSize(64,64),cvSize(16,16),cvSize(16,16),cvSize(16,16),9);
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);
(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” );
在以上訓練過程中,要特別注意的是在創建樣本矩陣的時候,其矩陣大小由樣本數量和樣本提取的特征維數決定的。比如上面創建的樣本矩陣大小為:
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++;
}
小樣本圖片SVM的識別結果還是很不錯的。本文的測試圖片較少,也不能說明模型到底有多好,但基于opencv SVM的識別分類流程基本是這樣了。
總結
- 上一篇: linux换源 最最简单
- 下一篇: 详细的基于opencv svm hog的