感知器的原理及代码实现

  • 前言
  • 感知器数学模型
  • 感知器实验代码
  • 总结


前言

感知器是机器学习的入门算法,同时也是将来可能学习深度学习的基础。感知器是模拟神经元工作模式的一种简单机器学习算法。神经元模型是神经网络方面研究的一种模型。西瓜书中对于神经网络的定义为

  神经网络是由具有适应性的简单单元组成的广泛并进行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。

  生物原理:每个神经元都接受来自其他神经原的化学信号,接受的化学信号能改变本神经元的电压,当神经元的电压超过预定的阈值时就会表现出兴奋的状态。

   1943年McCulloch与Pitts将其生物原理抽象为简单的M-P神经元模型。如下图所示,图片来自于网络。

感知器神经网络实验报告 感知器网络原理_机器学习

感知器数学模型

感知器的M-P模型如上图所示,其中感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_02为外界输入,感知器神经网络实验报告 感知器网络原理_机器学习_03为其对应的权重。其中感知器神经网络实验报告 感知器网络原理_神经网络_04感知器神经网络实验报告 感知器网络原理_机器学习_05,即对外界输入与对应权重乘积和。感知器神经网络实验报告 感知器网络原理_神经网络_06为阈值,将其一起输入到激活函数中,获得最终的输出。
感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_07
其中激活函数有阶跃函数感知器神经网络实验报告 感知器网络原理_神经网络_08

同时也有sigmoid函数感知器神经网络实验报告 感知器网络原理_神经网络_09
由于阶跃函数感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_10不连续以及不光滑,所以一般都是选择感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_11函数。
在代码的实现中,为了简便起见,我们将阈值感知器神经网络实验报告 感知器网络原理_神经网络_06符号改写为感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_13,同时设定感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_14。上述输出公式感知器神经网络实验报告 感知器网络原理_感知器神经网络实验报告_07转变为感知器神经网络实验报告 感知器网络原理_感知器_16

最后对与感知器网络模型的调整。其中对于权重感知器神经网络实验报告 感知器网络原理_算法_17的调整感知器神经网络实验报告 感知器网络原理_感知器_18
感知器神经网络实验报告 感知器网络原理_神经网络_19
其中感知器神经网络实验报告 感知器网络原理_机器学习_20为学习率。当输出的预测值与实际值相等时,即感知器神经网络实验报告 感知器网络原理_神经网络_21时,不对相应权重做出调整,否则调整权重。学习率感知器神经网络实验报告 感知器网络原理_机器学习_20可以设定为固定值,也可以使用梯度下降法最终寻找到合适的学习率感知器神经网络实验报告 感知器网络原理_机器学习_20

通过不断的对原始数据进行训练,不断调整感知器的权重,使之适合该数据集,最终可以得到一个比较不错的感知器模型。然而对于模型的训练,并不是次数越多越好,训练次数过多容易造成过拟合现象。过拟合现象详见其他资料,不展开赘述。

感知器实验代码

#code by young_monkeysun 2019
import csv
from datetime import datetime
import random
import numpy 
import pandas
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
'''
     背景:
            一 通过随机生成数据样本集来训练感知器网络
            二 感知器网络对实际样本数据的拟合程度与训练样本有关
                    1 训练样本的数量越多,感知器网络越能够逼近实际对应的线性超平面,但是并不是训练样本数量越多越好。
                      当训练样本数量足够多时,就已经能以很小的误差拟合真实的线性超平面,如果要进一步缩小误差,
                      所花费的代价将变得更大

                    2 训练样本的质量。感知器网络对实际的线性超平面拟合程度也与训练样本质量有关。
                      训练样本质量越好越能够拟合实际线性超平面。

                    3 学习率 eta 。感知器网络也与学习率有关。学习率越小,对实际线性超平面拟合的误差越小,但是其学习的
                      时间也越长。学习率越大,对实际线性超平面拟合的误差越大,但是其学习的时间越快。

                    4 训练次数。感知器网络的拟合程度和训练次数有关。训练次数本质上和训练样本数量的性质一样。在每次训练的
                      实际样本集中,实际对感知器网络产生影响的是那些感知器预测错误的样本。因为只有当预测错误了才能产生误差
                      对多感知器网络的权值进行调整,从而拟合实际对应的线性超平面。训练样本越多预测样本的数量也就越多,越能
                      成功拟合,在每次训练中都是对上一次出错的样本数据进行训练宠幸调整权值。
            三 补充
                    在该程序生成的数据集中,数据 X 和 Y 都是高精度浮点数 , 由(X , Y)组成的点几乎可以认为是不重复的。
                    所以可以不用对数据集进行预处理。
'''
#生成随机数据样本
def creat_csvfile():
    datestr = (datetime.now()).strftime("%d%b%Y-%H%M%S")
    
    file = 'data-' + datestr +'.csv'
    with open(file,'w',newline="") as f:
        csv_write = csv.writer(f)
        csv_head = ['x','y','target']
        csv_write.writerow(csv_head)
    return file

