Python语言编写BP神经网络

2016年10月31日 16:42:44 ldy944758217 阅读数 3135
 

人工神经网络是一种经典的机器学习模型,随着深度学习的发展神经网络模型日益完善.

联想大家熟悉的回归问题, 神经网络模型实际上是根据训练样本创造出一个多维输入多维输出的函数, 并使用该函数进行预测, 网络的训练过程即为调节该函数参数提高预测精度的过程.神经网络要解决的问题与最小二乘法回归解决的问题并无根本性区别.

回归和分类是常用神经网络处理的两类问题, 如果你已经了解了神经网络的工作原理可以在http://playground.tensorflow.org/上体验一个浅层神经网络的工作过程.

感知机(Perceptron)是一个简单的线性二分类器, 它保存着输入权重, 根据输入和内置的函数计算输出.人工神经网络中的单个神经元即是感知机.

在前馈神经网络的预测过程中, 数据流从输入到输出单向流动, 不存在循环和返回的通道.

目前大多数神经网络模型都属于前馈神经网络, 在下文中我们将详细讨论前馈过程.

多层感知机(Multi Layer Perceptron, MLP)是由多个感知机层全连接组成的前馈神经网络, 这种模型在非线性问题中表现出色.

所谓全连接是指层A上任一神经元与临近层B上的任意神经元之间都存在连接.

反向传播(Back Propagation,BP)是误差反向传播的简称,这是一种用来训练人工神经网络的常见算法, 通常与最优化方法(如梯度下降法)结合使用.

本文介绍的神经网络模型在结构上属于MLP, 因为采用BP算法进行训练, 人们也称其为BP神经网络.

BP神经网络原理

经典的BP神经网络通常由三层组成: 输入层, 隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同, 隐含层的层数与神经元数均可以自定义.

每个神经元代表对数据的一次处理:

每个隐含层和输出层神经元输出与输入的函数关系为:

 
 

其中

表示神经元i与神经元j之间连接的权重,

代表神经元j的输出, sigmod是一个特殊的函数用于将任意实数映射到(0,1)区间.

上文中的sigmod函数称为神经元的激励函数(activation function), 除了sigmod函数

外, 常用还有tanh和ReLU函数.

我们用一个完成训练的神经网络处理回归问题, 每个样本拥有n个输入.相应地,神经网络拥有n个输入神经元和1个输出神经元.

实际应用中我们通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正;或者为每个隐含层神经元设置一个偏置参数.

我们将n个特征依次送入输入神经元, 隐含层神经元获得输入层的输出并计算自己输出值, 输出层的神经元根据隐含层输出计算出回归值.

上述过程一般称为前馈(Feed-Forward)过程, 该过程中神经网络的输入输出与多维函数无异.

现在我们的问题是如何训练这个神经网络.

作为监督学习算法,BP神经网络的训练过程即是根据前馈得到的预测值和参考值比较, 根据误差调整连接权重

的过程.

训练过程称为反向传播过程(BackPropagation), 数据流正好与前馈过程相反.

首先我们随机初始化连接权重

, 对某一训练样本进行一次前馈过程得到各神经元的输出.

首先计算输出层的误差:

 

其中

代表神经元j的误差,表示神经元j的输出, 表示当前训练样本的参考输出,

是上文sigmod函数的一阶导数.

计算隐含层误差:

 

隐含层输出不存在参考值, 使用下一层误差的加权和代替

.

计算完误差后就可以更新

:

 

其中

是一个称为学习率的参数,一般在(0,0.1)区间上取值.

实际上为了加快学习的效率我们引入称为矫正矩阵的机制, 矫正矩阵记录上一次反向传播过程中的

值, 这样

更新公式变为:

 

是一个称为矫正率的参数.随后更新矫正矩阵:

 

每一个训练样本都会更新一次整个网络的参数.我们需要额外设置训练终止的条件.

最简单的训练终止条件为设置最大迭代次数, 如将数据集迭代1000次后终止训练.

单纯的设置最大迭代次数不能保证训练结果的精确度, 更好的办法是使用损失函数(loss function)作为终止训练的依据.

损失函数可以选用输出层各节点的方差:

 

为了避免神经网络进行无意义的迭代, 我们通常在训练数据集中抽出一部分用作校验.当预测误差高于阈值时提前终止训练.

Python实现BP神经网络

首先实现几个工具函数:

  1.  
    def rand(a, b):
  2.  
    return (b - a) * random.random() + a
  3.  
     
  4.  
     
  5.  
    def make_matrix(m, n, fill=0.0): # 创造一个指定大小的矩阵
  6.  
    mat = []
  7.  
    for i in range(m):
  8.  
    mat.append([fill] * n)
  9.  
    return mat

定义sigmod函数和它的导数:

  1.  
    def sigmoid(x):
  2.  
    return 1.0 / (1.0 + math.exp(-x))
  3.  
     
  4.  
     
  5.  
    def sigmod_derivate(x):
  6.  
    return x * (1 - x)

