Ex6 支持向量机SVM

Ex6 支持向量机SVM

​ 在本练习的上半部分,将使用支持向量机(SVM)与各种示例2维数据集。 实验这些数据集将有助于直观了解SVM工作的过程,以及如何使用高斯内核与SVM。 在下一个练习中,将使用支持向量机来构建垃圾邮件分类器。

​ 如下图所示的2维数据集,可以通过线性边界分离,图中正样本通过“+”描述,负样本通过“o”描述,之间有很大的间隙(gap),但需注意在点(0.1,4.1)处有一个正样本点"+"。

![1.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fhgawxp96gj20aj07rmx6.jpg)
​ 在本部分练习中,您将尝试使用SVM的C参数的不同值。通俗来讲,参数C是一个正值,用于控制误差分类训练样本的惩罚权重。 一个大的C参数告诉SVM必须正确地分类所有的例子。 C起到类似于1 / lambda的作用,其中lambda是我们之前用于逻辑回归的正则化参数。

​ 下图对应于参数C=1下的决策边界:

![2.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fhgawy3c54j20ae07v3yl.jpg)
​ 下图对应于参数C=100下的决策边界:
![3.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fhgawy8menj20ab07vjrg.jpg)
​ 一般的,大多数SVM软件包(包括svmTrain.m)自动会添加额外的特征x0 = 1,并会自动注意学习截距项的$\theta_0$。 所以当将训练数据传递给SVM软件时,不需要自己添加这个额外的特征x0 = 1。该部分练习代码如下:
% Load from ex6data1: 
% You will have X, y in your environment
load('ex6data1.mat');

C = 1;
model = svmTrain(X, y, C, @linearKernel, 1e-3, 20);
visualizeBoundaryLinear(X, y, model);
%svmTrain函数为简化版本的训练SVMs算法,建议使用LIBSVM或SVMLight包

高斯核(SVM with Gaussian Kernels)

​ 对于找寻SVM的非线性决策边界,我们需要利用高斯核函数,可以认为高斯核为一种相似度函数用来度量一对样本的距离“distance”(\(x^{(i)},y^{(i)}\))。带宽参数\(\delta\)也可以对高斯内核进行参数化,该参数决定了相似性度量下降到多快(至0),因为示例进一步分开。

​ 首先需要完成函数gussianKernel.m以计算在两个样本(\(x^{(i)},y^{(i)}\))间的高斯核。高斯核函数定义如下:
4.jpg

代码如下:

function sim = gaussianKernel(x1, x2, sigma)
%RBFKERNEL returns a radial basis function kernel between x1 and x2
%   sim = gaussianKernel(x1, x2) returns a gaussian kernel between x1 and x2
%   and returns the value in sim

% Ensure that x1 and x2 are column vectors
x1 = x1(:); x2 = x2(:);

% You need to return the following variables correctly.
sim = 0;

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

​ 将datase2中的样本绘出如下:可以看出,一般的线性决策分界线无法将正负样本分离,但是通过高斯核的SVM可以很好的将数据集分开。

![5.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fhgawz5ih4j20af07qwfs.jpg)
~~~matlab %% ========== Part 5: Training SVM with RBF Kernel (Dataset 2) ========== % After you have implemented the kernel, we can now use it to train the % SVM classifier. % load('ex6data2.mat');

% SVM Parameters
C = 1; sigma = 0.1;

% We set the tolerance and max_passes lower here so that the code will run
% faster. However, in practice, you will want to run the training to
% convergence.
model= svmTrain(X, y, C, @(x1, x2) gaussianKernel(x1, x2, sigma));
visualizeBoundary(X, y, model);



<div align=center>
![6.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fhgawzhn1hj20ab07owfx.jpg)
</div>
​	在提供的数据集ex6data3.mat中,给出变量X,y,Xval,yval。 ex6.m中提供的代码使用从dataset3Params.m加载的参数,使用训练集(X,Y)来训练SVM分类器。
​	该部分应使用交叉验证集Xval,yval来确定要使用的最佳C和$\delta$参数。建议在乘法步骤中尝试值(例如,0.01,0.03,0.1,0.3,1,3,10,30)。请注意,应尝试C和的所有可能的$\delta$值对(例如,C = 0.3和 $\delta$ =0.1)。 例如,尝试上面列出的8个C中的每个值为C和 $\delta^2$,将最终训练和评估(交叉验证集),总共8*8= 64个不同的模型。
<div align=center>
![7.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fhgawzw520j20al07vjrs.jpg)
</div>
~~~matlab
function [C, sigma] = dataset3Params(X, y, Xval, yval)

C = 0.01;
sigma = 0.01;

