BP+梯度下降算法推导

本篇博客来自其他博客以及论文消化吸收,假如读者熟悉BP网络正向传播,但是一直疑惑反向传播,那么可以继续看。

资料来源:

https://blog.csdn.net/SZU_Hadooper/article/details/78619575

https://www.jianshu.com/p/c7e642877b0e

http://www.cnblogs.com/charlotte77/p/5629865.html

假设一个网络如下:

第一层是输入层,包含两个神经元i1,i2,和截距项b1;注:截距项类似z=ax+by+c中的常数项c。

第二层是隐含层,包含两个神经元h1,h2和截距项b2;

第三层是输出层,包含两个神经元o1,o2。

每条线上标的wi是层与层之间连接的权重,激活函数我们默认为sigmoid函数。

现在对他们赋上初值,如下图:

 其中,输入数据  i1=0.05,i2=0.1;

    输出数据 o1=0.01,o2=0.99;

    初始权重  w1=0.15,w2=0.2,w3=0.25,w4=0.3;

                     w5=0.4,w6=0.45,w7=0.5,w8=0.55;

                             w=1

 目标:给出输入数据i1,i2(0.05和0.1),使输出尽可能与原始输出o1,o2(0.01和0.99)接近。



Step 1 前向传播

1.输入层······>隐含层:

计算神经元h1的输入加权和:

IN_{h1} = w_1 * i_1+w_2*i_2+b_1*1

IN_{h1} = 0.15*0.05+0.2*0.1+0.35*1=0.3775

计算神经元h1的输出:(此处用到激活函数为sigmoid函数):

OUT_{h1} = \frac{1}{1+e^{-IN_{h1}}}=\frac{1}{1+e^{-IN_{-0.3775}}}=0.593269992

同理:

计算神经元h2的输入加权和:

IN_{h2} = w_3 * i_1+w_2*i_4+b_1*1

IN_{h2} = 0.25*0.05+0.3*0.1+0.35*1=0.3925

计算神经元h2的输出:(此处用到激活函数为sigmoid函数):

OUT_{h2} = \frac{1}{1+e^{-IN_{h2}}}= \frac{1}{1+e^{-IN_{-0.3925}}}=0.56884378

2.隐含层---->输出层:

计算神经元o1的输入加权和:

IN_{o1} = w_5 * OUT_{h1}+w_6*OUT_{h2}+b_2*1

IN_{o1} = 0.4*0.593269992+0.45*0.596884378+0.6*1=1.105905967

计算神经元o2的输出:(此处用到激活函数为sigmoid函数):

OUT_{o1} = \frac{1}{1+e^{-IN_{o1}}}= \frac{1}{1+e^{-1.105905967}}=0.75136507

计算神经元o2的输入加权和:

IN_{o2} = w_7 * OUT_{h1}+w_8*OUT_{h2}+b_2*1

IN_{o2} = 0.5*0.593269992+0.55*0.596884378+0.6*1=1.224921404

计算神经元o2的输出:(此处用到激活函数为sigmoid函数):

OUT_{o2} = \frac{1}{1+e^{-IN_{o2}}}= \frac{1}{1+e^{-1.224921404}}=0.772928465

这样前向传播的过程就结束了,我们得到输出值为[0.75136507 , 0.772928465],与实际值[0.01 , 0.99]相差还很远,现在我们对误差进行反向传播,更新权值,重新计算输出。



Step 2 反向传播

1.计算总误差

总误差:(square error),取误差平方和然后除以2,为的是方便后面求导。

E_{total}=\sum \frac{1}{2}{(target-output)}^{2}

但是有两个输出,所以分别计算o1和o2的误差,总误差为两者之和:

E_{o1}=\frac{1}{2}{(target_{o1}-OUT_{o1})}^2=\frac{1}{2}{(0.01-0.75136507)}^2=0.274811083

E_{o2}=\frac{1}{2}{(target_{o2}-OUT_{o2})}^2=\frac{1}{2}{(0.99-0.772928465)}^2=0.023560026

