用pytorch跟tensorflow实现神经网络固然爽。但是想要深入学习神经网络,光学会调包是不够的,还是得亲自动手去实现一个神经网络,才能更好去理解。

一、问题介绍

传说中线性分类器无法解决的异或分类问题。我们就拿它来作为我们神经网络的迷你训练数据。把输入数据拼成一个矩阵X:

import numpy as np#训练数据:经典的异或分类问题
train_X = np.array([[0,0],[0,1],[1,0],[1,1]])
train_y = np.array([0,1,1,0])

我们定义一个简单的2层神经网络:
对应的代码

linear1 = LinearLayer(2,3)
relu1 = Relu()
linear2 = LinearLayer(3,1)

我们还需要定义一个损失函数Loss,用来衡量我们的输出结果与实际结果的误差。这里用的是均方误差MSE,表达式如下

二、BP算法 和 计算图(Computing Graph)模型

里面的线性层,Relu层也得自己动手实现。

实现这些,我们首先需要知道计算图模型。计算图模型是所有神经网络框架的核心理论基础。

我们依然还是对着这个例子来讲解。

上面的神经网络,用纯数学公式表达可以表达。
计算图模型把一个复合运算拆分成为多个子运算,因此,我们需要引入很多中间变量。

定义:
根据这些公式,我们就可以用计算图模型来表示我们的神经网络,如下:
这个计算图就是上面那一堆公式的可视化表示。

在图里面,公式里每个出现过的变量都被视为一个节点,变量之间的连线描述了变量之间存在直接的计算的关系。计算图的表示方法,有什么好处呢?

下面我们基于这个计算图来用BP算法进行模型的训练。

对模型进行训练,就是找到一组模型的参数,使得我们的网络模型能够准确预测我们的训练数据。在我们这个例子里面,需要训练参数其实只有线性层的矩阵跟bias项: [公式] 。

训练采用的是BP算法,采用梯度下降法来逐渐迭代去更新参数。

梯度下降法的原理很简单,每次迭代中,用损失函数关于参数的梯度乘以学习率,来更新参数。
关于梯度我要多说几句。梯度表示Y关于x的变化率,可以理解成x的速度。由于W1是一个2X3的矩阵,那么loss关于W1的梯度可以理解W1的每个元素的瞬时速度。W1的梯度的形状,必然是严格跟参数本身的形状是一样的(每个点都有对应的速度)。也就是说损失函数关于W1的梯度也必然是一个2X3的矩阵(不然更新公式里面无法做加减)。

下面开始训练过程,

首先给定输入X,初始化 [公式] (记住不能初始为全0)。

正向传播(forward pass)

BP算法首先在计算图上面进行正向传播(forward pass),即从左到右计算所有未知量:
严格按照顺序计算,所有的 [公式] 都能先后求出,右边的y就是网络的当前预测结果。

反向传播(backward pass)

既然我们想要用参数的梯度来更新参数,那么我们需要求出最后的节点输出loss关于每个参数的梯度,求梯度的方法是反向传播。

由于我们已经进行过一次正向传播,因此图里面所有的节点的值都变成了已知量。
我们现在要求的是图里面标为红色的这4个梯度,它们距离loss有点儿远。

但是不急,有了这个计算图,我们可以慢慢从右往左推出这4个值。

先从最右边开始,观察到Loss节点只有一条边跟y连着,计算loss关于y的导数(这个求导只有一个变量y,怎么求不用我解释了吧):
我们就求得了损失函数关于输出y的导数,然后继续往左边计算。

(已经求出的梯度我们用橙色来标记)
y是通过O2计算出来的,我们可以计算y关于O2的梯度:
但是我们想要的是loss关于O2的梯度,这里应用到了链式求导法则:
loss关于y的梯度在之前已经求出来过了,然后就可以求出loss关于O2的梯度。

继续往左计算梯度:
上图中 a2 关于它每个变量的梯度,可以直接根据 a2 与它左边3个变量的表达式来算出,如下:
这一步我们算出了两个需要计算的梯度,似乎并没有遇到困难,继续往左传播。

在计算 a1梯度的时候,我们遇到了relu激活函数,relu函数的梯度也很好求:
它的梯度就是在输入X的基础上,所有大于0的位置导数都是1,其他位置导数都是0,比如:
(这括号里的看不懂不要紧,当N维向量对M维向量求导应用链式法则时,通用一点儿的结果是一个NM的jacobian矩阵再乘M1向量,但是这里由于1. N=M。2. jacobian矩阵是一个对角方阵。所以可以简化成两个向量相乘)

