当前位置:   article > 正文

NLP文本相似度(word2vec)的原理及实现_word2vec计算文本相似度

word2vec计算文本相似度

word2vec的原理及实现方式、训练优化方式前文已做详细介绍,这里不再累赘。这里主要记录NLP文本相似度(word2vec)怎样实现的及实现结果。

怎样实现:在问答系统(KBQA)中输入一个query,比如query为“贪污公款被捕后要还钱吗”?是怎么匹配到知识库中的最相近的一条问题并返回相对应的答案呢?

利用word2vec进行句子相似度计算,是先将输入query,进行分词,把目标句子的各个词的词向量进行相加取平均,从而把任意长的句子表示为固定维度的向量,然后计算两句子词嵌入之间的余弦相似度,进行相似度比较。当然,这么做虽然忽略了句子中的词顺序,但可以作为baseline简单衡量句子相似度计算以及词向量的训练效果。

一般情况下,在平均词嵌入之间求余弦相似性的word2vec方法表现得非常好,据了解比词移距离(Word Mover’s Distance)效果要好。

语料:爬取的问答语料

word2vec实现步骤:全局参数设置、文本预处理、训练词向量、计算相似度、得到最后的文本结果。

全局参数设置:

​主要设置编码方式,文件存储位置,word2vec参数设置。

数据预处理

训练之前需要对文本进行预处理:

  • 读取csv,提取问题及其答案,分词并按行保存到文件中,方便训练词向量。
  • 去停用词,这里由于要保留上下文信息,停用词大都包括标点符号及其他特殊字符。
  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. #停用词+分词+数据预处理
  4. import GrobalParament
  5. import jieba
  6. import pandas as pd
  7. #去掉回车行
  8. def delete_r_n(line):
  9. return line.replace('\r','').replace('\n','').strip()
  10. #读取停用词
  11. def get_stop_words(stop_words_dir):
  12. stop_words = []
  13. with open(stop_words_dir,'r',encoding=GrobalParament.encoding) as f_reader:
  14. for line in f_reader:
  15. line = delete_r_n(line)
  16. stop_words.append(line)
  17. stop_words = set(stop_words)
  18. return stop_words
  19. #jieba精准分词
  20. def jieba_cut(content,stop_words):
  21. word_list = []
  22. if content != '' and content is not None:
  23. seg_list = jieba.cut(content)
  24. for word in seg_list:
  25. if word not in stop_words:
  26. word_list.append(word)
  27. return word_list
  28. #结巴搜索引擎分词
  29. def jieba_cut_for_search(content,stop_words):
  30. word_list = []
  31. if content != '' and content is not None:
  32. seg_list = jieba.cut_for_search(content)
  33. for word in seg_list:
  34. if word not in stop_words:
  35. word_list.append(word)
  36. return word_list
  37. #清理不在词汇表中的词语
  38. def clear_word_from_vocab(word_list,vocab):
  39. new_word_list = []
  40. for word in word_list:
  41. if word in vocab:
  42. new_word_list.append(word)
  43. return new_word_list
  44. #文本预处理第一种方法Pandas
  45. def preprocessing_text_pd(text_dir,after_process_text_dir,stop_word_dir):
  46. stop_words = get_stop_words(stop_word_dir)
  47. sentences = []
  48. df = pd.read_csv(text_dir)
  49. for index,row in df.iterrows():
  50. print(index)
  51. title = delete_r_n(row['title'])
  52. word_list = jieba_cut(title,stop_words)
  53. df.loc[index,'title'] = ' '.join(word_list)
  54. sentences.append(word_list)
  55. df.to_csv(after_process_text_dir,encoding=GrobalParament.encoding,index=False)
  56. return sentences
  57. #文本预处理第二种方法
  58. def preprocessing_text(text_dir,after_process_text_dir,stop_word_dir):
  59. stop_words = get_stop_words(stop_word_dir)
  60. sentences = []
  61. f_writer = open(after_process_text_dir,'w',encoding=GrobalParament.encoding)
  62. # count = 0
  63. with open(text_dir,'r',encoding=GrobalParament.encoding) as f_reader:
  64. for line in f_reader:
  65. line_list = line.split(",")
  66. if len(line_list) == 2:
  67. line_list2 = delete_r_n(line_list[1])
  68. word_list = jieba_cut(line_list2,stop_words)
  69. sentences.append(word_list)
  70. f_writer.write(line_list[0] + "," + " ".join(word_list) + '\n')
  71. f_writer.flush()
  72. # count =count + 1
  73. # print(count)
  74. f_writer.close()
  75. return sentences
  76. if __name__ == "__main__":
  77. stop_words =get_stop_words(GrobalParament.stop_word_dir)
  78. # print(len(stop_words))
  79. # print(stop_words)
  80. # sentences = preprocessing_text(GrobalParament.test_set_dir, GrobalParament.test_after_process_text_dir,
  81. # GrobalParament.stop_word_dir)
  82. # print(sentences[:10])
  83. sentences = preprocessing_text(GrobalParament.test_set_dir,GrobalParament.test_after_process_text_dir,GrobalParament.stop_word_dir)
  84. print(sentences[:10])

