bert不同句子中的词向量会变化吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bert不同句子中的词向量会变化吗相关的知识,希望对你有一定的参考价值。

参考技术A

我们都体会到了BERT预训练模型的强大,主要一点就是它可以动态生成句向量,根据不同的上下文而得到不同的句向量,当然也可以得到词向量,但是如果我想比较不同语境下的词向量该怎么做呢?比如这两句话“在手机品牌中,我喜欢苹果”和“在水果中,我喜欢苹果”中“苹果”一词的相似度,显然,此“苹果”非彼“苹果”,如果我直接将这两句话输入给bert-as-service,它输出的是这两句话的句向量,如果我们想验证“苹果”这个词向量的话,我们可以这么做。

下面的代码只需要把预训练模型地址更改就可以运行:

import torch
from pytorch_pretrained_bert import BertTokenizer,BertModel

text0 = '水果中很多对人有好处,比如苹果'   #句子0
text1 = '外国手机有很多都不错,比如苹果'   #句子1
text2 = '我喜欢在饭吃不同水果,比如苹果'   #句子2

marked_text0 = '[CLS]' + text0 + '[SEP]'   #因为BERT的输入是按照[CLS]和[SEP]来区分句子的,所以加上
marked_text1 = '[CLS]' + text1 + '[SEP]'
marked_text2 = '[CLS]' + text2 + '[SEP]'
tokenizer = BertTokenizer('/home/zhu/BERT-BiLSTM-CRF-NER-master/chinese_L-12_H-768_A-12/vocab.txt') #加载你的预训练词表地址
tokenized_text0 = tokenizer.tokenize(marked_text0)   #将输入按照BERT的处理方式进行分割
tokenized_text1 = tokenizer.tokenize(marked_text1)
tokenized_text2 = tokenizer.tokenize(marked_text2)
# print(tokenized_text0)
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text0)  #将字符映射到id
indexed_tokens1 = tokenizer.convert_tokens_to_ids(tokenized_text1)
indexed_tokens2 = tokenizer.convert_tokens_to_ids(tokenized_text2)
# for tup in zip(tokenized_text0,indexed_tokens):
#     print(tup)
segments_ids = [1] * len(tokenized_text0)   #segment_id是用来区分句子的,这里我们的输入是一句话一句话的输入,所以都标成1即可。
segments_ids1 = [1] * len(tokenized_text1)
segments_ids2 = [1] * len(indexed_tokens2)
tokens_tensor = torch.tensor([indexed_tokens])  #将输入转换成张量的形式
segments_tensors = torch.tensor([segments_ids])
tokens_tensor1 = torch.tensor([indexed_tokens1])
segments_tensors1 = torch.tensor([segments_ids1])
segments_tensors2 = torch.tensor([segments_ids2])
tokens_tensor2 = torch.tensor([indexed_tokens2])
model = BertModel.from_pretrained('/home/zhu/BERT-BiLSTM-CRF-NER-master/chinese_L-12_H-768_A-12') #加载预训练模型地址
model.eval()  #这里是验证模型,可以节省很多不必要的反向传播
with torch.no_grad():    #将输入传入模型,得到每一层的输出信息,这里的encoded_layers为12层,可以打印验证
encoded_layers,_ = model(tokens_tensor,segments_tensors)
with torch.no_grad():
encoded_layers1,_ = model(tokens_tensor1,segments_tensors1)
with torch.no_grad():
encoded_layers2,_ = model(tokens_tensor2,segments_tensors2)
# print(len(encoded_layers))
batch_i = 0  #因为我们就输入一句话,所以batch的批大小为1,从0开始索引
token_embeddings = []
for token_i in range(len(tokenized_text0)):   #这里是将一句话中每个字符的每一层信息添加到token_embedding集合中
hidden_layers = []
for layer_i in range(len(encoded_layers)):
vec = encoded_layers[layer_i][batch_i][token_i]
hidden_layers.append(vec)
token_embeddings.append(hidden_layers)
token_embeddings1 = []
for token_i in range(len(tokenized_text1)):
hidden_layers1 = []
for layer_i in range(len(encoded_layers1)):
vec = encoded_layers1[layer_i][batch_i][token_i]
hidden_layers1.append(vec)
token_embeddings1.append(hidden_layers1)

token_embeddings2 = []
for token_i in range(len(tokenized_text2)):
hidden_layers2 = []
for layer_i in range(len(encoded_layers2)):
vec = encoded_layers2[layer_i][batch_i][token_i]
hidden_layers2.append(vec)
token_embeddings2.append(hidden_layers2)

# 有了一句话的每一层输出信息后,我们可以制定如何去拼接这些信息,这里我们选择将最后四层输出层的信息想加
#concatenated_last_4_layers = [torch.cat((layer[-1],layer[-2],layer[-3],layer[-4]),0) for layer in token_embeddings]
summed_last_4_layers = [torch.sum(torch.stack(layer)[-4:],0) for layer in token_embeddings]
#concatenated_last_4_layers1 = [torch.cat((layer[-1],layer[-2],layer[-3],layer[-4]),0) for layer in token_embeddings1]
summed_last_4_layers1 = [torch.sum(torch.stack(layer)[-4:],0) for layer in token_embeddings1]

#concatenated_last_4_layers2 = [torch.cat((layer[-1],layer[-2],layer[-3],layer[-4]),0) for layer in token_embeddings2]
summed_last_4_layers2 = [torch.sum(torch.stack(layer)[-4:],0) for layer in token_embeddings2]
# print(sentence_embedding[0].shape[0])
# for i,x in enumerate(tokenized_text0):
#     print(i,x)
# for i,x in enumerate(tokenized_text1):
#     print(i,x)
# for i,x in enumerate(tokenized_text2):
#     print(i,x)
token_0 = (summed_last_4_layers[14] + summed_last_4_layers[15]) / 2    #找到苹果两个字符的索引,并相加取平均值得到我们最终“苹果”的词向量
token_1 = (summed_last_4_layers1[14] + summed_last_4_layers1[15]) / 2
token_2 = (summed_last_4_layers2[14] + summed_last_4_layers2[15]) / 2
from sklearn.metrics.pairwise import cosine_similarity
print(cosine_similarity(token_0.reshape(1,-1),token_1.reshape(1,-1))[0][0],'0和1')
print(cosine_similarity(token_0.reshape(1,-1),token_2.reshape(1,-1))[0][0],'0和2')
print(cosine_similarity(token_1.reshape(1,-1),token_2.reshape(1,-1))[0][0],'1和2')

运行结果如上图所示,很明显三句话中苹果的相似度比较相对来说符合常理,虽然苹果手机和水果苹果也有着75%的相似度,个人认为句式的影响也很重要。

以上是关于bert不同句子中的词向量会变化吗的主要内容,如果未能解决你的问题,请参考以下文章

使用BERT模型生成句子序列向量

ELMO,BERT和GPT简介

新书自然语言处理嵌入:语义向量表示理论与进展,从Word2Vec到BERT,163页pdf

word2vec是如何得到词向量的?

预训练句子表征——EMNLP 2019Sentence-BERT

句子编码和语境化词嵌入有啥区别?