导读
趁着清明小假期,决定继续输出几篇文章。对于PyTorch学习教程系列,有了前几篇推文做铺垫,这次打算用三篇文章分别介绍一下深度学习中的三大基石:DNN、CNN、RNN。本文就从DNN开始,即深度神经网络。
本文建议结合历史推文《PyTorch学习系列教程:Tensor如何实现自动求导》配套阅读。
深度神经网络就是所谓的深度学习吗?
深度神经网络,英文Deep Nueral Network,简写DNN,是研究最早也最为常用的神经网络模块,其本质上是一个多层感知机(Multi-Layer Perceptron,MLP)结构,只不过当层数较少时叫做MLP,层数更多时则叫DNN,但其实质是一致的。此外,MLP可算作是传统机器学习模型的范畴,而DNN则归属于深度学习领域,所以从某种角度讲DNN也可算作是传统机器学习和深度学习的结合部。
那么,除了以上源于对MLP的认知,DNN网络还有哪些形态?它为什么有效?它有什么特点或者适用场景?为了回答这些疑问,本文从以下几个方面加以介绍:
- 什么是DNN
- DNN为何有效
- DNN的适用场景
- 在PyTorch中的使用
01 什么是DNN
DNN叫做深度神经网络,顾名思义,其包含两层含义:其一它是一个神经网络;其二它是一个层数较深的神经网络。而为了解释神经网络,那么就不得不先从生物神经元讲起(可能这也是所有介绍神经网络时,都会引用的一个例子):
生物神经元示意图
其实刚看到这张图时,我也很好奇伟大的先知者是怎样的脑洞大开受其启发而提出了神经网络——直到我们将这个神经元抽象为以下逻辑连接图:
神经网络中的一个神经元
也就是说,生物神经元虽然结构看上去错综复杂,但其实无外乎是多个像树枝一样的分支(这里的分支就是树突)汇聚到一个节点(也叫轴突),而后轴突根据所有分支汇入的能量之和大小来判断是否给上游一个刺激信号(这个根据能量大小决定输出的过程就是激活函数)。
有了单个神经元作为基础单元,很快就可以将其量产形成一个网络,这个由大量的神经元构成的网络就叫做神经网络。一个简单的神经网络示意图如下:
一个具有3层的DNN网络架构
当然,如果仔细对比这个3层的DNN和前述的单个神经元,那么严谨的说,这个DNN网络结构中省略了激活函数。
以上介绍了神经网络是怎么提出的,相当于回答了什么是神经网络的问题。那么进一步的,什么是深度神经网络呢?这个问题其实不难理解,但却也没有统一的标准答案。说其不难理解,是因为深度神经网络无外乎就是层数比较深,例如前面的是一个3层的DNN网络,可以很容易将其拓展为4层、5层乃至更多层,但究竟多少层开始算深度,其实是没有确切答案的。一般而言,当网络层数≥3时,就可以称之为深度神经网络了。
这里,还有几个细节值得注意:
- 神经网络的层数怎么算?以上述的网络为例,输入层就叫做输入层,不算做一层(如果习惯于计算机世界的从0开始,将其称之为第0层也可以),而后从第二列开始(也就是第一个隐藏层)开始算作网络的第一层,直至输出层(输出层也算作一层),所以上述的示意网络是一个3层的DNN模型;
- 相邻层之间的所有神经元均具有连接关系,即这是一个全连接结构。实际上,输入层的节点代表一个特征,之后的隐藏层和输出层的每个神经元代表一个信息提取结果,由于无法断言前一层的哪个节点对于后一层有作用或者没作用,所以最简单有效的办法就是相邻层的任意两个节点之间均建立连接。这在深度学习框架中就叫做网络权重(weight),是一个可训练的参数,网络的权重矩阵直接代表了模型;
- 相邻层之间的连接必须搭配适当的激活函数,否则增加层数无意义。个人以为,激活函数的提出可谓是深度学习中的救世主,虽然只是简单的提供了非线性关系,但却大大增强了网络承载信息的能力;换言之,如果不设置激活函数,那么任意多层的神经元线性组合的结果其实等价于单层的线性组合,结果就是增加层数是无意义的。
那么,为什么增加了激活函数就能保证神经网络的承载信息能力?什么又是激活函数?这就要引出下一话题:DNN为何有效?
02 DNN为何有效
DNN为何有效?其实提出神经网络的先知们也思考过这个问题,最终得出的答案是——通用近似定理。
摘自《神经网络与深度学习》-邱锡鹏
不过,看了这一段极为绕口的理论之后,似乎竟不能理解这是要表达什么含义。简言之,通用近似定理阐述的内涵是:通过增加神经网络深度 激活函数(体现为上述定理中非常数、单调、有界的函数φ())带来的非线性效果,可以逼近任意函数(这里的逼近体现为上述的ε)。
换言之:从机器学习的视角理解,模型经过在训练集上的学习过程,无非是拟合了一个由输入到输出的映射函数,只不过这个映射函数往往没有显式表达式,很可能是一个极为复杂且不连续的多段函数,那么经过足够层数的神经网络 激活函数之后,可以逼近这个映射。
关于这里的通用近似定理的介绍,参考书籍(邱锡鹏教授的《神经网络与深度学习》)中有一段非常贴切的描述,通过几次非线性的函数嵌套逼近了一个多段函数,以此来论证深度神经网络可以通用近似任意函数。
显然,也正如前面第一小节里提到的那样,这里之所以能够通用近似为任意函数,激活函数发挥了至关重要的作用。所以这里也简单的介绍一下几个常用的激活函数:
- sigmoid:最早使用和常用的激活函数之一
- tanh:sigmoid激活函数的缩放版,tanh(x) = 2(sigmoid(2x)-1),具有零点对称性和相对更好的梯度
- relu:最为常用的激活函数之一,具有单侧抑制特性,计算简单,梯度固定,可一定程度抑制梯度消失和梯度弥散
- elu:relu的改进版,在零点时梯度连续
- leakyRelu:relu的改进版,在负半轴梯度不再为0
- gelu:近年来论文中新提出的激活函数,不再具有单调性质。。
总的来说,激活函数的种类比较多,除了个别魔改的网络外,大体上用simoid、tanh和relu三者中的一种或几种组合是足够的,这也是最为常用的形式。
几种激活函数的曲线特性对比
小结一下,这里介绍DNN为什么是一个有效的神经网络模型,也就是why it works的问题。简言之:通过在层与层神经元的连接之间加入具有非线性特性的激活函数,增加神经元的层数,可以实现逼近任意函数的性质。这也是深度学习中的一个具有奠基地位的理论——通用近似定理。
03 DNN适用场景
DNN作为深度学习网络的早期代表,曾经在一段时间之内扮演着重要角色。但若论其适用的场景,其实答案是没有特别适用的场景:似乎其适用于任何深度学习场景,但又没有特殊专长的场景。相较于专长于图像视觉的卷积神经网络(CNN)和专长于序列建模的循环神经网络(RNN)来说,如果定要给DNN定下个适用场景,那么我个人觉得可以这样描述:
DNN适用于输入数据之间具有同质的特性且又不体现特殊依赖的场景,其中:数据同质的意义在于强调各输入数据间的含义和作用是相同的,而并非像传统机器学习中每列特征代表不同的含义;而不体现特殊依赖在于表明DNN不足以提取出输入数据间可能具有的方位组合或者顺序依赖信息——这是CNN和RNN分别擅长的场景。
当然,随着深度学习理论的迅猛发展,单纯的使用DNN进行网络建模的场景越来越少,更多的情况是与其他网络结构组合使用,例如在图像分类中,搭配若干个CNN单元之后配备一层或几层全连接单元用于最后的分类;在序列建模中的最后一层往往也是全连接单元。这或许才是DNN的真正价值和灵魂之所在吧!
04 在PyTorch中的使用
DNN作为深度学习中几乎是最常用的网络单元,在PyTorch中具有很好的封装结构。实际上,每个全连接层其实都在做一个线性变换,例如输入数据用矩阵X表示(X的维度为NxD1,N为样本数量,D1为特征数量),下一层有D2个神经元,那么描述这一变换只需要用一个矩阵乘法即可:Y = X*W^T b,其中W为权重矩阵,维度为D2xD1,b为偏置向量,维度为D2。
描述这一过程,在PyTorch中的模块即为nn.Linear(),其说明文档为:
文档交代得也比较清晰了:
- 类的初始化参数:in_features、out_features分别表示全连接前后的神经元数量,bias表示是否拟合偏置项;
- 类的输入输出形状,输入数据维度为(*, in_features),输出数据维度为(*, out_features),即保持前序的维度不变,仅将最后一个维度由in_features维度变换为out_features;
- 类的属性:weight,拟合的权重矩阵,维度为(out_features, in_features);bias,拟合的偏置向量。
ok,上述就是关于深度学习中最为基础和常用的网络模型——DNN的介绍,虽然现在已经很少单纯使用这种类型的网络架构,但却仍然是众多高阶复杂模型中不可缺少的一部分;甚至说,理解了DNN的工作原理,对于后续理解其他网络结构也是很有必要的。
参考资料:《神经网络与深度学习》 邱锡鹏 著.
相关阅读:
- 写在1024:一名数据分析师的修炼之路
- 数据科学系列:sklearn库主要模块简介
- 数据科学系列:seaborn入门详细教程
- 数据科学系列:pandas入门详细教程
- 数据科学系列:matplotlib入门详细教程
- 数据科学系列:numpy入门详细教程