·请参考本系列目录:【BERT-多标签文本分类实战】之一——实战项目总览
·下载本实战项目资源:>=点击此处=<
[1] BERT模型库
从BERT
模型一经Google
出世,到tensorflow
与pytorch
版本的BERT
相继发布,再到不同下游任务的BERT
模型被整合到transformers
库,到底pip
哪个库才能用BERT
对于刚入门的同学来说是非常的不友好。
其实大部分的人的需求都非常地简单:只想用BERT模型替代原始的嵌入层来得到文本表示。大部分的人更在乎的是如何把bert
当作和embedding
层一样的黑箱,输入文本,得到文本表征向量。
这里,本项目使用的是Hugging Face
的transformers
库,因为它整合的太棒啦!transformers
库里面有NLP里面很火的大模型,比如BERT
、ALBERT
、GPT
、GPT2
、XLNET
等等。本项目只用到了transformers.BERT
,其文档地址为:https://huggingface.co/docs/transformers/model_doc/bert。
本篇文章主要还是以实战为导向,简单说一下transformers.BERT
要怎么用。想要深入使用,大家仔细看看上面贴的文档,写得非常好。
[2] transformers.BERT的下载
其实在自己的模型里使用BERT
非常简单,并不会像其他教程一样与transformers
库耦合的很深,本次在数据集Reuters-21578
上进行多标签文本分类的实战项目,就是在之前单标签文本分类任务上改动的。
单标签文本分类实战项目,请参考:【BERT-多标签文本分类实战】之一——实战项目总览
[2.1] 使用BERT前的准备
使用BERT
需要2个前置条件:
1、安装transformers
库;
2、下载预训练参数文件。
第一点不必赘述。第二点可说的比较多。首先,为什么要下载预训练参数文件?BERT它是预训练模型,有自己的网络结构,而且里面的参数达到上亿级别,BERT在大规模无监督语料上训练得到最终参数之后,是要把参数保存下来,之后有新的数据集要跑的时候,我们需要把这些参数数据载入到模型。除此之外,光有预训练参数文件不够,还需要一个配置文件,来告诉它模型的很多配置是多少(例如词典有多大、隐藏层维度是几等等)。最后还需要一个txt文件来存放词典。
总结一下,BERT
的预训练参数文件里有3个文件:1)参数数据;2)模型配置信息;3)txt词典。
[2.2] 预训练参数文件的挑选与下载
BERT
的参数文件有很多个版本:
上面框出来的是我比较常用的两个。bert-base-uncased
表示不区分大小写的英文模型,bert-base-chinese
表示中文模型。
假设我们要下载bert-base-uncased
,我们到页面:https://huggingface.co/bert-base-uncased/tree/main,下载的文件已经在图中框出:
由于本次实战使用的是pytorch
框架,所以最终我们下载下来的文件是:
【注】只下载这三个就够了,其他都可以不下载。
[3] transformers.BERT的测试示例
我们对BERT
模型的需求是,利用它来编码句子。在我们的实战项目中,有一个测试.py文件,里面展示了BERT
的基本用法。
把下载下来的参数文件放在项目下,然后写代码开始测试:
import torch from transformers import BertModel, BertTokenizer pretrained_path = 'bert-base-uncased' # 从文件夹中加载bert模型 model = BertModel.from_pretrained(pretrained_path) # 从bert目录中加载词典 tokenizer = BertTokenizer.from_pretrained(pretrained_path) # 输出字典的大小 print(f'vocab size :{tokenizer.vocab_size}') # 把'[PAD]'编码 print(tokenizer.encode('[PAD]')) # 把'[SEP]'编码 print(tokenizer.encode('[SEP]'))
输出:
vocab size :30522
[101, 0, 102]
[101, 102, 102]
接下来对英文句子进行词典编码,这里主要是把英文句子,按照词典里面的词序进行转化:
# 把句子编码,默认加入了special tokens了,也就是句子开头加入了[CLS] 句子结尾加入了[SEP] ids = tokenizer.encode("I love you transport", add_special_tokens=True, padding='max_length', truncation='only_first', max_length=6) print(ids)
输出:
[101, 1045, 2293, 2017, 3665, 102]
【注】不要凌乱! 来总结一下到目前为止说了什么:
第一小节介绍了采用transformers库来使用BERT;
第二小节介绍了使用BERT要下载哪些东西;
第三小节给了一段测试代码,其实是让大家自己试试有没有下载成功,能不能把demo运行起来。
[4] transformers.BERT的实际使用
第三小节给的代码只是demo。本节将介绍BERT
的实际使用,很简单,只需要一个函数tokenizer.encode_plus
,但是在写代码前,大家需要了解为什么要这样做。
[4.1] BERT模型的输入格式
BERT
要求我们:
1、在句子的句首和句尾添加特殊的符号[CLS]
和[SEP]
2、给句子填充 or 截断,使每个句子保持固定的长度
3、用 attention mask
来显示的区分填充的tokens
和非填充的tokens
。
特殊符号:
[SEP]
在每个句子的结尾,需要添加特殊的[SEP]
符号。
在以输入为两个句子的任务中(例如:句子 A 中的问题的答案是否可以在句子 B 中找到),该符号为这两个句子的分隔符。
目前为止还不清楚为什么要在单句中加入该符号,但既然这样要求我们就这么做吧。
[CLS]
在分类任务中,我们需要将[CLS]
符号插入到每个句子的开头。
这个符号有特殊的意义,BERT 包含 12 个 Transformer 层,每层接受一组 token 的 embeddings 列表作为输入,并产生相同数目的 embeddings 作为输出(当然,它们的值是不同的)。
句长 & 注意力掩码(Attention Mask)
很明显,数据集中句子长度的取值范围很大,BERT
该如何处理这个问题呢?
BERT
有两个限制条件:
1、所有句子必须被填充或截断到固定的长度,句子最大的长度为512个tokens
。
2、填充句子要使用[PAD]
符号,它在BERT
词典中的下标为0。
下图是最大长度为8个tokens的填充说明:
Attention Mask
是一个只有 0 和 1 组成的数组,标记哪些tokens
是填充的,哪些不是的。掩码会告诉 BERT 中的Self-Attention
机制不去处理这些填充的符号。
[4.2] BERT模型的最终使用方法
首先使用tokenizer.encode_plus
对文本编码:
from transformers import BertModel, BertTokenizer # 参数文件地址 pretrained_path = 'bert-base-uncased' # 加载bert模型 bert = BertModel.from_pretrained(pretrained_path) # 加载分词器 tokenizer = BertTokenizer.from_pretrained(pretrained_path) # 对英文文本content进行分词 encoded_dict = config.tokenizer.encode_plus( content, # 输入文本 add_special_tokens=True, # 添加 '[CLS]' 和 '[SEP]' max_length=pad_size, # 填充 & 截断长度 pad_to_max_length=True, padding='max_length', truncation='only_first', return_attention_mask=True, # 返回 attn. masks. return_tensors='pt' # 返回 pytorch tensors 格式的数据 ) # 编码后的文本 IDs = torch.squeeze(encoded_dict['input_ids'],0) # 文本的 attention mask MASKs = torch.squeeze(encoded_dict['attention_mask'],0)
函数tokenizer.encode_plus
包含以下步骤:
· 将句子分词为 tokens。
· 在两端添加特殊符号 [CLS] 和[SEP]。
· 将 tokens 映射为下标 IDs。
· 将列表填充或截断为固定的长度。
· 创建 attention masks,将填充的和非填充 tokens 区分开来。
更多参数请参考官方文档:https://huggingface.co/docs/transformers/model_doc/bert。
拿到了编码后的文本、文本的attention mask
之后,作为输入放入bert模型里面跑:
out = bert(IDs, attention_mask=MASKs)
【注】这里演示的是基本使用方法,比较独立。真正应用在实战项目里,还是配着整体代码食用更佳。