机器学习(20) SVM 示例1:【Octave】解决二分类(线性SVM)

1 训练集ex6data1.mat可视化

绘制训练集函数plotData.m

function plotData(X, y)

  % plotData(X, y)绘制数据,

  % 输入参数:   X 输入特性矩阵,行数为样本数,列数为2,每一行是一个二维点

  %             y 输出特性向量,行数为样本数,列数为1,每个元素值为0或1.

  % 输出结果:  样本的输出特性向量为1的打上‘+’标记,为0的打上‘o'标记

  %          

  % Create New Figure

  figure; hold on;

  % Find Indices of Positive and Negative Examples

  pos = find(y == 1); neg = find(y == 0);

  % Plot Examples

  plot(X(pos, 1), X(pos, 2), 'k+','LineWidth', 1, 'MarkerSize', 5);

  plot(X(neg, 1), X(neg, 2), 'ko', 'MarkerFaceColor', 'y','MarkerSize', 5);

  hold off;

endfunction

训练集可视化脚本LinearSVM.m第一部分代码

%% Initialization

clear ; close all; clc

%% =============== Part 1: 加载并可视化数据 ================

fprintf('Loading and Visualizing Data ...\n')

% 从文件 ex6data1.mat中加载,会发现环境中有X,y变量值:

load('ex6data1.mat');

% 绘制训练集数据

plotData(X, y);

fprintf('Program paused. Press enter to continue.\n');

pause;

执行结果

可以看到这里的数据集分两类,其中存在一个异常点(0.1,4.1),因此这个偏离点将影响SVM的决策边界。 

2 SVM的线性核linearKernel.m

function sim = linearKernel(x1, x2)

  % linearKernel(x1, x2)返回一个在x1和x2之间的线性核,

  % 输入参数:   x1,x2 列向量。确保x1和x2是维度一致的列向量

  % 返 回 值:  sim 列向量x1、x2的点积

  %          

  x1 = x1(:); x2 = x2(:);

  % Compute the kernel

  sim = x1' * x2;  % dot product

end

3  训练函数svmTrain.m

