一、BERT论文
BERT是最近比较流行的预训练模型,目前很多的模型都是基于bert和bert的变体上的,可以点击BERT获取论文。网上对论文的解读有很多,我就不过多赘述了。可以自己直接百度 " bert论文笔记 “ 。
如果看了论文,我们可以看到BERT的三种编码:1)Token Embeddings 2)Segment Embedding 3)Position Embedding,以及两个任务:1)Masked Language Model 2)Masked Language Model 。那么BERT是用来干嘛的?其实很简单,就是编码,将word编码成embedding。如果有人不知道什么是embedding,byebye了您嘞。开个玩笑,传送门1,传送门2。简单来说,就是将一句话的每个word用一个向量表示,一句话的embedding就是一组一维向量组成的二维tensor。bert之前有one-hot编码、word2vec等等,他们本质上都是一个word表示一个向量,不同的是他们的向量长度和训练方式。废话太多,到此为止。
二、BERT用来做什么?
BERT就是用来获得文本的embedding的,怎么用呢?先上代码(中文模型):
from transformers import AutoModel,AutoTokenizer
import torch
bert =AutoModel.from_pretrained('bert_pretrain')
tokenizer=AutoTokenizer.from_pretrained('bert_pretrain')
text = "三个和尚在打架"
text = "[CLS] " + text + " [SEP]"
tokenized_text = tokenizer.tokenize(text) #分词,将句子切分成一个个字符
print('tokenized_text:',tokenized_text)
indexed_tokens1 = tokenizer.convert_tokens_to_ids(tokenized_text) #将字符转换为id
print('indexed_tokens1:',indexed_tokens1)
indexed_tokens1_tensor=torch.tensor([indexed_tokens1])
segments_ids1 = [1] * len(tokenized_text)
segments_ids1_tensor=torch.tensor([segments_ids1])
embedding=bert(indexed_tokens1_tensor,segments_ids1_tensor)[0] #词向量
sen=bert(indexed_tokens1,segments_ids1)[1] #句子向量
print(embedding.shape,embedding)
print(sen.shape,sen)
运行结果:
运行该代码,需要下载bert_pretrain文件,里面包含了模型参数和词表,可以到百度网盘下载,如下:
链接:https://pan.baidu.com/s/1BYwklGuHKQK9W7LdkyeVmw
提取码:1234
该文件夹里是中文的bert模型参数,英文的可以自行到抱抱脸(Hugging Face)上查找。
下载好bert_pretrain文件后,和代码放到同一文件夹下,如下图。
bert_pretrain文件里的前两个是bert的模型参数,vocab.txt里面是bert的分词所用到的训练好的字符。
代码解释:
1、导入bert和tokenizer
from transformers import AutoModel,AutoTokenizer
bert =AutoModel.from_pretrained('bert_pretrain')
tokenizer=AutoTokenizer.from_pretrained('bert_pretrain')
其实,这边还有其他的写法,但是个人偏向自己将模型参数下载下来使用。其他用法可以搜索“from_pretrain()函数”自行百度。![在这里插入图片描述](https://img-blog.csdnimg.cn/0b5a2fc3235d49bd9564f61796c45c22.png
2、分词和转换为字符id
text = "三个和尚在打架"
text = "[CLS] " + text + " [SEP]"
tokenized_text = tokenizer.tokenize(text) #分词,将句子切分成一个个字符
indexed_tokens1 = tokenizer.convert_tokens_to_ids(tokenized_text)#将字符转换为id
segments_ids1 = [1] * len(tokenized_text)
tokenizer.tokenize():对句子进行分词处理,分成一个个字,如下图:
tokenizer.convert_tokens_to_ids():将分好的每个字对应到bert词表的id,如下图
值得一提的是,对于单个句子,需要在句子的前面加’[CLS]‘和句子最后加’[SEP]‘,即’[CLS]’ + text + ‘[SEP]’,如果是两个句子,则要写成’[CLS]‘+sentence1+’[SEP]‘+sentence2+’[SEP]'的形式。segments_ids1对应的是bert论文中的Segment Embedding(个人是这么理解的),对于单句来说,全部置1,而如果是两个句子,则需要将’[CLS]‘到第一个’[SEP]'的位置置0,后面的置1。
3、bert编码
indexed_tokens1_tensor=torch.tensor([indexed_tokens1])
segments_ids1_tensor=torch.tensor([segments_ids1])
embedding=bert(indexed_tokens1_tensor,segments_ids1_tensor)[0] #词向量
sen=bert(indexed_tokens1_tensor,segments_ids1_tensor)[1] #句子向量
这边需要注意的点是,我们输入bert的数据必须是以二维tensor的形式。在前面得到的indexed_tokens1的数据是一维的list,所以需要在indexed_tokens1外面再加一层’[]'变成[indexed_tokens1]变成二维的list,再变成tensor。对于tensor,可以使用print(indexed_tokens1_tensor.shape)来观察其数据形式。这里的indexed_tokens1_tensor的数据形式是(1,sentence_len)。特别注意数据的维度,缺少某个维度会报错。
可以看到,经过bert编码得到的tensor多了个维度,原本的indexed_tokens1_tensor是(1,9),得到的embedding是(1,9,768),其实就是字符变成了向量,一个字符对应一个768维的向量。sen表示的是这个句子的向量,就是9个字合成了一句话,只用一个向量表示。具体的bert内部的代码,自己感兴趣的可以去看看源码,我反正看的头疼。
三、小结
以上就是bert编码的简单实现,有几个需要着重的点,我在文中都加粗显示了。再次强调一下,
1、bert_pretrain和你的.py放在同一目录下,或者你将bert_pretrain下载到电脑某个位置,找到该位置,直接复制过来,如’F:/my_file/bert_pretrain‘,则上面的代码改为:
bert=AutoModel.from_pretrained('F:/my_file/bert_pretrain')
2、text的前后需要加’[CLS]‘和’[SEP]’
3、特别要注意数据的维度问题,这是使用BERT的必须要注意的一个点,很多时候数据的形式不对,会导致很多问题。一定要小心应对。