讨论 2023-12-14
=
## 神经网络基础(三)
## 浅谈Deep Learning中的深度
本次分享主要讨论深度学习中的"深度"是什么意思?为什么需要深度,以及深度带来了哪些挑战。
### 什么是 "Deep"
深度学习之前就早有神经网络的概念,实际上,神经网络之前就有很多机器学习的方法讨论了如何构造复杂函数。简单来说,经典研究常常从两个角度思考问题,一是函数拟合,即认为任何任务的输入输出可以用某种函数映射表达,即$$Y = f(X), \quad Y,X \in R^{d} $$
当然,这里其实还涉及很多概率论的知识,因为很多数据本身就是以分布形式存在,并且这种映射关系有时也不是确定性的(函数的定义让其对同一个输入只能有一个输出,但引入概率密度函数就可以规避这一限制)。对于一个函数的复杂性,我们可以通过其阶数,项数,连续性,高阶导,条件数等性质加以描述。例如高次多项式显然比直线复杂。
第二类方法则是从区分样本出发,他们认为如果能找到一种方法把正确的输出和其他输出区分开,就能得到结果。例如SVM,决策树,聚类算法都体现了这种思想。同样的,区分样本也有难易之分,通常可以通过研究其VC维(指定函数族可以二元区分的最大样本数量,即对于某个数目的一组样本样本,不论他们的二元标签如何分布,总能找到一种分割方式),混淆矩阵(二元关系)的特征值等。
### 为什么需要深度
神经网络实际就是一种通过组合嵌套基本模块来组成复杂的函数的方法,而其深度就体现了所构造函数的复杂性。神经网络与其他方法的一个重要区别是它的每个模块是非常简单的,所以深度(也就是嵌套的层次)对它的作用是巨大的。如果每个模块不简单,比如一个模块是复杂的分段函数,或是复杂的非线性函数,那么一两个模块就能表达极其复杂的函数。也就没必要讨论深度了。
所以,我们同时可以知道,深度本身没有绝对含义,不同模块搭建的网络层数不是直接可比的,比如5层MLP+relu和5层attention+tanh,它们就不好直接比较。只有当组成元件高度相似时,深度才是有比较价值的。
### 深度为什么是瓶颈
一个直观的问题是,我们为什么要把复杂问题拆分成一堆基本模块的叠加?这就涉及到了参数估计和优化问题,参数越多越难估计,需要的数据也更多,同样的,在参数量相等时,模块越复杂,优化难度也越高。如果这些条件都一致,那么结构和优化顺序同样会带来影响,通常认为输出的天然产生过程是最容易估计的路线。即如果要完全了解一个事件,了解它是如何产生是最容易的路径。
简单来说,我们遇到了这样一串问题,目标任务需要非常复杂的函数来模拟或求解,而非常复杂的函数又往往需要大量的参数和复杂的函数形式,神经网络将其分解成简单模块的叠加,但叠加的深度过高同样难以处理。所以深度学习初期主要是讨论如何解决叠加很深的神经网络的参数估计问题。
此外,我们可以通过一个简单的例子来理解深度带来的麻烦。
如果$f(x)=Wx$,那么$f'(x) = W$,同理,在层数增加的时候,由于链式法则,我们知道网络的导数也是层次化的,并且深度和网络深度一样。那一个信号被经过一连串的缩放,很容带来两个结果,一是过大,一是过小。例如即便是一个稍大于对的1的数字在几十次方后也会很大,一个略小于1的数字在几十次方后也会很小。所以深度增加后遇到梯度爆炸和梯度消失的可能性会越来越高。
### 过去的解决方法
#### 无监督和Autoencoder
解决深度带来问题有多种思路,其中一种是减少优化到目标参数的路径长度,即我们先让模型参数处于一个良好的初始点,再让其走一小步到达目标参数。而什么是一个良好的初始点呢?往往是由无监督任务决定的,本质上现在利用MLM做预训练也是相同的思路,不过Autoencoder的基础是信息瓶颈理论,即一个样本的最重要的重构特征,往往是具有含义的,也是具有代表性的。
#### 卷积网络/循环网络
除了找到良好的起始点,另一种思路是缩减优化的可能性,即减少解空间。卷积神经网络,和现在众多以inductive bias为卖点的工作都是出于这类目的。
### 现在的方法
#### 残差连接
我们上面提到了深度指的是简单模块嵌套的层数,如果嵌套很少,优化就更加容易,但表达能力也会变弱。而残差连接便是一种平衡的策略,我们认为目标任务并不是一个完全复杂的任务,它更可能是由一个相对简单的主体和较为复杂的分支合并组成的。这其实和28定律有一定的呼应,所以残差提供了一种难易度的分工,并且让网络很容易退化为更简单的形式。
#### 预训练
不论是有监督的,还是无监督的,预训练本质还是尽可能的让网络参数处于一个有利于下游任务的起始点。
#### 激活值的模长/分布控制
除了不同层参数的连乘,很多非线性激活函数的导数是带有激活值的,比如
\begin{gather}
y = tanh(x) \quad y'= 1-y^2 \\
y = sigmoid(x) \quad y' = y(1-y)
\end{gather}
那么对于模长和激活值的控制就显得尤为重要了,把他们控制在1附近是更有利于优化层数很深的网络的。
### 优化路径的不可逆转性
初始点不只影响了优化的路径,很多时候也决定了优化的结果。我们的优化方法大多只能找到局部极值而非全局最优。优化的路径也是如此,它是有后效性的,即第一步走错,很可能是后续无数步无法修正的。因为梯度下降几乎是不会倒退的。所以优化过程本身存在着一定的不可逆性。
## RNN 循环神经网
假设有一个序列$X=x_0, x_1, x_2, ..., x_n$,与HMM类似,RNN通过递推的方式来建模这个序列,$$ h_t = \mathrm{tanh}(Wx_t+U h_{t-1}) $$
这种方式的好处是同一个模型可以应对任意长度的序列。同时RNN也有很多性质,而其中不乏双刃剑,比如RNN建模序列式可以考虑相对次序,输出与$t$无关。这在很多场景下是优势,比如类周期性的信号,没有明确的开始与结束。但同时有时确实劣势,因为与$t$无关也就意味着RNN不能感知绝对位置。但假如了解RNN的特性,这一缺点完全可以规避。\<BOS\> \<EOS\>就可以解决这一问题。
在对RNN的研究中,最具代表性的就是长时记忆问题,也就是探讨RNN记忆力如何的问题。实际上,在应用RNN的过程中,许多人都发现了RNN不能记住多步之前的信息(通常指几十步以上)。对此主流的解释是梯度弥散和梯度爆炸问题
\begin{align}
\frac{\partial h_t}{\partial h_{t-1}} &= (1-h_t^2)U \\
\frac{\partial h_t}{\partial h_{t-k}} &= \prod_{i=t-k+1}^t (1-h_i^2) U^k
\end{align}
我们可以很容易发现,k步间的导数传递十分不问题,很容易快速增大或快速减小。也就带来了梯度爆炸和梯度弥散问题。另外这种现象同样存在于深层网络中,因为相应的项从$U^k$变为$\prod_i U_i$同样很不稳定。同时根据这一推导我们也可以理解为甚RNN难以记住几十步以前的信息。并且我们发现只有$U$接近于1,RNN才可能稳定这一思想也影响了后续众多对RNN的改进工作。
### 正交初始化
假如矩阵$A$满足$A^TA=AA^T=I$,则A为正交阵,同时正交阵有一个重要性质,即模长不变
\begin{align}
||x||_2 &= x^T x \\
||Ax||_2 & = (Ax)^T Ax \\
& = x^T A^T A x \\
& = x^T x \\
& = ||x||_2 \\
\end{align}
知道了经过正交矩阵不会改变模长,我们就想到如果 $U$是一个正交矩阵,那么长距离梯度传递问题是不是就得到很好解决?
受这种想法启发,现在很多RNN的实现都会把 $U$初始化为一个正交阵。但遗憾的是在优化过程中很难保持正交性质,把$U$重参数化,让其强制为正交阵又会使网络能力大大下降。
### 激活函数的选择
我们已经知道了tanh的导数,并且发现它在0~1之间的,这不利于长距离梯度传递。所以也有一些工作探讨了是否有其他更适合RNN的激活函数,比如使用ReLU替代tanh,我们知道ReLU在正数部分的导数就是1,所以很有利于长距离传递。一些研究也表明这种改进在一些情况下确实提高了RNN的记忆能力,但我的个人经验是这种结构非常脆弱,很难调。(大家可以思考一下为什么比tanh难调)
### 集大成者 LSTM
LSTM既考虑了长距离梯度传递问题,又兼顾了网络的表达能力,看似复杂,其实考虑周全。
\begin{align}
i_t &= \sigma(Wx_t+Uh_{t-1}) \\
o_t &= \sigma(Wx_t+Uh_{t-1}) \\
f_t &= \sigma(Wx_t+Uh_{t-1}) \\
\hat{h}_t &= \mathrm{tanh}(Wx_t+Uh_{t-1}) \\
c_t &= f_t * c_{t-1} + i_t * \hat{h}_t \\
h_t &= o_t * \mathrm{tanh} (c_t)
\end{align}
注意每一行的$W$和$U$都是不同参数。 我们可以发现$c_t$是一条记忆的通路,与$U$无关,只收gate的控制。假如gate常开,LSTM可以允许长距离的梯度传输。 但也要注意,$f_t$ 是sigmoid函数出来的,所以初值不会太接近1,而指数衰减速度很快,所以想要学习长距离关系,一般要把$f_t$相应的参数单独初始化,让其初始值就接近1。
### RNN的层级结构
层级结构是一个在RNN及序列分析中很热门的一个词,但我们需要首先明确有两种层级结构,一是指数据本身的层级结构,如字符到单词,单词到词组,以及从句等。而另一种层级结构指的是模型本身的层级结构,可以简单理解为神经网络的层数。
绝大多数相关工作都是在探讨数据的层次和模型的层次之间的联系。一种自然的想法是浅层网络(近输入)对应低级数据结构,深层网络(近输出)对应高级数据结构。不少工作都验证了这种直觉,但也有一些反例。不过如果我们深入思考,会发现不论实际结果是否符合直觉,其背后的逻辑与我们通常所想并不相同。
实际上,网络每层的行为规律和它与输入和输出的距离更为相关。例如如果输入是一个主题,输出是一段相应的故事,那么深浅关系就反过来了。以此类推,我们既要考虑解决问题所需的深度,也要考虑网络本身的连续性。

这篇文章是一个典型的认为不同网络层功能有差异,并且有互补性的工作,这种观点在当时比较流行,认为这种Top-down的信息是对浅层有帮助的。
https://arxiv.org/pdf/1502.02367.pdf

这篇文章是做层次化结构比较清楚的一篇文章,以可解释性为第一目标,虽然性能未必有很大提升,但确实捕捉到了数据本身的层次特征。换言之,算是一个比较明确指出网络层如何与数据含有的层次对应的工作。论文见图
https://arxiv.org/pdf/1609.01704.pdf
### 超越序列的RNN
我们了解了RNN是如何处理序列(一维数据的),那么一个自然而然的问题便是RNN能否处理多维数据。解法也比较多,一种是逐个遍历各个维度,另一种是把多维数据当做一维数据处理,例如通过定序把二维图像变成一维序列。而Grid LSTM是一个比较有趣的工作,它把网络的深度也当成一个深度,这样原本的一维序列也变成了二维,一维的数据加上一维的网络深度。

https://arxiv.org/pdf/1801.00887.pdf
Grid LSTM 把多维问题进行了抽象,把它作为一个多输入问题,即一个LSTM单元会同时接受多个输入,对应不同的维度。而整个状态转移过程会同时兼顾多个维度的信息。
\begin{align}
H = [x_t^1, x_t^2,\cdots,x_t^K,h_t^1,\cdots,h_t^K] \\
H'_1 = LSTM_1(m_1,H) \\
H'_K = LSTM_K(m_K,H)
\end{align}
https://arxiv.org/pdf/1507.01526.pdf
### 外部记忆
在上一次讨论中,我们重点讲述了RNN的长时记忆问题,作为RNN最显著的缺点,围绕其展开的研究也众多。而运用外部记忆是其中非常重要的一种思路。
简而言之,既然RNN难以记住很久以前的信息,我们便在RNN之外添加一个记忆模块。比起改造RNN本身,这种方法更偏工程,但很实际,我们所处的NLP领域中也是以应用研究为主,所以解决问题比方法优美更加重要。
大部分使用外部记忆的模型都可以抽象为三部分,控制器(通常是一个RNN),读取内存,写入内存。

