神经网络之反向传播算法实现

1 神经网络模型

以下面神经网络模型为例,说明神经网络中正向传播和反向传播过程及代码实现

1.1 正向传播

(1)输入层神经元\(i_1,i_2\),输入层到隐藏层处理过程

\[HiddenNeth_1 = w_1i_1+w_2i_2 + b_1\]
\[HiddenNeth_2 = w_3i_1+w_4i_2 + b_1\]
\[h_1 = sigmoid(HiddenNeth_1)\]
\[h_2 = sigmoid(HiddenNeth_2)\]

(2)隐藏层:神经元\(h_1,h_2\),隐藏层到输出层处理过程

\[OutputNeto_1 = w_5h_1+w_6h_2\]
\[OutputNeto_2 = w_7h_1+w_8h_2\]
\[o_1 = sigmoid(OutputNeto_1)\]
\[o_2 = sigmoid(OutputNeto_2)\]

1.2 反向传播

反向传播是根据每次前向传播得到的误差来评估每个神经网络层中的权重对其的影响程度,再根据链式规则和梯度下降对权重进行更新的过程,下面以对更新权重\(w_5和w_1\)为例进行说明

  • totalo1表示参考值0.01
  • totalo2参考值0.99

1.2.1 更新权重w_5

\[\frac{\partial totalo1}{\partial w_5} = \frac{\partial totalo1}{\partial o_1} * \frac{\partial o_1}{\partial outputneto_1} * \frac{\partial outputneto_1}{\partial w_5}\]
其中:\[\frac{\partial totalo1}{\partial o_1} = \frac{\partial \frac{1}{2}{(totalo_1 - o_1)}^2}{\partial o_1} = -(totalo_1 -o_1)\]

\[\frac{\partial o_1}{\partial outputneto_1} = o_1(1-o_1)\]

\[\frac{\partial outputneto_1}{\partial w_5} = h_1\]

则:\[\frac{\partial totalo1}{\partial w_5} = -(totalo_1 -o_1)o_1(1-o_1)h_1\]
更新w_5的值:\[w_5new = w_5 - learningrate * \frac{\partial totalo1}{\partial w_5}\]

1.2.2 更新权重w_1

\(求w_1的偏导与求w_5偏导唯一的区别在于,w_1的值影响了o_1和o_2\),所以
\[\frac{\partial total}{\partial w_1} = \frac{\partial totalol}{\partial w_1} + \frac{\partial totalo2}{\partial w_1}\]

接下来的求导思路和上面一样:
\[\frac{\partial totalol}{\partial w_1} = \frac{\partial totalol}{\partial h1} * \frac{\partial h1}{\partial hiddenneth1} * \frac{\partial hiddenneth1}{\partial w_1}\]

\[\frac{\partial total}{\partial w_1} = -(totalo1 - o1)o1(1-o1)w_5*h1(1-h1)i1 + -(totalo2 - o2)o2(1-o2)w_7*h1(1-h1)i1\]
更新w_1的值:\[w_1new = w_1 - learningrate * \frac{\partial total}{\partial w_1}\]

2. 代码实现正向和方向传播算法

import random
import math

