多层感知机(MLP)算法原理和代码实现

2023-11-11

多层感知机入门

神经网络在最近几年,是个很火的名词了。常听到的卷积神经网络(CNN)或者循环神经网络(RNN),都可以看做是神经网络在特定场景下的具体应用方式。

本文我们尝试从神经网络的基础:多层感知机(Multilayer perceptron, MLP)入手,以此了解其解决预测问题的基本算法原理。

要入门MLP,个人认为比较简单的方式是将其理解为一种广义的线性模型

看上图,对于线性模型(左上图)
y = w [ 1 ] ∗ x [ 1 ] + w [ 2 ] ∗ x [ 2 ] + w [ 3 ] ∗ x [ 3 ] + b y=w[1]*x[1]+w[2]*x[2]+w[3]*x[3]+b y=w[1]x[1]+w[2]x[2]+w[3]x[3]+b
对于MLP(右上图), x x x y y y之间增加了一组黄色的 h h h模块,叫隐藏层。

y y y h h h x x x的关系为
h [ 1 ] = f ( w [ 1 , 1 ] x [ 1 ] + w [ 2 , 1 ] x [ 2 ] + w [ 3 , 1 ] x [ 3 ] + b [ 0 ] ) h[1]=f(w[1,1]x[1]+w[2,1]x[2]+w[3,1]x[3]+b[0]) h[1]=f(w[1,1]x[1]+w[2,1]x[2]+w[3,1]x[3]+b[0])
h [ 2 ] = f ( w [ 1 , 2 ] x [ 1 ] + w [ 2 , 2 ] x [ 2 ] + w [ 3 , 2 ] x [ 3 ] + b [ 1 ] ) h[2]=f(w[1,2]x[1]+w[2,2]x[2]+w[3,2]x[3]+b[1]) h[2]=f(w[1,2]x[1]+w[2,2]x[2]+w[3,2]x[3]+b[1])
y = v [ 1 ] h [ 1 ] + v [ 2 ] h [ 2 ] + s y=v[1]h[1]+v[2]h[2]+s y=v[1]h[1]+v[2]h[2]+s
其中, f ( ⋅ ) f(·) f()是某个非线性函数,叫激活函数。

从数学上来看,如果 f ( ⋅ ) f(·) f()是线性的,那么增加了隐藏层的MLP本质上还是一个线性模型。为了让MLP的学习能力更强大, f ( ⋅ ) f(·) f()需要使用非线性函数,目前应用比较多的有校正非线性(relu)、正切双曲线(tanh)或sigmoid函数。

三个函数的表达式分别为
relu : y = m a x ( 0 , x ) \text{relu}:y=max(0, x) relu:y=max(0,x)
tanh : y = e x − e − x e x + e − x \text{tanh}:y=\frac{e^x-e^{-x}}{e^x+e^{-x}} tanh:y=ex+exexex
sigmoid : y = 1 1 + e − x \text{sigmoid}:y=\frac{1}{1+e^{-x}} sigmoid:y=1+ex1
以下是它们各自对应的图形

此处最直接的一个疑问是,为什么有了这些激活函数后,学习能力就能变强大了?

我们可以这样理解:任意非线性函数都可以用多个分段线性函数来表示,即分段线性函数可以拟合任意非线性函数。而relu显然可以拟合分段线性函数,所以理论上便可以拟合任意非线性函数;从图上看,tanh和sigmoid可以认为是软化后的relu,所以对拟合效果影响不大,而且这两个函数计算梯度会更方便。

MLP的设计其实还有一种理解,就是看成是基于生物神经元的抽象模型,这也是神经网络模型的最初由来。神经元的结构如下,MLP和它的形状确实很像了。

算法优化原理

在上一节中,隐藏层只有1层,该层中也只有2个隐藏单元,变量的总个数为11个。但实际上,隐藏层的数量和每个隐藏层中的隐藏单元数量都可以为更多个,此时变量的个数就更多了。那么,该如何找到这些变量的最优值呢?

