ALBERT 论文+代码笔记

2023-11-10 05:10
文章标签 代码 笔记 论文 albert

本文主要是介绍ALBERT 论文+代码笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Paper:1909.11942.pdf

Code:google-research/albert: ALBERT: A Lite BERT for Self-supervised Learning of Language Representations

核心思想:基于 Bert 的改进版本:分解 Embedding 参数、层间参数共享、SOP 替代 NSP。

What

动机和核心问题

大模型有好表现,但因为内存限制和训练时间太久,导致模型无法继续变大。本文提出两种参数裁剪的方法来降低 Bert 的内存消耗并增加训练速度。

关于内存已有的一些解决方案:模型并行、更聪明的内存管理。ALBERT 结合了两种技术同时解决了内存和训练时长的问题:

  • 分解 Embedding 的参数

  • 跨层参数共享

还有个增益是可以充当正则化的形式,从而稳定训练并有助于泛化。

模型和算法

对 Bert 模型进行了三个方面调整:

  • 分解 Embedding 参数:WordPiece Embedding 学习的是 context-independent 表示;hidden-layer Embedding 学习的是 context-dependent 表示。前者 Size 取小点就可以缩小参数规模,因此本文将 Embedding 的参数分解为两个较小的矩阵。即首先将 One-hot 投影到尺寸为 E(128) 的较低维嵌入空间中,然后再将其投影到隐藏空间中。参数规模从 O(V × H) 减小到 O(V × E + E × H)。

  • 跨层共享:共享了层间的所有参数。这里作者对比了 Bert 和 ALBERT 层输入和输出的相似度,发现 ALBERT 的结果更加平滑,说明权重共享对稳定网络参数有影响。另外相似度的结果是振荡的,不是像 DQEs(见《相关工作》)所说的达到了平衡点(对于该平衡点,特定层的输入和输出嵌入保持不变)。

  • 句子连贯性损失函数:Bert 的 NSP(Next Sentence Prediction) 被发现不可靠,本文作者猜测任务难度相比 MLM 来说太小,其实它可以看作一个任务做了主题预测和连贯性预测,但主题预测很容易,而且和 MLM 有重叠。因此本文提出了 SOP(Sentence-order Prediction),聚焦在句子连贯的建模上,具体做法是:Positive 和 Bert 一样,来自同一个文档的两个连续片段;Negative 用的还是这两个片段,只不过交换了一下顺序。事实证明 NSP 根本无法解决 SOP 任务(即,它最终学习了更容易的主题预测信号,并在 SOP 任务上以随机基线水平执行),而 SOP 可以将 NSP 任务解决为合理的程度。

还有个地方要注意,本文并没有使用(其实是在 xxlarge 中)dropout

接下来是代码部分了,我们直接阅读官方代码(注意,是 1.x 的 Tensorflow,不是最新的 2.x)。模型的整体架构可以简化如下:

 1class AlbertModel:2    def __init__(self, config, input_ids, input_mask, token_type_ids):3        input_shape = get_shape_list(input_ids, expected_rank=2)4        batch_size, seq_length = input_shape56        # Token Embedding 7        # (batch_size, seq_length, embedding_size)8        word_embedding_output = embedding_lookup(9            input_ids=input_ids, vocab_size=config.vocab_size, embedding_size=128
