BP神经网络学习笔记

本文章是学习笔记,内容可能会有很多漏洞,如果有不正确的地方欢迎指正

一、神经网络的结构与原理

在这里插入图片描述
在两个隐含层和隐含层输出层之间是有些许的线是没有进行连接的,这并不是说两者之间没有关系。
上图是由一个输入层,一个输出层和两个隐含层组成

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

注意:在第三条理解的时候,图像要从后往前看

在这里插入图片描述
对于权重来讲,累计的权重 z z z是有多少层,上标就有多少,而对于 ω \omega ω来讲则是,会比层数要少一。

二、BP神经网络公式推导

在这里插入图片描述
通过上图的公式进行计算,是非常难算出所有的权重,在对式子进行偏导的时候是十分困难的,但是BP算法的出现,可以较为简单的解决上述的问题。

在这里插入图片描述
我们从对所有样本的代价函数求偏导,转换成对其中任何一个样本求偏导,在对所有的偏导求累积和

因为最后的输出值是关于上一层的权重和b的函数,所以我们采用倒推的方式进行找规律(如下图所示)
在这里插入图片描述
在这里插入图片描述
k k k表示的是最后一层输出时的有几个激活值(输出值)
s n l {s_{n_l}} snl 表示的是 n l {n_l} nl层的节点有多少个,在这里是2,分别减去实际的标签,然后在平方,就得到上图的第一个公式。
从上图的第三个公式可知,第四次的权重累加和,是由前一层的权重,乘上前一层的激活值,再加上偏置的值。
在这里插入图片描述

下面再根据链式法则进一步的求偏导。对其中一个节点求偏导时,其他的可以当作是常数,将k换成 i i i 就可以了。 z i n l z_i^{n_l} zinl 正好是 n l − 1 n_l-1 nl1 所以可以进行替换

最后一个式子表示为误差。

对于最后一层,由于映射函数、 y i y_i yi z i n l z_i{n_l} zinl 都是已知的,所以 δ i ( n l ) \delta_i^{(n_l)} δi(nl) 此时已经是唯一确定的,前面几层是未知的。

接下来是倒数第二层:
在这里插入图片描述
需要将式子转化成同一层的,这样可以进行消除,便于计算。

注意:k表示当前层数的第k个神经元,而第k个神经元的 f ( z k ( n l ) ) f(z_k^{(n_l)}) f(zk(nl)) 受到前一层的所有的第 i 个 z i ( n l − 1 ) z_i^{({n_l}-1)} zi(nl1) 影响,所以有:
在这里插入图片描述
上图中,求偏导针对的是下图的那个权重,其实只是公式比较的复杂,原理还是比较的好理解。
在这里插入图片描述
隐藏层的误差,是由后一层的误差乘上当前层的权重,在乘上偏导数
在这里插入图片描述
然后根据这个公式可以得出以下的几个公式
在这里插入图片描述
整体的流程:
在这里插入图片描述
权重的更新实际上就是,用原值减去, α \alpha α 乘上当前层的激活值(输出值),在乘上后一层的误差。偏置,同样是这样算的。
在这里插入图片描述

i是上一层的(红框圈住的范围),j是后一层的。 i 层的误差, j 层的激活值(输出值),再用两层之间的权重减去, α \alpha α 乘上 i 层的误差, j 层的激活值(输出值),就可以更新权重了。

三、代码解读

代码资料:https://pan.baidu.com/s/11ySWBrYYeQXGUMaQA6ZtLg
提取码:9hf5

net = network.Network([784, 30, 10])  #初始化函数,new一个对象,传入一个向量

如果代码无法完全的理解,可以通过一段一段的输出。

class Network(object):
    # 网络初始化
    def __init__(self, sizes):
        """The list ``sizes`` contains the number of neurons in the
        respective layers of the network.  For example, if the list
        was [2, 3, 1] then it would be a three-layer network, with the
        first layer containing 2 neurons, the second layer 3 neurons,
        and the third layer 1 neuron.  The biases and weights for the
        network are initialized randomly, using a Gaussian
        distribution with mean 0, and variance 1.  Note that the first
        layer is assumed to be an input layer, and by convention we
        won't set any biases for those neurons, since biases are only
        ever used in computing the outputs from later layers."""
        self.num_layers = len(sizes)  # 各层神经元个数 784 30 10
        self.sizes = sizes
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]  # 偏置初始化 [30x1,10x1]
        self.weights = [np.random.randn(y, x)
                        for x, y in zip(sizes[:-1], sizes[1:])]  # 权重初始化 [30x784,10x30]   如果两者的长度不相等,则会通过取最短的哪一方进行合并压缩

    ## 1、小批量梯度下降法

 # 小批量梯度下降法
    def update_mini_batch(self, mini_batch, eta):
        """Update the network's weights and biases by applying
        gradient descent using backpropagation to a single mini batch.
        The ``mini_batch`` is a list of tuples ``(x, y)``, and ``eta``
        is the learning rate."""
        nabla_b = [np.zeros(b.shape) for b in self.biases]    # 将值全部置0,初始化
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:  # 还是一个一个样本进行反向传播计算,这里是一个一个的进行计算的
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        self.weights = [w - (eta / len(mini_batch)) * nw          # 只不过10个样本更新一次权重,一批只更新一次。因为是一批一批的更新所以,需要除以长度
                        for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b - (eta / len(mini_batch)) * nb
                       for b, nb in zip(self.biases, nabla_b)]

一个一个的进行计算,只不过10个样本更新一次权重,一批只更新一次。因为是一批一批的更新所以,需要除以长度。

小批量算法,没有用矩阵算法,会导致运算比较慢

    2、前向传播

