1. 程式人生 > >解密SVM系列(五):matlab下libsvm的簡單使用:分類與迴歸

解密SVM系列(五):matlab下libsvm的簡單使用:分類與迴歸

本節簡單介紹一下libsvm的使用方法。關於libsvm似乎曾經使用過,那個時候主要用libsvm進行。當時還翻譯過關於

介紹與分類實驗

下載下來的libsvm其實包含好多個平臺的工具箱軟體,c++,matlab,java,python都有。他們的函式使用方法是一樣的。

那麼在下載完以後,點選裡面的matlab下平臺,直接在點選裡面的make.m函式就可以了。正常情況下如果你的matlab含有編譯平臺的話直接就可以運行了,如果沒有,還需要選擇一個平臺 mex -setup 。小提醒一下,這個編譯過程不要在c盤下使用,也就是libsvm先不要放在c盤,涉及到許可權,機器不讓編譯。編譯完後在matlab的設定路徑中新增進去編譯的資料夾及其內容,那麼就可以使用了。正常編譯的過程是這樣的:
這裡寫圖片描述

在上面的人臉識別實驗中曾經介紹過裡面的主要函式,這裡為了放在一塊,把那裡的拿過來吧:

目前版LIBSVM(3.2.0)在matlab下編譯完後只有四個函式,libsvmread,Libsvmwrite,svmtrain(matlab自帶的工具箱中有一個同名的函式),svmpredict。

(1)libsvmread主要用於讀取資料
這裡的資料是非matlab下的.mat資料,比如說是.txt,.data等等,這個時候需要使用libsvmread函式進行轉化為matlab可識別資料,比如自帶的資料是heart_scale資料,那麼匯入到matlab有兩種方式,一種使用libsvmread函式,在matlab下直接libsvmread(heart_scale);第二種方式為點選matlab的‘匯入資料’按鈕,然後導向heart_scale所在位置,直接選擇就可以了。個人感覺第二種方式超級棒,無論對於什麼資料,比如你在哪個資料庫下下載的資料,如何把它變成matlab下資料呢?因為有的資料libsvmread讀取不管用,但是‘匯入資料’後就可以變成matlab下資料。

(2)libsvmwrite寫函式,就是把已知資料存起來
使用方式為:libsvmwrite(‘filename’,label_vector, instance_matrix);
label_vector是標籤,instance_matrix為資料矩陣(注意這個資料必須是稀疏矩陣,就是裡面的資料不包含沒用的資料(比如很多0),有這樣的資料應該去掉再存)。

(3)svmtrain訓練函式,訓練資料產生模型的
一般直接使用為:model=svmtrain(label,data,cmd); label為標籤,data為訓練資料(資料有講究,每一行為一個樣本的所有資料,列數代表的是樣本的個數),每一個樣本都要對應一個標籤(分類問題的話一般為二分類問題,也就是每一個樣本對應一個標籤)。cmd為相應的命令集合,都有哪些命令呢?很多,-v,-t,-g,-c,等等,不同的引數代表的含義不同,比如對於分類問題,這裡-t就表示選擇的核函式型別,-t=0時線性核。-t=1多項式核,-t=2,徑向基函式(高斯),-t=3,sigmod核函式,新版出了個-t=4,預計算核(還不會用);-g為核函式的引數係數,-c為懲罰因子係數,-v為交叉驗證的數,預設為5,這個引數在svmtrain寫出來使用與不寫出來不使用的時候,model出來的東西不一樣,不寫的時候,model為一個結構體,是一個模型,可以帶到svmpredict中直接使用,寫出來的時候,出來的是一個訓練模型的準確率,為一個數值。一般情況下就這幾個引數重要些,還有好多其他引數,可以自己參考網上比較全的,因為下面的這種方法的人臉識別就用到這麼幾個引數,其他的就不寫了。