定义BPNeuralNetwork类, 使用三个列表维护输入层,隐含层和输出层神经元, 列表中的元素代表对应神经元当前的输出值.使用两个二维列表以邻接矩阵的形式维护输入层与隐含层, 隐含层与输出层之间的连接权值, 通过同样的形式保存矫正矩阵.

定义setup方法初始化神经网络:

  1.  
    def setup(self, ni, nh, no):
  2.  
    self.input_n = ni + 1
  3.  
    self.hidden_n = nh
  4.  
    self.output_n = no
  5.  
    # init cells
  6.  
    self.input_cells = [1.0] * self.input_n
  7.  
    self.hidden_cells = [1.0] * self.hidden_n
  8.  
    self.output_cells = [1.0] * self.output_n
  9.  
    # init weights
  10.  
    self.input_weights = make_matrix(self.input_n, self.hidden_n)
  11.  
    self.output_weights = make_matrix(self.hidden_n, self.output_n)
  12.  
    # random activate
  13.  
    for i in range(self.input_n):
  14.  
    for h in range(self.hidden_n):
  15.  
    self.input_weights[i][h] = rand(-0.2, 0.2)
  16.  
    for h in range(self.hidden_n):
  17.  
    for o in range(self.output_n):
  18.  
    self.output_weights[h][o] = rand(-2.0, 2.0)
  19.  
    # init correction matrix
  20.  
    self.input_correction = make_matrix(self.input_n, self.hidden_n)
  21.  
    self.output_correction = make_matrix(self.hidden_n, self.output_n)

定义predict方法进行一次前馈, 并返回输出:

  1.  
    def predict(self, inputs):
  2.  
    # activate input layer
  3.  
    for i in range(self.input_n - 1):
  4.  
    self.input_cells[i] = inputs[i]
  5.  
    # activate hidden layer
  6.  
    for j in range(self.hidden_n):
  7.  
    total = 0.0
  8.  
    for i in range(self.input_n):
  9.  
    total += self.input_cells[i] * self.input_weights[i][j]
  10.  
    self.hidden_cells[j] = sigmoid(total)
  11.  
    # activate output layer
  12.  
    for k in range(self.output_n):
  13.  
    total = 0.0
  14.  
    for j in range(self.hidden_n):
  15.  
    total += self.hidden_cells[j] * self.output_weights[j][k]
  16.  
    self.output_cells[k] = sigmoid(total)
  17.  
    return self.output_cells[:]

定义back_propagate方法定义一次反向传播和更新权值的过程, 并返回最终预测误差:

  1.  
    def back_propagate(self, case, label, learn, correct):
  2.  
    # feed forward
  3.  
    self.predict(case)
  4.  
    # get output layer error
  5.  
    output_deltas = [0.0] * self.output_n
  6.  
    for o in range(self.output_n):
  7.  
    error = label[o] - self.output_cells[o]
  8.  
    output_deltas[o] = sigmod_derivate(self.output_cells[o]) * error
  9.  
    # get hidden layer error
  10.  
    hidden_deltas = [0.0] * self.hidden_n
  11.  
    for h in range(self.hidden_n):
  12.  
    error = 0.0
  13.  
    for o in range(self.output_n):
  14.  
    error += output_deltas[o] * self.output_weights[h][o]
  15.  
    hidden_deltas[h] = sigmod_derivate(self.hidden_cells[h]) * error
  16.  
    # update output weights
  17.  
    for h in range(self.hidden_n):
  18.  
    for o in range(self.output_n):
  19.  
    change = output_deltas[o] * self.hidden_cells[h]
  20.  
    self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]
  21.  
    self.output_correction[h][o] = change
  22.  
    # update input weights
  23.  
    for i in range(self.input_n):
  24.  
    for h in range(self.hidden_n):
  25.  
    change = hidden_deltas[h] * self.input_cells[i]
  26.  
    self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
  27.  
    self.input_correction[i][h] = change
  28.  
    # get global error
  29.  
    error = 0.0
  30.  
    for o in range(len(label)):
  31.  
    error += 0.5 * (label[o] - self.output_cells[o]) ** 2
  32.  
    return error

定义train方法控制迭代, 该方法可以修改最大迭代次数, 学习率

, 矫正率

三个参数.

  1.  
    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
  2.  
    for i in range(limit):
  3.  
    error = 0.0
  4.  
    for i in range(len(cases)):
  5.  
    label = labels[i]
  6.  
    case = cases[i]
  7.  
    error += self.back_propagate(case, label, learn, correct)

编写test方法,演示如何使用神经网络学习异或逻辑:

  1.  
    def test(self):
  2.  
    cases = [
  3.  
    [0, 0],
  4.  
    [0, 1],
  5.  
    [1, 0],
  6.  
    [1, 1],
  7.  
    ]
  8.  
    labels = [[0], [1], [1], [0]]
  9.  
    self.setup(2, 5, 1)
  10.  
    self.train(cases, labels, 10000, 0.05, 0.1)
  11.  
    for case in cases:
  12.  
    print(self.predict(case))

完整源代码参见bpnn.py

