1. 程式人生 > >Andrew Ng機器學習程式設計作業:Support Vector Machines

Andrew Ng機器學習程式設計作業:Support Vector Machines

作業:

machine-learning-ex6

1. 支援向量機(Support Vector Machines)

在這節,我們將使用支援向量機來處理二維資料。通過實驗將會幫助我們獲得一個直觀感受SVM是怎樣工作的。以及如何使用高斯核(Gaussian kernel )。下一節我們將使用SVM建立一個垃圾郵件分類器。

1.1 樣本資料1

以二維線性可分資料開始。下面程式碼部分將會視覺化此資料集如圖1所示。在這個資料集中,正樣本使籤為1使用+表示,負樣本標籤為0使用o表示,由一條間隙隔開。注意有一個異常正樣本,在(0.1,4.1)的位置。在這部分練習我們會看到這個異常正樣本是如何影響SVM分類器的。

 

在這一部分,我們將嘗試使用SVM的C引數的不同值。具體來說,C引數是一個正值,可以控制對錯誤分類點的懲罰力度。C越大懲罰力度越小。一個很大的C告訴SVM嘗試將所有的樣本點分類正確。C跟1/λ非常類似

,λ是之前邏輯迴歸的正則化引數。下面程式碼將會使用svmTrain.m中的入門程式碼,通過SVM包執行SVM訓練。

大多數的SVM包都會自動新增額外的特徵x0=1,

當C=100時我們可以發現,SVM將會把每一樣本分類正確。但是這樣一個決策邊界有很差的泛化性。

1.2 應用高斯核的SVM

在這一部分練習,我們將使用SVM處理非線性可分問題。

1.2.1 高斯核

來尋找SVM的非線性的決策邊界,我們首先需要實現高斯核,我們可以把高斯核作為一個相似函式函式,可以衡量兩個樣本

間的距離。高斯核函式同樣被引數σ引數化。決定相似度減少的速度。

我們需要完成 gaussianKernel.m檔案中的程式碼來完成兩個樣本的高斯核函式,高斯核函式的定義為:

完成程式碼後,執行下面程式碼提供的兩個樣本,我們應該看到結果為 0.324652.

gaussianKernel.m程式碼

sim = exp(-(x1-x2)'*(x1-x2)/2/sigma^2);

1.2.2 樣本資料2

下面部分程式碼將會載入資料並繪製資料集2,通過圖4我們可以看到有一個非線性可分的決策邊界將正負樣本分開

 

 如果我們正確完成高斯核函式,下面程式碼將會在這個資料集上執行帶高斯核的SVM,結果如圖5所示:

1.2.3 樣本資料3

在這一部分,我們將獲得更多技巧如何使用帶高斯核的SVM,下面程式碼將會載入並可視化資料集3如圖6所示

我們將使用帶高斯核的SVM處理資料集,ex6data3.mat檔案提供我們資料集,包括變數X, y, Xval, yval。作業在dataset3Params.m提供的程式碼將會幫我們選擇SVM分類器的引數,C與σ。

我們的任務是使用交叉驗證集,Xval,yval,決定最好的的引數C與σ。我們應該補充一些必要的程式碼來幫助我們選擇最合適的C和σ。比如我們嘗試每個引數遍歷8個值,我們一共有84個組合。然後我們從這些組合中選出最合適的C和σ,我們應該完成dataset3Params.m中的程式碼,找到最佳引數。

當我們實現交叉驗證集選擇最好的引數C和σ,我們應該估計交叉驗證集的錯誤率。在Matlab中我們可以使用mean(double(predictions ~= yval))來計算錯誤率。 其中predictions 時一個向量,包含所有使用SVM計算的預測值。yval是交叉驗證集的正確的標籤,我們可以使用svmPredict函式來產生交叉驗證集的的預測值。

dataset3Params.m檔案中的程式碼:

k = 1;
for i = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30]
    for j = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30]
        model = svmTrain(X, y, i, @(x1, x2)gaussianKernel(x1, x2, j));
        predictions = svmPredict(model, Xval);
        k1 = mean(double(predictions ~= yval));
        if k>k1
            C = i;
            sigma = j;
            k = k1;
        end
    end
end

2.垃圾郵件分類

徐迪郵件服務商提供郵件過濾器,可以用來將郵件分成正常郵件和垃圾郵件,而且有很好的準確率。在這一節的練習中。我們將使用SVM來建立我們自己的垃圾郵件過濾其。我們將訓練一個分類器來確定一個郵件x,

 是正常郵件(y=1)還是一個垃圾郵件(y=0)。具體來說我們應該將一封郵件轉化成特徵向量。下面的部分的練習中將會引導我們如何將一封郵件轉化成一個這樣的特徵向量。

2.1 處理郵件

一封郵件的格式如圖8所示,包含URL,郵件地址,數字和美元金額。雖然很多郵件包含類似型別的內容,比如數字,URL和郵件地址等。但是這些具體內容對一封郵件幾乎都不相同。

因而,經常採用的一種處理郵件的方法是標準化這些值,將所有的URL值,數字值,視為內容都是相同的。比如的郵件中所有的URL替換成獨特的字串“httpaddr“來表示URL是存在的。這讓垃圾郵件分類器具有基於是否存在任何URL來做出分類決定。而不是一個特定的URL存在。這通常會提升垃圾郵件分類器的形成。因為垃圾郵件提供者通常會隨機使用URL,因此在一封新的郵件中再次看到一個特定的URL的概率非常低。

