基於word2vec的QA demo

問答系統

基於詞向量的相似度計算,在問答對字典裏返回最相近問題的答案

# -*- coding: utf-8 -*-

import docx
import sys
import jieba
from gensim.models import Word2Vec
import os
import numpy as np
import pickle as pk
import warnings
import gensim
from copy import deepcopy
warnings.filterwarnings("ignore")
from multiprocessing import cpu_count


class project(object):
    def __init__(self,path):
        self.path=path
    def load_text(self,file_path):
        doc = docx.Document(file_path)
        texts = []
        for p in doc.paragraphs:
            if len(p.text)>0:
                texts.append(p.text)

   # 讀入表格內容
   #  for t in doc.tables:
   #      for r in t.rows:
   #          tmp=[]
   #          for c in r.cells:
   #              tmp.append(c.text.decode('utf-8'))
   #          texts.append(' '.join(tmp))
        return texts
    def load_stopword(self):
        with open('stopword.txt','r') as f:
            sw=[line.strip() for line in f.readlines()]
        return sw

    def cal_cos(self,array1,array2):
        return np.dot(array1, array2) / (np.sqrt(np.sum(array1 ** 2)) * np.sqrt(np.sum(array2 ** 2)))

    def load_file(self,filename):
        with open(filename, 'rb') as f:
            original_sentences = pk.load(f)
        return original_sentences

    def save_file(self,texts,filename):
            with open(filename+'.pkl', 'wb') as f:
                pk.dump(texts, f, True)

    def dict_produce(self,texts,original_id=''):
        dict_num_text = {}
        if len(original_id)>0:
            for num, text in enumerate(texts):
                dict_num_text[original_id[num]] = text
        else:
            for num, text in enumerate(texts):
                dict_num_text[num] = text
        return dict_num_text

    def jie_ba(self,texts,mode='train',id_connect=False):
        '''
        結巴分詞,如果mode是train 則對訓練的語料分詞,否則對提問作分詞。
        '''
        stoping_words=self.load_stopword()
        jieba_text=[]
        index = []
        if mode == 'train':
            flag = 0
            for text in texts:  # 注意是在texts裏
                blank_connect_words = ' '.join(jieba.cut(text))  # 如果用lcut的話 空格會佔用list一個位置。
                list_word = [str(word) for word in blank_connect_words.strip().split() if word not in stoping_words]
                if len(list_word)>3:#分詞侯大於3個 才添加
                    jieba_text.append(list_word)
                    if id_connect:
                        index.append(flag)
                flag += 1

        # 對提問的切詞
        else:
            blank_connect_words = ' '.join(jieba.cut(texts.strip()))  # 輸入的是一個句子.
            jieba_text = [word for word in blank_connect_words.strip().split()]
            if len(jieba_text) ==0:
                print (u'輸入問題不能爲空')
                return
        return jieba_text,index

    def word_vector(self,index2word):
        dict_word_vector = {}
        doc_word = []
        for word in model.wv.index2word:
            dict_word_vector[word] = model[word]
            doc_word.append(word)
        return dict_word_vector

    def sentence_vector(self,dict_word_vector):
        '''
        :return:返回 一個字典 sentences_vector{原始句子id:句子的向量表示(每個詞的向量求了均值)}
        '''
        sentences_vector={}
        # 此處的num就是對應了最原始句子對應的情況

        sentences = self.load_file('num_jieba_text.pkl')
         #結巴的詞如果不在word2vector的詞典中,還是會缺失
        for origin_id, sentence_item in sentences.items():
            tmp = []
            for word in sentence_item:
                #此處如果在訓練的詞典裏沒有這個詞,就不用管了
                get_word=dict_word_vector.get(word)
                if get_word is not None:
                    tmp.append(get_word)
            if len(tmp)!=0:
                mean_vector = np.mean(tmp, axis=0)
                sentences_vector[origin_id] = mean_vector
        return sentences_vector



    def question_answer(self,inputs,dict_word_vector, dict_jeiba_origid_vector,model,answer_num=3):
        '''
        :param input: 輸入單個問題
        :param dict_word_vector:  word2vector訓練後的詞向量,格式是dict{單詞:向量}
        :param dict_jeiba_origid_vector:   dict{最原始句子的id:結巴分詞後句子的向量}
        :param model:   輸入訓練好的模型
        :return:  輸入的問題轉換爲句子的向量,尋找dict_origid_vector中相似度最高的句子對應的key,返回原始句子[key]
        '''
        if len(inputs)==0:
            print ('輸入不能爲空哦!')
            return

        original_sentences=self.load_file('num_text.pkl')
        jieba_sentences = self.load_file('num_jieba_text.pkl')
        for input in inputs:
            question,_ =self.jie_ba(input, 'question')  #此時返回的是 list
            word_list= list(dict_word_vector.keys())


            original_index = list(dict_jeiba_origid_vector.keys())
            dict_values = np.array(list(dict_jeiba_origid_vector.values()))
            #問題轉化爲詞向量

            record_vector = []  # 紀錄問題的詞向量
            for item in question:
                if item  in word_list:
                    record_vector.append(dict_word_vector.get(item))
            if len(record_vector)==0:
                print('Q:  ', input)
                print('您的問題在訓練語料中可能沒有出現過,請確認您的問題描述\n')
             #句子的詞向量
            else:
                  #得到輸入句子的向量
                sentence_vector = np.mean(record_vector, axis=0)

                lis=[]
                for i in dict_values:
                    lis.append(self.cal_cos(i,sentence_vector))


                sort_list = np.argsort(lis)
                print('Q:  ', input)

            # 從word2vector尋找的候選集裏, 找出最相似的一個結果,作爲問題在原文中的定位
                simi_num=-1
                set_len_num=-1
                for i in range(7):
                    _index=original_index[sort_list[-1-i]]
                    _len=len(set(jieba_sentences[_index]).intersection(set(question)))
                    if _len>set_len_num:
                        set_len_num=_len
                        simi_num = _index

             # 定位後的問題,輸出之後結巴詞大於 answer_num 個的句子。
                max_index=len(original_sentences)
                num=0
                index = simi_num + num
                while index<max_index and num<answer_num:
                    is_exist_jeiba=jieba_sentences.get(index)
                    if is_exist_jeiba is not None:
                        if  len(is_exist_jeiba)>5:  #結巴詞大於 5個才輸出
                            # print ( 'A{}:\t'.format(index), original_sentences[index])
                            print(original_sentences[index])
                            num+=1
                    index = index+1
                print('')


    def pre_processing(self):
        print (u"加載停用詞")
        stoping_words=self.load_stopword()
        print (u"加載文本")
        texts = self.load_text(self.path)  # 讀入的doc文件的每一行
        print (u'原始語料的行數')
        #語料處理成 dict{id:sentence}並保存
        dict_num_original_senteces=self.dict_produce(texts)
        self.save_file(dict_num_original_senteces, 'num_text')
        #句子結巴分詞並且保存結果
        jieba_text,original_id=self.jie_ba(texts,id_connect=True)
        #結巴分詞句子和原始句子的對應關係,並且保存
        dict_num_jieba=self.dict_produce(jieba_text,original_id)
        self.save_file(dict_num_jieba, 'num_jieba_text')

