单层感知器属于单层前向网络,即除输入层和输出层之外,只拥有一层神经元节点。
特点:输入数据从输入层经过隐藏层向输出层逐层传播,相邻两层的神经元之间相互连接,同一层的神经元之间没有连接。
感知器(perception)是由美国学者F.Rosenblatt提出的。与最早提出的MP模型不同,神经元突触权值可变,因此可以通过一定规则进行学习。可以快速、可靠地解决线性可分的问题。
1.单层感知器的结构
单层感知器由一个线性组合器和一个二值阈值元件组成。输入向量的各个分量先与权值相乘,然后在线性组合器中进行叠加,得到一个标量结果,其输出是线性组合结果经过一个二值阈值函数。二值阈值元件通常是一个上升函数,典型功能是非负数映射为1,负数映射为0或负一。
输入是一个N维向量 x=[x1,x2,...,xn],其中每一个分量对应一个权值wi,隐含层输出叠加为一个标量值:
随后在二值阈值元件中对得到的v值进行判断,产生二值输出:
可以将数据分为两类。实际应用中,还加入偏置,值恒为1,权值为b。这时,y输出为:
把偏置值当作特殊权值:
单层感知器结构图:
单层感知器进行模式识别的超平面由下式决定:
当维数N=2时,输入向量可以表示为平面直角坐标系中的一个点。此时分类超平面是一条直线:
这样就可以将点沿直线划分成两类。
2.单层感知器的学习算法
(1)定义变量和参数,这里的n是迭代次数。N是N维输入,将其中的偏置也作为输入,不过其值恒为1,。
x(n)=N+1维输入向量=[+1,x1(n),x2(n),...,xN(n)]T
w(n)=N+1维权值向量=[b(n),w1(n),w2(n),...,wN(n)]T
b(n)=偏置
y(n)=实际输出
d(n)=期望输出
η(n)=学习率参数,是一个比1小的正常数
所以线性组合器的输出为:v(n)=wT(n)x(n)
(2)初始化。n=0,将权值向量w设置为随机值或全零值。
(3)激活。输入训练样本,对每个训练样本x(n)=[+1,x1(n),x2(n),...,xN(n)]T,指定其期望输出d。即若x属于l1,则d=1,若x属于l2,则d属于-1(或者0)。
(4)计算实际输出。 y(n)=sgn(wT(n)x(n))
(5)更新权值向量 w(n+1)=w(n)+η[d(n)-y(n)]x(n)
感知器的学习规则:学习信号等于神经元期望输出与实际输出之差:
dj为期望的输出,oj为实际的输出。
W代表特征向量或者是矩阵。T代表矩阵的转置,X为输入信号或者是训练数据集中的一个m维样本
权值调整公式为:
公式右侧前面的符号η为学习率。权值调整值为学习率乘以误差乘以输入信号(X)。
当实际输出与期望值相同时,权值不需要调整,在有误差存在的情况下,并且期望值与实际输出值为1或者-1,此时权值调整公式可以简化为:
每一次带入一个数据进行迭代,而不是带入所有数据(为什么呢,感觉带入所有数据进行运算会好很多)其实也能一批数据全部直接带入计算,只不过现在的我还不太懂为什么可以这样。此问题留待日后回答。
这里
0<η<1
为什么权值更新会是这样呢:我认为可以这样理解,当y与d相同时,分类正确,权值不用更新。
当y与d不同时 : w(n+1)*x(n)=w(n)*x(n)+η[d(n)-y(n)]x(n)*x(n) 即当d为1而y为-1时,整体的w(n+1)*x(n)增加的,当d为-1而y为1时,整体是往-1的方向变化的,所以w(n+1)权值会使得整体往正确的分类走。但是这个权值更新公式怎么来的就还不知道了。
(6)判断。若满足收敛条件,则算法结束,若不满足,n++,转到第(3)步。
收敛条件:当权值向量w已经能正确实现分类时,算法就收敛了,此时网络误差为零。收敛条件通常可以是:
误差小于某个预先设定的较小的值ε。即
|d(n)-y(n)|<ε
两次迭代之间的权值变化已经很小,即
|w(n+1)-w(n)|<ε
为防止偶然因素导致的提取收敛,前两个条件还可以改进为连续若干次误差或者权值变化小于某个值。
设定最大迭代次数M,当迭代了M次就停止迭代。
需事先通过经验设定学习率η,不应该过大,以便为输入向量提供一个比较稳定的权值估计。不应过小,以便使权值能根据输入的向量x实时变化,体现误差对权值的修正作用。
- 学习率大于零,小于等于1
- 学习率太大,容易造成权值调整不稳定。
- 学习率太小,权值调整太慢,迭代次数太多。
- 学习率一般选择可变的,类似于调节显微镜一样,开始时步长比较大,后面步长比较小。
它只对线性可分的问题收敛,通过学习调整权值,最终找到合适的决策面,实现正确分类。
单层感知器在研究线性不可分的问题中:可以追求尽量正确的分类,定义一个误差准则,在不同的超平面中选择一个最优超平面,,使得误差最小,实现近似分类。
3.感知器的局限性
- 感知器的激活函数使用阈值函数,使得输出只能取两个值(-1/1或0/1)
- 只对线性可分的问题收敛
- 如果输入样本存在奇异样本,则网络需要花费很长时间。(奇异样本是数值上远远偏离其他样本的数据)
- 感知器的学习算法只对单层有效,因此无法套用其规则设计多层感知器。
- 感知器算法的另一个缺陷是,一旦所有样本均被正确分类,它就会停止更新权值,这看起来有些矛盾。直觉告诉我们,具有大间隔的决策面比感知器的决策面具有更好的分类误差。但是诸如“Support Vector Machines”之类的大间隔分类器不在本次讨论范围。
4.Python实现感知器算法
这是整个感知器算法的代码
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
import pandas as pd
def sgn(x): # the sgn function
if x >= 0:
return 1
else:
return -1
class Perception(object):
"""
This is the Perception of the Neural Newtork.
"""
def __init__(self, input_data, input_label, learning_rate=0.2, iter_times=500):
"""
:param input_data: is the np.array type and size is [m, n] which m is number of data, n is the length of a input
:param input_label: is the np.array type and size is [1, m] which m is number of data
:param learning_rate:
:param iter_times:
"""
self.datas = np.ones((input_data.shape[0], input_data.shape[1] + 1))
self.datas[:, 1:] = input_data
self.label = input_label
self.weights = (np.random.random(size=self.datas.shape[1]) - 0.5) * 2 # make the weights in [-1, 1]
self.learning_rate = learning_rate
self.iter_times = iter_times
for i in range(self.iter_times):
for j in range(self.datas.shape[0]):
y = sgn(np.dot(self.datas[j], self.weights.T))
new_w = self.weights + learning_rate * (self.label[j] - y) * self.datas[j]
self.weights = new_w
if sum(abs(self.label - [sgn(x) for x in np.dot(self.datas, self.weights)])) <= 0.0001: # if |y(n) - d(n)| <=0.00001, break
print("The perception is work well!")
break
print("The real data output: ", self.label)
print("Perception output is :", np.array([sgn(x) for x in np.dot(self.datas, self.weights.T)]))
用感知器算法实际处理一些数据
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
import pandas as pd
def sgn(x): # the sgn function
if x >= 0:
return 1
else:
return -1
class Perception(object):
"""
This is the Perception of the Neural Newtork.
I implement the algorithm by myself.
"""
def __init__(self, input_data, input_label, learning_rate=0.2, iter_times=500):
"""
:param input_data: is the np.array type and size is [m, n] which m is number of data, n is the length of a input
:param input_label: is the np.array type and size is [1, m] which m is number of data
:param learning_rate:
:param iter_times:
"""
self.datas = np.ones((input_data.shape[0], input_data.shape[1] + 1))
self.datas[:, 1:] = input_data
self.label = input_label
self.weights = (np.random.random(size=self.datas.shape[1]) - 0.5) * 2 # make the weights in [-1, 1]
self.learning_rate = learning_rate
self.iter_times = iter_times
for i in range(self.iter_times):
for j in range(self.datas.shape[0]):
y = sgn(np.dot(self.datas[j], self.weights.T))
new_w = self.weights + learning_rate * (self.label[j] - y) * self.datas[j]
self.weights = new_w
if sum(abs(self.label - [sgn(x) for x in np.dot(self.datas, self.weights)])) <= 0.0001: # if |y(n) - d(n)| <=0.00001, break
print("The perception is work well!")
break
print("The real data output: ", self.label)
print("Perception output is :", np.array([sgn(x) for x in np.dot(self.datas, self.weights.T)]))
def main():
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)
X = df.iloc[0:100, [0, 2]].values
Perception(X, y)
if __name__ == '__main__':
main()
代码还有许多不完善的第一,以后还会一直改进。