E_{total}=E_{o1}+E_{o2}=0.274811083+0.023560026=0.298371109

问题来了:怎么样才能使得E_{total}=\sum \frac{1}{2}{(target-output)}^{2}取得最小值呢??

解析:

(1)E_{total}可以看成是嵌套的函数,嵌套关系如下(中间有省略,示意即可):

E_{total}=\frac{1}{2}{(0.01-OUT_{o1})}^2+\frac{1}{2}{(0.99-OUT_{o2})}^2

OUT_{o1}=\frac {1}{1+e^{-IN_{o1}}}

IN_{o1} = w_5 * OUT_{h1}+w_6*OUT_{h2}+b_2*1

OUT_{h1} = \frac{1}{1+e^{-IN_{h1}}}

IN_{h1} = w_1 * i_1+w_2*i_2+b_1*1

(2)从上述关系可以发现,忽略中间关系,宏观上E_{total}可以看做是i1,i2作为变量,而w1,w2,w3,w4,w5,w6,w7.w8作为参数的函数。

(3)当输入数据时,i1和i2就有了具体值i1=0.01,i2=0.99。虽然w1,w2,w3,w4,w5,w6,w7.w8此时有值,但是可以将其看作是变量,将E_{total}看做是关于w1,w2,w3,w4,w5,w6,w7.w8的一个函数,问题就转换成了求当E_{total}为最小值时w1,w2,w3,w4,w5,w6,w7.w8的取值。

(4)E_{total}的最小值并不好找,所以借助高数中的梯度来一点点向E_{total}的最小值靠近。

(4)举例,设函数f(x,y)=mx+ny+5,x,y为变量,m,n为参数,令x=2,y=6则函数变为f(m,n)=2m+6n+5,求f(m,n)取得最小值时m,和n的值。

 

补充:如何用梯度寻找最小值。

(1)梯度方向指的是函数值上升最快的方向,这个方向是局部的,什么意思?假设当前点沿着梯度方向走1米一直都是上升最快的,但是1米以后不再上升最快,梯度方向可能就变了,这也是为什么要设置\eta学习率(步长)的原因。

(2)另外,由于我们要的是最小值,应该找梯度下降最快的方向。所以梯度方向要取反,朝着最快的方向趋向最小值。

(3)梯度下降法有将求解带入局部最小值的可能。

(4)梯度概念就是求偏导数,本例中就是E_{total}分别对w1,w2,w3,w4,w5,w6,w7.w8求偏导数,这8个偏导数就是8个维度上的长度值,他们合向量方向就是梯度的方向。如果不理解,该补一补基础了,参考博文:https://blog.csdn.net/u010916338/article/details/81288309

(5)梯度下降算法单变量函数举例

假设单变量函数:

J(\Theta )=\Theta ^2

函数求导,可看做梯度:

J^`(\Theta )=2\Theta

设学习率为:

\eta =0.4

设初试点为:

\Theta ^0 = 1            注:0只是标记第一个位置,无其他含义。

根据梯度下降公式,下一个点的位置为:

\Theta ^1=\Theta ^0-\eta *J^`(\Theta ^0)=1-0.4*2=0.2         注,\Theta =0.2时的函数值,小于\Theta =1时的函数值。

