1. 程式人生 > >svm以及各種版本的實現

svm以及各種版本的實現

一、簡述

以二維平面上的分類為例,下面給出了不同的分類可能,哪個才是最優的分類呢?

可以看出第一種分類方法是最好的,為什麼呢?因為它的分類平面到兩類邊界的距離(Margin)最大。

所以SVM也叫Large Margin分類器。


各種資料對它評價甚高,說“ 它在解決小樣本、非線性及高維模式識別中表現出許多特有的優勢,並能夠推廣應用到函式擬合等其他機器學習問題中”。

SVM之線性分類器

如果一個線性函式能夠將樣本完全正確的分開,就稱這些資料是線性可分的,否則稱為非線性可分的。

什麼叫線性函式呢?在一維空間裡就是一個點,在二維空間裡就是一條直線,三維空間裡就是一個平面,以此類推。

如果不關注空間的維數,這種線性函式就是前言中所說的那個統一的名稱——

超平面(Hyper Plane)!

在樣本空間中,劃分超平面可通過如下線性方程來描述:

如圖:

在這兩個超平面上的樣本點也就是理論上離分隔超平面最近的點,是它們的存在決定了H1和H2的位置,支撐起了分界線,它們就是所謂的支援向量,這就是支援向量機的由來

有了這兩個超平面就可以順理成章的定義上面提到的間隔(margin)了

二維情況下 ax+by=c1和ax+by=c兩條平行線的距離公式為:


而且這是一個凸二次規劃問題,一般的解決方法有兩種1是用現成的優化工具包直接求解,2是使用Lagrange Duality找到一種更有效的方法求解。

其中方法2具有兩個優點:

a、更好解

b、可以自然地引入核函式,推廣到非線性分類

所以這裡選用了第二種方法。

二、opencv版本svm

備註:

opencv svm預測的結果是對應的標籤值,是不帶置信度的,要想獲得置信度可以採用libsvm,經過測試比較opencv svm(libsvm2.6版本)比libsvm3.22的準確率高10%左右。為了獲得高的準確率,最後修改了opencv svm的原始碼,採用sigmod函式來獲得置信度,預測函式的最後返回值是置信度,大於0.5的代表類別1。具體修改的地方如下:

原始碼位置:opencv-2.4.13/modules/ml/src/svm.cpp

修改函式:floatCvSVM::predict( const float* row_sample, int row_len, bool returnDFVal ) const

新增程式碼:紅色區域, 1減的目的是為了使輸出結果大於0.5的為類別1.


重新編譯opencv: cd到opencv所在的目錄

         mkdir build

         cd build

         cmake ..

         sudo make

         sudo make install

或者

不改變opencv原始碼,在呼叫處加程式碼實現;

程式碼如下:

                  sum = svm.predict(data[i], True)

                   res= 1.0-(float)(1 / (1 + math.exp((-1.0)*sum)));  


1.   python版本

資料格式:numpy陣列,假如訓練樣本為1000,10個特徵、每個特徵最多10個

train_data =  np.array(np.zeros((num, feat_num*obj_num)),dtype=np.float32)  #(1000,100)

train_label =  np.array(np.zeros((num, 1)),dtype=np.float32)                 # (1000, 1)

svm = cv2.SVM()  #

param = dict(kernel_type=cv2.SVM_LINEAR,svm_type=cv2.SVM_C_SVC, C=1)

#cv2.SVM_LINEAR, cv2.SVM_RBF

svm_type:SVM的型別:

C_SVC表示SVM分類器,該型別可以用於n-類分類問題 (n 2)

C_SVR表示SVM迴歸

C = 1;  //給引數賦初始值

CvSVM::LINEAR : 線性核心,沒有任何向對映至高維空間,線性區分(或迴歸)在原始特徵空間中被完成,這是最快的選擇。

CvSVM::RBF: 基於徑向的函式,對於大多數情況都是一個較好的選擇.

ret = svm.train(train_data, train_label, None, None, param) #訓練

svm.save(path_model)#儲存模型

svm.load(path_model)#載入模型

sum = svm.predict(data, True)#預測,返回值為置信度,大於0.5為違建1.

res= 1.0-(float)(1 / (1 + math.exp((-1.0)*sum)));

