1. BERT简介
BERT全称,Bidirectional Encoder Representation from Transformers,双向Transformers编码表示.
特点:
(1)证明了双向预训练对语言表示的重要性。与之前使用的单向语言模型进行预训练不同,BERT使用遮蔽语言模型来实现预训练的深度双向表示。
(2)论文表明,预先训练的表示免去了许多工程任务需要针对特定任务修改体系架构的需求。 BERT是第一个基于微调的表示模型,它在大量的句子级和token级任务上实现了最先进的性能,强于许多面向特定任务体系架构的系统。
BERT是预训练模型,能够显著提高下游任务效果,不使用label样本。
成功的原因在于,
1. 代理任务的积累
从简单的CBOW, skipgram,最终到 Masked-LM,nsp。
2. 深层网络
越是深层、大容量的网络,越能抽取高层的特征、存储更丰富的知识,Resnet,各种 norm技术,SBBB结构( Shortcut 、BottleNeck、 Branches, BatchNorm)使得深层网络搭建和训练简单。
3. Attention
Attention注意力机制使得网络有专门的memory,以及各种基于 memory access 的功能: 对齐,组合,远程依赖,全局视野,极大丰富模型的表示能力。这在序列数据尤为重要。
4.CNN流崛起
相比RNN, CNN不是为序列数据设计,但容易并行,对工业应用友好,易堆叠,适合生成模型,欠缺的全局视野可以给Attention来完成。本质上,Transformer属于CNN流,宽度为1 的卷积核加上 self-Attention。
最近的convs2s, ByteNet, WaveNet, SliceNet, Transformer 等CNN工业级框架更加流行。
2. BERT发展历史
2.1 预训练在图像的应用
预训练好处:
(1)训练数据小,不足以训练复杂网络;
(3)加快训练速度;
(3)参数初始化,利于优化。
Q1: 为什么能预训练?
底层特征的可复用性
Q2:为什么要fine-tuning?
高层特征任务相关性。
Q3:预训练应用场景?
预测;特征提取;微调。
语言模型
神经网络语言模型NNLM; Word2vec,Word Embedding,ELMO.
2.2 从Word Embedding到ELMO:
Word Embedding无法区分多义词的不同语义,这就是它的一个比较严重的问题。
Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了。
2.3 ELMO: Embedding from language Models:
ELMO的本质思想是:事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分。在实际使用Word Embedding的时候,根据上下文单词的语义去调整单词的Word Embedding表示。
所以ELMO本身是个根据当前上下文对Word Embedding动态调整的思路。
ELMO 训练好后给下游任务:
2.4 从WE到GPT:
ELMO的缺点:
1、LSTM抽取能力远弱于Transformer;
2、拼接方式双向融合特征能力偏弱。
GPT是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。
GPT也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过Fine-tuning的模式解决下游任务。
上图展示了GPT的预训练过程,和ELMO是类似的,主要不同在于两点:
首先,特征抽取器不是用的RNN,而是用的Transformer;
其次,GPT的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型。
2.5 Bert的诞生:
Bert采用和GPT完全相同的两阶段模型,首先是语言模型预训练;其次是使用Fine-Tuning模式解决下游任务。
和GPT的最主要不同在于在预训练阶段采用了类似ELMO的双向语言模型,另外一点是语言模型的数据规模要比GPT大。
Q1: Bert应用场景
A1:句子关系类任务;单句分类任务;阅读理解任务;序列标注类任务。
Q2:如何构造双向语言模型?
A2:
构建任务一:
“Masked LM”: 随机选取15%单词。然后80%概率用 [MASK] 代替;10%概率代替为随机单词;10%概率保持不变。之后让模型根据所给的标签去学习这些地方该填的词。
构建任务二:
“Next Sentence Prediction”,指的是做语言模型预训练的时候,分两种情况选择两个句子,一种是选择语料中真正顺序相连的两个句子;另外一种是第二个句子从语料库中抛色子,随机选择一个拼到第一个句子后面。判断两个句子是否是文章的前后句。
之所以添加Masked 和 Next sentence prediction任务,是考虑到很多NLP任务是句子关系判断任务,单词预测粒度的训练到不了句子关系这个层级,增加这个任务有助于下游句子关系判断任务。所以可以看到,它的预训练是个多任务过程。这也是Bert的一个创新。
Q3 : 输入和输出处理?
输入部分是个线性序列,两个句子通过分隔符分割,最前面和最后增加两个标识符号。
每个单词有三个embedding:位置信息embedding,这是因为NLP中单词顺序是很重要的特征,需要在这里对位置信息进行编码;单词embedding,这个就是我们之前一直提到的单词embedding;第三个是句子embedding,因为前面提到训练数据都是由两个句子构成的,那么每个句子有个句子整体的embedding项对应给每个单词。把单词对应的三个embedding叠加,就形成了Bert的输入。
Masked LM 输出代码:
def get_masked_lm_output(bert_config, input_tensor, output_weights, positions,
label_ids, label_weights):
"""Get loss and log probs for the masked LM."""
# 获取mask词的encode
input_tensor = gather_indexes(input_tensor, positions)
with tf.variable_scope("cls/predictions"):
# 在输出之前添加一个非线性变换,只在预训练阶段起作用
with tf.variable_scope("transform"):
input_tensor = tf.layers.dense(
input_tensor,
units=bert_config.hidden_size,
activation=modeling.get_activation(bert_config.hidden_act),
kernel_initializer=modeling.create_initializer(
bert_config.initializer_range))
input_tensor = modeling.layer_norm(input_tensor)
# output_weights是和传入的word embedding一样的
# 这里再添加一个bias
output_bias = tf.get_variable(
"output_bias",
shape=[bert_config.vocab_size],
initializer=tf.zeros_initializer())
logits = tf.matmul(input_tensor, output_weights, transpose_b=True)
logits = tf.nn.bias_add(logits, output_bias)
log_probs = tf.nn.log_softmax(logits, axis=-1)
# label_ids表示mask掉的Token的id
label_ids = tf.reshape(label_ids, [-1])
label_weights = tf.reshape(label_weights, [-1])
one_hot_labels = tf.one_hot(
label_ids, depth=bert_config.vocab_size, dtype=tf.float32)
# 但是由于实际MASK的可能不到20,比如只MASK18,那么label_ids有2个0(padding)
# 而label_weights=[1, 1, ...., 0, 0],说明后面两个label_id是padding的,计算loss要去掉。
per_example_loss = -tf.reduce_sum(log_probs * one_hot_labels, axis=[-1])
numerator = tf.reduce_sum(label_weights * per_example_loss)
denominator = tf.reduce_sum(label_weights) + 1e-5
loss = numerator / denominator
return (loss, per_example_loss, log_probs)
Q4: Bert的局限性?
BERT 在第一个预训练阶段,假设句子中多个单词被 Mask 掉,这些被 Mask 掉的单词之间没有任何关系,是条件独立的,然而有时候这些单词之间是有关系的.
BERT 在分词后做[MASK]会产生的一个问题,为了解决 OOV 的问题,我们通常会把一个词切分成更细粒度的 WordPiece。
3. 总结
Q1 : ELMO和BERT在微调阶段使用的区别
在ELMO中,一个单词会得到多个embedding,对不同的embedding进行加权求和,可以得到最后的embedding用于下游任务。要说明一个这里的embedding个数,下图中只画了两层RNN输出的hidden state,其实输入到RNN的原始embedding也是需要的,所以你会看到说右下角的图片中,包含了三个embedding。
但不同的权重是基于下游任务学习出来的,上图中右下角给了5个不同的任务,其得到的embedding权重各不相同。
Bert模型:如果是中文的话,可以把字作为单位,而不是词。
在ELMO中,训练好的embedding是不会参与下游训练的,下游任务会训练不同embedding对应的权重。
但Bert是和下游任务一起训练的:
如果是分类任务,在句子前面加一个标志,将其经过Bert得到的embedding输出到二分类模型中,得到分类结果。二分类模型从头开始学,而Bert在预训练的基础上进行微调(fine-tuning)。
Q2:BERT的mask和Transformer的mask的区别
Transformer的Mask是用一个矩阵,不允许 t 时刻看到之后的内容;
BERT的mask是随机的。
Q3: Bert模型细节
BERTBASE (L=12, H=768, A=12, Total Param-
eters=110M) and BERTLARGE (L=24, H=1024,
A=16, Total Parameters=340M)
BERT的Transformer使用了双向的 self Attention。
输入使用 WordPiece embeddings 。
参考: