跟着问题学19——大模型基础之BERT详解(1)

BERT的基本思想

BERT如此成功的一个原因之一是它是基于上下文(context-based)的嵌入模型,不像其他流行的嵌入模型,比如word2vec,是上下文无关的(context-free)。

首先,让我们理解基于上下文和上下文无关的嵌入模型的区别。考虑下面两个句子:

Sentence A:He got bit by Python.

Sentence B:Python is my favorite programming language.

在句子A中,Python是蟒蛇的意思,而句子B中是一种编程语言。

如果我们得到上面两个句子中单词Python的嵌入向量,那么像word2vec这种嵌入模型就会为这两个句子中的Python赋予相同的嵌入。因为它是上下文无关的。

而BERT是基于上下文的模型,它可以根据上下文来生成单词的嵌入。因此它会给上面两个句子中的Python不同的嵌入向量。

那BERT是如何理解上下文的?

我们考虑句子A。首先BERT会将联系句子中每个单词与其他所有单词来理解每个单词的语境(contextual,或上行下文的)意思。所以为了理解单词Python的语境意思,BERT将单词Python与其他所有单词(包括自己)联系起来。

如上图所示,BERT能通过bit一词理解此句中的Python指的是蛇。这样,BERT根据上下文生成动态的嵌入表示。

BERT的原理

从BERT的全称,Bidirectional Encoder Representation from Transformer(来自Transformer的双向编码器表征),可以看出BERT是基于Transformer模型的,但只是其中的编码器。

我们输入一个句子,Transformer的编码器会输出句子中每个单词的编码表示。那双向的代表什么意思?

由于Transformer编码器天然就是双向的,因为它的输入是完整的句子,也就是说指定某个单词,BERT已经读入了一个句子两个方向上的所有单词。

我们举一个例子来理解BERT是如何从Transformer中得到双向编码表示的。

假设我们有一个句子A:He got bit by Python,现在我们把这个句子输入Transformer并得到了每个单词的上下文表示(嵌入表示)作为输出。

Transformer的编码器通过多头注意力机制理解每个单词的上下文,然后输出每个单词的嵌入向量

如下图所示,我们输入一个句子到Transformer的编码器,它输出句子中每个单词的上下文表示。我们可以叠加 N编码器。下图中 R_He 代表单词He的向量表示,每个单词向量表示的大小应当于每个编码器层的大小。

假设编码器层大小为768,那么单词的向量表示大小也就是768。

这样,通过BERT,给定一个句子,我们就得到了句子中每个单词的上下文嵌入向量表示。

BERT的配置

BERT的作者提出了两种标准的配置:

BERT-base

BERT-large

BERT-base

BERT-base包含12个编码器层。所有的编码器使用12个注意头。编码器中的全连接网络包含768个隐藏单元。因此,从该模型中得到的向量大小也就是768。

我们使用以下的记号:

编码器层数记为 L

注意力头数记为 A

隐藏单元数记为 H

因此BERT-base模型, L=12,A=12,H=768 。该模型的总参数大小为110M。BERT-base模型如下所示:

BERT-large

BERT-large包含24个编码器层。所有的编码器使用16个注意头。编码器中的全连接网络包含1024个隐藏单元。因此,从该模型中得到的向量大小也就是1024。

因此BERT-large模型,L=24,A=16,H=1024。该模型的总参数大小为340M。BERT-large模型如下所示:

BERT的其他配置

除了两种标准的配置,我们也可以使用其他不同的配置。一些更小的配置如下:

BERT-tiny, L=2,H=128

BERT-mini, L=4,H=256

BERT-small, L=4,H=512

BERT-medium, L=8,H=512

当计算资源有限时,我们可以使用这些更小的BERT模型。当然,标准的BERT模型能得到更准确的结果,同时也被广泛使用。

预训练BERT模型

本节我们会学习如何预训练BERT模型。预训练的意思是,假设我们有一个模型 m,首先我们为某种任务使用大规模的语料库训练模型m。现在来了一个新任务,并有一个新模型,我们使用已经训练过的模型(预训练的模型) m的参数来初始化新的模型,而不是使用随机参数来初始化新模型。然后根据新任务调整(微调)新模型的参数。这是一种迁移学习。

BERT模型在大规模语料库中通过两个任务来预训练,分别叫屏蔽语言建模和下一句预测。

我们会探讨如何进行预训练的,但在此之前,先看下如何表示输入数据。

输入数据表示

在把数据喂给BERT之前,我们通过下面三个嵌入层将输入转换为嵌入向量:

标记嵌入(Token embedding)

片段嵌入(Segment embedding)

位置嵌入(Position embedding)

标记嵌入

首先,我们有一个标记嵌入层。还是以一个例子来理解。

考虑下面两个句子:

Sentence A: Paris is a beautiful city.

Sentence B: I love Paris.

首先我们对这两个句子分词,得到分词后的标记(单词),然后连到一起,本例中,我们没有进行小写转换:

tokens = [Paris, is, a, beautiful, city, I, love, Paris]

接下来,我们增加一个新的标记,叫作[CLS]标记,到第一个句子前面:

tokens = [ [CLS], Paris, is, a, beautiful, city, I, love, Paris]

然后我们增加一个新的标记,叫作[SEP]标记,到每个句子的结尾:

tokens = [ [CLS], Paris, is, a, beautiful, city, [SEP], I, love, Paris, [SEP]]

注意[CLS]标记只加在第一个句子前面,而[SEP]标记加到每个句子末尾。

