感知机介绍及MATLAB实现

感知机介绍及MATLAB实现文章目录前言 1 感知机原理 1 1 感知机结构 1 2 感知机学习过程 2MATLAB 实现 1 引入库 2 读入数据总结前言提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门技术也越来越重要 很多人都开启了学习机器学习 本文就介绍了机器学习的基础内容 提示 以下是本篇文章正文内容 下面案例可供参考 1 感知机原理 感知机 Perceptron 是二分类线性分类模型 以二维平面为例 感知机分类的过程可以看做是寻找一条直线用于分割两组数据 假设直线的一侧是正类 则直线的


前言

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

操作环境
  MATLAB 2020a


1 感知机简介

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


图1 感知机分类示意图


2 感知机结构

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

在这里插入图片描述


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


3 感知机学习过程

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


表1 训练样例

第一组 第二组 第三组 第四组
输入样本 5.0 6.2 1.2 1.9
5.8 5.5 2.8 1.7
标记 1 1 -1 -1

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

3.1 数据传播过程

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

  其中, ω 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)


图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)的解自然也是最优解。




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


4 代码实现

4.1 准备数据

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


表2 部分样例

第1组 第2组 第499组 第500组
输入样本 5.4979 3.5828 3.1211 2.0781
4.3552 4.1159 2.3162 3.7475
标记 1 1 -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 感知机仿真测试

% 开始仿真 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分。多次程序运行,结果可能不同,诸君请自行测试。


总结

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

附录

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). ↩︎




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/177211.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午8:02
下一篇 2026年3月26日 下午8:03


相关推荐

  • PyCharm 必备插件,更换背景(自用,持续更新)|CSDN创作打卡

    PyCharm 必备插件,更换背景(自用,持续更新)|CSDN创作打卡PyCharm 的插件很好用 能够在写代码时加成很多 目录如何进入插件管理如何安装 1 Chinese Simplified LanguagePack 中文插件 2 RegexpTester 正则表达式 3 RainbowBrack 括号高亮 4 JsonParser js 校验 5 NyanProgress 彩虹猫进度条 好用插件持续更新中 如何进入插件管理 pycharm 页面 gt File gt Settings amp

    2026年3月27日
    1
  • java 二维数组排序

    java 二维数组排序①使用Comparator生成一个比较器对象初始化数组:int[][]arr=newint[m][n];排序规则:先按数组的第一个元素进行升序排序,若第一个元素相等,则按照第二个元素进行升序排序。使用API:Arrays.sort()(T[]a,Comparator<?superT>c),该API根据给定的比较器(设定排序方式)对指定的数组进行排序。代码实现:Arrays.sort(arr,newComparator<int[]>(){

    2022年6月5日
    40
  • 2018,我的这一年

    这一年是和自己对话的一年,是矛盾的一年,是抑郁的一年。时间过的很快,2018已经过去很多天了,是时候对过去的这一年进行一个简单的总结了,不管这一年过的如何,在时间的巨轮下,一切都成为过往,成为了生命中的一段经历,若干年后这一段经历或许只剩下一些碎片的回忆,那也没有关系,顺其自然即可! 泰戈尔曾说过:”天空没留下翅膀的痕迹,但我已飞过“。虽然多年之后记忆中很多事情没有了痕迹,但那些事情的确曾经…

    2022年2月27日
    49
  • 全局作用域中,用const和let声明的变量去哪了?

    全局作用域中,用const和let声明的变量去哪了?

    2021年6月17日
    132
  • 什么是kit_django reinhardt

    什么是kit_django reinhardt#介绍ImageKit是用于处理图像的Django应用程序。如果需要从原图上生成一个长宽为50×50的图像,则需要ImageKit。ImageKit附带了一系列图像处理器,用于调整大小和裁剪等常见

    2022年8月7日
    6
  • mysql 主键分区

    mysql 主键分区mysql 主键分区通常对于数据比较多的表 可以采用字段分区 一般是 id 首先需要把 id 指定为 bigint 20 类型 然后进行 id 分区 ALTERTABLE sh ad hit PARTITIONBYR id PARTITIONS21 PARTITION p0 VALUESLESSTH PARTITION p1 VALUESL

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号