回到MLP设计的初衷,我们是要让MLP针对样本的预测误差最小。针对任意一个样本,误差可以定义为
L o s s = ( y − y ^ ) 2 Loss=(y-\hat y)^2 Loss=(yy^)2
还是以之前的实例为例,上式变为
L o s s = [ y − ( v 1 f ( w 11 x 1 + w 21 x 2 + w 31 x 3 + b 0 ) + v 2 f ( w 12 x 1 + w 22 x 2 + w 32 x 3 + b 1 ) + s ) ] 2 Loss=[y-(v_1f(w_{11}x_1+w_{21}x_2+w_{31}x_3+b_0)+v_2f(w_{12}x_1+w_{22}x_2+w_{32}x_3+b_1)+s)]^2 Loss=[y(v1f(w11x1+w21x2+w31x3+b0)+v2f(w12x1+w22x2+w32x3+b1)+s)]2
如果训练集中共有 N N N个样本,那么需要将每个样本的误差加和,并使其最小化
m i n ∑ i = 1 N L o s s i min \quad \sum_{i=1}^NLoss_i mini=1NLossi
上式中,待优化变量为 v , w , b \pmb v,\pmb w,\pmb b v,w,b s s s,而且为无约束问题,因此可以通过梯度类算法求解。

经典的梯度类算法,可以参考梯度类算法原理:最速下降法、牛顿法和拟牛顿法。不过由于MLP中的优化变量数量通常非常大,优化过程较为耗时,所以像牛顿法这类需要计算海森矩阵的算法自然是不合适的;即使是拟牛顿法这种已经使用其他方案替代海森矩阵的算法也逐渐被大家舍弃;最终,在提升训练效率的驱使下,人们普遍选择了最速下降法这类只需要一阶梯度的算法作为基本算法。

既然需要使用一阶梯度的信息,那么该如何计算梯度呢?在计算之前,我们要先理清楚的是,这里的梯度,指的是损失函数Loss针对优化变量为 v , w , b \pmb v,\pmb w,\pmb b v,w,b s s s的梯度。

我们以Loss针对 w w w的梯度为例,说明一下求解过程。还是使用之前的MLP实例,同时把表达式写成矩阵形式
h = f ( w T x + b ) ⇒ ∂ h ∂ w T = f ′ ⋅ x h=f(w^Tx+b) \Rightarrow \frac{\partial h}{\partial w^T}=f'·x h=f(wTx+b)wTh=fx
y ^ = v T h + s ⇒ ∂ y ^ ∂ h = v T \hat y = v^Th+s \Rightarrow \frac{\partial \hat y}{\partial h}=v^T y^=vTh+shy^=vT
L o s s = ( y − y ^ ) 2 ⇒ ∂ L o s s ∂ y ^ = y − y ^ Loss=(y-\hat y)^2 \Rightarrow \frac{\partial Loss}{\partial \hat y}=y-\hat y Loss=(yy^)2y^Loss=yy^
根据求导的链式法则
∂ L o s s ∂ w T = ∂ L o s s ∂ y ^ ⋅ ∂ y ^ ∂ h ⋅ ∂ h ∂ w T = ( y − y ^ ) ⋅ v T ⋅ f ′ ⋅ x \frac{\partial Loss}{\partial w^T}=\frac{\partial Loss}{\partial \hat y}·\frac{\partial \hat y}{\partial h}·\frac{\partial h}{\partial w^T}=(y-\hat y)·v^T·f'·x wTLoss=y^Losshy^wTh=(yy^)vTfx
这就是我们常说的:误差反向传播,此处可以理解为梯度计算的过程是从后向前逐步推导过来的。

以上实例给的是,针对一个样本的梯度求解过程。为了得到准确的梯度值,就需要针对每个样本都走一遍如上的过程。如果我们的训练样本数量比较大,就会导致梯度计算的过程非常慢。

如果换个思路来处理,我们每次只计算其中一个样本的梯度值,然后将该值作为最终的梯度值,那么结果就是梯度的计算过程快但值不那么准确。

所以当下流行的折中方案是,每次挑选一部分样本(batch)出来,计算出梯度值作为整体的梯度值。通过batch数量的调整,便可以兼顾计算的效率和值的准确性。

有了梯度后,我们回顾一下梯度类算法的基本迭代公式:
θ k + 1 = θ k − η k ⋅ d k \theta_{k+1}=\theta_k-\eta_k·d_k θk+1=θkηkdk
其中, θ \theta θ表示优化变量, η k \eta_k ηk d k d_k dk分别为第 k k k次的迭代步长(MLP中更多称之为“学习率”)和迭代方向。

在最速下降法中
d k = ∂ L o s s ∂ θ = ∇ ( θ ) d_k=\frac{\partial Loss}{\partial \theta}=\nabla(\theta) dk=θLoss=(θ)

d k d_k dk的计算过程刚刚已经描述了。如果算法逻辑到此为止,我们一般会称之为随机梯度下降法(Stochastic gradient descent,SGD)。

