深度神经网络(DNN,Deep Neural Networks)简介

首先让我们先回想起在之前博客(数据挖掘入门系列教程(七点五)之神经网络介绍)中介绍的神经网络:为了解决M-P模型中无法处理XOR等简单的非线性可分的问题时,我们提出了多层感知机,在输入层和输出层中间添加一层隐含层,这样该网络就能以任意精度逼近任意复杂度的连续函数。

然后在数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST博客中,我们使用类似上图的神经网络结构对MINIST数据集进行了训练,最后在epochs = 100的条件下,F1 socre达到了约\(86\%\)。

这个时候我们想一想,如果我们将中间的隐含层由一层变为多层,如下图所示:

那么该网络就变成了深度神经网络(DNN),也可以称之为多层感知机(Multi-Layer perceptron,MLP)。

下面将对这个网络进行介绍以及公式推导。

DNN的基本结构及前向传播

在上面的图中,我们可以很容易的观察到,在DNN中,层与层之间是全连接的,也就是如同感知机一样,第\(i\)层的任意一个神经元与第\(i+1\)层的任意一个神经元都有连接。尽管这个网络看起来很庞大复杂,但是如果我们只看某一小部分,实际上它的原理与感知机很类似。

如同感知机,我们可以很简单的知道:

对于\(LayerL_2\)的输出,可知:

\[\begin{equation}\begin{aligned}
&a_{1}^{2}=\sigma\left(z_{1}^{2}\right)=\sigma\left(w_{11}^{2} x_{1}+w_{12}^{2} x_{2}+w_{13}^{2} x_{3}+b_{1}^{2}\right)\\
&\begin{array}{l}
a_{2}^{2}=\sigma\left(z_{2}^{2}\right)=\sigma\left(w_{21}^{2} x_{1}+w_{22}^{2} x_{2}+w_{23}^{2} x_{3}+b_{2}^{2}\right) \\
a_{3}^{2}=\sigma\left(z_{3}^{2}\right)=\sigma\left(w_{31}^{2} x_{1}+w_{32}^{2} x_{2}+w_{33}^{2} x_{3}+b_{3}^{2}\right)
\end{array}
\end{aligned}\end{equation}
\]

对于\(w\)的参数上标下标解释,以下图为例:

对于\(w_{24}^3\),上标3代表\(w\)所在的层数,下标2对应的是第三层的索引2,下标4对应的是第二层的索引4。至于为什么标记为\(w_{24}^3\)而不是\(w_{42}^3\),我们可以从矩阵计算的角度进行考虑:

在下图中,为了得到\(a\),我们可以直接使用\(a = Wx\),也可使用\(a = W^Tx\)这种形式,但是对于第二种形式,我们需要使用转置,这样会加大计算量,因此我们采用第一种形式。

对于\(LayerL_3\)的输出,可知:

\[\begin{equation}a_{1}^{3}=\sigma\left(z_{1}^{3}\right)=\sigma\left(w_{11}^{3} a_{1}^{2}+w_{12}^{3} a_{2}^{2}+w_{13}^{3} a_{3}^{2}+b_{1}^{3}\right)\end{equation}
\]

假设我们在\(l-1\)层一共有\(m\)个神经元,对于第\(l\)层第\(j\)个神经元的输出\(a_j^l\),有:

\[\begin{equation}a_{j}^{l}=\sigma\left(z_{j}^{l}\right)=\sigma\left(\sum_{k=1}^{m} w_{j k}^{l} a_{k}^{l-1}+b_{j}^{l}\right)\end{equation}
\]

如果我们采用矩阵的方式进行表示,则第\(l\)层的输出为:

\[a^l = \sigma(z^l) = \sigma(W^la^{l-1} + b^l)
\]

因此,我们可以对DNN的前向传播算法进行推导,从输入到输出有:

输入: 总层数\(L\),所有隐藏层和输出层对应的矩阵\(W\),偏倚向量\(b\),输入值向量\(x\)

输出:输出层的输出\(a^L\)

​ 1) 初始化\(a^1 = x\)

​ 2) for \(l=2\) to \(L\),计算:

\[\begin{equation}a^{l}=\sigma\left(z^{l}\right)=\sigma\left(W^{l} a^{l-1}+b^{l}\right)\end{equation}
\]

最后结果的输出即为\(a^L\)

以上便是DNN的前向传播算法,实际上挺简单的,就是一层一层向下递归。

DNN反向传播(BP)算法