Python语言编写BP神经网络的更多相关文章

  1. 运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程,把获取的信息存入数据库

    运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程 有关前两篇的链接: 运用Python语言编写获取Linux基本系统信息(一):获得Linux版本.内核.当前时间 运 ...

  2. 运用Python语言编写获取Linux基本系统信息(二):文件系统使用情况获取

    本文跟着上一篇文章继续写,上一篇文章的链接 运用Python语言编写获取Linux基本系统信息(一):获得Linux版本.内核.当前时间 一.随便说说 获取文件系统使用情况的思路和上一篇获取主要系统是 ...

  3. 利用c++编写bp神经网络实现手写数字识别详解

    利用c++编写bp神经网络实现手写数字识别 写在前面 从大一入学开始,本菜菜就一直想学习一下神经网络算法,但由于时间和资源所限,一直未展开比较透彻的学习.大二下人工智能课的修习,给了我一个学习的契机. ...

  4. 运用Python语言编写获取Linux基本系统信息(一):获得Linux版本、内核、当前时间

    申请博客有一段时间了,然而到现在还一篇没有写过..... 主要因为没有想到需要写些什么,最近在学习Python语言,照着书上看了看最基础的东西,发现根本看不进去,而且光看的话今天看了觉得都理解懂了,过 ...

  5. python手写bp神经网络实现人脸性别识别1.0

    写在前面:本实验用到的图片均来自google图片,侵删! 实验介绍 用python手写一个简单bp神经网络,实现人脸的性别识别.由于本人的机器配置比较差,所以无法使用网上很红的人脸大数据数据集(如lf ...

  6. Python实现——二层BP神经网络

    2019/4/23更新 下文中的正确率极高是建立在仅有50组训练数据的基础上的,十分不可靠.建议使用提供的另一个生成训练集的generate_all函数,能产生所有可能结果,更加可靠. 2019/4/ ...

  7. 用python语言编写网络爬虫

    本文主要用到python3自带的urllib模块编写轻量级的简单爬虫.至于怎么定位一个网页中具体元素的url可自行百度火狐浏览器的firebug插件或者谷歌浏览器的自带方法. 1.访问一个网址 re= ...

  8. Python语言编写脚本时,对日期控件的处理方式

    对日期控件,日期控件的输入控一般是不能手动输入的:把readonly属性去掉就好 其实很简单,我们不去搞时间日期空间,我们把它当成一个普通的input框处理就好了! 但是,很多此类型input框都是禁 ...

  9. 使用python语言编写脚本控制freeswitch总结

    1.  在Linux环境下已经安装了freeswitch,(没安装freeswitch,请安装说明文档) 2.  进入源代码目录 cd  libs/esl 目录下 首先安装 yum install p ...

随机推荐

  1. 基于web站点的xss攻击

    XSS(Cross Site Script),全称跨站脚本攻击,为了与 CSS(Cascading Style Sheet) 有所区别,所以在安全领域称为 XSS. XSS 攻击,通常指黑客通过 HT ...

  2. Newtonsoft.Json.Linq.JObject 遍历验证每个属性内容

    业务需求,拦截器验证每个请求inputstream(实际是application/json流)的数据,但是json反序列化实体格式不同. var req = filterContext.Request ...

  3. Radmin入侵

    Radmin (Remote Administrator)是一款屡获殊荣的远程控制软件,它将远程控制.外包服务组件.以及网络监控结合到一个系统里,提供目前为止最快速.强健而安全的工具包 生成被控端 点 ...

  4. 如何自行给指定的SAP OData服务添加自定义日志记录功能

    有的时候,SAP标准的OData实现或者相关的工具没有提供我们想记录的日志功能,此时可以利用SAP系统强大的扩展特性,进行自定义日志功能的二次开发. 以SAP CRM Fiori应用"My ...

  5. python 中json和字符串互相转换

      string =" {  "status": "error",  "messages": ["Could not f ...

  6. day 02 预科

    目录 什么是编程语言 什么是编程 为什么要编程 计算机的组成 CPU 存储器 I/O设备(Input/Output设备) 输入设备 输出设备 多核CPU 32位和64位 机械硬盘工作原理 机械手臂 磁 ...

  7. xshell 连接出现 The remote SSH server rejected X11 forwarding request

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ yum install xorg-x11-xauth 同时sshd的config文件开启X11Forwarding yes vim ...

  8. ondblclick和dblclick区别

    1.ondblclick是一个HTML DOM Event 对象,没有jquery也可以触发这个事件的,使用方法例如 1 <button ondblclick="xxx"&g ...

  9. bat文件中运行python脚本方法

    在脚本中使用start命令: @echo off start python xxx.py 注: start命令:启动单独的“命令提示符”窗口来运行指定程序或命令.如果在没有参数的情况下使用,start ...

  10. 前端JS

    目录 1.javascript介绍 1.1Web前端有三层: 1.2其中JavaScript基础又分为三个部分: 1.3JavaScript入门易学性 1.4JavaScript的组成 1.5Java ...