10        )
11
12        # Add positional embeddings and token type embeddings, 
13        # then layer normalize and perform dropout.
14        # (batch_size, seq_length, embedding_size)
15        embedding_output = embedding_postprocessor(
16            input_tensor=word_embedding_output, token_type_ids=token_type_ids,
17            token_type_vocab_size=2, max_position_embeddings=512,
18            dropout_prob=config.hidden_dropout_prob, # 0.1, 0.1, 0.1, 0
19            token_type_embedding_name="token_type_embeddings",
20            position_embedding_name="position_embeddings"
21        )
22
23        # Run the stacked transformer.
24        # `sequence_output` shape = [batch_size, seq_length, hidden_size].
25        # 下面注释的参数分别对应 base large xlarge 和 xxlarge
26        all_encoder_layers = transformer_model(
27            input_tensor=embedding_output, attention_mask=input_mask,
28            hidden_size=config.hidden_size, # 768, 1024, 2048, 4096
29            num_hidden_layers=config.num_hidden_layers, # 12, 24, 24, 12
30            num_hidden_groups=1, 
31            num_attention_heads=config.num_attention_heads, # 12, 16, 16, 64
32            intermediate_size=config.intermediate_size, # 3072, 4096, 8192, 16384
33            inner_group_num=1,
34            intermediate_act_fn=get_activation("gelu"),
35            hidden_dropout_prob=config.hidden_dropout_prob, # 0.1, 0.1, 0.1, 0
36            attention_probs_dropout_prob=config.attention_probs_dropout_prob, # as above
37            initializer_range=0.02, do_return_all_layers=True, use_einsum=True
38        )
39
40        sequence_output = all_encoder_layers[-1]
41        # The "pooler" converts the encoded sequence tensor of shape
42        # [batch_size, seq_length, hidden_size] to a tensor of shape
43        # [batch_size, hidden_size]. This is necessary for segment-level
44        # (or segment-pair-level) classification tasks where we need a fixed
45        # dimensional representation of the segment.
46        # We "pool" the model by simply taking the hidden state corresponding
47        # to the first token. We assume that this has been pre-trained
48        first_token_tensor = tf.squeeze(sequence_output[:, 0:1, :], axis=1)
49        pooled_output = tf.layers.dense(
50            first_token_tensor,
51            config.hidden_size,
52            activation=tf.tanh,
53            kernel_initializer=create_initializer(config.initializer_range)
54        )

共分为三个组块:Embedding,Transformer 和 Pooling,其中 Embedding 包括了 Token Embedding、Position Embedding 和 Token type Embedding;Transformer 。。。;Pooling 。。。。

Embedding

Token Embedding 比较简单,没有特别的,稍微需要提一下的是 table 的 initializer 都是使用了 tf.truncated_normal_initializer(stddev=0.02),这也包括下面的位置编码和 token type 编码。再就是第一个调整的地方:使用了低维的嵌入(128),后面进到 Transformer 后会先转成 hidden_size。

然后是 Token type Embedding,这里的 type_vocab_size 等于 2,其实就是用 0 和 1 分别表示两个句子,label 就是第二个句子是不是第一个句子的下一句,这是 Bert 的基本配置,参见这里。

接下来是 Position Embedding,使用的是绝对位置编码,先用 max_sequence_len(512)创建一个 table,然后根据 sequence 的长度切片。

此处有感慨,参见《打开脑洞》。

Transformer

首先就是将进来的 Embedding 转为 hidden size(Bert 不需要这一步,因为两者是相等的):

1# (batch_size, seq_length, embedding_size) =>  (batch_size, seq_length, hidden_size)
2prev_output = dense_layer_2d(input_tensor, hidden_size)

然后就是 stack Transformer 了:

 1for layer_idx in range(num_hidden_layers=12):2    group_idx = int(layer_idx / num_hidden_layers * num_hidden_groups=1)3    layer_output = prev_output4    for inner_group_idx in range(inner_group_num=1):5        layer_output = attention_ffn_block(...)6        prev_output = layer_output7        all_layer_outputs.append(layer_output)89# 原始代码如下:
10def transformer_model(input_tensor,
11                      attention_mask,
12                      hidden_size=768,
13                      num_hidden_layers=12,
14                      num_hidden_groups=1,
15                      num_attention_heads=12,
16                      intermediate_size=3072,
17                      inner_group_num=1,
18                      intermediate_act_fn="gelu",
19                      hidden_dropout_prob=0.1,
20                      attention_probs_dropout_prob=0.1,
21                      initializer_range=0.02,
22                      do_return_all_layers=False,
23                      use_einsum=True):
24    attention_head_size = hidden_size // num_attention_heads
25    input_shape = get_shape_list(input_tensor, expected_rank=3)
26    input_width = input_shape[2]
27
28    all_layer_outputs = []
29
30    if input_width != hidden_size:
31        # 本文的情况(第一个调整点)
32        prev_output = dense_layer_2d(
33            input_tensor, hidden_size, create_initializer(initializer_range),
34            None, use_einsum=use_einsum, name="embedding_hidden_mapping_in")
35    else:
36        # 正常情况(如 Bert)
37        prev_output = input_tensor
38
39    # 层间参数共享(第二个调整点)
40    with tf.variable_scope("transformer", reuse=tf.AUTO_REUSE):
41        for layer_idx in range(num_hidden_layers):
42            group_idx = int(layer_idx / num_hidden_layers * num_hidden_groups)
43            with tf.variable_scope("group_%d" % group_idx):
44                with tf.name_scope("layer_%d" % layer_idx):
45                    layer_output = prev_output
46                    for inner_group_idx in range(inner_group_num):
47                        with tf.variable_scope("inner_group_%d" % inner_group_idx):
48                            layer_output = attention_ffn_block(
49                                layer_input=layer_output,
50                                hidden_size=hidden_size,
51                                attention_mask=attention_mask,
52                                num_attention_heads=num_attention_heads,
53                                attention_head_size=attention_head_size,
54                                attention_probs_dropout_prob=attention_probs_dropout_prob,
55                                intermediate_size=intermediate_size,
56                                intermediate_act_fn=intermediate_act_fn,
57                                initializer_range=initializer_range,
58                                hidden_dropout_prob=hidden_dropout_prob,
59                                use_einsum=use_einsum)
60                            prev_output = layer_output
61                            all_layer_outputs.append(layer_output)
62    if do_return_all_layers:
63        return all_layer_outputs
64    else:
65        return all_layer_outputs[-1]

因为这里其实并没有分组(都是 1),所以就是 12 层的 attention_ffn_block stack。看到这里的时候有个关于归一化的疑惑,之前看 Transformer 的时候看的是这个版本:The Annotated Transformer,每个 block 里面是先 norm 再传给 attention layer,出来后和输入做残差连接,当时觉得和图不太一样,也没有多想;后来看到 OpenNMT 也是这么实现的,就更没多想了。但是这次回看了一下 Bert 的代码实现,发现就是和图示一样的:先将输入和 attention layer 的输出做残差连接,再 norm。

接下来看一下 attention_ffn_block:

 1# 参数都是 base 的2def attention_ffn_block(layer_input,3                        attention_mask,4                        hidden_size=768,5                        num_attention_heads=12,6                        attention_head_size=64,7                        attention_probs_dropout_prob=0.1,8                        intermediate_size=3072,9                        intermediate_act_fn="gleu",
10                        initializer_range=0.02,
11                        hidden_dropout_prob=0.1,
12                        use_einsum=True):
13    with tf.variable_scope("attention_1"):
14        with tf.variable_scope("self"):
15            attention_output = attention_layer(
16                from_tensor=layer_input,
17                to_tensor=layer_input,
18                attention_mask=attention_mask,
19                num_attention_heads=num_attention_heads,
20                attention_probs_dropout_prob=attention_probs_dropout_prob,
21                initializer_range=initializer_range,
22                use_einsum=use_einsum
23            )
24        # Run a linear projection of `hidden_size` then add a residual
25        # with `layer_input`.
26        with tf.variable_scope("output"):
27            attention_output = dense_layer_3d_proj(
28                attention_output,
29                hidden_size,
30                attention_head_size,
31                create_initializer(initializer_range),
32                None,
33                use_einsum=use_einsum,
34                name="dense"
35            )
36            attention_output = dropout(attention_output, hidden_dropout_prob)
37    attention_output = layer_norm(attention_output + layer_input)
38
39    with tf.variable_scope("ffn_1"):
40        with tf.variable_scope("intermediate"):
41            intermediate_output = dense_layer_2d(
42                attention_output,
43                intermediate_size,
44                create_initializer(initializer_range),
45                intermediate_act_fn,
46                use_einsum=use_einsum,
47                num_attention_heads=num_attention_heads,
48                name="dense")
49        with tf.variable_scope("output"):
50            ffn_output = dense_layer_2d(
51                intermediate_output,
52                hidden_size,
53                create_initializer(initializer_range),
54                None,
55                use_einsum=use_einsum,
56                num_attention_heads=num_attention_heads,
57                name="dense")
58        ffn_output = dropout(ffn_output, hidden_dropout_prob)
59    ffn_output = layer_norm(ffn_output + attention_output)
60    return ffn_output
61
62# 去掉参数共享的简化版
63def attention_ffn_block(...):
64    # Multi-Head Attention
65    attention_output = attention_layer(...)
66    attention_output = dense_layer_3d_proj(...)
67    attention_output = dropout(attention_output, hidden_dropout_prob)
68    attention_output = layer_norm(attention_output + layer_input)
69
70    # Feed Forward
71    intermediate_output = dense_layer_2d(...)
72    ffn_output = dense_layer_2d(...)
73    ffn_output = dropout(ffn_output, hidden_dropout_prob)
74    ffn_output = layer_norm(ffn_output + attention_output)
75
76    return ffn_output

