svm以及各種版本的實現
一、簡述
以二維平面上的分類為例,下面給出了不同的分類可能,哪個才是最優的分類呢?
可以看出第一種分類方法是最好的,為什麼呢?因為它的分類平面到兩類邊界的距離(Margin)最大。
所以SVM也叫Large Margin分類器。
各種資料對它評價甚高,說“ 它在解決小樣本、非線性及高維模式識別中表現出許多特有的優勢,並能夠推廣應用到函式擬合等其他機器學習問題中”。
SVM之線性分類器
如果一個線性函式能夠將樣本完全正確的分開,就稱這些資料是線性可分的,否則稱為非線性可分的。
什麼叫線性函式呢?在一維空間裡就是一個點,在二維空間裡就是一條直線,三維空間裡就是一個平面,以此類推。
如果不關注空間的維數,這種線性函式就是前言中所說的那個統一的名稱——
在樣本空間中,劃分超平面可通過如下線性方程來描述:
如圖:
在這兩個超平面上的樣本點也就是理論上離分隔超平面最近的點,是它們的存在決定了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準確率能高些;
參考文獻: