2.1 计算文本相似度
Bert在大量语料库上进行预训练,学到了丰富的语言知识,并能为单词、短语、句子生成深层次的语义表示。通过使用 BERT 输出的[CLS]
标记来表示整个句子的语义信息,所以我们可以用Bert向量来计算文本语义相似度。具体代码如下:
from transformers import BertTokenizer, BertModel
import torch
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# 1. 加载 BERT 模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertModel.from_pretrained('bert-base-chinese')
# 2. 定义计算文本相似度的函数
def get_sentence_embedding(sentence):
# 对输入句子进行编码
inputs = tokenizer(sentence, return_tensors='pt', max_length=128,
truncation=True, padding='max_length')
# 使用 BERT 模型获取输出
with torch.no_grad():
outputs = model(**inputs)
# 提取 [CLS] 标记的向量(句子级别向量)
cls_embedding = outputs.last_hidden_state[:, 0, :].numpy()
return cls_embedding
def calculate_similarity(text1, text2):
# 计算两个文本的嵌入向量
embedding1 = get_sentence_embedding(text1)
embedding2 = get_sentence_embedding(text2)
# 计算余弦相似度
similarity = cosine_similarity(embedding1, embedding2)
return similarity[0][0]
# 3. 示例文本
text1 = "这个商品挺好用的"
text2 = "这个商品一点也不好用"
# 4. 计算相似度
similarity_score = calculate_similarity(text1, text2)
print(f"Similarity: {similarity_score:.4f}")
运行上述代码你会发现text1和text2这两个语义完全相反的文本的相似度却高达0.91(即使在GPT系列的embedding模型上也会出现类似情况)。这可能是由以下几个原因导致的:
- 模型无法捕捉否定关系:BERT 等预训练模型的主要任务是捕捉词语和句子中的语义关系,但它们在处理语义反转(如“我喜欢”与“我不喜欢”)时,可能难以充分理解否定词的影响,导致两者生成的嵌入向量仍然较为接近。
- 语义结构相似性:即便两个句子的语义相反,它们可能在结构上非常相似,例如“我喜欢苹果”和“我讨厌苹果”在句法和词汇上只有否定词的不同,因此生成的嵌入向量仍然较接近。
- 预训练数据的局限:BERT 在大量通用语料上进行预训练,未必对所有语境中的细微差异或否定词有足够的敏感性,这会导致某些反向语义的文本仍有较高的相似度。
Bert提供了一个变体模型sentence-bert来专门生成句子的特征,计算句子的文本相似度。其用法举例如下:
from sentence_transformers import SentenceTransformer, util
# 加载Sentence-BERT模型
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
# 定义两个文本
text1 = "这个商品挺好用的"
text2 = "这个商品一点也不好用"
# 使用模型生成文本的embedding
embedding1 = model.encode(text1, convert_to_tensor=True)
embedding2 = model.encode(text2, convert_to_tensor=True)
# 计算两个文本的余弦相似度
similarity_score = util.pytorch_cos_sim(embedding1, embedding2)
# 输出相似度结果
print(f"文本相似度: {similarity_score.item():.4f}") #0.8351