这个就是 Transformer(准确来说是 Encoder)的一个 block 了,和原 Bert 的代码对比就可以发现,ALBERT 版本是把 block 单独抽出来作为一个函数(同时也是方便参数共享),另外也把原来的 dense 连接单独抽为一个函数(dense_layer_xd),代码看起来更加清晰。dense_layer_2d 输出的 shape 是 (batch_size, seq_length, hidden_size),而 dense_layer_3d 输出的 shape 则是 (batch_size, seq_length, num_heads, head_size)

再下来就是 attention layer,也就是 Self attention,Transformer Encoder 的 Attention,qkv 都来自前一层输出的 Attention(可以参考这里的代码):

 1def attention_layer(from_tensor,2                    to_tensor,3                    attention_mask,4                    num_attention_heads=12,5                    attention_probs_dropout_prob=0.1,6                    initializer_range=0.02,7                    use_einsum=True):89    # (batch_size, seq_length, hidden_size)
10    from_shape = get_shape_list(from_tensor, expected_rank=[2, 3])
11    to_shape = get_shape_list(to_tensor, expected_rank=[2, 3])
12    # 768/12 = 64
13    size_per_head = int(from_shape[2]/num_attention_heads)
14
15    batch_size = from_shape[0]
16    from_seq_length = from_shape[1]
17    to_seq_length = to_shape[1]
18
19    # `q,k,v` = [B, F, N, H]
20    # from_tensor == to_tensor == prev_layer_output
21    q = dense_layer_3d(from_tensor, num_attention_heads, size_per_head)
22    k = dense_layer_3d(to_tensor, num_attention_heads, size_per_head)
23    v = dense_layer_3d(to_tensor, num_attention_heads, size_per_head)
24
25    q = tf.transpose(q, [0, 2, 1, 3])
26    k = tf.transpose(k, [0, 2, 1, 3])
27    v = tf.transpose(v, [0, 2, 1, 3])
28
29    if attention_mask:
30        attention_mask = tf.reshape(attention_mask, [batch_size, 1, to_seq_length, 1])
31
32    # 'new_embeddings = [B, N, F, H]'
33    new_embeddings = dot_product_attention(
34        q, k, v, attention_mask, attention_probs_dropout_prob)
35    return tf.transpose(new_embeddings, [0, 2, 1, 3])

我们也稍微回顾一下 attention 的计算,之前写过类似的笔记:Bahdanau Attention 和 Luong Attention,另外在 Transformer 笔记中也有介绍过 OpenNMT 的实现,可以参考。下面的实现来自 [Attention is All You Need](Attention Is All You Need Note | Yam)。

 1def dot_product_attention(q, k, v, mask, dropout_rate=0.1):2    # (seq_length, num_heads, q_length, kv_length)3    # (qk^t)/sqrt(d_k)·v, d_k = q_d4    logits = tf.matmul(q, k, transpose_b=True)5    logits = tf.multiply(logits, 1.0 / math.sqrt(float(get_shape_list(q)[-1])))6    if mask is not None:7        # `attention_mask` = [B, T]8        from_shape = get_shape_list(q)9        broadcast_ones = tf.ones([from_shape[0], 1, from_shape[2], 1], tf.float32)
10        mask = tf.matmul(broadcast_ones,
11                         tf.cast(mask, tf.float32), transpose_b=True)
12
13        # Since attention_mask is 1.0 for positions we want to attend and 0.0 for
14        # masked positions, this operation will create a tensor which is 0.0 for
15        # positions we want to attend and -10000.0 for masked positions.
16        adder = (1.0 - mask) * -10000.0
17
18        # Since we are adding it to the raw scores before the softmax, this is
19        # effectively the same as removing these entirely.
20        logits += adder
21    else:
22        adder = 0.0
23
24    attention_probs = tf.nn.softmax(logits, name="attention_probs")
25    attention_probs = dropout(attention_probs, dropout_rate)
26    return tf.matmul(attention_probs, v)

到这里 Transformer 就介绍完了,可以看到虽然源代码量看起来很大,但其实读起来并不复杂,整体还是非常清晰流畅的。

Pooling

这部分非常简单:

1first_token_tensor = tf.squeeze(self.sequence_output[:, 0:1, :], axis=1)
2# (batch_size, hidden_size)
3pooled_output = tf.layers.dense(
4    first_token_tensor, 
5    config.hidden_size, 
6    activation=tf.tanh,
7    kernel_initializer=create_initializer(config.initializer_range))

这里的 first_token 其实就是句子标记 CLS,在本模型中就是 0 或 1,0 表示两个句子是连贯的,1 表示两个句子是交换了顺序的,这样做的目的是为了接下来计算 Loss(因为只要 CLS 的结果即可)。

以上就是模型和算法的所有部分了,简单总结一下:

  • 模型还是基于 Bert,即 Transformer 的 Encoder 架构。

  • 对模型进行了三个调整:分解 Embedding、共享层间参数、SOP 替代 NSP(代码见《如何开始训练》)。

特点和创新

  • 分解 Embedding 参数

  • SOP 替代 NSP

  • 证明 dropout 有损基于 Transformer 的模型

How

如何构造数据

主要的不同是使用了 n-gram masking,也就是随机选择 mask n-gram,n 最大取 3,分布为:

n 为 1 时就是 mask 一个词。

1# 取 unigram, bigram, thigram 的概率
2pvals = 1. / np.arange(1, 3 + 1)
3pvals /= pvals.sum(keepdims=True)
4# pvals = array([0.54545455, 0.27272727, 0.18181818])

这段预处理代码比较繁琐,我们稍微简化一下,以词 token 为例:

 1def create_masked_lm_predictions(2    tokens, 3    masked_lm_prob=0.15, 4    max_predictions_per_seq=20, 5    vocab_words=list(tokenizer.vocab.keys()), 6    rng=random.Random(12345)):7    cand_indexes = []8    for (i, token) in enumerate(tokens):9        if token == "[CLS]" or token == "[SEP]":
10            continue
11        cand_indexes.append([i])
12
13    output_tokens = list(tokens)
14    masked_lm_positions = []
15    masked_lm_labels = []
16    num_to_predict = min(max_predictions_per_seq, 
17                         max(1, int(round(len(tokens) * masked_lm_prob))))
18
19    print("num_to_predict: ", num_to_predict)
20    # 不同 gram 的比例
21    ngrams = np.arange(1, 3 + 1, dtype=np.int64)
22    pvals = 1. / np.arange(1, 3 + 1)
23    pvals /= pvals.sum(keepdims=True)
24
25    # 每个 token 对应的三个 ngram
26    ngram_indexes = []
27    for idx in range(len(cand_indexes)):
28        ngram_index = []
29        for n in ngrams:
30            ngram_index.append(cand_indexes[idx:idx+n])
31        ngram_indexes.append(ngram_index)
32    rng.shuffle(ngram_indexes)
33
34    masked_lms = []
35    # 获取 masked tokens
36    # cand_index_set 其实就是每个 token 的三个 ngram
37    # 比如:[[[13]], [[13], [14]], [[13], [14], [15]]]
38    for cand_index_set in ngram_indexes:
39        if len(masked_lms) >= num_to_predict:
40            break
41        # 根据 cand_index_set 不同长度 choice
42        n = np.random.choice(
43            ngrams[:len(cand_index_set)], 
44            p=pvals[:len(cand_index_set)] / pvals[:len(cand_index_set)].sum(keepdims=True))
45        # [16, 17] = sum([[16], [17]], [])
46        index_set = sum(cand_index_set[n - 1], [])
47        # 处理选定的 ngram index :80% MASK,10% 是原来的,10% 随机替换一个
48        for index in index_set:
49            masked_token = None
50            if rng.random() < 0.8:
51                masked_token = "[MASK]"
52            else:
53                if rng.random() < 0.5:
54                    masked_token = tokens[index]
55                else:
56                    masked_token = vocab_words[rng.randint(0, len(vocab_words) - 1)]
57            output_tokens[index] = masked_token
58            masked_lms.append(MaskedLmInstance(index=index, label=tokens[index]))
59
60    masked_lms = sorted(masked_lms, key=lambda x: x.index)
61    for p in masked_lms:
62        masked_lm_positions.append(p.index)
63        masked_lm_labels.append(p.label)
64    return (output_tokens, masked_lm_positions, masked_lm_labels)

这里做了不少简化,但足够说明思路了,其实就是根据给定的 p(n) 选择 ngram 作为预测词处理。原代码除了考虑 wordpiece 的情况,还考虑了其他的一些细节,比如不能超过 num_to_predict,不重复处理等等,想起了陈皓的一句话:“细节处尽是魔鬼。”。