首先,内存(Memory)是一个固定大小的矩阵(N $\times$ M),而读取和写入的单位都是一个向量,所以我们要考虑从什么地方读,写什么和写在哪的问题。我们以NTM为例,把这些过程类比到内存寻址问题,即内存由key value pair组成,我们通过比对key来找的最后的位置,然后读取它的value作为结果。
\begin{align}
r_t = \sum_i w_t(i)M_t(i)
\end{align}
$w_t$是一个分布,我们再看如何写入
\begin{align}
M_t(i) = M_{t-1}(i)[1-w_t(i)e_t] + w_t(i)a_t
\end{align}
$e_t$控制擦除那些维度,$a_t$是要写入的新内容
我们发现读写最重要的部分就是分布$w$,我们也可以用Attention的思想理解它,它就是对每个位置Attention的系数。而求这个Attention的方法和我们现在常用的有一定区别。
大致可以分为两种思路,很多模型都同时使用了这两种思路,一是内容索引,二是位置索引,顾名思义,内容索引指只根据内容决定读写的位置,不考虑所在位置。即同一个向量放在内存中不同位置是等效的,而位置索引相反,只考虑所在位置,不考虑内容,比如存放在第一个位置的向量就应该如何如何而不考虑它的具体内容。

具体方法比较繁琐,但不难,原paper写的比较清楚
https://arxiv.org/pdf/1410.5401.pdf

