【理论】NLP语言模型概览-语言模型结构分类、BERT
语言模型结构分类
- Encoder-Decoder(Transformer): Encoder 部分是 Masked Multi-Head Self-Attention,Decoder 部分是 Casual Multi-Head Cross-Attention 和 Casual Multi-Head Self-Attention 兼具。比如T5,BART,MASS
- CLM(Causal Language Model, 因果语言模型): 即Transformer的Decoder,比如GPT。也叫自回归语言模型(Auto-Regressive Language Models)
- MLM(Masked Language Model,掩蔽语言模型): 即Transformer的Encoder。相比自回归模型,自编码模型的学习过程能看到预测词的前后内容,所以对文本的理解更深入,在同等成本的情况下理论上自编码模型对文本的分类、回归方面的NLU问题会有更好表现。典型的自编码模型有 BERT、ERNIE、ALBERT、RoBERTa、DistilBERT、ConvBERT、XLM、XLM-RoBERTa、FlauBERT、ELECTRA、Funnel Transformer。
- 前缀语言模型(Prefix Language Model): 如UniLM。与自回归语言模型相比,前缀语言模型在抽取输入文本特征时用了 Fully-Visible Mask(Encoder 用的掩码,能看到「过去」和「未来」)而不是 Future Mask(Decoder 用的掩码,只能看到「过去」),而生成本文部分则与自回归语言模型一样,只看到左侧
Pre-trained Language Model(PLM)模型
- BERT−Tokenizer
- Flan-T5:instruction-finetuned
- Falcon 7B:
- RoBERTa
- DeBERTa
- AlBERT
- ELECTRA
BERT (2018)
Bidirectional Encoder Representations from Transformers
BERT 具有两种输出,一个是pooler output,对应的[CLS]的输出,以及sequence output,对应的是序列中的所有字的最后一层hidden输出。
所以BERT主要可以处理两种,一种任务是分类/回归任务(使用的是pooler output),一种是序列任务(sequence output)
输入层
为了使得BERT模型适应下游的任务(比如说分类任务,以及句子关系QA的任务),输入将被改造成[CLS]+句子A(+[SEP]+句子B+[SEP]) 其中
[CLS]:代表的是分类任务的特殊token,它的输出就是模型的pooler output
[SEP]:分隔符
句子A以及句子B是模型的输入文本,其中句子B可以为空,则输入变为[CLS]+句子A
BERT预训练和微调
1. Mask Language Model(MLM):类似于完形填空(Cloze task)
具体的做法: 我们会随机mask输入的几个词,然后预测这个词。但是这样子做的坏处是因为fine-tuning阶段中并没有[MASK] token,所以导致了pre-training 和 fine-tuning的不匹配的情况。所以为了减轻这个问题,文章中采用的做法是:对于要MASK 15%的tokens,
(1) 80%的情况是替换成[MASK]
(2) 10%的情况是替换为随机的token
(3) 10%的情况是保持不变
for index in cand_indexes:
if len(masked_lms) >= num_to_predict: # 15% of total tokens
break
...
masked_token = None
# 80% of the time, replace with [MASK]
if rng.random() < 0.8:
masked_token = "[MASK]"
else:
# 10% of the time, keep original
if rng.random() < 0.5:
masked_token = tokens[index]
# 10% of the time, replace with random word
else:
masked_token = vocab_words[rng.randint(0, len(vocab_words) - 1)]
output_tokens[index] = masked_token
BPE (Byte Pair Encoding)
注意,这边的token的level是采用Byte Pair Encoding (BPE)生成word piece级别的,什么是word piece呢,就是一个subword的编码方式,经过WordpieceTokenizer 之后,将词变为了word piece, 例如:
# input = "unaffable"
# output = ["un", "##aff", "##able"]
好处是,可以有效的解决OOV(Out-Of-Vocabulary)的问题。
但是mask wordpiece的做法也被后来(ERNIE以及SpanBERT等)证明是不合理的,没有将字的知识考虑进去,会降低精度。
于是google在此版的基础上,进行Whole Word Masking(WWM)的模型。需要注意的是,中文的每个字都是一个word piece,所以WWM的方法在中文中,就是MASK一个词组。
2. Next sentence order(NSP) 预测两个句子是不是下一句的关系
具体来说:50%的概率,句子A和句子B是来自同一个文档的上下句,标记为is_random_next=False, 50%的概率,句子A和句子B不是同一个文档的上下句,具体的做法就是,采用从其他的文档(document)中,加入新的连续句子(segments)作为句子B。具体参考create_instances_from_document函数
首先我们会有一个all_documents存储所有的documents,每个documents是由句子segments组成的,每个segment是由单个token组成的。
我们首先初始化一个chunk数组,每次都往chunk中添加同一个document中的一个句子,当chunk的长度大于target的长度(此处target的长度一般是max_seq_length,但是为了匹配下游任务,target的长度可以设置一定比例short_seq_prob的长度少于max_seq_length)的时候,随机选择一个某个句子作为分割点,前面的作为句子A,后面的作为句子B。
chunk = [Sentence1, Sentence2,..., SentenceN], 我们随机选择选择一个句子作为句子A的结尾,例如2作为句子结尾,则句子A为=[Sentence1, Sentence2]。我们有50%的几率选择剩下的句子[Sentence3,...SentenceN]作为句子B,或者50%的几率时的句子B是从其他文档中的另外多个句子。
这时候可能会导致我们的训练样本的总长度len(input_ids)大于或者小于我们的需要的训练样本长度max_seq_length。
- 如果len(input_ids) > max_seq_length, 具体的做法是分别删除比较长的一个句子中的头(50%)或尾(50%)的token
- 如果len(input_ids) < max_seq_length, 采用的做法是补0。
根据我们的两个任务,我们预训练模型的输入主要由以下7个特征组成。
- input_ids: 输入的token对应的id
- input_mask: 输入的mask,1代表是正常输入,0代表的是padding的输入
- segment_ids: 输入的0:代表句子A或者padding句子,1代表句子B
- masked_lm_positions:我们mask的token的位置
- masked_lm_ids:我们mask的token的对应id
- masked_lm_weights:我们mask的token的权重,1代表是真实mask的,0代表的padding的mask
- next_sentence_labels:句子A和B是否是上下句
features = collections.OrderedDict()
features["input_ids"] = create_int_feature(input_ids)
features["input_mask"] = create_int_feature(input_mask)
features["segment_ids"] = create_int_feature(segment_ids)
features["masked_lm_positions"] = create_int_feature(masked_lm_positions)
features["masked_lm_ids"] = create_int_feature(masked_lm_ids)
features["masked_lm_weights"] = create_float_feature(masked_lm_weights)
features["next_sentence_labels"] = create_int_feature([next_sentence_label])
3. fine-tuning
在Fine-Tuning阶段的时候,我们可以简单的plugin任务特定的输入和输出,作为训练。 例如:
- 2句子 pairs: 相似度任务,
- 假设-前提 pairs: 推理任务,
- 问题-文章 pairs : QA任务
- text−∅ pair: 文本分类 or 序列标注.
在这个任务中,就不需要MLM任务以及NSP任务所需要的输入了,所以就只有固定输入features(input_ids, input_mask, segment_ids)以及任务特定features
例如分类任务的输入特征:
- input_ids: 输入的token对应的id
- input_mask: 输入的mask,1代表是正常输入,0代表的是padding的输入
- segment_ids: 输入的0:代表句子A或者padding句子,1代表句子B
- label_ids:输入的样本的label
features["input_ids"] = create_int_feature(feature.input_ids)
features["input_mask"] = create_int_feature(feature.input_mask)
features["segment_ids"] = create_int_feature(feature.segment_ids)
features["label_ids"] = create_int_feature([feature.label_id])
参考资料
https://blog.csdn.net/m0_64768308/article/details/128118706
版权声明:
作者:Zhang, Hongxing
链接:http://zhx.info/archives/386
来源:张鸿兴的学习历程
文章版权归作者所有,未经允许请勿转载。