BP神经网络与Python实现

2023-11-18

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

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

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

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

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

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

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

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

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

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

BP神经网络原理

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

793413-20161010152810539-341221931.png

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

793413-20161010152655274-1512072099.png

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

\[I_j=\sum_iW_{ij}O_i\]

\[O_j= sigmod(I_l) =\frac{1}{1+e^{-I_l}}\]

其中\(W_{ij}\)表示神经元i与神经元j之间连接的权重,\(O_j\)代表神经元j的输出, sigmod是一个特殊的函数用于将任意实数映射到(0,1)区间.

上文中的sigmod函数称为神经元的激励函数(activation function), 除了sigmod函数\(\frac{1}{1+e^{-I_l}}\)外, 常用还有tanh和ReLU函数.

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

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

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

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

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

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

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

首先我们随机初始化连接权重\(W_{ij}\), 对某一训练样本进行一次前馈过程得到各神经元的输出.

首先计算输出层的误差:

\[E_j= sigmod'(O_j)*(T_j-O_j) =O_j(1-O_j)(T_j-O_j)\]

其中\(E_j\)代表神经元j的误差,\(O_j\)表示神经元j的输出, \(T_j\)表示当前训练样本的参考输出, \(sigmod'(x)\)是上文sigmod函数的一阶导数.

793413-20170106165524300-795195019.jpg

计算隐含层误差:

\[E_j= sigmod'(O_j)*\sum_kE_kW_{jk} =O_j(1-O_j)\sum_kE_kW_{jk}\]

隐含层输出不存在参考值, 使用下一层误差的加权和代替\((T_j-O_j)\).

计算完误差后就可以更新\(W_{ij}\)\(\theta_j\):

\[ W_{ij}=W_{ij}+\lambda E_jO_i \]

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

实际上为了加快学习的效率我们引入称为矫正矩阵的机制, 矫正矩阵记录上一次反向传播过程中的\(E_jO_i\)值, 这样\(W_j\)更新公式变为:

\[ W_{ij}=W_{ij}+\lambda E_jO_i + \mu C_{ij}\]

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

\[ C_{ij} = E_jO_i \]

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

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

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

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

\[ L = \sum_j(T_j-O_j)^2\]

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

Python实现BP神经网络

首先实现几个工具函数:

def rand(a, b):
    return (b - a) * random.random() + a


def make_matrix(m, n, fill=0.0):  # 创造一个指定大小的矩阵
    mat = []
    for i in range(m):
        mat.append([fill] * n)
    return mat

定义sigmod函数和它的导数:

def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))


def sigmod_derivate(x):
    return x * (1 - x)

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

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

def setup(self, ni, nh, no):
    self.input_n = ni + 1
    self.hidden_n = nh
    self.output_n = no
    # init cells
    self.input_cells = [1.0] * self.input_n
    self.hidden_cells = [1.0] * self.hidden_n
    self.output_cells = [1.0] * self.output_n
    # init weights
    self.input_weights = make_matrix(self.input_n, self.hidden_n)
    self.output_weights = make_matrix(self.hidden_n, self.output_n)
    # random activate
    for i in range(self.input_n):
        for h in range(self.hidden_n):
            self.input_weights[i][h] = rand(-0.2, 0.2)
    for h in range(self.hidden_n):
        for o in range(self.output_n):
            self.output_weights[h][o] = rand(-2.0, 2.0)
    # init correction matrix
    self.input_correction = make_matrix(self.input_n, self.hidden_n)
    self.output_correction = make_matrix(self.hidden_n, self.output_n)

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

def predict(self, inputs):
    # activate input layer
    for i in range(self.input_n - 1):
        self.input_cells[i] = inputs[i]
    # activate hidden layer
    for j in range(self.hidden_n):
        total = 0.0
        for i in range(self.input_n):
            total += self.input_cells[i] * self.input_weights[i][j]
        self.hidden_cells[j] = sigmoid(total)
    # activate output layer
    for k in range(self.output_n):
        total = 0.0
        for j in range(self.hidden_n):
            total += self.hidden_cells[j] * self.output_weights[j][k]
        self.output_cells[k] = sigmoid(total)
    return self.output_cells[:]

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

def back_propagate(self, case, label, learn, correct):
    # feed forward
    self.predict(case)
    # get output layer error
    output_deltas = [0.0] * self.output_n
    for o in range(self.output_n):
        error = label[o] - self.output_cells[o]
        output_deltas[o] = sigmod_derivate(self.output_cells[o]) * error
    # get hidden layer error
    hidden_deltas = [0.0] * self.hidden_n
    for h in range(self.hidden_n):
        error = 0.0
        for o in range(self.output_n):
            error += output_deltas[o] * self.output_weights[h][o]
        hidden_deltas[h] = sigmod_derivate(self.hidden_cells[h]) * error
    # update output weights
    for h in range(self.hidden_n):
        for o in range(self.output_n):
            change = output_deltas[o] * self.hidden_cells[h]
            self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]
            self.output_correction[h][o] = change
    # update input weights
    for i in range(self.input_n):
        for h in range(self.hidden_n):
            change = hidden_deltas[h] * self.input_cells[i]
            self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
            self.input_correction[i][h] = change
    # get global error
    error = 0.0
    for o in range(len(label)):
        error += 0.5 * (label[o] - self.output_cells[o]) ** 2
    return error