2.c++版本

CvSVMParams param;

params.svm_type = CvSVM::C_SVC; //用於n-類分類問題 (n  2)

params.C = 1;

params.kernel_type = CvSVM::LINEAR;  //CvSVM::RBF; //向量積

params.term_crit =cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

CvSVM svm;

Mat data = Mat::zeros(nSampleNum, nCols,CV_32FC1);

Mat label = Mat::zeros(nSampleNum, 1,CV_32FC1);

svm.train(data, label, Mat(), Mat(),m_cParams); //訓練

svm.save(“model.xml”); //儲存模型

svm.load(“model.xml”); //儲存模型

sum = svm.predict(data, True)#預測

dCof= 1.0-(float)(1 / (1 + math.exp((-1.0)*sum)));

// dCof = 1.0-max(min((float)(1 / (1 + exp((-1.0)*dSum))),float(1.0)),float(0.0));

備註:

1. opencv2.4.13   python、c++版本預測結果不同,原因是params.term_crit= cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6); python版本採用預設的迭代次數1000;

1.      cvTermCriteria(type,max_iter, epsilon);

int type---迭代演算法終止條件的型別,是下面之一:

CV_TERMCRIT_ITER---在完成最大的迭代次數之後,停止演算法

CV_TERMCRIT_EPS----當演算法的精確度小於引數double epsilon指定的精確度時,停止演算法

CV_TERMCRIT_ITER+CV_TERMCRIT_EPS--無論是哪一個條件先達到,都停止演算法

預設:CV_TERMCRIT_ITER+CV_TERMCRIT_EPS

 max_iter; 最大迭代次數,預設:1000

epsilon; 精度, 預設:1e-6


三、libsvm

         libsvm:臺灣大學林智仁(Lin Chih-Jen)教授等開發設計的一個簡單、易於使用和快速有效的SVM模式識別與迴歸的軟體包.

1. 安裝過程:

1)解壓到某個你指定的路徑;

2)cd 進入libsvm-3.22資料夾,然後make;

3)cd 進入libsvm的python子資料夾 /libsvm-3.17/python,然後make;

    4)假設你此時位於libsvm的python子資料夾/libsvm-3.22/python路徑下:

$ sudo cp *.py /usr/lib/python2.7/dist-packages/  

$ cd ..  

$ sudo cp libsvm.so.2 /usr/lib/python2.7/ 

5) 檢驗一下,新開一個terminal,進入python

import svm  

import svmutil  

如果成功,說明裝好了

2.程式碼路徑:/home/zhangjing/ocr/svm/train_libsvm.py

資料集:/home/zhangjing/ocr/svm/datasets

train:訓練集

txt:存放檢測到的特徵以及置信度,格式(特徵編號置信度)

txt/label0.txt: 存放非違建的標籤,格式(08280001.txt 0)

txt/label1.txt 存放違建對應的標籤,格式(08280002.txt 1)

test:測試集

引數說明:

訓練的引數:

param = svmutil.svm_parameter('-t 2  -b 1')

-t:  核函式型別(kernel_type)

·        0 --linear(線性核):

u'*v

·        1 --polynomial(多項式核):

(gamma*u'*v + coef0)^degree

·        2 --radial basis function(RBF,徑向基核/高斯核):

exp(-gamma*|u-v|^2)

·        3 --sigmoid(S型核):

tanh(gamma*u'*v + coef0)

·        4 --precomputed kernel(預計算核):

核矩陣儲存在training_set_file

-b: 是否估算正確概率,取值0 - 1,預設為0備註:該引數只有在訓練時設定為1時,預測時使用’-b 1’時才計算置信度的。

測試的引數:

p_label, p_acc, p_val = svmutil.svm_predict(label,data, model, '-b 1')

p_labs: 是儲存預測標籤的列表。

p_acc: 儲存了預測的精確度,均值和迴歸的平方相關係數。

p_vals: 在指定引數'-b 1'時將返回判定係數(判定的可靠程度)。

實驗結果:

1. libsvm可預測出置信度,opencv svm只預測出標籤號;

2. opencv、matlab是libsvm2.6版本,而測試的libsvm3.22,發現opencv的svm準確率能高些;

參考文獻: