用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次迭代时候完成训练。

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

往期精彩回顾适合初学者入门人工智能的路线及资料下载机器学习及深度学习笔记等资料打印机器学习在线手册深度学习笔记专辑《统计学习方法》的代码复现专辑
AI基础下载机器学习的数学基础专辑获取一折本站知识星球优惠券,复制链接直接打开:https://t.zsxq.com/662nyZF本站qq群1003271085。加入微信群请扫码进群(如果是博士或者准备读博士请说明):

【机器学习基础】使用python实现BP算法相关推荐

  1. Python机器学习基础之Python的基本语法(一)

    当今世界已经进入了大数据的时代.随着信息化的不断发展,人工智能.机器学习等词语越来越被人们所熟知,而他们也渐渐地成了这个时代的弄潮儿,走在了信息时代的前端.从本篇博客开始,小编将带领大家一起走进人工智 ...

  2. 【机器学习基础】Python机器学习入门指南(全)

    前言 机器学习 作为人工智能领域的核心组成,是计算机程序学习数据经验以优化自身算法,并产生相应的"智能化的"建议与决策的过程. 一个经典的机器学习的定义是: A computer ...

  3. 机器学习与数据挖掘--编程实现BP算法

    机器学习与数据挖掘实验五 (编程实现误差逆传播算法BP算法) 实验目的: 掌握误差逆传播算法(BP算法)的工作流程 实验环境: Anaconda/Jupyter notebook/Pycharm 实验 ...

  4. 机器学习基础6--集群模型和算法

    案例: 现在手上有许多的文档,需要将其按照类型(体育,财经,科技等)进行分类,也就是对文章进行分组或聚类. 分析: 如果手上有已经标记过类型的数据,可以将其作为训练集进行学习. 那么这是否是一个多元分 ...

  5. python实现BP算法

    误差逆传播算法,又称BP算法,被誉为神经网络中最好的算法,其广泛应用在多层网络中. 在这之前,我们先来理解几个概念 M-P神经元模型 所谓M-P模型,其实是按照生物神经元的结构和工作原理构造出来的一个 ...

  6. 【机器学习基础】Python机器学习的神器- Scikit-learn使用说明

    全文共 26745 字,106 幅图表, 预计阅读时间 67 分钟. 0 引言 Sklearn (全称 Scikit-Learn) 是基于 Python 语言的机器学习工具.它建立在 NumPy, S ...

  7. 机器学习 | 基础通俗讲解集成学习算法!

    来源:Datawhale 本文约6000字,建议阅读10+分钟 本文以图文的形式对模型算法中的集成学习,以及对集中学习在深度学习中的应用进行了详细解读. 标签:机器学习 数据及背景 阿里天池-零基础入 ...

  8. python代码基础题-python每日经典算法题5(基础题)+1(中难题)

    现在,越来越多的公司面试以及考验面试对算法要求都提高了一个层次,从现在,我讲每日抽出时间进行5+1算法题讲解,5是指基础题,1是指1道中等偏难.希望能够让大家熟练掌握python的语法结构已经一些高级 ...

  9. 【机器学习基础】Python实现行转列?!超简单,赶快get起来

    ◆ ◆ ◆  ◆ ◆ 前言 数据的行转列操作,在实际工作过程中应用非常广泛. 由于不同人员.不同部门对数据结构的认识是不大相同的,尤其是从基层人员手里拿到的数据,更是五花八门,横七竖八. 比如有这样一 ...

最新文章

  1. Java 集合框架(二)—— ArrayList
  2. python——异常(1),捕获特定异常
  3. pytorch历史版本下载:
  4. 使用Action启动/关闭Service
  5. eclipse 导入myeclipse web项目
  6. python ui自动化_pythonUI自动化整理
  7. C#实现软键盘的几个关键技术实现方法
  8. React Native Fetch封装那点事...
  9. socket与TCP/UDP编程-转
  10. python数据分类聚类案例_Python 数据分类与聚类分析(5)
  11. 爬虫实战—模拟登陆oschina
  12. testbench文件显示波形_十大基本功之testbench
  13. 拳王虚拟项目公社:如何搭建虚拟资源解析站,全自动化卖会员网络赚钱项目
  14. xlsx的python处理
  15. Android 照相机
  16. 明天全国哀悼日,小程序只需三行代码秒变黑白
  17. 服务器中转发器干啥用,转发器是什么意思
  18. ESP32用自签CA进行MQTT的TLS双向认证通信
  19. js实现省市区三级联动
  20. Linux解压rar压缩文件的详细方法

热门文章

  1. vs2008 添加头文件路径
  2. 前端开发学习二——由JSON和Object Literal Notation引起的思考
  3. beacon帧字段结构最全总结(三)——VHT字段总结
  4. eclipse版本详解以及下载安装步骤及启动eclispe报错整理
  5. 如何能让mediawiki实现共享
  6. Mvc示例代码调试之一----调试工具及设置(用firebug与vs联合调试)
  7. .NET 导出Excel
  8. 今天去医院拿宝宝的微量元素检查结果
  9. 转载:掩膜矩阵操作数学解释(权重表,锐化)
  10. 剪切板复制word图片是本地路径_如何将图片数学公式快速输入到Word中?