在processEmail.m中,我們需要實現下面的郵件處理和規範化步驟。

 

 

 

 根據上面要求處理後的結果如圖9所示,雖然預處理留下了單詞和非單詞,但是這種形式更容易進行特徵提取。

2.11 詞彙表

在處理了這些郵件後,我們為每一封郵件列出了一些列的單詞。下一步是選擇哪個單詞在我們的分類器中可能被使用。在這節練習中我們選擇使用最經常使用的單詞作為我們考慮的單詞集合(詞彙表).因為一些單詞出現在很少一部分郵件,這會造成我們訓練集過擬合的情況。詞彙表出現在vocab.txt 檔案中,如圖10所示。我們的詞彙表的單詞是至少在垃圾郵件中出現了100此的單詞。結果是1899個單詞。在實際情況中,一般使用10000到50000個單詞。

給定詞彙表,我先現在可以將處理後的郵件的單詞對映成單詞索引列表,內容為詞彙表的單詞的索引。圖11展示了對映後的郵件。具體來說,一封簡單的郵件,單詞‘anyone'首先被被規範化成‘anyon’接著對映成詞彙表的索引86.

我們的任務是完成processEmail.m的程式碼來完成對映。在程式碼中,我們給定一個字元str表示處理過郵件中的一個單獨的單詞。我們應該尋找這個單詞是否在詞彙表中存在,如果存在,我們應該將索引新增到單詞索引變數中。如果不存在,跳過這個單詞。

我們一旦實現了processEmail.m。下面的程式碼將會執行我們完成的程式碼,應該看到結果如圖9與圖11所示。

在MATLAB中我們可以使用strcmp函式比較兩個單詞是否相同。如果相同則會返回1。在提供的程式碼中vocabList是一個cell陣列,在MATLAB中,cell陣列就與正常陣列在相同,除了元素可以是字串外。使用花括號而不是方括號標註它們。具體來說,得到第i個單詞,我們可以使用{i},我們同樣可以使用length(vocabList)來得到詞彙表單詞的數量。

processEmail.m中新增程式碼

  for i = 1:size(vocabList)
            if strcmp(str,vocabList{i})
                word_indices(m) = i;
                m = m+1;
            end
        end

 

2.2 從郵件中提取特徵

現在我們將實現特徵提取,將每一封郵件轉化為一個向量。在這部分練習中,我們將使用詞彙表中的單詞。具體來說,特徵一封郵件的的特徵對應是否詞彙表中第i個單詞出現在這封郵件中。若果等於1表示出現,反之沒有出現。

因此一封典型的郵件的特徵像下面這樣:

 

 我們現在應該完成emailFeatures.m檔案中的程式碼,來產生每一封郵件的特徵向量。完成後執行下面程式碼,我們會看到特徵向量長度為1899,有45個非0元素。

emailFeatures.m中的程式碼:

for i = 1:length(word_indices)
    x(word_indices(i)) = 1;
end

 2.3 使用SVM訓練垃圾郵件分類器

在我們完成特徵提取函式後,這一部分的程式碼將會載入處理後的訓練集來訓練SVM分類器。spamTrain.mat包括4000個垃圾郵件和正常郵件樣本。 spamTest.mat 包括1000個測試時用例,每一封原始郵件都被 processEmail 和 emailFeatures 函式處理成一個特徵向量。在載入資料集後,程式碼將訓練SVM分類器。訓練完成後,我們將看到分類器獲得訓練精度大約為99.8%。測試精度為98.5%。

% Load the Spam Email dataset
% You will have X, y in your environment
load('spamTrain.mat');
C = 0.1;
model = svmTrain(X, y, C, @linearKernel);

p = svmPredict(model, X);
fprintf('Training Accuracy: %f\n', mean(double(p == y)) * 100);

% Load the test dataset
% You will have Xtest, ytest in your environment
load('spamTest.mat');

p = svmPredict(model, Xtest);
fprintf('Test Accuracy: %f\n', mean(double(p == ytest)) * 100);

2.4 垃圾郵件的主要預測因子

為了更好理解垃圾郵件分類器是如何工作的,我們可以檢查引數,看分類器認為哪個單詞最可能是垃圾郵件。下面程式碼將會找到分類器中有最大正值的引數並顯示相應的單詞,如圖12所示。因此如果一封郵件包含單詞比如 'guarantee', 'remove', 'dollar'和 'price'等,它很可能被分類成垃圾郵件。

% Sort the weights and obtin the vocabulary list
[weight, idx] = sort(model.w, 'descend');
vocabList = getVocabList();
for i = 1:15
    if i == 1
        fprintf('Top predictors of spam: \n');
    end
    fprintf('%-15s (%f) \n', vocabList{idx(i)}, weight(i));
end

結果:

2.5 使用自己的郵件

2.6 建立自己的資料集

這次練習作業為我們提供了,訓練集與測試集。使用了processEmail.m 和 emailFeatures.m兩個函式。現在我們可以使用 SpamAssassin Public Corpus.中的資料集來訓練分類器。我們還可以建立自己詞彙表(選擇最長出現的單詞),還可以使用高度優化的SVM工具比如LIBSVM或者 SVM functionality

processEmail.m and emailFeatures.m