赞
踩
word2vec的原理及实现方式、训练优化方式前文已做详细介绍,这里不再累赘。这里主要记录NLP文本相似度(word2vec)怎样实现的及实现结果。
怎样实现:在问答系统(KBQA)中输入一个query,比如query为“贪污公款被捕后要还钱吗”?是怎么匹配到知识库中的最相近的一条问题并返回相对应的答案呢?
利用word2vec进行句子相似度计算,是先将输入query,进行分词,把目标句子的各个词的词向量进行相加取平均,从而把任意长的句子表示为固定维度的向量,然后计算两句子词嵌入之间的余弦相似度,进行相似度比较。当然,这么做虽然忽略了句子中的词顺序,但可以作为baseline简单衡量句子相似度计算以及词向量的训练效果。
一般情况下,在平均词嵌入之间求余弦相似性的word2vec方法表现得非常好,据了解比词移距离(Word Mover’s Distance)效果要好。
语料:爬取的问答语料
word2vec实现步骤:全局参数设置、文本预处理、训练词向量、计算相似度、得到最后的文本结果。
全局参数设置:
主要设置编码方式,文件存储位置,word2vec参数设置。
数据预处理
训练之前需要对文本进行预处理:
- #!/usr/bin/python
- # -*- coding: UTF-8 -*-
- #停用词+分词+数据预处理
-
- import GrobalParament
- import jieba
- import pandas as pd
-
- #去掉回车行
- def delete_r_n(line):
- return line.replace('\r','').replace('\n','').strip()
-
- #读取停用词
- def get_stop_words(stop_words_dir):
- stop_words = []
- with open(stop_words_dir,'r',encoding=GrobalParament.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
-
- #jieba精准分词
- def jieba_cut(content,stop_words):
- 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 jieba_cut_for_search(content,stop_words):
- word_list = []
- if content != '' and content is not None:
- seg_list = jieba.cut_for_search(content)
- for word in seg_list:
- if word not in stop_words:
- word_list.append(word)
- return word_list
-
- #清理不在词汇表中的词语
- def clear_word_from_vocab(word_list,vocab):
- new_word_list = []
- for word in word_list:
- if word in vocab:
- new_word_list.append(word)
- return new_word_list
-
- #文本预处理第一种方法Pandas
- def preprocessing_text_pd(text_dir,after_process_text_dir,stop_word_dir):
- stop_words = get_stop_words(stop_word_dir)
- sentences = []
- df = pd.read_csv(text_dir)
- for index,row in df.iterrows():
- print(index)
- title = delete_r_n(row['title'])
- word_list = jieba_cut(title,stop_words)
- df.loc[index,'title'] = ' '.join(word_list)
- sentences.append(word_list)
- df.to_csv(after_process_text_dir,encoding=GrobalParament.encoding,index=False)
- return sentences
-
- #文本预处理第二种方法
- def preprocessing_text(text_dir,after_process_text_dir,stop_word_dir):
- stop_words = get_stop_words(stop_word_dir)
- sentences = []
- f_writer = open(after_process_text_dir,'w',encoding=GrobalParament.encoding)
- # count = 0
- with open(text_dir,'r',encoding=GrobalParament.encoding) as f_reader:
- for line in f_reader:
- line_list = line.split(",")
- if len(line_list) == 2:
- line_list2 = delete_r_n(line_list[1])
- word_list = jieba_cut(line_list2,stop_words)
- sentences.append(word_list)
- f_writer.write(line_list[0] + "," + " ".join(word_list) + '\n')
- f_writer.flush()
- # count =count + 1
- # print(count)
- f_writer.close()
- return sentences
-
-
-
- if __name__ == "__main__":
- stop_words =get_stop_words(GrobalParament.stop_word_dir)
- # print(len(stop_words))
- # print(stop_words)
-
-
- # sentences = preprocessing_text(GrobalParament.test_set_dir, GrobalParament.test_after_process_text_dir,
- # GrobalParament.stop_word_dir)
- # print(sentences[:10])
- sentences = preprocessing_text(GrobalParament.test_set_dir,GrobalParament.test_after_process_text_dir,GrobalParament.stop_word_dir)
- print(sentences[:10])
代码中首先读取语料库文件data/train.csv和data/test.csv,提取目标文本,预处理后保存到data/train_after_process.csv和data/test_after_process.csv,
训练模型:
在理解词向量的训练原理和相关参数设置后,实现部分利用开源工具,过程相对简单。该部分使用gensim对上面预处理后的文本进行词向量的训练:
- #!/usr/bin/python
- # -*- coding: UTF-8 -*-
- #训练模型
-
- import GrobalParament
- import utils
- from gensim.models import word2vec
-
- def train(sentences,model_out_put_path):
- print('开始训练')
- model = word2vec.Word2Vec(sentences=sentences,size=GrobalParament.train_size,window=GrobalParament.train_window,min_count=1)
- model.save(model_out_put_path)
- print('训练完成')
-
- if __name__ == "__main__":
- # sentences = utils.preprocessing_text(GrobalParament.train_set_dir,GrobalParament.train_after_process_text_dir,GrobalParament.stop_word_dir)
- # train(sentences,GrobalParament.model_output_path)
-
- model = word2vec.Word2Vec.load(GrobalParament.model_output_path)
- vocab = list(model.wv.vocab.keys())
- for e in model.most_similar(positive=['借贷'],topn=5):
- print(e[0],e[1])
- print(len(vocab))
这里使用默认的训练参数,几个代表性的参数:
代码中首先用LineSentence将语料文本中的内容按行读入为词序列作为训练数据。接着对语料库进行5次迭代,并取词的min_count为1,即所有词汇都参与训练。最后保存训练好的模型。
相似度计算:
这里进行简单测试,从所有问题集作为目标句子,然后从读取测试集中的问题,从目标句子中匹配相似度前五的5个问题。
- #!/usr/bin/python
- # -*- coding: UTF-8 -*-
- #计算相似度
-
- import GrobalParament
- import utils
- from gensim.models.word2vec import Word2Vec
-
- def calc_sim(model_path,train_path,test_path,out_result_path):
- model = Word2Vec.load(model_path)
- # vocab = list(model.wv.vocab.keys())
- #改进
- vocab = set(model.wv.vocab.keys())
- # print(len(vocab))
- # print(vocab[:20])
-
- #计算相似度
- f_write = open(out_result_path,'w',encoding=GrobalParament.encoding)
- with open(test_path,'r',encoding=GrobalParament.encoding) as f_test_reader:
- f_test_reader.readline()
- for test_line in f_test_reader:
- # print(test_line)
- test_line = utils.delete_r_n(test_line)
- test_line_list = test_line.split(',')
- print('测试集合:' + test_line_list[0])
-
- test_word_list = test_line_list[1].split()
- test_word_list = utils.clear_word_from_vocab(test_word_list,vocab)
- sim_score = dict()
-
- with open(train_path,'r',encoding=GrobalParament.encoding) as f_train_reader:
- f_train_reader.readline()
- for train_line in f_train_reader:
- train_line = utils.delete_r_n(train_line)
- train_line_list = train_line.split(',')
- if len(train_line_list) == 2:
- print('训练集合:' + train_line_list[0])
- train_word_list = train_line_list[1].split()
- train_word_list = utils.clear_word_from_vocab(train_word_list,vocab)
-
- if len(train_word_list) > 0:
- sim_score[train_line_list[0]] = model.n_similarity(test_word_list,train_word_list)
- sim_score = sorted(sim_score.items(),key=lambda d:d[1],reverse=True)
- #print(sim_score[:5])
- print('开始计算前5个最相似的')
- train_doc_num = ''
- for items in sim_score[:5]:
- train_doc_num = train_doc_num + items[0] + ' '
- f_write.write(test_line_list[0] + ','+train_doc_num.strip() + '\n')
- f_write.flush()
- f_write.close()
-
-
- if __name__ =="__main__":
- 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转化为真正所对应的文本结果。
- #!/usr/bin/python
- # -*- coding: UTF-8 -*-
- #最终结果
-
- import GrobalParament
- import utils
-
- #构建文档字典
- def build_dict(file_path):
- doc_dict = dict()
- with open(file_path,'r',encoding=GrobalParament.encoding) as f_reader:
- for line in f_reader:
- line = utils.delete_r_n(line)
- line_list = line.split(',')
- if len(line_list) == 2:
- doc_dict[line_list[0]] = line_list[1]
-
- return doc_dict
-
- def sim_result_out(sim_out_path,test_dict,train_dict,result_path):
- f_writer = open(result_path,'w',encoding=GrobalParament.encoding)
- with open(sim_out_path,'r',encoding=GrobalParament.encoding) as f_reader:
- for line in f_reader:
- line = utils.delete_r_n(line)
- line_list = line.split(',')
- if len(line_list) == 2:
- test_docid = line_list[0]
- sim_result = test_docid + ',' + test_dict[test_docid] + '\n'+'***最相似的前20个***\n'
-
- train_docid_list = line_list[1].split()
- for id in train_docid_list:
- sim_result = sim_result+ id + ',' +train_dict[id]+ '\n'
- f_writer.write(sim_result)
- f_writer.write('**********************************************\n')
- f_writer.close()
-
-
- if __name__ =="__main__":
- train_dict =build_dict(GrobalParament.train_set_dir)
- test_dict = build_dict(GrobalParament.test_set_dir)
- sim_result_out(GrobalParament.result_out_path,test_dict,train_dict,GrobalParament.sim_result_path)
随机测试几个问题所得到的结果:
- 1,贪公款被捕后要还钱吗
- ***最相似的前5个***
- 1883,贪公款被捕后要还钱吗
- 3988,借了别人的钱还不起会坐牢吗
- 1847,贪污公款坐牢还需要还钱吗
- 1383,借债人突然去世钱还能要回来吗?
- 3263,碰到无赖亲戚不还钱怎么办
- *********************************************
- 2,贪污公款坐牢还需要还钱吗
- ***最相似的前5个***
- 1847,贪污公款坐牢还需要还钱吗
- 1883,贪公款被捕后要还钱吗
- 24257,催人还钱,可以用这四大招
- 6853,捡到的东西多少钱以上构成犯罪
- 7197,遇离谱“好心人” 捡钱包只还证件不还钱
- *********************************************
- 3,2019欠钱不还能告他吗
- ***最相似的前5个***
- 3301,2019欠钱不还能告他吗
- 44459,2015欠钱不还能告他吗
- 44728,2015别人欠钱不还怎么办
- 44643,2015欠钱不还可以报警吗
- 44658,2015欠钱不还会坐牢吗
- *********************************************
小结
这里简单对语料文本进行词向量的训练,并直接通过词序列向量求和取平均作为句子的向量表示,进行句子相似度的计算。这种方式简单粗暴,对于像文章中的短句,而且歧义少的文本有不错的效果。如果目标文本是长文,并且需要考虑词顺序,句子顺序的情况,这种简单的方法很难适应。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。