感知机是一个二类分类的线性分类模型。所谓二类分类就是它只能将实例分为正类和负类两个类别。那么为什么是线性分类模型呢,我的理解是感知机学习旨在求出可以将数据进行划分的分离超平面,而分离超平面的方程
圈圈表示正类,而叉叉表示负类。圈圈与叉叉之间的直线即上文所说的分离超平面(注意分离超平面并不是唯一的!)它将所有的样本划分为两部分。位于分离超平面上方的为正类,记为+1,位于分离超平面下方的为负类,记为-1。也就是说,假设给一个样本的特征向量x,如果 w⋅x+b>0 , 那么样本为正类(+1),反之若 w⋅x+b<0 , 样本则属于负类(-1)。我们 引入符号函数sign(x),即
给定一个线性可分的数据集
感知机学习算法是误分类驱动的,具体采用随机梯度下降法。首先,任意选取一个超平面 w0,b0 ,然后用梯度下降法不断地极小化损失函数。极小化过程中不是一次使M中所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。损失函数 L(w,b) 的梯度为
综上所述,得到如下算法(感知机学习算法的原始形式)
输入:训练集 T={(x1,y1),(x2,y2),...(xN,yN)} ,其中 xi∈X=Rn , yi∈Y={+1,−1} , i=1,2,3,...N ;学习率 η(0<η≤1) ;
输出: w,b ;感知机模型 f(x)=sign(w⋅x+b)
(1)选取初值 w0,b0
(2)在训练集中选取数据 (xi,yi)
(3)如果 yi(w⋅xi+b)≤0
例子:如图所示,正实例点是 x1=(3,3)T,x2=(4,3)T ,负实例点是 x3=(1,1)T ,使用感知机算法求解感知机模型 f(x)=sign(w⋅x+b)
代码如下
#include "stdafx.h"
#include
using namespace std;
const double lr = 1;
const int dim = 2;
const int n = 3;
double w[dim] = {0, 0};
double b = 0;
double samples[n][dim] = {3,3, 4,3, 1,1};
int labels[n] = {1, 1, -1};
//计算两个向量的内积
double dot(double *w, double *feature)
{
double sum = 0;
for(int i = 0; i < dim; i++)
{
sum += (*w) * (*feature);
w++;
feature++;
}
return sum;
}
//感知机算法
void perceptron()
{
while(true)
{
int i;
for(i = 0; i < n; i++)
{
if(labels[i] * (dot(w, samples[i]) + b) <= 0)
{
cout << "w:(";
for(int j = 0; j < dim; j++)
{
w[j] = w[j] + lr*labels[i]*samples[i][j];
cout << w[j];
if( j != dim-1)
cout<<",";
}
b = b + lr*labels[i];
cout << ") b:"<break;
}
}
if(i == n)
break;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
perceptron();
system("pause");
return 0;
}
下面来介绍感知机算法的对偶形式
对偶形式的基本思想是,将 w 和 b 表示为实例 xi 和 yi 的线性组合的形式,通过求解其系数而求得 w 和 b 。我们假设初始值 w0 和 b0 均为0,在原始形式中,对误分类点 (xi,yi) 通过
综上所述,我们可以得到感知机学习算法的对偶形式
输入:训练集 T={(x1,y1),(x2,y2),...(xN,yN)} ,其中 xi∈X=Rn , yi∈Y={+1,−1} , i=1,2,3,...N ;学习率 η(0<η≤1) ;
输出: α,b ;感知机模型 f(x)=sign(∑Nj=1αjyjxj⋅x+b) ,其中 α=(α1,α2,.....αN) 。
(1) α←0,b←0
(2)在训练集中选取数据 (xi,yi)
(3)如果 yi(∑Nj=1αjyjxj⋅x+b)≤0
我们依然使用上文给出的例子,利用感知机算法的对偶形式来求解,代码如下
#include "stdafx.h"
#include
using namespace std;
// 学习率
const double lr = 1;
//特征维度
const int dim = 2;
//样本个数
const int n = 3;
//样本
double samples[n][dim] = {3,3, 4,3, 1,1};
//样本标记
int labels[n] = {1, 1, -1};
//Gram矩阵
double g[n][n];
double a[n] = {0};
double b = 0;
//计算两个向量的内积
double dot(double *w, double *feature)
{
double sum = 0;
for(int i = 0; i < dim; i++)
{
sum += (*w) * (*feature);
w++;
feature++;
}
return sum;
}
//求解Gram矩阵
void getGram()
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
g[i][j] = dot(samples[i],samples[j]);
}
}
}
double dot_antithesis(int subs)
{
double sum = 0;
for(int i = 0; i < n; i++)
{
sum += a[i]*labels[i]*g[i][subs];
}
return sum;
}
//感知机算法的对偶形式
void pereption_antithesis()
{
getGram();
while(true)
{
int i;
for(i = 0; i < n; i++)
{
if(labels[i] * (dot_antithesis(i) + b) <= 0)
{
a[i] += lr;
b = b + lr*labels[i];
cout << "a: ";
for(int j = 0; j < n; j++)
cout << a[j] << " ";
cout << "b: " << b << endl;
break;
}
}
if(i == n)
break;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
pereption_antithesis();
system("pause");
return 0;
}