% ====================== YOUR CODE HERE ======================
% Instructions: Fill in this function to return the optimal C and sigma
%               learning parameters found using the cross validation set.
%               You can use svmPredict to predict the labels on the cross
%               validation set. For example, 
%                   predictions = svmPredict(model, Xval);
%               will return the predictions on the cross validation set.
%
%  Note: You can compute the prediction error using 
%        mean(double(predictions ~= yval))
%
temp_C = 0;
temp_sigma = 0;
temp_error = 0;
try_value = [0.01,0.03,0.1,0.3,1,3,10,30];

for i = 1:8
    for j = 1:8
        temp_C = try_value(i);
        temp_sigma = try_value(j);
        model= svmTrain(X, y, temp_C, @(x1, x2) gaussianKernel(x1, x2, temp_sigma));
        predictions = svmPredict(model, Xval);
        error = mean(double(predictions ~= yval));
        if((i==1) && (j==1))
            temp_error = error;
        else if(temp_error > error)
                temp_error = error;
                C = temp_C;
                sigma = temp_sigma;
            end
        end
    end
end

end

最佳参数选取结果为:C=0.3和 \(\delta\) =0.1,决策边界如下所示:

![8.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fhgax09q1rj20ai07odga.jpg)

垃圾邮件分类器(Spam classification)

​ 如今的许多邮箱服务器都提供了垃圾邮件过滤器,足以较高的精确度区分垃圾与非垃圾邮件,此部分联系在于使用SVMs建立自己的垃圾邮件过滤器。定义一封给定的信件email,是垃圾邮件(y=1)否则(y=0)。特殊地,需要将每一封email转换为特征向量,接下来的练习为此目的,可以使用邮件的主题以及标题(body&headers)。

convert email into a feature

![9.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fhgax0xov4j20en04kaaj.jpg)
​ 在开始机器学习任务之前,可以先从数据集中查看示例。 上图显示了一个包含链接--URL,电子邮件地址(最后),数字和美元金额的电子邮件示例。 虽然许多电子邮件将包含相似类型的实体(例如,数字,其他URL或其他电子邮件地址),但特定实体(例如,特定URL或特定的金额)将几乎在每个电子邮件中都是不同的。 因此,处理电子邮件时常常使用的一种方法是“规范化”这些值,以便所有URL被视为相同,所有数字都被视为相同等。例如,我们可以用唯一的字符串替换电子邮件中的每个URL “httpaddr”表示URL存在。这有利于使垃圾邮件分类器基于是否存在URL而不是特定URL是否存在的分类的方式判断,这通常会提高垃圾邮件分类器的性能,因为垃圾邮件发送者通常随机将URL随机化,因此在新的垃圾邮件中再次查看任何特定URL的可能性非常小。

​ 在处理邮件的函数processEmail.m中,我们提前做了以下预处理和标准化步骤:

  • 小写转换 -- 将整个邮件转化为小写字母,忽略大写的差异(如:IndIcaTE与Indicate效果相同)
  • 跳过网页 -- 将所有的HTML去除
  • 标准化链接 -- 以“httpaddr”替代所有的URLs
  • 标准化邮箱地址 -- 以“emailaddr”替代所有的地址
  • 标准化数字 -- 将所有的数字以“number‘替代
  • 标准化美元 -- 将所有的$美元标志以”dollar“代替
  • 单词缩写 -- 将所有同一单词进行变种的单词,以源词进行替换,如:discount、discounts、discounting均以discount替代
  • 去除非字(non-words) --非字和标点符号已被删除。 所有空格(标签,换行符,空格)都已被修剪为单个空格。

按照以上处理步骤进行后的结果如下图所示:

![10.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fhgax1eh25j20ej02oglu.jpg)
**词汇表**

​ 预处理电子邮件后,我们会为每封电子邮件列出单词。 下一步是选择我们要在我们的分类器中使用哪些词和我们想要忽略哪些词。 对于这个练习,我们只选择最频繁出现的单词作为我们考虑的单词集(词汇表)。 由于在训练集中很少发生的单词只能在少数电子邮件中发生,所以可能会导致模型超出我们的训练集。 完整的词汇列表在文件vocab.txt中,一并在下图右侧中显示。我们的词汇列表是通过选择在垃圾邮件语料库中至少发生100次的所有单词进行选择的,从而导致1899个单词的列表。 在实践中,常常使用大约10,000到50,000个单词的词汇表。

​ 给定词汇列表,我们现在可以将预处理的电子邮件上图中的每个单词映射到包含词汇表中单词的索引的单词索引列表。下下图显示了示例电子邮件的映射。 具体来讲,在示例电子邮件中,“任何人”一词首先被标准化为“anyon”,然后映射到词汇列表中的索引86上。

