Main Content

感知器神经网络

Rosenblatt [Rose61] 创建了感知器的许多变体。其中最简单的一个是单层网络,其权重和偏置可以训练,以在向其提交对应的输入向量时生成正确的目标向量。使用的训练方法称为感知器学习规则。感知器引起了人们极大的兴趣,因为它能够从其训练向量中泛化,从最初随机分布的连接中学习。感知器特别适用于模式分类中的简单问题。对于它们求解的问题来说,它们是快速可靠的网络。此外,了解感知器的运行,能够为您了解更复杂的网络奠定良好的基础。

鉴于篇幅所限,本节仅对感知器进行了简短讨论。有关更多更深入的讨论,请参阅 [HDB1996] 的第 4 章“Perceptron Learning Rule”,其中讨论了使用多层感知器求解单层无法解决的高难度问题。

神经元模型

使用硬限制传递函数 hardlim 的感知器神经元如下所示。

Diagram of a perceptron neuron showing input data being multiplied by individual weights, a bias value being added, and the hard-limit transfer function being applied to the result.

每个外部输入都用适当的权重 w1j 进行加权,将加权输入之和发送到硬限制传递函数,该函数还通过偏置将输入 1 传递给它。硬限制传递函数返回 0 或 1,如下所示。

Plot of the hard-limit transfer function. For inputs greater than or equal to 0, the function returns 1. For inputs less than 0, the function returns 0.

如果传递函数的净输入等于或大于 0,则感知器神经元生成 1;否则生成 0。

硬限制传递函数通过将输入空间划分为两个区域,使感知器能够对输入向量进行分类。具体来说,如果净输入 n 小于 0,输出将为 0;如果净输入 n 等于或大于 0,输出将为 1。下图显示了一个双输入硬限制神经元的输入空间,权重 w1,1 = −1,w1,2 = 1 且偏置 b = 1。

Plot of the input space of a two-input hard-limit neuron showing a decision boundary.

两个分类区域由
Wp + b = 0 处的决策边界线 L 构成。这条线垂直于权重矩阵 W 并根据偏置 b 偏移。线 L 上方和左侧的输入向量将生成大于 0 的净输入,因此,使硬限制神经元输出 1。线 L 下方和右侧的输入向量使神经元输出 0。您可以选择权重和偏置值来定向和移动分隔线,以便根据需要对输入空间进行分类。

没有偏置的硬限制神经元始终有一条分类线穿过原点。增加偏置可以让神经元求解两组输入向量不在原点不同侧的问题。该偏置允许决策边界偏离原点,如上图所示。

感知器架构

感知器网络由单层 S 感知器神经元组成,这些神经元通过一组权重连接到 R 输入 wi,j,有如下所示的两种形式。与之前一样,网络索引 i 和 j 指示 wi,j 是从第 j 个输入到第 i 个神经元的连接的强度。

Network diagram of a perceptron network.

这种简单描述的感知器学习规则只能训练一层。因此,此处只考虑单层网络。这种约束限制了感知器可以执行的计算。感知器能够求解的问题类型在限制和注意事项中讨论。

创建感知器

您可以使用以下代码创建一个感知器:

net = perceptron;
net = configure(net,P,T);

其中输入参量如下:

  • P 是由 Q 个输入向量组成的 R×Q 矩阵,每个输入向量包含 R 个元素。

  • T 是由 Q 个目标向量组成的 S×Q 矩阵,每个目标向量包含 S 个元素。

通常,hardlim 函数用于感知器,因此该函数是默认值。

以下命令创建一个感知器网络,该网络有一个单元素输入向量(值为 0 和 2)和一个神经元(输出可以是 0 或 1):

P = [0 2];
T = [0 1];
net = perceptron;
net = configure(net,P,T);

您可以通过执行以下命令来查看创建了什么网络:

inputweights = net.inputweights{1,1}

它生成

inputweights = 
        delays: 0
       initFcn: 'initzero'
         learn: true
      learnFcn: 'learnp'
    learnParam: (none)
          size: [1 1]
     weightFcn: 'dotprod'
   weightParam: (none)
      userdata: (your custom info)

