深度学习最近两年在音频分析,视频分析,游戏博弈等问题上取得了巨大的成果。由于微软,谷歌等科技巨头的推动及应用上的可见突破,使得深度学习成为目前学术界和工业界的超热门话题。包括国内很多公司也乐见其成,适时宣布自己的产品或算法也拥抱了深度学习。不过对于具体如何使用,达到了什么效果等问题讳莫如深。
事实上,关于深度学习的大量研究成果在上世纪已经完成,包括卷积神经网络(CNN)、长短时记忆网络(LSTM),多层网络的表达能力等。宣称自己在使用某个算法工具其实并不具备天然的正义性,如何能将工具用好,解决关键场景才是我们应该关心的问题。
为提升对非结构化数据的处理,玻森(bosonnlp.com)3内部也在开展深度学习的讨论。接下来的一段时间,我们将不定期更新深度学习的讨论 notes。
我们不谈情怀,不讨论人工智能是否要统治人类,也不论奇点是否临近。我们就谈点落地的深度学习基础数学理论,及背后的几何直观。世界太大,认知有限,这点上我们遵从维特根斯坦的观点,“凡能够说的,都能够说清楚;凡不能谈论的,就应该保持沉默”。
人类大脑的神经系统相当复杂,其具体的工作机制至今仍是前沿的生物课题。在1957年时由Cornell大学的学者 Frank Rosenblatt 博士所提出的感知机(Perceptron)模型可以认为是对人类神经系统的一个高度简化概括:
其中w和b作为模型的参数。
Frank Rosenblatt 博士恐怕是史上对人工智能贡献最大的心理学家。他的兴趣不仅停留在从数学上抽象出感知机和提出相应的学习算法,还亲自打造了下面这台被称之为Mark I Perceptron的“实体”感知机,其具备512个隐藏单元哦!
当加权求和输入值时,感知机会与0做对比,以确定自己是否处于激发状态。实际的神经元在人脑中数以百亿计。上式中神经元所输出的状态非0即1,而实际在计算机所建立的模型中,往往将其泛化:
f为激活函数(activation function)。
神经网络可以看做是感知机的一种泛化形式,通过递归嵌套及选择不同的激活函数,我们可以不断将原始数据x进行变换,或特征抽取。
通过对上述式子的嵌套,我们可以构造具有更为复杂表达能力的函数。如两层嵌套形式:
注意到感知机实际上相当于采用Step函数作为激活函数。其他常用的激活函数有Sigmoid, Tanh, ReLU等。其中Sigmoid函数
在早期的神经网络研究文献中被广泛使用。目前对于大多非结构化数据,人们发现分段线性(piece-wise linear)函数
具备更好的鲁棒性,并能够更快收敛。常见的激活函数可以参见下表。
无环结构:
一层神经元的输出作为下一层神经元的输入,信息总是向前反馈。这一类型的神经网络也叫 Feed-forward Nerual Network。卷积网络(Convolutional Nerual Network)也包括在其中。
有环结构:
一层神经元的输出可以作为当前神经元的输入,即产生了反馈循环(feedback loop)。反馈环表示了神经网络的内部状态,使得网络的输出与之前的输入数据相关。如果把时间维度展开的话,有环结构也相当于随着时间走的无环结构。这一类型的神经网络也叫 Recurrent Neural Network (RNN)。
对于给定的网络拓扑结构,具体网络参数的确定其实是一个标准的优化问题。我们通过定义一个损失函数(cost function),来衡量当前神经网络的输出与我们期望的输出之间的距离。
我们构造的 Net(x, w) 以 w 为参数的,x 为输入的网络的输出值。而对 w 参数的寻找,在深度学习中最常用的是随机梯度下降法(Stochastic Gradient Descent)。其基本思想仍然是让变量沿着损失函数在目前参数的尽可能下降的方向进行调整
η 称之为学习率,用以控制修改参数的激进程度。后面的讨论中我们会提到,其实严格按照梯方向更新并无必要,只需要确保更新能够降低损失函数即可。这个观察与Liang Huang etc.在前些年所发表的Structured Perceptron with Inexact Search1可以进行类比。
如果我们能够得到参数 w 的 Hessian 矩阵,可以规避选择的问题,但对于只采用梯度这样一阶信息的优化方法,如何选择学习率,是否能利用某个参数的历史更新信息来估算Hessian矩阵等,都是比较有意思的研究问题。有兴趣的朋友可以参考Schaul etc.的 No More Pesky Learning Rates。
感知机和 Sigmoid 等价吗?
Sigmoid 可以很容易用来模拟感知机或 step function 输出。为验证这一点,Sigmoid 的所有输入值扩大 c 倍(c >> 1),即 c(wx+b),除 wx+b=0 这个输入点,Sigmoid 的行为处处与感知器的输行为可以任意接近。利用讨论1的结论,可以证明二者等价。
打乱神经网络的输入顺序会影响学习的结果吗?
全连通的神经网络不会。因为这样的神经网络对输入数据没有区分,且输出是当下数据的最优解,即相对应的权重向量和 bias。但局部连通的神经网络比如 Convolutional Nerual Network 就会有影响。
为什么不直接采用“有多少被错误分类的数据个数”作为 cost function 来评估神经网络的表现?
“有多少被错误分类的数据个数”虽然常常是我们优化的最终目标,但因为该函数并不属于凸函数(convex function),对其优化较为困难(事实上优化该cost function 可以被证明是一个NP问题)。所以人们常采用凸函数作为其“代理”。相关资料可以参考 surrogate loss function1。
为什么梯度下降是朝着下降最快的方向?为什么随机梯度能够收敛?
根据Cauchy-Schwarz不等式,我们得知向量的点积,在 和负梯度的夹角为0°时取得最小值。因此,在负梯度方向上,损失函数降的最快。并且只要变化向量和负梯度方向的夹角为锐角,即为负。因此,单个数据的下降方向虽然不是整体最优(下降最快)的方向,但只要是走在收敛的路上,就能使得函数逼近极值,因此可以收敛。
上一次的分享我们提到了神经网络的几个基本概念,其中提到了随机梯度下降(SGD)算法是神经网络学习(或者更通用的,一般性参数优化问题)的主流方法。概念上,神经网络的学习非常简单,可以被归纳为下面的步骤:
(a) 构造神经网络结构(选择层数、激活函数等)
(b) 初始化构造出的神经网络参数
(c) 对于给定的训练样本与当前的,计算梯度
(d) 通过(随机)梯度下降算法更新。
例如,不考虑任何正则化因子情况的最简单参数更新为
神经网络的初学者往往会发现,上述四个步骤当中,对于给定样本,计算其梯度是最 不直观 的一个步骤。本文我们玻森(bosonnlp.com)的讨论就围绕解决梯度的核心算法:后向传播 算法来展开。
首先理清一个概念,步骤(d)的梯度下降算法是一种优化算法,而我们要讨论的后向传播算法,是计算步骤(c)中所需要梯度的一种算法。下面的讨论,我们首先完成单参数(即只有一个参数需要学习)的特例情况下的推导,进而通过 动态规划(Dynamic programming) 思想,将其推导泛化到多变量的情况。需要注意的是,虽然后向传播概念上并不复杂,所用到的数学工具也很基本,但由于所涉及的变量较多、分层等特点,在推导的时候需要比较仔细,类似绣花。
特例
在讨论后向传播算法之前,我们简单回顾一下单变量微积分中的求导规则。来看个例子,假设我们有一个极端简化的网络,其中只有一个需要学习的参数,形式如下
并且假设损失函数Cost为平方误差(MSE)。
假设我们只有一个训练样本。因为这个形式非常简单,我们试试将样本直接带入损失函数:
显然当时,我们可以让损失函数为0,达到最优。下面让我们 假装 不知道最优解,考虑如何用梯度下降方法来求解。假设我们猜为最优,带入计算得到
嗯,不算太坏的一个初始值。让我们计算其梯度,或者损失函数关于的导数。
设置学习率参数,我们可以通过梯度下降方法来不断改进,以达到降低损失函数的目的。三十个迭代的损失函数变化如下:
生成上图采用的是如下Python代码
import matplotlib.pyplot as plt
w0, eta, n_iter = 2, 0.02, 30
gradient_w = lambda w: 2*(w**3)
cost = lambda w: 0.5*(w**4)
costs = []
w = w0
for i in range(n_iter):
costs.append(cost(w))
w = w – eta*gradient_w(w) # SGD
plt.plot(range(n_iter), costs)
可以发现,经过30次迭代后,我们的参数从初始的2改进到了0.597,正在接近我们的最优目标。
对于一般的情况
回忆一下,上面的结果是基于我们给定 下得到的,注意这里我们假设输入信号为常量。我们将上面的求解步骤做一点点泛化。
重复上面的求解
关于w求导,
注意,上面求导用到了 链式法则(Chain Rule),即
或者写成偏导数形式:
对于一般性损失函数的情况
上式推导基于损失函数为平方最小下得出,那么我们再泛化一点,对于任意给定的可导损失函数,其关于的梯度:
其中是损失函数关于的导数。实际上这个形式很通用,对于 任意 特定的损失函数和神经网络的激活函数,都可以通过这个式子进行梯度计算。譬如,对于一个有三层的神经网络
同样通过链式法则,
上式看上去比较复杂,我们可以在符号上做一点简化。令每一层网络得到的激活函数结果为,即, 那么:
即:不论复合函数本身有多么复杂,我们都可以将其导数拆解成每一层函数的导数的乘积。
上面的推导我们给出了当神经网络仅仅被一个可学习参数所刻画的情况。一句话总结,在单参数的网络推导中,我们真正用到的唯一数学工具就是 链式法则。实际问题中,我们面对的参数往往是数以百万计的,这也就是为什么我们无法采用直觉去“猜”到最优值,而需要用梯度下降方法的原因。下面我考虑在多参数情况下,如何求解梯度。
首先,不是一般性的,我们假设所构建的为一个层的神经网络,其中每一层神经网络都经过线性变换和非线性变换两个步骤(为简化推导,这里我们略去对bias项的考虑):
定义网络的输入,而作为输出层。一般的,我们令网络第层具有个节点,那么。注意此时我们网络共有个参数需要优化。
为了求得梯度,我们关心参数关于损失函数的的导数:,但似乎难以把简单地与损失函数联系起来。问题在哪里呢?事实上,在单参数的情况下,我们通过链式法则,成功建立第一层网络的参数与最终损失函数的联系。譬如,,的改变影响函数的值,而连锁反应影响到的函数结果。那么,对于值的改变,会影响,从而影响。通过的线性变换(因为),的改变将会影响到每一个。
将上面的过程写下来:
可以通过上式不断展开进行其梯度计算。这个方式相当于我们枚举了 每一条 改变对最终损失函数影响的 路径。通过简单使用链式法则,我们得到了一个 指数级 复杂度的梯度计算方法。稍仔细观察可以发现,这个是一个典型的递归结构(为什么呢?因为定义的是一个递归结构),可以采用动态规划(Dynamic programming)方法,通过记录子问题答案的进行快速求解。设用于动态规划的状态记录。我们先解决最后一层的边界情况:
上式为通用形式。对于Sigmoid, Tanh等形式的element-wise激活函数,因为可以写成的形式,所示上式可以简化为:
即该情况下,最后一层的关于的导数与损失函数在导数和最后一层激活函数在的导数相关。注意当选择了具体的损失函数和每层的激活函数后,与也被唯一确定了。下面我们看看动态规划的 状态转移 情况:
成功建立与的递推关系,所以整个网络的可以被计算出。在确定了后,我们的对于任意参数的导数可以被简单表示出:
至此,我们通过链式法则和动态规划的思想,不失一般性的得到了后向传播算法的推导。
不难看出,为了进行后向传播,我们首先需要计算每一层的激活函数,即,这一步与后向传播相对,通常称为前向传播,复杂度为,与网络中参数的个数相当。而后向传播的步骤,通过我们的状态转移的推导,也可以看出其复杂度为,所以总的时间复杂度为。需要注意的是,采用mini-batch的方式优化时,我们会将个样本打包进行计算。这本质上将后向传播的矩阵-向量乘积变成了矩阵-矩阵乘积。对于任意两个的矩阵的乘法,目前理论最优复杂度为的类Coppersmith–Winograd算法。这类算法由于常数巨大,不能很好利用GPU并行等限制,并没有在真正在机器学习或数值计算领域有应用。寻求智力挑战的朋友可阅读Powers of tensors and fast matrix multiplication。
这是一个有争议的问题。Hinton教授在其How to do backpropagation in a brain演讲当中,讲到了人们对于后向传播不能在生物学上实现的三个原因:
a) 神经元之间不传播实数信号,而通过尖峰信号(spikes)沟通。Hinton解释说通过Poisson过程,意味着可以传递实数信号,并且采用spike而不是实数进行信号传递是一个更鲁邦的过程。
b) 神经元不会求导()。Hinton说通过构建filter可以实现(数值)求导过程。
c) 神经元不是对称的?或者说神经元连接不是一个无向图而是有向图。这意味着通过前向传播与后向传播的并不应该是一个。Hinton教授解释说,通过一些数值实验发现,其实即便前向后向的不对称(比如让后向传播的W为固定的随机矩阵),采用类似的梯度算法也可以收敛到不错的解。
我不同意Hinton教授的观点。其解释在逻辑上混淆一个基本常识:大脑可以做到并不意味着大脑事实是这样完成运算的。其实我们已经看到,通过与非门也可以完成所有的函数运算,但这并不代表我们大脑里面一定装载了10亿个与非门。而有大量证据表明(如能耗,小样本学习),后向传播算法与真实大脑学习的机制相去甚远。所以我觉得更合理的对待,仍然是将后向传播作为一种高效计算嵌套函数梯度的数值算法工具,而不必强行将其附会成大脑的工作原理。
相信对机器学习有一定了解的朋友对正则化(Regularization)这个概念都不会陌生。可以这么说,机器学习中被讨论最广泛也可以说是最重要的一个问题,就是防止 过拟合(Overfitting) 。
为什么这么说呢?因为通过增加模型复杂性来在训练数据上达到完美的拟合实在太过容易。如果仅希望在训练数据上得到输入预期的输出,那么我们仅仅需要记住所有的训练数据就可以了。而对于机器学习,我们真正的关心的问题,是在那些没有见过的数据上,模型的表现如何,也就是模型的泛化能力。
至于正则化的方法,很多时候被大家简化理解为了“在目标函数上增加关于参数的或范数”的简单表达,但对于为何需要增加范数约束,以及增加以后所产生的效果不甚明了。
我们这里通过一个最简单的Logistic regression的例子来看看增加范数约束的必要性。对于Logistic regression问题,通过给定的个数据,其中,我们希望寻求到一个足够好的,使得尽可能接近,其中就是我们前文神经网络常用的Sigmoid函数。我们可以将Sigmoid函数的输出解释为某个输出为1的概率,那么按照最大似然原则,需要求解如下优化问题:
假设我们求得某个,能够成功让所有的正样本,而负样本输出,已经意味着我们可以通过该分类器完美对数据进行区分。但如果仔细查看我们的最优化问题可以发现,如果我们将替换成,则可以进一步降低目标函数值,我们可以通过不断放大来进一步降低目标函数值。事实上,与所表达的是同一个分类器超平面(hyperplane),可以通过限制使其收敛到唯一解。
上面的例子是数学上一类Inverse problem1的特例。这其中最简单的当属线性方程组求解的问题。当我们已知其生成过程,其中为独立同分布(i.i.d)的高斯随机噪声时,可以证明,采用范数进行优化,是在最大似然意义下的理论最优解:
类似上述能够给出具体数学描述的生成过程,及其Inverse problem最优求解方法的问题还有很多。最近哥伦比亚大学John Wright教授在字典学习问题的突破(参见Complete Dictionary Recovery over the Sphere)的结果非常漂亮,堪称是这一类Inverse problem和正则化应用的典范。
谈回深度学习,我们可以看到目前应用最广泛的两类正则化的方法,一类是通过给网络参数增加的范数(并且往往p=2):
另一类是采用由Hinton et. al所提出的Dropout方法进行正则化(参见Dropout: A Simple Way to Prevent Neural Networks from Overfitting)。并且这两类方法经常同时在训练神经网络时被采用。
这两种正则化方法在深度学习的应用,与其说是理论家的贡献,不如说是实践者的胜利。虽然通过添加范数的形式在传统凸优化中有大量可被证明正确性的应用,并且将这个想法使用于神经网络极为自然,但对于超过一层隐藏层的神经网络,范数所带来的优势还很难被严格证明。这里我提供几个非完全严格的“直观”解释:
Dropout是另一类目前被广泛使用的正则化方法,其基本思路非常简单:在训练阶段,对于每个mini-batch随机抹去一定比例的神经元进行前向及后向传播更新。这种方法在Hinton et. al的论文中所给出的解释为:
This prevents units from co-adapting too much.
This should make each hidden unit more robust and drive it towards creating useful features on its own without relying on other hidden units to correct its mistakes.
但由于深层神经网络的一大优势其实就在于对于特征的层级化表达,并且深层单元可以对浅层单元进行组合与复用。所以上述解释虽然乍听之下合理,但真实的可靠性存疑。上述论文中提到了另个解释,可能更为合理:通过控制每个单元的开关,我们潜在使用了个不同的网络(为采用Dropout的神经元数量),这样的模型相当于将个不同的共享变量的网络在预测时进行几何平均,从而达到平滑的效果。
对于一类更特定的模型,Dropout有在理论上有进一步的论证。如果我们限制所研究的为广义线性模型(Generalized linear models),并且Dropout只在输入变量上进行,那么通过对广义线性模型通过泰勒展开进行二次近似,可以得到
其中为参数的Fisher information矩阵:
那么,对于输入进行Dropout,实际上就相当于首先将输入采用进行归一化得到,然后采用传统范数对参数进行正则化,从而建立起了采用Dropout与范数进行正则化的联系。具体的推导可以参见:Dropout Training as Adaptive Regularization。
From: http://suanfazu.com/