数据挖掘入门系列教程(七点五)之神经网络介绍中,我们提到过BP算法,并进行过详细的数学公式的推导。BP算法的目的就是为了寻找合适的\(W,b\)使得损失函数\(Loss\)达到某一个比较小的值(极小值)。

在DNN中,损失函数优化极值求解的过程最常见的一般是通过梯度下降法来一步步迭代完成的,当然也有其他的方法。而在这里,我们将使用梯度下降法对DNN中的反向传播算法进行一定的数学公式推导。图片和部分过程参考了Youtube:反向传播算法,但是对其中的某一些图片进行了修改。

在左边的图片中,是一个比较复杂的DNN网络,我们针对该DNN网络进行简化,将其看成每一层只有一个神经元的网络,如右图所示

此时我们还可以将问题进行简化,如果我们只看简化模型的最后面两个神经元,则有:

\(y\)代表期望值,\(C_o\)代表损失函数\(\mathrm{C_0}=\operatorname{Loss}=\left(a^{(L)}-y\right)^{2}\),\(\sigma\)代表激活函数,比如说Relu,sigmoid,具体的表达式在图中,我就不写出来了。

在下图所示,当\(w^{(L)}\)发生微小的改变(\(\partial w^{(L)}\))时,会通过一连串的反应使得\(C_0\)发生微小的改变:类似蝴蝶扇动翅膀一样,响应流程如下\(\partial w^{(L)} \longrightarrow z^{(L)} \longrightarrow a^{(L)} \longrightarrow C_0\)。

此时我们对\(C_0\)求其\(W^L\)的偏导,则得到了下式:

\[\begin{equation}\frac{\partial C_{0}}{\partial w^{(L)}}=\frac{\partial z^{(L)}}{\partial w^{(L)}} \frac{\partial a^{(L)}}{\partial z^{(L)}} \frac{\partial C_0}{\partial a^{(L)}}\end{equation}
\]

我们分别求各自偏导的结果:

\[\begin{equation}\begin{aligned}
&\because C_{0}=\left(a^{(L)}-y\right)^{2} \\
& \therefore \frac{\partial C 0}{\partial a^{(L)}}=2\left(a^{(L)}-y\right)\\
&\because a^{(L)}=\sigma\left(z^{(L)}\right) \\
& \therefore\frac{\partial a^{(L)}}{\partial z^{(L)}}=\sigma^{\prime}\left(z^{(L)}\right)\\
&\because z^{(L)}=w^{(L)} a^{(L-1)}+b^{(L)}\\
& \therefore \frac{\partial z^{(L)}}{\partial w^{(L)}}=a^{(L-1)}
\end{aligned}\end{equation}
\]

综上,结果为:

\[\begin{equation}\frac{\partial C_{0}}{\partial w^{(L)}}=\frac{\partial z^{(L)}}{\partial w^{(L)}} \frac{\partial a^{(L)}}{\partial z^{(L)}} \frac{\partial C 0}{\partial a^{(L)}}=a^{(L-1)} \sigma^{\prime}\left(z^{(L)}\right) 2\left(a^{(L)}-y\right)\end{equation}
\]

同理我们可得:

\[\begin{equation}\frac{\partial C_{0}}{\partial b^{(L)}}=\frac{\partial z^{(L)}}{\partial b^{(L)}} \frac{\partial a^{(L)}}{\partial z^{(L)}} \frac{\partial C 0}{\partial a^{(L)}}=1 \sigma^{\prime}\left(z^{(L)}\right) 2\left(a^{(L)}-y\right)\end{equation}
\]

\[\begin{equation}\frac{\partial C_{0}}{\partial a^{(L-1)}}=\frac{\partial z^{(L)}}{\partial a^{(L-1)}} \frac{\partial a^{(L)}}{\partial z^{(L)}} \frac{\partial C 0}{\partial a^{(L)}}=w^{(L)} \sigma^{\prime}\left(z^{(L)}\right) 2\left(a^{(L)}-y\right)\end{equation}
\]

这时候,我们可以稍微将问题复杂化一点,考虑多个神经元如下图所示,那么此时所有的变量\(W,x,b,z,a,y\)也就变成了一个矩阵:

求导结果如下(这里我们使得Loss为\(J(W, b, x, y)=\frac{1}{2}\left\|a^{L}-y\right\|_{2}^{2}\)表示,代表Loss与\(W, b, x, y\)有关):

