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. Git版本管理工具使用

    1.Git简介 Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. Git 是 Linus Torvalds 为了帮助管理 Linux 内核 ...

  2. [AI] 切换cuda版本的万金油

    1. 环境 ubuntu16.04 GTX1080Ti x 4 nvidia-418 cuda-10.1 pytorch1.0.0 目标:在最新的显卡驱动下,使用不同版本的cuda和深度学习框架来执行 ...

  3. PAT甲级1012题解——选择一种合适数据存储方式能使题目变得更简单

    题目分析: 本题的算法并不复杂,主要是要搞清楚数据的存储方式(选择一种合适的方式存储每个学生的四个成绩很重要)这里由于N的范围为10^6,故选择结构体来存放对应下标为学生的id(N只有2000的范围, ...

  4. scrapy框架爬取开源中国项目大厅所有的发布项目。

    本文爬取的字段,项目名称,发布时间,项目周期,应用领域,最低报价,最高报价,技术类型 1,items中定义爬取字段. import scrapy class KaiyuanzhongguoItem(s ...

  5. Collaborative Knowledge base Embedding (CKE)

    Collaborative Knowledge base Embedding (CKE) 在推荐系统中存在着很多与知识图谱相关的信息,以电影推荐为例: 结构化知识(structural knowled ...

  6. ms08067 分析与利用

    分析 漏洞位于 NetpwPathCanonicalize 函数里面,这个函数的作用在于处理路径中的 ..\ 和 .\ 信息.该函数声明如下: DWORD NetpwPathCanonicalize( ...

  7. 编程小白入门分享二:IntelliJ IDEA的入门操作小知识

    idea简介 IDEA 全称 IntelliJ IDEA,是java编程语言开发的集成环境.IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手.代码自动提示.重构.J2EE支 ...

  8. 10. vue-router命名路由

    命名路由的配置规则 为了更加方便的表示路由的路径,可以给路由规则起一个别名, 即为"命名路由". const router = new VueRouter ({ routes: [ ...

  9. HDU - 3311: Dig The Wells (斯坦纳树)

    题意:给你n个寺庙,m个村庄,p条路,现在你要在这n+m个位置中选出若干个位置打井,每个位置打井的费用会告诉你,同时p条路也有修建费用,现在每个寺庙都住着一个和尚,问你最小的费用让这n个和尚都能喝上水 ...

  10. docker-compose部署gitlab

    一.安装docker-compose步骤可参考本博客其他文章 二.这里的ssl证书是使用letsencrypt生成,可参考文档https://my.oschina.net/u/3042999/blog ...