class NeuralNetwork:
    learning_rate = 0.5
    def __init__(self,input_num,hidden_num,output_num,input_hidden_weights=None,
                 input_hidden_bias=None,hidden_output_weights=None,hidden_output_bias=None):

        self.input_num = input_num

        # 构建掩藏层
        self.hidden_layer = NeuralLayer(hidden_num,input_hidden_bias)
        # 构建输出层
        self.output_layer = NeuralLayer(output_num,hidden_output_bias)
        # 初始化输入层到隐藏层权重
        self.init_input_to_hidden_weights(input_hidden_weights)
        # 初始化隐藏层到输出层权重
        self.init_hidden_to_output_weights(hidden_output_weights)

    def init_input_to_hidden_weights(self,weights):
        weight_num = 0
        for i_num in range(len(self.hidden_layer.neurons)):
            for o_num in range(self.input_num):
                if weights is None:
                    self.hidden_layer.neurons[i_num].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[i_num].weights.append(weights[weight_num])
                weight_num += 1

    def init_hidden_to_output_weights(self,weights):
        weight_num = 0
        for i_num in range(len(self.output_layer.neurons)):
            for o_num in range(len(self.hidden_layer.neurons)):
                if weights is None:
                    self.output_layer.neurons[i_num].weights.append(random.random())
                else:
                    self.output_layer.neurons[i_num].weights.append(weights[weight_num])
                weight_num += 1

    def inspect(self):
        print('..................')
        print('input inspect:',[i for i in self.inputs])
        print('..................')
        print('hidden inspect:')
        self.hidden_layer.inspect()
        print('..................')
        print('output inspect:')
        self.output_layer.inspect()
        print('..................')

    def forward(self,inputs):
        hidden_layer_outout = self.hidden_layer.forward(inputs)
        print('hidden_layer_outout',hidden_layer_outout)
        ouput_layer_ouput = self.output_layer.forward(hidden_layer_outout)
        print('ouput_layer_ouput',ouput_layer_ouput)
        return ouput_layer_ouput

    def train(self,x,y):
        ouput_layer_ouput = self.forward(x)

        # 求total / neto的偏导
        total_o_pd = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):
            total_o_pd[o] = self.output_layer.neurons[o].calculate_total_net_pd(y[o]) 

        # 求total / h的偏导 = total.1 / h的偏导 + total.2 / h的偏导
        total_neth_pd = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):
            total_h_pd = 0
            for o in range(len(self.output_layer.neurons)):
                total_h_pd += total_o_pd[o] * self.hidden_layer.neurons[h].weights[o]
            total_neth_pd[h] = total_h_pd * self.output_layer.neurons[h].calculate_output_net_pd()

        # 更新输出层神经元权重
        for o in range(len(self.output_layer.neurons)):
            for ho_w in range(len(self.output_layer.neurons[o].weights)):
                ho_w_gradient = total_o_pd[o] * self.output_layer.neurons[o].calculate_net_linear_pd(ho_w)
                self.output_layer.neurons[o].weights[ho_w] -= self.learning_rate * ho_w_gradient

        # 更新隐藏层神经元权重
        for h in range(len(self.hidden_layer.neurons)):
            for ih_w in range(len(self.hidden_layer.neurons[h].weights)):
                ih_w_gradient = total_neth_pd[h] * self.hidden_layer.neurons[h].calculate_net_linear_pd(ih_w)
                self.hidden_layer.neurons[h].weights[ih_w] -= self.learning_rate * ih_w_gradient

    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]
            self.forward(training_inputs)
            for o in range(len(training_outputs)):
                total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
        return total_error

class NeuralLayer:
    def __init__(self,neural_num,bias):
        self.bias = bias if bias else random.random()

        self.neurons = []

        for i in range(neural_num):
            self.neurons.append(Neuron(self.bias))

    def inspect(self):
        print('weights:',[neuron.weights for neuron in self.neurons])
        print('bias:',[neuron.bias for neuron in self.neurons])

    def get_output(self,inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs

    def forward(self,inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs

class Neuron:
    def __init__(self,bias):
        self.bias = bias
        self.weights = []

    def calculate_output(self,inputs):
        self.inputs = inputs
        total_net_outputs = self.calculate_total_net_output()
        self.output = self.sigmoid(total_net_outputs)
        return self.output

    def calculate_total_net_output(self):
        total = 0
        for i in range(len(self.inputs)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias

    def sigmoid(self,total_net_input):
        return 1 / (1 + math.exp(-total_net_input))

    def calculate_total_output_pd(self,total_output):
        return -(total_output - self.output)

    def calculate_output_net_pd(self):
        return self.output * (1 - self.output)

    def calculate_total_net_pd(self,total_output):
        return self.calculate_total_output_pd(total_output) * self.calculate_output_net_pd()

    def calculate_net_linear_pd(self,index):
        return self.inputs[index]

    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2
nn = NeuralNetwork(2, 2, 2, input_hidden_weights=[0.15, 0.2, 0.25, 0.3], input_hidden_bias=0.35,
                   hidden_output_weights=[0.4, 0.45, 0.5, 0.55], hidden_output_bias=0.6)
for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.99])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.99]]]), 9))
hidden_layer_outout [0.5932699921071872, 0.596884378259767]
ouput_layer_ouput [0.7513650695523157, 0.7729284653214625]
hidden_layer_outout [0.5932662857458805, 0.5968782561589732]
ouput_layer_ouput [0.7420894573166411, 0.775286681791022]
迭代1次 误差0.291028391
hidden_layer_outout [0.5932662857458805, 0.5968782561589732]
ouput_layer_ouput [0.7420894573166411, 0.775286681791022]
hidden_layer_outout [0.5932623797502049, 0.5968720205376407]
ouput_layer_ouput [0.7324789213021788, 0.7775850216973027]
迭代2次 误差 0.283547957
hidden_layer_outout [0.5932623797502049, 0.5968720205376407]
ouput_layer_ouput [0.7324789213021788, 0.7775850216973027]
hidden_layer_outout [0.5932582778661456, 0.5968656853189723]
ouput_layer_ouput [0.7225410209751278, 0.7798256906644981]
迭代3次 误差 0.275943973
hidden_layer_outout [0.5932582778661456, 0.5968656853189723]
ouput_layer_ouput [0.7225410209751278, 0.7798256906644981]
hidden_layer_outout [0.5932539857457403, 0.5968592652405116]
ouput_layer_ouput [0.7122866732919141, 0.7820107943242337]
迭代4次 误差0.268233041
...
...
...
hidden_layer_outout [0.5930355856011269, 0.5965960242813179]
ouput_layer_ouput [0.016365976874939202, 0.9836751989707726]
hidden_layer_outout [0.5930355857116121, 0.5965960243868048]
ouput_layer_ouput [0.016365393177895763, 0.9836757760229975]
迭代10000次 误差 4.0257e-05

迭代10000次后结果:

hidden_layer_outout [0.5930355856011269, 0.5965960242813179]

ouput_layer_ouput [0.016365976874939202, 0.9836751989707726]

hidden_layer_outout [0.5930355857116121, 0.5965960243868048]

ouput_layer_ouput [0.016365393177895763, 0.9836757760229975]

迭代10000次 误差 4.0257e-05

参考:https://www.cnblogs.com/charlotte77/p/5629865.html,感谢博主的分析

原文地址:https://www.cnblogs.com/xiaobingqianrui/p/11128359.html

时间: 2024-10-07 15:26:01

神经网络之反向传播算法实现的相关文章

深度学习基础--神经网络--BP反向传播算法

BP算法: 1.是一种有监督学习算法,常被用来训练多层感知机.  2.要求每个人工神经元(即节点)所使用的激励函数必须可微. (激励函数:单个神经元的输入与输出之间的函数关系叫做激励函数.) (假如不使用激励函数,神经网络中的每层都只是做简单的线性变换,多层输入叠加后也还是线性变换.因为线性模型的表达能力不够,激励函数可以引入非线性因素) 下面两幅图分别为:无激励函数的神经网络和激励函数的神经网络 如图所示,加入非线性激活函数后的差异:上图为用线性组合逼近平滑曲线来分割平面,下图为使用平滑的曲线

机器学习之五:神经网络、反向传播算法

一.逻辑回归的局限 在逻辑回归一节中,使用逻辑回归的多分类,实现了识别20*20的图片上的数字. 但所使用的是一个一阶的模型,并没有使用多项式,为什么? 可以设想一下,在原有400个特征的数据样本中,增加二次.三次.四次多项式,会是什么情形? 很显然,训练样本的特征数量将会拔高多个数量级,而且,更重要的,要在一个式子中拟合这么多的特征,其难度是非常大的,可能无法收敛到一个比较理想的状态. 也就是说,逻辑回归没法提供很复杂的模型. 因为其本质上是一个线性的分类器,擅长解决的是线性可分的问题. 那么

神经网络和反向传播算法推导

注:由于自己画图实在太难画,本文中基本所有插图来源于算法团子机器学习班,请勿转载 1.普通的机器学习模型: 其实,基本上所有的基本机器学习模型都可以概括为以下的特征:根据某个函数,将输入计算并输出.图形化表示为下图: 当我们的g(h)为sigmoid函数时候,它就是一个逻辑回归的分类器.当g(h)是一个只能取0或1值的函数时,它就是一个感知机.那么问题来了,这一类模型有明显缺陷:当模型线性不可分的时候,或者所选取得特征不完备(或者不够准确)的时候,上述分类器效果并不是特别喜人.如下例: 我们可以

