感知机

概述

感知机在1957年由Rosenblatt提出,是神经网络和支持向量机的基础,它是一个二类分类的线性分类模型,属于判别模型。感知机学习旨在求出将训练数据进行线性划分的分离超平面,为此,导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化,求得感知机模型。

感知机模型

1、定义

假设输入空间(特征空间)是x∈Rn,输出空间是 Y={+1,-1}.输入x属于X表示实例的特征向量,对应于输入空间(特征空间)的点;输出y属于 Y表示实例的类别。由输入空间到输出空间的如下函数:

f (x)=sign(w*x+b)

其中,w和b为感知机模型参数,w叫作权值(weight)或权值向量(weightvectot)   b叫作偏置(bias).  w⋅x表示w和x的内积。感知机学习的目的就在于确定参数w和b的值。

感知机是一种线性分类模型,属于判别模型。感知机模型的假设空间是定义在特征空间中的所有线性分类模型(linear classification model)或线性分类器(linear classifier)。

2、感知机模型几何解释

如下图所示,w*x+b表示一个超平面: 

感知机学习策略

1、数据集的线性可分性

如果存在某个超平面S: w*x+b=0能够将数据集的正实例点和负实例点完全正确地划分到超平面的两侧,则称数据集为线性可分数据集(linearly aeparahle data sec);否则,称数据集线性不可分。

2、感知机学习策略

为了找出这样的超平面,即确定感知机模型参数w,b。需要确定一个学习策略,即定义(经验)损失函数并将损失函数极小化。

损失函数不易采用误分类点的总数,这是因为这样的损失函数不是参数w,b的连续可导函数,不易优化。

损失函数另一种选择是误分类点到超平面的距离,是感知机所采用的。

首先写出输入空间Rn中任一点x0到分离超平面的距离

这里‖w‖ 是w的L2范数。
其次对于误分类的数据(xi,yi)来说,

因为当w⋅xi+b>0,yi=−1,而当w⋅xi+b<0,yi=+1。因此误分类点xi到超平面的距离是

这样假设误分类点的集合为M,那么所有误分类点到超平面的总距离为

不考虑1/‖w‖,就得到感知机学习的损失函数

显然,损失函数L(w,b)是非负的。如果没有误分类点,损失函数值为0,而且,误分类点越少,误分类点离超平面越近,损失函数的值越小。

感知机学习的策略是在假设空间中选取使损失函数最小的模型参数w,b。

感知机学习算法

1、学习算法的原始形式

感知机学习算法是误分类驱动的,具体采用随机梯度下降法。

首先,任意选取一个超平面w0,b0,然后用梯度下降法不断地极小化损失函数。极小化过程中不是一次使M中所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。损失函数L(w,b)的梯度为

随机选取一个误分类点(xi,yi),对w,b进行更新:

式中η(0<η1)是步长,在统计学习中又称为学习率。

综上所述,得到如下算法(感知机学习算法的原始形式) 

 

2、学习算法的对偶形式

对偶形式的基本思想是:

将w和b表示为实例xi和yi的线性组合的形式,通过求解其系数而求得w和b。我们假设初始值w0和b0均为0,在原始形式中,对误分类点(xi,yi)通过

逐步修改w和b。假设我们现在运行感知机算法的原始形式求得w和b,当算法结束,在样本点(xi,yi)处总共对w和b修改了ni次,则w和b关于(xi,yi)的增量分别是αi⋅yi⋅xi和αi⋅yi,这里αi=ni⋅η。这样最后学到的w和b可以分别表示为

这里αi≥0,i=1,2,3,....N,当η=1时,αi表示第i个实例点由于误分而进行更新的次数。

综上所述,我们可以得到感知机学习算法的对偶形式

 

对偶形式中训练实例仅以内积的形式出现。可以预先将训练集中实例间的内积计算出来并以矩阵的形式存储,这个矩阵就是所谓的Gram矩阵:G = [xi⋅yi]NxN

例子

如图所示,正实例点是,负实例点是,使用感知机算法求解感知机模型f(x)=sign(w⋅x+b)

#include "stdafx.h"
#include <iostream>

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:"<<b << endl;
                break;
            }
        }
        if(i == n)
            break;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    perceptron();
    system("pause");
    return 0;
}

 

我们依然使用上文给出的例子,利用感知机算法的对偶形式来求解,代码如下

#include "stdafx.h"
#include <iostream>

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;
}

 

参考博客:https://blog.csdn.net/u013358387/article/details/53303932

posted @ 2018-12-15 11:08  深夜十二点三十三  阅读(376)  评论(0编辑  收藏  举报