1. 程式人生 > >SVM多分類問題 libsvm在matlab中的應用

SVM多分類問題 libsvm在matlab中的應用

對於支援向量機,其是一個二類分類器,但是對於多分類,SVM也可以實現。主要方法就是訓練多個二類分類器。
一、多分類方式
1、一對所有(One-Versus-All OVA)
給定m個類,需要訓練m個二類分類器。其中的分類器 i 是將 i 類資料設定為類1(正類),其它所有m-1個i類以外的類共同設定為類2(負類),這樣,針對每一個類都需要訓練一個二類分類器,最後,我們一共有 m 個分類器。對於一個需要分類的資料 x,將使用投票的方式來確定x的類別。比如分類器 i 對資料 x 進行預測,如果獲得的是正類結果,就說明用分類器 i 對 x 進行分類的結果是: x 屬於 i 類,那麼,類i獲得一票。如果獲得的是負類結果,那說明 x 屬於 i 類以外的其他類,那麼,除 i 以外的每個類都獲得一票。最後統計得票最多的類,將是x的類屬性。
2、所有對所有(All-Versus-All AVA)
給定m個類,對m個類中的每兩個類都訓練一個分類器,總共的二類分類器個數為 m(m-1)/2 .比如有三個類,1,2,3,那麼需要有三個分類器,分別是針對:1和2類,1和3類,2和3類。對於一個需要分類的資料x,它需要經過所有分類器的預測,也同樣使用投票的方式來決定x最終的類屬性。但是,此方法與”一對所有”方法相比,需要的分類器較多,並且因為在分類預測時,可能存在多個類票數相同的情況,從而使得資料x屬於多個類別,影響分類精度。
對於多分類在matlab中的實現來說,matlab自帶的svm分類函式只能使用函式實現二分類,多分類問題不能直接解決,需要根據上面提到的多分類的方法,自己實現。雖然matlab自帶的函式不能直接解決多酚類問題,但是我們可以應用libsvm工具包。libsvm工具包採用第二種“多對多”的方法來直接實現多分類,可以解決的分類問題(包括C- SVC、n - SVC )、迴歸問題(包括e - SVR、n - SVR )以及分佈估計(one-class-SVM )等,並提供了線性、多項式、徑向基和S形函式四種常用的核函式供選擇。