【神经网络和深度学习】笔记 - 第二章 反向传播算法

上一章中我们遗留了一个问题,就是在神经网络的学习过程中,在更新参数的时候,如何去计算损失函数关于参数的梯度.这一章,我们将会学到一种快速的计算梯度的算法:反向传播算法. 这一章相较于后面的章节涉及到的数学知识比较多,如果阅读上有点吃力的话也可以完全跳过这一章,把反向传播当成一个计算梯度的黑盒即可,但是学习这些数学知识可以帮助我们更深入的理解神经网络. 反向传播算法的核心目的是对于神经网络中的任何weight或bias计算损失函数$C$关于它们的偏导数$\frac{\partial C}{\par

机器学习之反向传播算法

Thoughts of Algorithms 博客园 首页 联系 订阅 管理 随笔 - 54  文章 - 1  评论 - 141 机器学习公开课笔记(5):神经网络(Neural Network)--学习 这一章可能是Andrew Ng讲得最不清楚的一章,为什么这么说呢?这一章主要讲后向传播(Backpropagration, BP)算法,Ng花了一大半的时间在讲如何计算误差项δδ,如何计算ΔΔ的矩阵,以及如何用Matlab去实现后向传播,然而最关键的问题--为什么要这么计算?前面计算的这些量到

DL4NLP——神经网络(一)前馈神经网络的BP反向传播算法步骤整理

这里把按[1]推导的BP算法(Backpropagation)步骤整理一下,备忘使用.[1] 中直接使用矩阵微分的记号进行推导,整个过程十分简洁.而且这种矩阵形式有一个非常大的优势就是对照其进行编程实现时非常方便. 但其实用标量计算推导也有一定的好处,比如可以清楚地知道某个权重是被谁所影响的. 记号约定: $L$:神经网络的层数.输入层不算. $n^l$:第 $l$ 层神经元的个数.偏置神经元不算在内. $W^{l}\in\mathbb R^{n^l\times n^{l-1}}$:第 $l-1

神经网络训练中的Tricks之高效BP(反向传播算法)

神经网络训练中的Tricks之高效BP(反向传播算法) 神经网络训练中的Tricks之高效BP(反向传播算法) [email protected] http://blog.csdn.net/zouxy09 Tricks!这是一个让人听了充满神秘和好奇的词.对于我们这些所谓的尝试应用机器学习技术解决某些问题的人,更是如此.曾记得,我们绞尽脑汁,搓手顿足,大喊“为什么我跑的模型不work?”,“为什么我实现的效果那么差?”,“为什么我复现的结果没有他论文里面说的那么好?”.有人会和你说“你不懂调参!

循环神经网络(RNN)模型与前向反向传播算法

在前面我们讲到了DNN,以及DNN的特例CNN的模型和前向反向传播算法,这些算法都是前向反馈的,模型的输出和模型本身没有关联关系.今天我们就讨论另一类输出和模型间有反馈的神经网络:循环神经网络(Recurrent Neural Networks ,以下简称RNN),它广泛的用于自然语言处理中的语音识别,手写书别以及机器翻译等领域. 1. RNN概述 在前面讲到的DNN和CNN中,训练样本的输入和输出是比较的确定的.但是有一类问题DNN和CNN不好解决,就是训练样本输入是连续的序列,且序列的长短不

《神经网络和深度学习》系列文章十六:反向传播算法代码

出处: Michael Nielsen的<Neural Network and Deep Learning>,点击末尾“阅读原文”即可查看英文原文. 本节译者:哈工大SCIR硕士生 李盛秋 声明:如需转载请联系[email protected],未经授权不得转载. 使用神经网络识别手写数字 反向传播算法是如何工作的 热身:一个基于矩阵的快速计算神经网络输出的方法 关于损失函数的两个假设 Hadamard积 反向传播背后的四个基本等式 四个基本等式的证明(选读) 反向传播算法 反向传播算法代码