默认学习函数是 learnp,在感知器学习规则 (learnp)中讨论该函数。hardlim 传递函数的净输入是 dotprod,它生成输入向量和权重矩阵的乘积,并与偏置相加以计算净输入。

默认初始化函数 initzero 用于将权重的初始值设置为零。

类似地,

biases = net.biases{1}

可得

biases = 
       initFcn: 'initzero'
         learn: 1
      learnFcn: 'learnp'
    learnParam: []
          size: 1
      userdata: [1x1 struct]

您可以看到,偏置的默认初始化值也是 0。

感知器学习规则 (learnp)

感知器是基于期望行为的示例来训练的。期望的行为可以通过一组输入、输出对组来汇总:

p1t1,p2t1,,pQtQ

其中 p 是网络的输入,t 是对应的正确(目标)输出。目标是减少误差 e,表示为 ta 之差,即神经元响应 a 和目标向量 t 之间的差。感知器学习规则 learnp 在给定输入向量 p 和关联误差 e 的情况下,计算期望的感知器权重和偏置变化。目标向量 t 必须包含 0 或 1 值,因为感知器(具有 hardlim 传递函数)只能输出这些值。

每次执行 learnp 时,感知器生成正确输出的可能性都会提高。如果存在一个解,则证明感知器规则在有限次迭代中收敛于解。

如果不使用偏置,learnp 的求解方式是仅更改权重向量 w,以指向要分类为 1 的输入向量并远离要分类为 0 的向量。这将产生垂直于 w 并对输入向量进行适当分类的决策边界。

一旦提交输入向量 p 并且网络响应 a 计算出来,单个神经元可能出现三种情况:

第 1 种情况。如果提交输入向量并且神经元的输出正确(a = te = ta = 0),则权重向量 w 不变。

第 2 种情况。如果神经元输出为 0 但应为 1(a = 0 且 t = 1,而且 e = ta = 1),则将输入向量 p 与权重向量 w 相加。这使得权重向量更接近输入向量,从而增加了输入向量在将来分类为 1 的几率。

第 3 种情况。如果神经元输出为 1 但应为 0(a = 1 且 t = 0,而且 e = ta = –1),则从权重向量 w 中减去输入向量 p。这使得权重向量更偏离输入向量,从而增加了输入向量在将来分类为 0 的几率。

可以使用误差 e = ta 对权重向量所做的更改 Δw 更简洁地编写感知器学习规则:

第 1 种情况。如果 e = 0,则更改 Δw 等于 0。

第 2 种情况。如果 e = 1,则更改 Δw 等于 pT

第 3 种情况。如果 e = –1,则更改 Δw 等于 –pT

这三种情况都可以用一个表达式来表述:

Δw=(tα)pT=epT

注意到偏置只是输入始终为 1 的权重,可以得到神经元偏置变化的表达式:

Δb=(tα)(1)=e

对于一层神经元的情况,可得到

ΔW=(ta)(p)T=e(p)T

Δb=(ta)=e

感知器学习规则可以总结如下:

Wnew=Wold+epT

bnew=bold+e

其中 e = ta

现在尝试一个简单示例。从具有一个输入向量的单个神经元开始,该输入向量只包含两个元素。

net = perceptron;
net = configure(net,[0;0],0);

为了简化问题,将偏置设置为等于 0,将权重设置为 1 和 -0.8:

net.b{1} =  [0];
w = [1 -0.8];
net.IW{1,1} = w;

输入目标对组由下式给出

p = [1; 2];
t = [1];

您可以使用下式计算输出和误差

a = net(p)
a =
     0
e = t-a
e =
     1

并使用函数 learnp 求出权重的变化。

dw = learnp(w,p,[],[],[],[],e,[],[],[],[],[])
dw =
     1     2

然后,通过以下方式获得新权重:

w = w + dw
w =
    2.0000    1.2000

可以重复进行求新权重(和偏置)的过程,直到没有误差为止。前面提到过,对于所有可以由感知器求解的问题,感知器学习规则可保证在有限步数内收敛。其中包括所有线性可分的分类问题。在这种情况下,要分类的对象可以用一条线分离。

训练 (train)

如果重复使用 simlearnp 向感知器提交输入,并根据误差更改感知器的权重和偏置,则感知器最终会找到求解问题的权重和偏置值,前提是感知器可以求解该问题。每一次经历所有训练输入和目标向量称为一次遍历