定义train方法控制迭代, 该方法可以修改最大迭代次数, 学习率\(\lambda\), 矫正率\(\mu\)三个参数.

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

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

def test(self):
    cases = [
            [0, 0],
            [0, 1],
            [1, 0],
            [1, 1],
        ]
    labels = [[0], [1], [1], [0]]
    self.setup(2, 5, 1)
    self.train(cases, labels, 10000, 0.05, 0.1)
    for case in cases:
        print(self.predict(case))

完整源代码参见bpnn.py

使用tensorflow实现一个神经网络可能是更简单高效的方法, 可以参见tensorflow入门指南中的第二节:实现一个简单神经网络.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BP神经网络与Python实现 的相关文章

随机推荐

  • 自定义一个softirq

    本文章添加自己定义一个额外的软中断 首先添加软中断种类 MY SOFTIRQ enum HI SOFTIRQ 0 TIMER SOFTIRQ NET TX SOFTIRQ NET RX SOFTIRQ BLOCK SOFTIRQ BLOCK
  • c语言之-umask()函数

    此函数的主要作用是在创建文件时设置或者屏蔽掉文件的一些权限 一般与open 函数配合使用 umask 设置建立新文件时的权限遮罩 相关函数 creat open 表头文件 sys types h sys stat h 定义函数 mode t
  • java数据类型陷阱_java学习_3.原生数据类型使用陷阱

    原生数据类型使用陷阱 Pitfall of Primitive Data Type 1 Java中的原生数据类型共有8种 1 整型 使用int表示 32位 2 字节型 使用byte表示 表示 128 127之间的256个整数 8位 3 短整
  • 8 种流行的计算机视觉应用

    计算机视觉是人工智能的一部分 它使计算机能够从计算机化的图片 视频中获取重要数据 并根据这些数据提出建议 简单地说 你可以理解 如果人工智能允许计算机思考 那么计算机视觉就会鼓励它们去看 注意到和理解 这是在深度学习和机器学习的帮助下完成的
  • 深入理解Solidity——作用域和声明

    作用域和声明 Scoping and Declarations 已声明的变量将具有其字节表示为全0的初始值 变量的初始值是任何类型的典型 零状态 zero state 例如 bool的初始值为false uint或int类型的默认值为0 对
  • FBX SDK的环境配置与FbxLine结构的输出

    FBX SDK的环境配置与FbxLine结构的输出 近期项目中 用到了FBX SDK 根据官网教程与博客等相关资料 在使用过程中主要发现了两点问题 1 FBX SDK的环境配置网上说法不一 2 FbxLine结构体官网教程没有给出具体例子
  • antd a-form-model 动态表单 自定义校验柯里化

    1 需求 前端通过后端字段遍历formItem 由于字段可能是金额 电话号码等 单独if太多了太麻烦 所以想到柯里化 2 代码 响应请求 xxx then res gt if res data list length 0 return fa
  • ftp客服端实现自动更新文件(带更新完自动启动功能)-python

    ftp客服端实现自动更新文件 带自动启动功能并封装为带配置文件的工具 python 前言 一 项目环境和结构 二 使用介绍 三 程序封装和注册服务 四 填坑 希望读者能用到 总结 前言 由于工位机不可能做到实时看守 当更新程序的时候我们还得
  • wxWidgets:使用wxDataViewCtrl类的示例

    wxWidgets 使用wxDataViewCtrl类的示例 wxWidgets是一个跨平台的C 图形用户界面 GUI 库 它提供了丰富的控件和功能 使开发者能够轻松构建跨平台的应用程序 其中的wxDataViewCtrl类是一个强大的控件
  • SVM(支持向量机)原理与应用

    1 支持向量机 支持向量机 Support Vector Machine SVM 是一类按监督学习 supervised learning 方式对数据进行二元分类的广义线性分类器 generalized linear classifier
  • vue flex 布局实现div均分自动换行

    vue flex 布局实现div均分自动换行 许久没有更新了 今天才意外发现以前还是没有看懂盒模型 今天才算看懂了 首先我们今天来看一下想要实现的效果是什么 当然适配是必须的 1920 或者 1376都测试过 效果如图所选中区域所示 一 关
  • 【博客管理】短期长期计划【置顶】

    短期计划 2016 项目 内容 拟完成时间 完成情况 未完成原因 链接 颜色恒常图像增强 递归高斯 英 5 9 page5 total13 1 13 连接 颜色恒常图像增强 递归高斯 英 5 10 0 0 无 颜色恒常图像增强 递归高斯 英
  • 一台浮点计算机 数码为,导论简答.doc

    第一章 一 1 什么是计算机 计算机系统是一种能够按照事先存储的程序 自动 高速的对数据进行输入 处理 输出和存储的系统 一个计算机系统包括硬件和软件两大部分 2 解释冯 诺依曼所提出的 存储程序 概念 把程序和数据都以二进制的形式同意存放
  • Session机制

    除了使用Cookie Web应用程序中还经常使用Session来记录客户端状态 Session是服务器端使用的一种记录客户端状态的机制 使用上比Cookie简单一些 相应的也增加了服务器的存储压力 什么是Session Session是另一
  • Anaconda新建虚拟环境并配置

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 python学习之新建环境配置 一 查看当前环境 二 创建新的虚拟环境 三 pycharm新建的虚拟环境添加及环境更换 python学习之新建环境配置 前提 Ana
  • 数据结构与算法2--数组常见操作

    数据结构与算法2 数组常见操作 数组是最常见也是我们使用最多的数据结构了 它是一块连续的内存空间 以下标来描述空间的位置 C 中int arr len 表示的的数组一旦配置后大小就无法改变 vector
  • CUDA-同步

    主机与设备之间的同步 分为隐式和显式 一 隐式 cudaMemcpy函数的作用在于传输传输 但在执行结束之前会产生阻塞 许多与内存相关的操作都会产生阻塞 这些不必要的阻塞会对性能产生较大的影响 如 锁页主机内存分配 设备内存分配 设备内存初
  • 2021年中国航天发射列表(55发见证历史)

    序号 有效载荷 发射时间 火箭型号 发射场 状态 1 天通一号03星 2021 01 20 00 25 CZ 3B 西昌 成功 2 遥感三十一号02组 2021 01 29 12 47 CZ 4C 酒泉 成功 3 方舟2号 2021 02
  • C++三目运算

    三目运算也叫条件运算或三元运算 可以实现简单if语句的功能 但是书写更简洁 语法 表达式一 表达式二 表达式三 先计算表达式一的值 如果为真 整个表达式的结果为表达式二的值 如果为假 整个表达式的结果为表达式三的值 int a b c a
  • BP神经网络与Python实现

    人工神经网络是一种经典的机器学习模型 随着深度学习的发展神经网络模型日益完善 联想大家熟悉的回归问题 神经网络模型实际上是根据训练样本创造出一个多维输入多维输出的函数 并使用该函数进行预测 网络的训练过程即为调节该函数参数提高预测精度的过程