神经网络模型反向传播代码完全解析

代码来源:https://www.cnblogs.com/charlotte77/p/5629865.html

#coding:utf-8
#与文章 https://www.cnblogs.com/charlotte77/p/5629865.html 中的步骤搭配使用
#该模型具有泛用性,即可以添加任意多的隐藏层,但需要修改train部分代码来连接新加入的层和原有的层,使其能够正常地向前和向后传递
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):
        #python中,放在类名下面的是类变量,可以通过类名直接调用;放在__init__()下面的是成员变量
        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#用来在hidden_layer_weights数组中寻址的
        for h in range(len(self.hidden_layer.neurons)):#对于每一个隐藏层的节点
            for i in range(self.num_inputs):#对隐藏层节点一一对应
                if not hidden_layer_weights:#如果hidden_layer_weights数组为空的话,就随机初始化
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:#在第h个节点中加入权重
                    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. 输出神经元的值
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)#初始化输出数组
        for i in range(len(self.output_layer.neurons)):
            # ?Etotal/?net输出,是总的误差对该节点的输入求导
            pd_errors_wrt_output_neuron_total_net_input[i] = self.output_layer.neurons[i].calculate_pd_error_wrt_total_net_input(training_outputs[i])

        # 2. 隐含层神经元的值
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):

            # dE/dy? = Σ ?E/?z? * ?z/?y? = Σ ?E/?z? * w??
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                #?Etotal/?net输出*输出层与隐藏层之间的weight,也即?net输出/?out隐藏,隐藏层目标节点对各个输出的节点的求导之和
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]

            #?Etotal/?net输出*?net输出/?out隐藏=输出层与隐藏层之间的weight * ?out隐/?net隐,用于更新隐藏层权重
            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)):#更新每一个节点的1每一个权重

                # ?E?/?w?? =  ?Etotal/?net输出 * ?net输出/?w??
                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)):

                # ?E?/?w? = ?E/?z? * ?z?/?w?
                #,这里的Z表示隐藏层的输入
                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]
            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):

        # 同一层的神经元共享一个截距项b,这是初始化,之后可以通过反向传播进行更新
        # 这种直接将偏置设计为节点的成员变量也挺好的,比较省事
        self.bias = bias if bias else random.random()

        self.neurons = []
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))#初始化layer,在数组中加入num_neurons个节点对象,并初始化一样的截距

    def inspect(self):#学到了!自下而上设计一个inspect函数,可以通过调用高级的inspect方法直接打印出所有的有用信息,便于调试
        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):
        self.inputs = inputs#这里的input是个数组
        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)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias#将计算结果加上偏置节点

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

    #?Etotal/?net=?Etotal/?out *?out/?net(用于输出层)
    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

    #?Etotal/?out,是总的误差对输出层的节点求导(用于输出层)
    def calculate_pd_error_wrt_output(self, target_output):
        return -(target_output - self.output)

    #?out/?net,是该节点的输出对该节点的输入求导(用于任何层)
    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)

    #?net/?w,net是下一层的输入,w是这个过程中的参数,这里的input指的就是输入到该节点的值(用于要更新的层)
    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)
#2个输入数据,2个隐藏节点,2个输出节点,初始化了输入节点和隐藏节点之间的权重为[0.15, 0.2, 0.25, 0.3],设置
for i in range(10000):#重复训练,一次训练是一次正向传播+一次反向传播
    #一次反向传播相当于对每个参数做了一次梯度下降,但是一次梯度下降显然不能达到最优化结果,所以需要多次运行
    #对于我们这个非常简单的问题,1000次运行可以达到0.0058的误差率,10000次就能达到10的-5次方的地步了
    nn.train([0.05, 0.1], [0.01, 0.09])#输入训练集的x和y向量
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))

#另外一个例子,可以把上面的例子注释掉再运行一下:

# training_sets = [
#     [[0, 0], [0]],
#     [[0, 1], [1]],
#     [[1, 0], [1]],
#     [[1, 1], [0]]
# ]

# nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
# for i in range(10000):
#     training_inputs, training_outputs = random.choice(training_sets)
#     nn.train(training_inputs, training_outputs)
#     print(i, nn.calculate_total_error(training_sets))

原文地址:https://www.cnblogs.com/jiading/p/11621236.html

时间: 2024-10-08 08:56:23

神经网络模型反向传播代码完全解析的相关文章

tensorflow 2.0 学习 (七) 反向传播代码逐步实现

