【深度学习】单层感知器及其Python实现

【深度学习】单层感知器及其Python实现

首先,原谅一个初学者无法给出神经网络和感知器通俗易懂的解释。在我的理解里,感知器是用来实现分类问题的,而单层感知器只能实现线性分类,对于“异或”这种线性不可分的问题,可以通过构造多层感知器,或者采用连续可微激活函数的BP神经网络来实现。

感知器介绍

在这里插入图片描述
感知器物理层结构如上图所示,包括:
输入节点
输出节点
激活函数

对于一个事物描述最重要归结到数学模型上来,感知器中的数学变量有:
输入矢量 P
目标矢量 T
网络权值 W
阈值激活函数 sgn(x)
输入节点数r
输出节点数s
偏置因子b(图中w0)

这里也存在我的一个问题:偏置因子存在的意义是什么,为什么绝大多数情况下都是1个?
(在后面的描述里,偏置因子使感知器摆脱了纯线性,具有了增量线性的性质,扩展了可分范围)

感知器最重要的是根据实际输出和目标输出对网络权值进行训练,相当于PID里面的反馈,只不过这里的反馈具有自我学习、适应的过程,这就是智能的体现。如果将PID反馈变为对Kd些参数的调整,是不是使PID也具有了某些智能控制的意思。

下面对感知器最重要的激活函数进行介绍,激活函数在某种程度上决定了它只能是个感知器而不是个BP神经网络。

1. 感知器阈值激活函数

在这里插入图片描述
在这里插入图片描述
从图中和表达式看出,不管输入量是什么、有多少个,其经过阈值激活函数处理后得到的输出只能是0/1,(当然也可以3/9,但是意义是一样的),这代表了其可以进行简单分类判断。

下面根据我自己的理解来说明为什么只能进行线性分类:

假设输入为p1、p2,对激活函数的输入为:w1p1+w2p2+b;在二维平面里描述就是:直线w1p1+w2p2+b=0将整个二维平面分为两个部分(0/1),将这个二位平面转化到以p1为X轴、p2为Y轴的坐标平面,将直线转化为斜截式为:p2=(-w1/w2)p1+(-b/w1)。

假如将输入矢量也放在同一个坐标平面中表示,这条直线的意义是不是将这些输入矢量分为两部分,那么怎么能按照我们的意愿将输入矢量准确分类,在前面我们定义了一个目标矢量T,假设T=[1 1 0 0 ],1和0意义就是表示第一组输入和第二组输入是一类,第三组输入和第四组输入为另一类,加入这时我们输入第5组数据,感知器也会给出一个值(0/1),这就是感知器的预测功能,当然要保证其准确性,需要对感知器进行深度训练。

当然会不会出现不可分的情况,肯定啊,感知器进行分类的是一个增量线性方程,所以大部分非线性数据是进行不了分类的,比如典型“异或”问题,下篇博客应该是构造多层感知器来解决异或问题

2. 学习规则

通过以上描述解释了感知器怎么进行分类的,要保证准确性需要对感知器进行训练,就是对网络权值的训练,对w1、w2、b的训练,对直线斜率和截距的调整。学习规则如下图所示:
在这里插入图片描述
W-H规则由威德罗和霍夫提出。

学习规则里面对网络权值的调整是通过误差矢量E来实现的。通过目标输出和实际输出的插值来调整网络权值,在误差上有一个权值(η)也称为学习速率,可理解为PID里面的比例常数,代表了调整的快慢。

下面来解释学习速率的重要性
学习规则的目的是降低分类误差,定义一个误差函数E=1/2[T-WP-B]^2,该误差函数具有抛物线性质,也就是说误差存在最小值,学习速率就是以不同的步长来逼近最小值,若学习速率过大,实际误差就会在极值点振荡,若使得每次实际误差构成的数列发散,那么设计的这个神经网络也就宣告失败。

对于网络权值的说明:网络取值进行计算时会有一个初始值,实验证明不同网络权值初始值对实验影响很大,直观的就是达到同样误差所用的循环次数。

了解了感知器的结构和网络权值的学习规则就可以进行感知器的设计了,第一次我只建立了单层感知器模型

单层感知器模型建立

由于是学习C/C++起步,建立的这个程序基本思路还是C的,对Python库的一些便捷操作用的不是很多,关于矩阵运算也是通过for循环做的。

用的是Python3.5 基于Pycharm

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import random

import numpy as np
import matplotlib.pyplot as plt

xh_number=1000


class My_Per:
    def __init__(self, lr=0.1):
        self.lr = lr
        self.errors = []
        self.b = random.random()
        self.w = [0.12365, -0.12569]

    def train(self, Px, t):
        update = self.lr * (t - self.predict(Px))
        self.b += update
        self.w[0] += update*Px[0]
        self.w[1] += update*Px[1]

    def predict(self, Px):
        number = self.w[0]*Px[0] + self.w[1]*Px[1] + self.b
        return np.where(number >=0, 1, 0)


def main():

    P = [[-0.5, -0.5], [-0.5, 0.5], [0.3, -0.5], [0, 1]]
    T = [1.0, 1.0, 0, 0]
    my_per = My_Per(0.1)
    plt.figure()
    plt.plot([-0.5, -0.5], [-0.5, 0.5], 'bo')
    plt.plot([0.3, 0], [-0.5, 1], 'bo')
    for i in range(xh_number):
        for i in range(4):
            Px = P[i]
            t = T[i]
            my_per.train(Px, t)
        print(-my_per.w[0]/my_per.w[1])
        print(-my_per.b/my_per.w[1])
    x = np.arange(-1, 1)
    y = -my_per.w[0]/my_per.w[1]*x-my_per.b/my_per.w[1]
    plt.plot(x, y)
    plt.show()


if __name__ == "__main__":
    main()

程序对P = [[-0.5, -0.5], [-0.5, 0.5], [0.3, -0.5], [0, 1]] 四组输入矢量分为两类目标矢量为: T = [1.0, 1.0, 0, 0]
分类结果如下图:
在这里插入图片描述

逛了CSDN两年第一次自己发博客,大家多包涵?,这次发博客是对智能系统这门课第二章部分内容的总结,传说科大10系最牛的一门课,我已经感觉到了一种神秘的力量。