再往左继续传,我就不写每个步骤了。
总之可以一直传到所有梯度都求出来为止。接下来一步就是愉快地进行随机梯度下降法的更新操作了。

三、模块化各种Layer

观察我们的网络,发现里面的几个模块之间其实大部分干的事情都是相似的,无非就是层数不一样。那么我们就可以复用,我们完全可以把它们抽象成不同的Layer:
于是,我们可以把这些类似模块看成一个小黑盒子,我们的模型等价于下面这个:
于是上面那个复杂的网状结构,被我们简化成了线性结构。

下面我对照代码实现每个小黑盒子吧,实现代码在这个文件里面:
Layers.py
首先介绍线性全连接层,先看代码吧:

class LinearLayer:def __init__(self, input_D, output_D):self._W = np.random.normal(0, 0.1, (input_D, output_D)) #初始化不能为全0self._b = np.random.normal(0, 0.1, (1, output_D))self._grad_W = np.zeros((input_D, output_D))self._grad_b = np.zeros((1, output_D))def forward(self, X):return np.matmul(X, self._W) + self._bdef backward(self, X, grad): self._grad_W = np.matmul( X.T, grad)self._grad_b = np.matmul(grad.T, np.ones(X.shape[0])) return np.matmul(grad, self._W.T)def update(self, learn_rate):self._W = self._W - self._grad_W * learn_rateself._b = self._b - self._grad_b * learn_rate

forward太简单了,就不讲了,看一下backward。

backward里面其实要计算3个值,W, b的梯度算完以后要存起来,前一层的梯度算完以后直接作为返回值传出去,推导的公式如下:
注意矩阵求导应用链式法则的时候,顺序非常重要。要严格按照指定顺序来乘,不然形状对不上。具体什么顺序,可以自己想办法慢慢拼凑出来。

还有一个update函数,调用此函数这一层会按照梯度下降法来更新它的W跟b的值,这个实现也很简单直接看代码就明白了。

然后实现Relu层:

class Relu:def __init__(self):passdef forward(self, X):return np.where(X < 0, 0, X)def backward(self, X, grad):return np.where(X > 0, X, 0) * gr

由于这一层没有需要保存参数,只需要实现以下forward跟backward方法就行了,非常简单。

接下来开始实现神经网络训练。

四、搭建神经网络

训练部分的代码在 nn.py 里面,里面的代码哪里看不懂可以翻回去看之前的解释,命名都是跟上面说的一样的。

#训练数据:经典的异或分类问题
train_X = np.array([[0,0],[0,1],[1,0],[1,1]])
train_y = np.array([0,1,1,0])#初始化网络,总共2层,输入数据是2维,第一层3个节点,第二层1个节点作为输出层,激活函数使用Relu
linear1 = LinearLayer(2,3)
relu1 = Relu()
linear2 = LinearLayer(3,1)#训练网络
for i in range(10000):#前向传播Forward,获取网络输出o0 = train_Xa1 = linear1.forward(o0)o1 = relu1.forward(a1)a2 = linear2.forward(o1)o2 = a2#获得网络当前输出,计算损失lossy = o2.reshape(o2.shape[0])loss = MSELoss(train_y, y) # MSE损失函数#反向传播,获取梯度grad = (y - train_y).reshape(result.shape[0],1)grad = linear2.backward(o1, grad)grad = relu1.backward(a1, grad)grad = linear1.backward(o0, grad)learn_rate = 0.01  #学习率#更新网络中线性层的参数linear1.update(learn_rate)linear2.update(learn_rate)#判断学习是否完成if i % 200 == 0:print(loss)if loss < 0.001:print("训练完成! 第%d次迭代" %(i))break

我觉得没啥好讲的,就直接对着我们的计算图,一步一步来。

注意一下中间过程几个向量的形状。列向量跟行向量是不一样的,一不小心把列向量跟行向量做运算,numpy不会报错,而是会广播成一个矩阵。所以运算的之前,记得该转置得转置。

#将训练好的层打包成一个model
model = [linear1, relu1, linear2]#用训练好的模型去预测
def predict(model, X):tmp = Xfor layer in model:tmp = layer.forward(tmp)return np.where(tmp > 0.5, 1, 0)

把模型打包然后用上面的predict函数来预测。也没啥好说的,就直接往后一直forward就完儿事。

#开始预测
print("-----")
X = np.array([[0,0],[0,1],[1,0],[1,1]])
result = predict(model, X)
print("预测数据1")
print(X)
print("预测结果1")
print(result)

预测训练完的网络就能拿去搞预测了,我这里设置学习率为0.01的情况下,在第3315次迭代时候完成训练。

