简明机器学习教程(一)——实践:从感知机入手

有那么一段时间不出干货了,首页都要被每周歌词霸占了,再不写一点东西都要变成咸鱼了。进入正题。本篇教程的目标很明显,就是实践。进一步的来说,就是,当你学到了一些关于机器学习的知识后,怎样通过实践以加深对内容的理解。这里,我们从李航博士的《统计学习方法》的第2章感知机来做例子,由此引出大致的学习方法。需要注意的是,这篇教程并不是来介绍感知机模型的,而是用来说明如何学习并实践一个模型的,所以对感知机的解释不会很详细。本篇教程的内容较基础,内容主要面向对机器学习有兴趣且有初步了解的人。由于本文目标人群特殊,加之作者水平实在有限,有表述不严谨或错误之处,还请各路大神多多指出。本篇需要读者的准备:matlab(测试模型用)、热爱机器学习的大脑(啊喂我的严肃气氛!)

首先:了解模型

模型类型

当我们在学习一个模型时,很重要的一点就是我们要了解这个模型的作用,以及其适用的情况。下面我们就来分析感知机:
感知机(perceptron)二类分类线性分类模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值。开篇第一句,我们就能对这个模型建立起一个大概的框架了。首先,感知机是一个二类分类模型,也就是说感知机只能分类出两个类别。其次,感知机是线性的分类模型,也就是说感知机这个模型所适用的数据必须是线性可分的。到此为止,对于感知机的适用范围,我们已经知道了不少:首先,感知机是判别模型,适用于分类问题,且可以区分的类别数为2类;其次,感知机是线性分类模型。如果你还是不理解感知机适用的问题类型,那我在这里举个例子:在二维的情况下,感知机相当于在平面上划一根线,从而把平面分成两半;在三维的情况下,感知机相当于拿一把菜刀在空间里切一刀,从而把空间分为两类。这两句话在其适用范围内,等价于下面这句话:感知机对应于输入空间(特征空间)中将实例划分为正负两类的分离超平面(三维下是“菜刀”),属于判别模型。

模型

下式即感知机模型中,将输入空间映射到输出空间的函数:

f(x)=sign(w\cdot x+b)

w就是模型的权值(weight),又称权重;b即偏置(bias)。其中的sign函数定义如下(有些地方作:sgn):

\text{ sign }(x)=\begin{cases}+1, & x\geq 0 \\ -1, & x<0 \end{cases}

需要注意的是,一般情况下,sign(0)的值是0。这里为了保证模型输出为+1或-1,故规定sign(0)=+1。根据模型,我们不难看出感知机的几何意义。线性方程 w\cdot x+b=0 就是分开空间的超平面。其中,w是平面的一个法向量(几何上),b就是其截距。

训练

损失函数

简而言之,最小化损失函数。首先,定义(经验)损失函数(详细过程请见原书2.2.2 P27):

J(w,b)=-\sum_{x_i\in M} y_i (w\cdot x_i+b)

损失函数,可以理解为是对感知机模型错误分类程度的评估函数。有了损失函数,我们就可以将训练感知机问题转化为极小化损失函数问题。

min_{w,b} J(w,b)=-\sum_{x_i\in M} y_i (w\cdot x_i+b)

普通形式——梯度下降

这里,我们采用梯度下降法(gradient descent)的变式随机梯度下降法(Stochastic Gradient Descent)进行极小化。关于两种算法的关系、优劣均不在本文讨论范围内,故省略。偏导求解梯度:

\bigtriangledown_{w} J(w,b)=-\sum_{x_i\in M} y_i x_i
\bigtriangledown_{b} J(w,b)=-\sum_{x_i\in M} y_i

算法步骤如下:

  1. 选择初始超平面S,即选择 w_0, b_0
  2. 随机选择一个误分类点 (x_i, y_i) ,更新w、b。其中,α 是每次迭代的步长,又称为学习率。
  3. 重复2,直到无分类点为止。

不难发现,若数据集是线性可分的,那么损失函数最终将会等于0。

实践

下面,我们就来用matlab来实现感知机。首先是模型预测函数predict.m(防止重复內建函数sign,作sgn):

function [ y ] = predict( x, w, b )
	 
y = sgn(x*w + b);

end

之后是损失函数costFunc.m。这里采用了向量化的方式以避免循环:

function [ J ] = costFunc( w, b, X, y )
% the cost function of perceptron model.

J = sum((X * w + b) .* y);

end

接下来就是关键部分——SGD算法训练模型了(train_SDG.m),首先初始化:

alpha = 0.5; 	 
% init w,b 	 
w = ones(n, 1);
b = 1;

然后开始迭代,首先是随机选择一个错误分类点:

% chose error points
idx = 1; 	 
while y(idx)*(X(idx,:)*w+b)<0 
 idx = unidrnd(m);
end

计算梯度,更新参数:

% calculate gradient 
grad_w = -(y(idx)*X(idx,:))';
grad_b = -y(idx);
% update
w = w + alpha .* grad_w; 
b = b + alpha * grad_b;

最后是判断是否所有点被正确归类:

if sum(abs(predict( X, w, b )-y))==0 
 break;
end

在我提供的实现中,还包含一个测试数据。运行load_data.m即可载入。然后运行train_SGD.m进行感知机的训练,程序会自动绘制每一次迭代的决策边界(即砍得那一刀的位置),在测试集运行如下:

感知机第3次迭代

感知机第3次迭代

感知机最后结果(第5次迭代)

感知机最后结果(第5次迭代)

写在后面

本篇教程中所用代码可以在此处下载:提取密码1375
如有问题,欢迎评论或发送邮件至:admin@kaaass.net。

分享到

KAAAsS

喜欢二次元的程序员,喜欢发发教程,或者偶尔开坑。(←然而并不打算填)

相关日志

  1. 没有图片
  2. 没有图片
  3. 没有图片
  4. 没有图片
  5. 没有图片

评论

  1. 开发者头条 2017.04.17 8:53上午

    感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/9vy7x1 欢迎点赞支持!
    欢迎订阅《KAAAsS的J&A教室》https://toutiao.io/subject/69380

  2. h 2017.11.30 1:41下午

    才高中就开始写程序,呵呵

  3. KAAAsS 2018.07.07 4:23下午

    更新了一处符号错误,当时太不小心了!非常抱歉!
    预计几天之内改写下文章。

  4. ZuoTiJia 2021.02.04 4:02下午

    划一刀也太形象了吧

在此评论中不能使用 HTML 标签。