但是,人们一般很难满足于这种比较基础的算法,所以在此之上,相继出现了很多改进算法。从迭代公式可以看出,改进点主要包含3类:第一类是针对迭代方向,如Momentum;第二类是针对学习率,如Adagrad;第三类是同时改进迭代方向和学习率,如Adam。具体改进的策略可以参考学术文献:An overview of gradient descent optimization algorithms

sklearn代码实现

sklearn中已经集成了MLP的工具包,下例中使用分类功能:MLPClassifier。该函数参数的含义可以参考MLPClassifier参数详解。数据集我们使用two_moons,该数据集的详细介绍可以参考之前的文章:决策树入门、sklearn实现、原理解读和算法分析

import mglearn.plots
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

from data.two_moons import two_moons
import matplotlib.pyplot as plt


def train_two_moons():
    # 导入两个月亮数据集
    features, labels = two_moons()
    
    # 将数据集分为训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(features, labels, stratify=labels, random_state=42)
    # 设置 MLP 分类器属性:激活函数为tanh,2个隐藏层,每层均包含10个隐藏单元,最大迭代次数为1000
    mlp = MLPClassifier(solver='lbfgs', activation='tanh', random_state=0, hidden_layer_sizes=[10, 10], max_iter=1000)

    # 使用训练集对 MLP 分类器进行训练
    mlp.fit(X_train, y_train)
    # 打印模型在训练集上的准确率
    print('Accuracy on train set: {:.2f}'.format(mlp.score(X_train, y_train)))
    # 打印模型在测试集上的准确率
    print('Accuracy on test set: {:.2f}'.format(mlp.score(X_test, y_test)))
    
    # 绘制 2D 分类边界
    mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
    # 绘制训练集的散点图
    mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
    # 设置 x 轴标签
    plt.xlabel("Feature 0")
    # 设置 y 轴标签
    plt.ylabel("Feature 1")
    

if __name__ == '__main__':

    train_two_moons()

运行以上代码后,首先可以得到MLP的性能指标:

Accuracy on train set: 1.00
Accuracy on test set: 0.84

然后还可以得到该模型的决策边界。显然,该决策边界是非线性的,但是看起来还算平滑。

核心优缺点分析

MLP的主要优点是,可以通过调整隐藏层的层数和每个隐藏层内的隐藏单元数,构建出复杂的模型,从而学习到数据中包含的各类信息。如果给予足够的计算时间和数据,并且仔细调节参数,MLP通常可以打败其他机器学习算法(无论是分类任务还是回归任务)。

其主要缺点有两个:第一个是复杂模型往往需要花费较长的时间去训练模型中的参数,第二节中SGD->Momentum、Adagrad->Adam的算法进化,可以理解为人们尝试通过算法改进的方式,降低模型训练的总时间;第二个是可解释差,即我们无法解释清楚模型给出某个预测结果的原因,所以这个模型很多时候也被称之为黑箱模型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多层感知机(MLP)算法原理和代码实现 的相关文章