最后看一个输入和输出的例子:

 1print(" ".join(tokens))2"""3[CLS] Text should be one-sentence-per-line, with empty lines between documents. [SEP] This sample text is public domain and was randomly selected from Project Guttenberg. [SEP]4"""5print(" ".joint(output_tokens))6"""7[CLS] Text should be one-sentence-per-line, with empty lines between [MASK] [SEP] [MASK] [MASK] text is public domain and was randomly 屿 from Project Guttenberg. [SEP]8"""9masked_lm_positions # [9, 11, 12, 20]
10masked_lm_labels # ['documents.', 'This', 'sample', 'selected']

如何开始训练

这里主要提一下 SOP( Sentence Order Prediction Loss Function),其他的和 Bert 类似,具体原理前面已经提到了,代码如下:

 1def get_sentence_order_output(pooled_output, labels):2    # Simple binary classification. Note that 0 is "next sentence" and 1 is3    # "random sentence". This weight matrix is not used after pre-training.4    with tf.variable_scope("cls/seq_relationship"):5        output_weights = tf.get_variable(6            "output_weights",7            shape=[2, 768],8            initializer=create_initializer(0.02))9        output_bias = tf.get_variable(
10            "output_bias", 
11            shape=[2], 
12            initializer=tf.zeros_initializer())
13
14    # (batch_size, hidden_size) * (2, hidden_size)^T
15    # => (batch_size, 2), 2 是类别数量
16    logits = tf.matmul(pooled_output, output_weights, transpose_b=True)
17    logits = tf.nn.bias_add(logits, output_bias)
18    log_probs = tf.nn.log_softmax(logits, axis=-1)
19    labels = tf.reshape(labels, [-1])
20    # (batch_size, 2)
21    one_hot_labels = tf.one_hot(labels, depth=2, dtype=tf.float32)
22    per_example_loss = -tf.reduce_sum(one_hot_labels * log_probs, axis=-1)
23    loss = tf.reduce_mean(per_example_loss)
24    return (loss, per_example_loss, log_probs)

其实就是个多(二)分类。MLM 的 Loss 不再赘述。

如何使用结果

与 Bert 一样。

数据和实验

层间相似度,对应《模型和算法》的第二个调整:

参数量

RoBERTa 的参数量在这里,DistilBERT 的参数量是 66M。

Bert vs. ALBERT

Speedup 是训练时间,以 Bert large 为基准,ALBERT large 速度是 1.7 倍,但 xxlarge 比 Bert large 慢了 3 倍。

Embedding Size 的影响

层间参数共享的影响

NSP vs. SOP

Dropout

在各项任务中的表现可以查阅这里。

Discussion

相关工作

扩大语言表征模型

  • 语言表征是有用的,近几年最显著的变化是从词或上下文表征到全网络预训练然后下游任务精调。

    • 词表征:Tomas Mikolov, Ilya Sutskever, Kai Chen, Greg S Corrado, and Jeff Dean. Distributed represen- tations of words and phrases and their compositionality. In Advances in neural information processing systems, pp. 3111–3119, 2013.

    • 句表征:Quoc Le and Tomas Mikolov. Distributed representations of sentences and documents. In Proceedings of the 31st ICML, Beijing, China, 2014.

    • 词表征:Jeffrey Pennington, Richard Socher, and Christopher Manning. Glove: Global vectors for word rep- resentation. EMNLP 2014.

    • 下游任务:Andrew M Dai and Quoc V Le. Semi-supervised sequence learning. In Advances in neural infor- mation processing systems, pp. 3079–3087, 2015.

    • 上下文:Bryan McCann, James Bradbury, Caiming Xiong, and Richard Socher. Learned in translation: Contextualized word vectors. NIPS 2017.

    • 上下文:Matthew Peters, Mark Neumann, Mohit Iyyer, Matt Gardner, Christopher Clark, Kenton Lee, and Luke Zettlemoyer. Deep contextualized word representations. ACL 2018.

    • 下游任务:Alec Radford, Karthik Narasimhan, Tim Salimans, and Ilya Sutskever. Improving language understanding by generative pre-training. OpenAI, 2018.

    • 下游任务:Jacob Devlin, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova. BERT: Pre-training of deep bidirectional transformers for language understanding. ACL 2019.

    • 大模型好效果:Alec Radford, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei, and Ilya Sutskever. Language models are unsupervised multitask learners. OpenAI Blog, 1(8), 2019.

  • 已有方案:

    • 时间换空间:Tianqi Chen, Bing Xu, Chiyuan Zhang, and Carlos Guestrin. Training deep nets with sublinear memory cost. arXiv preprint arXiv:1604.06174, 2016.

    • 时间换空间:Aidan N Gomez, Mengye Ren, Raquel Urtasun, and Roger B Grosse. The reversible residual network: Backpropagation without storing activations. In Advances in neural information processing systems, pp. 2214–2224, 2017.

    • 并行训练:Colin Raffel, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang, Michael Matena, Yanqi Zhou, Wei Li, and Peter J Liu. Exploring the limits of transfer learning with a unified text-to-text transformer. arXiv preprint arXiv:1910.10683, 2019.