class LoadCorpora(project):
    def __init__(self, s):
        self.path = s
    def __iter__(self):
        setences =self.load_file(self.path)
        for num, texts in setences.items():
            yield ' '.join(texts).split(' ')

if __name__=='__main__':
    # file_path=u"AS.docx"
    file_path = 'CC雷達.doc'
    ques_and_answer = project(file_path)

    if not os.path.exists('num_jieba_text.pkl'):
        #處理原始語料
        ques_and_answer.pre_processing()

    if not os.path.exists('word2vec.model'):
        print (u'開始建立模型---')
        sentences =LoadCorpora('num_jieba_text.pkl')
        model = Word2Vec(sentences, size=250, min_count=3, workers=cpu_count(),iter=10) # 詞向量維度爲200,丟棄出現次數少於min_count次的詞 #注意workers在不同機器上的取值。
        copy_model=deepcopy(model)
        copy_model.save('word2vec.model')

    model = Word2Vec.load('word2vec.model')
    dict_word_vector=ques_and_answer.word_vector(model.wv.index2word)
    dict_id_vector=ques_and_answer.sentence_vector(dict_word_vector)

    query = ['CC雷達伺服分系統是什麼',
             '天線方位或仰角定位不準,怎麼維修?',
             '伺服不能啓動,咋整?',
             '可控硅風機怎麼辦',
             '冷卻開關脫扣處理方法',
             '俯仰電源故障21#故障解決辦法',
             '俯仰電源故障16#故障問題出在哪裏',
             '控制按鈕壞了,咋整?',
             '譬如秋風忽至,再有一場早霜',
             '我纔想到,當年我總是獨自跑到地壇去,曾經給母親出了一個怎樣的難題。',
             '你中午喫的什麼?']
    ques_and_answer.question_answer(query,dict_word_vector, dict_id_vector,model,answer_num=1)

效果如下:

Q:   CC雷達伺服分系統是什麼
CC雷達伺服分系統可以接收綜合機櫃中伺服分機控制鍵盤發出的操作指令或雷達終端室主微機、監控微機經監控分系統送來的操作指令,經過相應軟件的運算和處理,產生驅動信號去控制天線作多種方式的掃描運動,以滿足氣象探測的需要,同時接收天線位置(方位角和仰角)信息,並經過量化後送往信號處理分系統。

Q:   天線方位或仰角定位不準,怎麼維修?
3. 天線方位或仰角定位不準-----R/D板,減速機間隙

Q:   伺服不能啓動,咋整?
伺服開機後,伺服控制不能啓動,本地方位和仰角無顯示;

Q:   可控硅風機怎麼辦
可控硅風機損壞,不能正常轉動、

Q:   冷卻開關脫扣處理方法
在發射機櫃I最上邊配電分機中有一個控制冷卻電源的空氣開關和一個輔助節點,當該空氣開關斷開時,其輔助節點也隨之斷開,雷達發射機報冷卻開關脫扣故障,高壓自動斷開

Q:   俯仰電源故障21#故障解決辦法
1. 俯仰電源故障21#故障-----匯流環(9-18環)

Q:   俯仰電源故障16#故障問題出在哪裏
打開伺服分機本地顯示面板,觀察俯仰驅動器上的故障號爲16#故障;

Q:   控制按鈕壞了,咋整?
使用示波器測試雷達伺服數據電纜插頭中第 腳(監控發串口),在終端上發送伺服控制指令,同時在這裏沒有測試到監控串口發出的控制信號;(如果能測試到控制信號,則可能是伺服控制板損壞,雷達伺服控制板)

Q:   譬如秋風忽至,再有一場早霜
您的問題在訓練語料中可能沒有出現過,請確認您的問題描述

Q:   我纔想到,當年我總是獨自跑到地壇去,曾經給母親出了一個怎樣的難題。
將充氣機開啓,在波導接頭處塗上肥皂水,觀察沒有氣泡冒出,波導不存在漏氣現象;(如果波導接頭處有氣泡冒出,則該波導接頭出漏氣,將波導斷開後,更換橡膠密封圈重新連接)

Q:   你中午喫的什麼?
您的問題在訓練語料中可能沒有出現過,請確認您的問題描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章