最后我们成功地预测了训练数据。

五、总结

本文出现过的所有图片笔记的PDF、还有代码,全都在我的github上:

denggaoshan/DeepLearningStudy

转载于:折射

入门讲解:使用numpy实现简单的神经网络(BP算法)相关推荐

  1. 数据挖掘之人工神经网络BP算法

    /*人工神经网络BP算法思想: 神经网络一般分为3层(也可以多层),包括输入层,隐含层和输出层.通过有监督的学习拟合非线性函数.假如输入层有3个神经元,隐含层有5个神经元,输出层有1个神经元.有监督的 ...

  2. python对编写神经网络作用_神经网络(BP)算法Python实现及应用

    本文实例为大家分享了Python实现神经网络算法及应用的具体代码,供大家参考,具体内容如下 首先用Python实现简单地神经网络算法: import numpy as np # 定义tanh函数 de ...

  3. 神经网络——BP算法

    一.BP算法的意义 对于初学者来说,了解了一个算法的重要意义,往往会引起他对算法本身的重视.BP(Back Propagation,后向传播)算法,具有非凡的历史意义和重大的现实意义. 1.1.历史意 ...

  4. 人工智能神经网络bp算法及其数学演算过程

    BP 算法 预备知识 在开始之前,首先需要补充一点预备知识. 1. 激活函数sigmoid函数: sigmoid 函数是人工智能神经网络中最常使用的一类激活函数,其数学表达式为: sigmoid函数有 ...

  5. 神经网络bp算法应用,bp神经网络动量因子

    伤寒.副伤寒流行预测模型(BP神经网络)的建立 由于目前研究的各种数学模型或多或少存在使用条件的局限性,或使用方法的复杂性等问题,预测效果均不十分理想,距离实际应用仍有较大差距. NNT是Matlab ...

  6. 【Pandas】一文入门Pandas处理csv文件数据集(神经网络/机器学习算法数据预处理)

    Motivation 和某个大佬采集的数据是csv格式的,之前没处理过csv格式的数据.拿来写神经网络训练的时候踩了不少坑,这里记录一下,也方便后来人学习. Pandas处理csv文件 处理csv文件 ...

  7. 图解人工神经网络 BP算法代码和注释

    动图很长 代码 # -*- coding: utf-8 -*-import numpy as np import matplotlib.pyplot as pyp OUTPUT = 'output' ...

  8. 多层神经网络(BP算法)介绍

    误差反向传播(Error Back Propagation, BP)算法 1.BP算法的基本思想是,学习过程由信号的正向传播与误差的反向传播两个过程组成. 1)正向传播:输入样本->输入层-&g ...

  9. 全链接神经网络——BP算法推导过程

最新文章

  1. python 之 分割参数getopt
  2. oledb 获取所有表的名字和列名
  3. Latex Smartdiagram
  4. Flask --- 框架快速入门
  5. ACM团队周赛题解(2)
  6. python构造http请求_Python实现简单的HTTP请求发送详解
  7. 基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令
  8. 计算几何-求线段交点算法和代码(C++语言)
  9. Matlab 括号用法
  10. 阿里巴巴集团CTO张建锋:将开放阿里的技术能力
  11. 国庆车流激增,南京启用无人机报路况
  12. 关于oracle预言机
  13. 微信支付遇到 商户号该产品权限未开通,请前往商户平台产品中心检查后重试
  14. Calibre for Mac v5.29.0电子书阅读管理工具
  15. React前端面试题
  16. STM32——快速识别芯片引脚数
  17. bt面板从7.1升级到7.5.2之后网络挂了
  18. jsdelivr cdn npm替代方案
  19. Javascript 设计模式之代理模式【讲师辅导】-曾亮-专题视频课程
  20. Java实现简单二维码制作

热门文章

  1. Docker搭建SonarQube代码质量检查平台
  2. (草稿)如何判断一名UiPath开发人员是否合格?
  3. Let‘s Encrypt 免费Https证书
  4. C#LeetCode刷题之#594-最长和谐子序列​​​​​​​​​​​​​​(Longest Harmonious Subsequence)
  5. C#开发笔记之06-为什么要尽可能的使用尾递归,编译器会为它做优化吗?
  6. synchronized关键字理解
  7. vm ubuntu设置中文_如何在本地Ubuntu Linux机器或VM上设置LAMP服务器
  8. netlify 部署vue_如何使用Netlify构建和部署网站-全面的教程
  9. 想作为程序员工作 需要什么_您不想作为程序员玩的游戏
  10. 计算机一级办公软件试题,计算机一级WPS模拟练习题及答案