NoneWait's Workspace.

阅读笔记

Word count: 1.6kReading time: 6 min
2019/11/25 Share

11月主要论文整理

最近刚写完毕设和实习复现论文的工作,打算浏览下近期的预训练语言模型的论文(毕设里简单介绍了下这个的思想,并且以后工作了可能会做相关的工作,所以在这学习下)。我们可以以Glue
榜单来check下最近的预训练语言模型的论文。据目前所知的情况来看,可以分为几个部分:

  • 修改bert的模型:参数量|本身框架
  • 修改预训练目标
  • 添加一些先验知识

2019-11-18

修改bert模型

  1. xlnet
  2. albert

出发点:目前的预训练模型参数量太大,降低模型参数量

贡献:设计了两种减少参数量的技术,设计了一个自监督损失来建模句内共现,有利于多句输入的下游任务

code

摘要:

  • 作者发现:训练步不够时bert模型性能不佳,增倍bert-large的hidden_size性能也不佳(why?)
  • 要设计一个模型:address the memory limitation problem, the communication overhead
  • and model degradation,也就是说既要降低参数量又要保证模型不退化,所 以设计了模型ALBERT(A Lite BERT)

模型:

  • factorized embedding parameterization:把词表的embedding matrix分解为两个子矩阵
  • cross-layer parameter sharing:随着网络加深保留参数->意思就是底层的参数给上层共享,也就是多层共享一个参数,而不是每层都是一个新参数(所有层只有一组参数?)->这边会有一个问题:虽然参数量变小了,但是其实本质上速度没有变,还是迭代了同样N次
  • SOP: sentence-order prediction:改进NSP

details:

  • 对于wordpiece embedding学习的是context-independent表示(和词向量一样,与上下文无关),
    而hidden-layer embedding则学的是context-dependent表示。所以,因为要更需要学到的是上下文相关的表示,这也是为什么H>>E的原因,原有的模型的词向量矩阵是$V \times E$。所以该方法就是先将词表映射到低维向量,再从低维升到hidden_size,则参数量则从$O(V\times H)$到$O(V \times E+ E \times H)$,当H>>E时参数量会大幅度减少。那么我们的E要设多大呢?在
    ALBERTZ中是设为128。

  • 跨层的参数共享:可以共享FFN,也可以共享attention parameters,ALBERT的选择是共享所有参数

  • Inter-sentence coherence loss:使用SOPloss来实现,正例和NSP中一样生成,而负例则用同样相邻的两个片段但是它们的顺序调换。

实验结果:还是很牛逼的,参数量降低了10倍和base版的bert效果差不多,通过增大H的大小,能够达到更好的效果。在Squad上,(xlarge(ALBERT)60M->bert_large(334M))

  • 具体实现上:(参考transformers中的albert实现):在模型部分主要要改动两个部分
    1. 改动原有的embedding layers的输入输出大小
1
2
3
4
5
6
7
8
9
class AlbertEmbeddings(nn.Module):
def __init__(self, config):
super(AlbertEmbeddings, self).__init__()
# 原始的bert是使用了config.vocab_size*config.hidden_size
# V*H->V*E
self.word_embeddings = nn.Embedding(config.vocab_size, config.embedding_size, padding_idx=0)
self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.embedding_size)
self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.embedding_size)
self.layer_norm = torch.nn.LayerNorm(config.embedding_size, eps=config.layer_norm_eps)

在transformer输入前转为hidden_size

1
2
3
4
class AlbertEncoder(nn.Module):
def __init__(self, config):
super(AlbertEncoder, self).__init__()
self.embedding_hidden_mapping_in = nn.Linear(config.embedding_size, config.hidden_size)
  1. 共享所有跨层的参数:对应self-attention+FFN迭代N次(layer numbers),现在只用一组参数
    来共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AlbertTransformer(nn.Module):
def __init__(self, config):
super(AlbertTransformer, self).__init__()

self.config = config
self.layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
self.attention = AlbertAttention(config)
self.ffn = nn.Linear(config.hidden_size, config.intermediate_size)
self.ffn_output = nn.Linear(config.intermediate_size, config.hidden_size)

def forward(self, hidden_states, attention_mask=None, head_mask=None):
for i in range(self.config.num_hidden_layers):
# 共享所有层的参数
attention_output = self.attention(hidden_states, attention_mask)[0]
ffn_output = self.ffn(attention_output)
ffn_output = gelu_new(ffn_output)
ffn_output = self.ffn_output(ffn_output)
hidden_states = self.layer_norm(ffn_output+attention_output)
return hidden_states

预训练任务 or 增强训

  1. Roberta
  2. SpanBERT
  3. T5

先验知识

  1. ernie2.0

2019-11-20

主要是bert模型压缩的问题,从alberta中我们可以
知道一部分模型压缩的方式。有几个技术分享:

美团bert模型探索

BERT 瘦身之路

All The Ways You Can Compress BERT

结合以上,主要的方法有权重分解(like 上面讲的alberta),知识蒸馏,权重分享,剪纸,混合精度(fp16之类的)。今天我们check一下知识蒸馏。

知识蒸馏:就是用到teacher+student的思想,利用student模型来拟合teacher的输出分布,
框架如图所示:
t-s框架
可以看到,主要是通过输出类别的概率来计算loss,在pytorch中可以使用KLD散度loss来等价计算软标签的交叉熵。(参考BERT瘦身之路)

1
2
loss = nn.KLDivLoss(F.log_softmax(s_logits/temperature),F.softmax(t_logits/temperature))    # temperature保存分布平滑
loss = loss * (temperature)**2 # 保存梯度量级的不变

那我们来看看DistillBERT模型。这个模型压缩了bert的40%的大小,保留了97%的性能和加速了60%的速度。在8个16GB的V100上训了90个小时,这种蒸馏的方式相当只更新student部分的参数,所以才能达到这么快的速度。
可以看hf的博客文章Smaller, faster, cheaper, lighter: Introducing DistilBERT, a distilled version of BERT
核心点在于”We are trainning the student to generalize the same way
as the teacher by matching the output distribution.”

训练损失如下:

其中$t$是从teacher model中预测的logits,而$s$则是从student中预测的。

接着引入softmax-temperature做平滑:

当T趋向0,则满足Kronecker分布,而趋向无穷大时,则成uniform分布。
在训练的时候在s和t中都设置T使得能从训练样本中学习到更多信号,而预测时则设置
T=1。

在具体实现中使用KL损失:

可以在pytorch中实现这个过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import torch
import torch.nn as nn
import torch.nn.functional as F

KD_loss = nn.KLDivLoss(reduction='batchmean')


def kd_step(teacher: nn.Module,
student: nn.Module,
temperature: float,
inputs: torch.tensor,
optimizer):
teacher.eval() # 不更新参数
student.train() # 更新参数

with torch.no_grad():
logits_t = teacher(inputs=inputs)

logits_s = student(inputs=inputs)

loss = KD_loss(input=F.log_softmax(logits_s/temperature, dim=-1),
target=F.softmax(logits_t/temperature, dim=-1))

loss.backward()
optimizer.step()
optimizer.zero_grad()

整个项目的具体实现可以参考code

混合精度加速

混合精度的训练主要流程是在模型训练过程中使用FP16加速计算过程,而
模型训练的过程中权重会存储成FP32格式,参数更新的时候则采用FP32类型。

CATALOG
  1. 1. 11月主要论文整理
  2. 2. 2019-11-18
    1. 2.1. 修改bert模型
    2. 2.2. 预训练任务 or 增强训
    3. 2.3. 先验知识
  3. 3. 2019-11-20
  4. 4. 混合精度加速