1. 程式人生 > 其它 >比較兩個向量的相似度_比較不同語境下BERT生成的詞向量相似度

比較兩個向量的相似度_比較不同語境下BERT生成的詞向量相似度

技術標籤:比較兩個向量的相似度

我們都體會到了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')

5ae4b3aace65347ece74843152db4d3b.png

執行結果如上圖所示,很明顯三句話中蘋果的相似度比較相對來說符合常理,雖然蘋果手機和水果蘋果也有著75%的相似度,個人認為句式的影響也很重要。