基于Doc2vec的跨文档段落语义匹配及相似度百分比换算问题
基于Gensim Doc2Vec的语义相似文档/段落查找方案
参考实现代码
目标是从候选文档列表中匹配与目标段落语义最接近的内容,基于Gensim Doc2Vec的实现如下:
import os import sys import gensim import smart_open import random from nltk.tokenize import word_tokenize # 配置文件路径 test_data_dir =('C:\\Users\\hamza\\Desktop\\') train_file = os.path.join(test_data_dir, 'read-me.txt') target_file = os.path.join(test_data_dir, 'read-me2.txt') def read_file(filename): try: with open(filename, 'r') as f: data = f.read() return data except IOError: print("Error opening or reading input file: ", filename) sys.exit() def read_corpus(fname, tokens_only=False): with smart_open.open(fname, encoding="iso-8859-1") as f: for i, line in enumerate(f): tokens = gensim.utils.simple_preprocess(line) if tokens_only: yield tokens else: # 训练数据需要附加唯一标签,这里用行号作为标签 yield gensim.models.doc2vec.TaggedDocument(tokens, [i]) train_data = list(read_corpus(train_file)) target_data = word_tokenize(read_file(target_file)) # 初始化并训练模型 model = gensim.models.doc2vec.Doc2Vec(vector_size=50, min_count=2, epochs=40) model.build_vocab(train_data) model.train(train_data, total_examples=model.corpus_count, epochs=model.epochs) # 推理目标文本向量,计算相似度 inferred_vector = model.infer_vector(target_data) sims = model.dv.most_similar([inferred_vector], topn=len(model.dv)) print(sims)
注:原代码缺少
sys模块导入,上述代码已补全,避免运行时报错。
示例运行输出
[(1, 0.20419511198997498), (2, 0.1924923211336136), (0, 0.10696495324373245)]
核心问题解答
1. 目标数据与训练数据的匹配方法
现有代码已经实现了完整匹配逻辑,匹配结果的映射规则非常直接:
- 训练阶段给
read-me.txt的每一行都分配了从0开始的整数标签(行号),模型训练完成后会存储每个标签对应的段落向量 most_similar返回的元组第一个元素就是匹配到的段落标签,直接用标签索引train_data就能拿到对应的原始段落内容- 以上述输出为例,排名第一的结果标签为1,对应
read-me.txt中第2行(行号从0开始计数)的内容,就是和目标文本语义最接近的候选段落。
如果要做文档级而非段落级匹配,只需要在读取语料时把整篇文档作为一个TaggedDocument单元,给每个文档分配唯一ID标签即可。
2. 相似程度的判定规则
Doc2Vec默认采用余弦相似度计算向量间的匹配程度,原始取值范围为[-1, 1]:
- 数值越接近1,两段文本的语义相似度越高
- 数值为0代表两段文本语义无关联
- 数值为负代表两段文本语义倾向相反
这个相似度是相对值而非绝对值,判定阈值需要结合自己的语料场景调整:通用场景下一般认为相似度高于0.6属于高度语义相关,0.3~0.6属于弱相关,低于0.3基本可以判定为不相关。当前输出的最高相似度仅0.2左右,通常是训练语料量过少、目标文本和候选语料重合度低导致的,可以通过扩充训练语料、调整模型参数优化效果。
3. 相似度缩放为百分比的可行方法
余弦相似度的取值范围包含负数,不能直接乘以100作为百分比,需要先做值域映射,把原始分数线性转换到[0,1]区间后再转百分比,参考实现如下:
def sim2percent(sim_score): # 将[-1,1]的余弦值线性映射到[0,1]区间 scaled_score = (sim_score + 1) / 2 return round(scaled_score * 100, 2) # 批量转换相似度结果 percent_sims = [(doc_tag, sim2percent(score)) for doc_tag, score in sims] print(percent_sims)
转换后当前的运行结果会输出:
[(1, 60.21), (2, 59.62), (0, 55.35)]
注意:转换后的百分比仅代表当前模型、当前候选集范围内的相对匹配程度,不是绝对语义重合度,不能跨模型、跨候选集直接对比数值高低。
内容的提问来源于stack exchange,提问作者Hamza Dabeer khan
相关产品推荐
相关产品推荐

