此篇文章只为学习交流,方便本人后续回顾查看,有需要的朋友可以参考以下步骤自行理解操作!
一、文本语义相似度,顾名思义,
就是判断两段文字的意思有多接近。比如:
-
“我喜欢猫” 和 “我讨厌猫” 意思相反,相似度低。
-
“我喜欢猫” 和 “我喜爱猫咪” 意思几乎一样,相似度高。
-
“我喜欢猫” 和 “今天天气不错” 话题无关,相似度很低。
二、为批量判定两句话甚至是两篇文章之间的相似度,本文最后选用模型
paraphrase-multilingual-MiniLM-L12-v2
当然也可以根据需要选用不同的模型,本人一开始也有考虑使用BERT-base模型,但考虑到它对多语言的适应能力并不够强,且该模型用起来不够轻量高效,对句子级别的感知能力也不够强,故选用paraphrase-multilingual-MiniLM-L12-v2。
(其实吧……为什么不坚持使用bert-base-chinese,是因为试过了觉得它真的不太合适用来做语义识别,但是它做命名实体识别任务还是非常优秀的!!!有不同意见的朋友也可以换着模型多去试试,上面仅是我个人的看法哈!)
这边给大家附上模型的链接,模型不大,但还是建议先下载到本地,后续进度会更快一些!
https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
三、在确定好自己喜欢的模型之后,就可以开始代码的实战训练(不理解的可以直接复制粘贴,运行完之后再回过头慢慢悟,这种学习效果可能会更好一些,反正我喜欢先调试成功再复盘!)
第一步:你需要安装下面代码中使用到的库,比如sentence-transformers、sklearn,如果你还用到别的库,自行安装就可以!
# -*- coding: utf-8 -*-
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
第二步:加载模型(模型下载到了本地的哪一个路径自行更换即可)
# 加载预训练的句子编码模型
model = SentenceTransformer("D:/paraphrase-multilingual-MiniLM-L12-v2")
第三步:输入需要判定语义相似度的两段文本
特别提示,为了本次语义识别任务适合处理长文本,在接下来的步骤中会设置文本分段处理识别(因为我一开始选的模型paraphrase-multilingual-MiniLM-L12-v2就是限制了512token,没办法,长文本只能分段……呜呜),所以在这一步中两段文本的字数均可自拟,不做字数要求。
# 两段文本
text1 = """我爱打篮球"""
text2 = """我不喜欢打篮球"""
第四步:这边提供文本分割函数,逻辑上也很简单,就是每段长文本都按句号进行划分,每句话的长度小于512token。
# 文本分割函数,按句子分割
def split_text(text):
sentences = text.split('。')
chunks = []
chunk = []
token_count = 0
for sentence in sentences:
tokens = model.encode([sentence])
if token_count + len(tokens[0]) <= 512:
chunk.append(sentence)
token_count += len(tokens[0])
else:
chunks.append(''.join(chunk))
chunk = [sentence]
token_count = len(tokens[0])
if chunk:
chunks.append(''.join(chunk))
return chunks
第五步:这里我解释一下下面写的代码吧,首先两个文本对应test1和test2,就是我们要计算相似度的那两段文本,每一段文本里面我们又把它们分成了很多小句,比如test1里面切出来的第一句是chunk1,那么我就把chunk1与test2中的每一句话都进行循环的相似度比较,如果比较出来chunk1与test2中的chunk2的相似度大于95%,那么我就把它提取出来显示。
PS:由于本人的任务只是想提取相似度高的句子,如果要得出最终test1和test2完整两段话的相似度,可以采用先计算出相似的字数再除以总字数的方法。(这个代码就不提供了,相关函数库里面都有,直接调用就可以。当然这个代码我也写过,后续有需要的再说,主要是时间久远,我得找一下……可能也许大概找不到了……)
如果只是短句,可以设置直接输出结果,没必要分句了哈!!!
# 计算两个文本的段落间相似度
def calculate_similarity(text1, text2, threshold=0.95):
chunks1 = split_text(text1)
chunks2 = split_text(text2)
# 对每段文本计算嵌入
embeddings1 = model.encode(chunks1)
embeddings2 = model.encode(chunks2)
# 计算相似度矩阵
similarity_matrix = cosine_similarity(embeddings1, embeddings2)
# 过滤出相似度大于 95% 的段落对
similar_pairs = []
for i in range(len(chunks1)):
for j in range(len(chunks2)):
if similarity_matrix[i, j] > threshold:
similar_pairs.append((chunks1[i], chunks2[j], similarity_matrix[i, j]))
return similar_pairs
# 获取相似度大于95%的段落对
similar_pairs = calculate_similarity(text1, text2, threshold=0.95)
第六步:输出最后的答案
# 输出相似度大于95%的段落对和字数比例
if similar_pairs:
print("Found similar chunks with similarity > 95%:")
for chunk1, chunk2, sim in similar_pairs:
print(f"\nText 1 Chunk: {chunk1}")
print(f"Text 2 Chunk: {chunk2}")
print(f"Similarity: {sim:.2f}")
写代码可能最难的不是写,而是重复的报错和调试。我一直认为,一遍一遍的运行,不是一次次的失败,而是一步一步向前走的证明,偶尔会觉得改来改去,兜兜转转又回到了原点,但其实,当代码成功运行后,回头看!咱走过的路每一步都算数!!!
如果上述步骤都正常运行的话,那么恭喜你,答案已经出现,剩下的就是代码的优化了,慢慢来吧!咱不要急。
Text 1 Chunk: 体重未发生明显改变
Text 2 Chunk: 体重未发生明显改变
Similarity: 1.00
文本1与文本2的相似度: 0.5218
报错问题:
1.模型没有提前下载到本地,直接安装容易报错,因为可能外网会有限制。
2.所需要的库没有安装好就运行代码,那肯定报错喽!如果有需要的库不能直接pip安装,可以通过github上找到相关的库下载到本地,代码中直接引用,就不会出现找不到相关库的问题啦!
欢迎大家有问题一起提出来交流解决!!!
一路生花,ByteWhispers!