讨论 2020-10-22 = ###### tags: `tutorials` `Fudan` `2020` # NLP中的常用模型 (三) ### RNN的层级结构 层级结构是一个在RNN及序列分析中很热门的一个词,但我们需要首先明确有两种层级结构,一是指数据本身的层级结构,如字符到单词,单词到词组,以及从句等。而另一种层级结构指的是模型本身的层级结构,可以简单理解为神经网络的层数。 绝大多数相关工作都是在探讨数据的层次和模型的层次之间的联系。一种自然的想法是浅层网络(近输入)对应低级数据结构,深层网络(近输出)对应高级数据结构。不少工作都验证了这种直觉,但也有一些反例。不过如果我们深入思考,会发现不论实际结果是否符合直觉,其背后的逻辑与我们通常所想并不相同。 实际上,网络每层的行为规律和它与输入和输出的距离更为相关。例如如果输入是一个主题,输出是一段相应的故事,那么深浅关系就反过来了。以此类推,我们既要考虑解决问题所需的深度,也要考虑网络本身的连续性。 ![](https://i.imgur.com/e0zzxHq.png) 这篇文章是一个典型的认为不同网络层功能有差异,并且有互补性的工作,这种观点在当时比较流行,认为这种Top-down的信息是对浅层有帮助的。 https://arxiv.org/pdf/1502.02367.pdf ![](https://i.imgur.com/lwHpVv8.png) 这篇文章是做层次化结构比较清楚的一篇文章,以可解释性为第一目标,虽然性能未必有很大提升,但确实捕捉到了数据本身的层次特征。换言之,算是一个比较明确指出网络层如何与数据含有的层次对应的工作。论文见图 https://arxiv.org/pdf/1609.01704.pdf ### 超越序列的RNN 我们了解了RNN是如何处理序列(一维数据的),那么一个自然而然的问题便是RNN能否处理多维数据。解法也比较多,一种是逐个遍历各个维度,另一种是把多维数据当做一维数据处理,例如通过定序把二维图像变成一维序列。而Grid LSTM是一个比较有趣的工作,它把网络的深度也当成一个深度,这样原本的一维序列也变成了二维,一维的数据加上一维的网络深度。 ![](https://i.imgur.com/enWKKUZ.png) 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 ### Transformer #### 什么是Transformer(广义) 由多层self-attention层叠加而成的模型,可以搭配全连接层,卷积层。attention机制也可以从多种实现中挑选任意一种。 ![](https://i.imgur.com/vnMGY4L.png) #### 什么是Transformer(狭义) Transformer远不止一个模型,更提出了相应的训练框架,优化方法。 ![](https://i.imgur.com/E4Gg1eB.png) #### 动机和实现的差异 Transformer主要有两个优势,一是速度快,并行度高,二是可以轻易访问相距很远的节点,解决了CNN和RNN常提到的local,non-local问题。可反观Transformer的设计,其中不乏很多“多余”之举。下面通过几个小例子来看看这些设计会否真的“多余”。 ```python= import torch import torch.nn as nn import tqdm import numpy as np BATCH_SIZE = 16 D_MODEL = 256 N_HEAD = 4 N_LAYER = 6 IN_LEN = 10 IN_DIM = 50 class Trans(nn.Module): def __init__(self): super(Trans, self).__init__() self.trans = nn.TransformerEncoder(nn.TransformerEncoderLayer(D_MODEL, N_HEAD), N_LAYER) self.in_fc = nn.Linear(IN_DIM*2, D_MODEL) self.out_fc = nn.Linear(D_MODEL, IN_DIM) def forward(self, x): h = self.in_fc(x) h = self.trans(h) y = self.out_fc(h) return y def run(model, test=False): model.train() losses = [] lr = 1e-3 optimizer = torch.optim.Adam(model.parameters(), lr=lr) with tqdm.trange(1000) as tq: for i,_ in enumerate(tq): a = torch.randn((BATCH_SIZE, IN_LEN, IN_DIM)).cuda() b = torch.randn((BATCH_SIZE, IN_LEN, IN_DIM)).cuda() tar = (a+1)**2 + b y = model(torch.cat([a,b], -1)) loss = (0.5*(y-tar)**2).mean() model.zero_grad() if not test: loss.backward() optimizer.step() tq.set_postfix({'loss':loss.item()}) losses.append(loss.item()) if test: print('AVG Loss', np.mean(losses)) if __name__ == '__main__': model = Trans() model.cuda() run(model) run(model, True) ``` |Setting|MSE| |-|-| |lr=1e-3, no warm-up| 3.37| |lr=1e-4, no warm-up| 0.56| |lr=1e-5, no warm-up| 2.31| |lr=1e-3, 1% warm-up| 0.10| |Setting|MSE| |-|-| |default| 0.097| |xavier_uniform| 0.191| |xavier_normal| 0.189| |normal 0.02| 0.66| |normal 0.002| 2.95| |normal 0.1| 3.50| |uniform +-0.05| 3.50| |uniform +-0.01| 0.424| |uniform +-0.001| 3.44| |kaiming_uniform| 3.50| |kaiming_normal| 3.50| |Setting|MSE| |-|-| |kaiming_normal 2 layer| 0.524| |kaiming_uniform 2 layer| 0.531| |xavier_uniform 2 layer| 0.124| |default 2 layer| 0.092| |uniform +-0.01 2 layer| 0.117| |Setting|MSE| |-|-| |lr=1e-3, no warm-up 2 layer| 0.086| |lr=1e-4, no warm-up 2 layer | 0.614| |Setting|MSE| |-|-| |lr=1e-3, no warm-up batch_size 64| 0.037| ### 为什么Transformer不好训练 - 初值问题,Transformer初始倾向于bag of words,CNN倾向于N-gram,RNN倾向于moving average - 太自由,Transformer的解空间(严格说是比较探索到的解空间)比RNN和CNN要大,范围越大,搜索难度越高。 ## 预告: # 经典模型及算法导览 (一) ### Variational Auto Encoder(VAE)