代码中首先读取语料库文件data/train.csv和data/test.csv,提取目标文本,预处理后保存到data/train_after_process.csv和data/test_after_process.csv,

训练模型:

在理解词向量的训练原理和相关参数设置后,实现部分利用开源工具,过程相对简单。该部分使用gensim对上面预处理后的文本进行词向量的训练:

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. #训练模型
  4. import GrobalParament
  5. import utils
  6. from gensim.models import word2vec
  7. def train(sentences,model_out_put_path):
  8. print('开始训练')
  9. model = word2vec.Word2Vec(sentences=sentences,size=GrobalParament.train_size,window=GrobalParament.train_window,min_count=1)
  10. model.save(model_out_put_path)
  11. print('训练完成')
  12. if __name__ == "__main__":
  13. # sentences = utils.preprocessing_text(GrobalParament.train_set_dir,GrobalParament.train_after_process_text_dir,GrobalParament.stop_word_dir)
  14. # train(sentences,GrobalParament.model_output_path)
  15. model = word2vec.Word2Vec.load(GrobalParament.model_output_path)
  16. vocab = list(model.wv.vocab.keys())
  17. for e in model.most_similar(positive=['借贷'],topn=5):
  18. print(e[0],e[1])
  19. print(len(vocab))

这里使用默认的训练参数,几个代表性的参数:

  • 词向量的维度size=150
  • 上下文窗口window=5
  • CBOW模型、负采样

代码中首先用LineSentence将语料文本中的内容按行读入为词序列作为训练数据。接着对语料库进行5次迭代,并取词的min_count为1,即所有词汇都参与训练。最后保存训练好的模型。

相似度计算:

这里进行简单测试,从所有问题集作为目标句子,然后从读取测试集中的问题,从目标句子中匹配相似度前五的5个问题。

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. #计算相似度
  4. import GrobalParament
  5. import utils
  6. from gensim.models.word2vec import Word2Vec
  7. def calc_sim(model_path,train_path,test_path,out_result_path):
  8. model = Word2Vec.load(model_path)
  9. # vocab = list(model.wv.vocab.keys())
  10. #改进
  11. vocab = set(model.wv.vocab.keys())
  12. # print(len(vocab))
  13. # print(vocab[:20])
  14. #计算相似度
  15. f_write = open(out_result_path,'w',encoding=GrobalParament.encoding)
  16. with open(test_path,'r',encoding=GrobalParament.encoding) as f_test_reader:
  17. f_test_reader.readline()
  18. for test_line in f_test_reader:
  19. # print(test_line)
  20. test_line = utils.delete_r_n(test_line)
  21. test_line_list = test_line.split(',')
  22. print('测试集合:' + test_line_list[0])
  23. test_word_list = test_line_list[1].split()
  24. test_word_list = utils.clear_word_from_vocab(test_word_list,vocab)
  25. sim_score = dict()
  26. with open(train_path,'r',encoding=GrobalParament.encoding) as f_train_reader:
  27. f_train_reader.readline()
  28. for train_line in f_train_reader:
  29. train_line = utils.delete_r_n(train_line)
  30. train_line_list = train_line.split(',')
  31. if len(train_line_list) == 2:
  32. print('训练集合:' + train_line_list[0])
  33. train_word_list = train_line_list[1].split()
  34. train_word_list = utils.clear_word_from_vocab(train_word_list,vocab)
  35. if len(train_word_list) > 0:
  36. sim_score[train_line_list[0]] = model.n_similarity(test_word_list,train_word_list)
  37. sim_score = sorted(sim_score.items(),key=lambda d:d[1],reverse=True)
  38. #print(sim_score[:5])
  39. print('开始计算前5个最相似的')
  40. train_doc_num = ''
  41. for items in sim_score[:5]:
  42. train_doc_num = train_doc_num + items[0] + ' '
  43. f_write.write(test_line_list[0] + ','+train_doc_num.strip() + '\n')
  44. f_write.flush()
  45. f_write.close()
  46. if __name__ =="__main__":
  47. calc_sim(GrobalParament.model_output_path,GrobalParament.train_after_process_text_dir,GrobalParament.test_after_process_text_dir,GrobalParament.result_out_path)

