神经网络详解及代码实现


原创不易,转载请标明出处谢谢。
链接: https://blog.csdn.net/weixin_41841149/article/details/95226984
BP算法是一种最有效的多层神经网络学习方法,其特点是信号前向传递,而误差后向传播,通过不断调节网络矩阵权重值,使得网络的最终输出与期望输出尽可能接近,以达到训练和预测的目的。本文侧重点在于公式推导。在写本篇文章时,期间反复阅读大佬 Alex的博客,谢谢大佬写出优秀文章供许多后来者学习。
本人能力有限,难免本博客存在问题,请不吝指正。

1 神经网络结构特点

下图为常见的神经网络
在这里插入图片描述
神经网络包含输入层,隐含层,输出层,上图就是4层神经网络(或称3层神经网络输入层不计入层数)。
下面声明一些符号和向量。
假设有m个输入样本:
在这里插入图片描述
令输入向量为:
在这里插入图片描述

令输出向量为:
在这里插入图片描述
第l隐含层输出为:
在这里插入图片描述
其sl为第l层神经元的个数。
设W(l)ij为从l-1层第j个神经元与l层第i个神经元之间的连接权重;b(l)i为第l层第i个神经元的偏置,那么:
在这里插入图片描述
zi(l)为l层第i个神经元的输入,f(⋅)为神经元的激活函数。通常在多层神经网络中采用非线性激活函数,而不是用线性激活函数,因为采用基于线性激活函数的多层神经网络本质上还是多个线性函数的叠加,其结果仍然为一个线性函数。如果神经网络结果仍然为一个线性函数,那不能就不能针对各种各样的需求生成各类复杂非线性的函数,其神经网络功能也大大减弱。从另一方面来说与其构建这么复杂的网络结构生成线性函数还不如直接用线性回归更快。

2 BP算法公式推导

有m个样本,神经网络共有L层(忽略输入层),第i个输入样本x(i)对应预计输出为H(i)
对于给定的m个训练样本,E(i)为单个样本误差,E为m个训练样本的误差。定义误差函数为:
在这里插入图片描述
BP算法关键在于求各层的权值参数偏导数。下面对偏导数的过程进行推导。先来看单个样本。
在这里插入图片描述
对于输出层:(sl 等于p)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于L-1层:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由上可推,第l层(2≤l≤L−1)的权值和偏置的偏导可以表示为:
在这里插入图片描述

3 向量化

==如果你能看懂第2节,那么这节会相对于简单,实质上在第二节基础上加上向量化,构成矩阵进行计算。==对样本进行训练时,有两种常见的方法:一种是每次只对单个样本进行训练,另一个是多个样本同时进行训练。多样本训练有多种方法,在这里只分析全部样本同时进行训练。随着神经网络处理一个较为复杂问题时,每次只对一个样本进行训练往往效率会很低。多个样本进行训练,构建一系列矩阵来进行一系列矩阵运算,充分利用CPU和GPU的并行运算的能力从而加快训练速度,所以很有必要神经网络的向量化。为了方便分析,构建一个2层神经网络。
若是单样本的输入:
在这里插入图片描述
若是多个样本的输入记为A0
在这里插入图片描述

3.1 前向传播

记输入有n个输入,第一层有n1个神经元,第二层有n2个神经元,但是有m个样本,所以需要一个n1xm的矩阵来存储从输入层传过来的数据,记该矩阵为A1
在这里插入图片描述
函数f为启动函数,Z1为偏置与输入的累加和。由此就可以获得m个样本第一层的神经元的输出。同理就可以获得第二层A2

3.2 反向更新

经过一次前向传播后,则需要进行反向更新权值。
对于第2层,即输出层:
对输出矩阵记为A2,那么误差err
在这里插入图片描述
由第2节所推的公式,w和b参数的公式有:
在这里插入图片描述
在这里插入图片描述
注:上两式公式有1/m,因为m个样例,所以结果你必须对m个结果求以平均。h表示第2层的神经元输出,a表示第一层的神经元输出。看懂这里的表达,可以自己对这些矩阵计算一下,结果就很快会懂了。
其它层的向量化,相信在你看懂前面两节后,自己也可以推导出来,我在这里就不重复造车了。

4 代码实现

'''
本程序能构建任意层的神经网络
执行分为两部分:
    第一部分正向传播:
    第二部分反向更新
create by Jiang at 2019.07.08
'''
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

keep_prob = 0.8

def sigmoid(x):
    return np.tanh(x)

# 函数sigmoid的派生函数
def dsigmoid(x):
    return 1.0 - np.multiply(x,x)

def mnist_extraction():
    '''
    该函数,获取数据。数据我已经下好了。
    没获取的情况下,代码会有略微不同,自己可以百度一下。
    :return: 返回加载好的数据
    '''
    mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
    train_nums = mnist.train.num_examples
    validation_nums = mnist.validation.num_examples
    test_nums = mnist.test.num_examples
    print('MNIST数据集的个数')
    print(' >>>train_nums=%d' % train_nums, '\n',
          '>>>validation_nums=%d' % validation_nums, '\n',
          '>>>test_nums=%d' % test_nums, '\n')

    '''2)获得数据值'''
    train_data = mnist.train.images  # 所有训练数据
    val_data = mnist.validation.images  # (5000,784)
    test_data = mnist.test.images  # (10000,784)
    print('>>>训练集数据大小:', train_data.shape, '\n',
          '>>>一副图像的大小:', train_data[0].shape)
    '''3)获取标签值label=[0,0,...,0,1],是一个1*10的向量'''
    train_labels = mnist.train.labels  # (55000,10)
    val_labels = mnist.validation.labels  # (5000,10)
    test_labels = mnist.test.labels  # (10000,10)
    # test_data = mnist.test.images
    # print('>>>训练集标签数组大小:', train_labels.shape, '\n',
    #       '>>>一副图像的标签大小:', train_labels[1].shape, '\n',
    #       '>>>一副图像的标签值:', train_labels[0])
    #
    # '''4)批量获取数据和标签【使用next_batch(batch_size)】'''
    # batch_size = 100  # 每次批量训练100幅图像
    # batch_xs, batch_ys = mnist.train.next_batch(batch_size)
    # print('使用mnist.train.next_batch(batch_size)批量读取样本\n')
    # print('>>>批量读取100个样本:数据集大小=', batch_xs.shape, '\n',
    #       '>>>批量读取100个样本:标签集大小=', batch_ys.shape)
    return mnist