\[\begin{equation}\begin{array}{c}
\frac{\partial J(W, b, x, y)}{\partial W^{L}}=\frac{\partial J(W, b, x, y)}{\partial z^{L}} \frac{\partial z^{L}}{\partial W^{L}}=\frac{\partial J(W, b, x, y)}{\partial a^{L}} \frac{\partial a^{L}}{\partial z^{L}} \frac{\partial z^{L}}{\partial W^{L}}=\left(a^{L}-y\right) \odot \sigma^{\prime}\left(z^{L}\right)\left(a^{L-1}\right)^{T} \\
\frac{\partial J(W, b, x, y)}{\partial b^{L}}=\frac{\partial J(W, b, x, y)}{\partial z^{L}} \frac{\partial z^{L}}{\partial b^{L}}=\frac{\partial J(W, b, x, y)}{\partial a^{L}} \frac{\partial a^{L}}{\partial z^{L}} \frac{\partial z^{L}}{\partial b^{L}}=\left(a^{L}-y\right) \odot \sigma^{\prime}\left(z^{L}\right)
\end{array}\end{equation}
\]

注意上式中有一个符号\(\odot\),它代表Hadamard积, 对于两个维度相同的向量 \(A\left(a_{1}, a_{2}, \ldots a_{n}\right)^T 和 B\left(b_{1}, b_{2}, \ldots b_{n}\right)^{T},\) 则 \(A \odot B=\) \(\left(a_{1} b_{1}, a_{2} b_{2}, \ldots a_{n} b_{n}\right)^{T}\)。怎么理解这个变量呢?从一个不怎么严谨的角度进行理解:

假设第\(L-1\)层有\(i\)个神经元,第\(L\)层有\(j\)个神经元(如上图所示),那么毋庸置疑,\(\partial W\)为一个\(j \times i\)的矩阵(因为\(W\)为一个\(j \times i\)的矩阵,至于为什么,前面前向传播中已经提到了)。\(A \odot B\)则是一个\(j \times 1\)的矩阵,然后与\(\left(a^{L-1}\right)^{T}\)*(它是一个$1 \times i \(的矩阵)*相乘,最后结果则为一个\)j \times i$的矩阵。

在求导的结果\(\frac{\partial J(W, b, x, y)}{\partial W^{L}} 和 \frac{\partial J(W, b, x, y)}{\partial b^{L}}\)有公共部分,也就是\(\frac{\partial J(W, b, x, y)}{\partial z^{L}}=\left(a^{L}-y\right) \odot \sigma^{\prime}\left(z^{L}\right)\),代表输出层的梯度,因此我们令:

\[\begin{equation}\delta^{L}=\frac{\partial J(W, b, x, y)}{\partial z^{L}}=\left(a^{L}-y\right) \odot \sigma^{\prime}\left(z^{L}\right)\end{equation}
\]

根据前向传播算法,对于与第\(l\)层的\(W^l 和 b^l\)的梯度有如下结论:

\[\begin{equation}\begin{array}{c}
\frac{\partial J(W, b, x, y)}{\partial W^{l}}=\frac{\partial J(W, b, x, y)}{\partial z^{l}} \frac{\partial z^{l}}{\partial W^{l}}=\delta^{l}\left(a^{l-1}\right)^{T} \\
\frac{\partial J(W, b, x, y)}{\partial b^{l}}=\frac{\partial J(W, b, x, y)}{\partial z^{l}} \frac{\partial z^{l}}{\partial b^{l}}=\delta^{l}
\end{array}\end{equation}
\]

因此问题就变成了如何求得任意一层\(l\)的\(\delta^{l}\),假设我们一共有\(L\)层,则对于\(\delta^{L}\)我们还是能够直接进行求解\(\delta^{L}=\left(a^{L}-y\right) \odot \sigma^{\prime}\left(z^{L}\right)\),那么我们如何对\(L-1\)层进行求解呢?

设第\(l+1\)层的\(\delta^{l+1}\)已知,则对\(\delta^{l}\)的求解如下:

\[\delta^{l} = \frac{\partial J(W,b,x,y)}{\partial z^l} = (\frac{\partial z^{l+1}}{\partial z^{l}})^T\frac{\partial J(W,b,x,y)}{\partial z^{l+1}} =(\frac{\partial z^{l+1}}{\partial z^{l}})^T \delta^{l+1}
\]

也就是说,求解关键点又到了\((\frac{\partial z^{l+1}}{\partial z^{l}})^T\)的求解,根据前向传播算法:

\[z^{l+1}= W^{l+1}a^{l} + b^{l+1} = W^{l+1}\sigma(z^l) + b^{l+1}
\]

因此,有:

\[\frac{\partial z^{l+1}}{\partial z^{l}} = W^{l+1}diag(\sigma^{'}(z^l))
\]

综上可得:

\[\delta^{l} = (\frac{\partial z^{l+1}}{\partial z^{l}})^T\frac{\partial J(W,b,x,y)}{\partial z^{l+1}} = diag(\sigma^{'}(z^l))(W^{l+1})^T\delta^{l+1} =
(W^{l+1})^T\delta^{l+1}\odot \sigma^{'}(z^l)
\]

因此当我们可以得到任意一层的\(\delta^{l}\)时,我们也就可以对任意的\(W^l和b^l\)进行求解。

算法流程

下面算法流程是copy深度神经网络(DNN)反向传播算法(BP)的,因为他写的比我好多了,我就直接用他的了。

现在我们总结下DNN反向传播算法的过程。由于梯度下降法有批量(Batch),小批量(mini-Batch),随机三个变种, 为了简化描述, 这里我们以最基本的批量梯度下降法为例来描述反向传播算法。实际上在业界使用最多的是mini-Batch的 梯度下降法。不过区别又仅在于迭代时训练样本的选择而已。

输入: 总层数\(L\), 以及各隐藏层与输出层的神经元个数, 激活函数, 损失函数, 选代步长 \(\alpha\),最大迭代次数\(MAX\)与停止迭代阈值\(\epsilon\), 输入的\(m\)个训练样本 \(\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \ldots,\left(x_{m}, y_{m}\right)\right\}\)。

输出: 各隐藏层与输出层的线性关系系数矩阵 \(W\) 和偏倚向量\(b\)

  1. 初始化各隐藏层与输出层的线性关系系数矩阵\(W\)和偏倚向量\(b\)的值为一个随机值。

  2. for iter to 1 to MAX:

    2.1 for \(i=1\) to \(m\) :

    ​ a. 将DNN输入 \(a^{1}\) 设置为 \(x_{i}\)

    ​ b. for \(l=2\) to \(L,\) 进行前向传播算法计算 \(a^{i, l}=\sigma\left(z^{i, l}\right)=\sigma\left(W^{l} a^{i, l-1}+b^{l}\right)\)

    ​ c. 通过损失函数计算输出层的 \(\delta^{i, L}\)

    ​ d. for \(l=\) L-1 to 2 , 进行反向传播算法计算 \(\delta^{i, l}=\left(W^{l+1}\right)^{T} \delta^{i, l+1} \odot \sigma^{\prime}\left(z^{i, l}\right)\)

    2.2 for \(l=2\) to \(\mathrm{L},\) 更新第\(l\)层的 \(W^{l}, b^{l}:\)

\[\begin{array}{c}
W^{l}=W^{l}-\alpha \sum_{i=1}^{m} \delta^{i, l}\left(a^{i, l-1}\right)^{T} \\
b^{l}=b^{l}-\alpha \sum_{i=1}^{m} \delta^{i, l}
\end{array}
\]

​ 2-3. 如果所有\(W,b\)的变化值都小于停止迭代阈值 \(\epsilon,\) 则跳出迭代循环到步骤3。

  1. 输出各隐藏层与输出层的线性关系系数矩阵\(W\)和偏倚向量\(b\)。

总结

这一篇博客主要是介绍了以下内容:

  • DNN介绍
  • DNN的基本结构
  • DNN的前向传播
  • DNN的BP算法

本来是想在这一章博客中将CNN也介绍一下,但是想了想,可能还是分开介绍比较好。因此我将会在下一篇博客中主要会对CNN进行介绍以及部分推导。

参考

数据挖掘入门系列教程(十点五)之DNN介绍及公式推导的更多相关文章

  1. 数据挖掘入门系列教程(五)之Apriori算法Python实现

    数据挖掘入门系列教程(五)之Apriori算法Python实现 加载数据集 获得训练集 频繁项的生成 生成规则 获得support 获得confidence 获得Lift 进行验证 总结 参考 数据挖 ...

  2. 数据挖掘入门系列教程(十一)之keras入门使用以及构建DNN网络识别MNIST

    简介 在上一篇博客:数据挖掘入门系列教程(十点五)之DNN介绍及公式推导中,详细的介绍了DNN,并对其进行了公式推导.本来这篇博客是准备直接介绍CNN的,但是想了一下,觉得还是使用keras构建一个D ...

  3. 数据挖掘入门系列教程(四点五)之Apriori算法

    目录 数据挖掘入门系列教程(四点五)之Apriori算法 频繁(项集)数据的评判标准 Apriori 算法流程 结尾 数据挖掘入门系列教程(四点五)之Apriori算法 Apriori(先验)算法关联 ...

  4. 数据挖掘入门系列教程(十二)之使用keras构建CNN网络识别CIFAR10

    简介 在上一篇博客:数据挖掘入门系列教程(十一点五)之CNN网络介绍中,介绍了CNN的工作原理和工作流程,在这一篇博客,将具体的使用代码来说明如何使用keras构建一个CNN网络来对CIFAR-10数 ...

  5. 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST

    目录 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST 下载数据集 加载数据集 构建神经网络 反向传播(BP)算法 进行预测 F1验证 总结 参考 数据挖掘入门系 ...

  6. 数据挖掘入门系列教程(九)之基于sklearn的SVM使用

    目录 介绍 基于SVM对MINIST数据集进行分类 使用SVM SVM分析垃圾邮件 加载数据集 分词 构建词云 构建数据集 进行训练 交叉验证 炼丹术 总结 参考 介绍 在上一篇博客:数据挖掘入门系列 ...

  7. 数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列教程(二)之分类问题OneR算法 数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 项目地址:G ...

  8. 数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例)

    数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例) 简介 scikit-learn 估计器 加载数据集 进行fit训练 设置参数 预处理 流水线 结尾 数据挖掘入门系 ...

  9. 数据挖掘入门系列教程(四)之基于scikit-lean实现决策树

    目录 数据挖掘入门系列教程(四)之基于scikit-lean决策树处理Iris 加载数据集 数据特征 训练 随机森林 调参工程师 结尾 数据挖掘入门系列教程(四)之基于scikit-lean决策树处理 ...

随机推荐

  1. coding++:Spring_IOC(控制反转)详解

    IoC是什么: 1):Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想. 2):在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的 ...

  2. iOS 图片加载和处理

    一.图片显示 图片的显示分为三步:加载.解码.渲染.解码和渲染是由 UIKit 进行,通常我们操作的只有加载. 以 UIImageView 为例.当其显示在屏幕上时,需要 UIImage 作为数据源. ...

  3. 大部分程序员还不知道的 Servelt3 异步请求,原来这么简单?

    前言 博文地址:https://sourl.cn/URptix 当一个 HTTP 请求到达 Tomcat,Tomcat 将会从线程池中取出线程,然后按照如下流程处理请求: 将请求信息解析为 HttpS ...

  4. 【纯净镜像】原版Windows7集成USB3.0+NVME补丁+UEFI引导旗舰版下载

    系统简述: 1. 基于MSDN原版Windows7 Ultimate With SP1系统制作,无任何插件和垃圾软件. 2. 系统集成IE11浏览器,装完系统后默认浏览器就是IE11. 3.系统注入了 ...

  5. UVA11300 Spreading the Wealth 数学

    前方数学警告 题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=25&am ...

  6. 悟懂MapReduce,不纠结!

    在<谷歌 MapReduce 初探>中,我们通过统计词频的 WordCount 经典案例,对 Google 推出的 MapReduce 编程模型有了一个认识,但是那种认识,还只是停留在知道 ...

  7. 【tensorflow2.0】自动微分机制

    神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情. 而深度学习框架可以帮助我们自动地完成这种求梯度运算. Tensorflow一般使用梯度磁带tf.Gradi ...

  8. VUE一款适用于pc平台的简单toast

    新项目要求用typescript+vue+elementui的模式来搭建pc项目,最初踩了好多坑.产品说提示不想用element-ui的提示. 打算用toast的形式.所以就自己写了一个pc的toas ...

  9. MySQL从库实用技能(一)--巧用slave_exec_mode参数

    想必从库异常中断的情况不在少数,其中报错信息中1032及1062的错误占了不少的比重 错误1032指的是从库中找不到对应行的记录 错误1062指的是主键冲突 遇到此报错时,大多DBA会使用如下方法进行 ...

  10. php连接数据库,php连接mysql并查询的几种方式,PHP PDO连接以及预处理

    PHP连接数据库 面向过程 $config = [ 'host'=>'127.0.0.1', //数据库地址 'name'=>'test', //库名 'user'=>'root', ...