function [model] = svmTrain(X, Y, C, kernelFunction, ...

                            tol, max_passes)

  % svmTrain使用SMO算法的简化版本训练SVM分类器。

  % 输入: X   训练矩阵,行数为样本数,列数为输入特征数

  %       y   训练集输出特征向量,是一个包含1和0的列向量,行数为样本数,列数为1

  %       C   标准的SVM正则化的参数。

  %       kernelFunction  训练用的核函数

  %       tol 是一个公差值,用于确定相等浮点数。

  %       max_passes 控制算法退出之前对数据集的迭代次数(不改变alpha值)。

  %返回值:model 训练模型

  %

  %注:这是用于训练支持向量机的SMO算法的简化版本。

  %   在实践中,如果你想训练一个SVM分类器,我们建议使用一个优化的包,例如:

  %       LIBSVM (http://www.csie.ntu.edu.tw/ ~ cjlin / LIBSVM /)

  %       SVMLight (http://svmlight.joachims.org/)

  %设置默认的tol值和max_passes

  if ~exist('tol', 'var') || isempty(tol)

    tol = 1e-3;

  endif

  if ~exist('max_passes', 'var') || isempty(max_passes)

    max_passes = 5;

  endif

  % 训练集的参数

  m = size(X, 1); % 样本数

  n = size(X, 2); % 输入特征数

 

  % 将输出特征向量中的值为0的元素替换成-1

  Y(Y==0) = -1;

  % 初始化训练模型中的变量

  alphas = zeros(m, 1);

  b = 0;

  E = zeros(m, 1);

  passes = 0;

  eta = 0;

  L = 0;

  H = 0;

  % 预计算内核矩阵,因为我们的数据集很小

  % (在实践中,使用优化的SVM包,能优雅地处理大数据集,而不是用这个代码)

  % 我们在这里实现了优化的向量化kernel版本,使svm训练运行得更快。

  if strcmp(func2str(kernelFunction), 'linearKernel')

    % 线性核的矢量化计算。

    % 这相当于在每一对例子上计算内核

    K = X*X';

  elseif strfind(func2str(kernelFunction), 'gaussianKernel')

    % 矢量化RBF核

    % 这相当于在每一对例子上计算内核

    X2 = sum(X.^2, 2);

    K = bsxfun(@plus, X2, bsxfun(@plus, X2', - 2 * (X * X')));

    K = kernelFunction(1, 0) .^ K;

  else

    % 预计算核矩阵

    % 由于缺乏矢量化,下面的操作可能会很慢

    K = zeros(m);

    for i = 1:m

        for j = i:m

             K(i,j) = kernelFunction(X(i,:)', X(j,:)');

             K(j,i) = K(i,j); %the matrix is symmetric

        endfor

    endfor

  endif

  % 训练

  fprintf('\nTraining ...');

  dots = 12;

  while passes < max_passes,

    num_changed_alphas = 0;

    for i = 1:m,

      % 使用(2)计算Ei = f(x(i)) - y(i)。.

      % E(i)=b +sum (X(i, :)*(repmat(alphas.*Y,1,n).*X)')- Y(i);

      E(i) = b + sum (alphas.*Y.*K(:,i)) - Y(i);

      if ((Y(i)*E(i)<-tol && alphas(i)< C) || (Y(i)*E(i)>tol && alphas(i)>0)),

        %在实践中,可以使用许多启发式方法来选择i和j。

        % 在这个简化的代码中,我们随机选择它们。

        j = ceil(m * rand());

        while j == i,  % Make sure i \neq j

          j = ceil(m * rand());

        endwhile

        % 使用(2)计算Ej = f(x(j)) - y(j)。

        E(j) = b + sum (alphas.*Y.*K(:,j)) - Y(j);

        % 保存旧alphas

        alpha_i_old = alphas(i);

        alpha_j_old = alphas(j);

        % 通过(10)或(11)计算L和H。

        if (Y(i) == Y(j)),

          L = max(0, alphas(j) + alphas(i) - C);

          H = min(C, alphas(j) + alphas(i));

        else

          L = max(0, alphas(j) - alphas(i));

          H = min(C, C + alphas(j) - alphas(i));

        endif

        if (L == H),

          % continue to next i.

          continue;

        endif

        % 通过(14)计算eta。

        eta = 2 * K(i,j) - K(i,i) - K(j,j);

        if (eta >= 0),

          % continue to next i.

          continue;

        endif

        % 使用(12)和(15)计算并剪辑alphas的新值。

        alphas(j) = alphas(j) - (Y(j) * (E(i) - E(j))) / eta;

        % 剪辑

        alphas(j) = min (H, alphas(j));

        alphas(j) = max (L, alphas(j));

           

        % 检查alphas的变化是否显著

        if (abs(alphas(j) - alpha_j_old) < tol),

          % continue to next i.

          % replace anyway

          alphas(j) = alpha_j_old;

          continue;

        endif

           

        % 使用(16)确定alpha i的值。

        alphas(i) = alphas(i) + Y(i)*Y(j)*(alpha_j_old - alphas(j));

           

        % 分别使用(17)和式(18)计算b1和b2。

        b1 = b - E(i) ...

               - Y(i) * (alphas(i) - alpha_i_old) *  K(i,j)' ...

               - Y(j) * (alphas(j) - alpha_j_old) *  K(i,j)';

        b2 = b - E(j) ...

               - Y(i) * (alphas(i) - alpha_i_old) *  K(i,j)' ...

               - Y(j) * (alphas(j) - alpha_j_old) *  K(j,j)';

        % 用(19)计算b。

        if (0 < alphas(i) && alphas(i) < C),

          b = b1;

        elseif (0 < alphas(j) && alphas(j) < C),

          b = b2;

        else

          b = (b1+b2)/2;

        endif

        num_changed_alphas = num_changed_alphas + 1;

      endif

    endfor

 

    if (num_changed_alphas == 0),

      passes = passes + 1;

    else

      passes = 0;

    endif

    fprintf('.');

    dots = dots + 1;

    if dots > 78

      dots = 0;

      fprintf('\n');

    endif

    if exist('OCTAVE_VERSION')

      fflush(stdout);

    endif

  endwhile

  fprintf(' Done! \n\n');

  % Save the model

  idx = alphas > 0;

  model.X= X(idx,:);

  model.y= Y(idx);

  model.kernelFunction = kernelFunction;

  model.b= b;

  model.alphas= alphas(idx);

  model.w = ((alphas.*Y)'*X)';

endfunction

4 可视化线性SVM决策边界函数visualizeBoundaryLinear.m

function visualizeBoundaryLinear(X, y, model)

  % visualizeBoundaryLinear绘制支持向量机学习到的线性决策边界

  % 输入: X 训练矩阵,行数为样本数,列数为输入特征数

  %       y 训练集输出特征向量,是一个包含1和0的列向量,行数为样本数,列数为1

  %       model  由svmTrain函数得出的模型

  w = model.w;

  b = model.b;

  xp = linspace(min(X(:,1)), max(X(:,1)), 100);

  yp = - (w(1)*xp + b)/w(2);

  plotData(X, y);

  hold on;

  plot(xp, yp, '-b');

  hold off

endfunction

5 线性SVM脚本LinearSVM.m第二部分代码

%% ==================== Part 2: 线性SVM训练 ====================

%  下面的代码将在数据集上训练一个线性支持向量机,并绘制学习到的决策边界

% 从文件 ex6data1.mat中加载,会发现环境中有X,y变量值: 

load('ex6data1.mat');

fprintf('\nTraining Linear SVM ...\n')

% 你应该尝试改变下面的C值,看看决策边界是如何变化的(例如,尝试C = 1000)

C = 1;

model = svmTrain(X, y, C, @linearKernel, 1e-3, 20);

visualizeBoundaryLinear(X, y, model);

fprintf('Program paused. Press enter to continue.\n');

pause;

load('ex6data1.mat');

fprintf('\nTraining Linear SVM ...\n')

% 你应该尝试改变下面的C值,看看决策边界是如何变化的(例如,尝试C = 1000)

C = 100;

model = svmTrain(X, y, C, @linearKernel, 1e-3, 20);

visualizeBoundaryLinear(X, y, model);

6 执行LinearSVM.m脚本结果

在这里,脚本先加载C=1时的分类情况,可以看到偏离点位于决策边界下方,效果不好(下图左边),在代码中更改C=100后,输出为下图右边。C=100相当于减小正则项系数λ,增加了代价函数中前半部分特征x的权重,因此可以看到偏离点已经在边界以上。

在左图中,C = 1,SVM将决策边界放到了两个数据的间隔之间,并且将左边较远位置的数据点错误的划分了。

在右图中,C = 100,SVM现在将所有样本正确的分类,但是这个决策边界并不能自然的拟合数据。

猜你喜欢

转载自blog.csdn.net/luyouqi11/article/details/132173238