bert下游_原来你是这样的BERT,i了i了!——超详细BERT介
绍(三)BERT下游任务...
原来你是这样的BERT,i了i了! —— 超详细BERT介绍(三)BERT下游任务
BERT(Bidirectional Encoder Reprentations from Transformers)是⾕歌在2018年10⽉推出的深度语⾔表⽰模型。
⼀经推出便席卷整个NLP领域,带来了⾰命性的进步。
从此,⽆数英雄好汉竞相投⾝于这场追剧(芝⿇街)运动。
只听得这边G家110亿,那边M家⼜1750亿,真是好不热闹!
然⽽⼤家真的了解BERT的具体构造,以及使⽤细节吗?
本⽂就带⼤家来细品⼀下。
前⾔
本系列⽂章分成三篇介绍BERT,上两篇分别介绍了BERT主模型的结构及其组件相关和BERT预训练相关,这⼀篇是最终话,介绍如何将BERT应⽤到不同的下游任务。
⽂章中的⼀些缩写:NLP(natural language processing)⾃然语⾔处理;CV(computer vision)计算机视觉;DL(deep learning)深度学习;NLP&DL ⾃然语⾔处理和深度学习的交叉领域;CV&DL 计算机视觉和深度学习的交叉领域。
⽂章公式中的向量均为⾏向量,矩阵或张量的形状均按照PyTorch的⽅式描述。
向量、矩阵或张量后的括号表⽰其形状。
本系列⽂章的代码均是基于transformers库(v2.11.0)的代码(基于Python语⾔、PyTorch框架)。
为便于理解,简化了原代码中不必要的部分,并保持主要功能等价。
阅读本系列⽂章需要⼀些背景知识,包括Word2Vec、LSTM、Transformer-Ba、ELMo、GPT等,由于本⽂不想过于冗长(其实是懒),以及相信来看本⽂的读者们也都是冲着BERT来的,所以这部分内容还请读者们⾃⾏学习。
本⽂假设读者们均已有相关背景知识。
⽬录
序列分类任务就是输⼊⼀个序列,输出整个序列的标签。
输⼊的序列可以是单句也可以是双句。
单句序列分类任务就是⽂本分类(text classification)任务,包括主题(topic)、情感(ntiment)、垃圾邮件(spam)等的分类任务;双句序列分类任务包括相似度(similarity)、释义(paraphra)、蕴含(entailment)等的分类任务。
根据标签数量分,可以分成单标签和多标签(multi-label)的分类任务。
根据标签的类别数量分,可以分成⼆分类或三分类、五分类等多分类任务。
BERT中的序列分类任务包括单句和双句的单标签回归或分类任务,涉及到语⾔可接受性(linguistic acceptability)、情感、相似度、释义、蕴含等特征的分类,即GLUE(General Language Understanding Evaluation)中的任务。
如下为⼀个相似度回归任务的例⼦(来⾃transformers库的⽰例):
5.000A plane is taking off. ||| An air plane is taking off.
3.800A man is playing a large flute. ||| A man is playing a flute.
3.800A man is spreading shreded chee on a pizza. ||| A man is spreading shredded chee on an uncooked pizza.
其中,最左边的是标签,表⽰两句话的相似度分数,分数越⾼,相似度越⾼,分数的取值范围是\([0, 5]\)。
再如下为⼀个双句释义⼆分类任务的例⼦(来⾃transformers库的⽰例):
1He said the foodrvice pie business ... ||| The foodrvice pie business ...
0Magnarelli said Racicot hated ... ||| His wife said he was ...
0The dollar was at 116.92 yen against the yen ... ||| The dollar was at 116.78 yen JPY ...其中,最左边的是标签,如果后句是前句的释义,即解释说明,那么标签为1,否则为0。
序列分类代码如下:
代码
# BERT之序列分类
class BertForSeqCls(BertPreTrainedModel):
def __init__(lf, config):
super().__init__(config)
# 标签的类别数量
lf.num_labels = config.num_labels
# 主模型
lf.bert = BertModel(config)
lf.dropout = nn.Dropout(config.hidden_dropout_prob)
# 线性回归或分类器
lf.cls = nn.Linear(config.hidden_size, config.num_labels)
# 回归或分类损失函数
lf.loss_fct = LossRgrsCls(config.num_labels)
lf.init_weights()
def forward(lf,
tok_ids, # 标记编码(batch_size * q_length)
pos_ids=None, # 位置编码(batch_size * q_length)
nt_pos_ids=None, # 句⼦位置编码(batch_size * q_length)
att_masks=None, # 注意⼒掩码(batch_size * q_length)
labels=None, # 标签(batch_size)
):
_, pooled_outputs = lf.bert(
tok_ids,
pos_ids=pos_ids,
nt_pos_ids=nt_pos_ids,
att_masks=att_masks,
)
pooled_outputs = lf.dropout(pooled_outputs)
logits = lf.cls(pooled_outputs)
if labels is None:
return logits # 对数⼏率(batch_size * num_labels)
loss = lf.loss_fct(logits, labels)
return loss
其中,
num_labels是标签的类别数量(注意:并不是标签数量,BERT的序列分类任务均为单标签分类任务),=1时为回归任务。
标记分类任务就是输⼊⼀个序列,输出序列中每个标记的标签。
输⼊的序列⼀般是单句。
标记分类任务就是序列标注(quence tagging)任务,包括中⽂分词(Chine word gmentation)、词性标注(Part-of-Speech tagging,POS tagging)、命名实体识别(named entity recognition,NER)等。
序列标注任务常规的做法是BIO标注,B表⽰需要标注的⽚段的开头标记,I表⽰⾮开头标记,O表⽰不需要标注的标记。
如下为⼀个NER任务的例⼦(来⾃transformers库的⽰例):
例⼦
Schartau B-PER
sagte O
dem O
" O
Tagesspiegel B-ORG
" O
vom O
Freitag O
, O
Fischer B-PER
i O
" O
in O
einer O
Wei O
aufgetreten O
, O
die O
überzeugend O war O
" O
. O
Firmengründer O Wolf B-PER
Peter I-PER
Bree I-PER arbeitete O Anfang O
der O
siebziger O
Jahre O
als O
Möbelvertreter O , O
als O
er O
einen O fliegenden O
Händler O
aus O
dem O
Libanon B-LOC traf O
. O
Ob O
sie O
dabei O
nach O
dem O
23. O
April O
in O
Berlin B-LOC
durch O
ein O
pädagogisches O
Konzept O
unterstützt O
wird O
, O
ist O
allerdings O
zu O
bezweifeln O
. O
其中,每⼀⾏为⼀个标记和其标签,空⾏分隔不同的句⼦;PER是⼈名、ORG是组织名、LOC是地名。标记分类代码如下:
代码
# BERT之标记分类
class BertForTokCls(BertPreTrainedModel):
def __init__(lf, config):
super().__init__(config)
# 标签的类别数量
lf.num_labels = config.num_labels
# 主模型
lf.bert = BertModel(config)
lf.dropout = nn.Dropout(config.hidden_dropout_prob)
# 线性分类器
lf.cls = nn.Linear(config.hidden_size, config.num_labels)