感知机介绍及MATLAB实现


前言

  本文介绍了感知机的相关内容,并进行了代码实现。了解感知机的原理,并学会对其进行编程,对后续学习神经网络有很大帮助,本文的完整代码请见附录。

操作环境
  MATLAB 2020a


1 感知机简介

  感知机(Perceptron)是线性二分类模型,由Rosenblatt于1957年提出,感知机是学习神经网络和支持向量机的基础1。要想利用感知机对数据进行分类,我们首先需要用一些数据让感知机进行训练学习,训练完毕后,就可以用感知机进行分类了。如图1所示,以二维平面为例,感知机的学习可以看做是寻找一条直线的过程。感知机将位于直线一侧的数据视为正类,并用“1”进行标识,另一侧的数据视为负类,用“-1”标识。
在这里插入图片描述

图1 感知机分类示意图

2 感知机结构

  感知机由两层神经元组成,如图2所示,其中输入层用于接收外界输入信号,然后将信号线性变换后传递给输出层,输出层对信号进行激活处理,然后输出结果。需注意,输入层神经元仅接受输入,不对信号进行处理,输出层神经元是具有激活函数的功能神经元,会对信号进行加工2

在这里插入图片描述

图2 感知机网络结构示意图

3 感知机学习过程

  一般情况下,感知机需要很多轮训练学习才能用于分类,具体学习多少轮根据情况进行设定。如果训练数据有P组,则每轮需要进行P次训练学习,用于训练的数据是已知分类的数据,也就是带有标记的数据,一般称为样例,不带标记的数据称为样本。表1中展示了四组样例,前两组是正类,后两组是负类。感知机的输入样本为二维,即每个样本都有两个数据值,每次输入一个样本都会得出一个结果,根据样本对应的标记可以判断训练结果是否正确。

表1 训练样例
第一组第二组第三组第四组
输入样本 5.06.21.21.9
5.85.52.81.7
标记11-1-1

  感知机每次训练学习都会经过数据传播过程,数据传播过程包括两个步骤:线性转换激活处理。输入信号通过带权重的连接进行传递即是线性转换,然后在输出层对线性转换结果进行激活处理,通过激活转换后的结果为最终训练结果值。我们将训练结果与真实值进行对比,如果是错误的分类结果,则对参数(参数包括权值和阈值)进行更新

3.1 数据传播过程

  感知机每次学习都需要一组数据,这里的一组指的是一个数据点,比如我们给感知机输入一个样本点 ( x 1 , x 2 ) \left( {x_1, x_2} \right) (x1,x2),下面分析这个样本点的传播过程。

第一步:线性转换
n e t o = ω 1 x 1 + ω 2 x 2 + b = ∑ i = 1 2 ω i x i + b (1) ne{t_o}{\rm{ = }}{\omega _1}{x_1} + {\omega _2}{x_2} + b{\rm{ = }}\sum\limits_{i = 1}^2 {{\omega _i}{x_i}} + b \tag {1} neto=ω1x1+ω2x2+b=i=12ωixi+b(1)

  其中, ω i ω_i ωi和b分别是感知机的权值和偏置, n e t o net_o neto是线性转换结果。可以将公式(1)写成矩阵形式:

n e t o = ( ω 1 ω 2 ) ( x 1 x 2 ) + b = ( ω 1 ω 2 b ) ( x 1 x 2 1 ) (2) net_o=\left( \begin{matrix} \omega _1& \omega _2\\ \end{matrix} \right) \left( \begin{array}{c} x_1\\ x_2\\ \end{array} \right) +b=\left( \begin{matrix} \omega _1& \omega _2& b\\ \end{matrix} \right) \left( \begin{array}{c} x_1\\ x_2\\ 1\\ \end{array} \right) \tag {2} neto=(ω1ω2)(x1x2)+b=(ω1ω2b)x1x21(2)

第二步:激活处理
  由于感知机的输出结果只有-1和1,所以激活函数(activation function)可以选为阶跃函数,阶跃函数可以将输入的数据映射为-1或1,其波形如图3所示。