### RNN中的Attention
Attention最早出现并不是我们现在常用的self-attention,当时通常是把attention作为一种信息选取的工具,类似于NTM的读取机制。
除了在encoder-decoder模型中大量使用外,attention还有很多其他用法,比如在RNN的每一步都对历史信息进行attention。
当然我们在这里更为关注的是attention的形式问题,
\begin{align}
\alpha_i &= V^T \mathrm{tanh}(WQ+UK_i) \\
\alpha_i &= MLP([Q;K_i])
\end{align}
早期的Attention大部分使用加法,或者拼接(实际是一种)的方式。其实和我们的现在常用的点积形式并没有太多性能差别。但如果拓展到self-attention,效率就比较底了。
从这里也可以看出,Attention系列的研究动机比RNN时代要简略,很多工作更倾向于通过实验从多种设计中选择一种,而不是探讨不同设计的特性。
### Attention的含义演变
如我们以上所示,attention的含义开始是从多个候选项中选择一个,而之后一些工作提出,这种选择是否也应该考虑特征的维度,即不同维度是否可以套用不同的attention。而其代表工作就是多维attention,每个维度都有不同的attention。这种方法确实显著提高了attention的表达能力,但物理含义和效率都不佳,人们又慢慢转向了多头机制(multi-head)。其实多头机制很早就有了,在NTM中就有应用,但过了几年,才最终形成我们现在常用的 multi-head dot-product self-attention。其实现在的做法未必最优,只是一个权衡了多方面因素的暂时解决方案。
图太长就不贴上来了,[这篇工作](https://arxiv.org/pdf/1709.04696.pdf)对self-attention的探索很有意义
Attention的具体含义其实并不清晰,有人解释为多选一,有人解释为动态卷积,有人解释为GNN。
但有一些基本的概念,即Attention和CNN和RNN不同,在关系建模上与输入内容相关,换言之是对不同输入是动态处理的。这里的“动态”和“静态”指的是直接影响,因为从参数角度,所有网络都是“静态”的。另外需要注意的是attention并不一定是bilinear形式的。
\begin{align}
\alpha = x^T W y
\end{align}