函数 train 执行这样的循环计算。在每次遍历中,函数 train 都通过指定的输入序列,在提交输入时针对序列中的每个输入向量计算输出和误差并进行网络调整。

请注意,train 无法保证生成的网络能够实现预期目标。您必须通过计算每个输入向量的网络输出来检查 Wb 的新值,以查看是否达到所有目标。如果网络不能成功执行,您可以再次调用 train 对其进一步训练,使用新的权重和偏置进行更多遍训练,您也可以分析该问题,看看它是否适合用感知器求解。限制和注意事项中讨论了感知器网络无法求解的问题。

我们通过一个简单问题来说明训练过程。假设有一个单神经元感知器,其单个向量输入具有两个元素:

Diagram of a one-neuron perceptron network.

此网络以及您将要求解的问题非常简单,您甚至可以通过手算来完成求解。下面讨论的问题引述了 [HDB1996] 中的内容。

假设您有以下分类问题,并希望用单向量输入、二元素感知器网络来求解它。

{p1=[22],t1=0}{p2=[12],t2=1}{p3=[22],t3=0}{p4=[11],t4=1}

使用初始权重和偏置。通过在变量后面的圆括号中使用数字来表示该计算的每步的变量。因此,上面的初始值是 W(0) 和 b(0)。

W(0)=[00]b(0)=0

首先使用初始权重和偏置计算第一个输入向量 p1 的感知器输出 a。

α=hardlim(W(0)p1+b(0))=hardlim([00][22]+0)=hardlim(0)=1

输出 a 与目标值 t1 不相等,因此使用感知器规则根据误差找出权重和偏置的增量变化。

e=t1α=01=1ΔW=ep1T=(1)[22]=[22]Δb=e=(1)=1

您可以使用感知器更新规则计算新的权重和偏置。

Wnew=Wold+epT=[00]+[22]=[22]=W(1)bnew=bold+e=0+(1)=1=b(1)

现在提交下一个输入向量 p2。输出的计算如下。

α=hardlim(W(1)p2+b(1))=hardlim([22][12]1)=hardlim(1)=1

在这种情况下,目标是 1,所以误差为零。因此,权重或偏置没有变化,即 W(2) = W(1) = [−2 −2] 且 b(2) = b(1) = −1。

您可以继续以这种方式提交 p3,计算输出和误差,并更改权重和偏置等。对所有四个输入进行一次遍历后,您将得到值 W(4) = [−3 −1] 和 b(4) = 0。要确定是否获得了满意解,请对所有输入向量进行一次遍历,看看它们是否都生成了所需的目标值。第四个输入并未生成所需的目标值,但算法在第六次提交输入时是收敛的。最终值是

W(6) = [−2 −3] 和 b(6) = 1。

这是手算的结果。现在,如何使用 train 函数来实现呢?

以下代码定义一个感知器。

net = perceptron;

假设有以下单一输入

p = [2; 2];

目标为

t = [0];

epochs 设置为 1,这样 train 只需遍历一次输入向量(本例中只有一个输入向量)。

net.trainParam.epochs = 1;
net = train(net,p,t);

新的权重和偏置是

w = net.iw{1,1}, b = net.b{1}
w =
    -2    -2
b =
    -1

因此,初始权重和偏置为 0,在只使用第一个向量进行训练后,其值为 [−2 −2] 和 −1,与手算结果相符。

现在应用第二个输入向量 p2。在更改权重和偏置前,输出始终为 1,不过现在目标为 1,因此误差为 0,变化为零。您可以从上一个结果开始,按照这种方式反复应用一个新的输入向量。您可以使用 train 自动完成这项工作。

每轮都应用 train,对由所有四个输入向量组成的序列进行一次遍历。从网络定义开始。

net = perceptron;
net.trainParam.epochs = 1;

输入向量和目标是

p = [[2;2] [1;-2] [-2;2] [-1;1]]
t = [0 1 0 1]

现在使用以下代码训练网络

net = train(net,p,t);

新的权重和偏置是

w = net.iw{1,1}, b = net.b{1}
w =
    -3    -1
b =
     0

这与您之前手算的结果相同。

