讨论 2020-10-22
=
###### tags: `tutorials` `Fudan` `2020`
# NLP中的常用模型 (三)
### 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
### Transformer
#### 什么是Transformer(广义)
由多层self-attention层叠加而成的模型,可以搭配全连接层,卷积层。attention机制也可以从多种实现中挑选任意一种。

#### 什么是Transformer(狭义)
Transformer远不止一个模型,更提出了相应的训练框架,优化方法。

#### 动机和实现的差异
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)