概述

神经网络结构由输入层,隐藏层和输出层构成,神经网络中的每一个结点都与上一层所有的结点都有连接,我们称之为全连接,如下图



在图中的神经网络中,原始的输入数据,通过第一层隐含层的计算得出的输出数据,会传到第二层隐含层。而第二层的输出,又会作为输出层的输入数据。

向前传播

计算出输入层数据传输到隐藏层:

由下图可知我们可以计算出隐藏层的第一个神经元

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征

的值:

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_02

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_03

其中

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_04

为激活函数由下图可知我们可以计算出隐藏层的第二个神经元

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_05

的值:

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_06

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_07

由下图可知我们可以计算出隐藏层的第三个神经元

bp神经网络的隐层特征 bp神经网络隐藏层_权重_08

的值:

bp神经网络的隐层特征 bp神经网络隐藏层_权重_09

bp神经网络的隐层特征 bp神经网络隐藏层_python_10

到此我们己经算出所有从输入层到隐藏层的所有值。

计算隐藏层到输出层的过程:

由下图可知我们可以计算出隐藏层到第一个输出神经元

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_11

的值:

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_12

bp神经网络的隐层特征 bp神经网络隐藏层_python_13

同理可以得出

bp神经网络的隐层特征 bp神经网络隐藏层_权重_14


bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_15

的值:

bp神经网络的隐层特征 bp神经网络隐藏层_权重_16

bp神经网络的隐层特征 bp神经网络隐藏层_权重_17

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_18

bp神经网络的隐层特征 bp神经网络隐藏层_python_19

为了简化我们以后的数据处理流程,现在我们设第

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_20

层的输入数据为向量

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征_21

,权重为

bp神经网络的隐层特征 bp神经网络隐藏层_python_22

,偏置变量为

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_23

。则我们从上面的求解流程可以得出

bp神经网络的隐层特征 bp神经网络隐藏层_权重_24

层的数据为:

bp神经网络的隐层特征 bp神经网络隐藏层_权重_25

bp神经网络的隐层特征 bp神经网络隐藏层_人工智能_26

至此神经网络的前向传播过程己经讲完。

反向传播

反向转播的思想就是,我们通过前向传播后计算出网络的输出值,知道输出值后我们就可以求出输出层的残差,再从输出层反向把残差传回各层的神经元中。

假设我们有一个固定样本集

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_27

,它包含

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_28

个样例。我们可以用批量梯度下降法来求解神经网络。具体来讲,对于单个样例

bp神经网络的隐层特征 bp神经网络隐藏层_python_29

,其代价函数如下图(摘自网络):

我们可以定义整体代价函数如下图(摘自网络):

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征_30

以上关于

bp神经网络的隐层特征 bp神经网络隐藏层_python_31

定义中的第一项是一个均方差项。第二项是一个规则化项(也叫权重衰减项),其目的是减小权重的幅度,防止过度拟合。

有了总体代价函数后,我们的目标可以转化成求代价函数的最小值。我们使用梯度下降算法求代价函数的最小值,所以我们得出以下公式:

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征_32

bp神经网络的隐层特征 bp神经网络隐藏层_python_33

我们对总体代价函数求偏导:

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_34

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征_35

通过上面两个式子可以看出,我们把问题转化成求

bp神经网络的隐层特征 bp神经网络隐藏层_bp神经网络的隐层特征_36


bp神经网络的隐层特征 bp神经网络隐藏层_python_37

的值所以对于第

bp神经网络的隐层特征 bp神经网络隐藏层_权重_38

层(输出层)的每个输出单元

bp神经网络的隐层特征 bp神经网络隐藏层_权重_39

,我们根据以下公式计算残差(下图摘自网络): 对

bp神经网络的隐层特征 bp神经网络隐藏层_权重_40

的各个层,第

bp神经网络的隐层特征 bp神经网络隐藏层_神经网络_20

层的第

bp神经网络的隐层特征 bp神经网络隐藏层_权重_39

个节点的残差计算方法如下(下图摘自网络): 以上逐次从后向前求导的过程即为“反向传导”的本意所在。

计算我们需要的偏导数,计算方法如下:


BP神经网络python实现

# -*- coding: utf-8 -*-
'''
Created on

@author: Belle
'''
from numpy.random.mtrand import randint
import numpy as np


'''双曲函数'''
def tanh(value):
    return (1 / (1 + np.math.e ** (-value)))

'''双曲函数的导数'''
def tanhDer(value):
    tanhValue = tanh(value)
    return tanhValue * (1 - tanhValue)

