OpenCV中的SVM引數優化

IT修道者發表於2014-08-19


     SVM(支援向量機)是機器學習演算法裡用得最多的一種演算法。SVM最經常使用的是用於分類,只是SVM也能夠用於迴歸,我的實驗中就是用SVM來實現SVR(支援向量迴歸)。

    對於功能這麼強的演算法,opencv中自然也是有整合好了,我們能夠直接呼叫。OpenCV中的SVM演算法是基於LibSVM軟體包開發的,LibSVM是臺灣大學林智仁(Lin Chih-Jen)等開發設計的一個簡單、易於使用和高速有效的SVM模式識別與迴歸的軟體包。

    網上講opencv中SVM使用的文章有非常多,但講SVM引數優化的文章卻非常少。所以在這裡不重點講怎麼使用SVM,而是談談如何通過opencv中自帶的庫優化SVM中的各引數。
    相信用SVM做過實驗的人都知道,SVM的各引數對實驗結果有非常大的影響,比方C,gama,P,coef等等。以下就是CvSVMParams類的原型。

C++: CvSVMParams::CvSVMParams()

C++: CvSVMParams::CvSVMParams(int svm_type

int kernel_type

double degree

double gamma

double coef0

double Cvalue

double nu,                   

double p

CvMat* class_weights,

CvTermCriteria term_crit

        )

<1>svm_type:指定SVM的型別(5種):
  • 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種):

  • CvSVM::LINEAR : 線性核心,沒有不論什麼向對映至高維空間,線性區分(或迴歸)在原始特徵空間中被完畢,這是最快的選擇。
            .
  • CvSVM::POLY : 多項式核心:
             .
  • CvSVM::RBF : 基於徑向的函式,對於大多數情況都是一個較好的選擇:
             .
  • CvSVM::SIGMOID : Sigmoid函式核心:
            .
<3>degree:核心函式(POLY)的引數degree。
<4>gamma:核心函式(POLY/ RBF/ SIGMOID)的引數
<5>coef0:核心函式(POLY/ SIGMOID)的引數coef0。
<6>Cvalue:SVM型別(C_SVC/ EPS_SVR/ NU_SVR)的引數C。
<7>nu:SVM型別(NU_SVC/ ONE_CLASS/ NU_SVR)的引數 
<8>p:SVM型別(EPS_SVR)的引數
<9>class_weights:C_SVC中的可選權重,賦給指定的類,乘以C以後變成 。所以這些權重影響不同類別的錯誤分類懲處項。權重越大,某一類別的誤分類資料的懲處項就越大。
<10>term_crit:SVM的迭代訓練過程的中止條件,解決部分受約束二次最優問題。您能夠指定的公差和/或最大迭代次數。
    當然對於一個特定的SVM訓練器,裡面的全部引數不一定全用。比方我用的svm_type為EPS_SVR,那麼我要用到的引數主要就是p,c,gama這三個引數。以下是設定引數的程式碼
	CvSVMParams param;  
	param.svm_type = CvSVM::EPS_SVR;  //我的實驗是用SVR作迴歸分析,可能大部分人的實驗是用SVM來分類,方法都一樣
	param.kernel_type = CvSVM::RBF;  
	param.C = 1;  
	param.p = 5e-3;  
	param.gamma = 0.01;  
	param.term_crit = cvTermCriteria(CV_TERMCRIT_EPS, 100, 5e-3); 

       設定引數後就能夠用CvSVM.train()進行訓練了,以下是train的原型

C++: bool CvSVM::train(const Mat& trainData

      const Mat& responses

const Mat& varIdx=Mat()

const Mat& sampleIdx=Mat()

CvSVMParams params=CvSVMParams()

 )

      我在用train完畢訓練預測時出現了過擬合的情況,即對於訓練集的資料有非常好的預測結果,但對不在訓練集的測試集預測值都一樣(我在網上看到非常多網友也遇到這個問題)。於是我開始調整引數,調了半天也沒個好結果。
     後面我發現事實上opencv中SVM類是提供了優化引數值功能的,瞬間感覺世界美好了。以下講講詳細的做法。
     要讓svm自己主動優化引數,那麼訓練時就不能再用train函式了,而應該用train_auto函式。以下是train_auto的函式原型