随机推荐

  • 七. Kubernetes Objects对象,对象状态与yaml

    目录 一 基础概念理解 二 k8s 对象中的spec与status 三 如何编写yaml 一 基础概念理解 Kubernetes Objects 官方地址 在k8s中所有操作资源实体都可以称为对象 先下图中的这些 都可以称为对象 不同对象功
  • 汇编语言随笔(10)-内中断及实验12(返回到dos的中断处理程序)

    不同类型内中断的区分 中断类型码 8086cpu中在下面4种情况下会产生内中断 1 除法错误 如之前提到的除法溢出 2 单步执行 3 执行into指令 4 执行int指令 那么当内中断发生时cpu如何来区分到底是哪种中断源呢 通过中断类型码
  • 代码随想录训练营day9

    题目一 实现strStr 力扣题目链接 题目描述 给你两个字符串 haystack 和 needle 请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标 下标从 0 开始 如果 needle 不是 haysta
  • GLSL的in、out存储限制符使用错误

    在GLSL中用in修饰的变量表示传入的数据 用out修饰的变量表示传出的数据 通过这样可以实现顶点着色器向片段着色器传递数据 但要注意这个变量的命名要相同 不相同的话 片段着色器是获取不到从顶点着色器传入的数据的 1 顶点着色器 versi
  • unity打包技巧

    打包准备 1 android 需要jdk 和android sdk 如果有使用C 的库 还需要NDK 只有将 so文件放在 Assets Plugins Android libs下 Unity才会将 so文件识别为共享库 并在打包时将之拷贝
  • 分布式事务-seata AT模式-强一致性。

    目录 1 seata原理 2 关键组件 3 seate服务端参数 4 微服务配置 5 业务流程 6 依次启动eureka seate服务器 微服务 1 seata原理 Seata 是一款开源的分布式事务解决方案 致力于提供高性能和简单易用的
  • element-ui 表格一行显示多行内容并实现多行内某一行列合并

    这是加上边框的 去掉边框后这个表格看着更明显一点 表格一行放多行内容 并让第二行进行列合并 第一行不合并
  • 详解数据结构之顺序栈的基本操作(C语言描述)

    1 栈是线性表的特例 因此栈的顺序存储其实也就是线性表顺序存储的简化 我们称之为顺序栈 线性表是采用数组来实现的 因此顺序栈也采用数组来实现 2 栈的结构定义 elementype类型根据实际情况而定 这里假设为int类型 栈的结构体定义为
  • 时间序列ARIMA滚动预测

    什么是时间序列 时间序列简单的说就是各时间点上形成的数值序列 时间序列分析就是通过观察历史数据预测未来的值 在这里需要强调一点的是 时间序列分析并不是关于时间的回归 它主要是研究自身的变化规律的 这里不考虑含外生变量的时间序列 为什么用py
  • C语言结构体struct的比较

    两个struct结构体进行比较 首先不能直接比较 struct A a b a和b相比是错误的 其次不能进行内存比较 如下 程序运行的结果会如何 void DiffStructWithMultiVar struct A a 0 struct
  • 远程连接身份验证错误,找不到加密Oracle修正(正解)

    出现问题 使用远程连接弹出一个对话框 提示 发生身份验证错误 要求的函数不受支持 方法一 win r 输入gpedit msc 找到下面路径 策略路径 计算机配置 gt 管理模板 gt 系统 gt 凭据分配 设置名称 加密 Oracle 修
  • 图像质量评估

    图像质量评估 http jingyan baidu com article cbf0e500f5505a2eab28936e html 客观评价方法 图像质量的客观评价方法是根据人眼的主观视觉系统建立数学模型 并通过具体的公式计算图像的质量
  • Java基础——集合

    首先呢 给大家讲一下集合的由来 java的思想是面向对象思想 如果想统一的管理所有的对象 就涉及到用容器来储存对象 而对象数组是可以存储对象的 但是数组有一个缺点就是长度固定 为了突破这一限制 集合就应运而生了 数组和集合的优缺点 长度 数
  • 代码随想录刷题--(链表篇)19. 删除链表的倒数第 N 个结点 ---快慢指针法

    题目链接 https leetcode cn com problems remove nth node from end of list 代码随想录链接 https programmercarl com 0019 删除链表的倒数第N个节点
  • 前端实现换肤功能

    项目背景 由于项目要求 需要前端对不同的企业用户展示不一样的颜色 也就是简单的更改肤色 本来使用前端框架会很容易解决 但是公司目前的架构不是很好 前后端分离也没有那么彻底 web工程还是搭配jsp 没办法 只好用最纯粹的css来实现换肤要求
  • 一个月拿下十几份测试岗offer的简历是什么样子的?

    一 简历模板 在应聘之前 不能不聊一聊简历 简历是职场的敲门砖 是获得offer的通行证 那样对一个初级测试工程师来说 应该如何写简历呢 可能对萌新来说 完完全全不知道怎么下手 在这里大家从0开始写 第一步你需要去找一份简历模板 可以是网络
  • 实时系统RTX之理解一

    文献来源 http wzhyblog yo2 cn articles e5 ae 9e e6 97 b6 e7 b3 bb e7 bb 9frtx e5 ae 98 e6 96 b9 e6 96 87 e6 a1 a3 e4 b8 ad e
  • Windows环境下使用UHD PythonAPI开发USRP X310

    目录 UHD介绍 安装UHD 烧写X310固件 安装uhd python API 测量功率实例 UHD介绍 UHD是USRP开源软件无线电硬件架构的底层软件包 包含上位机和USRP设备中的FPGA的bit文件两个部分 提供了上位机控制FPG
  • BeanPostProcessor(Spring后置处理器)如何使用呢?

    转自 BeanPostProcessor Spring后置处理器 如何使用呢 BeanPostProcessor接口 我们通常称其为后置处理器 通过重写接口中的方法可实现初始化前后的操作 BeanPostProcessor 接口源码 pub
  • 多层感知机(MLP)算法原理和代码实现

    文章目录 多层感知机入门 算法优化原理 sklearn代码实现 核心优缺点分析 多层感知机入门 神经网络在最近几年 是个很火的名词了 常听到的卷积神经网络 CNN 或者循环神经网络 RNN 都可以看做是神经网络在特定场景下的具体应用方式 本