0.前言


  • 本文总结了初学者对​​BERT​​的一些常见疑惑
  • BERT 的五个关键词: ​​Pre-training、Deep、Bidirectional、Transformer、Language Understanding​


1. ​​Bert​​是什么?

首先要明白什么是预训练模型【这个我后面再更~】。在这个基础上,我们来看 Bert 模型。

1.1 定义


  • 标志BERT出身的那篇论文的标题是​​BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding​​​,​​论文链接​​。
  • BERT( ​​Bidirectional Encoder Representation from Transformers​​,其中文译名应该就是“基于transformers 的双向(深度)编码表示”)


Bert is a method of pretraining language representations


  • BERT是首个无监督的、深度双向预训练NLP模型,仅使用纯文本语料库进行训练。

1.2 特点


  • Bert最关键两点,一点是特征抽取器采用Transformer;第二点是预训练的时候采用双向语言模型。
  • BERT 是从大量的文本中经过半监督训练得来

2.​​BERT​​可以做什么?


You can either use these models to extract high quality language features from your text data, or you can fine-tune these models on a specific task (classification, entity recognition, question answering, etc.) with your own data to produce state of the art predictions.


主要可以用于两种用途,也就是:


  • extract feature
  • fine-tuning model

3.​​Embedding​​ 有什么用?


First, these embeddings are useful for keyword/search expansion, semantic search and information retrieval.
【即使没有关键字重叠,还是可以找出比较相似的句子】



Second, and perhaps more importantly, these vectors are used as high-quality feature inputs to downstream models.
这句话说的意思就是:使用Bert得到的embedding可以为下游任务提供高质量的特征。


4.​​BERT​​ 和 ​​word2vector​​

(1)word2vec 相关的内容可以参考我的另外一篇​​文章​​。word2vec 的思想是非常重要的,很多模型的背后都有这种思想。所以还需要认真阅读。

(2)这两者都是nlp发展路程上的一个里程碑,只不过​​word2vec​​很少出现在当下的论文里,而bert尚流行于世。但是二者的思想都是相通的,都是想将文本的特征映射到一个高维的向量中,并尽可能完整地保留所有特征(实际上这是很难的)。

4.1 二者的关系

​BERT​​​是 ​​word2vec​​​的高级进化版,进化的后果【用进废退】就是:​​BERT​​​广泛成长,​​word2vec​​逐渐废弃。

4.2 二者的区别

​Bert​​​ 与 ​​word2vec​​​ 的区别是:​​word2vec​​中每个单词都有一个固定的表示,而与单词出现的上下文无关;bert生成的单词表示是由单词周围的单词动态形成的。

  • 为什么使用​​word2vec​​​得到的embedding 与上下文无关?
    传统的​​​word2vec​​​包括很多种方法,比如:cbow,skip-gram 等都是常用的基础算法。这些算法都会有一个固定大小的词表,与这个词表对应有一个相应大小的矩阵(这个矩阵可训练,根据预先得到的数据训练,训练好就停止了)。如果需要某个词的embedding,就去这个词表矩阵中获取就可以了,所以说传统的​​word2vec​​得到的向量是固定的,那么以后无论在什么上下文中得到的向量都是相同的,所以说是上下文无关。

4.3 ​​Bert​​​ 也做 ​​word embedding​​吗?

Bert 有自己的word to vector方法,可以将其看做是embedding生成机制中的一种,但Bert得到embedding是动态的,即使同一个单词得到的embedding可能都不是一样的(因为其上下文可能不同)。但有个问题是:BERT 是由12层 ​​Transformer​​​ 构成,每个层得到的输出embedding各不相同,所以就需要我们自己考虑取哪些层的输出。通常情况下,取最后一层的​​embedding​​就OK了。

Bert 可以得到word的embedding,句子的embedding,句子的​​embedding​​​是通过​​[CLS]​​获取的。

4.4 ​​BERT​​​ 相比 ​​word2vec​​ 的优势是什么?


BERT produces word representations that are dynamically informed by the words around them.【最主要的就是利用了上下文信息,每个单词都不是一座孤岛】


5. ​​BERT、Transformer、Attention​​之间的关系

5.1 Attention

​Attention​​​ 是一种算法,可以将其看成是平级于 ​​RNN,LSTM​​ 的一种方法。这种方法的特别之处在于它对一个句子不同的词赋以不同的优先级。

5.2 Transformer

​transformer​​​ 是一种架构,它基于Attention 机制实现。所谓的​​transformer​​​也就是数个ecoder layer叠加得到的块。【同时需要注意,我们平常也会使用一个包叫做​​Transformer​​​,这个包实现了诸如​​BERT,GPT​​​这种常用且著名的算法,这个包由著名的​​hugging face​​团队开发】

5.3 Bert

​BERT​​​是一种集合​​transfomer+双向检索​​思想得到的算法,它可以很好的提取出文本中的信息。

6. BERT的 tokenize 过程

6.1 常见术语

  • ​sentence​


a “sentence” can be an arbitrary span of contiguous text, rather than an actual linguistic sentence.A “sequence” refers to the input token sequence to BERT, which may be a single sentence or two sentences packed together.


  • Tokens:标记
    BERT初学者九问_nlp

常用的​​[CLS]​​,​​[SEP]​​都是token,只不过他们是 ​​special token​​;​​token​​ 也可以是常见的汉字,也可以是常见的英语单词。但是也有可能

把常见的单词拆分成几个字母的组合。


Here are some examples of the tokens contained in our vocabulary.
说明单词表中存在的词就可以称之为 tokens 。



  • Tokenize: 将文本处理成token 的过程就是tokenize。这个过程常常使用Transformer中的Tokenizer类
  • Tokenizer:这个是代码中的类,用于实现tokenize的具体操作。例如BERT 有一个tokenzier 就叫做 BertTokenizer

6.2 tokenize的过程

预训练好的模型是在一个固定大小的单词表上搞出来的,这个单词表包含四种词汇:


  • 整个单词
  • 单词的开头部分
  • 单词的子成分
  • 独立的字符

当然,任何单词表也不一定能涵盖text中所有的单词。其中​​OOV:out of vocabulary; UNK:unknown vocabulary​​就是常出现的两种情况。对于单词表无法涵盖所有的单词,​​BERT​​并没有用​​OOV,UNK​​来表示这些不认识的单词,而是将不认识的单词拆分成多个部分。基于这个模型【指的是WordPiece模型】,tokenize的过程是:

01.先查找单词表中有没有这样的完整单词

02.如果有,则直接解析,执行下一个单词;如果没有,则拆分成概率最大的几个零散单词

03.如果还不行,则拆分成字符

讲完,即怎么用​​BERT​​​ 提取出 ​​embedding​​?

7. 如何用Bert提取出embedding?【如何用NLP预训练模型做​​word embedding​​?】

这个过程现在回过头来看实际比较简单,可以参考我的其它文章来理解。根据 ​​sentence​​​ 得出 ​​embedding​​​的过程可以参考​​文章​​ 来实现,这文章写的真的超级好。【英语不好读起来真是超要命】

7.1 代码实现

光说不练假把式,这些东西还是需要自己动手,使用bert生成embedding是个非常简单的过程。

"""
实现bert做embedding
"""
import torch
from transformers import BertTokenizer, BertModel # 导入transformer 的包
#torch.set_printoptions(profile="full") # 输出tensor的整个部分
"""
1.from_pretrained()方法是类PreTrainedModel的一个方法
2.这里的如果加上 output_hidden_states=True,那么就会把所有的hidden_states 给输出
如果没有加,那么就只能得到最后一个隐层的输出。
"""
model = BertModel.from_pretrained("bert-base-uncased",
output_hidden_states = True)

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

inputs = tokenizer("hello,my dog",return_tensors='pt')
# 执行model[BertModel实例]的forward()方法,但是在执行之前,仍然做了很多其他的事情
with torch.no_grad():
outputs = model(**inputs)
hiddden_states = outputs[2] # get the hidden states

# The last hidden-state is the first element of the output tuple
last_hidden_states = outputs[0]

print(last_hidden_states)

"""
1.如果我的句子是 "hello,my dog is cute",那么得到last_hidden_state 的size
就是torch.Size([1, 8, 768]);如果我的句子是"hello,my dog",那么得到的last_hidden_state
的 就是 torch.size([1,6,768])。也就是中间那个维度的大小是跟句子长度有关系

"""
print(last_hidden_states.size()) #

"""
inputs 是个字典,的内容如下:
{'input_ids': tensor([[ 101, 7592, 1010, 2026, 3899, 2003, 10140, 102]]),
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])
}
"""

得到的每个单词的 ​​embedding​​ 如下所示,因为这里共有4+2个​​token​​,所以其各个​​embedding​​显示如下:

BERT初学者九问_nlp_02

