深度神经网络(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. Ubuntu 18 安装MySQL 5.7

    1.首先把系统换到阿里云的镜像源,需要等待一会 2.系统更新完毕后执行MySQL安装命令:sudo apt install mysql-server 3.查看MySQL服务状态:sudo servic ...

  2. 使用 Visual Studio 开发、测试和部署 Azure Functions(一)开发

    1,什么是Azure functions Azure Functions 是 Microsoft Azure 提供的完全托管的 PaaS 服务,用于实现无服务器体系结构. Azure Function ...

  3. Luogu1039 侦探推理

    题目描述 明明同学最近迷上了侦探漫画<柯南>并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏.游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明 ...

  4. SQL Server 创建链接服务器的脚本,自定义链路服务器的简短名称

    USE [master]GO /****** Object:  LinkedServer [SQL01]    Script Date: 2020/4/9 11:51:17 ******/EXEC m ...

  5. Java并发基础07. ThreadLocal类以及应用技巧

    在前面的文章(6. 线程范围内共享数据)总结了一下,线程范围内的数据共享问题,即定义一个 Map,将当前线程名称和线程中的数据以键值对的形式存到 Map 中,然后在当前线程中使用数据的时候就可以根据当 ...

  6. 使用gulp自动构建项目

    网址:https://segmentfault.com/a/1190000011514257

  7. 为什么要学习Oracle技术?

    为什么要学习Oracle技术? 众所周知,Oracle占据着企业数据库领域超过48.1%的市场份额,成为高端企业数据库软件的绝对领导者.随着时间的推移,企业数据库的规模不断扩大,富有经验的资深Orac ...

  8. go的channel

    go语言channel go语言提供了goroutine来实现并发,go语言也提供了channel来实现并发事件之间的通信. 传统的编程语言通过共享内存来实现通信,当多个线程同时操作一个共享变量的时候 ...

  9. MySQL数据库三

    MySQL数据库三 多表查询: 有条件的内连接 将两张表根据相同的id连接起来 select * from info join details on info.id = details.id sele ...

  10. 7.2 java 类的定义和使用

    /* * 类的定义: * 类是用来描述现实世界的事物的 * * 事物: * 属性 事物的描述信息 * 行为 事物能够做什么 * * 类是如何和事物进行对应的呢? * 类: * 成员变量 * 成员方法 ...