(3)svmpredict訓練函式,使用訓練的模型去預測來的資料型別
使用方式為:
[predicted_label,accuracy,decision_values/prob_estimates]= svmpredict(testing_label_vector,testing_instance_matrix,model,’libsvm_options’)
或者:
[predicted_label]=svmpredict(testing_label_vector,testing_instance_matrix, model, ‘libsvm_options’)
第一種方式中,輸出為三個引數,預測的型別,準確率,評估值(非分類問題用著),輸入為測試型別(這個可與可無,如果沒有,那麼預測的準確率accuracy就沒有意義了,如果有,那麼就可以通過這個值與預測出來的那個型別值相比較得出準確率accuracy,但是要說明一點的是,無論這個值有沒有,在使用的時候都得加上,即使沒有,也要隨便加上一個型別值,反正你也不管它對不對,這是函式使用所規定的的),再就是輸入資料值,最後是引數值(這裡的引數值只有兩種選擇,-p和-b引數),曾經遇到一個這樣的問題,比如說我在訓練函式中規定了-g引數為0.1,那麼在預測的時候是不是也要規定這個引數呢?當你規定了以後,程式反而錯誤,提醒沒有svmpredict的-g引數,原因是在svmtrain後會出現一個model,而在svmpredict中你已經用了這個model,而這個model中就已經包含了你所有的訓練引數了,所以svmpredict中沒有這個引數,那麼對於的libsvm_options就是-p和-b引數了。對於函式的輸出,兩種方式呼叫的方法不一樣,第一種呼叫把所有需要的資料都調用出來了,二第二種呼叫,只調用了predicted_label預測的型別,這裡我們可以看到,在單純的分類預測模型中,其實第二種方式更好一些吧,既簡單有實用。

致此,四個函式在分類問題中的介紹大概如此,當然還有很多可以優化的細節就不詳細說了,比如可以再使用那些引數的時候,你如果不規定引數的話,所有的-引數都是使用預設的,預設的就可能不是最好的吧,這樣就涉及到如何去優化這個引數了。

使用就介紹到這裡吧,下面實戰一下,樣本集選擇前面使用的200個非線性樣本集,函式如下:

%%
% * libsvm 工具箱簡單使用
%
%% 載入資料
% * 最終data格式:m*n,m樣本數,n維度
% * label:m*1  標籤為-1與1這兩類
clc
clear
close all
data = load('data_test1.mat');
data = data.data';
%選擇訓練樣本個數
num_train = 80;
%構造隨機選擇序列
choose = randperm(length(data));
train_data = data(choose(1:num_train),:);
gscatter(train_data(:,1),train_data(:,2),train_data(:,3));
label_train = train_data(:,end);
test_data = data(choose(num_train+1:end),:);
label_test = test_data(:,end);
predict = zeros(length(test_data),1);
%% ----訓練模型並預測分類
model = svmtrain(label_train,train_data(:,1:end-1),'-t 2');
% -t = 2 選擇徑向基函式核 
true_num = 0;
for i = 1:length(test_data)
    % 作為預測,svmpredict第一個引數隨便給個就可以
    predict(i) = svmpredict(1,test_data(i,1:end-1),model);
end
%% 顯示結果
figure;
index1 = find(predict==1);
data1 = (test_data(index1,:))';
plot(data1(1,:),data1(2,:),'or');
hold on
index2 = find(predict==-1);
data2 = (test_data(index2,:))';
plot(data2(1,:),data2(2,:),'*');
hold on
indexw = find(predict~=(label_test));
dataw = (test_data(indexw,:))';
plot(dataw(1,:),dataw(2,:),'+g','LineWidth',3);
accuracy = length(find(predict==label_test))/length(test_data);
title(['predict the testing data and the accuracy is :',num2str(accuracy)]);

可以看到,關於svm的部分就那麼一點,其他的都是輔助吧,那麼一個結果如下:
這裡寫圖片描述

資料人為設定了一些重疊,這個結果算是非常好了。當然對於libsvm函式,裡面還有許多細節,像引數選擇等等,不同的引數結果是不一樣的,這就待你去探究了。

至此SVM系列文章就到這裡吧,感謝能看到這裡的朋友~_~。

迴歸實驗

迴歸問題不像分類問題,迴歸問題相當於根據訓練樣本訓練出一個擬合函式一樣,可以根據這個擬合函式可以來預測給定一個樣本的輸出值。可以看到分類問題輸出的是樣本所屬於的類,而回歸問題輸出的是樣本的預測值。

常用的地方典型的比如股票預測,人口預測等等此類預測問題。

