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)
= config
# 标签的类别数量
_labels = _labels
# 主模型
= BertModel(config)
t = t(_dropout_prob)
# 线性回归或分类器
= (_size, _labels)
# 回归或分类损失函数
_fct = LossRgrsCls(_labels)
_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 = (
tok_ids,
pos_ids=pos_ids,
nt_pos_ids=nt_pos_ids,
att_masks=att_masks,
)
pooled_outputs = t(pooled_outputs)
logits = (pooled_outputs)
if labels is None:
return logits # 对数⼏率(batch_size * num_labels)
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
alles O
andere O
als 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
Runden O
Tisch O
am 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)
= config
# 标签的类别数量
_labels = _labels
# 主模型
= BertModel(config)
t = t(_dropout_prob)
# 线性分类器
= (_size, _labels)
# 分类损失函数
_fct = LossCls(_labels)
_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 * q_length)
):
outputs, _ = (
tok_ids,
pos_ids=pos_ids,
nt_pos_ids=nt_pos_ids,
att_masks=att_masks,
)
outputs = t(outputs)
logits = (outputs)
if labels is None:
return logits # 对数⼏率(batch_size * q_length * num_labels)
# 只计算⾮填充标记的损失
if att_masks is not None:
active = att_(-1)>0
logits = (-1, _labels)[active]
labels = (-1)[active]
loss = _fct(logits, labels)
return loss
BERT中的选择题是给出前句以及num_choices个后句,选择最优的后句。
如下(来⾃SWAG数据集):
2
Students lower their eyes nervously. She
pats her shoulder, then saunters toward someone.
turns with two students.
walks slowly towards someone.
wheels around as her dog thunders out.
其中,第⼀⾏是标签,第⼆⾏是前句,第三⾏到最后是四个后句;标签数字从0开始计数,即标签为2表⽰第三个(walks slowly towards
someone.)为正确选项。
BERT将每个样本转换成num_choices个双句:
Students lower their eyes nervously. ||| She pats her shoulder, then saunters toward someone.
Students lower their eyes nervously. ||| She turns with two students.
Students lower their eyes nervously. ||| She walks slowly towards someone.
Students lower their eyes nervously. ||| She wheels around as her dog thunders out.
然后每个双句的序列表⽰产⽣⼀个对数⼏率,num_choices个双句就得到⼀个长度为num_choices的对数⼏率向量,最后将这个向量作为
这个样本的输出,计算损失即可。
选择题代码如下:
代码
# BERT之选择题
class BertForMultiChoice(BertPreTrainedModel):
def __init__(lf, config):
super().__init__(config)
= config
# 选项个数
_choices = _choices
# 主模型
= BertModel(config)
t = t(_dropout_prob)
# 线性分类器
= (_size, 1)
# 分类损失函数
_fct = LossCls(1)
_weights()
def forward(lf,
tok_ids, # 标记编码(batch_size * num_choices * q_length)
pos_ids=None, # 位置编码(batch_size * num_choices * q_length)
nt_pos_ids=None, # 句⼦位置编码(batch_size * num_choices * q_length)
att_masks=None, # 注意⼒掩码(batch_size * num_choices * q_length)
labels=None, # 标签(batch_size)
):
q_length = tok_[-1]
# 调整形状,每个前句-后句选项对看作⼀个双句输⼊
tok_ids = tok_(-1, q_length)
if pos_ids is not None: pos_ids = pos_(-1, q_length)
if nt_pos_ids is not None: nt_pos_ids = nt_pos_(-1, q_length)
if att_masks is not None: att_masks = att_(-1, q_length)
_, pooled_outputs = (
tok_ids,
pos_ids=pos_ids,
nt_pos_ids=nt_pos_ids,
att_masks=att_masks,
)
pooled_outputs = t(pooled_outputs)
logits = (pooled_outputs)
# 调整形状,每num_choices个对数⼏率看作⼀个样本的输出
logits = (-1, _choices)
if labels is None:
return logits # 对数⼏率(batch_size * num_choices)
loss = _fct(logits, labels)
return loss
其中,
num_choices是选项个数。
BERT中的问答任务其实是抽取式的机器阅读理解(machine reading comprehension)任务,即给定⼀段话,给定⼀个问题,问题的答案
来⾃这段话的某个连续的⽚段。
如下(来⾃transformers库的⽰例):
0Computational complexity theory
What branch of theoretical computer science deals with broadly classifying computational problems by difficulty and class
of relationship?
Computational complexity theory is a branch of the theory of computation in theoretical computer science that focus on
classifying computational problems according to their inherent difficulty ...
其中,第⼀⾏是答案,答案左边的数字表⽰这个答案在给定的这段话的起始位置(从0开始计数),第⼆⾏是问题,第三⾏是给定的⼀段话。
BERT将这个抽取式任务转化为⼀个预测答案起始和结束位置的分类任务,标签的类别数量是q_length,起始位置和结束位置分别预测,
即相当于两个标签。
注意:这个起始和结束位置是标记化等预处理后答案在输⼊的编码向量⾥的位置。
BERT将所有的标记表⽰转化成两个对数⼏率,然后横向切⽚,得到两个长度为q_length的对数⼏率向量,分别作为起始和结束位置的预
测,最后计算损失即可。
问答代码如下:
代码
# BERT之问答
class BertForQustAns(BertPreTrainedModel):
def __init__(lf, config):
super().__init__(config)
= config
# 主模型
= BertModel(config)
# 线性分类器
= (_size, 2)
_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)
start_pos=None, # 起始位置标签(batch_size)
end_pos=None, # 结束位置标签(batch_size)
):
q_length = tok_[-1]
outputs, _ = (
tok_ids,
pos_ids=pos_ids,
nt_pos_ids=nt_pos_ids,
att_masks=att_masks,
)
logits = (outputs)
# 拆分起始和结束位置对数⼏率
start_logits, end_logits = (1, dim=-1)
start_logits = start_(-1, q_length)
end_logits = end_(-1, q_length)
if start_pos is None or end_pos is None:
return (
start_logits, # 起始位置对数⼏率(batch_size * q_length)
end_logits, # 结束位置对数⼏率(batch_size * q_length)
)
# 标签值裁剪,使值 (- [0, q_length],
# 其中合法值 (- [0, q_length-1],⾮法值 = q_length
start_pos = start_(0, q_length)
end_pos = end_(0, q_length)
# ignore_index=q_length:忽略标签值 = q_length对应的损失
loss_fct = LossCls(q_length, ignore_index=q_length)
start_loss = loss_fct(start_logits, start_pos)
end_loss = loss_fct(end_logits, end_pos)
loss = (start_loss + end_loss) / 2
return loss
后记
本⽂作为系列的最后⼀篇⽂章,详细地介绍了BERT下游任务,BERT的通⽤性就体现在只需要添加少量模块就能应⽤到各种不同的下游任
务。
BERT充分地利⽤了主模型输出的标记表⽰和序列表⽰,并对其进⾏⼀定地修改,从⽽可以应⽤到各种不同的下游任务中。
其中应⽤到选择题和问答任务的⽅式特别巧妙,分别活⽤了序列和标记表⽰。
然⽽,如同预训练,标记分类任务每个标记的标签是独⽴产⽣的,以及问答任务的起始和结束位置也是独⽴产⽣的,这其实不是⾮常合理。
本文发布于:2023-05-22 19:17:02,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/zhishi/a/1684754223174020.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:bert下游_原来你是这样的BERT,i了i了!——超详细BERT介绍(三)BERT下游任务....doc
本文 PDF 下载地址:bert下游_原来你是这样的BERT,i了i了!——超详细BERT介绍(三)BERT下游任务....pdf
留言与评论(共有 0 条评论) |