深度神经网络(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. logstash用jdbc插件将数据库内容导入elasticsearch时间字段相差5小时

    logstash将mysql的数据导入elasticsearch之后发现时间字段的相差5个小时 解决办法: 在数据库连接配置后面加上?serverTimezone=UCT这个就OK了 logstash ...

  2. Benelux Algorithm Programming Contest 2019

    J. Jazz it Up!题目要求,n*m的因子中不能含有平方形式,且题目中已经说明n是一个无平方因子的数, 那么只要m是无平方因子的数,并且n和m没有共同的因子即可.要注意时间复杂度!代码:#in ...

  3. Linux下使用FastDFS

    本文所有操作均在CentOS 7.x环境下进行. 1.1.单节点FastDFS 整个安装过程非常复杂,很容易出错,建议进行多次备份. 我们这里不打算安装多台虚拟机,因此会把tracker和storag ...

  4. SpringCloud入门(八): Zuul 过滤器详解

    Zuul 过滤器 zuul 有四种过滤器类型,分别是: 1.Pre:过滤器在请求被路由之前调用.我们可利用这种过滤器实现身份验证.在集群中选择请求的微服务.记录调试信息等: 2.Routing:过滤器 ...

  5. JavaScipt创建函数的方法

    JavaScipt的函数的定义有三种方式:  一.命名函数定义 1.JavaScript 函数通过 function 关键词进行定义,其后是函数名和括号 (). 2.函数名可包含字母.数字.下划线和美 ...

  6. 【杂谈】SpringBoot为啥不用配置启动类

    前言 在学习SparkJava.Vert.x等轻量级Web框架的时候,都遇到过打包问题,这两个框架打包的时候都需要添加额外的Maven配置,并指定启动类才能得到可执行的JAR包: 而springboo ...

  7. 徒手生撸一个验证框架,API 参数校验不再怕!

    你们之中大概率早已练就了代码的拷贝.粘贴,无敌的码农神功,其实做久了业务功能开发,练就这两个无敌神功,那是迟早的事儿.今天先抛一个小问题,来打通你的任督二脉,就是很好奇的问一下:业务功能开发中,输入参 ...

  8. 《Three.js 入门指南》3.1.1 - 基本几何形状 -圆环结(TorusKnotGeometry)

    3.1 基本几何形状 圆环结(TorusKnotGeometry) 构造函数 THREE.TorusKnotGeometry(radius, tube, radialSegments, tubular ...

  9. 经常登录Linux,用户密码背后的知识了解一下

    一,用户密码存放在哪里? 说到这个问题,绝大部分的同学肯定都知道/etc/passwd这个文件,不错,这个文件里存储的就是用户名,密码等信息. 每一行都是一个account,每一行有7个信息,分别用 ...

  10. python--爬虫(XPath与BeautifulSoup4)

    获取页面内容除使用正则意外,还可以使用XPath,其原理是将html代码转换为xml格式,然后使用XPath查找html节点或元素. 选取节点 XPath使用路径表达式来选取XML文档中的节点或节点集 ...