output_hidden_states 的控制在 modeling_bert.py 中的 BertModel类的forward()方法中,如下所示:

BERT初学者九问_bert_03有了这个参数,就可以看到每个隐层的输出结果,如下图所示:

BERT初学者九问_python_04output是个包含三个元素的tuple,分别的含义就是====

上面这段代码我放到了​​github​​中。库里面还有更好的内容哦,也在不断整理中~

7.2 关键点

我觉得关键点主要就如下几个:


  • 熟悉BERT的输入、输出长什么样子
  • 熟悉BERT的模型结构,只有这样才能理解BERT的工作流程,才能清楚数据在BERT中的流动过程
    上面这两点是比较关键的,
  • 再次者,我觉得熟悉源码操作的过程是比较重要的

8.BERT实战相关?

8.1 如何使用BERT?

可以使用 ​​Hugging Face​​​组织提供的 ​​Transformers​​​包,里面提供了Bert的实现。其中​​transformers​​​提供了将BERT应用到不同下游任务【​​token classification,text classification....​​】的许多类。


​transformers​​ provides a number of classes for applying BERT to different tasks (token classification, text classification, …).




安装包
可以采用普通命令进行安装​​pip install transformers​​ 这里我建议可以尝试源码安装,毕竟后面会常用到bert,阅读其源码也是一个基本学习方法。



使用包
​import transformers​



导入包中指定的模型
​from transformers import BertModel, BertTokenizer​​ 这句话的含义是从模块​​transformers​​中导入​​BertTokenizer​​ 和 ​​BertModel​​。这里请记住​​BertModel​​ 这个类,可以说这是源码中较为重要的一个类了。以后肯定会更加经常的碰到~



指定格式的输入
因为Bert是一个预训练好的模型,所以有很严格的数据输入格式。格式主要如下:
每个sentence都用​​SEP, CLS​​ 分割;分割得到的​​tokens​​必须与代码中预训练的​​BERT​​模型使用相一致的的词表【也就是我们在下载的文件中常见到的​​vocab.txt​​文件】;Tokens的Token IDs,来自于BERT 的tokenzier;Mask IDs:表明句中的元素谁是tokens,谁是被 padding 的元素; Segment IDs :用于区分不同的句子;​​Positional Embeddings​​: 用于展示剧中的token位置
这部分不再赘述,可以在参考博客中查看。
上面这些东西都可由 ​​BertTokenizer​​的实例 的tokenize()方法得到。



8.2 ​​BERT tokenizer​​ 是基于什么模型构建的?


BERT tokenizer was created with a WordPiece model.


这个部分尚未深入,待更新吧~

9.Bert结构

这部分我们主要讲讲Bert 的具体架构。这是初学者需要格外注意的问题,也是NLP工程师面试常考的问题。

9.1 体系结构

提纲挈领的一句话就是:


BERT is basically a trained Transformer Encoder stack.


我们常用的BERT分成​​bert-base​​ 和 ​​bert-large​​ 版,但是无论哪一种结构都是由一些​​encoder layers​​构成的,这些​​encoder layers​​又被叫做​​Transformer blocks​​。如下图所示:

BERT初学者九问_nlp_05但实际代码中使用 BERT 的架构同论文中BERT的架构是稍有不同的【大致相似】。

9.2 Bert预训练中输入的Embedding

Bert 输入的Embedding 有三个,如下图所示:

BERT初学者九问_python_06

其各个含义是,


  • Token Embeddings : 使用 ​​WordPiece​​方法得到的Embedding
  • Segement Embeddings: 在Bert中,我们称一个输入为sequence,原文中是这么说的:


A “sequence” refers to the input token sequence to BERT, which may be a single sentence or two sentences packed together. Throughout this work, a “sentence” can be an arbitrary span of contiguous text, rather than an actual linguistic sentence.


那问题来了,如果我们传入的是两个 ​​setence​​​ ,那么该怎么区分开呢(因为这也是一种信息,所以需要利用上)?bert中使用的方法就是使用 ​​Segment Embedding​​ 用于区分不同的句子。【论文中说,这个Segment Embedding 是一个可学习的参数】

  • Position Embeddings: 每个输入的token在sentence 中的位置是不同的,所以现在就需要对各个token的位置信息利用,所以提出使用position embedding .

10. Bert模型中有哪些参数?哪些参数会更新?怎么查看参数属性?

~ 待更新

参考资料


we will use BERT to extract features, namely word and sentence embedding vectors, from text data.