跨层参数共享

关注 Encoder-Decoder 架构:Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez, Łukasz Kaiser, and Illia Polosukhin. Attention is all you need. In Advances in neural information processing systems, pp. 5998–6008, 2017.

跨层共享效果更好(与本文观察不一致):Mostafa Dehghani, Stephan Gouws, Oriol Vinyals, Jakob Uszkoreit, and Łukasz Kaiser. Universal transformers. arXiv preprint arXiv:1807.03819, 2018.

DQEs,Input 和 Output 能达到平衡点(与本文观察不一致):Shaojie Bai, J. Zico Kolter, and Vladlen Koltun. Deep equilibrium models. In Neural Information Processing Systems (NeurIPS), 2019.

加一个共享参数的 Transformer:Jie Hao, Xing Wang, Baosong Yang, Longyue Wang, Jinfeng Zhang, and Zhaopeng Tu. Modeling recurrence for transformer. Proceedings of the 2019 Conference of the North, 2019.

句子顺序

  • 连贯性和衔接性:

    • Jerry R. Hobbs. Coherence and coreference. Cognitive Science, 3(1):67–90, 1979.

    • M.A.K. Halliday and Ruqaiya Hasan. Cohesion in English. Routledge, 1976.

    • Barbara J. Grosz, Aravind K. Joshi, and Scott Weinstein. Centering: A framework for modeling the local coherence of discourse. Computational Linguistics, 21(2):203–225, 1995.

  • 用句子预测相邻句子的词:

    • Ryan Kiros, Yukun Zhu, Ruslan Salakhutdinov, Richard S. Zemel, Antonio Torralba, Raquel Urtasun, and Sanja Fidler. Skip-thought vectors. NIPS 2015.

    • Felix Hill, Kyunghyun Cho, and Anna Korhonen. Learning distributed representations of sentences from unlabelled data. ACL 2016.

  • 预测未来的句子:Zhe Gan, Yunchen Pu, Ricardo Henao, Chunyuan Li, Xiaodong He, and Lawrence Carin. Learn- ing generic sentence representations using convolutional neural networks. ACL 2017.

  • 预测话语标记:

    • Yacine Jernite, Samuel R Bowman, and David Sontag. Discourse-based objectives for fast unsupervised sentence representation learning. arXiv preprint arXiv:1705.00557, 2017.(本文类似)

    • Allen Nie, Erin Bennett, and Noah Goodman. DisSent: Learning sentence representations from explicit discourse relations. ACL 2019.

  • 预测句对的第二部分是不是被另一个文档的句子替换:Jacob Devlin, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova. BERT: Pre-training of deep bidirectional transformers for language understanding.  ACL 2019.(Bert)

  • 将预测句子顺序结合在一个三分类任务中:Wei Wang, Bin Bi, Ming Yan, Chen Wu, Zuyi Bao, Liwei Peng, and Luo Si. StructBERT: Incorporating language structures into pre-training for deep language understanding. arXiv preprint arXiv:1908.04577, 2019.

Dropout
CNN 中同时使用批归一化和 dropout 可能导致结果更差:

  • Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, and Alexander A Alemi. Inception-v4, inception-resnet and the impact of residual connections on learning. In Thirty-First AAAI Confer- ence on Artificial Intelligence, 2017.

  • Xiang Li, Shuo Chen, Xiaolin Hu, and Jian Yang. Understanding the disharmony between dropout and batch normalization by variance shift. In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pp. 2682–2690, 2019.

