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

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 时代深度学习作为爆发技术的主要原因。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容