最后,使用每个输入来对经过训练的网络进行仿真。

a = net(p)
a = 
      0     0     1     1

输出还不等于目标,所以需要通过多次遍历来训练网络。尝试进行多轮训练。该运行在两轮训练后的均值绝对误差性能为 0:

net.trainParam.epochs = 1000;
net = train(net,p,t);

这样,在第三轮训练中提交输入时,网络已完成训练。(通过手算可知,网络在提交第六个输入向量时收敛。这种情况发生在第二轮训练中,但要在第三轮才能检测到网络收敛。)最终的权重和偏置是

w = net.iw{1,1}, b = net.b{1}
w =
    -2    -3
b =
     1

各种输入的仿真输出和误差为

a = net(p)
a =
             0             1             0             1
error = a-t
error =
             0             0             0             0

您确认训练过程十分成功。该网络收敛并为四个输入向量生成正确的目标输出。

使用 perceptron 创建的网络的默认训练函数是 trainc。(您可以通过执行 net.trainFcn 来查找此函数。)该训练函数以其纯形式应用感知器学习规则,即依次单独应用各输入向量,并在每次提交一个输入向量后对权重和偏置进行更正。因此,使用 train 进行的感知器训练将在有限步数内收敛,除非提交的问题无法用简单的感知器求解。

其他网络也可以通过各种方式使用 train 函数。键入 help train 可了解关于此基本函数的更多信息。

您可能想尝试各种示例程序。例如,用双输入感知器分类说明简单感知器的分类与训练。

限制和注意事项

应该使用 adapt 来训练感知器网络,该函数按一次一个的方式向网络提交输入向量,并根据每次提交的结果来更正网络。以这种方式使用 adapt 可以保证任何线性可分问题都能在提交有限次数的训练数据后得以求解。

如前几页所述,也可以用函数 train 来训练感知器。通常情况下,当使用 train 训练感知器时,它会将输入批量提交给网络,并基于各次更正的总和来更正网络。遗憾的是,目前没有证据表明这种训练算法可以使感知器训练收敛。因此,不推荐使用 train 来训练感知器。

感知器网络有若干限制。首先,由于硬限制传递函数,感知器的输出值只能取两个值之一(0 或 1)。其次,感知器只能对线性可分的向量集进行分类。如果能绘制出一条直线或一个平面将输入向量正确分类,则输入向量是线性可分的。如果向量不是线性可分的,学习将永远无法到达对所有向量都正确分类的程度。然而,事实证明,如果向量是线性可分的,自适应训练的感知器始终能在有限时间内求得解。您可能想尝试线性不可分向量。它显示尝试对非线性可分的输入向量分类的难度。

但公平来说,具有多个感知器的网络可用于求解更高难度的问题。例如,假设有一组向量(包含四个向量),您想将其分成不同组,可以绘制两条线将它们分开。我们可以找到一个双神经元网络,通过它的两个决策边界将输入分为四个类别。有关感知器的更多讨论和更复杂的感知器问题,请参阅 [HDB1996]。

离群值和归一化感知器规则

离群值输入向量的长度远远大于或小于其他输入向量,因此离群值的存在可能造成训练时间过长。应用感知器学习规则涉及到根据误差在当前权重和偏置基础上加减输入向量。因此,具有较大元素的输入向量可能导致权重和偏置的更改,而明显较小的输入向量需要很长时间才能克服这些更改。您可能想尝试离群值输入向量,查看离群值对训练的影响。

稍微更改感知器学习规则,即可让训练时间对极大或极小的离群值输入向量不太敏感。

以下是更新权重的原始规则:

Δw=(tα)pT=epT

如上所示,输入向量 p 越大,它对权重向量 w 的影响就越大。因此,如果一个输入向量明显大于其他输入向量,则较小的输入向量必须多次提交才能产生影响。

解决方法是将规则归一化,使每个输入向量对权重的影响有相同的量级:

Δw=(tα)pTp=epTp

通过函数 learnpn 来实现归一化感知器规则,该函数的调用方式与 learnp 完全相同。归一化感知器规则函数 learnpn 的执行时间稍微有所增加,但如果存在离群值输入向量,则可大大减少轮数。您可以尝试归一化感知器规则,了解归一化的训练规则的工作原理。