文章的开头我们先思考一个问题,对于给定的如下的两组数据,如何找到一条线将这两组数据一分为二?
负类数据:(1,2),(1.5,1.5),(2,1),(2.5,1),(3,0.5)
正类数据:(1,4),(1.5, 3),(2, 3),(2.5,2.5),(3,2)
感知机(perceptron)实质上解决的就是上面这种问题,找到一个超平面将两组数据一分为二。举个例子,我们如何训练出一个分类器来识别一封邮件是不是垃圾邮件呢。首先,我们需要两组标记好的邮件数据,一组是垃圾邮件,另外一组不是。然后我们可以将每一封邮件都抽象为一个坐标点(不一定是二维的),然后邮件分类问题就抓换为上述这种求解超平面的问题了。
废话不多说,直接讲解如何求解超平面。
感知器是一个线性分类模型,因此超平面也是线性超平面,不妨设超平面的方程为;其中
是向量(
, ...,
),
为向量(
, ... ,
),向量的维度为实例的特征个数。求解超平面方程其实就是求解参数
和b。
考虑一个具有两个特征的数据集,这时和
就是一个二维向量,超平面的方程可以写成
;或者是我们更熟悉的形式
,即直线方程。
我们如何求这样一条直线方程呢?首先我们会参数和b随机指定一个初始值,然后通过不断对其调整来求得最终的参数值。这个调整的过程就是机器学习的过程。那么我们如何调整参数的值呢(即确定一个学习策略,定义损失函数)?考虑初始情况,由于参数的值是随机指定的,那么肯定会有很多个数据点被误分类,我们希望每一次参数调整之后这个误分类的点的个数会越来越少,换言之我们希望误分类的点到超平面的距离的总和S越来越小。
这里直接给出点到超平面的距离公式:
,其中
可以理解为向量
的长度。
接下来我们把距离公式中的的绝对值符号去掉。
我们知道,训练数据集中的每一个数据点都会对应于一个
代表数据的类别,如果
是正类数据那么
的值就为1否则就为-1。
考虑误分类的两种情况:
1.如果被超平面错误的分为正类,那么
一定大于0(这点如果不明白可以复习一下解析几何)
由于本身是负类
= -1;所以
2.如果被超平面错误的分为负类,那么
一定小于0,由于
本身是正类
= 1;
所以
综上两种情况可得:
因此等价于
,接下来我们进一步对其进行优化。
令,可得
至此我们就可以写出误分类的点到超平面的距离的总和S的表达式了,假设被误分类的数据集为M
可以看出S是关于
和b的函数。
至此,我们就将一个二分类问题转化成了一个求函数极小值的问题了。求极小值我们选择随机梯度下降法可得
和b的更新方程为:
,
其中
为学习率。
至此感知机的原理部分讲解完毕,下面直接上代码。
bool Perceptron::update_wb()
{
bool retval = true;
int data_num;
data_num = this->x.size();
for (int i = 0; i < data_num; i++)
{
if (-y[i] * (this->w * this->x[i] + this->b) > 0)
{
retval = false;
//η取1
this->w = this->w + y[i]*this->x[i];
this->b = this->b + y[i];
}
}
return retval;
}
bool Perceptron::train(string file_name)
{
bool is_over = false;
if (!this->read_data(file_name))
{
return false;
}
//初始化w,b
this->w.resize(this->n, 1);
this->b = 1;
// 随机选取被误分类的点,进行更新w和b, 直到没有被误分类的
while (!is_over)
{
is_over = update_wb();
}
}