数据集为: 代码为: 1 # encoding: utf-8 2 3 import tensorflow as tf 4 import numpy as np 5 import seaborn as sns 6 import matplotlib.pyplot as plt 7 from sklearn.datasets import make_moons 8 # from sklearn.datasets import make_circles 9 from sklearn.model_sel

神经网络模型及R代码实现

一.神经网络基本原理 神经元模型 图中x1~xn是从其他神经元传来的输入信号,wij表示表示从神经元j到神经元i的连接权值,θ表示一个阈值 ( threshold ),或称为偏置( bias ).则神经元i的输出与输入的关系表示为: 图中 yi表示神经元i的输出,函数f称为激活函数 ( Activation Function )或转移函数 ( Transfer Function ) ,net称为净激活(net activation).若将阈值看成是神经元i的一个输入x0的权重wi0,则上面的式子

VGG卷积神经网络模型解析

VGG卷积神经网络模型解析 一:VGG介绍与模型结构 VGG全称是Visual Geometry Group属于牛津大学科学工程系,其发布了一些列以VGG开头的卷积网络模型,可以应用在人脸识别.图像分类等方面,分别从VGG16-VGG19.VGG研究卷积网络深度的初衷是想搞清楚卷积网络深度是如何影响大规模图像分类与识别的精度和准确率的,最初是VGG-16号称非常深的卷积网络全称为(GG-Very-Deep-16 CNN),VGG在加深网络层数同时为了避免参数过多,在所有层都采用3x3的小卷积核,

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

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

神经网路(三) 反向传播直观理解

这是典型的三层神经网络的基本构成,Layer L1是输入层,Layer L2是隐含层,Layer L3是隐含层,我们现在手里有一堆数据{x1,x2,x3,...,xn},输出也是一堆数据{y1,y2,y3,...,yn},现在要他们在隐含层做某种变换,让你把数据灌进去后得到你期望的输出.如果你希望你的输出和原始输入一样,那么就是最常见的自编码模型(Auto-Encoder).可能有人会问,为什么要输入输出都一样呢?有什么用啊?其实应用挺广的,在图像识别,文本分类等等都会用到,我会专门再写一篇Au

《卷积神经网络的Python实现》PDF代码+《解析深度学习卷积神经网络原理与视觉实践》PDF分析

CNN正在革新几个应用领域,如视觉识别系统.自动驾驶汽车.医学发现.创新电子商务等.需要在专业项目或个人方案中利用复杂的图像和视频数据集来实现先进.有效和高效的CNN模型. 深度卷积网络DCNN是目前十分流行的深度神经网络架构,它的构造清晰直观,效果引人入胜,在图像.视频.语音.语言领域都有广泛应用. 深度学习,特别是深度卷积神经网络是人工智能的重要分支领域,卷积神经网络技术也被广泛应用于各种现实场景,在许多问题上都取得了超越人类智能的结果. <卷积神经网络的Python实现>作为深度学习领域

反向传播神经网络极简入门

反向传播神经网络极简入门 我一直在找一份简明的神经网络入门,然而在中文圈里并没有找到.直到我看到了这份162行的Python实现,以及对应的油管视频之后,我才觉得这就是我需要的极简入门资料.这份极简入门笔记不需要突触的图片做装饰,也不需要赘述神经网络的发展历史:要推导有推导,要代码有代码,关键是,它们还对得上.对于欠缺的背景知识,利用斯坦福大学的神经网络wiki进行了补全. 单个神经元 神经网络是多个“神经元”(感知机)的带权级联,神经网络算法可以提供非线性的复杂模型,它有两个参数:权值矩阵{W

基于tensorflow的MNIST手写字识别(一)--白话卷积神经网络模型

一.卷积神经网络模型知识要点卷积卷积 1.卷积 2.池化 3.全连接 4.梯度下降法 5.softmax 本次就是用最简单的方法给大家讲解这些概念,因为具体的各种论文网上都有,连推导都有,所以本文主要就是给大家做个铺垫,如有错误请指正,相互学习共同进步. 二.卷积神经网络讲解 2.1卷积神经网络作用 大家应该知道大名鼎鼎的傅里叶变换,即一个波形,可以有不同的正弦函数和余弦函数进行叠加完成,卷积神经网络也是一样,可以认为一张图片是由各种不同特征的图片叠加而成的,所以它的作用是用来提取特定的特征,举

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

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