美文网首页
原理就是这么简单 用Python搭建神经网络

原理就是这么简单 用Python搭建神经网络

作者: 云雾中的墨先生 | 来源:发表于2018-11-22 16:40 被阅读0次

    Date: 11/22/2018

    不做调包侠!

    之前我在网易云课堂与稀牛学院的深度学习公开课上讲述了“不调包,仅用Python如何四步搭建神经网络”。
    我发现各位小伙伴们对原理的渴求非常强烈!说明大家在“不做调包侠”上非常重视,我也非常开心。
    这篇文章也是“原理就是这么简单”的系列文章之一,希望通过文章的方式将原理描述的更加丰满!帮助更多想学习深度学习的朋友!

    对比Sklearn中的线性回归

    学过机器学习的朋友都知道Sklearn是非常重要的工具包,很多复杂的机器学习模型也许用sklearn几行代码就能搞定,
    举个例子比如线性回归,我们可以构建如下的线性回归对象训练和预测一气呵成:

    linreg = LinearRegression()
    linreg.fit(train_features,train_targets) 
    y_pred = linreg.predict(val_features)
    

    发现使用非常简单,仅仅三行代码就能得到线性回归的结果(y_pred
    如果使用的案例是波士顿房价的数据集,那相应可以得到如下的预测结果:

    Boston_Housing_predict.PNG

    预测结果还不错。

    那如何构建一个类似Skleran.LinearRegression 的神经网络工具包呢?
    接下来我就要将数学原理转换为代码。

    Python搭建神经网络完成回归工作

    终于到了本篇文章的重点,我会用数学原理和python代码详细讲解如何构建如下结构的神经网络:


    nn.png

    介绍网络

    大家可以看到这个神经网络分别有

    • Input Layer
    • Hidden Layer
    • Output Layer

    这个是这个网络的基本结构,在进行下面的讲解之前需要在这里声明一下,

    既然是Layer 层 , 都会有这个层本身输入和输出,就好比一个净水过滤器,传进去的是脏水,传出来的是净水,那么神经网络的各个层也有这个性质。

    1, Input Layer 值得注意的地方是Input Layer 只有输出,传入的就是数据特征,如果使用的是预测房价的案例传入的就是(面积,朝向,地段,etc..)。
    公式中会表示成X

    2, Hidden Layer 有输入和输出两个概念了,就好比净水器。那么针对Hidden Layer净水器的过滤网是什么呢? 其实就是激活函数(这个后面我会详细讲解)。
    公式中会表示成h_input/h_out

    3, Output Layer 也有输入和输出两个概念,只不过这次我搭建的是处理回归模型的网络,所以其输入和输出是一样的值。
    公式中会表示成O_input/O_out

    4, 链接层与层之间的是权重矩阵。
    公式中会表示成W_i_h/ W_h_o

    BP算法数学原理和代码实现

    其实 BP 算法的全名为"Error BackPropagation 误差反向传播"
    但是其实BP算法的流程分为三个部分:
    一为前向传播求误差
    二为反向传播求梯度
    三为通过梯度更新权重

    可能大家会对这三个部分晦涩难懂,其实我举个不恰当的例子你就能明白。
    比如小明想去学习自由泳,他没有教练,不过好在有一个泳池他可以无限次的在里面尝试。
    然后小明开始自学:

    • 步骤一,他脑子里想了A,B,C 三套动作准备在水中尝试,然后直接跳入水中瞎扑腾,看看到底是落水还是前进。
    • 步骤二,在水中他认真体会各种动作对于游泳的影响,发现使用C动作他还能扑腾一会,但是使用A,B动作,瞬间落水。
    • 步骤三,他上了岸,然后仔细总结刚才的C动作并加以改进,并且尝试去除自己的A,B动作。

    然后再次跳入水中。

    经过n 次的刻苦训练,小明练成了自由泳。

    其实这个流程就好比BP算法的流程。
    步骤一就像前向传播,带着一些不靠谱的动作相当于初始权重矩阵
    步骤二就像于反向传播,仔细体验哪个动作更加有效这相当于求权重的梯度
    步骤三就像于梯度更新,更新自己的动作相当于更新权重矩阵

    下面我就要仔细用数学推导BP算法:
    首先来学习一下数学基础:


    base.JPG

    Loss function 不用过多解释,了解机器学习的都知道这是MSE。
    Sigmoid 就是刚才提到的过滤器也就是激活函数。

    前向传播:

    前向传播.JPG
    代码如下:
    代码中final_outputs 就是 O_out
    hidden_inputs = np.dot(X,self.input_hidden_weight)
    hidden_outputs = self.sigmoid(hidden_inputs)
    final_inputs = np.dot(hidden_outputs,self.hidden_output_weight)
    final_outputs = final_inputs
    

    反向传播:
    基于链式求导法则如下:


    反向传播.JPG

    关注O_input_error_term, h_input_error_term
    因为这两项是关于权重矩阵W_i_h和W_h_o的函数。
    代码如下:

    final_output_error = y-final_outputs
    final_input_error_term = final_output_error*1
    hidden_output_error = np.dot(self.hidden_output_weight, final_input_error_term)
    hidden_input_error_term = hidden_output_error*hidden_outputs*(1-hidden_outputs)
    

    梯度更新:

    梯度更新.JPG
    代码如下:
    代码中update_x_x 就是权重的梯度
    update_i_h += np.dot(X_item,hidden_error_term.T)
    update_h_o += np.dot(hidden_outputs,output_error_term.T)
    

    到了这里你已经学会了BP算法的精华了,但是如果想构造一个可以使用的神经网络还差一点点,是什么呢?
    下面我会根据代码给你讲解。

    Python 四步构造NeuralNetwork

    想要构造一个类似Sklearn的方便使用的神经网络,我们需要四步

    Step1:
    初始化:

    • 构造sigmoid 激活函数
    • 构造mse 用于求损失
    • 设置学习率,以及网络节点
    • 初始化两个权重矩阵

    Step2

    • 实现前向传播
    • 实现predect函数基于前向传播

    Step3

    • 实现反向传播

    整个代码如下:
    ···
    class NeuralNetwork(object):
    def init(self, input_units, hidden_units = 4, output_units = 1, learning_rate = 0.01):

        self.sigmoid = lambda x : 1/(1+np.exp(-x))
        self.mean_squared_error = lambda y_true, y_pred: np.mean((y_true-y_pred)**2)
        
        self.input_units = input_units
        self.hidden_units = hidden_units
        self.output_units = output_units
        
        self.learning_rate = learning_rate
        
        np.random.seed(1119)
        
        self.input_hidden_weight = np.random.randn(self.input_units,self.hidden_units)
    
        self.hidden_output_weight = np.random.randn(self.hidden_units,self.output_units)
    
    def __forward__(self, X):
        ''' forward pass through the neural network with X 
        
            Arguments
            ---------
            X: 2D array
            features
            
            Returns
            -------
            hidden_outputs: 1D array 
            final_outputs: 1D array 
            
        '''
        hidden_inputs = np.dot(X,self.input_hidden_weight)
        
        hidden_outputs = self.sigmoid(hidden_inputs)
        
        final_inputs = np.dot(hidden_outputs,self.hidden_output_weight)
        
        final_outputs = final_inputs
        
        return hidden_outputs,final_outputs
    
    def __backward__(self, y, hidden_outputs, final_outputs):
        ''' backward pass through the neural network with X 
        
            Arguments
            ---------
            X: 2D array
            features
            
            Returns
            -------
            hidden_input_error_term: 1D array 
            final_input_error_term: 1D array 
            
        '''
        final_output_error = y-final_outputs
        
        final_input_error_term = final_output_error*1
            
        hidden_output_error = np.dot(self.hidden_output_weight, final_input_error_term)
    
        hidden_input_error_term = hidden_output_error*hidden_outputs*(1-hidden_outputs)
        
        return hidden_input_error_term,final_input_error_term
    
    def fit(self, X, y):
        ''' fit(X, y) method of NeuralNetwork
        
            Parameters
            ----------
            X : 2D array
            Training data
    
            y : 1D array
            Target values
            
            Returns
            -------
            void
        
        '''
        n_records = X.shape[0]
        for X_item, y_item in zip(X, y):
            hidden_outputs, final_outputs = self.__forward__(X_item)
            hidden_error_term, output_error_term = self.__backward__(y_item,hidden_outputs,final_outputs)
            update_i_h += np.dot(X_item,hidden_error_term.T)
            update_h_o += np.dot(hidden_outputs,output_error_term.T)
    
        self.hidden_output_weight += self.learning_rate * (update_h_o / n_records)
        self.input_hidden_weight += self.learning_rate * (update_i_h / n_records)
        
     
    def predict(self, X):
        ''' predict(X) method of NeuralNetwork
        
            Arguments
            ---------
            X: 2D array
            features
            
            Returns
            -------
            outputs: 1D array 
            predicted values
        '''
        hidden_outputs, final_outputs = self.__forward__(X)
        
        return final_outputs
    

    总结

    对比了一下自己构造的神经网络和线性回归,预测波士顿房价的案例可以得到如下结果:


    线性回归VS神经网络.PNG

    可以明显看出神经网络的性能更好一些。

    并且神经网络可以做回归和分类两种任务,只要将Output layer 加一个Sigmoid 即可实现二分类,加一个Softmax 又可以实现多分类(可参考另一篇 原理就是这么简单 Softmax 分类)

    理论上来说神经网络,有足够都的数据有足够多的层数和节点数,可以拟合任何函数,这也是神经网络的强大之处。
    也是如今的AI 时代深度学习作为爆发技术的主要原因。

    你也可以自己尝试实现,如果有问题,欢迎给我留言

    相关文章

      网友评论

          本文标题:原理就是这么简单 用Python搭建神经网络

          本文链接:https://www.haomeiwen.com/subject/dlclqqtx.html