# feedforward    前向传播算法
        activation = x             # 存储每层的z和a值
        activations = [x]  # list to store all the activations, layer by layer
        zs = []  # list to store all the z vectors, layer by layer
        for b, w in zip(self.biases, self.weights):     # b的个数有30个
            z = np.dot(w, activation) + b             # w:30 x 784 ,x1 = 784 x 1   x指前面不加 b 的运算得到的变量,再加上 30 x 1 的向量 得到第一层的 z
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)
            
#反向求导
# backward pass 计算最后一层的误差
        delta = self.cost_derivative(activations[-1], y) * \
                sigmoid_prime(zs[-1])
        nabla_b[-1] = delta    # 将误差存起来,把其他值全部算出来,在统一的更新
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())    #误差是10 x 1的向量
        # 计算从倒数第二层至第二层的误差
        for l in range(2, self.num_layers):    #计算隐含层
            z = zs[-l]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp    # delta 是后一层的 , sp 是偏导数
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())

        return (nabla_b, nabla_w)


 # 估值函数
    def evaluate(self, test_data):
        """Return the number of test inputs for which the neural
        network outputs the correct result. Note that the neural
        network's output is assumed to be the index of whichever
        neuron in the final layer has the highest activation."""
        test_results = [(np.argmax(self.feedforward(x)), y)    #返回最大索引值
                        for (x, y) in test_data]
        return sum(int(x == y) for (x, y) in test_results)

    def cost_derivative(self, output_activations, y):
        """Return the vector of partial derivatives \partial C_x /
        \partial a for the output activations."""
        return (output_activations - y)

具体的代码可以从链接上获取,学习。

四、改进神经网络的学习方法

在这里插入图片描述
   1、交叉熵代价函数
在这里插入图片描述
得到的结论是:如果代价函数是,误差平方和,并且激活函数是 sigmoid 函数,那么这种情况下,会出现梯度饱和。(误差平方和,s型函数,两个共同作用,导致偏导数非常的小,在误差非常大的情况下)
在这里插入图片描述
      1.1、引入交叉熵代价函数
在这里插入图片描述
以下是最终计算出来的公式
在这里插入图片描述
对交叉熵函数的应用总结:
在这里插入图片描述

注意:是输出神经元是S型函数时

发现最后是输出层的表达式,表示,当计算中间层时,还是出现梯度爆发、饱和等问题
在这里插入图片描述
1.2、柔性最大值(简要带过)
多分类问题一般会使用SoftMax
在这里插入图片描述
2、权重初始化

class Network(object):
    # 网络初始化
    def __init__(self, sizes):
        """The list ``sizes`` contains the number of neurons in the
        respective layers of the network.  For example, if the list
        was [2, 3, 1] then it would be a three-layer network, with the
        first layer containing 2 neurons, the second layer 3 neurons,
        and the third layer 1 neuron.  The biases and weights for the
        network are initialized randomly, using a Gaussian
        distribution with mean 0, and variance 1.  Note that the first
        layer is assumed to be an input layer, and by convention we
        won't set any biases for those neurons, since biases are only
        ever used in computing the outputs from later layers."""
        self.num_layers = len(sizes)  # 各层神经元个数 784 30 10
        self.sizes = sizes
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]  # 偏置初始化 [30x1,10x1]
        self.weights = [np.random.randn(y, x)
                        for x, y in zip(sizes[:-1], sizes[1:])]  # 权重初始化 [30x784,10x30]   如果两者的长度不相等,则会通过取最短的哪一方进行合并压缩

在示例的代码中,是以高斯分布的方式,对权重进行初始化。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、如何选择神经网络的超参数
3.1、先搞定二分类问题
在这里插入图片描述
先从小样本开始搞起,然后在慢慢的扩增
4、梯度的多种问题

4.1、梯度消失:
每次改变的都是后面的误差权重,前面的不改变,或者改变的比较慢。

4.2、梯度爆炸
初始化时,权重搞的特别的大,出现连乘大于1,导致梯度变的非常的大。

五、代码解读(二)

# -----------------------------------------第二部分------------------------------------
# --------------------------------一个简单改进的神经网络------------------------------------
# - network2.py example: L2范数罚 交叉熵代价函数 早停止策略 初始化策略更改 代价函数监控


from 新建文件夹.Pytorch.BPNetwork_better import mnist_loader
from 新建文件夹.Pytorch.BPNetwork_better import network2

training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
training_data = list(training_data)

net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
# net.large_weight_initializer()
net.SGD(training_data, 30, 10, 0.1, lmbda=5.0, evaluation_data=validation_data,
        monitor_evaluation_accuracy=True)     # 参数1:训练数据  参数2:训练的回合   参数3:批次尺寸  参数4:学习率  参数5:正则化参数5.0  参数6:评估数据使用的时验证集  参数7:可以监控准确率

代码改进的部分

  """
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]] # 偏差初始化方式不变
        self.weights = [np.random.randn(y, x)/np.sqrt(x)  # 将方差减少,避免饱和
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

添加了一些参数,方便监控

def SGD(self, training_data, epochs, mini_batch_size, eta,
            lmbda = 0.0,
            evaluation_data=None,
            monitor_evaluation_cost=False,
            monitor_evaluation_accuracy=False,
            monitor_training_cost=False,
            monitor_training_accuracy=False,
            early_stopping_n = 0):

设置了保存的函数,每跑多少代,进行保存

    def save(self, filename):
        """Save the neural network to the file ``filename``."""
        data = {"sizes": self.sizes,
                "weights": [w.tolist() for w in self.weights],
                "biases": [b.tolist() for b in self.biases],
                "cost": str(self.cost.__name__)}
        f = open(filename, "w")
        json.dump(data, f)
        f.close()

六、参考

https://www.bilibili.com/video/BV16t411Q7TM?p=7&spm_id_from=pageDriver

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值