C++: bool CvSVM::train_auto(const Mat& trainData,

const Mat& responses

const Mat& varIdx

const Mat& sampleIdx

CvSVMParams params,

int k_fold=10

CvParamGrid Cgrid=CvSVM::get_default_grid(CvSVM::C), CvParamGrid gammaGrid=CvSVM::get_default_grid(CvSVM::GAMMA), CvParamGrid pGrid=CvSVM::get_default_grid(CvSVM::P), CvParamGrid nuGrid=CvSVM::get_default_grid(CvSVM::NU), CvParamGrid coeffGrid=CvSVM::get_default_grid(CvSVM::COEF), CvParamGrid degreeGrid=CvSVM::get_default_grid(CvSVM::DEGREE)

bool balanced=false

)

      自己主動訓練函式的引數凝視(13個)
  • 前5個引數參考建構函式的引數凝視。
  • k_fold: 交叉驗證引數。訓練集被分成k_fold的自子集。當中一個子集是用來測試模型,其它子集則成為訓練集。所以,SVM演算法複雜度是執行k_fold的次數。
  • *Grid: (6個)相應的SVM迭代網格引數。
  • balanced: 假設是true則這是一個2類分類問題。這將會建立很多其它的平衡交叉驗證子集。
    自己主動訓練函式的使用說明
  • 這種方法依據CvSVMParams中的最佳引數C, gamma, p, nu, coef0, degree自己主動訓練SVM模型。
  • 引數被覺得是最佳的交叉驗證,其測試集預估錯誤最小。
  • 假設沒有須要優化的引數,對應的網格步驟應該被設定為小於或等於1的值。比如,為了避免gamma的優化,設定gamma_grid.step = 0,gamma_grid.min_val, gamma_grid.max_val 為隨意數值。所以params.gamma 由gamma得出。
  • 最後,假設引數優化是必需的,可是對應的網格卻不確定,你可能須要呼叫函式CvSVM::get_default_grid(),建立一個網格。比如,對於gamma,呼叫CvSVM::get_default_grid(CvSVM::GAMMA)。
  • 該函式為分類執行 (params.svm_type=CvSVM::C_SVC 或者 params.svm_type=CvSVM::NU_SVC) 和為迴歸執行 (params.svm_type=CvSVM::EPS_SVR 或者 params.svm_type=CvSVM::NU_SVR)效果一樣好。假設params.svm_type=CvSVM::ONE_CLASS,沒有優化,並指定執行一般的SVM。
     這裡須要注意的是,對於須要的優化的引數儘管train_auto能夠自己主動選擇最優值,但在程式碼中也要先賦初始值,要不然編譯能通過,但執行時會報錯。以下是演示樣例程式碼
	CvSVMParams param;  
	param.svm_type = CvSVM::EPS_SVR;  
	param.kernel_type = CvSVM::RBF;  
	param.C = 1;  //給引數賦初始值
	param.p = 5e-3;  //給引數賦初始值
	param.gamma = 0.01;  //給引數賦初始值
	param.term_crit = cvTermCriteria(CV_TERMCRIT_EPS, 100, 5e-3); 
	//對不用的引數step設為0
	CvParamGrid nuGrid = CvParamGrid(1,1,0.0);
	CvParamGrid coeffGrid = CvParamGrid(1,1,0.0);
	CvParamGrid degreeGrid = CvParamGrid(1,1,0.0);

	CvSVM regressor;
	regressor.train_auto(PCA_training,tr_label,NULL,NULL,param,
		10,
		regressor.get_default_grid(CvSVM::C),
		regressor.get_default_grid(CvSVM::GAMMA),
		regressor.get_default_grid(CvSVM::P),
		nuGrid,
		coeffGrid,
		degreeGrid);

      用上面的程式碼的就能夠自己主動訓練並優化引數。最後,若想檢視優化後的引數值,能夠使用CvSVM::get_params()函式來獲得優化後的CvSVMParams。以下是演示樣例程式碼:
CvSVMParams params_re = regressor.get_params();
	regressor.save("training_srv.xml");
	float C = params_re.C;
	float P = params_re.p;
	float gamma = params_re.gamma;
	printf("\nParms: C = %f, P = %f,gamma = %f \n",C,P,gamma);
 
 本文系原創,轉載請註明轉載自 http://blog.csdn.net/computerme/article/details/38677599


      

相關文章