基于word2vec提出了两个计算句子相似度的方法,一个是PV-DM,对应于word2vec中的CBOW算法,另一个是PV-DBOW,对应于word2vec中的Skip-gram算法。
想弄明白doc2vec,首先要搞清楚word2vec,这里对word2vec进行简要说明,word2vec简单的说就是实现了单词到向量空间的一种映射,通过word2vec生成的词向量使稠密的,并且在映射之后的向量中保留了一定的语义信息。例如V“King” - V“Man” + V"Woman" = V"Queen", V表示下标单词的词向量。
word2vec模型结构其实就是一个简单的神经网络,见下图:
我们最终用该神经网络的隐层权重来表示词向量。
在进行训练的时候有两种不同的训练方式:
和CBOW几乎相同,就是加一个段落矩阵和周围单词一起预测中心词,可以简单的将这个代表段落的向量当作一个表征段落信息的单词,他和其他表征单词的向量没有区别,但是要注意:上下文信息是按照固定长度在段落上根据滑动窗口不断采样,段落向量会被该段落产生的所有上下文窗口所共同拥有,但是不跨越段落,也就是说,不同段落的段落向量是不同的。但是不同段落的词向量是相同的。
PV-DBOW方法是在输入中忽略上下文单词,但是在输出中强制模型对段落中随机采样的单词进行预测。事实上,SGD的每一次迭代中,我们都会随机选择一个文本窗口,然后从这个文本窗口中随机采样一个单词并且构建一个基于段落向量的分类任务。
论文中表明,PV-DM通常可以取得很好的成绩在很多任务上,但是如果和PV-DBOW搭配的话(即每个段落向量由2部分组成:一个是通过标准段落向量(PV-DM)另一个是(PV-DBOW)),能对多个系统都取得更连续的好的成绩。
# Author Hans
import sys
import utils
import gensim
import sklearn
import numpy as np
import jieba
from gensim.models.doc2vec import Doc2Vec, LabeledSentence
from jieba import analyse
TaggededDocument = gensim.models.doc2vec.TaggedDocument
def jieba_cut(content, stop_words):
'''jieba精准分词(常用)
:param content: 要分词的句子
:param stop_words: 停用词集合或者列表
:return: 返回一个列表,包含content所分出来的词
'''
word_list = []
if content != '' and content is not None:
seg_list = jieba.cut(content)
for word in seg_list:
if word not in stop_words:
word_list.append(word)
return word_list
def get_stop_words(stop_words_dir):
'''读取停用词
:param stop_words_dir: 停用词文件路径
:return: 返回包含所有停用词的集合
'''
stop_words = []
with open(stop_words_dir, 'r', encoding=GlobalParaments.encoding) as f_reader:
for line in f_reader:
line = delete_r_n(line)
stop_words.append(line)
stop_words = set(stop_words)
return stop_words
def get_fencihou_dataset(path):
f = open(path, 'r', encoding='utf-8')
lines = f.readlines()
corpus = []
documents = []
# 建立语料库list文件(list中是已经分词后的)
for i, each in enumerate(lines):
text = list(each.replace('\n', '').split(' '))
# print(text)
document = TaggededDocument(text, tags=[i])
corpus.append(document)
print('语料库句子数:', len(corpus))
return corpus
def train(x_train, model_path , size=200, epoch_num=20, dm=1):
print('开始训练')
model_dm = Doc2Vec(x_train, min_count=10, window=5, size=size, sample=1e-3, negative=5, workers=4, dm=dm)
model_dm.train(x_train, total_examples=model_dm.corpus_count, epochs=epoch_num)
model_dm.save(model_path)
print('训练结束')
return model_dm
def ceshi(model_path, str):
model_dm = Doc2Vec.load(model_path)
test_text = ' '.join(jieba.cut(str)).split(' ')
print('test_text:', test_text)
inferred_vector_dm = model_dm.infer_vector(test_text)
print('inferred_vector_dm:', inferred_vector_dm)
# Gensim 中有内置的 most_similar,得到向量后,可以计算相似性
sims = model_dm.docvecs.most_similar([inferred_vector_dm], topn=10)
return sims
if __name__ == '__main__':
model_path = 'doc2vec_model.medel'
corpus = get_fencihou_dataset(r'fencihou.txt')
print(corpus[:20])
# 训练数据
model_dm = train(corpus, model_path=model_path)
# 测试模型
sims = ceshi(model_path=model_path, str='有向图和无向图是两个不同的概念。')
for count, sim in sims:
sentence = corpus[count]
words = ''
for word in sentence[0]:
words = words + word + ' '
print(words, sim, len(sentence[0]))