在这里插入图片描述

图3 阶跃函数

激活转换:

y ^ = f ( n e t o ) = s i g n ( n e t o ) (3) \hat{y}=f\left( net_o \right) =sign\left( net_o \right) \tag {3} y^=f(neto)=sign(neto)(3)
其中, y ^ \hat{y} y^是激活转换结果, y ^ ∈ { − 1 , 1 } \hat{y} \in \left\{ { - 1, 1}\right\} y^{1,1}

3.2 参数更新过程

  如果某次激活转换结果是错误的,则我们称这个点为误分类点。文章开头我们已经说了,感知机的学习过程就是找一条直线的过程,如果有一条直线能够将所有的训练样例区分开,那这条直线就是最终的训练结果。那到底该如何找到这条命中注定的直线呢?我们知道超平面3 4的一般式可以表示为:
w T x + b = 0 (4) \mathbf{w}^T\mathbf{x}+b=0\tag {4} wTx+b=0(4)

  其中 w \mathbf{w} w是垂直于平面的法向量x是连接原点到超平面上任意一点的向量b是常数项。而直线不过是二维空间的超平面5,所以直线也可以用公式(4)表示, w \mathbf{w} wb相当于直线的权值和阈值,如此一来,只要不断的改变参数(权值和阈值),我们就能找到朝思暮想的那条直线。那我们到底该依据什么准则改变直线的参数呢?不难想象,如果训练过程中,误分类点越少,说明直线越接近那条命中注定的直线。
  请进一步思考,如果误分类点越少,则所有误分类点到直线的距离总和就会越小。假设有M个误分类点,则所有误分类点到直线的距离总和可以用式(5)表示6

1 ∥ w ∥ ∑ x i ∈ M ∣ w T x i + b ∣ (5) \frac{1}{\left\| \mathbf{w} \right\|}\sum_{\mathbf{x}_i\in M}{\left| \mathbf{w }^T\mathbf{x}_i+b \right|} \tag {5} w1xiMwTxi+b(5)

  我们得到了式(5),如果将其作为目标函数,这样就可以将找直线的问题转化为最优化问题7,只要求得目标函数的最优值,则对应权值和阈值就确定了。但公式(5)中有绝对值,这就不太好了,我们可以将公式(5)转换成以下格式:

− 1 ∥ w ∥ ∑ x i ∈ M y i ( w T x i + b ) (6) -\frac{1}{\left\| \mathbf{w} \right\|}\sum_{\mathbf{x}_i\in M}{y_i\left( \mathbf{w}^T\mathbf{x}_i+b \right)} \tag {6} w1xiMyi(wTxi+b)(6)
  其中, y i y_i yi是样本 x i \mathbf{x}_i xi对应的标记,下面分析为什么公式(5)可以转化成公式(6)。我们知道公式(5)是误分类点到直线距离的总和,而误分类情况有以下两种:

