原文链接:https://blog.csdn.net/weixin_46649052/article/details/118936381?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163149603816780357297206%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163149603816780357297206&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-3-118936381.pc_search_result_control_group&utm_term=BERT&spm=1018.2226.3001.4187
本节将按照思维导图逐步了解BERT语言模型(基于transformer的网络结构)。
BERT带来了新的NLP范式。
大规模的预训练语言模型的模型参数量呈几何倍数的增长趋势。下面我们了解BERT预训练的原理。
基于词向量为基础的模型并不是在BERT中首次出现。在Word2vec中,词向量表示是有局限性的。这是因为词向量表达固定,无法表达上下文。2017年之前,NLP模型的普遍形态为词向量+encoder。
利用LSTM或者Transformer模型通过训练集来学习如何提取上下文信息,最终输出分类标签。这里的训练集指的是具有分类或者NER的标注信息。这种模式有如下缺陷:
这两个缺陷导致2017年以前的模型非常具有局限性。
基于上述的缺陷,我们现在想要有效的预训练embedding+编码器。有标签的文本数据获取成本大,相比来说,获取大量无标签的文本数据代价很小。所以如何进行预训练呢?这就涉及到在大量无标签的文本上进行自监督学习(self-supervised training)。
掩码预测任务与Word2vec中CBOW非常神似,但略有不同。
掩码预测任务中,输入是句子当中遮掩掉几个词(用”[MASK]"字符替换原词),经过BERT网络输出每个字的向量表征。在遮掩的这个字的位置上要经过一个线性层来预测这个位置是哪个字的概率。掩码预测任务存在的问题:在下游任务中,eg:预测一个句子的情感是不会有[Mask]的。因此,在实现掩码任务时,需要遮掩的字只占语料全体的字数的一小部分(15%);在这15%里面:
这样做的目的是使得模型能够区分这个词放在这里是否合理,使得模型具有判断这句话是否合理的能力,加深了模型对语言的理解。BERT模型在经过预训练之后天生就能够做一些下游任务,比如:纠错任务。
下句预测指的是:判断句子B是否是句子A的下文。此时,BERT句子的输入会是[CLS] A sent [SEP] B sent [SEP]
的格式。
我们经常通过句首的第一个token来表示句子整体的语义关系,在上下句预测的任务当中,上下句的关系是保存在输入的[CLS]
符号的当中,在预测时使用BertPooler提取整个输入序列的表征:
这里要注意:不是直接拿[CLS]的向量表征;而是要经过BertPooler这个模块,其中包含MLP,tanh操作得到一个向量表示,再输入到2分类层,BertPooler也是参与预训练的,预训练会更新整个bert的模型参数,微调时候其实是可以更新部分参数,后面会有介绍。
用了 Masked LM 和 Next Sentence Prediction 两种方法分别捕捉词语和句子级别的 representation
subword tokenizer就是将长的复杂的单词分成成短的简单的单词,eg:句子” play the song little robin redbreast”在输入模型时变为”[CLS] play the song red ##bre ##ast [SEP]”
。Tokenizer是预训练时候用了哪个,后面微调就要用哪个。使用subword tokenizer的原因是:
常见的subword模型:Byte Pair Encoding (BPE), WordPiece
BPE算法生成词表过程如下:
假设现在语料库中有如下词汇(及其频率):
观察词汇表大小在每一步如何变化
BPE算法编码:得到Subword词表后,针对每一个单词,我们可以采用如下的方式来进行编码:
WordPiece算法生成词表的步骤与BPE类似;加上”##”前缀表示token不作为一个完整单词的开始部分。与BPE的最大区别在于,如何选择两个子词进行合并∶BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表;
S
=
(
t
1
,
t
2
,
t
n
)
S =(t_1, t_2,t_n)
S=(t1,t2,tn)由n个子词组成,表示子词,且假设各个子词之间是独立存在的
简而言之,WordPiece每次选择合并的两个子词,他们具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性,它们经常在语料中以相邻方式同时出现。
Wordpiece举例:
BERT的嵌入层包含三种embedding
句子分类任务就是把文本按照一定的规则分门别类,文本分类有以下应用场景:
那么,在下游任务中,如何进行BERT句子分类任务的微调?不妨以单个句子分类为例(还有句子对分类,判定句子对是否同义),比如,我们现在想要对“想看黑寡妇”进行情感分类,首先将“想看黑寡妇”转换成BERT模型的输入形式,在句前加[CLS]
符号,句尾加[SEP]
符号,使用subword tokenizer进行分词,将这样处理好的文本输入到BERT模型中,得到句子的向量表征(句子中的每个字均有向量表征),通过BertPooler模块,取[CLS]
的向量表征,经过MLP,tanh操作后得到一个整句的向量表征,再经过分类层得到分类结果。
由于NLP中句子是不定长的,所以可以通过Pooling层将变长的向量转换成特定的size。除了使用BertPooler模块,还可以增加一个Pooling操作,将句子表征转化成句子级别的向量。这里的Pooling操作指:
分类任务微调的损失函数:假设模型对分类任务的训练样本为
(
x
,
y
)
(x,y)
(x,y),预测类别为
c
c
c的概率为
p
c
p_c
pc,则损失函数为
C
E
(
P
C
,
X
)
=
−
I
(
y
=
c
)
∗
l
o
g
(
p
c
)
CE(P_C,X)=-I(y=c)*log(p_c)
CE(PC,X)=−I(y=c)∗log(pc)
有了损失函数就可以利用梯度的更新做网络的训练。
分类任务微调的原理与一般的网络训练没有区别。微调时,如果不固定参数,所有层的参数会更新。你可以选择固定某些层的参数,比如:embedding层。
序列标注任务指的是对于待标注的一段序列
x
=
{
x
1
,
x
2
,
.
.
.
,
x
n
}
x=\{x_1, x_2,..., x_n\}
x={x1,x2,...,xn},我们需要给每个
x
i
x_i
xi预测一个标签
(
t
a
g
)
y
i
(tag)y_i
(tag)yi,标签
(
t
a
g
)
(tag)
(tag)集合是
T
=
{
t
1
,
t
2
,
.
.
.
,
t
m
}
T=\{t_1,t_2,...,t_m\}
T={t1,t2,...,tm}。在不同的序列标注任务中对应的标签不一样。
(
t
a
g
)
(tag)
(tag)集合是
{
B
e
g
i
n
M
i
d
d
l
e
E
n
d
S
i
n
g
l
e
}
\{Begin \ Middle\ End\ Single\}
{Begin Middle End Single}
eg:“风险基因协同的神经生物学作用”被分词为
风险 基因 协同 的 神经 生物学 作用,转化为序列标注任务为:风/B险/E基/B因/E协/B同/E的/s神/B经/E生/B物/M学/E作/B用/E。
关系分类任务就是从非结构化文本中抽取出结构化知识;具体为:区分出头实体与尾实体之间的语义关系,比如:
关系分类任务最直接的应用是构建知识图谱。
BERT关系分类任务微调方法有三种:
在自然语言处理应用场景中,经常出现样本不均衡场景的问题,这是因为在自然语言的语料库中,一个单词出现的频率与它在频率表里的排名成反比,即频率越高的单词,出现的次数越多。碰到样本不均衡问题时,需要对训练方法做一定的改变。
C
C
C是数据集类别数,
n
n
n是类别
i
i
i的样本数量,则从类别
i
i
i中采样一个样本的概率:
instance-balanced sampling:每个样本被等概率的抽到,即
p
i
=
n
i
∑
j
=
1
C
n
j
p_i=\frac{n_i}{{\sum_{j=1}^Cn_j}}
pi=∑j=1Cnjni
Class balanced sampling:每个类别被抽到的概率都相等
p
i
=
1
∑
j
=
1
C
1
p_i=\frac{1}{{\sum_{j=1}^C1}}
pi=∑j=1C11
一般重采样,假设
q
∈
(
0
,
1
)
q∈(0,1)
q∈(0,1),
n
i
q
∑
j
=
1
C
n
j
q
\frac{n_i^q}{{\sum_{j=1}^Cn_j^q}}
∑j=1Cnjqniq
如果对您有帮助,麻烦点赞关注,这真的对我很重要!!!如果需要互关,请评论或者私信!