• 第四节,Neural Networks and Deep Learning 一书小节(上)


    最近花了半个多月把Mchiael Nielsen所写的Neural Networks and Deep Learning这本书看了一遍,受益匪浅。

    该书英文原版地址地址:http://neuralnetworksanddeeplearning.com/

    回顾一下这本书主要讲的内容

    1.使用神经网络识别手写数字

    作者从感知器模型引申到S型神经元。然后再到神经网络的结构。并用一个三层神经网络结构来进行手写数字识别,

    作者详细介绍了神经网络学习所使用到梯度下降法,由于当训练输入数量过大时,学习过程将变的时分缓慢,就引

    入了随机梯度下降的算法用来加速学习。

    选取二次代价函数

    神经网络的权重偏置更新法则如下:

    其中m是随机选取的m个训练样本,我们把这些随机训练样本标记为X1,X2,X3,..,Xm.。并把它们称为一个小批量数据。

    2.反向传播算法如何工作

     这一章作者主要介绍了反向传播的四个公式。并给出了反向传播算法的计算流程:

     以MNIST数据集为例,包含50000幅用于训练的手写图片,10000幅用于校验的手写图片,10000幅用于测试的手写图片。

    MNIST数据集下载地址:https://github.com/mnielsen/neural-networks-and-deep-learning

    1.输入训练集样本的集合

    2.初始化迭代期次数(epochs),开始循环 for i in range(epochs):

               2.1 打算输入训练集样本,按mini_batch_size(小批量大小)划分成许多组

               2.2 针对每一小批量数据应用随机梯度下降法,并更新权重和偏置(程序中update_mini_batch(self,mini_batch,eta)函数)

               2.3 一轮训练结束,用测试数据集检验准确率

    3.神经网络学习结束

    其中2.2步骤,尤为重要,针对小批量数据(mini_batch),如何应用随机梯度下降法,更新网络参数(update_mini_batch)

    1.输入小批量数据的集合 mini_batch

    2遍历每一个实例 (x,y),开始循环 for x,y in mini_batch:

             2.1计算每一个实例的梯度 (backprop(self,x,y)函数)

                      2.1.1 对每层l = 2,3,...,L(输入层记做l=1,输出层l=L),计算每一层带全权输入zl = wlal-1+bl,激活输出al = σ(zl)

                      2.1.2 计算输出层误差 δL=C/∂aLσ‘(zL),计算Cx/ωLL(aL-1)T,∂Cx/∂bLL。(注意当选择不同的代价函数时δL值是不一样,

                                当选择二次代价函数时,δL=(aL-y)σ‘(zL),当选择交叉熵代价函数时,δL=(aL-y))

                      2.1.3 反向传播误差,对每个l = L-1,L-2,...,2 计算δl = ((ωl+1)Tδl+1)σ‘(zl),计算∂Cx/∂ωll(al-1)T,∂Cx/∂bll

                      2.1.4 ∂Cx/∂ω = [∂Cx/∂ω2,∂Cx/∂ω3,...,∂Cx/∂ωL], ∂Cx/∂b = [∂Cx/∂b2,∂Cx/∂b3,...,∂Cx/∂bL]               

             2.2计算梯度的累积和,Σ∂Cx/∂ω,Σ∂Cx/∂b

      注意:步骤2中a,b,z,y,δl均是列向量

      为了提高计算的速度,步骤2我们可以采用向量化的计算方式。提示矩阵al可以写成al = [实例1al  实例2al   ....  实例mal ]

     3.应用随机梯度下降法权重偏置更新法则更新权重和偏置 ω = ω-η/mΣ∂Cx/∂ω,b = b-η/mΣ∂Cx/∂b

    选用三层神经网络,激活函数选取S型神经元,代价函数选取二次代价函数,实现程序如下:

     Network1.py:非向量化方式

    # -*- coding: utf-8 -*-
    """
    Created on Mon Mar  5 20:24:32 2018
    
    @author: Administrator
    """
    
    '''
    书籍:神经网络与深度学习
    第一章:利用梯度下降法训练神经网络算法  这里代价函数采用二次代价函数
    '''
    
    import numpy as np
    import random
    
    '''
    定义S型函数
    当输入z是一个向量或者numpy数组时,numpy自动地按元素应用sigmod函数,即以向量形式
    '''
    def sigmod(z):
        return 1.0/(1.0+np.exp(-z))
    
    
    '''
    定义S型函数的导数
    '''
    def sigmod_prime(z):
        return sigmod(z)*(1-sigmod(z))
        
        
    
    '''
    定义一个Network类,用来表示一个神经网络
    '''
    class Network(object):
        '''
        sizes:各层神经元的个数
        weights:权重,随机初始化,服从(0,1)高斯分布 weights[i]:是一个连接着第i层和第i+1层神经元权重的numpy矩阵 i=0,1...
        biases:偏置,随机初始化,服从(0,1)高斯分布 biases[i]:是第i+1层神经元偏置向量 i=0,1....
        '''
        def __init__(self,sizes):
            #计算神经网络的层数
            self.num_layers = len(sizes)
            #每一层的神经元个数
            self.sizes = sizes
            #随机初始化权重  第i层和i+1层之间的权重向量
            self.weights = [np.random.randn(y,x) for x,y in zip(sizes[:-1],sizes[1:])]
            #随机初始化偏置  第i层的偏置向量  i=1...num_layers
            self.biases = [np.random.randn(y,1) for y in sizes[1:]]     
            
        '''
        前向反馈函数,对于网络给定一个输入向量a,返回对应的输出
        '''    
        def  feedforward(self,a):        
            for b,w in zip(self.biases,self.weights):
                #dot矩阵乘法  元素乘法使用*
                a = sigmod(np.dot(w,a) + b)
            return a
        
        '''
        随机梯度下降算法:使用小批量训练样本来计算梯度(计算随机选取的小批量数据的梯度来估计整体的梯度)
        training_data:元素为(x,y)元祖的列表 (x,y):表示训练输入以及对应的输出类别  这里的输出类别是二值化后的10*1维向量
        epochs:迭代期数量 即迭代次数
        mini_batch:小批量数据的大小
        eta:学习率
        test_data:测试数据 元素为(x,y)元祖的列表 (x,y):表示训练输入以及对应的输出类别  这里的输出就是对应的实际数字 没有二值化   
        '''
        def SGD(self,training_data,epochs,mini_batch_size,eta,test_data=None):
            if test_data:
                #计算测试集样本个数
                n_test = len(test_data)
            #计算训练集样本个数        
            n = len(training_data)
            #进行迭代
            for j in range(epochs):
                #将训练集数据打乱,然后将它分成多个适当大小的小批量数据
                random.shuffle(training_data)            
                mini_batches = [training_data[k:k+mini_batch_size] for k in range(0,n,mini_batch_size)]
                #训练神经网络
                for mini_batch in mini_batches:
                    self.update_mini_batch(mini_batch,eta)
                    
                #每一次迭代后 都评估一次对测试集数据进行预测的准确率    
                if test_data:
                    print('Epoch {0}:  {1}/{2}'.format(j,self.evaluate(test_data),n_test))
                else:
                    print('Epoch {0} complete'.format(j))
                    
        '''
        mini_batch:小批量数据 元素为(x,y)元祖的列表 (x,y)
        eta:学习率
        对每一个mini_batch应用梯度下降,更新权重和偏置
        '''
        def update_mini_batch(self,mini_batch,eta):
            #初始化为0
            nabla_b = [np.zeros(b.shape) for b in self.biases]
            nabla_w = [np.zeros(w.shape) for w in self.weights]
            #依次对每一个样本求梯度,并求和
            for x,y in mini_batch:
                #计算每一个样本代价函数的梯度(∂Cx/∂ω∂Cx/∂b)
                delta_nabla_b,delta_nabla_w = self.backprop(x,y)
                #梯度分量求和 Σ∂Cx/∂ω
                nabla_b = [nb + dnb for nb,dnb in zip(nabla_b,delta_nabla_b)]
                #梯度分量求和 Σ∂Cx/∂b
                nabla_w = [nw + dnw for nw,dnw in zip(nabla_w,delta_nabla_w)]
            #更新权重 w = w -  η/m*Σ∂Cx/∂ω
            self.weights = [w - (eta/len(mini_batch))*nw for w,nw in zip(self.weights,nabla_w)]
            #更新偏置 b = b -  η/m*Σ∂Cx/∂b
            self.biases = [b - (eta/len(mini_batch))*nb for b,nb in zip(self.biases,nabla_b)]
            
            
        '''
        计算给定一个样本二次代价函数的梯度 单独训练样本x的二次代价函数 C = 0.5||y - aL||^2 = 0.5∑(yj - ajL)^2
        返回一个元组(nabla_b,nabla_w) = ∂Cx/∂ω,∂Cx/∂b) :和权重weights,偏置biases维数相同的numpy数组
        '''
        def backprop(self,x,y):
            #初始化与self.baises,self.weights维数一样的两个数组 用于存放每个训练样本偏导数的累积和
            nabla_b = [np.zeros(b.shape) for b in self.biases]
            nabla_w = [np.zeros(w.shape) for w in self.weights]
            #前向反馈
            activation = x
            #保存除了输入层外所有层的σ(z)的值
            activations = [x]
            #保存除了输入层外所有层的z的值
            zs = []
            #计算除了输入层外每一层z和σ(z)的值
            for b,w in zip(self.biases,self.weights):
                z = np.dot(w,activation) + b
                zs.append(z)
                activation = sigmod(z)
                activations.append(activation)
            
            #计算输出层误差
            delta = self.cost_derivative(activations[-1],y)*sigmod_prime(zs[-1])
            nabla_b[-1] = delta
            nabla_w[-1] = np.dot(delta,activations[-2].transpose())
            
    #计算反向传播误差
    for l in range(2,self.num_layers): z = zs[-l] sp = sigmod_prime(z) delta = np.dot(self.weights[-l+1].transpose(),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): #np.argmax返回最大值所在的索引 这里获取预测数值和实际数值组成元组的列表 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) ''' 计算损失函数的偏导数C/a a是实际输出 ''' def cost_derivative(self,output_activations,y): return (output_activations - y)

    import mnist_loader
    def  network_baseline():
        #遇到编码错误:参考链接http://blog.csdn.net/qq_41185868/article/details/79039604S
        #traning_data:[(784*1,10*1),...],50000个元素
        #validation_data[(784*1,1*1),....],10000个元素
        #test_data[(784*1,1*1),....],10000个元素
        training_data,validation_data,test_data = mnist_loader.load_data_wrapper()
        print('训练集数据长度',len(training_data))
        print(training_data[0][0].shape)      #训练集每一个样本的特征维数   (784,1)
        print(training_data[0][1].shape)      #训练集每一个样本对应的输出维数  (10,1)
        
        print('测试集数据长度',len(test_data))
        print(test_data[0][0].shape)         #测试机每一个样本的特征维数,1,1   (784,1)
        #print(test_data[0][1].shape)         #测试机每一个样本对应的输出维数   () 这里与训练集的输出略有不同,这里输出是一个数 并不是二指化后的10*1维向量
        print(test_data[0][1])               #7
           
        #测试
        net = Network([784,30,10])
        '''
        print(net.num_layers)      #3
        print(net.sizes)
        print(net.weights)
        print(net.biases)
        '''
        
        net.SGD(training_data,30,10,3.0,test_data=test_data)


    #运行程序
    network_baseline()

     向量化方式:

    # -*- coding: utf-8 -*-
    """
    Created on Mon Mar  5 20:24:32 2018
    
    @author: Administrator
    """
    
    '''
    书籍:神经网络与深度学习
    第一章:利用梯度下降法训练神经网络算法  这里代价函数采用二次代价函数
    这里采用向量化的方式,实现了正向传播和反向传播
    '''
    
    import numpy as np
    import random
    
    '''
    定义S型函数
    当输入z是一个向量或者numpy数组时,numpy自动地按元素应用sigmod函数,即以向量形式
    '''
    def sigmod(z):
        return 1.0/(1.0+np.exp(-z))
    
    
    '''
    定义S型函数的导数
    '''
    def sigmod_prime(z):
        return sigmod(z)*(1-sigmod(z))
        
        
    
    '''
    定义一个Network类,用来表示一个神经网络
    '''
    class Network(object):
        '''
        sizes:各层神经元的个数
        weights:权重,随机初始化,服从(0,1)高斯分布 weights[i]:是一个连接着第i层和第i+1层神经元权重的numpy矩阵 i=0,1...
        biases:偏置,随机初始化,服从(0,1)高斯分布 biases[i]:是第i+1层神经元偏置向量 i=0,1....
        '''
        def __init__(self,sizes):
            #计算神经网络的层数
            self.num_layers = len(sizes)
            #每一层的神经元个数
            self.sizes = sizes
            #随机初始化权重  第i层和i+1层之间的权重向量
            self.weights = [np.random.randn(y,x) for x,y in zip(sizes[:-1],sizes[1:])]
            #随机初始化偏置  第i层的偏置向量  i=1...num_layers
            self.biases = [np.random.randn(y,1) for y in sizes[1:]]     
               
            
        '''
        前向反馈函数,对于网络给定一个输入向量a,返回对应的输出
        '''    
        def  feedforward(self,a):        
            for b,w in zip(self.biases,self.weights):
                #dot矩阵乘法  元素乘法使用*
                a = sigmod(np.dot(w,a) + b)
            return a
        
        '''
        随机梯度下降算法:使用小批量训练样本来计算梯度(计算随机选取的小批量数据的梯度来估计整体的梯度)
        training_data:元素为(x,y)元祖的列表 (x,y):表示训练输入以及对应的输出类别  这里的输出类别是二值化后的10*1维向量
        epochs:迭代期数量 即迭代次数
        mini_batch:小批量数据的大小
        eta:学习率
        test_data:测试数据 元素为(x,y)元祖的列表 (x,y):表示训练输入以及对应的输出类别  这里的输出就是对应的实际数字 没有二值化   
        '''
        def SGD(self,training_data,epochs,mini_batch_size,eta,test_data=None):
            if test_data:
                #计算测试集样本个数
                n_test = len(test_data)
            #计算训练集样本个数        
            n = len(training_data)
            #进行迭代
            for j in range(epochs):
                #将训练集数据打乱,然后将它分成多个适当大小的小批量数据
                random.shuffle(training_data)            
                mini_batches = [training_data[k:k+mini_batch_size] for k in range(0,n,mini_batch_size)]
                #训练神经网络
                for mini_batch in mini_batches:
                    self.update_mini_batch(mini_batch,eta)
                    
                #每一次迭代后 都评估一次对测试集数据进行预测的准确率    
                if test_data:
                    print('Epoch {0}:  {1}/{2}'.format(j,self.evaluate(test_data),n_test))
                else:
                    print('Epoch {0} complete'.format(j))
                          
            
        '''
        mini_batch:小批量数据 元素为(x,y)元祖的列表 (x,y)
        eta:学习率
        对每一个mini_batch应用梯度下降,更新权重和偏置  采用向量化方式计算
        '''
        def update_mini_batch(self,mini_batch,eta):
             #初始化与self.baises,self.weights维数一样的两个数组 用于存放每个训练样本偏导数的累积和
            nabla_b = [np.zeros(b.shape) for b in self.biases]
            nabla_w = [np.zeros(w.shape) for w in self.weights]
            
            #实例数
            m = len(mini_batch)
            #print('小批量数目',m)
                    
            
            #把输入样本形式转换为 [样本1列向量 样本2列向量 ...  样本m列向量]的形式
            #[样本1类别列向量 样本2类别列向量 ...  样本m类别列向量]的形式
            training_x = [x for x,y in mini_batch]         #(10,(784,1))
            training_y = [y for x,y in mini_batch]         #(10,(10,1))  
                      
            #注意这里需要转置 
            X = np.array(training_x).reshape(m,training_x[0].shape[0]).T
            Y = np.array(training_y).reshape(m,training_y[0].shape[0]).T
            
            #print('X',X.shape)                      #(784,10)
            #print('Y',Y.shape)                      #(10,10) 
            
            #计算前向输出        
            activation = X
            #保存除了输入层外所有层的σ(z)的值
            activations = [X]
            #保存除了输入层外所有层的z的值
            zs = []
            #计算除了输入层外每一层z和σ(z)的值
            for b,w in zip(self.biases,self.weights):
                z = np.dot(w,activation) + b
                zs.append(z)
                activation = sigmod(z)
                activations.append(activation)
    
            #计算输出层误差
            delta = self.cost_derivative(activations[-1],Y)*sigmod_prime(zs[-1])         
            nabla_w[-1] = np.dot(delta,activations[-2].transpose())
            #按照行求和
            nabla_b[-1] = np.sum(delta,axis = 1, keepdims = True)                                
            
            #计算反向传播误差
            for l in range(2,self.num_layers):            
                delta = np.dot(self.weights[-l+1].transpose(),delta)*sigmod_prime(zs[-l])
                nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
                nabla_b[-l] = np.sum(delta,axis =1,keepdims=True)          
                
            #更新权重 w = w -  η/m*Σ∂Cx/∂ω
            self.weights = [w - eta/m*nw for w,nw in zip(self.weights,nabla_w)]
            #更新偏置 b = b -  η/m*Σ∂Cx/∂b
            self.biases = [b - eta/m*nb for b,nb in zip(self.biases,nabla_b)]
            
        
        
        '''
        对神经网络预测准确率进行评估
        '''
        def evaluate(self,test_data):
            #np.argmax返回最大值所在的索引  这里获取预测数值和实际数值组成元组的列表
            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)
        
        '''
        计算损失函数的偏导数∂C/∂a  a是实际输出  
        '''
        def cost_derivative(self,output_activations,y):
            return (output_activations - y)
        
        
    import mnist_loader
    
    def  network_baseline():
        #遇到编码错误:参考链接http://blog.csdn.net/qq_41185868/article/details/79039604S
        #traning_data:[(784*1,10*1),...],50000个元素
        #validation_data[(784*1,1*1),....],10000个元素
        #test_data[(784*1,1*1),....],10000个元素
        training_data,validation_data,test_data = mnist_loader.load_data_wrapper()
        print('训练集数据长度',len(training_data))
        print(training_data[0][0].shape)      #训练集每一个样本的特征维数   (784,1)
        print(training_data[0][1].shape)      #训练集每一个样本对应的输出维数  (10,1)
        
        print('测试集数据长度',len(test_data))
        print(test_data[0][0].shape)         #测试机每一个样本的特征维数,1,1   (784,1)
        #print(test_data[0][1].shape)         #测试机每一个样本对应的输出维数   () 这里与训练集的输出略有不同,这里输出是一个数 并不是二指化后的10*1维向量 
        print(test_data[0][1])               #7
           
        #测试
        net = Network([784,30,10])
        '''
        print(net.num_layers)      #3
        print(net.sizes)
        print(net.weights)
        print(net.biases)
        '''
        
        net.SGD(training_data,30,10,3,test_data=test_data)
    
    network_baseline()

     参考文章

    [1]深度神经网络(DNN)模型与前向传播算法

    [2]深度神经网络(DNN)反向传播算法(BP)

  • 相关阅读:
    java新手的session初体验
    菜鸟身份看泛型
    Java初学者不可不知的MyEclipse的设置技巧(自动联想功能)
    GCT之数学公式(几何部分)
    GCT之数学公式(代数部分)
    GCT之语文细节知识
    单元测试的方法
    常用的测试方法
    SQL 经典语句大全
    待处理(一)
  • 原文地址:https://www.cnblogs.com/zyly/p/8638856.html
Copyright © 2020-2023  润新知