① 正类被错分为负类

  比如 y i y_i yi=1,但感知机的输出为 − 1 -1 1,此时有 ( w T x i + b ) < 0 \left( {{{\bf{w}}^T}{{\bf{x}}_i} + b} \right) < 0 (wTxi+b)<0,所以 − y i ( w T x i + b ) = − ( w T x i + b ) > 0 - {y_i}\left( {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right){\rm{ = }} - \left( {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right) > 0 yi(wTxi+b)=(wTxi+b)>0

② 负类被错分为正类

  比如 y i = − 1 y_i=-1 yi=1,但感知机的输出为1,此时有 ( w T x i + b ) > 0 \left( {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right) > 0 (wTxi+b)>0,所以 − y i ( w T x i + b ) = ( w T x i + b ) > 0 - {y_i}\left( {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right){\rm{ = }}\left( {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right) > 0 yi(wTxi+b)=(wTxi+b)>0


综合以上两种情况我们可以得出:

− y i ( w T x i + b ) = ∣ w T x i + b ∣ > 0 - {y_i}\left( {{{\bf{w}}^T}{{\bf{x}}_i} + b} \right){\rm{ = }}\left| {{{\bf{w }}^T}{{\bf{x}}_i} + b} \right| > 0 yi(wTxi+b)=wTxi+b>0
因此可以得到:
1 ∥ w ∥ ∑ x i ∈ M ∣ w T x i + b ∣ = − 1 ∥ w ∥ ∑ x i ∈ M y i ( w T x i + b ) \frac{1}{{\left\| {\bf{w}} \right\|}}\sum\limits_{{{\bf{x}}_i} \in M} {\left| {{{\bf{w}}^T}{{\bf{x}}_i} + b} \right|} {\rm{ = }} - \frac{1}{{\left\| {\bf{w}} \right\|}}\sum\limits_{{{\bf{x}}_i} \in M} {{y_i}\left( {{{\bf{w}}^T}{{\bf{x}}_i} + b} \right)} w1xiMwTxi+b=w1xiMyi(wTxi+b)

分析完毕。

  实际上,为了简化运算,感知机的损失函数往往不带 1 ∥ w ∥ \frac{1}{{\left\| {\bf{w}} \right\|}} w1,如公式(7)所示:
L ( w , b ) = − ∑ x i ∈ M y i ( w T x i + b ) (7) L\left( {{\bf{w }},b} \right) = - \sum\limits_{{{\bf{x}}_i} \in M} {{y_i}\left( {{{\bf{w}}^T}{{\bf{x}}_i} + b} \right)} \tag {7} L(w,b)=xiMyi(wTxi+b)(7)
  因为感知机的任务是进行二分类工作,它最终并不关心得到的超平面离各点的距离有多少,只关心最后是否已经正确分类7,而且 1 ∥ w ∥ \frac{1}{{\left\| {\bf{w}} \right\|}} w1是个常数项,所以只要公式(7)得到最优解,公式(6)的解自然也是最优解。

  有了公式(7)就可以采用随机梯度下降法计算参数的下降梯度。
∇ w L ( w , b ) = − ∑ x i ∈ M y i x i {\nabla _{\bf{w}}}L({\bf{w}},b) = - \sum\limits_{{{\bf{x}}_i} \in M} {{y_i}} {{\bf{x}}_i} wL(w,b)=xiMyixi ∇ b L ( w , b ) = − ∑ x i ∈ M y i {\nabla _b}L({\bf{w}},b) = - \sum\limits_{{{\bf{x}}_i} \in M} {{y_i}} bL(w,b)=xiMyi
  可以看到,每次产生误分类点后,我们只需让参数朝着负梯度的反方向改变,就能得到最终的最优解。那每次改变多少呢?可以通过学习率 η ( 0 ≤ η ≤ 1 ) η(0≤η≤1) η0η1控制参数改变的量。但是当学习率设置过小时,收敛过程将变得十分缓慢,而当学习率设置过大时,梯度可能会在最小值附近来回震荡,甚至可能无法收敛8。所以我们要给感知机选择一个合适的学习率,具体的学习率值,可以在实践中调试获得。

  经过以上分析我们可以得出,对于每个误分类点,权值和阈值的更新形式如下所示:
w ← w + η y i x i {\bf{w}} \leftarrow {\bf{w}} + \eta {y_i}{{\bf{x}}_i} ww+ηyixi b ← b + η y i b \leftarrow b + \eta {y_i} bb+ηyi

  从图形上看,每次调整 w \bf{w} w , b时,超平面都会向该误分类点移动,以减少该误分类点与超平面的距离,误分类点与超平面的总距离越小,误分类点就会越少9


4 代码实现

4.1 准备数据

  首先为感知机准备500组数据,其中一半是正类,一半是负类,表2展示了部分数据。

表2 部分样例
第1组第2组...第499组第500组
输入样本 5.49793.5828...3.12112.0781
4.35524.1159...2.31623.7475
标记11...-1-1

  将数据的顺序随机打乱,然后把数据集划分成训练集和测试集,其中前400个样例用于训练,后100个样例用于测试。

clc;clear;close all;
%% 加载原始数据
load('data.mat')
%% 打乱数据集的顺序
    randindex = randperm(length(data));
    data = data(:,randindex); % 每一列代表一个样例
%% 划分数据集   
    sample = data(1:2, :); % 每一列代表一个样本
    label = data(end, :);  % 标记数据
% 训练集
    x_train = sample(:, 1:400);
    y_train = label(:, 1:400);
% 测试集
    x_test = sample(:, 401:end);
    y_test = label(:, 401:end);
%% 绘制数据点
    figure();
    draw_Func(x_train', y_train) % 绘训练集
    hold on
    draw_Func(x_test', y_test) % 绘测试集

运行结果:
在这里插入图片描述

图4 数据可视化

  图中左下角为负类数据,右上角为正类数据。负类中的红色和正类中的蓝色代表训练数据,负类中的紫色和正类中的黄色代表测试数据。图中是数据让我们人类来分类的话,除了正类和负类交叉的地方难以分开,大部分数据很容易就能分开,而感知机需要一步步进行学习才能将数据进行正确分类。下面请看感知机训练代码。

4.2 感知机训练学习

  首先将感知机参数随机初始化,设置让感知机训练100轮,学习率设置为0.1。最后将训练所得的直线绘制出来10 11 12

% 模型初始化
    [m, n] = size(x_train); 
    w = rand(m, 1); % 初始化权值(二维列向量)
    b = rand(1);    % 初始化阈值
    y_s = zeros(size(y_train));   % 为y_s预分配空间
% 开始训练
maxStep = 100; % 让感知机训练100轮
eta = 0.1;  % learning rate
for step = 1:maxStep
    for i = 1:n
        neto = w'*x_train(:,i)+b;   % 线性转换
        y_s(i) = sign(neto);        % 激活转换 
        % 如果出现误分类点,则对参数进行更新
        if y_train(i) ~= y_s(i)
            dw = y_train(i)*x_train(:,i);   % 权值变化量
            db = y_train(i);                % 阈值变化量
            w = w + eta*dw;     % 权值更新
            b = b + eta*db;     % 阈值更新
        end
    end
end  
%% 绘制训练所得直线
    figure();
    draw_Func(x_train', y_train) % 绘训练集
    hold on
    xline = 0:10;
    yline = -w(1)/w(2) * xline - b/w(2);
    plot(xline,yline);

运行结果:
在这里插入图片描述

图5 感知机训练结果

  可以看到我们训练出来的感知机模型可以将大多数的数据区分开,少部分位于交叉位置的数据无法区分。从程序中可以看到,感知机“学习”到的东西就是两个参数:权值w和阈值b

4.3 感知机仿真测试

  期末到了,老师从500道题中随机抽400道题送给学生,让学生自己去学习,过一段时间后,老师将剩下的100道题拿出来,给学生进行一场考试,检验学生的学习情况,一般成绩越高,说明学生学习越认真。
  同理,感知机训练的好不好,需要测试才知道。将测试集的样本输入到训练好的感知机中,测试代码如下所示:

% 开始仿真
    y_sim = sign(w'*x_test+b);
    fprintf("感知机分类正确率为:%f%%\n",sum(y_test == y_sim))
    
    figure();
    draw_Func(x_test', y_test) % 绘测试集
    hold on
    plot(xline,yline);

运行结果:

>> 感知机分类正确率为:100.00%

在这里插入图片描述

图6 感知机测试结果

  感知机本次的测试结果为100%,但这并不代表其对所有的数据的分类都能做到100%正确。比如在一次考试中得100分的学生,并不是所有考试都能得100分。多次程序运行,结果可能不同,诸君请自行测试。


总结

  本文主要讲述了感知机的原理,感知机主要用于数据分类,且只能用于二分类。在代码实现部分,对感知机每一步的实现进行了讲解。本程序用到的数据,是随机生成的,有需要的请留言。


2022年11月11日23:42:02更新

  由于需要数据的人较多,现将数据以百度网盘链接的方式分享,有需要的请自取。如果您觉得这篇文章有帮助,那就点个赞吧,如果您发现文章中有错误,恳请指出,感谢各位的支持和建议~

链接: https://pan.baidu.com/s/1qrpzl0Hq9Teu5eJBExcAtA
提取码:peoi


附录

main.m

clc;clear;close all;
%% 加载原始数据
load('data.mat')
%% 打乱数据集的顺序
    randindex = randperm(length(data));
    data = data(:,randindex); % 每一列代表一个样例
%% 划分数据集   
    sample = data(1:2, :); % 每一列代表一个样本
    label = data(end, :);  % 标记数据
% 训练集
    x_train = sample(:, 1:400);
    y_train = label(:, 1:400);
% 测试集
    x_test = sample(:, 401:end);
    y_test = label(:, 401:end);
%% 绘制数据点
    figure();
    draw_Func(x_train', y_train) % 绘训练集
    hold on
    draw_Func(x_test', y_test) % 绘测试集
%% 感知机训练
% 模型初始化
    [m, n] = size(x_train); 
    w = rand(m, 1);	% 初始化权值(二维列向量)
    b = rand(1);    % 初始化阈值
    y_s = zeros(size(y_train));   % 为y_s预分配空间
% 开始训练
maxStep = 100; % 让感知机训练100轮
eta = 0.1;  % learning rate
for step = 1:maxStep
    for i = 1:n
        neto = w'*x_train(:,i)+b;	% 线性转换
        y_s(i) = sign(neto);        % 激活转换 
        % 如果出现误分类点,则对参数进行更新
        if y_train(i) ~= y_s(i)
            dw = y_train(i)*x_train(:,i);	% 权值变化量
            db = y_train(i);                % 阈值变化量
            w = w + eta*dw; 	% 权值更新
            b = b + eta*db;     % 阈值更新
        end
    end
end  
%% 绘制训练所得直线
    figure();
    draw_Func(x_train', y_train) % 绘训练集
    hold on
    xline = 0:10;
    yline = -w(1)/w(2) * xline - b/w(2);
    plot(xline,yline);

%% 感知机测试

% 开始仿真
    y_sim = sign(w'*x_test+b);
    fprintf("感知机分类正确率为:%f%%\n",sum(y_test == y_sim))
    
    figure();
    draw_Func(x_test', y_test) % 绘测试集
    hold on
    plot(xline,yline);

draw_Func.m

function draw_Func(sample, label) %绘点函数,将不同类别的点,标记成不同的颜色
    idx_pos = find(label==1);
    idx_neg = find(label~=1);
    plot(sample(idx_pos, 1), sample(idx_pos, 2),'o')
    hold on
    plot(sample(idx_neg, 1), sample(idx_neg, 2),'*')
    axis([0 10 0 10])
    grid on
end

参考资料


  1. 感知机算法原理及推导_Mr番茄蛋的博客-CSDN博客_感知器算法. ↩︎

  2. 机器学习——周志华. ↩︎

  3. 点到直线距离公式的几种推导 - 知乎 (zhihu.com). ↩︎

  4. 如何理解超平面? - 简书 (jianshu.com). ↩︎

  5. 超平面的数学基础知识 - 简书 (jianshu.com). ↩︎

  6. 几何间隔为什么是离超平面最近的点到超平面的距离? - 知乎 (zhihu.com). ↩︎

  7. 机器学习 第35集:感知机用什么做损失函数? ( 含有笔记、代码、注释 ) - 知乎 (zhihu.com). ↩︎ ↩︎

  8. 学习率(Learning rate)的理解以及如何调整学习率 - LLLiuye - 博客园 (cnblogs.com). ↩︎

  9. 【机器学习教程】感知机详解_chenmo69的专栏-CSDN博客. ↩︎

  10. 感知机 MATLAB实现(数据+代码). ↩︎

  11. ML—感知机算法(MATLAB). ↩︎

  12. matlab 实现感知机线性二分类算法(Perceptron). ↩︎

  • 42
    点赞
  • 153
    收藏
    觉得还不错? 一键收藏
  • 80
    评论
评论 80
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值