load训练好的模型,利用n_similarity进行两个词序列的相似度计算,方法的输入参数是测试集csv文件中 句子分词后的词序列。

得到最后的文本结果:

​​​将​前一步骤得到相似结果的id转化为真正所对应的文本结果。

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. #最终结果
  4. import GrobalParament
  5. import utils
  6. #构建文档字典
  7. def build_dict(file_path):
  8. doc_dict = dict()
  9. with open(file_path,'r',encoding=GrobalParament.encoding) as f_reader:
  10. for line in f_reader:
  11. line = utils.delete_r_n(line)
  12. line_list = line.split(',')
  13. if len(line_list) == 2:
  14. doc_dict[line_list[0]] = line_list[1]
  15. return doc_dict
  16. def sim_result_out(sim_out_path,test_dict,train_dict,result_path):
  17. f_writer = open(result_path,'w',encoding=GrobalParament.encoding)
  18. with open(sim_out_path,'r',encoding=GrobalParament.encoding) as f_reader:
  19. for line in f_reader:
  20. line = utils.delete_r_n(line)
  21. line_list = line.split(',')
  22. if len(line_list) == 2:
  23. test_docid = line_list[0]
  24. sim_result = test_docid + ',' + test_dict[test_docid] + '\n'+'***最相似的前20个***\n'
  25. train_docid_list = line_list[1].split()
  26. for id in train_docid_list:
  27. sim_result = sim_result+ id + ',' +train_dict[id]+ '\n'
  28. f_writer.write(sim_result)
  29. f_writer.write('**********************************************\n')
  30. f_writer.close()
  31. if __name__ =="__main__":
  32. train_dict =build_dict(GrobalParament.train_set_dir)
  33. test_dict = build_dict(GrobalParament.test_set_dir)
  34. sim_result_out(GrobalParament.result_out_path,test_dict,train_dict,GrobalParament.sim_result_path)

随机测试几个问题所得到的结果:

  1. 1,贪公款被捕后要还钱吗
  2. ***最相似的前5***
  3. 1883,贪公款被捕后要还钱吗
  4. 3988,借了别人的钱还不起会坐牢吗
  5. 1847,贪污公款坐牢还需要还钱吗
  6. 1383,借债人突然去世钱还能要回来吗?
  7. 3263,碰到无赖亲戚不还钱怎么办
  8. *********************************************
  9. 2,贪污公款坐牢还需要还钱吗
  10. ***最相似的前5***
  11. 1847,贪污公款坐牢还需要还钱吗
  12. 1883,贪公款被捕后要还钱吗
  13. 24257,催人还钱,可以用这四大招
  14. 6853,捡到的东西多少钱以上构成犯罪
  15. 7197,遇离谱“好心人” 捡钱包只还证件不还钱
  16. *********************************************
  17. 3,2019欠钱不还能告他吗
  18. ***最相似的前5***
  19. 3301,2019欠钱不还能告他吗
  20. 44459,2015欠钱不还能告他吗
  21. 44728,2015别人欠钱不还怎么办
  22. 44643,2015欠钱不还可以报警吗
  23. 44658,2015欠钱不还会坐牢吗
  24. *********************************************

小结

这里简单对语料文本进行词向量的训练,并直接通过词序列向量求和取平均作为句子的向量表示,进行句子相似度的计算。这种方式简单粗暴,对于像文章中的短句,而且歧义少的文本有不错的效果。如果目标文本是长文,并且需要考虑词顺序,句子顺序的情况,这种简单的方法很难适应。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/881927
推荐阅读
相关标签
  

闽ICP备14008679号