class NN():

    def __init__(self,layers,mnist):

        L = len(layers)
        # 设置神经元权重参数和偏置参数
        self.W = {}
        self.b = {}

        for l in range(1,L):
            # print(layers[l])
            self.W["W" + str(l)] = np.mat(np.random.randn(layers[l], layers[l - 1]) * 0.1)
            self.b["b" + str(l)] = np.mat(np.random.randn(layers[l],1) * 0.1)

        self.n = {}
        for l in range(0,L):
            self.n["n" + str(l)] = layers[l]

        self.A = {}
        self.Z = {}
        self.cache = {}

        # 将数据写入类中
        self.mnist = mnist

    def forward_activation_02(self,L,flag):
        '''
        :param L: 神经网络的层数
        :param flag: flag为标记,是否启用dropOut正则化,flag = 1启用dropOut正则化
        如果你熟悉,dropout正则化,可以令flag = 0,关闭它。
        :return: 返回大致误差
        '''
        n,m = self.inputs.shape

        if n != self.n['n0']:
            raise ValueError('与输入层节点数不符')

        # 初始化输入
        self.A["A0"] = self.inputs
        for l in range(1,L):
            if flag == 0 or l == 1 or l == L-1:
                self.Z["Z" + str(l)] = self.W["W" + str(l)] * self.A["A" + str(l-1)] + self.b["b" + str(l)]
                self.A["A" + str(l)] = sigmoid(self.Z["Z" + str(l)])
            else:
                #启用dropout正则化
                self.d = np.random.rand(self.A["A" + str(l-1)].shape[0],self.A["A" + str(l-1)].shape[1])
                self.d = self.d < keep_prob
                self.A["A" + str(l-1)] = np.multiply(self.A["A" + str(l-1)],self.d)
                self.A["A" + str(l-1)] /= keep_prob
                self.Z["Z" + str(l)] = self.W["W" + str(l)] * self.A["A" + str(l - 1)] + self.b["b" + str(l)]
                self.A["A" + str(l)] = sigmoid(self.Z["Z" + str(l)])

        # 更新cache
        for l in reversed(range(1,L)):
            if l == L-1:
                self.cache["C" + str(l)] = np.multiply(self.A["A" + str(l)] - self.output,dsigmoid(self.A["A" + str(l)]))
            else:
                self.cache["C" + str(l)] = np.multiply(self.W["W" + str(l+1)].T*self.cache["C" + str(l+1)],
                                                       dsigmoid(self.A["A" + str(l)]))

        err = np.sum(np.abs(self.A["A"+str(L-1)]-self.output))
        return 1.0/2*err*err

    def backPropagate_02(self,learning_rate,L):
        alpha = learning_rate
        m = self.inputs.shape[1]

        for i in range(L):
            l = L - i - 1
            if l > 0:
                self.b['b' + str(l)] = self.b['b' + str(l)] - alpha * 1.0/m*(self.cache["C" + str(l)]*np.ones((m,1)))
                self.W['W' + str(l)] = self.W['W' + str(l)] - alpha * 1.0/m*(self.cache["C" + str(l)] * self.A["A" + str(l-1)].T)

    def init_prameter(self,batch_size):
        # 每次批量训练batch_size幅图像
        batch_xs, batch_ys = self.mnist.train.next_batch(batch_size)
        self.inputs = np.mat(batch_xs).transpose()
        self.output = np.mat(batch_ys).transpose()

    def train(self,iterations,learning_rate,L,batch_size):
        # 批处理训练
        m = 100 # m为批处理的次数
        err = 0.0
        for i in range(iterations):
            for j in range(m):
                self.init_prameter(batch_size)
                err += self.forward_activation_02(L,1)
                self.backPropagate_02(learning_rate,L)
            if i % 100 == 0:
                # print(self.A["A" +str(L-1)])
                print('第%d次迭代误差----%lf'%(i,err))
                err = 0.0

    def run(self,iterations,learning_rate,L,batch_size):
        self.train(iterations,learning_rate,L,batch_size)

    def test(self,L):
        batch_xs, batch_ys = self.mnist.test.next_batch(100)
        self.inputs = np.mat(batch_xs).transpose()
        self.output = np.mat(batch_ys).transpose()
        self.forward_activation_02(L,0)
        print("100个测试样本的实际结果:")
        print(self.output.transpose())
        print("100个测试样本的预测结果:")
        print(self.A["A"+str(L-1)].transpose())

if __name__ == '__main__':

    # 实例,构建一个三层的神经网络处识别手写字体。
    minst = mnist_extraction()
    #各层网络神经元的参数
    layers = [784,16,10]
    L = len(layers)
    nn = NN(layers,minst)
    nn.run(20500,0.2,L,100)
    nn.test(L)

参考文献

BP神经网络推导过程详解-Alex
AI遇见机器学习关注专栏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值