ALBERT: 轻量级的BERT

ALBERT

前言

当前的趋势是预训练模型越大,效果越好,但是受限算力,需要对模型进行瘦身。这里的ALBERT字如其名(A lite BERT),就是为了给BERT瘦身,减少模型参数,降低内存占用和训练时间(待思考)。

embedding参数因式分解

将大的语料矩阵分解成两个小的矩阵。减少模型宽度。
我们先看看BERT这一块是如何操作的。
首先,我们对序列中每一个token,有一个wordpieceid+segment id + position id,最终返回一个合起来的token id
ALBERT: 轻量级的BERT
后续我们需要基于token id生产一个embedding vector,进入transformer结构。BERT的方法是将token id进行one_hotting化(向量长度为词库长度V,bert里是30000),然后将one_hotting直接用look_up查找embedding表(V*H,这里H是设定的embedding vector长度)。
具体操作参考google bert代码

def embedding_lookup(input_ids,
                     vocab_size,
                     embedding_size=128,
                     initializer_range=0.02,
                     word_embedding_name="word_embeddings",
                     use_one_hot_embeddings=False):
  """Looks up words embeddings for id tensor.
  Args:
    input_ids: int32 Tensor of shape [batch_size, seq_length] containing word
      ids.
    vocab_size: int. Size of the embedding vocabulary.
    embedding_size: int. Width of the word embeddings.
    initializer_range: float. Embedding initialization range.
    word_embedding_name: string. Name of the embedding table.
    use_one_hot_embeddings: bool. If True, use one-hot method for word
      embeddings. If False, use `tf.gather()`.
  Returns:
    float Tensor of shape [batch_size, seq_length, embedding_size].
  """
  # This function assumes that the input is of shape [batch_size, seq_length,
  # num_inputs].
  #
  # If the input is a 2D tensor of shape [batch_size, seq_length], we
  # reshape to [batch_size, seq_length, 1].
  if input_ids.shape.ndims == 2:
    input_ids = tf.expand_dims(input_ids, axis=[-1])

  embedding_table = tf.get_variable(
      name=word_embedding_name,
      shape=[vocab_size, embedding_size],
      initializer=create_initializer(initializer_range))

  flat_input_ids = tf.reshape(input_ids, [-1])
  if use_one_hot_embeddings:
    one_hot_input_ids = tf.one_hot(flat_input_ids, depth=vocab_size)
    output = tf.matmul(one_hot_input_ids, embedding_table)
  else:
    output = tf.gather(embedding_table, flat_input_ids)

  input_shape = get_shape_list(input_ids)

  output = tf.reshape(output,
                      input_shape[0:-1] + [input_shape[-1] * embedding_size])
  return (output, embedding_table)

注意,在bert config里,embedding_size = H = 768
当然这个embedding表需要后期训练得到,这里的计算复杂度非常高 O ( H ∗ V ) O(H*V) O(H∗V),考虑到V是词库大小的化。
Albert的改进是,考虑wordpiece里词向量是上下文无关的,而Bert词向量结果是有关的,那么后者信息含量要高于前者,所以作者认为embeding_size < H。
为此,作者添加了一个中间环节,先将one_hotting的wordpiece向量转化为一个E维向量( O ( V ∗ E ) O(V*E) O(V∗E)),然后将这个E维向量转化为H维向量( O ( V ∗ H ) O(V*H) O(V∗H)),所以时间复杂度计为 O ( V ∗ E ) + O ( E ∗ H ) O(V*E)+O(E*H) O(V∗E)+O(E∗H))
训练也是需要训练这两个矩阵。
单纯看结果,参数因式分解有效果,但是不是主要原因
通过实验发现embedding size=128效果最好
ALBERT: 轻量级的BERT

参数共享

采用全层参数共享(FFN layer + Attention Layer)减少模型深度。
比较四种共享方法:不共享(Bert-type),只共享Attention,只共享FFN ,全共享(Albert-type)。
从实验结果来看,全共享形式对模型效果有明显下降,下降原因主要来源共享FNN层,而共享Attention层不会影响模型效果(至少看起来不明显),但是对模型压缩效果有限。
ALBERT: 轻量级的BERT
但是最终模型效果中与BERT_large比较的ALBERT是基于BERT_xxlarge压缩的,虽然参数相比BERT更少。但不足以证明参数共享那么高效(压缩是有损的)

SOP instead of NSP

不用NSP是因为任务难度不够(与MLM相比),NSP会混淆主题预测和语序连贯性预测
SOP实际上正样本是BERT同上下文,负样本是乱序上下文,避免了主题预测,只关注语言连贯性。
同时发现,SOP可以解决NSP,但是NSP解决不了SOP
ALBERT: 轻量级的BERT

结论

这里同上,采用ALBERT_xxlarge是L=12, H=4096。相比BERT_large,这是一个宽而浅的模型,参数量大约为前者70%。
ALBERT: 轻量级的BERT

其他压缩方法

参见:BERT压缩

论文来源:Lan, Z. , Chen, M. , Goodman, S. , Gimpel, K. , Sharma, P. , & Soricut, R. . (2019). Albert: a lite bert for self-supervised learning of language representations.

上一篇:ConstraintLayout2.0之MotionEffect,简单的代码实现炫酷的动效!


下一篇:写一篇最好懂的HTTPS讲解