感知器

感知器是Frank Rosenblatt在1957年就职于Cornell航空实验室时所发明的一种人工神经网络。它可以被视为一种最简单形式的前馈神经网络,是一种二元线性分类器。 Frank Rosenblatt给出了相应的感知机学习算法,常用的有感知机学习、最小二乘法和梯度下降法。

来源: 维基百科
简介

感知机 (英语:Perceptron)是Frank Rosenblatt在1957年就职于康奈尔航空实验室(Cornell Aeronautical Laboratory)时所发明的一种人工神经网络。它可以被视为一种最简单形式的前馈神经网络,是一种二元线性分类器。

Frank Rosenblatt给出了相应的感知机学习算法,常用的有感知机学习、最小二乘法和梯度下降法。譬如,感知机利用梯度下降法对损失函数进行极小化,求出可将训练数据进行线性划分的分离超平面,从而求得感知机模型。

感知机是生物神经细胞的简单抽象。神经细胞结构大致可分为:树突、突触、细胞体及轴突。单个神经细胞可被视为一种只有两种状态的机器——激活时为‘是’,而未激活时为‘否’。神经细胞的状态取决于从其它的神经细胞收到的输入信号量,及突触的强度(抑制或加强)。当信号量总和超过了某个阈值时,细胞体就会激活,产生电脉冲。电脉冲沿着轴突并通过突触传递到其它神经元。为了模拟神经细胞行为,与之对应的感知机基础概念被提出,如权量(突触)、偏置(阈值)及激活函数(细胞体)。

在人工神经网络领域中,感知机也被指为单层的人工神经网络,以区别于较复杂的多层感知机(Multilayer Perceptron)。作为一种线性分类器,(单层)感知机可说是最简单的前向人工神经网络形式。尽管结构简单,感知机能够学习并解决相当复杂的问题。感知机主要的本质缺陷是它不能处理线性不可分问题。

Example:

一个多层神经网络的前馈运算如下所示:

perceptron有四个部分:输入输出;权重和偏置;Net sum;激活函数。如下图所示:

  1. Input values or One input layer
  2. Weights and Bias
  3. Net sum
  4. Activation Function

它如何工作得呢?

a.输入为x并乘对应这权重w,即为k。

b.将所有相乘的值相加,并称之为加权和 Weighted Sum.

c.将加权和应用于正确的激活函数。

使用单位阶跃激活函数。Unit Step Activation Function.如下图所示:

为何需要权重和偏置:权重显示了特定节点的强度。偏差值允许将激活函数移到左边或右边。

感知器通常用于将数据分为两部分。因此,它也被称为线性二元分类器。如下图

定义

感知器使用特征向量来表示的前馈神经网络,它是一种二元分类器,把矩阵上的输入x(实数值向量)映射到输出值f(x)上(一个二元的值)。

f(x)=[\begin{cases}

1, \if: w\times\x+b>0\\

0, \else:\\

\end{cases} \] 是实数的表示权重的向量,是点积。是偏置,一个不依赖于任何输入值的常数。偏置可以认为是激励函数的偏移量,或者给神经元一个基础活跃等级。

(0或1)用于对进行分类,看它是肯定的还是否定的,这属于二元分类问题。如果是负的,那么加权后的输入必须产生一个肯定的值并且大于,这样才能令分类神经元大于阈值0。从空间上看,偏置改变了决策边界的位置(虽然不是定向的)。

由于输入直接经过权重关系转换为输出,所以感知器可以被视为最简单形式的前馈式人工神经网络。

结构

设有n维输入的单个感知机(如下图示),a1至an为n维输入向量的各个分量,w1至wn, 为各个输入分量连接到感知机的权量(或称权值),b为偏置,f为激活函数(又曰激励函数或传递函数),t为向量输出。输出的数学描述为:

其中

及f(x)为反对称的符号函数,其定义为:

从式( 1 )可知,偏置被引申为权量,而对应的输入值为1。故,感知机的输出行为是求得输入向量与权向量的内积后,经一个线性激活函数所得一个标量结果。

设输入向量与权向量的内积为零,可得出维的超平面。平面的法向量为,并经过(n+1)维输入空间的原点。法向量指向的输入空间,其输出值为+1,而与法向量反向的输入空间,其输出值则为-1。故可知这个超平面定义了决策边界,并把输入空间划分为二。

准则函数

设一训练集为D={(p1,t1),...(pm,tm)},其中pi表示输入,而是对应的ti目标输出。由于符号函数的不连续性,如果采用标准的均方误差,所得误差函数必然是不连续的,因而基于梯度的学习算法也就不能被使用。为此,Rosenblatt提出了感知机准则函数:

其中M是被当前w错误分类的的输入向量集合。当(w^T)pi>=0时,ti为-1,而当(w^T)pi<0时,ti为1。故,误差函数E(w)是一组正数的和,又或当训练集里所有输入都被正确分类时,等于零。

学习算法

学习算法对于所有的神经元都是一样的,因此下面各项都是独立的应用于每个神经元。一些变量的定义如下:

  • x(j)表示n维输入向量中的第j项
  • w(j)表示权重向量的第j项
  • f(x)表示神经元接受输入产生的输出
  • α是一个常数,符合0<α<=1(接受率)

更进一步,为了简便我们假定b偏置量等于0。因为一个额外的维n+1维,可以用x(n+1)=1的形式加到输入向量,这样我们就可以用w(n+1)代替偏置量。

感知器的学习通过对所有训练实例进行多次的迭代进行更新的方式来建模。令D={(x1,y1),...(xm,ym)}表示一个有m个实例的训练集。

每次迭代权重向量以如下方式更新:

对于每个D={(x1,y1),...(xm,ym)}中每个(x,y)

w(j):=w(j)+\alpha(y-f(x))x(j)   (j=1,...,n)

这意味着,仅当针对给定训练实例(x,y)产生的输出值f(x)与预期的输出值y不同时,权重向量才会发生改变。

如果存在一个正的常数γ和权重向量w,对所有i的满足yi*(<w,xi>+b)>γ,训练集Dm就被叫被做线性分隔的(并把输入空间划分为二)。Novikoff(1962)证明如果训练集是线性分隔的,那么感知器算法可以在有限次迭代后收敛,错误的数量由(2R/γ)^2限定,其中R为输入向量的最大平均值。然而,如果训练集不是线性分隔的,那么这个算法则不能确保会收敛。

https://zh.wikipedia.org/wiki/%E6%84%9F%E7%9F%A5%E5%99%A8

【来源:DATASCIENCE; URL: https://towardsdatascience.com/what-the-hell-is-perceptron-626217814f53

Code:

import numpy as np
import matplotlib.pyplot as plt
class perceptron:
 def __init__(self,dimension):
     #size=the dimension of x
     self.dimension=dimension
     self.w=np.zeros(dimension+1)
 def cyclic_train(self,X,Y):
     #X should be a list including the tuples storing the inputing x
     w=self.w 
     train_set_size=len(X)
     X=np.array([list(x)+[1] for x in X])
     error_loc=range(train_set_size)
     while i!=error_loc: 
     #correct and record the last error location
     if sign(np.dot(X[i],w))!=Y[i]:
     w=w+X[i]*Y[i]
     error_loc=i
     #move on and iterate cyclic
     if i<train_set_size-1:
     else:
     self.w=w
 def predict(self,x):
 return sign(np.dot(self.w,x))
def sign(x):
     if x>0:
     return 1
     elif x<0:
     return -1
     else:
     return 0
#The prepared test data
Xtr=[(2,4),(-1,2),(-3,-1),(-1,-4),(1,-1),(2,-4)]
Ytr=[1,1,1,-1,-1,-1]
#test
p1=perceptron(2)
p1.cyclic_train(Xtr,Ytr)
fig=plt.figure()
axes=fig.add_axes([0.1, 0.1, 0.8, 0.8])
axes.scatter([Xtr[i][0] for i in range(len(Xtr)) if Ytr[i]==1],\
 [Xtr[i][1] for i in range(len(Xtr)) if Ytr[i]==1],color='red')
axes.scatter([Xtr[i][0] for i in range(len(Xtr)) if Ytr[i]==-1],\
 [Xtr[i][1] for i in range(len(Xtr)) if Ytr[i]==-1],color='blue')
k=-p1.w[0]/p1.w[1]
b=-p1.w[2]/p1.w[1] 
axes.plot([-10,10],[k*(-10)+b,k*10+b])
axes.spines['right'].set_color('none')
axes.spines['top'].set_color('none')
axes.xaxis.set_ticks_position('bottom')
axes.spines['bottom'].set_position(('data',0))
#set position of x spine to x=0