\Theta ^2=\Theta ^1-\eta *J^`(\Theta ^1)=0.2-0.4*0.4=0.04        注,\Theta =0.04时的函数值,小于\Theta =0.2时的函数值。

(6)梯度下降算法多变量函数举例

假设多变量函数:

J(\Theta ) = \Theta _{1}^{2}+\Theta _{2}^{2}

我们通过观察就能发现最小值其实就是 (0,0)点。但是接下来,我们会从梯度下降算法开始一步步计算到这个最小值!

函数梯度为:

\bigtriangledown J(\Theta )=<2\Theta _{1},2\Theta _{2}>

设置学习率:

\eta =0.1

设置初试起点为:

\Theta ^0=(1,3)

根据梯度下降法求下一个点的位置:

\Theta ^1=\Theta ^0-\eta \bigtriangledown J(\Theta )=(1,3)-0.1(2,6)=(0.8,2.4)        注,\Theta =(0.8,2.4)时的函数值,小于\Theta =(1,3)时的函数值。

\Theta ^2=\Theta ^1-\eta \bigtriangledown J(\Theta ^1)=(0.8,2.4)-0.1(1.6,4.8)=(0.64,1.92)    注,\Theta =(0.64,1.92)时的函数值,小于\Theta =(0.8,2.4)时的函数值。

 

2.隐含层······>输出层的权值更新:

以修改权重参数w5为例,先用整体误差对w5求偏导(链式法则),为什么这样求后续再介绍。

\frac{\partial E_{total}}{\partial w_5}=\frac{\partial E_{total}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}*\frac{\partial IN_{o1}}{\partial w_5}

现在我们来分别计算每个式子的值:

计算\frac{\partial E_{total}}{\partial OUT_{o1}}

E_{total}= \frac{1}{2}{(target_{o1}-OUT_{o1})}^{2}+\frac{1}{2}{(target_{o2}-OUT_{o2})}^{2}

\frac{\partial E_{total}}{\partial OUT_{o1}}=OUT_{o1}-target_{o1}=0.75136507-0.01=0.74136507

计算\frac{\partial OUT_{o1}}{\partial IN_{o1}}

OUT_{o1}=\frac {1}{1+e^{-IN_{o1}}}       

\frac{\partial OUT_{o1}}{\partial IN_{o1}}=OUT_{o1}(1-OUT_{o1})=0.75136507(1-0.75136507)=0.186815602

补充:sigmod求导过程:

(\frac {1}{1+e^{-x}})^`=(\frac {e^x}{e^x+1})^`=\frac {e^x(e^x +1)-e^x*e^x}{(e^x+1)^2}=\frac {e^x}{e^x+1}-(\frac {e^x}{e^x+1})^2=\frac {e^x}{e^x+1}(1-\frac {e^x}{e^x+1})^2=\frac {1}{1+e^{-x}}(1-\frac {1}{1+e^{-x}})^2

计算\frac{\partial IN_{o1}}{\partial w_5}

IN_{o1} = w_5 * OUT_{h1}+w_6*OUT_{h2}+b_2*1

\frac{\partial IN_{o1}}{\partial w_5}=OUT_{h1}=0.593269992

最后三者相乘:

\frac{\partial E_{total}}{\partial w_5}=\frac{\partial E_{total}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}*\frac{\partial IN_{o1}}{\partial w_5}=0.74136507*0.186815602*0.593269992=0.082167041

更新w5权重值(\eta是学习率,这里取值0.5):

w_{5}^{+}=w_5 - \eta *\frac{\partial E_{total}}{\partial w_5}=0.4-0.5*0.082167041=0.35891648

同理更新w6,w7,w8:

w_{6}^{+}=0.408666186

w_{7}^{+}=0.511301270

w_{8}^{+}=0.561370121

另外一点比较重要,如下所示:

\frac{\partial E_{total}}{\partial w_5}=\frac{\partial E_{total}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}*\frac{\partial IN_{o1}}{\partial w_5}=(OUT_{o1}-target_{o1})*OUT_{o1}(1-OUT_{o1})*OUT_{h1}

\delta _{o1}=\frac{\partial E_{total}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}=\frac{\partial E_{total}}{\partial IN_{o1}}

\frac{\partial E_{total}}{\partial w_5}=\delta _{o1}*OUT_{h1}

\delta _{o1}用来表示输出层误差(显然输出层误差并不是这样的,如此表示是因为后续隐藏层的误差没法计算,是为了形式上的统一。具体用途结合后续隐藏层权重修改部分理解。)

注:简化公式的原因是为了编码的统一,为的是神经网络层统一定义,显然输出层的误差很好理解,但是隐藏层的误差不好理解,但是可以在形式上统一。

3.隐含层······>隐含层的权值更新:

以修改权重参数w1为例,先用整体误差对w1求偏导(链式法则),为什么这样求后续再介绍。

\frac{\partial E_{total}}{\partial w_1}=\frac{\partial E_{total}}{\partial OUT_{h1}}*\frac{\partial OUT_{h1}}{\partial IN_{h1}}*\frac{\partial IN_{h1}}{\partial w_1}

现在我们来分别计算每个式子的值:

计算\frac{\partial E_{total}}{\partial OUT_{h1}}

 

因为OUT_{h1}到最后总误差计算有岔路,所以没有办法用链式求导法则进行统一的求导计算,两条路用的加权和函数不同,函数形式相同,但是参数不同。

解决思路如下,将整体误差拆分成两个小误差进行计算。

\frac{\partial E_{total}}{\partial OUT_{h1}}=\frac{\partial {(E_{o1}+E_{o2})}}{\partial OUT_{h1}}=\frac{\partial E_{o1}}{\partial OUT_{h1}}+\frac{\partial E_{o2}}{\partial OUT_{h1}}

计算\frac{\partial E_{o1}}{\partial OUT_{h1}}

\frac{\partial E_{o1}}{\partial OUT_{h1}}=\frac{\partial E_{o1}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}*\frac{\partial IN_{o1}}{\partial OUT_{h1}}

\frac{\partial E_{o1}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}=0.74136507*0.186815602=0.138498562

IN_{o1}=w_5*OUT_{h1}+w_6*OUT_{h2}+b_2*1

\frac{\partial IN_{o1}}{\partial OUT_{h1}}=w_5=0.4

\frac{\partial E_{o1}}{\partial OUT_{h1}}=0.138498562*0.4=0.055399425

同理计算出:

\frac{\partial E_{o2}}{\partial OUT_{h1}}=-0.019049119

则:

\frac{\partial E_{total}}{\partial OUT_{h1}}=\frac{\partial {(E_{o1}+E_{o2})}}{\partial OUT_{h1}}=\frac{\partial E_{o1}}{\partial OUT_{h1}}+\frac{\partial E_{o2}}{\partial OUT_{h1}}=0.055399425-0.019049119=0.036350306

计算\frac{\partial OUT_{h1}}{\partial IN_{h1}}

OUT_{h1}=\frac {1}{1+e^{-IN_{h1}}}       

\frac{\partial OUT_{h1}}{\partial IN_{h1}}=OUT_{h1}(1-OUT_{h1})=0.59326999(1-0.59326999)=0.241300709

计算\frac{\partial IN_{h1}}{\partial w_1}

IN_{h1}=w_1*i_1+w_2*i_2+b_1*1

\frac{\partial IN_{h1}}{\partial w_1}=i_1=0.05

最后三者相乘:

\frac{\partial E_{total}}{\partial w_1}=\frac{\partial E_{total}}{\partial OUT_{h1}}*\frac{\partial OUT_{h1}}{\partial IN_{h1}}*\frac{\partial IN_{h1}}{\partial w_1}=0.036350306*0.241300709*0.05=0.000438568

更新w1权重值(\eta是学习率,这里取值0.5):

w_{1}^{+}=w_1 - \eta *\frac{\partial E_{total}}{\partial w_1}=0.15-0.5*0.000438568=0.149780716

同理更新w2,w3,w4:

w_{2}^{+}=0.19956143

w_{3}^{+}=0.24975114

w_{4}^{+}=0.29950229

公式简化

\frac{\partial E_{o1}}{\partial OUT_{h1}}=\frac{\partial E_{o1}}{\partial OUT_{o1}}*\frac{\partial OUT_{o1}}{\partial IN_{o1}}*\frac{\partial IN_{o1}}{\partial OUT_{h1}}

\frac{\partial E_{total}}{\partial w_1}=\frac{\partial E_{total}}{\partial OUT_{h1}}*\frac{\partial OUT_{h1}}{\partial IN_{h1}}*\frac{\partial IN_{h1}}{\partial w_1}

\frac{\partial E_{total}}{\partial w_1}=(\frac{\partial E_{o1}}{\partial OUT_{h1}}+\frac{\partial E_{o2}}{\partial OUT_{h1}})*\frac{\partial OUT_{h1}}{\partial IN_{h1}}*\frac{\partial IN_{h1}}{\partial w_1}

\frac{\partial E_{total}}{\partial w_1}=(\sum_{o=1}^{n} \frac{\partial E_{o}}{\partial OUT_o}*\frac{\partial OUT_{o}}{\partial IN_1}*\frac{\partial IN_{o}}{\partial OUT_{h1}})*\frac{\partial OUT_{h1}}{\partial IN_{h1}}*\frac{\partial IN_{h1}}{\partial w_1}(o为输出层神经元编号)

\frac{\partial E_{total}}{\partial w_1}=(\sum_{o=1}^{n} \delta _o*w_{ho})*OUT_{h1}(1-OUT_{h1})*i_1(解析\delta _o*w_{ho}w_{ho}为隐藏层···>输出层的权重,因为OUT_{h1}求导,所以剩下的就只有权重,左半边的\delta _o依然和输出层的公式简写一致,表示的是输出层的误差。)

\frac{\partial E_{total}}{\partial w_1}=\delta _{h1}*i_1(将(\sum_{o=1}^{n} \delta _o*w_{ho})*OUT_{h1}(1-OUT_{h1})看作是隐藏层的误差,记为:\delta _{h1},注意只是为了形式保持一致,便于编码。)

注:结合输出层公式简写来看,输出层和隐藏层对于权重的偏导数都可以简写成:误差乘以上层传入的数据(误差形式不同分别定义即可,但对于权重的偏导数却可以统一定义。)

注意:

       这样一次误差反向传播法就完成了,那么是不是可以接着输入新数据了呢?

       不是这样的,刚才输入的数据i1=0.05,i2=0.1还要在进行一次正向传输,如果输出误差仍然在可接受范围之外,那么就会一直循环上述操作,再反向传播,再正向传播。直到把误差压缩在可接受范围之内时结束,为了防止迭代次数过多而一直循环,还要设置迭代次数,当迭代次数用尽之后,也要自动跳出迭代。

        在这个例子中第一次迭代之后,总误差E(total)由0.298371109下降至0.291027924。迭代10000次后,总误差为0.000035085,输出为[0.015912196,0.984065734](原输入为[0.01,0.99]),证明效果还是不错的。

python代码实现:

# -*- coding: utf-8 -*-
"""
Created on Thu Oct 25 16:56:30 2018

@author: hrh
"""
#博客地址:http://www.cnblogs.com/charlotte77/p/5629865.html


#coding:utf-8
import random
import math


#
#   参数解释:
#   "pd_" :偏导的前缀
#   "d_" :导数的前缀
#   "w_ho" :隐含层到输出层的权重系数索引
#   "w_ih" :输入层到隐含层的权重系数的索引


#定义整个神经网络
class NeuralNetwork:
 
    
    #设置步长,学习率
    LEARNING_RATE = 0.5


    def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
        self.num_inputs = num_inputs
        #构造一个隐藏层
        self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
        #构造一个输出层
        self.output_layer = NeuronLayer(num_outputs, output_layer_bias)
        #初始化输入层到隐藏层权重
        self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
        #初始化隐藏层到输出层权重
        self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)
 
    
    #初始化进入隐藏层的权重
    def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):#hidden_layer_weights是个列表
        weight_num = 0
        for h in range(len(self.hidden_layer.neurons)):#隐藏层神经元个数
            for i in range(self.num_inputs):#输入层神经元个数
                if not hidden_layer_weights:
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])#估计是有些权重需要提前设定
                weight_num += 1


    #初始化进入输出层的权重
    def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
        weight_num = 0
        for o in range(len(self.output_layer.neurons)):
            for h in range(len(self.hidden_layer.neurons)):
                if not output_layer_weights:
                    self.output_layer.neurons[o].weights.append(random.random())
                else:
                    self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                weight_num += 1
  
    
    def inspect(self):
        print('------')
        print('* Inputs: {}'.format(self.num_inputs))
        print('------')
        print('Hidden Layer')
        self.hidden_layer.inspect()
        print('------')
        print('* Output Layer')
        self.output_layer.inspect()
        print('------')


    #前向传播
    def feed_forward(self, inputs):
        hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
        return self.output_layer.feed_forward(hidden_layer_outputs)

    
    def train(self, training_inputs, training_outputs):
        self.feed_forward(training_inputs)

        #1. 求输出误差error对上一层神经元in的偏导数
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):
            #求输出误差error对上一层神经元in的偏导数
            pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])

        #2. 求隐藏层误差,求隐藏层误差error对于上层input的偏导数。
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                #此处为计算隐藏层的输出误差,其实并不是真正的误差,只是在公式形式上和输出层误差形式类似。具体解释需要看博客。
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]
            #隐藏层总误差对于隐藏层输入input的偏导数。
            pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()

        #3. 修改隐藏层到输出层的权重。
        for o in range(len(self.output_layer.neurons)):
            for w_ho in range(len(self.output_layer.neurons[o].weights)):
                #误差对输出层权重的偏导数。
                pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)
                # Δw = α * ∂Eⱼ/∂wᵢ根据学习率修改输出层神经元权重。
                self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight

        #4. 更新隐含层的权重系数
        for h in range(len(self.hidden_layer.neurons)):
            for w_ih in range(len(self.hidden_layer.neurons[h].weights)):
                #计算隐藏层误差对于输入层到隐藏层的权重的偏导数。
                pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)
                # Δw = α * ∂Eⱼ/∂wᵢ根据学习率修改隐藏层神经元权重。
                self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight

    
    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]#list中的元素还是一个list
            self.feed_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 NeuronLayer:
    
    
    def __init__(self, num_neurons, bias):
        #偏置值
        self.bias = bias if bias else random.random()#python三目运算符
        #定义神经元列表
        self.neurons = []
        #按照神经元个数初始化神经元列表,偏置值作为属性赋值给所有的神经元。
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))


    def inspect(self):
        print('Neurons:', len(self.neurons))
        for n in range(len(self.neurons)):
            print(' Neuron', n)
            for w in range(len(self.neurons[n].weights)):
                print('  Weight:', self.neurons[n].weights[w])
            print('  Bias:', self.bias)


    #一层神经元的前向输出
    def feed_forward(self, inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs


    #一层神经元的前向输出,与上个函数功能相同,目前用途未知??????
    def get_outputs(self):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs


#定义单个神经元
class Neuron:
    
    
    #初始化设置截距
    def __init__(self, bias):
        #偏置
        self.bias = bias
        #上一层连接神经元的权重
        self.weights = []


    #计算神经元输出
    def calculate_output(self, inputs):
        #上一层输出都视为input,inputs是一个列表
        self.inputs = inputs
        #经过激活函数后的输出
        self.output = self.squash(self.calculate_total_net_input())
        return self.output


    #计算神经元输入的加权平均和
    def calculate_total_net_input(self):
        total = 0
        for i in range(len(self.inputs)):
            #上一层都视为input,权重指的是上一层连接神经元的连接权重,而不是下一层连接神经元的权重。
            total += self.inputs[i] * self.weights[i]
        #最后每个神经元的总input都加上偏置项
        return total + self.bias


    # 定义激活函数
    def squash(self, total_net_input):
        return 1 / (1 + math.exp(-total_net_input))


    #求输出误差error对上一层神经元in的偏导数
    def calculate_pd_error_wrt_total_net_input(self, target_output):
        return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();


    #计算神经元输出与目标值误差计算
    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2


    #求输出误差error对神经元out的偏导数
    def calculate_pd_error_wrt_output(self, target_output):
        return - (target_output - self.output)


    #求神经元输出out对于输入in的偏导数
    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)


    #求输入in对权重w的偏导数,求完以后其实就是上层的输入值
    def calculate_pd_total_net_input_wrt_weight(self, index):
        return self.inputs[index]



# 文中的例子:

nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)

for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.09])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))



        
        
        
        
        
        

 

 

 

  • 11
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值