二、用libsvm在matlab中實現多分類(訓練函式svmtrain+預測函式svmpredict)
對於libsvm中訓練模型的函式svmtrain來說,model = svmtrain(訓練資料類別, 訓練資料, ‘一系列引數’);其中引數的形式如‘-c 2 -g 0.02’在這當中,引數主要包括以下幾個方面:
(1) -s —— 其表示SVM的型別,包括上面提到的(預設值為0):
0 —— C-SVC
1—— v-SVC
2 —— 一類SVM
3 —— e -SVR
4 —— v-SVR
(2)-t—— 其表示核函式型別(預設值為2)
0 – 線性:u’v
1 – 多項式:(r*u’v + coef0)^degree
2 – RBF函式:exp(-gamma|u-v|^2)
3 –sigmoid:tanh(r*u’v + coef0)
(3)核函式引數設定
-d ——核函式中的degree設定(針對多項式核函式)(預設3)
-g ——核函式中的gamma函式設定(針對多項式/rbf/sigmoid 核函式)(預設1/ k,k為特徵值個數)
-r ——核函式中的coef0設定(針對多項式/sigmoid核函式)((預設0)
-c ——設定C-SVC,e -SVR和v-SVR的引數(損失函式)(預設1)
-n——設定nu-SVC,一類SVM和nu- SVR的引數(預設0.5)
-p ——設定e -SVR 中損失函式p的值(預設0.1)
-m ——設定cache記憶體大小,以MB為單位(預設40)
-e ——設定允許的終止判據(預設0.001)
-h ——是否使用啟發式,0或1(預設1)
-b ——是否訓練一個SVC或者SVR模型,0或1(預設0)
-wi ——設定第i類的引數C為weight*C(C-SVC中的C)(預設1)
-v——n-fold互動檢驗模式,n為fold的個數,必須大於等於2(訓練中使用了-v引數進行交叉驗證時,返回的不是一個模型,而是交叉驗證的分類的正確率或者回歸的均方根誤差)

使用函式svmtrain訓練分類模型後,會返回一個結構體,其中包括資料:
(1) parameters(一個5*1的陣列)
第一個元素:-s,SVM的型別(int預設為0)
第二個元素:-t,核函式型別(預設為2)
第三個元素:-d,核函式中的degree設定(針對多項式核函式)(預設3);
     第四個元素:-g,核函式中的r(gamma)函式設定(針對多項式/rbf/sigmoid核函式) (預設為類別數目的倒數);
第五個元素:-r 核函式中的coef0設定(針對多項式/sigmoid核函式)((預設0)
(2)-nr_class: 表示資料集中有多少個類(int)
(3)-totalSV: 表示支援向量的總數。(int)
(4)-rho: 決策函式wx+b中的常數偏置的相反數(-b)。
(5)-Label: 表示資料集中類別的標籤
(6)ProbA: 使用-b引數時用於概率估計的數值,否則為空。
ProbB: 使用-b引數時用於概率估計的數值,否則為空。
(7)-nSV: 表示每類樣本的支援向量的數目,和Label的類別標籤對應。
(8)-sv_coef: 表示每個支援向量在決策函式中的係數。
(9)-SVs: 表示所有的支援向量,如果特徵是n維的,支援向量一共有m個,則為m x n的稀疏矩陣。
(10)nu: -n引數的顯示
(11)iter: 迭代次數
(12)obj: 表示SVM檔案轉換的二次規劃求解的最小值

svmpredict函式根據測試資料類屬性,測試資料,以及通過svmtrain函式獲得的模型來進行分類預測並計算分類精度,[predict_label, accuracy, dec_values]=svmpredict(測試資料類屬性,測試資料,分類模型),返回值介紹如下:
(1)predicted_label:儲存著分類後樣本所對應的類屬性。
(2)accuracy:一個3 * 1的陣列,依次為:分類的正確率、迴歸的均方根誤差、迴歸的平方相關係數。
(3)decision_values/prob_estimates:是一個表示概率的陣列,對於一個m個數據,n個類的情況,如果指定“-b 1”引數(使用,則n x k的矩陣,每一行表示這個樣本分別屬於每一個類別的概率;如果沒有指定“-b 1”引數,則為n * n×(n-1)/2的矩陣,每一行表示n(n-1)/2個二分類SVM的預測結果。

三、 使用libsvm進行分類的步驟
(1) 對資料做歸一化(simple scaling)
對資料進行簡單的縮放處理(scaling),縮放的最主要優點是能夠避免大數值區間的屬性過分支配了小數值區間的屬性。另一個優點能避免計算過程中數值複雜度。(在試驗中發現,歸一化過程,分別對訓練資料和測試資料進行歸一化處理,比對訓練資料和測試資料整體進行歸一化處理獲得的分類精度要高)
(2) 應用 RBF kernel
(3) 選擇得到最優的c和g
c是懲罰因子,是一個在訓練模型前需要自己設定的一個數值,它表示我們對類中的離群資料的重視程度,c的數值越大,表明我們越重視,越不想丟掉這些離群的資料;g是核函式中的gamma函式設定(針對多項式/rbf/sigmoid 核函式)(預設1/ k,k為特徵值個數) 。c和g的選擇對分類精度影響很大,在本文的試驗中,用函式SVMcgForClass選擇完成c和g的選擇。
(4) 用得到的最優c和g訓練分類模型
使用libsvm中的svmtrain函式來訓練多分類模型
(5)測試
使用libsvm中的svmpredict函式來測試分類精度

四、實驗
使用libsvm提供的資料進行多分類實驗,下載的wine資料,是關於葡萄酒種類,下載的資料包括這裡寫圖片描述三部分,分別是類數3,178個酒資料的13個屬性列表,178個酒資料對應的類屬性列表。程式碼如下:

function [ classfication ] = test( train,test )

load chapter12_wine.mat                       %下載資料

train=[wine(1:30,:);wine(60:95,:);wine(131:153,:)]; %選取訓練資料
train_group=[wine_labels(1:30);wine_labels(60:95); wine_labels(131:153)];%選取訓練資料類別標識
test=[wine(31:59,:);wine(96:130,:);wine(154:178,:)];%選取測試資料
test_group=[wine_labels(31:59);wine_labels(96:130); wine_labels(154:178)]; %選取測試資料類別標識

%資料預處理,用matlab自帶的mapminmax將訓練集和測試集歸一化處理[0,1]之間
%訓練資料處理
[train,pstrain] = mapminmax(train');
% 將對映函式的範圍引數分別置為0和1
pstrain.ymin = 0;
pstrain.ymax = 1;
% 對訓練集進行[0,1]歸一化
[train,pstrain] = mapminmax(train,pstrain);
% 測試資料處理
[test,pstest] = mapminmax(test');
% 將對映函式的範圍引數分別置為01
pstest.ymin = 0;
pstest.ymax = 1;
% 對測試集進行[0,1]歸一化
[test,pstest] = mapminmax(test,pstest);
% 對訓練集和測試集進行轉置,以符合libsvm工具箱的資料格式要求
train = train';
test = test';

%尋找最優c和g
%粗略選擇:c&g 的變化範圍是 2^(-10),2^(-9),...,2^(10)
[bestacc,bestc,bestg] = SVMcgForClass(train_group,train,-10,10,-10,10);
%精細選擇:c 的變化範圍是 2^(-2),2^(-1.5),...,2^(4), g 的變化範圍是 2^(-4),2^(-3.5),...,2^(4)
[bestacc,bestc,bestg] = SVMcgForClass(train_group,train,-2,4,-4,4,3,0.5,0.5,0.9);

%訓練模型
cmd = ['-c ',num2str(bestc),' -g ',num2str(bestg)];
model=svmtrain(train_group,train,cmd);
disp(cmd);

%測試分類
[predict_label, accuracy, dec_values]=svmpredict(test_group,test,model);

%列印測試分類結果
figure;
hold on;
plot(test_group,'o');
plot(predict_label,'r*');
legend('實際測試集分類','預測測試集分類');
title('測試集的實際分類和預測分類圖','FontSize',10);
end


五、實驗結果

這裡寫圖片描述

這裡寫圖片描述

六、問題
疑惑(1)!!!!!!!!!!!!!!!!!!!!!!!!!!!!
上面的實驗是參照libsvm 中的示例寫的,最開始按照示例思路,但是在歸一化時,自己是將訓練資料和測試資料一起歸一化的, 獲得的精度只有61%,後來改為示例中的分別對訓練資料和測試資料進行歸一化後,精度就到了現在的88%,但是不理解造成此差異的原因,希望知道原因的朋友多多指教(程式碼如下(1)和(2))

1、訓練資料和測試資料分別歸一化到[0,1]程式碼(獲得分類精度88%):

%訓練資料處理
[train,pstrain] = mapminmax(train');
% 將對映函式的範圍引數分別置為0和1
pstrain.ymin = 0;
pstrain.ymax = 1;
% 對訓練集進行[0,1]歸一化
[train,pstrain] = mapminmax(train,pstrain);

% 測試資料處理
[test,pstest] = mapminmax(test');
% 將對映函式的範圍引數分別置為0和1
pstest.ymin = 0;
pstest.ymax = 1;
% 對測試集進行[0,1]歸一化
[test,pstest] = mapminmax(test,pstest);
% 對訓練集和測試集進行轉置,以符合libsvm工具箱的資料格式要求
train = train';
test = test';

2、訓練資料和測試資料整體歸一化到[0,1]程式碼(獲得分類精度61%):

[mtrain,ntrain]=size(train);
[mtest,ntest]=size(test);
dataset=[train;test];
[dataset_scale,ps]=mapminmax(dataset',0,1);
dataset_scale=dataset_scale';
train=dataset_scale(1:mtrain,:);
test=dataset_scale((mtrain+1):(mtrain+mtest),:);

疑惑(2)!!!!!!!!!!!!!!!!!!!!!!!!!!!!
再有就是,對於c和g這兩個數值有函式可以自動調優,但是在歸一化時,如下面我要是把對訓練資料和測試資料分別歸一化的範圍從[0,1]變成[0,4],獲得的分類精度就從88%提高到了96%,這種歸一化的範圍又該怎麼確定呢?

訓練資料和測試資料分別歸一化到[0,4]程式碼(獲得分類精度96%):

%訓練資料處理
[train,pstrain] = mapminmax(train');
% 將對映函式的範圍引數分別置為0和4
pstrain.ymin = 0;
pstrain.ymax = 4;
% 對訓練集進行[0,4]歸一化
[train,pstrain] = mapminmax(train,pstrain);

% 測試資料處理
[test,pstest] = mapminmax(test');
% 將對映函式的範圍引數分別置為0和4
pstest.ymin = 0;
pstest.ymax = 4;
% 對測試集進行[0,4]歸一化
[test,pstest] = mapminmax(test,pstest);
% 對訓練集和測試集進行轉置,以符合libsvm工具箱的資料格式要求
train = train';
test = test';