CLS\]标记用于分类任务,而\[SEP\]标记用于表示每个句子的结尾。 现在,在把所有的标记喂给BERT之前,我们使用一个叫作标记嵌入的嵌入层转换这些标记为嵌入向量。 这些嵌入向量的值会在训练过程中学习。如下图所示,我们有每个标记的嵌入,即,E_\[CLS\] 表示标记\[CLS\]的嵌入, E_Pairs 表示标记Pairs的嵌入,等等: ![](https://i-blog.csdnimg.cn/direct/f63c389d75be4c1e83bc5523d0905a9b.jpeg) **片段嵌入** 其次,我们有一个片段嵌入层。用于区分给定的两个句子。还是考虑上面介绍的例子: Sentence A: Paris is a beautiful city. Sentence B: I love Paris. 在对上面两个句子分词之后,我们得到了: tokens = \[ \[CLS\], Paris, is, a, beautiful, city, \[SEP\], I, love, Paris, \[SEP\]

然后,除了[SEP]标记,我们还要给模型某种标志来区分两个句子。因此,我们将输入的标记喂给片段嵌入层。

片段嵌入层只返回两种嵌入, E_A或E_B,作为输出。即如果输入标记属于句子 A,那么该标记会映射到嵌入E_A ;反之属于句子B的话,则映射到嵌入E_B。

如下图所示:

那如果我们只有一个句子时,片段嵌入是如何工作的呢?很简单,假设我们只有一个句子*Paris is a beautiful city*,那么所有的标记只会映射到同一个嵌入 E_A :

那如果我们有n个句子呢

位置嵌入

接下来,我们有一个位置嵌入层。我们知道Transformer为了得到句子中单词的顺序信息,使用了位置编码。

我们也知道BERT本质上就是Transformer的编码器,所以我们需要提供句子中标记的位置信息,然后才能输入到BERT。位置嵌入层就是干这个工作的。

如图所示,E_0表示标记[CLS]的位置嵌入,E_1表示标记Paris的位置嵌入,以此类推。

最终的表示

现在我们看一下最终的输入表示是怎样的。如下图所示,首先我们将给定的输入序列分词为标记列表,然后**分别?**喂给标记嵌入层,片段嵌入层和位置嵌入层,得到对应的嵌入表示。然后,累加所有的嵌入表示作为BERT的输入表示。

比如标记[CLS]的输入表示为标记嵌入 E_CLS+片段嵌入E_A +位置嵌入E_0。

下面我们来看一下BERT使用的叫作WordPiece的分词器。

WordPiece分词器

WordPiece分词器基于子词(subword)分词模式。还是以一个例子来理解该分词器是如何工作的。考虑下面的句子:

"Let us start pretraining the model."

现在,如果我们使用WordPiece分词器来分词,我们会得到如下所示的标记:

tokens = [let, us, start, pre, ##train, ##ing, the, model]

我们可以观察到,在使用WordPiece分词器对句子进行分词时,单词pretraining被拆分为以下子词------pre、##train、##ing。

这是什么意思?

当使用WordPiece分词器分词,首先我们检测单词在词表(vocabulary)中是否存在,若存在,则作为标记;否则,我们将该单词拆分为一些子词,然后我们检查这些子词是否存在于词表。

如果某个子词存在于词表,那么将它作为一个标记;否则继续拆分子词,然后检查更小的子词是否存在于词表中。

这样,我们不断地拆分子词,并用词表检查子词,直到碰到单个字符为止。这种做法可以有效地处理未登录词(out-of-vocabulary,OOV)。

BERT的词表有30K个标记,如果某个单词属于这30K个标记中一个,那我们将该单词视为一个标记;否则,我们拆分单词为子词,然后检查子词是否属于这30K个标记之一。

在我们的例子中,单词pretraining不在BERT的词表中。我们将它拆分为子词pre,##train和##ing。前面的#表示这个单词为一个子词,并且它前面有其他单词。现在我们检查子词##train和##ing是否出现在词表中。因为它们正好在词表中,所以我们不需要继续拆分。

通过使用一个WordPiece分词器,我们得到了下面的标记:

tokens = [let, us, start, pre, ##train, ##ing, the, model]

接着增加[CLS]到句子的开始和[SEP]到句子的结尾:

tokens = [ [CLS], let, us, start, pre, ##train, ##ing, the model, [SEP] ]

更详细的关于WordPiece分词器如何工作的讨论留到本文的结尾处。

参考资料

Getting Started with Google BERT

https://blog.csdn.net/yjw123456/article/details/120211601

相关推荐
亿牛云爬虫专家6 分钟前
深度学习在DOM解析中的应用:自动识别页面关键内容区块
深度学习·爬虫代理·dom·性能·代理ip·内容区块·东方财富吧
豆芽8197 分钟前
强化学习(Reinforcement Learning, RL)和深度学习(Deep Learning, DL)
人工智能·深度学习·机器学习·强化学习
山北雨夜漫步13 分钟前
机器学习 Day14 XGboost(极端梯度提升树)算法
人工智能·算法·机器学习
basketball61614 分钟前
Python torchvision.transforms 下常用图像处理方法
开发语言·图像处理·python
兔子蟹子19 分钟前
Java集合框架解析
java·windows·python
宁酱醇23 分钟前
各种各样的bug合集
开发语言·笔记·python·gitlab·bug
yzx99101324 分钟前
集成学习实际案例
人工智能·机器学习·集成学习
CodeJourney.26 分钟前
DeepSeek与WPS的动态数据可视化图表构建
数据库·人工智能·信息可视化
jndingxin26 分钟前
OpenCV 图形API(62)特征检测-----在图像中查找最显著的角点函数goodFeaturesToTrack()
人工智能·opencv·计算机视觉
努力犯错29 分钟前
昆仑万维开源SkyReels-V2,解锁无限时长电影级创作,总分83.9%登顶V-Bench榜单
大数据·人工智能·语言模型·开源