0


深度学习初遇——自己动手实现三层神经网络

介绍

深度学习中,神经网络是最基础的数据结构,本文想自己动手实现一个神经网络,并使用它来对mnist数据集做预测。
本文内容参考了大神塔里克拉希德的著作《Python神经网络编程》,有兴趣的读者可以在网上找到这本书,或联系笔者,笔者免费赠送。

神经网络结构

神经网络的结构想必大家都很熟悉了,看张图吧,这里除了输入层外,其他层每个节点都有激活函数,比如sigmoid或relu等。
在这里插入图片描述
这里,权重很重要,在神经网络中,要更新的参数就是权重,而且后面我们会看到,误差的反向传播也要用到当前的权重。每两层之间的权重可以用一个矩阵表示,在理论研究和代码编程时,这很有用。

前馈

前馈的过程是根据输入数据,逐层计算输出值,进而得到输出层的输出值,给定权重(通过优化算法更新前的权重,最开始是按一定规则人为初始化的)和输入值,代入公式即可得到,这个操作很简单,但很重要,后面我们会看到,权重的更新需要用到后一层节点的输出值以及前一层节点的输出值(注意,我们是站在两层之间的地方),从上一小节的图示中,我们很容易有一个形象的感受。

误差的反向传播

我们通过当前权重和输入值计算出了输出值,而且训练数据的目标值是给定的,所以模型的误差也很容易算出来,有了整个模型的误差值,那还要每一层的误差做什么呢?或者为什么要进行误差反向传播呢?误差又是怎么进行反向传播的呢?
后面我们会看到,每一层权重的更新,都要用到下一层的误差值,所以反向传播很有必要,那它怎么传播呢?
如下图所示,一般地,误差会根据当前节点输入连接的权重进行拆分,假如右边节点的输出误差为

    e
   
  
  
   e
  
 
e,左边第一个节点的误差为

 
  
   
    e
   
   
    r
   
   
    
     r
    
    
     1
    
   
   
    =
   
   
    
     
      W
     
     
      
       1
      
      
       ,
      
      
       1
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        1
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        1
       
      
     
    
   
   
    ∗
   
   
    e
   
  
  
   err_1=\frac{W_{1,1}}{W_{1,1}+ W_{2,1}}*e
  
 
err1​=W1,1​+W2,1​W1,1​​∗e,左边第二个节点的误差为

 
  
   
    e
   
   
    r
   
   
    
     r
    
    
     2
    
   
   
    =
   
   
    
     
      W
     
     
      
       2
      
      
       ,
      
      
       1
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        1
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        1
       
      
     
    
   
   
    ∗
   
   
    e
   
  
  
   err_2=\frac{W_{2,1}}{W_{1,1}+ W_{2,1}}*e
  
 
err2​=W1,1​+W2,1​W2,1​​∗e

在这里插入图片描述
多个节点的误差会拆分后叠加到上一层中每一个节点的误差上去,例如下图所示
在这里插入图片描述
这时,

    e
   
   
    r
   
   
    
     r
    
    
     1
    
   
   
    =
   
   
    
     
      W
     
     
      
       1
      
      
       ,
      
      
       1
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        1
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        1
       
      
     
    
   
   
    ∗
   
   
    
     e
    
    
     1
    
   
   
    +
   
   
    
     
      W
     
     
      
       1
      
      
       ,
      
      
       2
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        2
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        2
       
      
     
    
   
   
    ∗
   
   
    
     e
    
    
     2
    
   
  
  
   err_1=\frac{W_{1,1}}{W_{1,1}+ W_{2,1}}*e_1+\frac{W_{1,2}}{W_{1,2}+ W_{2,2}}*e_2
  
 
err1​=W1,1​+W2,1​W1,1​​∗e1​+W1,2​+W2,2​W1,2​​∗e2​,左边第二个节点的误差为

 
  
   
    e
   
   
    r
   
   
    
     r
    
    
     2
    
   
   
    =
   
   
    
     
      W
     
     
      
       2
      
      
       ,
      
      
       1
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        1
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        1
       
      
     
    
   
   
    ∗
   
   
    
     e
    
    
     1
    
   
   
    +
   
   
    
     
      W
     
     
      
       2
      
      
       ,
      
      
       2
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        2
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        2
       
      
     
    
   
   
    ∗
   
   
    
     e
    
    
     2
    
   
  
  
   err_2=\frac{W_{2,1}}{W_{1,1}+ W_{2,1}}*e_1+\frac{W_{2,2}}{W_{1,2}+ W_{2,2}}*e_2
  
 
err2​=W1,1​+W2,1​W2,1​​∗e1​+W1,2​+W2,2​W2,2​​∗e2​

不断重复上述拆分,便可以计算出每一层的输出误差,在实际应用中,通常用权重代替上面公式中的分数部分,即使用

     W
    
    
     
      1
     
     
      ,
     
     
      1
     
    
   
  
  
   W_{1,1}
  
 
W1,1​代替

 
  
   
    
     
      W
     
     
      
       1
      
      
       ,
      
      
       1
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        1
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        1
       
      
     
    
   
  
  
   \frac{W_{1,1}}{W_{1,1}+ W_{2,1}}
  
 
W1,1​+W2,1​W1,1​​,使用

 
  
   
    
     W
    
    
     
      1
     
     
      ,
     
     
      2
     
    
   
  
  
   W_{1,2}
  
 