libsvm同樣可以進行迴歸預測,所需要改變的只是裡面的引數設定。檢視libsvm的官網介紹引數詳情如下:

options:
-s svm_type : set type of SVM (default 0)
    0 -- C-SVC
    1 -- nu-SVC
    2 -- one-class SVM
    3 -- epsilon-SVR
    4 -- nu-SVR
-t kernel_type : set type of kernel function (default 2)
    0 -- linear: u'*v
    1 -- polynomial: (gamma*u'*v + coef0)^degree
    2 -- radial basis function: exp(-gamma*|u-v|^2)
    3 -- sigmoid: tanh(gamma*u'*v + coef0)
-d degree : set degree in kernel function (default 3)
-g gamma : set gamma in kernel function (default 1/num_features)
-r coef0 : set coef0 in kernel function (default 0)
-c cost : set the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1)
-n nu : set the parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5)
-p epsilon : set the epsilon in loss function of epsilon-SVR (default 0.1)
-m cachesize : set cache memory size in MB (default 100)
-e epsilon : set tolerance of termination criterion (default 0.001)
-h shrinking: whether to use the shrinking heuristics, 0 or 1 (default 1)
-b probability_estimates: whether to train a SVC or SVR model for probability estimates, 0 or 1 (default 0)
-wi weight: set the parameter C of class i to weight*C, for C-SVC (default 1)

可以看到-s svm_type 控制的就是訓練型別,而當-s等於3或4的時候,就是迴歸模型SVR。
-s 3 就是常用的帶懲罰項的 SVR模型,我們用這個實驗。我使用的是libsvm3.2.0工具箱,版本不同可能會帶來呼叫方式的不同。測試實驗的程式碼如下,可能會有一些細節需要自己去探索:

close all;
clear;
clc;
%%
% 生成待迴歸的資料
x = (-1:0.1:1)';
y = -100*x.^3 + x.^2 - x + 1;
% 加點噪聲
y = y+ 20*rand(length(y),1);
%% 採用交叉驗證選擇引數
mse = 10^7;
for log2c = -10:0.5:3,
    for log2g = -10:0.5:3,
        % -v 交叉驗證引數:在訓練的時候需要,測試的時候不需要,否則出錯
        cmd = ['-v 3 -c ', num2str(2^log2c), ' -g ', num2str(2^log2g) , ' -s 3 -p 0.4 -t 3'];
        cv = svmtrain(y,x,cmd);
        if (cv < mse),
            mse = cv; bestc = 2^log2c; bestg = 2^log2g;
        end
    end
end
%%  訓練--
cmd = ['-c ', num2str(2^bestc), ' -g ', num2str(2^bestg) , ' -s 3 -p 0.4 -n 0.1'];
model = svmtrain(y,x,cmd);
% model
% 利用建立的模型看其在訓練集合上的迴歸效果
% 注意libsvm3.2.0的svmpredict函式必須有三個引數輸出
[py,~,~] = svmpredict(y,x,model);
figure;
plot(x,y,'o');
hold on;
plot(x,py,'g+');
%% 
% 進行預測新的x值
%-- 產生[-1 1]的隨機數
testx = -2+(2-(-2))*rand(10,1);
testy = zeros(10,1);% 理論y值無所謂
[ptesty,~,~] = svmpredict(testy,testx,model);
hold on;
plot(testx,ptesty,'r*');
legend('原始資料','迴歸資料','新資料');
grid on;
% title('t=0:線性核')
% title('t=1:多項式核')
% title('t=2:徑向基函式(高斯)')
title('t=3:sigmod核函式')

這裡我隨機生成一個3次函式的隨機資料,測試了幾種不同svm裡面的核函式:
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

因為我們的資料是由三次函式模擬生成的,所以可以看到,在這種情況下使用線性核t=0時候效果更好,然而實際情況下一般我們也不知道資料的分佈函式,所以在選擇核函式的時候還是需要多實驗,找到最適合自己資料的核函式。

這裡採用了交叉驗證的方式自適應選擇模型中重要的兩個引數,需要注意的是引數的範圍,不要太大,步長可能也需要控制,否則在資料量很大的時候需要執行很久。