为何相同分词词列表下Doc2Vec的相似度结果异常?
问题:Doc2Vec计算相似句子余弦相似度接近0的排查与修复
我使用Doc2Vec模型,基于2万余篇维基百科文章完成预训练。测试模型效果时,选用了两句语义几乎完全一致的句子:
- The process of searching for a job can be very stressful.
- The job search process can be very stressful.
预处理分词后得到:
- list_1 = ['process', 'search', 'job', 'stress']
- list_2 = ['job', 'search', 'process', 'stress']
但通过doc2vec_model.infer_vector()生成向量,再用gensim.matutils.full2sparse转换为稀疏向量后计算余弦相似度,结果却接近0(如0.00709335870),与预期的接近1相差甚远。相关代码如下:
# model.tokenize_word(data['document_1'] 返回 ['process', 'search', 'job', 'stress'] vec_1 = doc2vec_model.infer_vector(model.tokenize_word(data['document_1'])) doc2vec_model.random.seed(0) # model.tokenize_word(data['document_2'] 返回 ['job', 'search', 'process', 'stress'] vec_2 = doc2vec_model.infer_vector(model.tokenize_word(data['document_2'])) vec_1 = gensim.matutils.full2sparse(vec_1) vec_2 = gensim.matutils.full2sparse(vec_2) similarity = gensim.matutils.cossim(vec_1, vec_2) print(similarity) # 输出 0.00709335870
问题原因
- 随机种子设置时机错误:生成vec_1之后才设置随机种子,导致两次向量推断使用了不同的初始随机状态。Doc2Vec的
infer_vector()依赖随机初始化的向量开始迭代,不同种子会让最终生成的向量差异极大。 - 冗余的稀疏转换:
infer_vector()返回的是密集向量,gensim.matutils.cossim本身支持直接计算密集向量的余弦相似度,转换成稀疏向量完全没必要,甚至可能引入计算误差。 - 分词过度精简:原始句子被大量裁剪,仅保留4个核心词,上下文信息严重不足。Doc2Vec需要足够的文本长度来捕捉语义,过短的输入会让模型难以生成符合预期的向量。
修复方案
- 统一随机种子时机:在调用
infer_vector()之前设置随机种子,确保两次推断使用相同的初始状态:
doc2vec_model.random.seed(0) vec_1 = doc2vec_model.infer_vector(model.tokenize_word(data['document_1'])) vec_2 = doc2vec_model.infer_vector(model.tokenize_word(data['document_2']))
- 移除稀疏转换步骤:直接用密集向量计算相似度:
similarity = gensim.matutils.cossim(vec_1, vec_2)
也可以用numpy直接计算,结果更直观:
from numpy import dot from numpy.linalg import norm similarity = dot(vec_1, vec_2) / (norm(vec_1) * norm(vec_2))
- 优化分词策略:保留必要的功能词,避免过度精简。比如分词后保留更多上下文词汇(需与模型训练时的预处理逻辑一致,比如词形还原、小写转换):
- list_1 = ['process', 'of', 'searching', 'for', 'job', 'can', 'be', 'very', 'stressful']
- list_2 = ['job', 'search', 'process', 'can', 'be', 'very', 'stressful']
- 增加推断迭代次数:默认
infer_vector()迭代次数为5,针对短文本可调高至20次左右,让模型更充分地调整向量:
vec_1 = doc2vec_model.infer_vector(model.tokenize_word(data['document_1']), steps=20) vec_2 = doc2vec_model.infer_vector(model.tokenize_word(data['document_2']), steps=20)
内容的提问来源于stack exchange,提问作者Nguyễn Thanh Huyền
相关产品推荐
相关产品推荐