def write_csvfile(fileName,data):
    with open(fileName,'a+',newline="") as f:
        csv_write = csv.writer(f)
        csv_write.writerows(data)


def set_function(x,y):
    result = 0.5*x+1-y
    return result



def produce_data(sumN):
    lists=[]
    for N in range(0,sumN):
        lis=[]
        x = random.uniform(0,10)
        y = random.uniform(0,10)
        if set_function(x,y) > 0 :
            lis.extend((x,y,'true'))
        else:
            lis.extend((x,y,'false'))
        lists.append(lis)

    return lists

def trans_attribute(arr):
    arrs=[]
    for element in arr:
        if str(element) == 'True':
            arrs.append(1)
        else:
            arrs.append(-1)
    return arrs

#感知器算法
'''
感知器算法:                    1   target>0    
            y = sign(target)= 
                              -1   target<0
            target = W1*X1+.....+Wn*Xn+W0
            sign(target)为激活函数,对输出节点的输出值进行二分判断
            W0为偏置因子
            W[i] i in range(1,n+1)  :为各节点到输出节点的权值
            将target改写为:
                            target = W0*X0+W1*X1+....+Wn*Xn=W*X
            其中将X0=1
            权值更行公式:Wj(k+1)=Wj(k)+eta*update*Xij
                        n 
感知器算法过程:输入训练样本数据通过感知器所得到的结果与训练样本数据的实际结果得到误差值Update,
通过Update不断修改感知器的输入权值,直到将感知器调整到
能够大部分拟合训练数据。
            
'''
class proceptron(object):
    def __init__(self , eta, n_iter):
        self.eta = eta          #学习率
        self.iter=n_iter        #训练样本次数
    
    def fit(self , x , y):
        self.w = numpy.zeros(x.shape[1]+1)
        self.errors=[]
        for i in range(self.iter):
            error = 0
            for xi , target in zip(x,y):
                correction = self.eta*(target-self.prodict(xi))
                self.w[1:]+=correction*xi
                self.w[0]+=correction
                error += int(correction != 0.0) 
            self.errors.append(error)
        
    def net_input(self,x):
        return numpy.dot(x,self.w[1:]) + self.w[0]

    def prodict(self,x):
        return numpy.where(self.net_input(x) >= 0 , 1 , -1)
            
#生成样本文件
fileName=creat_csvfile()
lis=produce_data(200)
write_csvfile(fileName,lis)

#读取数据文件,取出数据
read_file = pandas.read_csv(fileName,header=0)
target= read_file.loc[0:200,'target'].values
target=trans_attribute(target)
x = read_file.iloc[0:200,[0,1]].values
x_train=x[0:100]
target_train=target[0:100]
x_test=x[100:200]
target_test=target[100:200]

#画出训练样本图像
for i in range(len(target_train)):
    if target_train[i] > 0 :
        plt.scatter(x[i,0],x[i,1],color = 'black',marker='o')
    else:
        plt.scatter(x[i,0],x[i,1],color = 'red',marker='x')

plt.xlabel('x轴')
plt.ylabel('y轴')
plt.title('训练样本图像')
plt.legend(loc='uper left')
plt.show()


#训练模型
procept= proceptron(eta = 0.06 , n_iter =10)
procept.fit(x_train,target_train)


#画出错误次数折线图
plt.plot(range(1,len(procept.errors)+1),procept.errors,marker='x')
plt.xlabel("次数")
plt.ylabel("错误次数")
plt.title('单次训练错误次数折线图')
plt.show()


#画出所有训练样本图像与训练求得的超平面
x1 = 0
y1 = -1 / procept.w[2] * (procept.w[0] * 1 + procept.w[1] * x1)
x2 =11
y2 = -1 / procept.w[2] * (procept.w[0] * 1 + procept.w[1] * x2)
for i in range(len(target)):
    if target[i] > 0 :
        plt.scatter(x[i,0],x[i,1],color = 'black',marker='o')
    else:
        plt.scatter(x[i,0],x[i,1],color = 'red',marker='x')
plt.plot([x1,x2], [y1,y2],'r')
plt.xlabel('x轴')
plt.ylabel('y轴')
plt.legend(loc = 'upper left')
plt.show()

#测试训练结果
correctN = 0
wrongN = 0
for i in  range(100):
    consequet = procept.prodict(x_test[i])*target_test[i]
    if consequet > 0:
        correctN+=1
    else:
        wrongN+=1
cor_and_wrong=[]
cor_and_wrong.append(correctN)
cor_and_wrong.append(wrongN)
label=["正确","错误"]
plt.pie(cor_and_wrong,labels=label,autopct="%.2f%%")
plt.title('错误率饼图')
plt.legend(loc="best")
plt.show()

总结

感知器模型作为机器学习最最基础的算法,是后面要学习的BP算法与将来可能要学习的深度学习的基础。对于入坑机器学习有着重要的启蒙意义,反正对我是这样。