W1,2​代替

 
  
   
    
     
      W
     
     
      
       1
      
      
       ,
      
      
       2
      
     
    
    
     
      
       W
      
      
       
        1
       
       
        ,
       
       
        2
       
      
     
     
      +
     
     
      
       W
      
      
       
        2
       
       
        ,
       
       
        2
       
      
     
    
   
  
  
   \frac{W_{1,2}}{W_{1,2}+ W_{2,2}}
  
 
W1,2​+W2,2​W1,2​​,以此类推。这样利用权重矩阵和当前层的输出误差,便可以计算出前一层的误差。

权重更新

在机器学习中,对模型的优化一般采用梯度下降法,这就需要知道误差对更新参数的斜率,在神经网络中,我们需要知道输出误差对每一个权重的斜率。
求导的过程《Python神经网络编程》里面讲的很详细了,这里只给出这个神奇的公式,
在这里插入图片描述
这里,每一层的输出误差已经通过误差的反向传播获得,当前的权重是给定的,输出值也可以通过前馈过程获得,所以,误差对权重的斜率也自然可以轻松获得,用矩阵的语言,某两层之间的权重矩阵更新值可以表示为:

在这里插入图片描述
本文代码部分摘自《Python神经网络编程》并对其做了注释。

代码

import numpy
import numpy as np
import scipy.special

# 神经网络类classNeuralNetwork:def__init__(self, inodes, hnodes, onodes, learning_rate=0):# 输入层节点数
        self.inodes = inodes
        # 隐藏层节点数
        self.hnodes = hnodes
        # 输出层节点数
        self.onodes = onodes
        # 学习率
        self.learning_rate = learning_rate
        # 输入层到隐藏层的权重矩阵# self.wih = np.random.rand(self.hnodes, self.inodes) - 0.5
        self.wih = np.random.normal(0.0,pow(self.hnodes,-0.5),(self.hnodes, self.inodes))# 隐藏层到输出层的权重矩阵# self.who = np.random.rand(self.onodes, self.hnodes) - 0.5
        self.who = np.random.normal(0.0,pow(self.onodes,-0.5),(self.onodes, self.hnodes))# 激活函数
        self.activation_function =lambda x: scipy.special.expit(x)# 前馈defforward(self, input_data):# 输入
        inputs = np.array(input_data, ndmin=2).T
        # 隐藏层输入数据
        hidden_inputs = np.dot(self.wih, inputs)# 隐藏层输出数据
        hidden_outputs = self.activation_function(hidden_inputs)# 输出层输入数据
        final_inputs = np.dot(self.who, hidden_outputs)# 输出层输出数据
        final_outputs = self.activation_function(final_inputs)return final_outputs

    # 模型训练deftrain(self, input_data, target_data):# 输入
        inputs = np.array(input_data, ndmin=2).T
        # 输入数据标签
        targets = np.array(target_data, ndmin=2).T
        # 隐藏层输入数据
        hidden_inputs = np.dot(self.wih, inputs)# 隐藏层输出数据
        hidden_outputs = self.activation_function(hidden_inputs)# 输出层输入数据
        final_inputs = np.dot(self.who, hidden_outputs)# 输出层输出数据
        final_outputs = self.activation_function(final_inputs)# 输出层误差
        output_errors = targets - final_outputs
        # 反向传播至隐藏层的误差
        hidden_errors = np.dot(self.who.T, output_errors)# 更新隐藏层和输出层的权重矩阵
        delta_who = np.dot((output_errors * final_outputs *(1.0- final_outputs)), np.transpose(hidden_outputs))
        self.who += self.learning_rate * delta_who
        # 更新输入层和隐藏层的权重矩阵
        delta_wih = np.dot((hidden_errors * hidden_outputs *(1.0- hidden_outputs)), np.transpose(inputs))
        self.wih += self.learning_rate * delta_wih

if __name__ =="__main__":# 神经网络模型
    inodes =784
    hnodes =100
    onodes =10
    model = NeuralNetwork(inodes, hnodes, onodes,0.3)# 读取训练数据
    data_file =open("./mnist_dataset/mnist_train.csv")
    data_set = data_file.readlines()
    data_file.close()# 对每一项输入数据进行模型训练for record in data_set:
        all_values = record.split(",")# 对训练数据进行放缩
        inputs =(numpy.asfarray(all_values[1:])/255.0*0.99)+0.01# 对训练数据标签进行编码
        targets = np.zeros(onodes)+0.01
        targets[int(all_values[0])]=0.99# 训练模型
        model.train(inputs, targets)# 加载测试数据
    test_data_file =open("./mnist_dataset/mnist_test.csv",'r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()# 分数卡
    scorecard =[]# 对每一项测试数据进行预测for record in test_data_list:
        all_values = record.split(',')# 正确的标签
        correct_label =int(all_values[0])print(correct_label,"correct label")
        inputs =(numpy.asfarray(all_values[1:])/255.0*0.99)+0.01# 用神经网络模型预测
        outputs = model.forward(inputs)# 取出最大输出值对应的标签索引
        label = numpy.argmax(outputs)print(label,"network's answer")# 计分卡计数if label == correct_label:
            scorecard.append(1)else:
            scorecard.append(0)print(scorecard)# 计算分数
    scorecard_array = np.asarray(scorecard)print("performance = ", scorecard_array.sum()/ scorecard_array.size)

运行结果

performance =  0.9468

作者这水平有限,有不足之处欢迎留言指正

标签:

本文转载自: https://blog.csdn.net/weixin_37522117/article/details/127040325
版权归原作者 肥猪猪爸 所有, 如有侵权,请联系我们删除。

“深度学习初遇——自己动手实现三层神经网络”的评论:

还没有评论