第二章 使用Python进行DIY(附Python神经网络编程.pdf)

Python神经网络编程.pdf
链接: https://pan.baidu.com/s/1mZTCas8GVjLdSyNehFAHkg 提取码: que5

第二章 使用Python进行DIY

  • numpy模块创建3乘以2的数组

    import numpy
    a = numpy.zeros([3,2])
    print(a)
    
    [[0. 0.]
     [0. 0.]
     [0. 0.]]
    
  • 使用matplotlib给数组使用颜色

    import matplotlib.pyplot
    # %matplotlib inline # 确保绘制的图形在notebook上显示
    # 创建绘图的指令是imshow(),第一个参数是我们要绘制的数组。
    # 最后一项“interpolation”是告诉Python,不要为了让绘图看起来
    # 更加平滑而混合颜色,这是Python为了帮助我们而进行的缺省设置。
    matplotlib.pyplot.imshow(a, interpolation="nearest")
    

    image-20190313112224340

  • python类的一个简单实例

    class Dog:
        def __init__(self, petname, temp):
            self.name = petname;
            self.temperature = temp;
        
        def status(self):
            print("dog name is ", self.name)
            print("dog temperature is ", self.temperature)
            pass
        
        def setTemperature(self,temp):
            self.temperature = temp;
            pass
        
        def bark(self):
            print("woof!")
            pass
        
        pass
    
    lassie = Dog("Lassie", 37)
    print(lassie.status())
    lassie.setTemperature(40)
    print(lassie.status())
    
    dog name is  Lassie
    dog temperature is  37
    None
    dog name is  Lassie
    dog temperature is  40
    None
    
  • 使用Python制作神经网络

    勾勒一下神经网络类的大概样子,它应该至少有3个函数:

    • 初始化函数——设定输入层节点、隐藏层节点和输出层节点的数量。
    • 训练——学习给定训练集样本后,优化权重。
    • 查询——给定输入,从输出节点给出答案。

    代码框架:

    # neural network class definition
    class neuralNetwork:
        # initialise the neural network
        def __init__()
            pass
        
        # train the neural network
        def train()
            pass
        
        # query the neural network
        def query()
            pass
    
  • 初始化网络

    我们需要设置输入层节点、隐藏层节点和输出层节点的数量。

    这些节点数量定义了神经网络的形状和尺寸。

    我们不会将这 些数量固定,而是当我们使用参数创建一个新的神经网络对象时,才会确定这些数量。通过这种方式,我们保留了选择的余地,轻松地创建不同大 小的新神经网络。(在我们刚刚所做出决定中,其底层蕴含着一个重要意义。优秀的程序 员、计算机科学家和数学家,只要可能,都尽力创建一般代码,而不是具 体的代码。这是一种好习惯,它迫使我们以一种更深更广泛的适用方式思 考求解问题。如果能做到这点,就意味着我们的解决方案可以适用于不同 的场景。在此处,这意味着,我们将尽可能地为神经网络开发代码,使神 经网络保持尽可能多地开放有用的选项,并将假设降低到最低限度,从而 使代码很容易根据不同需要得到使用。我们希望同一个类可以创建一个小 型的神经网络,也可创建一个大型的神经网络——只需传递所需的大小给 参数即可。)

    同时也请不要忘了学习率。

    __ init__()函数

    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate)
            # set number of nodes in each input, hidden, output layer
            self.inodes = inputnodes
            self.hnodes = hiddennodes
            self.onodes = outputnodes
            
            # learning rate
            self.lr = learningrate
            pass
    

    让我们使用所定义的神经网络类,尝试创建每层3个节点、学习率为 0.5的小型神经网络对象。

    # number of input, hidden and output nodes
    input_nodes = 3
    hidden_nodes = 3
    output_nodes = 3
    
    # learning rate is 0.5
    learning_rate = 0.5
    
    # create instance of neural network
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
    

    当然,这段代码创建了一个对象,但是由于我们还没有编码任何函数执行实际的工作,因此这个对象还没有任何用途。没关系,从小处着眼, 让代码逐步成长,在通往目标的途中,查找并解决问题,这是一种很好的技术。

  • 权重——网络的核心

    下一步是创建网络的节点和链接。网络中最重要的部分是链接权重, 我们使用这些权重来计算前馈信号、反向传播误差,并且在试图改进网络 时优化链接权重本身。

    • 在输入层与隐藏层之间的链接权重矩阵Winput_hidden,大小为hidden_nodes 乘以 input_nodes。
    • 在隐藏层和输出层之间的链接权重矩阵Whidden_output,大小为hidden_nodes乘以 output_nodes。
  • 生成3*3的数组,数组中的每个值都是0~1的随机值。

    numpy. random. rand(3,3)
    
    array([[0.95669773, 0.86421237, 0.76923625],
           [0.87079086, 0.84554158, 0.53004889],
           [0.79264941, 0.730875  , 0.6135984 ]])
    

    因为权重的范围在-1.0到+1.0之间。为了简单起见,我们可以将上面数组中 的每个值减去0.5,这样,在效果上,数组中的每个值都成为了-0.5到0.5之 间的随机值。

  • 创建初始权重矩阵

    numpy. random. rand(3,3) - 0.5
    
    array([[-0.39259059, -0.22955753,  0.24067198],
           [-0.4549577 ,  0.23078032,  0.06623471],
           [ 0.2263834 , -0.29358245,  0.40512887]])
    

    权重是神经 网络的固有部分,与神经网络共存亡,它不是一个临时数据集,不会随着 函数调用结束而消失。这意味着,权重必须也是初始化的一部分,并且可 以使用其他函数(如训练函数和查询函数)来访问。

  • 神经网络的心脏——创建两个链接权重矩阵

    # link weight matrices, wih and who
    # weights inside the arrays are w_i_j, where link is from node i to node j in the next layer
    # w11 w21
    # w12 w22 etc
    self.wih = (numpy.random.rand(self.hnodes, self.inodes) - 0.5)
    self.wh0 = (numpy.random.rand(self.onodes, self.hnodes) - 0.5)
    
  • 较复杂的权重——正态概率分布采样权重,其中平均值为0,标准方差为节点传入链接数目的开方, 即1/ 根号传入链接数目。

    具体代码???
    
  • query()函数

    query()函数接受神经网络的输入,返回网络的输出。这个功能非常简 单,但是,为了做到这一点,你要记住,我们需要传递来自输入层节点的 输入信号,通过隐藏层,最后从输出层输出。你还要记住,当信号馈送至 给定的隐藏层节点或输出层节点时,我们使用链接权重调节信号,还应用 S激活函数抑制来自这些节点的信号。

    下式显示了输入层 和隐藏层之间的链接权重矩阵Winput_hidden如何与输入矩阵I相乘,给出隐藏层节点的输 入信号。

    image-20190313141522980

    hidden_inputs = numpy.dot(self.wih, inputs)
    

    这一段简单的Python完成了所有的工作,将所有的输入与所有正确的 链接权重组合,生成了组合调节后的信号矩阵,传输给每个隐藏层节点。 如果下一次选择使用不同数量的输入层节点或隐藏层节点,不必重写这段 代码就可以进行工作。

    为了获得从隐藏层节点处出现的信号,我们简单地将S抑制函数应用 到每一个出现的信号上:

    image-20190313141815991

    Scipy python库里的sigmoid函数叫expit()

    # scipy.special for the sigmoid function expit()
    import scipy.special
    

    在神经网络初始化部分的代码内部,下列代码定义了希望使用的激活函数。

    # activation function is the sigmoid function
    self.activation_function = lambda x : scipy.special.expit(x)
    

    这个函数接受了x,返回 scipy.special.expit(x),这就是S函数。使用lambda创建的函数是没有名字 的,经验丰富的程序员喜欢称它们为匿名函数,但是这里分配给它一个名 字self.activation_function()。所有这些事情意味着,无论何时任何人需要使用激活函数,那么他所需做的就是调用self.activation_function()。

    接下来,我们要将激活函数应用到组合调整后,准备进入隐藏层节点的信号。

    # calculate the signals emerging from hidden layer
    hidden_outputs = self.activation_function(hidden_inputs)
    

    也就是说,隐藏层节点的输出信号在名为hidden_outputs的矩阵中。

    那么信号如何到达最终输出层呢?

    下面的代码总结了我们如何计算隐藏层信号和输出层信号。

    # calculate signals into hidden layer
    hidden_inputs = numpy.dot(self.wih, inputs)
    
    # calculate the signals emerging from hidden layer
    hidden_outputs = self.activation_function(hidden_inputs)
    
    # calculate signals into final output layer
    final_inputs = numpy.dot(self.who, hidden_outputs)
    
    # calculate the signals emerging from final output layer
    final_outputs = self.activation_function(final_inputs)
    
  • 在训练神经网络的过程中有两个阶段,第一个阶段就是计算输出, 如同query()所做的事情,第二个阶段就是反向传播误差,告知如何优化链接权重。

    下图显示的是所创建的小型网络,其中,在输入层、隐藏层和输出层 中,每层有3个节点,并且使用随机选择的输入(1.0,0.5,-1.5)查询网络。

    image-20190313144827421

  • 训练网络

    训练任务分为两个部分:

    第一部分,针对给定的训练样本计算输出。这与我们刚刚在query()函数上所做的没什么区别。

    第二部分,将计算得到的输出与所需输出对比,使用差值来指导网络权重的更新。

    def train(self, input_list, targets_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            targets = numpy.array(targets_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
            
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            
            pass
    

    我们使用完全相同的方式从输入层前馈信号到最终输出层,所以这段 代码与在query()函数中的几乎完全一样。

    由于需要使用包含期望值或目标答案的训练样本来训练网络——因此 唯一的区别是,这部分代码中有一个额外的参数,即在函数的名称中定义 的targets_list。

    接下来越来越接近神经网络工作的核心,即基于所计算输出与目标输出之间的误差,改进权重。

    首先需要计算误差,这个值等于训练样本所提供的预期目标输出值与 实际计算得到的输出值之差。这个差也就是将矩阵targets和矩阵 final_outputs中每个对应元素相减得到的。

    # error is the (target - actual)
    output_errors = targets - final_outputs
    

    我们可以计算出隐含层节点反向传播的误差。请回忆一下如何根据所 连接的权重分割误差,为每个隐藏层节点重组这些误差。对于这个计算过 程,我们得到了其矩阵形式:

    image-20190313150318511

    # hidden layer error is the output_errors, split by weigths, recombined at hidden nodes
    hidden_errors = numpy.dot(self.who.T, output_errors)
    

    这样,我们就拥有了所需要的一切,可以优化各个层之间的权重了。 对于在隐蔽层和最终层之间的权重,我们使用output_errors进行优化。对于 输入层和隐藏层之间的权重,我们使用刚才计算得到的hidden_errors进行优化。

    先前,我们得到了用于更新节点j与其下一层节点k之间链接权重的矩 阵形式的表达式:

    image-20190313150605719

    我们首先为隐藏层和最终层之间的权重进行编码。

    # update the weights for the links between the hidden and output layers
    self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
    

    然后用于输入层和隐藏层之间权重的代码也是类似的。我们只是利用对称性,重写代码,更换名字,这样它们指的就是神经网络的前一层了。

    # update the weights for the links between the input and hidden layers
    self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outpyts)), numpy.transpose(inputs))
    
  • 完整的神经网络代码

    import numpy 
    # scipy.special for the sigmoid function expit() 
    import scipy.special
    
    '''
    这些代码可用于创建、训练和查询3层神经网络,进行几乎任何任务, 这么看来,代码不算太多。
    '''
    # neural network class definition
    class neuralNetwork:
        # initialise the neural network
        def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
            # set number of nodes in each input, hidden, output layer
            self.inodes = inputnodes
            self.hnodes = hiddennodes
            self.onodes = outputnodes
            
            # link weight matrices, wih and who
            # weights inside the arrays are w_i_j, where link is from node i to node j in the next layer
            # w11 w21
            # w12 w22 etc
            self.wih = (numpy.random.rand(self.hnodes, self.inodes) - 0.5)
            self.who = (numpy.random.rand(self.onodes, self.hnodes) - 0.5)
            # learning rate
            self.lr = learningrate
            
            # activation function is the sigmoid function
            self.activation_function = lambda x : scipy.special.expit(x)
            pass
        
        # train the neural network
        def train(self, inputs_list, targets_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            targets = numpy.array(targets_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
            
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            # error is the (target - actual)
            output_errors = targets - final_outputs
            # hidden layer error is the output_errors, split by weigths, recombined at hidden nodes
            hidden_errors = numpy.dot(self.who.T, output_errors)
            
            # update the weights for the links between the hidden and output layers
            self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
            # update the weights for the links between the input and hidden layers
            self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
            pass
        
        # query the neural network
        def query(self, inputs_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
    
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
    
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
    
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            return final_outputs
        
    # number of input, hidden and output nodes
    input_nodes = 3
    hidden_nodes = 3
    output_nodes = 3
    
    # learning rate is 0.5
    learning_rate = 0.5
    
    # create instance of neural network
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
    n.query([1.0, 0.5, -1.5])
    
    array([[0.49313182],
           [0.54598096],
           [0.51478851]])
    
  • 手写数字的数据集MNIST

    • 训练集是用来训练神经网络的60 000个标记样本集。标记 是指输入与期望的输出匹配,也就是答案应该是多少。

    • 可以使用较小的只有10 000个样本的测试集来测试我们的想法或算法 工作的好坏程度。由于这也包含了正确的标记,因此可以观察神经网络是 否得到正确的答案。

    • 将训练和测试数据集分开的想法,是为了确保可以使用神经网络之前 没有见过的数据进行再次测试。否则,我们就可以采用欺骗手段,让神经 网络简单地记忆训练数据,得到一个完美、但是有欺骗性的得分。在整个 机器学习领域,将测试数据与训练数据分开是一种很常见的想法。

      image-20190313152644383

      • 第一个值是标签,即书写者实际希望表示的数字,如“7”或“9”。这是我们希望神经网络学习得到的正确答案。
      • 随后的值,由逗号分隔,是手写体数字的像素值。像素数组的尺寸是 28 乘以28,因此在标签后有784个值。如果想知道这是否有784个值, 可以一个一个地数一下。
      • 第一个数字是“5”,这是标签,并且其余的784个数字是构成图像像素的颜色值。这些颜色值介于0和255之间。

      因此,第一个记录表示数字“5”,就是所显示的第一个值,这行文本的 其余部分是某人的手写数字5的像素值。第二个记录表示数字“0”,第三个 记录表示数字“4”,第四个记录表示“1”,第五个表示“9”。你可以从MNIST 数据文件中挑选任一行,第一个数字告诉你接下来图像数据的标签是什 么。

  • 使用Python打开文件并获取其中的内容

    data_file = open("mnist_dataset/mnist_train_100.csv",'r')
    data_list = data_file.readlines()
    data_file.close()
    
    • open()函数创建了此文件的一个文件句柄、一个 引用,我们将这个句柄分配给命名为data_file的变量。现在已经打开了文 件,任何进一步的操作,如读取文件,都将通过句柄完成。

    • data _list这个变量包含了一个列表,列表中的一项是表示文件中一行的字符串。data_list [0] 是第一条记录,data_list [9]是第十条记录,以此类推。

      (顺便说一句,由于readlines()会将整个文件读取到内存中,因此你可能 会听到别人告诉你不要使用这种方法。他们会告诉你,一次读取一行,对 这行进行所需要进行的操作,然后移动到下一行。他们都没有错,不要将 整个文件读入内存中,而是一次在一行上工作,这更有效率。但是,我们 的文件不是很大,如果使用readlines(),那么代码相对容易一些,对我们而 言,在学习Python时简单和清晰是很重要的。)

    • 最后一行代码关闭文件。在用完如文件这样的资源后,关闭和清理文 件是一种很好的做法。如果不这样做,文件依然开着,这可能会造成问 题。什么问题呢?有些程序可能不希望写入处在打开状态的文件,以免导 致不一致。这就像是两个人试图在同一张纸上写信!有时候,计算机可能 会锁定文件,防止发生这种冲突。如果使用完文件不清理,那么你就有一 堆锁定的文件。最起码应该关闭文件,让计算机释放用于保存文件的部分 内存。

  • 使用imshow()函数绘制数字矩形数组

    • 先将由逗号分隔,长的文本字符串值,拆分成单个值,在逗号处进行分割。
    • 忽略第一个值,这是标签,将剩余的28 × 28 = 784个值转换成28列28 行的数组。
    • 绘制数组!
    import numpy
    import matplotlib.pyplot
    %matplotlib inline
    
    all_values = data_list[0].split(',')
    image_array = numpy.asfarray(all_values[1:]).reshape((28,28))
    matplotlib.pyplot.imshow(image_array, cmap='Greys', interpolation='None')
    

    image-20190313155248471

    可以看到绘制的图像是5,这就是标签所表示的预期数字。如果转而选择下一条记录data_list [1],就可以得到图片0。

  • 准备MNIST训练数据

    我们需要做的第一件事情是将输入颜色值从较大的0到255的范围,缩放至较小的0.01到1.0的范围。

    将在0到255范围内的原始输入值除以255,就可以得到0到1范围的输入值。

    然后,需要将所得到的输入乘以0.99,把它们的范围变成0.0 到0.99。 接下来,加上0.01,将这些值整体偏移到所需的范围0.01到1.00。

    scaled_input = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
    print(scaled_input)
    

    输出确认,这些值当前的范围为0.01到0.99。

    image-20190313155950544

    我们已经通过缩放和移位让MNIST数据准备就绪,可以输入神经网络进行训练和查询了。

  • 思考神经网络的输出

    先前,我们看到输出值应该匹配激活函数可以输出值的范围。我们使用的逻辑函数不能输出如-2.0或 255 这样的数字,能输出的范围为0.0到1.0,事实上不能达到0.0或1.0,这是逻 辑函数的极限值,逻辑函数仅接近这两个极限,但不能真正到达那里。因 此,看起来在训练时必须调整目标值。

    但是,实际上,我们要问自己一个更深层次的问题。输出应该是什么 样子的?这应该是图片答案吗?这意味着有28×28 = 784个输出节点。

    如果退后一步,想想要求神经网络做什么,我们会意识到,要求神经 网络对图像进行分类,分配正确的标签。这些标签是0到9共10个数字中的 一个。这意味着神经网络应该有10个输出层节点,每个节点对应一个可能 的答案或标签。如果答案是“0”,输出层第一个节点激发,而其余的输出节 点则保持抑制状态。如果答案是“9”,输出层的最后节点会激发,而其余的输出节点则保持抑制状态。下图详细阐释了这个方案,并显示了一些示例输出。

    image-20190313160456081

    现在,我们需要把这些想法转换成目标数组,用于神经网络的训练。

    如果训练样本的标签为“5”,那么需要创建输出节点的目标数组,其中除了对应于标签“5”的节点,其他所有节点的值应该都很小,这个数组看起来可能如[0,0,0,0,0,1,0,0,0,0]。

    我们将使用值0.01和0.99来代替0和1,这样标签为“5”的目标输出数组为[0.01, 0.01, 0.01, 0.01, 0.01, 0.99, 0.01, 0.01, 0.01, 0.01]。

    目标矩阵代码

    # output nodes is 10(example)
    onodes = 10
    targets = numpy.zeros(onodes) + 0.01
    targets[int(all_values[0])] = 0.09
    print(targets)
    
    # [0.09 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01]
    

    最后一行代码获得了MNIST数据集记录中的第一个元素,也就是训练目 标标签,将其从字符串形式转换为整数形式。请记住,从源文件读取的记 录是文本字符串,而不是数字。一旦转换完成,我们使用目标标签,将目 标列表的正确元素设置为0.99。标签“0”将转换为整数0,这与标签对应的 targets []中的索引是一致的,因此这看起来非常整洁。类似地,标签“9”将 转换为整数9,targets [9]确实是此数组的最后一个元素。

  • title

    # python notebook for make your own neural network
    # code for a 3-layer neural network, and code for learning the MNIST dataset
    import numpy 
    # scipy.special for the sigmoid function expit() 
    import scipy.special
    # library for plotting arrays 
    import matplotlib.pyplot 
    # ensure the plots are inside this notebook, not an external window 
    %matplotlib inline
    
    # neural network class definition
    class neuralNetwork:
        # initialise the neural network
        def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
            # set number of nodes in each input, hidden, output layer
            self.inodes = inputnodes
            self.hnodes = hiddennodes
            self.onodes = outputnodes
            
            # link weight matrices, wih and who
            # weights inside the arrays are w_i_j, where link is from node i to node j in the next layer
            # w11 w21
            # w12 w22 etc
            self.wih = (numpy.random.rand(self.hnodes, self.inodes) - 0.5)
            self.who = (numpy.random.rand(self.onodes, self.hnodes) - 0.5)
            # learning rate
            self.lr = learningrate
            
            # activation function is the sigmoid function
            self.activation_function = lambda x : scipy.special.expit(x)
            pass
        
        # train the neural network
        def train(self, inputs_list, targets_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            targets = numpy.array(targets_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
            
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            # error is the (target - actual)
            output_errors = targets - final_outputs
            # hidden layer error is the output_errors, split by weigths, recombined at hidden nodes
            hidden_errors = numpy.dot(self.who.T, output_errors)
            
            # update the weights for the links between the hidden and output layers
            self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
            # update the weights for the links between the input and hidden layers
            self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
            pass
        
        # query the neural network
        def query(self, inputs_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
    
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
    
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
    
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            return final_outputs
        
    # number of input, hidden and output nodes
    input_nodes = 784
    hidden_nodes = 100
    output_nodes = 10
    
    # learning rate is 0.3
    learning_rate = 0.3
    
    # create instance of neural network
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
    
    # load the mnist training data CSV file into a list 
    training_data_file = open("mnist_dataset/mnist_train_100.csv", 'r') 
    training_data_list = training_data_file.readlines() 
    training_data_file.close()
    
    # train the neural network
    
    # go through all records in the training data set 
    for record in training_data_list:
        # split the record by the ',' commas 
        all_values = record.split(',') 
        # scale and shift the inputs 
        inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 
        # create the target output values (all 0.01, except the desired label which is 0.99) 
        targets = numpy.zeros(output_nodes) + 0.01 
        # all_values[0] is the target label for this record 
        targets[int(all_values[0])] = 0.99 
        n.train(inputs, targets) 
        pass
    

    为什么选择784个输入节点呢?请记住,这是28×28的结果,即组成手 写数字图像的像素个数。

    选择使用100个隐藏层节点并不是通过使用科学的方法得到的。我们认 为,神经网络应该可以发现在输入中的特征或模式,这些模式或特征可以 使用比输入本身更简短的形式表达,因此没有选择比784大的数字。通过选 择使用比输入节点的数量小的值,强制网络尝试总结输入的主要特点。但 是,如果选择太少的隐藏层节点,那么就限制了网络的能力,使网络难以 找到足够的特征或模式,也就会剥夺神经网络表达其对MNIST数据理解的 能力。给定的输出层需要10个标签,对应于10个输出层节点,因此,选择 100这个中间值作为中间隐藏层的节点数量,似乎有点道理。

    这里应该强调一点。对于一个问题,应该选择多少个隐藏层节点,并 不存在一个最佳方法。同时,我们也没有最佳方法选择需要几层隐藏层。 就目前而言,最好的办法是进行实验,直到找到适合你要解决的问题的一 个数字。

  • 测试网络

    首先需要获得测试记录,这与用于获取训练数据的Python代码非常相似。

    # load the mnist test data CSV file into a list 
    test_data_file = open("mnist_dataset/mnist_test_10.csv", 'r') 
    test_data_list = test_data_file.readlines() 
    test_data_file.close()
    

    下图显示从测试数据集中取出第一条记录,查询当前 已得到训练的神经网络。

    可以看到,测试数据集的第一条记录具有标签“7”。这是当我们查询这 条记录时,我们希望神经网络给出的回答。

    # get the first test record
    all_values = test_data_list[0].split(',')
    # print the label
    print(all_values[0])
    
    # 7
    

    绘制像素值,使数据变成图像,我们确认该手写数字的确为“7”。

    image_array = numpy.asfarray(all_values[1:]).reshape((28,28))
    matplotlib.pyplot.imshow(image_array,cmap='Greys',interpolation='None')
    

    image-20190313195339086

    查询已得到训练的网络,生成了对应每个输出节点所输出的一串数 字。你很快就会发现,其中一个输出值比其他输出值大很多,且对应于标 签“7”。由于第一个元素对应于标签“0”,因此这就是第8个元素。

    n.query((numpy.asfarray(all_values[1:])/255.0 * 0.99) + 0.01)
    
    array([[0.14975374],
           [0.02160699],
           [0.01228465],
           [0.04529811],
           [0.06070987],
           [0.0029977 ],
           [0.02369097],
           [0.78854187],
           [0.01373058],
           [0.07526987]])
    

    成功了!

    这是一个需要细细品味的时刻。我们在本书中进行的辛勤工作都有了 价值!

    我们训练了神经网络,让神经网络告诉我们图片中所代表的数字是什 么。请记住,神经网络之前没有见过那张图片,它不是训练数据集的一部 分。因此,神经网络能够正确区分它从来没有见过的手写字符。这真是让 人印象深刻啊!

    只需几行简单的Python,我们就已经创建了一个神经网络,这个神经 网络可以执行许多人认为是具备人工智能的事情——它学会了识别人的笔迹图片。

  • 我们可以记录分数,来看看神经网络对数据集的其余记录有何表现。

    # test the neural network
    
    # scorecard for how well the network performs, initially empty
    scorecard = []
    
    # go through all the records in the test data set
    for record in test_data_list:
        # split the record by the ',' commas
        all_values = record.split(',')
        # correct answer is first value
        correct_label = int(all_values[0])
        print(correct_label, "correct label")
        # scale and shift the inputs
        inputs = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
        # query the network
        outputs = n.query(inputs)
        # the index of the highest value corresponds to the label
        label = numpy.argmax(outputs)
        print(label, "network's answer")
        # append correct or incorrect to list
        if (label==correct_label):
            # network's answer matches correct answer, add 1 to scorecard
            scorecard.append(1)
        else:
            # network's answer doesn't match correct answer, add 0 to scorecard
            scorecard.append(0)
            pass
        pass
    print(scorecard)
    
    7 correct label
    7 network's answer
    2 correct label
    1 network's answer
    1 correct label
    1 network's answer
    0 correct label
    0 network's answer
    4 correct label
    4 network's answer
    1 correct label
    1 network's answer
    4 correct label
    7 network's answer
    9 correct label
    4 network's answer
    5 correct label
    6 network's answer
    9 correct label
    7 network's answer
    [1, 0, 1, 1, 1, 1, 0, 0, 0, 0]
    

    循环可以使用测试数据集中的所有记录进行测试,在跳进这个循环之 前,创建一个空的列表,称为计分卡(scorecard),这个记分卡在测试每 条记录之后都会进行更新。

    可以看到,在循环内部,我们所做的与先前所做的一样,根据逗号拆 分文本记录,分离出数值。记下第一个数字,这是正确答案。然后,重新 调整剩下的值,让它们适合用于查询神经网络。

    我们将来自神经网络的回答保存在名为outputs的变量中。

  • 将测试成绩作为分数并打印出来,结束程序。

    # calculate the performance score, the fraction of correct answers
    scorecard_array = numpy.asarray(scorecard)
    print("performance=",scorecard_array.sum()/scorecard_array.size)
    
    # performance= 0.5
    
  • 使用完整数据集进行训练和测试

    使用60 000个训练样本训练简单的3层神经网络,然后使用10 000条记 录对网络进行测试,得到的总表现分数为0.9473。这个表现简直太棒了, 几乎是95%的准确率!

    image-20190313202832732

    这个略低于95%的准确性,可以与记录 在http://yann.lecun.com/exdb/mnist/ 网页的行业标准媲美。我们可以看到, 比起一些历史基准,这个准确率还是略胜一筹的,这里列出的最简单的神 经网络方法所表现的准确率为95.3%,而我们的神经网络的性能大致相 当。

    这一点也不糟糕。我们应该感到高兴,第一次尝试的简单神经网络就 实现了研究者所开发的专业神经网络的性能。

    顺便说一句,计算60 000个训练样本,每个样本的计算都需要进行一 组784个输入节点、经过100个隐藏层节点的前馈计算,同时还要进行误差 反馈和权重更新,即使对于一台快速的现代家用计算机而言,这一切也需 要花上一段时间,这一点都不令人吃惊。我的新笔记本计算机花了约2分钟 时间完成了训练循环。你的计算机应该也差不多。(我的笔记本大约是10秒钟。)

  • 一些改进:调整学习率

    原先的学习率是0.3,试一下将学习率翻倍,设置为0.6,看看提高学习率对整个网络的学习 能力是否有益。如果此时运行代码,会得到0.9047性能得分。这比以前更 糟。因此,看起来好像大的学习率导致了在梯度下降过程中有一些来回跳 动和超调。

    使用0.1的学习率再试一次。这次,性能有所改善,得到了0.9523分。 在性能上,这与网站上列出的具有1000个隐藏层节点的神经网络类似。我 们“以少胜多”了。

    如果继续设置一个更小的0.01学习率,会发生什么情况?性能没有变 得更好,得分为0.9241。因此,似乎过小的学习率也是有害的。

    性能与学习率的关系

    image-20190313203202316

    上图表明,学习率在0.1和0.3之间可能会有较好的表现,因此,尝试 0.2的学习率,得到0.9537的性能得分。比起0.1或0.3,这个表现确实好了一 些。我们可以绘制图表,对所发生的事情得到一种较好的认识,在其他情 况下,你也应该考虑这种方法——和一串数字相比,图表有助于更好地理 解!因此,我们将坚持使用0.2的学习率,这看起来似乎是MNIST数据集和 神经网络的甜蜜点。

  • 一些改进:多次运行

    接下来可以做的改进,是使用数据集,重复多次进行训练。

    有些人把训练一次称为一个世代。因此,具有10个世代的训练,意味 着使用整个训练数据集运行程序10次。为什么要这么做呢?特别是,如果 这次计算机花的时间增加到10或20甚至30分钟呢?这是值得的,原因是通 过提供更多爬下斜坡的机会,有助于在梯度下降过程中进行权重更新。

    试一下使用2个世代。由于现在我们在训练代码外围添加了额外的循 环,因此代码稍有改变。下面的代码显示了外围循环,将代码着色有助于 看到发生了什么。

    # train the neural network
    
    # epochs is the number of times the training data set is used for training
    epochs = 2
    for e in range(epochs):
        # go through all records in the training data set
        for record in training_data_list:
        # split the record by the ',' commas 
        all_values = record.split(',') 
        # scale and shift the inputs 
        inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 
        # create the target output values (all 0.01, except the desired label which is 0.99) 
        targets = numpy.zeros(output_nodes) + 0.01 
        # all_values[0] is the target label for this record 
        targets[int(all_values[0])] = 0.99 
        n.train(inputs, targets) 
        pass
    

    使用2个世代神经网络所得到的性能得分为0.9579,比只有1个世代的神经网络有所改进。

    就像调整学习率一样,让我们使用几个不同的世代进行实验并绘图, 以可视化这些效果。直觉告诉我们,所做的训练越多,所得到的性能越 好。有人可能会注意到,太多的训练实际上会过犹不及,这是由于网络过 度拟合训练数据,因此网络在先前没有见到过的新数据上表现不佳。不仅 是神经网络,在各种类型的机器学习中,这种过度拟合也是需要注意的。

    性能与世代数目的关系

    image-20190313203813998

    结果呈现出不可预测性。在大约5或7个世代时,有一个甜蜜点。在此 之后,性能会下降,这可能是过度拟合的效果。性能在6个世代的情况下下 降,这可能是运行中出了问题,导致网络在梯度下降过程中被卡在了一个 局部的最小值中。事实上,由于没有对每个数据点进行多次实验,无法减 小随机过程的影响,因此我们已经预见到结果会有各种变化。这就是为什 么保留了6个世代这个奇怪的点,这是为了提醒我们,神经网络的学习过程 其核心是随机过程,有时候工作得不错,有时候工作得很糟。

    另一个可能的原因是,在较大数目的世代情况下,学习率可能设置过 高了。继续这个实验,将学习率从0.2减小到0.1,看看会发生什么情况。

    在7个世代的情况下,峰值性能高达0.9628或96.28%。

    下图显示了在学习率为0.1情况下,得到的新性能与前一幅图叠加的情 况。

    image-20190313203912007

    可以看到,在更多世代的情况下,减小学习率确实能够得到更好的性 能。0.9689的峰值表示误差率接近3%,这可以与Yann LeCun网站上的神经 网络标准相媲美了。

    直观上,如果你打算使用更长的时间(多个世代)探索梯度下降,那 么你可以承受采用较短的步长(学习率),并且在总体上可以找到更好的 路径,这是有道理的。确实,对于MNIST学习任务,我们的神经网络的甜 蜜点看起来是5个世代。请再次记住,我们在使用一种相当不科学的方式来 进行实验。要正确、科学地做到这一点,就必须为每个学习率和世代组合 进行多次实验,尽量减少在梯度下降过程中随机性的影响。

  • 改变网络形状

    试着改变中间隐藏层节点的数目。之前是100个。

    如果隐藏层节点太少,比如说3个,那么你可以想象,这不可能有足够 的空间让网络学习任何知识,并将所有输入转换为正确的输出。这就像要5 座车去载10个人。你不可能将那么多人塞进去。计算机科学家称这种限制 为学习容量。虽然学习能力不可能超过学习容量,但是可以通过改变车辆 或网络形状来增加容量。

    如果有10 000个隐藏层节点,会发生什么情况呢?虽然我们不会缺少 学习容量,但是由于目前有太多的路径供学习选择,因此可能难以训练网 络。这也许需要使用10 000个世代来训练这样的网络。

    性能与隐藏层节点的数目

    image-20190313204139567

    我们还创造了准确度的新纪录,使用200个节点,得分0.9751。使用 500个节点,运行较长的时间,我们的神经网络得到了0.9762分。相比于 Yann LeCun的网站上列出的基准,这是相当不错的成绩了。

  • 最终代码

    # python notebook for make your own neural network
    # code for a 3-layer neural network, and code for learning the MNIST dataset
    import numpy 
    # scipy.special for the sigmoid function expit() 
    import scipy.special
    # library for plotting arrays 
    import matplotlib.pyplot 
    # ensure the plots are inside this notebook, not an external window 
    %matplotlib inline
    
    # neural network class definition
    class neuralNetwork:
        # initialise the neural network
        def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
            # set number of nodes in each input, hidden, output layer
            self.inodes = inputnodes
            self.hnodes = hiddennodes
            self.onodes = outputnodes
            
            # link weight matrices, wih and who
            # weights inside the arrays are w_i_j, where link is from node i to node j in the next layer
            # w11 w21
            # w12 w22 etc
            self.wih = (numpy.random.rand(self.hnodes, self.inodes) - 0.5)
            self.who = (numpy.random.rand(self.onodes, self.hnodes) - 0.5)
            # learning rate
            self.lr = learningrate
            
            # activation function is the sigmoid function
            self.activation_function = lambda x : scipy.special.expit(x)
            pass
        
        # train the neural network
        def train(self, inputs_list, targets_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            targets = numpy.array(targets_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
            
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            # error is the (target - actual)
            output_errors = targets - final_outputs
            # hidden layer error is the output_errors, split by weigths, recombined at hidden nodes
            hidden_errors = numpy.dot(self.who.T, output_errors)
            
            # update the weights for the links between the hidden and output layers
            self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
            # update the weights for the links between the input and hidden layers
            self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
            pass
        
        # query the neural network
        def query(self, inputs_list):
            # convert inputs list to 2d array
            inputs = numpy.array(inputs_list, ndmin=2).T
            
            # calculate signals into hidden layer
            hidden_inputs = numpy.dot(self.wih, inputs)
    
            # calculate the signals emerging from hidden layer
            hidden_outputs = self.activation_function(hidden_inputs)
    
            # calculate signals into final output layer
            final_inputs = numpy.dot(self.who, hidden_outputs)
    
            # calculate the signals emerging from final output layer
            final_outputs = self.activation_function(final_inputs)
            return final_outputs
        
    # number of input, hidden and output nodes
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10
    
    # learning rate is 0.3
    learning_rate = 0.1
    
    # create instance of neural network
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
    
    # load the mnist training data CSV file into a list 
    training_data_file = open("mnist_dataset/mnist_train.csv", 'r') 
    training_data_list = training_data_file.readlines() 
    training_data_file.close()
    
    # train the neural network
    
    # epochs is the number of times the training data set is used for training
    epochs = 5
    for e in range(epochs):
        # go through all records in the training data set
        for record in training_data_list:
            # split the record by the ',' commas 
            all_values = record.split(',') 
            # scale and shift the inputs 
            inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 
            # create the target output values (all 0.01, except the desired label which is 0.99) 
            targets = numpy.zeros(output_nodes) + 0.01 
            # all_values[0] is the target label for this record 
            targets[int(all_values[0])] = 0.99 
            n.train(inputs, targets) 
            pass
       	pass
    
    # load the mnist test data CSV file into a list 
    test_data_file = open("mnist_dataset/mnist_test.csv", 'r') 
    test_data_list = test_data_file.readlines() 
    test_data_file.close()
    
    # test the neural network
    
    # scorecard for how well the network performs, initially empty
    scorecard = []
    
    # go through all the records in the test data set
    for record in test_data_list:
        # split the record by the ',' commas
        all_values = record.split(',')
        # correct answer is first value
        correct_label = int(all_values[0])
        #print(correct_label, "correct label")
        # scale and shift the inputs
        inputs = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
        # query the network
        outputs = n.query(inputs)
        # the index of the highest value corresponds to the label
        label = numpy.argmax(outputs)
        #print(label, "network's answer")
        # append correct or incorrect to list
        if (label==correct_label):
            # network's answer matches correct answer, add 1 to scorecard
            scorecard.append(1)
        else:
            # network's answer doesn't match correct answer, add 0 to scorecard
            scorecard.append(0)
            pass
        pass
    
    # calculate the performance score, the fraction of correct answers
    scorecard_array = numpy.asarray(scorecard)
    print("performance=",scorecard_array.sum()/scorecard_array.size)
    
    # performance= 0.9735
    

猜你喜欢

转载自blog.csdn.net/qq_39362996/article/details/88545698