'''
Bp神经网络model
'''
class BpNeuralNetWorkModel:
    def __init__(self, trainningSet, label, layerOfNumber, studyRate):
        '''学习率'''
        self.studyRate = studyRate
        '''计算隐藏层神经元的数量'''
        self.hiddenNeuronNum = int(np.sqrt(trainningSet.shape[1] + label.shape[1]) + randint(1, 10))
        '''层数据'''
        self.layers = []
        '''创建输出层'''
        currentLayer = Layer()
        currentLayer.initW(trainningSet.shape[1], self.hiddenNeuronNum)
        self.layers.append(currentLayer)
        
        '''创建隐藏层'''
        for index in range(layerOfNumber - 1):
            currentLayer = Layer()
            self.layers.append(currentLayer)
            '''输出层后面不需要求权重值'''
            if index == layerOfNumber - 2:
                break
            nextLayNum = 0
            
            '''初始化各个层的权重置'''
            if index == layerOfNumber - 3:
                '''隐藏层到输出层'''
                nextLayNum = label.shape[1]
            else:
                '''隐藏层到隐藏层'''
                nextLayNum = self.hiddenNeuronNum
            currentLayer.initW(self.hiddenNeuronNum, nextLayNum)
        '''输出层的分类值'''
        currentLayer = self.layers[len(self.layers) - 1]
        currentLayer.label = label
    
    '''神经网络前向传播'''
    def forward(self, trainningSet):
        '''计算输入层的输出值'''
        currentLayer = self.layers[0]
        currentLayer.alphas = trainningSet
        currentLayer.caculateOutPutValues()
        
        preLayer = currentLayer
        for index in range(1, len(self.layers)):
            currentLayer = self.layers[index]
            '''上一层的out put values就是这一层的zValues'''
            currentLayer.zValues = preLayer.outPutValues
            '''计算alphas'''
            currentLayer.caculateAlphas()
            '''最后一层不需要求输出值,只要求出alpha'''
            if index == len(self.layers) - 1:
                break
            '''输入层计算out puts'''
            currentLayer.caculateOutPutValues()
            '''指向上一层的layer'''
            preLayer = currentLayer
    
    '''神经网络后向传播'''
    def backPropogation(self):
        layerCount = len(self.layers)
        
        '''输出层的残差值'''
        currentLayer = self.layers[layerCount - 1]
        currentLayer.caculateOutPutLayerError()
        
        '''输出层到隐藏层'''
        preLayer = currentLayer
        layerCount = layerCount - 1
        while layerCount >= 1:
            '''当前层'''
            currentLayer = self.layers[layerCount - 1]
            '''更新权重'''
            currentLayer.updateWeight(preLayer.errors, self.studyRate)
            if layerCount != 1:
                currentLayer.culateLayerError(preLayer.errors)
            layerCount = layerCount - 1
            preLayer = currentLayer
            
'''
创建层
'''
class Layer:
    def __init__(self):
        self.b = 0
    
    '''使用正态分布的随机值初始化w的值'''
    def initW(self, numOfAlpha, nextLayNumOfAlpha):
        self.w = np.mat(np.random.randn(nextLayNumOfAlpha, numOfAlpha))
    
    '''计算当前层的alphas'''
    def caculateAlphas(self):
        '''alpha = f(z)'''
        self.alphas = np.mat([tanh(self.zValues[row1,0]) for row1 in range(len(self.zValues))])
        '''求f'(z)的值(即f的导数值)'''
        self.zDerValues = np.mat([tanhDer(self.zValues[row1,0]) for row1 in range(len(self.zValues))])
    
    '''计算out puts'''
    def caculateOutPutValues(self):
        '''计算当前层z = w * alpha的的下一层的输入值'''
        self.outPutValues = self.w * self.alphas.T + self.b
    
    '''计算输出层的残差'''
    def caculateOutPutLayerError(self):
        self.errors = np.multiply(-(self.label - self.alphas), self.zDerValues)
        print("out put layer alphas ..." + str(self.alphas))
    
    '''计算其它层的残差'''
    def culateLayerError(self, preErrors):
        self.errors = np.mat([(self.w[:,column].T * preErrors.T * self.zDerValues[:,column])[0,0] for column in range(self.w.shape[1])])
    
    '''更新权重'''
    def updateWeight(self, preErrors, studyRate):
        data = np.zeros((preErrors.shape[1], self.alphas.shape[1]))
        for index in range(preErrors.shape[1]):
            data[index,:] = self.alphas * (preErrors[:,index][0,0])
        self.w = self.w - studyRate * data

'''
训练神经网络模型
@param train_set: 训练样本
@param labelOfNumbers: 训练总类别
@param layerOfNumber:  神经网络层数,包括输出层,隐藏层和输出层(默认只有一个输入层,隐藏层和输出层)
'''
def train(train_set, label, layerOfNumber = 3, sampleTrainningTime = 5000, studyRate = 0.6):
    neuralNetWork = BpNeuralNetWorkModel(train_set, label, layerOfNumber, studyRate)
    '''训练数据'''
    for row in range(train_set.shape[0]):
        '''当个样本使用梯度下降的方法训练sampleTrainningTime次'''
        for time in range(sampleTrainningTime):
            '''前向传播 '''
            neuralNetWork.forward(train_set[row,:])
            '''反向传播'''
            neuralNetWork.backPropogation()
            


复制代码

测试代码

# -*- coding: utf-8 -*-
'''
Created on 2018��5��27��

@author: Belle
'''

import BpNeuralNetWork
import numpy as np

train_set = np.mat([[0.05, 0.1], [0.3, 0.2]])
labelOfNumbers = np.mat([0.1, 0.99, 0.3])
layerOfNumber = 4

bpNeuralNetWork = BpNeuralNetWork.train(train_set, labelOfNumbers, layerOfNumber)

复制代码

以下是测试代码的输出值