前些文章曾經(jīng)細(xì)數(shù)過從決策樹、貝葉斯算法等一些簡單的算法到神經(jīng)網(wǎng)絡(luò)(BP)、支持向量機(jī)(SVM)、adaboost等一些較為復(fù)雜的機(jī)器學(xué)習(xí)算法(對其中感興趣的朋友可以往前的博客看看),各種算法各有優(yōu)缺點(diǎn),基本上都能處理線性與非線性樣本集,然通觀這些算法來看,個(gè)人感覺對于數(shù)據(jù)(無論線性還是非線性)的分類上來說,里面比較好的當(dāng)數(shù)BP、SVM、adaboost元算法這三種了,由于前面在介紹相應(yīng)算法原理以及實(shí)驗(yàn)的時(shí)候所用的樣本以及分類情況都是二分類的,對于多分類的情況未曾涉及過,而實(shí)際情況往往是分類多分類數(shù)據(jù)的樣本較多,本節(jié)旨在對BP、SVM、adaboost這三種個(gè)人感覺較好的算法進(jìn)行一個(gè)對比,同時(shí)實(shí)驗(yàn)一個(gè)簡單的非線性多分類樣本。
既然是多分類樣本,首先對樣本需要理解,所謂多分類就是樣本集中不止2類樣本,至少3類才稱得上是多分類。比如下面一個(gè)二維非線性的多類樣本集(這也是后面我們實(shí)驗(yàn)的樣本集):
好了,曾經(jīng)在單個(gè)算法介紹的時(shí)候,里面的實(shí)驗(yàn)都是二分類的(也就是只有上述5類樣本中的兩類),二分類的方式很簡單,不是你就是我的這種模式,那么從二分類到多分類該怎么轉(zhuǎn)換呢?假如一個(gè)樣本不是我,那也可能不是你呀,可能是他它她對吧,這個(gè)時(shí)候該如何呢?
現(xiàn)在一般的方式都是將多分類問題轉(zhuǎn)化為二分類問題,因?yàn)榍懊嬖S多算法在原理推導(dǎo)上都是假設(shè)樣本是二分類的,像SVM,整個(gè)推導(dǎo)過程以至結(jié)論都是相對二分類的,根本沒有考慮多分類,依次你想將SVM直接應(yīng)用于多分類是不可能的,除非你在從原理上去考慮多分類的情況,然后得到一個(gè)一般的公式,最后在用程序?qū)崿F(xiàn)這樣才可以。
那么多分類問題怎么轉(zhuǎn)化為二分類問題?很簡單,一個(gè)簡單的思想就是分主次,采取投票機(jī)制。轉(zhuǎn)化的方式有兩種,因?yàn)榉诸悊栴}最終需要訓(xùn)練產(chǎn)生一個(gè)分類器,產(chǎn)生這個(gè)分類器靠的是訓(xùn)練樣本,前面的二分類問題實(shí)際上就是產(chǎn)生了一個(gè)分類器,而多分類問題根據(jù)訓(xùn)練集產(chǎn)生的可不止是一個(gè)分類器,而是多個(gè)分類器。
那第一種方式就是將訓(xùn)練樣本集中的某一類當(dāng)成一類,其他的所有類當(dāng)成另外一類,像上面的5類,我把最中間的一類當(dāng)成是第一類,并重新賦予類標(biāo)簽為1,而把四周的四類都認(rèn)為是第二類,并重新賦予類標(biāo)簽維-1,好了現(xiàn)在的問題是不是就是二分類問題了?是的。那二分類好辦,用之前的任何一個(gè)算法處理即可。好了,這是把最中間的當(dāng)成一類的情況下建立的一個(gè)分類器。同理,我們是不是也可以把四周任何一類自成一類,而把其他的統(tǒng)稱為一類呀?當(dāng)然可以,這樣依次類推,我們共建立了幾個(gè)分類器?像上面5類就建立了5個(gè)分類器吧,好了到了這我們該怎么劃分測試集的樣本屬于哪一類了?注意測試集是假設(shè)不知道類標(biāo)簽的,那么來了一個(gè)測試樣本,我把它依次輸入到上述建立的5個(gè)分類器中,看看最終它屬于哪一類的多,那它就屬于哪一類了吧。比如假設(shè)一個(gè)測試樣本本來是屬于中間的(假設(shè)為第5類吧),那么先輸入第五類自成一類的情況,這個(gè)時(shí)候發(fā)現(xiàn)它屬于第五類,記錄一下5,然后再輸入左上角(假設(shè)為1類)自成一類的情況,那么發(fā)現(xiàn)這個(gè)樣本時(shí)不屬于1類的,而是屬于2,3,4,5這幾類合并在一起的一類中,那么它屬于2,3,4,5中的誰呢?都有可能吧,那么我都記一下,此時(shí)記一下2,3,4,5。好了再到有上角,此時(shí)又可以記一下這個(gè)樣本輸入1,3,4,5.依次類推,最后把這5個(gè)分類器都走一遍,就記了好多1~5的標(biāo)簽吧,然后去統(tǒng)計(jì)他們的數(shù)量,比如這里統(tǒng)計(jì)1類,發(fā)現(xiàn)出現(xiàn)了3次,2,3,4都出現(xiàn)了3次,就5出現(xiàn)了5次,那么我們就有理由認(rèn)為這個(gè)樣本屬于第五類,那么現(xiàn)在想想是不是就把多類問題解決了呢?而這個(gè)過程參考這位大神博客中的一張圖表示就如下:
這是第一種方式,那還有第二種分類方式,思想類似,也是轉(zhuǎn)化為二分類問題,不過實(shí)現(xiàn)上不同。前面我們是挑一類自成一類,剩下的所有自成一類,而這里,也是從中挑一類自成一類,然剩下的并不是自成一類,而是在挑一類自成一類,也就是說從訓(xùn)練樣本中挑其中的兩類來產(chǎn)生一個(gè)分類器。像上述的5類,我先把1,2,類的訓(xùn)練樣本挑出來,訓(xùn)練一個(gè)屬于1,2,類的分類器,然后把1,3,挑出來訓(xùn)練一個(gè)分類器,再1,4再1,5再2,3,等等(注意2,1與1,2一樣的,所以省去了),那這樣5類樣本需要建立多少個(gè)分類器呢?n*(n-1)/2吧,這里就是5*4/2=10個(gè)分類器,可以看到比上面的5個(gè)分類器多了5個(gè)。而且n越大,多的就越多。好了建立完分類器,剩下的問題同樣采取投票機(jī)制,來一個(gè)樣本,帶到1,2建立的發(fā)現(xiàn)屬于1,屬于1類的累加器加一下,帶到1,3建立的發(fā)現(xiàn)也屬于1,在加一下,等等等等。最后看看5個(gè)類的累加器哪個(gè)最大就屬于哪一類。那么一個(gè)問題來了,會(huì)不會(huì)出現(xiàn)像上面那種情況,有兩個(gè)或者更多個(gè)累加器的值是一樣的呢?答案是有的,但是這種情況下,出現(xiàn)一樣的概率可比上述情況的概率小多了(比較是10個(gè)分類器來的結(jié)果,怎么也得比你5個(gè)的要好吧),同樣一個(gè)示意圖如下:
首先采用神經(jīng)網(wǎng)絡(luò)算法來實(shí)驗(yàn),同時(shí)為了速度與準(zhǔn)確率,我們實(shí)驗(yàn)matlab的神經(jīng)網(wǎng)絡(luò)工具箱,關(guān)于該工具箱怎么用,請參考:
機(jī)器學(xué)習(xí)之實(shí)戰(zhàn)matlab神經(jīng)網(wǎng)絡(luò)工具箱
為了實(shí)現(xiàn)較好的效果,這里我們直接使用matlab在BP下建立起來的模式識(shí)別工具箱(nprtool)。該工具箱的使用可以通過GUI界面直接操作,也可以命令操作,需要說明一點(diǎn)的就是數(shù)據(jù)的輸入形式,尤其是對于類標(biāo)簽的設(shè)置,在該工具箱下,類標(biāo)簽已經(jīng)不再是用數(shù)字1~5直接表示,而是用一個(gè)向量,比如類別1可以表示為[1,0,0,0,0],類別3可以表示為[0,0,1,0,0]這種表示方式。同時(shí)如果樣本輸入每一行表示一個(gè)樣本,那么類別就得像上面那一,每一行表示一個(gè)樣本類別。如果每一列為一個(gè)樣本,那么對應(yīng)的標(biāo)簽也是每一列一個(gè)樣本,下面實(shí)驗(yàn)每一列表示一個(gè)樣本的樣本集:
%% % * matlab模式識(shí)別工具箱的分類設(shè)計(jì)% * 多類非線性分類% %% clcclearclose all%% Load data% * 數(shù)據(jù)預(yù)處理data = load('data_test.mat');data = data.data;%選擇訓(xùn)練樣本個(gè)數(shù)num_train = 200;%共500個(gè)樣本%構(gòu)造隨機(jī)選擇序列choose = randperm(length(data));train_data = data(choose(1:num_train),:);label_temp = train_data(:,end);label_train = zeros(length(train_data),5);%把輸出分類標(biāo)簽改為工具箱要求的格式for i = 1:length(train_data) label_train(i,label_temp(i)) = 1; endtrain_data = train_data(:,1:end-1)';label_train = label_train';%test_data = data(choose(num_train+1:end),:);label_temp = test_data(:,end);label_test = zeros(length(test_data),5);%把輸出分類標(biāo)簽改為工具箱要求的格式for i = 1:length(test_data) label_test(i,label_temp(i)) = 1; endtest_data = test_data(:,1:end-1)';label_test = label_test';%%% Create a Pattern Recognition NetworkhiddenLayerSize = 10;net = patternnet(hiddenLayerSize);% 將訓(xùn)練集再按比例內(nèi)分為訓(xùn)練集、驗(yàn)證集、測試集net.divideParam.trainRatio = 70/100;net.divideParam.valRatio = 15/100;net.divideParam.testRatio = 15/100;% Train the Network[net,tr] = train(net,train_data,label_train);% Test the Networkpredict = net(test_data);[~,predict] = max(predict);%% show the result --testingsfigure;gscatter(test_data(1,:),test_data(2,:),predict);[~,label_test] = max(label_test);accuracy = length(find(predict==label_test))/length(test_data);title(['predict the testing data and the accuracy is :',num2str(accuracy)]);
可以看到,其實(shí)程序開頭許多對數(shù)據(jù)進(jìn)行了訓(xùn)練樣本與測試樣本的選擇,同時(shí)對類標(biāo)簽進(jìn)行了變化。之間部分是建立模式識(shí)別的神經(jīng)網(wǎng)絡(luò)網(wǎng)路系統(tǒng),最后應(yīng)用這個(gè)網(wǎng)絡(luò)對測試集進(jìn)行測試,得到一個(gè)結(jié)果如下:
下面我們來通過svm方法進(jìn)行上述數(shù)據(jù)的分類。由于上面的BP部分直接采用工具箱函數(shù),并沒有涉及到前面我們說的兩種由二分類到多分類的方法,對于svm我們將把兩種方式都演示一遍。這里我會(huì)用到libsvm工具箱,關(guān)于該工具箱怎么使用請看:
第一種:
%% % * libsvm工具箱實(shí)驗(yàn)% * 多類非線性分類% %% clcclearclose all%% Load data% * 數(shù)據(jù)預(yù)處理--分兩類情況data = load('data_test.mat');data = data.data;%選擇訓(xùn)練樣本個(gè)數(shù)num_train = 200;%構(gòu)造隨機(jī)選擇序列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);%% svm的構(gòu)建與訓(xùn)練for i = 1:5 %5類 %重新歸類 label_temp = label_train; index1 = find(label_train == i); index2 = find(label_train ~= i); label_temp(index1) = 1; label_temp(index2) = -1; % 訓(xùn)練模型 model{i} = svmtrain(label_temp,train_data(:,1:end-1),'-t 2');end% 用模型來預(yù)測測試集的分類predict = zeros(length(test_data),1);for i = 1:length(test_data) data_test = test_data(i,:); addnum = zeros(1,5); for j = 1:5 temp = svmpredict(1,data_test(:,1:end-1),model{j}); if temp > 0 addnum(j) = addnum(j) + 1; else addnum = addnum + 1; addnum(j) = addnum(j) - 1; end end [~,predict(i)] = max(addnum);end%% show the result--testingfigure;gscatter(test_data(:,1),test_data(:,2),predict);accuracy = length(find(predict==label_test))/length(test_data);title(['predict the training data and the accuracy is :',num2str(accuracy)]);
結(jié)果如下:
%% % * libsvm工具箱實(shí)驗(yàn)% * 多類非線性分類% %% clcclearclose all%% Load data% * 數(shù)據(jù)預(yù)處理data = load('data_test.mat');data = data.data;%選擇訓(xùn)練樣本個(gè)數(shù)num_train = 200;%構(gòu)造隨機(jī)選擇序列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);%% svm的構(gòu)建與訓(xùn)練num = 0;for i = 1:5-1 %5類 for j = i+1:5 num = num + 1; %重新歸類 index1 = find(label_train == i); index2 = find(label_train == j); label_temp = zeros((length(index1)+length(index2)),1); %svm需要將分類標(biāo)簽設(shè)置為1與-1 label_temp(1:length(index1)) = 1; label_temp(length(index1)+1:length(index1)+length(index2)) = -1; train_temp = [train_data(index1,:);train_data(index2,:)]; % 訓(xùn)練模型 model{num} = svmtrain(label_temp,train_temp(:,1:end-1),'-t 2'); endend% 用模型來預(yù)測測試集的分類predict = zeros(length(test_data),1);for i = 1:length(test_data) data_test = test_data(i,:); num = 0; addnum = zeros(1,5); for j = 1:5-1 for k = j+1:5 num = num + 1; temp = svmpredict(1,data_test(:,1:end-1),model{num}); if temp > 0 addnum(j) = addnum(j) + 1; else addnum(k) = addnum(k) + 1; end end end [~,predict(i)] = max(addnum);end%% show the result--testingfigure;gscatter(test_data(:,1),test_data(:,2),predict);accuracy = length(find(predict==label_test))/length(test_data);title(['predict the testing data and the accuracy is :',num2str(accuracy)]);
結(jié)果如下:
關(guān)于adaboost元算法的詳細(xì)原理與實(shí)現(xiàn)過程請看上節(jié):
機(jī)器學(xué)習(xí)之白話與實(shí)戰(zhàn)adaboost元算法
考慮到adaboost元算法并沒有去找相應(yīng)的軟件工具箱,所以這里就用自己編寫的函數(shù)來實(shí)現(xiàn)吧,在上述博客中涉及到了下面會(huì)使用到的兩個(gè)子函數(shù)buildSimpleStump和adaBoostTrainDs,限于篇幅,這里不再貼出來,要使用的朋友可以自行把那里的拷貝過來。
那么在基于上述的兩個(gè)子函數(shù)下,我們在編寫兩個(gè)函數(shù),一個(gè)是adaboost的訓(xùn)練函數(shù),一個(gè)是adaboost的預(yù)測函數(shù),函數(shù)如下:
訓(xùn)練函數(shù):
function model = adaboost_train(label,data,iter)[model.dim,model.direction,model.thresh,model.alpha] = ... adaBoostTrainDs(data,label,iter);model.iter = iter;
預(yù)測函數(shù):
function predict = adaboost_predict(data,model)h = zeros(model.iter,1);for j = 1:model.iter if model.direction(j) == -1 if data(model.dim(j)) <= model.thresh(j) h(j) = -1; else h(j) = 1; end elseif model.direction(j) == 1 if data(model.dim(j)) <= model.thresh(j) h(j) = 1; else h(j) = -1; end endendpredict = sign(model.alpha'*h);
有了這兩個(gè)函數(shù)我們就可以進(jìn)行實(shí)驗(yàn)了,這里我們只一第二種方式的多分類為例,函數(shù)同上面的svm類似,只不過把那里的訓(xùn)練模型函數(shù)與預(yù)測函數(shù)改到我們這里的這種,主函數(shù)如下:
%% % * adaboost% * 多類非線性分類% %% clcclearclose all%% Load data% * 數(shù)據(jù)預(yù)處理data = load('data_test.mat');data = data.data;%選擇訓(xùn)練樣本個(gè)數(shù)num_train = 200;%構(gòu)造隨機(jī)選擇序列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);%% adaboost的構(gòu)建與訓(xùn)練num = 0;iter = 30;%規(guī)定弱分類器的個(gè)數(shù)for i = 1:5-1 %5類 for j = i+1:5 num = num + 1; %重新歸類 index1 = find(label_train == i); index2 = find(label_train == j); label_temp = zeros((length(index1)+length(index2)),1); %svm需要將分類標(biāo)簽設(shè)置為1與-1 label_temp(1:length(index1)) = 1; label_temp(length(index1)+1:length(index1)+length(index2)) = -1; train_temp = [train_data(index1,:);train_data(index2,:)]; % 訓(xùn)練模型 model{num} = adaboost_train(label_temp,train_temp,iter); endend% 用模型來預(yù)測測試集的分類predict = zeros(length(test_data),1);for i = 1:length(test_data) data_test = test_data(i,:); num = 0; addnum = zeros(1,5); for j = 1:5-1 for k = j+1:5 num = num + 1; temp = adaboost_predict(data_test,model{num}); if temp > 0 addnum(j) = addnum(j) + 1; else addnum(k) = addnum(k) + 1; end end end [~,predict(i)] = max(addnum);end%% show the result--testingfigure;gscatter(test_data(:,1),test_data(:,2),predict);accuracy = length(find(predict==label_test))/length(test_data);title(['predict the testing data and the accuracy is :',num2str(accuracy)]);
這還是在200個(gè)訓(xùn)練樣本下300個(gè)測試樣本的一個(gè)結(jié)果如下:
至此上述三種方法介紹完畢,上述三種方法對于監(jiān)督式的多分類問題來說確實(shí)都相當(dāng)好了。只要根據(jù)你的樣本來調(diào)節(jié)適當(dāng)參數(shù),感覺總是可以得到較好的結(jié)果的。敘述了這么多,喜歡的朋友頂一下吧~_~!同時(shí)也歡迎相互交流。
聯(lián)系客服