一、反向传播概述

反向传播可以说是神经网络历史上最重要的算法——如果没有有效的反向传播,就不可能将深度学习网络训练到我们今天看到的深度。反向传播可以被认为是现代神经网络和深度学习的基石。

反向传播的最初版本是在1970年代引入的,但直到1986年具有开创性的论文,Rumelhart、Hinton和Williams通过反向传播错误学习表示,我们才能够设计出更快的算法,更擅长训练更深的网络。

网络有若干个关于反向传播的教程。我最喜欢的一些包括:

1.    AndrewNg在Coursera的机器学习课程中对反向传播的讨论。

2.    数学动机很强的第2章–MichaelNielsen的神经网络和深度学习中的反向传播算法如何工作。

3.    斯坦福大学的cs231n对反向传播的探索和分析。

4.    马特·马祖尔的优秀具体例子证明反向传播如何工作。

如您所见,不乏反向传播的原理说明——我们这里使用Python语言构建一个直观、易于理解的反向传播算法实现。

我们将构建一个实际的神经网络并使用反向传播算法对其进行训练,您将了解反向传播的工作原理——也许更重要的是,您将对如何使用该算法从头开始训练神经网络有更深入的了解。

二、反向传播算法

反向传播算法包括两个阶段:

1、我们的输入通过网络并获得输出预测的前向传递(也称为传播阶段)。

2、反向传播,我们计算网络最后一层(即预测层)损失函数的梯度,并使用该梯度递归地应用链式法则来更新我们网络中的权重(也称为权重更新)阶段)。

下面我们将使用Python实现反向传播算法。

之后,使用下面两个数据集演示如何在以下两个方面使用反向传播和Python训练自定义神经网络:

1、XOR数据集

2、MNIST数据集

1、向前传递

表2:左:按位异或数据集(包括类标签)。右图:插入了偏差列的XOR数据集设计矩阵(为简洁起见,不包括类标签)。

前向传递的目的是通过应用一系列点积和激活来在网络中传播我们的输入,直到我们到达网络的输出层(即我们的预测)。为了可视化这个过程,让我们首先考虑XOR数据集(表2,左)。

我们可以看到设计矩阵(左)中的每个条目X都是二维的——每个数据点由两个数字表示。例如,第一个数据点由特征向量(0,0)表示,第二个数据点由(0,1)等表示。然后我们将输出值y作为右列。我们的目标输出值是类标签。给定来自设计矩阵的输入,我们的目标是正确预测目标输出值。

为了在这个问题上获得完美的分类精度,我们需要一个至少具有单个隐藏层的前馈神经网络,所以让我们继续并从2-2-1开始架构(图9,顶部)。这是一个好的开始;然而,我们忘记包括偏差项。从第9章我们知道,有两种方法可以在我们的网络中包含偏差项b。我们可以:

1、使用单独的变量。

2、通过在特征向量中插入一列1,将偏差视为权重矩阵中的可训练参数。

插入一列1的特征向量是通过编程完成的,但为了确保我们理解这一点,我们可视化XOR设计矩阵以明确看到这一点(表2,右)。可见,一列1已添加到我们的特征向量中。实际上,您可以将此列插入到您喜欢的任何位置,但我们通常将其放在特征向量中的最前面或最后面。

由于我们改变了输入特征向量的大小(通常在神经网络实现本身内部执行,因此我们不需要明确修改我们的设计矩阵),这将我们的(感知)网络架构从2-2-1更改为(内部)3-3-1(图9,底部)。

图9:顶部:为了构建一个神经网络来正确分类XOR数据集,我们需要一个具有两个输入节点、两个隐藏节点和一个输出节点的网络。这产生了2-2-1架构。底部:由于偏差技巧,我们实际的内部网络架构表示为3-3-1。在绝大多数神经网络实现中,对权重矩阵的这种调整发生在内部,您无需担心;然而,了解幕后发生的事情仍然很重要。

我们仍将这种网络架构称为2-2-1,但在实施时,由于添加了嵌入在权重矩阵中的偏置项,因此它实际上是3-3-1。

最后,回想一下我们的输入层和所有隐藏层都需要一个偏置项;然而,最终的输出层不需要偏置。应用偏差技巧的好处是我们不再需要明确跟踪偏差参数——它现在是权重矩阵中的一个可训练参数,从而使训练更有效并且更容易实现。请参阅第9章更深入地讨论为什么这种偏差技巧有效。

为了查看前向传递的作用,我们首先初始化网络中的权重,如图10所示。请注意权重矩阵中的每个箭头如何具有与其关联的值——这是给定节点的当前权重值,表示给定输入被放大或减少的量。这个权重值将在反向传播阶段更新。

在图10的最左边,我们展示了特征向量(0,1,1)(以及网络的目标输出值1)。在这里我们可以看到0、1和1已经分配给网络中的三个输入节点。为了通过网络传播值并获得最终分类,我们需要在输入和权重值之间取点积,然后应用激活函数(在这种情况下,sigmoid函数,σ)。

图10:前向传播的一个例子。输入向量[0,1,1]呈现给网络。取输入和权重之间的点积,然后应用sigmoid激活函数来获得隐藏层中的值(分别为0.899、0.593和0.378)。最后,计算最后一层的点积和sigmoid激活函数,产生0.506的输出。将step函数应用于0.506产生1,这确实是正确的目标类标签。

让我们计算隐藏层中三个节点的输入:

1、σ((0×0.351)+(1×1.076)+(1×1.116))=0.899

2、σ((0×0.097)+(1×0.165)+(1×0.542))=0.593

3、σ((0×0.457)+(1×0.165)+(1×0.331))=0.378

查看隐藏层的节点值(图10,中间),我们可以看到节点已更新以反映我们的计算。

我们现在有了对隐藏层节点的输入。为了计算输出预测,我们再次计算点积,然后是sigmoid激活:

因此,网络的输出为0.506。我们可以应用一个阶跃函数来确定这个输出是否是正确的分类:

应用net=0.506的step函数,我们看到我们的网络预测1,这实际上是正确的类标签。然而,我们的网络对这个类标签不是很有信心——预测值0.506非常接近步骤的阈值。理想情况下,这个预测应该更接近于0.98-0.99.,这意味着我们的网络已经真正了解了数据集中的潜在模式。为了让我们的网络真正“学习”,我们需要应用反向传播。

2、向后传播

为了应用反向传播算法,我们的激活函数必须是可微的,以便我们可以计算误差相对于给定权重wi,j的偏导数;Loss(E)、节点输出oj和网络输出netj。

由于反向传播背后的微积分在以前的作品中已经被详尽地解释了很多次(参见AndrewNg、MichaelNielsen、MattMazur),这里跳过反向传播链规则更新的推导,而是通过下面的代码来解释它。

对于精通数学的人,请参阅上面的参考资料,了解有关链式法则及其在反向传播算法中的作用的更多信息。通过在代码中解释这个过程,是为了更直观的理解反向传播。

3、使用Python实现反向传播

让我们创建一个命名为neuralnetwork.py。

# 导入包
import numpy as npclass NeuralNetwork:# layers,代表前馈网络实际架构的整数列表# alpha,学习率,在权重更新阶段应用此值。def __init__(self, layers, alpha=0.1):# 始化每个层的权重列表W# 然后存储层和学习率self.W = []self.layers = layersself.alpha = alpha# 初始化权重for i in np.arange(0, len(layers) - 2):# 网络中的每一层都是通过从标准正态分布中采样值来构建MxN权重矩阵来随机初始化的# 为了解决偏差,我们将层数[i]和层数[i+1]加一——这样做会改变我们的权重矩阵w,使其形状为3x3w = np.random.randn(layers[i] + 1, layers[i + 1] + 1)# 除以当前层节点数的平方根来缩放w,从而标准化每个神经元输出的方差self.W.append(w / np.sqrt(layers[i]))# the last two layers are a special case where the input# connections need a bias term but the output does notw = np.random.randn(layers[-2] + 1, layers[-1])self.W.append(w / np.sqrt(layers[-2]))def __repr__(self):# construct and return a string that represents the network# architecturereturn "NeuralNetwork: {}".format("-".join(str(l) for l in self.layers))def sigmoid(self, x):# 计算并返回给定输入值的 sigmoid 激活值return 1.0 / (1 + np.exp(-x))def sigmoid_deriv(self, x):# 计算 sigmoid 函数的导数return x * (1 - x)def fit(self, X, y, epochs=1000, displayUpdate=100):# insert a column of 1’s as the last entry in the feature# matrix -- this little trick allows us to treat the bias# as a trainable parameter within the weight matrixX = np.c_[X, np.ones((X.shape[0]))]# loop over the desired number of epochsfor epoch in np.arange(0, epochs):# loop over each individual data point and train# our network on itfor (x, target) in zip(X, y):self.fit_partial(x, target)# check to see if we should display a training updateif epoch == 0 or (epoch + 1) % displayUpdate == 0:loss = self.calculate_loss(X, y)print("[INFO] epoch={}, loss={:.7f}".format(epoch + 1, loss))def fit_partial(self, x, y):# construct our list of output activations for each layer# as our data point flows through the network; the first# activation is a special case -- it’s just the input# feature vector itselfA = [np.atleast_2d(x)]# FEEDFORWARD:# loop over the layers in the networkfor layer in np.arange(0, len(self.W)):# feedforward the activation at the current layer by# taking the dot product between the activation and# the weight matrix -- this is called the "net input"# to the current layernet = A[layer].dot(self.W[layer])# computing the "net output" is simply applying our# nonlinear activation function to the net inputout = self.sigmoid(net)# once we have the net output, add it to our list of# activationsA.append(out)# 反向传播# 反向传播的第一阶段是计算我们的预测与真实目标值之间的差异error = A[-1] - y# 从这里开始,我们需要应用链式规则并构建我们的增量列表“D”; # 增量中的第一项只是输出层的误差乘以我们的激活函数对输出值的导数D = [error * self.sigmoid_deriv(A[-1])]# 一旦你理解了链式规则,使用“for”循环就变得非常容易实现for layer in np.arange(len(A) - 2, 0, -1):# the delta for the current layer is equal to the delta# of the *previous layer* dotted with the weight matrix# of the current layer, followed by multiplying the delta# by the derivative of the nonlinear activation function# for the activations of the current layerdelta = D[-1].dot(self.W[layer].T)delta = delta * self.sigmoid_deriv(A[layer])D.append(delta)# since we looped over our layers in reverse order we need to# reverse the deltasD = D[::-1]# WEIGHT UPDATE PHASE# loop over the layersfor layer in np.arange(0, len(self.W)):# update our weights by taking the dot product of the layer# activations with their respective deltas, then multiplying# this value by some small learning rate and adding to our# weight matrix -- this is where the actual "learning" takes# placeself.W[layer] += -self.alpha * A[layer].T.dot(D[layer])def predict(self, X, addBias=True):# initialize the output prediction as the input features -- this# value will be (forward) propagated through the network to# obtain the final predictionp = np.atleast_2d(X)# check to see if the bias column should be addedif addBias:# insert a column of 1’s as the last entry in the feature# matrix (bias)p = np.c_[p, np.ones((p.shape[0]))]# loop over our layers in the networkfor layer in np.arange(0, len(self.W)):# computing the output prediction is as simple as taking# the dot product between the current activation value ‘p‘# and the weight matrix associated with the current layer,# then passing this value through a nonlinear activation# functionp = self.sigmoid(np.dot(p, self.W[layer]))# return the predicted valuereturn pdef calculate_loss(self, X, targets):# make predictions for the input data points then compute# the losstargets = np.atleast_2d(targets)predictions = self.predict(X, addBias=False)loss = 0.5 * np.sum((predictions - targets) ** 2)# return the lossreturn loss

4、示例1 - 异或数据

创建文件nn_xor.py

# import the necessary packages
from neuralnetwork import NeuralNetwork
import numpy as np# 构建异或数据集
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])# 定义神经网络并进行训练
nn = NeuralNetwork([2, 2, 1], alpha=0.5)
nn.fit(X, y, epochs=20000)# 进行预测
for (x, target) in zip(X, y):# 云测并打印结果pred = nn.predict(x)[0][0]step = 1 if pred > 0.5 else 0print("[INFO] data={}, ground-truth={}, pred={:.4f}, step={}".format(x, target[0], pred, step))

执行文件,输出如下

对于每一个数据点,神经网络都能够正确学习异或模式,证明我们的多层神经网络能够学习非线性函数。

为了证明学习XOR函数至少需要一个隐藏层,我们将上面的代码nn = NeuralNetwork([2, 2, 1], alpha=0.5)修改为NeuralNetwork([2, 1], alpha=0.5),然后重新训练,得到如下输出。