![11.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fhgax21gjhj20be06aaae.jpg)
​ 在代码processEmail.m中将获得一个字符串str,它是处理后的电子邮件中的一个单词。在词汇列表中通过查找词汇表,并查找单词是否存在于词汇列表中。 如果单词存在,您应该将单词的索引添加到索引变量中。 如果这个词不存在,因此不在词汇表中,跳过这个词。提示:可以使用Octave/MATLAB软件中的strcmp函数对两个字符串进行比较,二者相同的情况下返回1。代码如下:
function word_indices = processEmail(email_contents)

% Load Vocabulary
vocabList = getVocabList();

% Init return value
word_indices = [];

% Find the Headers ( \n\n and remove )
% Uncomment the following lines if you are working with raw emails with the
% full headers

% hdrstart = strfind(email_contents, ([char(10) char(10)]));
% email_contents = email_contents(hdrstart(1):end);

% Lower case--任何大写字符转换为相应的小写字符并保持所有其他字符不变而形成的字符串
email_contents = lower(email_contents);

% Strip all HTML---将 str 中与 expression 匹配的文本替换为 replace 描述的文本
% Looks for any expression that starts with < and ends with > and replace
% and does not have any < or > in the tag it with a space
email_contents = regexprep(email_contents, '<[^<>]+>', ' ');

% Handle Numbers
% Look for one or more characters between 0-9
email_contents = regexprep(email_contents, '[0-9]+', 'number');

% Handle URLS
% Look for strings starting with http:// or https://
email_contents = regexprep(email_contents, ...
                           '(http|https)://[^\s]*', 'httpaddr');

% Handle Email Addresses
% Look for strings with @ in the middle
email_contents = regexprep(email_contents, '[^\s]+@[^\s]+', 'emailaddr');

% Handle $ sign
email_contents = regexprep(email_contents, '[$]+', 'dollar');


% ========================== Tokenize Email ===========================

% Output the email to screen as well
fprintf('\n==== Processed Email ====\n\n');

% Process file
l = 0;

while ~isempty(email_contents)

    % Tokenize and also get rid of any punctuation
    [str, email_contents] = ...
       strtok(email_contents, ...
              [' @$/#.-:&*+=[]?!(){},''">_<;%' char(10) char(13)]);
   
    % Remove any non alphanumeric characters
    str = regexprep(str, '[^a-zA-Z0-9]', '');

    % Stem the word 
    % (the porterStemmer sometimes has issues, so we use a try catch block)
    try str = porterStemmer(strtrim(str)); 
    catch str = ''; continue;
    end;

    % Skip the word if it is too short
    if length(str) < 1
       continue;
    end
    
    % Look up the word in the dictionary and add to word_indices if
    % found
    % ====================== YOUR CODE HERE ======================
	temp = length(vocabList);  
    for i = 1:temp
        if(strcmp(str,vocabList(i)))
            word_indices = [word_indices;i];
        else 
            continue;
        end
    end
    % =============================================================
    
    % Print to screen, ensuring that the output lines are not too long
    if (l + length(str) + 1) > 78
        fprintf('\n');
        l = 0;
    end
    fprintf('%s ', str);
    l = l + length(str) + 1;

end

% Print footer
fprintf('\n\n=========================\n');

end

从邮件中找寻特征

​ 您现在将实现将每个电子邮件转换为\(\mathbb{R}^n\)中的向量的特征提取。 对于这个练习,您将在词汇列表中使用n =#个词。 具体来说,电子邮件的特征\(x_i\in\) {0,1}对应于电子邮件中是否存在字典中的第i个词。 也就是说,如果第i个单词在电子邮件中,则x_i = 1,如果电子邮件中不存在第i个字,则x_i = 0。基本如下:12.jpg

​ 通过完成emailFeatures.m中的代码以产生特征向量(feature vector)。

代码如下:

function x = emailFeatures(word_indices)
%EMAILFEATURES takes in a word_indices vector and produces a feature vector
%from the word indices
%   x = EMAILFEATURES(word_indices) takes in a word_indices vector and 
%   produces a feature vector from the word indices. 

% Total number of words in the dictionary
n = 1899;

% You need to return the following variables correctly.
x = zeros(n, 1);

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

end

测试垃圾邮件分类器

​ 完成特征提取功能后,ex6 spam.m的下一步将加载将用于训练SVM分类器的预处理训练数据集。 spamTrain.mat包含4000个垃圾邮件和非垃圾邮件的培训示例,而spamTest.mat包含1000个测试示例。 每个原始电子邮件都使用processEmail和emailFeatures函数进行处理,并转换为向量\(x^{(i)} \in \mathbb{R}^{1899}\)。训练精度为99.8%,测试精度为98.5%。

posted @ 2017-07-11 22:19  SrtFrmGNU  阅读(966)  评论(0编辑  收藏  举报