加快 ALBERT 的训练和推理速度

  • 稀疏注意力:Rewon Child, Scott Gray, Alec Radford, and Ilya Sutskever. Generating long sequences with sparse transformers. arXiv preprint arXiv:1904.10509, 2019.

  • 块注意力:Tao Shen, Tianyi Zhou, Guodong Long, Jing Jiang, and Chengqi Zhang. Bi-directional block self- attention for fast and memory-efficient sequence modeling. arXiv preprint arXiv:1804.00857, 2018.

打开脑洞

又到了自由讨论的时间了。首先,必须感慨一下,Google 的代码写的真是实在,很浅白,注释很清楚。记得张俊林老师曾在一篇介绍预训练的知乎文章中评价过 Bert,说它在模型上其实没有太多创新,但是自然、简洁、优雅、有效地解决了问题,我想这可能就是 “工程师” 的 “工程” 两字的价值体现吧。这可能是我们平时应该特别注意和学习的地方,大多数人总是不由自主地把一个别人本来很简洁的东西 “改造” 得很复杂,如果是研究需要,那为了 1-2 个百分点是可以的,但工程上就没太多必要了,那是事倍功半。Google 风格真是越看越爱,导致我现在基本上只跟踪 Google 的最新研究。当然还有个原因——最近这些年 NLP 领域的几个重大突破(Word2Vec,Transformer,Bert)基本都和 Google 有关,这是算法、工程、数据综合后的结果,不应该感到意外。Facebook 则总是会及时做出更加易用和优化的产品(FastText,RoBERTa),当然 Facebook 更强的是推荐和视频领域。由于自己目前做 NLP 工作偏多,自然会更加关注 Google 一些。

然后开始胡侃这篇文章。这篇文章和 Facebook 的 RoBERTa 结果相差不多,但是参数量的确少了不少(毕竟前两个改动都是减少参数的),而且除 xxlarge 外比 DistilBERT 都少(操作起来要比后者省事多了,个人意见,蒸馏真不是一种优雅的方法)。所以实际使用的时候可以酌情考虑 ALBERT 或 RoBERTa,当然对中文来说,考虑到要 wwm(Whole Word Masking),目前开源只有 RoBERTa。

另外有个有意思的地方是 ALBERT 在构造数据时用了 n-gram mask,有点类似 wwm,可以算是一种改进,并没有用 RoBERTa 中的动态 Mask(可能是考虑提升比较微弱吧,但把 NSP 给换成 SOP 了,因为 RoBERTa 证明那没啥用然后就把它给去了),也没有使用 DistilBERT 中的 token prob mask(就是让选择 mask 时更加关注低频词,进而实现对 mask 的平滑取样,我觉得是非常 make sense 的一个点),可能是时间相隔太近吧,看了一下 arxiv 上的第一版的投稿时间,确实只差了 6 天。所以,下一个创新点也许是把这个点加进去?也许说不定已经有了,只是我没关注到。

Appendix

  • transformers/modeling_albert.py at master · huggingface/transformers

  • kamalkraj/ALBERT-TF2.0: ALBERT model Pretraining and Fine Tuning using TF2.0

本文由作者授权AINLP原创发布于公众号平台,点击'阅读原文'直达原文链接,欢迎投稿,AI、NLP均可。

原文链接:

https://yam.gift/2020/05/10/Paper/2020-05-10-ALBERT/

 

人工智能与算法学习知识星球小组正式成立了!

阅读至此了,点个在看吧????

这篇关于ALBERT 论文+代码笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/380542

相关文章

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

AI hospital 论文Idea

一、Benchmarking Large Language Models on Communicative Medical Coaching: A Dataset and a Novel System论文地址含代码 大多数现有模型和工具主要迎合以患者为中心的服务。这项工作深入探讨了LLMs在提高医疗专业人员的沟通能力。目标是构建一个模拟实践环境,人类医生(即医学学习者)可以在其中与患者代理进行医学

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey

Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs),如GPT-4、Claude-3和Gemini的快

D4代码AC集

贪心问题解决的步骤: (局部贪心能导致全局贪心)    1.确定贪心策略    2.验证贪心策略是否正确 排队接水 #include<bits/stdc++.h>using namespace std;int main(){int w,n,a[32000];cin>>w>>n;for(int i=1;i<=n;i++){cin>>a[i];}sort(a+1,a+n+1);int i=1