无论你如何调整学习率或权重初始化,你永远无法逼近 XOR 函数。 这就是为什么具有通过反向传播训练的非线性激活函数的多层网络如此重要的原因。

5、示例2 - MINST数据集

第二个例子,是用于手写数字识别的MNIST数据集(下图)的一个子集。MNIST数据集的这个子集内置于scikit-learn库中,包括1797个示例数字,每个数字为8×8个灰度图像(原始图像为28×28)。当展平后,每个图像都是一个8×8=64维向量。

我们创建一个名为nn_mnist.py的文件。

from neuralnetwork import NeuralNetwork
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn import datasets# 加载 MNIST 数据集并应用最小/最大缩放将像素强度值缩放到 [0, 1] 范围(每个图像由 8 x 8 = 64-dim 特征向量表示)
print("[INFO] loading MNIST (sample) dataset...")
digits = datasets.load_digits()
data = digits.data.astype("float")
data = (data - data.min()) / (data.max() - data.min())
print("[INFO] samples: {}, dim: {}".format(data.shape[0],data.shape[1]))# 构建训练集和测试集
(trainX, testX, trainY, testY) = train_test_split(data, digits.target, test_size=0.25)# 将标签从整数转换为向量
trainY = LabelBinarizer().fit_transform(trainY)
testY = LabelBinarizer().fit_transform(testY)# train the network
print("[INFO] training network...")
nn = NeuralNetwork([trainX.shape[1], 32, 16, 10])
print("[INFO] {}".format(nn))
nn.fit(trainX, trainY, epochs=1000)# evaluate the network
print("[INFO] evaluating network...")
predictions = nn.predict(testX)
predictions = predictions.argmax(axis=1)
print(classification_report(testY.argmax(axis=1), predictions))

运行文件,得到如下输出

我们在测试集上获得了约98%的分类准确率;

三、小结

我们了解如何使用Python从头开始实现反向传播算法。反向传播是梯度下降算法系列的推广,专门用于训练多层前馈网络。

反向传播算法包括两个阶段:

1.    前向传递,我们通过网络传递输入以获得我们的输出分类。

2.    反向传播(即权重更新阶段),我们计算损失函数的梯度并迭代地应用链式规则来更新网络中的权重。

反向传播即用于训练使用简单的前馈神经网络,也用于训练复杂的深度卷积神经网络。这是通过确保网络内部的激活函数可微分来实现的,从而允许应用链式规则。此外,网络中需要更新权重/参数的任何其他层也必须与反向传播兼容。

我们使用Python编程语言实现了简单的反向传播算法,并设计了一个多层前馈NeuralNetwork类。然后在XOR数据集上对该实现进行了训练,以证明我们的神经网络能够通过应用具有至少一个隐藏层的反向传播算法来学习非线性函数。然后,我们将相同的反向传播 + Python实现应用于MNIST数据集的一个子集,以证明该算法也可用于处理图像数据。

在实践中,反向传播不仅难以实现(由于计算梯度的错误),而且如果没有特殊的优化库,也很难使其高效,这就是为什么我们经常使用诸如Keras、TensorFlow、mxnet之类的库,因为这些库已经正确使用优化策略实现反向传播。

