1 神经网络

人工神经网络(Artificial Neural Network,ANN),通常简称为神经网络,是深度学习的基础,它是受到人类大脑结构启发而诞生的一种算法。

ANN思想主要是来源于人类大脑神经系统,仿照人类做一些计算量比较复杂的工作。因此ANN现在已经成为科学领域的一个重要方向,ANN在函数的逼近、信息分类、模式识别、故障诊断和优化预测等诸多领域都有非常广泛的应用。

ANN如此受欢迎的原因是它具有逼近任意非线性函数(可以处理非线性方面的问题)、并行处理信息、较强的自学习能力等特点。目前,经过ANN产生的代表模型已有数十种,其中用得最为广泛的是BP神经网络和它的一些推广形式。

生物神经元
在这里插入图片描述

ANN神经元

在这里插入图片描述
在这里插入图片描述

(1)联接权向量: W = ( ω 1 , ω 2 , ⋯   , ω n ) T W=\left( \omega _1,\omega _2,\cdots ,\omega _n \right) ^T W=(ω1,ω2,,ωn)T

(2)输入层:对输入层信息单元进行加权求和,该神经元得到的通过加权后的输入信息效果用 n e t net net来表示,它就是网络的输入:

n e t = ∑ x i ω i net=\sum{x_i\omega _i} net=xiωi

(3)激活函数:非线性的因素由于激活函数的原因而被引入神经元里面,也正是因为如此神经网络才可以逼近非线性函数,这样神经网络就可以应用到非线性模型中。激活函数可以分为以下几类,具体为阀值型函数、分段型函数、S型函数和双曲正切函数,

在这里插入图片描述