机器学习笔记 - 使用python代码实现易于理解的反向传播相关推荐

  1. 李航统计学习方法----感知机章节学习笔记以及python代码

    目录 1 感知机模型 2 感知机学习策略 2.1 数据集的线性可分性 2.2 感知机学习策略 3 感知机学习算法 3.1 感知机学习算法的原始形式 3.2 感知机算法的对偶形式 4 感知机算法pyth ...

  2. 拥抱人工智能--机器学习(附python代码)

    机器学习简述 加入了代码,如果不想看代码请直接跳过,这不会产生任何影响 文章来自于云栖社区,修改及补充了一些内容,增添了代码 文章目录 机器学习简述 机器学习算法:是使计算机具有智能的关键 下面我们将 ...

  3. 史上最简单的spark教程第二十三章-运行第一个机器学习Java和Python代码案例

    [提前声明] 文章由作者:张耀峰 结合自己生产中的使用经验整理,最终形成简单易懂的文章 写作不易,转载请注明,谢谢! 代码案例地址: ?https://github.com/Mydreamandrea ...

  4. 吴恩达《机器学习》课程总结(8)_神经网络参数的反向传播算法

    Q1代价函数 (1)假设神经网络的训练样本有m个,每一个包含一组输入x和一组输出信号y,L表示神经网络的层数,Sl表示每一层的神经元个数,SL代表最后一层中处理单元的个数. 则代价函数为(同样不对θ0 ...

  5. 梯度的直观理解_BP反向传播算法的思考和直观理解 -卷积小白的随机世界

    本篇文章,本来计划再进一步完善对CNN卷积神经网络的理解,但在对卷积层反向传播算法的理解中,越发觉得之前对于BP反向传播算法的理解是不到位的.小白近日觉得,对于深度神经网络,"反向传播&qu ...

  6. 机器学习之数学系列(三)逻辑回归反向传播梯度计算公式推导

    一.简介   在深度学习领域,我们往往采用梯度下降(或上升)法来优化训练函数模型,梯度下降法尤其是在解决凸优化问题上表现极佳.模型优化涉及到反向传播过程,反向传播过程需要先推导出梯度计算公式然后利用机 ...

  7. 当Sklearn遇上Plotly,机器学习变得如此简单、易于理解

    在学习sklearn(机器学习)过程中,模型原理可谓是枯燥无味,加上大多数模型训练过程也是不可见的,这使得很多小伙伴们望而却步,当然也有很多学者试图通过各种方式以可视化模型学习及预测过程,但大多数是复 ...

  8. python画长尾图_t-SNE完整笔记 (附Python代码)

    t-SNE(t-distributed stochastic neighbor embedding)是用于降维的一种机器学习算法,是由 Laurens van der Maaten 和 Geoffre ...

  9. 杨桃的Python笔记1——Python代码中分号的使用

    本人CSDN博客专栏:https://blog.csdn.net/yty_7 Github地址:https://github.com/yot777/ 初学者都认为Python是一种没有分号的语言.的确 ...

最新文章

  1. 有多少状元能够成才?(zz)
  2. 关于NLP相关技术全部在这里:预训练模型、图神经网络、模型压缩、知识图谱、信息抽取、序列模型、语法分析、文本处理...
  3. 新疆自考大专计算机应用专业,新疆大学2017年新疆自考计算机信息管理(专科)考试计划...
  4. oracle slient静默安装并配置数据库及仅安装数据库不配置数据库shell
  5. 编译OSG的FreeType插件时注意的问题
  6. centos6.x 安装php5.6 tar,CentOs6.x安装php5.6.x Web程序 - 贪吃蛇学院-专业IT技术平台
  7. 程序员书籍哪里找?有这个GitHub项目就够了
  8. 第二:Java+MyBatis(深入学习)
  9. 九种跨域方式的实现原理,第一个就超惊艳!| 技术头条
  10. java循环树_for循环输出树木的形状【java】
  11. Kubernetes详解(十二)——节点选择器与资源注解
  12. RMAN 总括 组成 配置 检测
  13. 初学windows程序设计
  14. 博睿APM获《金融电子化》2021年金融业新技术应用创新突出贡献奖
  15. 缅因大学欧洛诺分校计算机科学,缅因大学申请要求高不高?开学时间是什么时候?...
  16. dat文件导入cad画图步骤_准确又高效的读入CAD几何——详解Fluent Meshing导入文件的几种方法...
  17. 使用pm命令安装或卸载apk,静默安装、卸载方法
  18. 人工智能——归结推理
  19. logback 自定义
  20. WeChatExtension:一款mac微信必备插件!

热门文章

  1. 推送近期三波关于Vue.js的资讯
  2. 怎么让联想计算机升级,联想电脑怎么升级win11?联想电脑升级win11的几种方法...
  3. Yolov5进阶之一摄像头实时采集识别
  4. Flutter 最佳扫码插件
  5. 三跨考生准备考研复试(机试)之路(日记版)
  6. 物联网操作系统再思考-Hello China操作系统的运营商网络协同机制
  7. 人之间的尊重是相互的_人与人之间彼此尊重是相互的,你若敬我一尺,我必敬你一丈...
  8. 1028: 安全路径(2014年中南大学研究生复试机试题 )
  9. pdf签名无效解决办法_为什么下载下来的电子合同提示有效性未知或至少一个签名有问题?...
  10. Ubuntu 16.04/18.04/20.04/deepin v20安装insight的方法