具体函数形式为:

  • 阀值型函数:
    f ( x ) = { 1 , x ≥ 0 0 , x < 0 f\left( x \right) =\left\{ \begin{array}{l} 1\text{,}x\ge 0\\ \\ 0\text{,}x<0\\ \end{array} \right. f(x)=1x00x<0
    s g n ( x ) = { 1 , x ≥ 0 − 1 , x < 0 sgn\left( x \right) =\left\{ \begin{array}{l} 1\text{,}x\ge 0\\ \\ -1\text{,}x<0\\ \end{array} \right. sgn(x)=1x01x<0

  • Relu函数:

f ( x ) = { x , x ≥ 0 0 , x < 0 f\left( x \right) =\left\{ \begin{array}{l} x\text{,}x\ge 0\\ \\ 0\text{,}x<0\\ \end{array} \right. f(x)=xx00x<0

  • Sigmoid函数:

f ( x ) = 1 1 + e − x f\left( x \right) =\frac{1}{1+e^{-x}} f(x)=1+ex1

  • tanh函数:

f ( x ) = tan ⁡ h ( x ) = e x − e − x e x + e − x f\left( x \right) =\tan\text{h}\left( x \right) =\frac{e^x-e^{-x}}{e^x+e^{-x}} f(x)=tanh(x)=ex+exexex

在这里插入图片描述
神经元的动作:
n e t = ∑ x i ω i net=\sum{x_i\omega _i} net=xiωi

y = f ( n e t ) y=f\left( net \right) y=f(net)

2 BP神经网络

2.1 模型概述

BP(Back Propagation)算法,又称为误差反向传递算法多层前馈神经网络,是人工神经网络中使用最为频繁的一种监督式的学习算法。

该模型要用到训练算法,其应用的误差反向传播,巧妙的化解了该模型的网络学习问题,从而较大程度的推动神经网络快速的发展。其在信息传播时为正向传播,而传播误差时采用反向传播,即BP神经网络是按照信号正向传播,误差反向传播的原理来对网络的结构进行训练和修正。

BP神经网络整体由这两个传播过程交替组成,是一种单向多层的前向神经网络,分别是输入层(input),隐含层(hidelayer),输出层(outputlayer),每一层通过各层的神经元相互连接,同一层的神经元又相互独立。


在信息的正向传播过程中,输入层的神经元接收到外部传来的信号,并将其全部传递给隐含层的神经元;隐含层首先接收到输入层传递过来的信息,然后对信息做一些处理后,传递给输出层;最后由输出层的神经元将信号进行处理后传到外界,这个过程就是完整的一次信号正向传播。在整个传递的过程中可以发现每一层的神经元只能从前一层的神经元中接收到传递的信息,同一层的几个神经元之间并没有关系。

在这里插入图片描述

如果输出层传递到外界的信号与实际的输出信号存在着不符,则进行误差的反方向传递,误差通过输出层原路再传回到隐含层,根据对各连接层的权值和阈值进行修改,使得预测输出与期望输出结果之间的误差变小。通过不断地进行信息的正向传播以及误差的反向传播,当BP神经网络的输出值与实际值之间的差距小于之前设定的最小值时,网络就停止训练。

信息在前向传递中,输入信号从输入层经隐含层逐层处理,直至输出层,每一层的神经元状态只影响下一-层神经元状态。如果输出层得不到期望输出,则转入反向传播,根据预测误差调整网络权值和阈值,从而使BP神经网络预测输出不断逼近期望输出。

在这里插入图片描述
其中,
(1)输入层 i i i
输入层的目的就是将不同的输入信号和权值数量积后,将结果传输到隐含层,作为BP神经网络隐含层的输入向量。

  • X 1 , X 2 , ⋯   , X n X_1,X_2,\cdots ,X_n X1,X2,,Xn是BP神经网络的输入值;
  • Y 1 , Y 2 , ⋯   , Y m Y_1,Y_2,\cdots ,Y_m Y1,Y2,,Ym是其预测值;
  • W i j W_{ij} Wij W j k W_{jk} Wjk 为输入层与隐藏层的连接权值。

可以看出,这个网络它可以看作一个非线性的函数,此时网络输入值是该函数的自变量,而预测值就是函数因变量。

(2)隐藏层 j j j

  • 隐含层的输入变量与激活函数通过某种运算后作为该层的输出向量。

(3)输出层 k k k

  • BP神经网络只有一层输出层,该层的输入信息为隐含层节点的输出信息和权值的标量积结果,输出结果的求解方法与隐含层的运算方法相同,同样是通过输出层的输入变量和激活函数进行运算。输出层的输出向量则作为BP神经网络预期的结果。

(4)期望输出: Y 1 ′ , Y 2 ′ , ⋯   , Y m ′ Y_{1}^{'},Y_{2}^{'},\cdots ,Y_{m}^{'} Y1,Y2,,Ym

  • BP神经网络根据预测结果与真实结果之间的误差,通过反方向传播将它们之间的差距传递到上一层,以此修正上一层的连接权值和阈值。通过误差的一层层反转,可以对每一层的参数进行修改,从而使得预测结果更接近于实际值。

2.2 BP神经网络的工作原理

两个阶段(正向传播、反向传播)

信号的正向传播阶段是BP神经网络的工作过程所包含的一个阶段,而它包含的另一个阶段是误差的反向传播阶段。

输入层传入输入信号,往前传它就传到隐含层,在隐含层里面进行操作处理,然后传输到输出层。如果输出层得到的输出不是想要的输出,则转到误差反向传播阶段,进入反向传播输出,网络的权值和阈值不断的进行修改。整个过程如此的循环往复,当实验的误差小于给定误差的时候,就终止学习的过程。

2.3 建模步骤

BP神经网络在信息的传播过程中通过对权值和阈值的不断调整,最终使误差减少到预期的目标。BP神经网络的训练步骤如下:

(1)数据预处理

BP神经网络首先需要对原始数据进行归一化,转化为区间[-1,1]或[0,1]类的数字,目的是取消各维数据间的数量级差别,提高神经网络的收敛速度,避免因为输入或输出数据的量纲影响加大预测误差。初始化公式为:

正指标
Y i = x i − x min ⁡ x max ⁡ − x min ⁡ , i = 1 , 2 , ⋯   , n Y_i=\frac{x_i-x_{\min}}{x_{\max}-x_{\min}}\text{,}i=1,2,\cdots ,n Yi=xmaxxminxixmini=1,2,,n

逆指标
Y i = x max ⁡ − x i x max ⁡ − x min ⁡ , i = 1 , 2 , ⋯   , n Y_i=\frac{x_{\max}-x_i}{x_{\max}-x_{\min}}\text{,}i=1,2,\cdots ,n Yi=xmaxxminxmaxxii=1,2,,n

其中, x i x_i xi 表示样本序列输入值, x m a x x_{max} xmax 表示输入序列的最小值, x m a x x_{max} xmax 表示输入序列的最大值, Y Y Y 是归一化后的数据。在得到BP神经网络的预测值后需要对预测值进行反归一化。

(2)BP神经网络初始化

BP神经网络为了解决不同的问题,从而需要确定不同的网络结构,主要包括隐含层层数和网络的学习参数。

①神经网络层数确定:在BP神经网络中,输入层和输出层只有一层,所以只需要确定隐含层层数。通常情况下,我们使用单–隐含层。单隐层网络也被称为三层前馈网络或三层感知器。

②输入、输出、隐含层神经元数确定:BP神经网络模型的输入层神经元数是由指标数来确定;输出层神经元数需要根据具体情况而定,确定隐含层神经元数是一项意义重大且十分重要的工作。

网络可能会因为数量少的原因而无法获得,样本规律也会因此而无法得到概括,数量过多,就会把样本中一些不重要的或者没有规律的信息(如噪声)吸取进来,网络的泛化能力会被降低,同时也会增加网络的学习时间。隐含层的神经元数可以通过下面的公式来确定它的大概范围:
s = m + n + α s=\sqrt{m+n}+\alpha s=m+n +α

s s s为隐含层神经元数, m m m为输入层神经元数, n n n为输出层神经元数, α α α为1~10之间的常数。

(3)激活函数的确定

隐含层和输出层需要确定激活函数,激活函数 f ( x ) f(x) f(x)的选取有许多种。如logsig、tansig、purelin 等多种形式。

{ log ⁡ si g ( x ) = 1 1 + e − x tan ⁡ s i g ( x ) = 2 1 + e − 2 x − 1 p u r e l i n ( x ) = x \left\{ \begin{array}{l} \log\text{si}g\left( x \right) =\frac{1}{1+e^{-x}}\\ \\ \tan sig\left( x \right) =\frac{2}{1+e^{-2x}}-1\\ \\ purelin\left( x \right) =x\\ \end{array} \right. logsig(x)=1+ex1tansig(x)=1+e2x21purelin(x)=x

(4)初始化权值、阈值,确定学习速率

(5)计算输入层和隐含层的结果

根据输入向量 X X X ω i j \omega_{ij} ωij分别是连接输入层和隐含层的权数,隐含层和输出层的阈值分别为 a 、 b a、b ab ω j k \omega_{jk} ωjk是这两层的连接权值, L L L为隐含层的节点数,则有:

隐含层输出: H j = f ( ∑ i = 1 n ω i j x i − a j ) , j = 1 , 2 , ⋯   , L H_j=f\left( \sum_{i=1}^n{\omega _{ij}x_i-a_j} \right) \text{,}j=1,2,\cdots ,L Hj=f(i=1nωijxiaj)j=1,2,,L

输出层输出: O k = ∑ i = 1 L H i ω i k − b k , k = 1 , 2 , ⋯   , m O_k=\sum_{i=1}^L{H_i\omega _{ik}-b_k}\text{,}k=1,2,\cdots ,m Ok=i=1LHiωikbkk=1,2,,m

(6)误差计算

由网络得出的预测输出 O O O和期望输出 Y Y Y之间的差值,计算预测误差 e e e

误差公式为:

e k = Y k − O k , k = 1 , 2 , ⋯   , m e_k=Y_k-O_k\text{,}k=1,2,\cdots ,m ek=YkOkk=1,2,,m

(7)权值更新

权值 ω i j \omega_{ij} ωij ω j k \omega_{jk} ωjk可以由预测误差 e e e来更新


ω i j = ω i j + η H ( j ) ( ( 1 − H ( j ) ) x ( i ) ∑ k = 1 m ω i k e k ) , i = 1 , 2 , ⋯   , n ; j = 1 , 2 , ⋯   , L \omega _{ij}=\omega _{ij}+\eta H\left( j \right) \left( \left( 1-H\left( j \right) \right) x\left( i \right) \sum_{k=1}^m{\omega _{ik}e_k} \right) \text{,}i=1,2,\cdots ,n\text{;}j=1,2,\cdots ,L ωij=ωij+ηH(j)((1H(j))x(i)k=1mωikek)i=1,2,,nj=1,2,,L
ω j k = ω j k + η H ( j ) e ( k ) , j = 1 , 2 , ⋯   , L ; k = 1 , 2 , ⋯   , m ; η 为学习速率 \omega _{jk}=\omega _{jk}+\eta H\left( j \right) e\left( k \right) \text{,}j=1,2,\cdots ,L\text{;}k=1,2,\cdots ,m\text{;}\eta \text{为学习速率} ωjk=ωjk+ηH(j)e(k)j=1,2,,Lk=1,2,,mη为学习速率

(8)阈值更新

也需要更新阈值a,b要依据预测误差 e e e

a ( j ) = a ( j ) + η H ( j ) ( 1 − H ( j ) ) ∑ k = 1 m ω j k e k , j = 1 , 2 , ⋯   , L a\left( j \right) =a\left( j \right) +\eta H\left( j \right) \left( 1-H\left( j \right) \right) \sum_{k=1}^m{\omega _{jk}e_k}\text{,}j=1,2,\cdots ,L a(j)=a(j)+ηH(j)(1H(j))k=1mωjkekj=1,2,,L

b ( k ) = b ( k ) + e ( k ) , k = 1 , 2 , ⋯   , m b\left( k \right) =b\left( k \right) +e\left( k \right) \text{,}k=1,2,\cdots ,m b(k)=b(k)+e(k)k=1,2,,m

(9)判断算法迭代能否终止,如果不能终止,则返回步骤(5)

最后,如果BP神经网络模型预测结果与实际结果之间的误差在预先设定的误差之内,说明网络预测结果已经达到最优值,若不然,返回步骤(5)继续进行下一步的训练学习。如果训练的次数超过之前设置的参数,误差还是不在范围之内,则说明该预测算法无法收敛。


BP神经网络建模流程图
在这里插入图片描述

3 BP神经网络的Python实现

这里我参考一下别人的代码

神经网络(BP)算法Python实现及简单应用

三层BP神经网络的python实现

# -*- coding: utf-8 -*-
"""
Created on Wed Jul 28 16:52:15 2021

@author: ABC
"""

import math
import random

random.seed(0)


# 产生随机值
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))

# sigmod函数de 导数
def sigmoid_derivative(x):
    return x * (1 - x)


# 定义BPNeuralNetwork类, 使用三个列表维护输入层,隐含层和输出层神经元, 
# 列表中的元素代表对应神经元当前的输出值.使用两个二维列表以邻接矩阵的形式维护输入层与隐含层, 
# 隐含层与输出层之间的连接权值, 通过同样的形式保存矫正矩阵.
class BPNeuralNetwork:
    def __init__(self):
        self.input_n = 0 # 输入层个数
        self.hidden_n = 0 # 隐藏层个数
        self.output_n = 0 # 输出层个数
        self.input_cells = [] # 输入层
        self.hidden_cells = [] # 隐藏层
        self.output_cells = [] # 输出层
        self.input_weights = [] # 输入权重
        self.output_weights = [] # 输出权重
        self.input_correction = [] # 输入系数
        self.output_correction = [] # 输出系数
        
    # 定义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):
        # 激活输入层
        for i in range(self.input_n - 1):
            self.input_cells[i] = inputs[i]
        # 激活隐藏层
        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)
        # 激活输出层
        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):
        # 前馈
        # 学习训练集潜在规律,并预测
        self.predict(case)
        # 得到输出层误差
        output_deltas = [0.0] * self.output_n
        for o in range(self.output_n):
            error = label[o] - self.output_cells[o]
            output_deltas[o] = sigmoid_derivative(self.output_cells[o]) * 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] = sigmoid_derivative(self.hidden_cells[h]) * error
        # 更新输出层权重
        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
        # 更新以隐藏层权重
        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
        # 得到全局误差
        error = 0.0
        for o in range(len(label)):
            error += 0.5 * (label[o] - self.output_cells[o]) ** 2
        # 返回最终预测误差
        return error


    # 定义train方法控制迭代, 该方法可以修改最大迭代次数, 学习率λ, 矫正率μ 三个参数.
    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
        # 开始迭代
        for j in range(limit):
            # 误差归0
            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))


if __name__ == '__main__':
    # 实例化 神经网络
    nn = BPNeuralNetwork()
    # 调用其中的方法
    nn.test()

参考:
《基于ARIMA模型和BP神经网络模型的江苏省GDP预测分析》
《基于神经网络和灰色模型的心血管死亡人数预测研究》
BP神经网络方法

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