引言
本部分任務(wù)主要是將用戶輸入問答系統(tǒng)的自然語(yǔ)言轉(zhuǎn)化成知識(shí)庫(kù)的查詢語(yǔ)句,因此本文將分成兩部分進(jìn)行介紹。
- 第一部分介紹任務(wù)所涉及的背景知識(shí);
- 第二部分則是相應(yīng)的代碼和其注釋
問答系統(tǒng)
問答系統(tǒng)簡(jiǎn)介
問答系統(tǒng)(Question Answering System,QA System),是未來(lái)自然語(yǔ)言處理的明日之星。問答系統(tǒng)外部的行為上來(lái)看,其與目前主流資訊檢索技術(shù)有兩點(diǎn)不同:首先是查詢方式為完整而口語(yǔ)化的問句,再來(lái)則是其回傳的為高精準(zhǔn)度網(wǎng)頁(yè)結(jié)果或明確的答案字串。以Ask Jeeves為例,使用者不需要思考該使用什么樣的問法才能夠得到理想的答案,只需要用口語(yǔ)化的方式直接提問如“請(qǐng)問誰(shuí)是美國(guó)總統(tǒng)?”即可。而系統(tǒng)在了解使用者問句后,會(huì)非常清楚地回答特朗普是美國(guó)總統(tǒng)。面對(duì)這種系統(tǒng),使用者不需要費(fèi)心去一一檢視搜索引擎回傳的網(wǎng)頁(yè),對(duì)于資訊檢索的效率與資訊的普及都有很大幫助。從系統(tǒng)內(nèi)部來(lái)看,問答系統(tǒng)使用了大量有別于傳統(tǒng)資訊檢索系統(tǒng)自然語(yǔ)言處理技術(shù),如自然語(yǔ)言剖析(Natural Language Parsing)、問題分類(Question Classification)、專名辨識(shí)(Named Entity Recognition)等等。少數(shù)系統(tǒng)甚至?xí)褂脧?fù)雜的邏輯推理機(jī)制,來(lái)區(qū)隔出需要推理機(jī)制才能夠區(qū)隔出來(lái)的答案。在系統(tǒng)所使用的資料上,除了傳統(tǒng)資訊檢索會(huì)使用到的資料外(如字典),問答系統(tǒng)還會(huì)使用本體論等語(yǔ)義資料,或者利用網(wǎng)頁(yè)來(lái)增加資料的豐富性。
截至目前為止,最著名的問答系統(tǒng)應(yīng)屬IBM的沃森系統(tǒng)。該系統(tǒng)在2011年于Jeopardy節(jié)目中,與人類同場(chǎng)較勁,并獲得最后的勝利。
問答系統(tǒng)分類
從知識(shí)領(lǐng)域劃分,可分為封閉領(lǐng)域以及開放領(lǐng)域兩類系統(tǒng)。封閉領(lǐng)域系統(tǒng)專注于回答特定領(lǐng)域的問題,如醫(yī)藥或特定公司等。由于問題領(lǐng)域受限,系統(tǒng)有比較大的發(fā)揮空間,可以導(dǎo)入如專屬本體論等知識(shí),或?qū)⒋鸢竵?lái)源全部轉(zhuǎn)換成結(jié)構(gòu)性資料,來(lái)有效提升系統(tǒng)的表現(xiàn)。開放領(lǐng)域系統(tǒng)則希望不設(shè)限問題的內(nèi)容范圍,天文地理無(wú)所不問。系統(tǒng)中所有知識(shí)與元件都必須盡量做到與領(lǐng)域不相關(guān),當(dāng)然難度也相對(duì)地提高。
-
從答案來(lái)源劃分,可分為“數(shù)據(jù)庫(kù)問答”、“常問問題問答”、“新聞問答”、“互聯(lián)網(wǎng)問答”等系統(tǒng)。數(shù)據(jù)庫(kù)是最常見的結(jié)構(gòu)化資料儲(chǔ)存媒介。雖然透過(guò)操控SQL語(yǔ)言便能夠有效率地存取資料,但有些系統(tǒng)試圖提供更直覺的自然語(yǔ)言查詢界面,希望能進(jìn)一步降低學(xué)習(xí)門檻。1970年代的LUNAR系統(tǒng)算是早期成功的案例,其正確答題率可以達(dá)到百分之七十,可回答月球隕石相關(guān)資料。微軟的English Query則是近期的一個(gè)商業(yè)產(chǎn)品。English Query在剖析完英文問句后,會(huì)根據(jù)底層數(shù)據(jù)庫(kù)結(jié)構(gòu),自動(dòng)產(chǎn)生出相對(duì)應(yīng)的SQL查詢。雖然有這些成功系統(tǒng)案例,但數(shù)據(jù)庫(kù)問答系統(tǒng)似乎很難被大眾所接受,其中一個(gè)因素可能是因?yàn)閷?duì)于結(jié)構(gòu)化資料來(lái)說(shuō),結(jié)構(gòu)化的查詢界面在查詢上更為方便。常問問題(Frequently Asked Questions, FAQs)是公司或者長(zhǎng)期經(jīng)營(yíng)領(lǐng)域中常見的重要資源。一份FAQ資料包含了一個(gè)問句以及相對(duì)應(yīng)的答案描述。FAQ問答系統(tǒng)的主要責(zé)任在比對(duì)使用者問句與現(xiàn)有FAQ問句的相似度,此與其他問答系統(tǒng)著重在答案語(yǔ)料中擷取答案的作法不同。另一種重要的系統(tǒng)為新聞問答系統(tǒng)。今日新聞媒體都已經(jīng)數(shù)字化了,每日累積所產(chǎn)生的新聞資訊量是相當(dāng)可觀的,加上新聞的內(nèi)容廣泛豐富,作為開放領(lǐng)域問答系統(tǒng)的答案來(lái)源是最適合不過(guò)的。這樣的特性使得此類系統(tǒng)的評(píng)估較為容易,因此稍后會(huì)提到的國(guó)際評(píng)估會(huì)議都是采用此類系統(tǒng)作為評(píng)估對(duì)象。最后一類的是互聯(lián)網(wǎng)問答系統(tǒng),這些系統(tǒng)利用搜索引擎回傳的結(jié)果網(wǎng)頁(yè),從中擷取答案。主要挑戰(zhàn)在于如何處理網(wǎng)絡(luò)多異質(zhì)性的資料,以及高噪聲網(wǎng)頁(yè)過(guò)濾等問題。
知識(shí)庫(kù)問答 -
從實(shí)現(xiàn)方式劃分,可分為基于流水線和端到端兩類?;诹魉€(pipeline)實(shí)現(xiàn):如下圖 1 所示,基于流水線實(shí)現(xiàn)的問答系統(tǒng)有四大核心模塊,分別由自然語(yǔ)言理解(NLU)、對(duì)話狀態(tài)跟蹤器(DST)、對(duì)話策略(DPL)和自然語(yǔ)言生成(NLG)依次串聯(lián)構(gòu)成的一條流水線,各模塊可獨(dú)立設(shè)計(jì),模塊間協(xié)作完成任務(wù)?;诙说蕉耍╡nd-to-end)實(shí)現(xiàn):基于端到端實(shí)現(xiàn)的問答系統(tǒng),主要是結(jié)合深度學(xué)習(xí)技術(shù),通過(guò)海量數(shù)據(jù)訓(xùn)練,挖掘出從用戶自然語(yǔ)言輸入到系統(tǒng)自然語(yǔ)言輸出的整體映射關(guān)系,而忽略中間過(guò)程的一種方法。但就目前工業(yè)界整體應(yīng)用而言,工業(yè)界的問答系統(tǒng)目前大多采用的還是基于流水線實(shí)現(xiàn)的方式。
流水線
Query理解
什么是Query理解
query理解是整個(gè)搜索系統(tǒng)中最上游的一環(huán),負(fù)責(zé)的是從query中提取信息,從而了解用戶希望通過(guò)這個(gè)query搜索出什么。
query理解,決定了下游的搜索召回策略。底層數(shù)據(jù)從技術(shù)上,有各種類型的數(shù)據(jù)庫(kù)需要檢索;從算法策略上,也有多種召回的方案,例如高準(zhǔn)確的、高召回的等等,要用什么策略,這要取決于query理解的結(jié)論。
例子
要把一個(gè)事情說(shuō)清楚,舉例是一個(gè)很好的方法。來(lái)一個(gè)query:唐人街探案
直觀的看,這里有一個(gè)核心詞——唐人街探案,其核心意圖就是想看看唐人街探案的相關(guān)內(nèi)容吧,來(lái)看看系統(tǒng)內(nèi)干了些啥:
糾錯(cuò):初步看來(lái),沒有錯(cuò)誤,過(guò)。 意圖識(shí)別和實(shí)體識(shí)別:有唐人街探案這個(gè)實(shí)體,常見的首先是一部電影,最近還上了網(wǎng)劇,從熱度上看,由于網(wǎng)劇比較新,所以用戶在近期更可能看的是網(wǎng)劇,當(dāng)然信息不足,不代表用戶真的就只想看網(wǎng)劇,所以電影的東西也要給一些,最大限度保證滿足需求。
好了,以百度為例看看結(jié)果:

前4條,分別給的是百科、愛奇藝網(wǎng)劇、豆瓣電影評(píng)論、愛奇藝電影。基本上就覆蓋了我上面的分析內(nèi)容,用戶只輸入了一個(gè)簡(jiǎn)單的實(shí)體,就會(huì)給出精準(zhǔn)的對(duì)應(yīng)信息,百科了解概況,愛奇藝網(wǎng)劇滿足近況,豆瓣電影有影評(píng),最終補(bǔ)充了電影,滿足更為全面的請(qǐng)求。
我們來(lái)復(fù)雜一些,升級(jí)為唐人街探案網(wǎng)劇怎么樣。
唐人街探案還是一個(gè)實(shí)體,網(wǎng)劇和電影雙意圖,但是由于用戶輸入了網(wǎng)劇,有關(guān)電影的內(nèi)容基本上就可以不出了,最后來(lái)了個(gè)怎么樣,說(shuō)明用戶是更在乎影評(píng),而非要看電視劇了,當(dāng)然給電視劇了用戶不會(huì)反感,屬于弱意圖了。好了,來(lái)看百度結(jié)果:

前5條都圍繞著劇評(píng)進(jìn)行,可以說(shuō)是分析的非常精準(zhǔn)了,且前面幾條也是比較出名的媒體給出的答案,知乎、新聞、豆瓣、松子電影,第六條很機(jī)智的給了愛奇藝的鏈接了,而且不是展開的,而是一個(gè)摘要形式的,大家可以對(duì)比一下上一條搜索的結(jié)果區(qū)別,從這里,大家就能理解,query理解具體做了些什么事情。
query理解的內(nèi)容
那么,要做query,要做什么工作呢。仔細(xì)想想,其實(shí)主要就是下面幾個(gè):
- 糾錯(cuò)改寫。針對(duì)用戶輸錯(cuò)的,沒輸入完全的,內(nèi)容,進(jìn)行修正。底層數(shù)據(jù)庫(kù)只支持精準(zhǔn)搜索,因此需要將query改寫到正確的內(nèi)容下。
- 意圖識(shí)別。通過(guò)分析語(yǔ)義等方式,在一定的類目結(jié)構(gòu)下,識(shí)別出具體意圖。這個(gè)意圖識(shí)別的目標(biāo),大家可以理解為告訴下游,需要在哪個(gè)庫(kù)數(shù)據(jù)進(jìn)行搜索。
- 實(shí)體識(shí)別。其實(shí)和意圖識(shí)別一樣,只不過(guò),粒度更細(xì),但是是詞級(jí)別的分析,從query中抽取關(guān)鍵的實(shí)體,如果說(shuō)意圖識(shí)別是為了告訴下游該檢索那個(gè)數(shù)據(jù)庫(kù),那實(shí)體識(shí)別就是為了告訴下游,在該數(shù)據(jù)庫(kù)下,該檢索哪些字段。
- 詞權(quán)重問題。query里面有兩個(gè)詞,兩個(gè)文檔分別匹配到了其中一個(gè)詞,那誰(shuí)能靠前?這就要看匹配到什么內(nèi)容更為重要。如家賓館,匹配到一個(gè)如家酒店和五洲賓館,如家酒店應(yīng)該在前,這里就是為了解決這個(gè)問題。
query理解的具體操作
query理解下的所有內(nèi)容,除了意圖識(shí)別本身外,其實(shí)我都或多或少介紹過(guò)。
糾錯(cuò)改寫
- 基于統(tǒng)計(jì)挖掘,分析最高頻的正確答案,在用戶錯(cuò)誤的時(shí)候,分析他的真實(shí)意圖,改寫過(guò)去。
- 基于機(jī)器學(xué)習(xí)和深度學(xué)習(xí),識(shí)別錯(cuò)誤,改正錯(cuò)誤。
意圖識(shí)別
意圖識(shí)別簡(jiǎn)單的理解,其實(shí)是一次文本分類,那么文本分類,我們把思路拓展開,其實(shí)也是兩條路——傳統(tǒng)方法和NLP。
- 傳統(tǒng)方法想必很多人其實(shí)了解的并不多,但其實(shí)是搜索領(lǐng)域內(nèi)非常常見,通過(guò)規(guī)則、詞典、正則等方式進(jìn)行識(shí)別,準(zhǔn)確率高、速度快。
- NLP,通過(guò)語(yǔ)義分析的手段,文本分類,達(dá)到語(yǔ)義分析的目的。
實(shí)體識(shí)別
其實(shí)問題抽象出來(lái),就是個(gè)難度高于文本分類的序列標(biāo)注問題,搜索中的命名實(shí)體識(shí)別,我聊過(guò)的,在這里:
具體思路仍然分為兩派,傳統(tǒng)方法和NLP。
詞權(quán)重問題
- 統(tǒng)計(jì)的方法,其中tfidf最為常見,而由于query的長(zhǎng)度都不長(zhǎng),所以其實(shí)就是idf的計(jì)算了。
- NLP方法,其實(shí)就是序列標(biāo)注問題的升級(jí)版了。
任務(wù)實(shí)踐

命名實(shí)體識(shí)別任務(wù)實(shí)踐
命名實(shí)體識(shí)別整體思路介紹
- step 1:對(duì)于用戶的輸入,先使用預(yù)先構(gòu)建的疾病、疾病別名、并發(fā)癥和癥狀的AC Tree進(jìn)行匹配;
- step 2:若全都無(wú)法匹配到相應(yīng)實(shí)體,則使用結(jié)巴切詞庫(kù)對(duì)用戶輸入的文本進(jìn)行切分;
- step 3:然后將每一個(gè)詞都去與疾病詞庫(kù)、疾病別名詞庫(kù)、并發(fā)癥詞庫(kù)和癥狀詞庫(kù)中的詞計(jì)算相似度得分(overlap score、余弦相似度分?jǐn)?shù)和編輯距離分?jǐn)?shù)),如果相似度得分超過(guò)0.7,則認(rèn)為該詞是這一類實(shí)體;
-
step 4:最后排序選取最相關(guān)的詞作為實(shí)體(項(xiàng)目所有的實(shí)體類型如下圖所示,但實(shí)體識(shí)別時(shí)僅使用了疾病、別名、并發(fā)癥和癥狀四種實(shí)體)
構(gòu)建 AC Tree
def build_actree(self, wordlist):
"""
構(gòu)造actree,加速過(guò)濾
:param wordlist:
:return:
"""
actree = ahocorasick.Automaton()
# 向樹中添加單詞
for index, word in enumerate(wordlist):
actree.add_word(word, (index, word))
actree.make_automaton()
return actree
這一塊主要使用了AC自動(dòng)機(jī)字符串匹配算法Aho-Corasick,通俗說(shuō)就是有個(gè)大的列表,客戶輸入一句話,如何根據(jù)客戶輸入的一句話,從大列表中匹配出字符串交集。
比如我們有一個(gè)wordlist列表,長(zhǎng)度很長(zhǎng),包含43430個(gè)元素:
['長(zhǎng)春海外制藥接骨續(xù)筋片', '香菇燉甲魚', '三鶴藥業(yè)黃柏膠囊', '上海衡山熊去氧膽酸片', '升和藥業(yè)依托泊苷注射液', '怡諾思', '人格障礙', '轉(zhuǎn)鐵蛋白飽和度', '脾囊腫', '素?zé)滋}卜', '利君現(xiàn)代冠脈寧片',
'上海復(fù)華藥業(yè)注射用還原型谷', '陰囊上有白色小疙瘩', '腹痛伴休克', '成都通德胰激肽原酶腸溶片', '蒸豬肝', '河北百善血尿膠囊', '精神障礙', '輸卵管畸形', '元和抑眩寧膠囊', '蓮藕豆腐', '辰欣哈西奈德溶液',
'信誼煙酸片', '慢性膽囊炎', '參芪降糖顆粒', '康普藥業(yè)鹽酸普萘洛爾片', '西安迪賽胸腺肽腸溶片', '雙鷺?biāo)帢I(yè)注射用復(fù)合輔酶', '慢性篩竇炎', '新高制藥維胺酯維E乳膏', '冰黃膚樂軟膏', '神經(jīng)類疾病', '液晶熱圖',
'棗(干)', '股外側(cè)皮神經(jīng)病', '浙江惠松硅炭銀片', '牙根外露', '湖北潛江氯霉素滴眼液', '鹽類皮質(zhì)激素分泌過(guò)多', '五子衍宗丸', '小兒陣發(fā)性睡眠性血紅蛋白尿癥', '功能失調(diào)性子宮出血病', '茵梔黃口服液',
'眼底出血和滲出', '斯達(dá)制藥注射用頭孢噻肟鈉', '復(fù)方白芷酊', '脛腓骨骨折', '西南藥業(yè)氯霉素片', '宮頸炎', '茶堿緩釋膠囊', '原發(fā)性硬化性膽管炎', '鄭州韓都利肺膠囊', '咽反射消失', '脊髓灰質(zhì)炎',
'甲狀腺片', '回盲瓣功能不全', '乙肝e抗體(抗...', '馬齒莧粥', '動(dòng)脈硬化', '寶寶樂', '腸閉鎖', '肺放線菌病', '江蘇晨牌產(chǎn)婦安顆粒', '犬吠樣咳嗽', '胃康靈膠囊', '小兒煙酸缺乏病', '青龍防風(fēng)通圣丸',
'廣東南國(guó)維生素C片', '碘化油咀嚼片', '西樂葆', '偉哥甲磺酸酚妥拉明分散片', '成都迪康藥業(yè)樟腦醑', '斑疹', '五花燉墨魚', '肉燉蕓豆粉條', '陜西東泰制藥益脈康膠囊', '桔梗八味顆粒', '華南牌溴丙胺太林片',
'吉林敖東洮南小牛脾提取物注', '仁青芒覺', '血吸蟲病與肝膽疾病',...,'持續(xù)性枕橫位難產(chǎn)', '彎曲菌感染', '絲瓜蘑菇肉片湯', '長(zhǎng)春銀諾克清咽片', '肝葉萎縮', '迪皿鹽酸左西替利嗪口服溶液']
index, (index, word)如下:
0 (0, '長(zhǎng)春海外制藥接骨續(xù)筋片')
1 (1, '香菇燉甲魚')
2 (2, '三鶴藥業(yè)黃柏膠囊')
3 (3, '上海衡山熊去氧膽酸片')
4 (4, '升和藥業(yè)依托泊苷注射液')
5 (5, '怡諾思')
6 (6, '人格障礙')
7 (7, '轉(zhuǎn)鐵蛋白飽和度')
8 (8, '脾囊腫')
9 (9, '素?zé)滋}卜')
10 (10, '利君現(xiàn)代冠脈寧片')
......
43422 (43422, '彎曲菌感染')
43423 (43423, '絲瓜蘑菇肉片湯')
43424 (43424, '長(zhǎng)春銀諾克清咽片')
43425 (43425, '肝葉萎縮')
43426 (43426, '迪皿鹽酸左西替利嗪口服溶液')
43427 (43427, '華潤(rùn)天和麝香壯骨膏')
43428 (43428, '湖北恒安曲咪新乳膏')
43429 (43429, '子宮小')
#############################
import ahocorasick
actree = ahocorasick.Automaton()
for index, word in enumerate(wordlist):
actree.add_word(word, (index, word))
actree.make_automaton()
#其中wordlist就是上面的那個(gè)長(zhǎng)度為43430的列表
for i in actree.iter('昨天發(fā)燒,服用了阿司匹林,并且還吃了牛黃清胃丸,飯是吃了瓜燒白菜,大便有點(diǎn)色淺'):
print(i)
這樣客戶輸入一個(gè)字符串,我們能夠快速的從之前的列表中匹配出相應(yīng)的實(shí)體元素:

因此我們可以使用AC Tree進(jìn)行問句過(guò)濾,得到匹配的詞和類型。如疾病,疾病別名,并發(fā)癥,癥狀
def entity_reg(self, question):
"""
模式匹配, 得到匹配的詞和類型。如疾病,疾病別名,并發(fā)癥,癥狀
:param question:str
:return:
"""
self.result = {}
for i in self.disease_tree.iter(question):
word = i[1][1]
if "Disease" not in self.result:
self.result["Disease"] = [word]
else:
self.result["Disease"].append(word)
for i in self.alias_tree.iter(question):
word = i[1][1]
if "Alias" not in self.result:
self.result["Alias"] = [word]
else:
self.result["Alias"].append(word)
for i in self.symptom_tree.iter(question):
wd = i[1][1]
if "Symptom" not in self.result:
self.result["Symptom"] = [wd]
else:
self.result["Symptom"].append(wd)
for i in self.complication_tree.iter(question):
wd = i[1][1]
if "Complication" not in self.result:
self.result["Complication"] = [wd]
else:
self.result["Complication"] .append(wd)
return self.result
使用相似度進(jìn)行實(shí)體匹配
當(dāng)AC Tree的匹配都沒有匹配到實(shí)體時(shí),使用查找相似詞的方式進(jìn)行實(shí)體匹配
def find_sim_words(self, question):
"""
當(dāng)全匹配失敗時(shí),就采用相似度計(jì)算來(lái)找相似的詞
:param question:
:return:
"""
import re
import string
from gensim.models import KeyedVectors
# 使用結(jié)巴加載自定義詞典
jieba.load_userdict(self.vocab_path)
# 加載詞向量
self.model = KeyedVectors.load_word2vec_format(self.word2vec_path, binary=False)
# 數(shù)據(jù)預(yù)處理,正則去除特殊符號(hào)
sentence = re.sub("[{}]", re.escape(string.punctuation), question)
sentence = re.sub("[,?!唬??、!【】]", " ", sentence)
sentence = sentence.strip()
# 使用結(jié)巴進(jìn)行分詞
words = [w.strip() for w in jieba.cut(sentence) if w.strip() not in self.stopwords and len(w.strip()) >= 2]
alist = []
# 對(duì)每個(gè)詞,都讓其與每類實(shí)體詞典進(jìn)行相似對(duì)比,
# 最終選取分?jǐn)?shù)最高的實(shí)體和其屬于的實(shí)體類型
for word in words:
temp = [self.disease_entities, self.alias_entities, self.symptom_entities, self.complication_entities]
for i in range(len(temp)):
flag = ''
if i == 0:
flag = "Disease"
elif i == 1:
flag = "Alias"
elif i == 2:
flag = "Symptom"
else:
flag = "Complication"
scores = self.simCal(word, temp[i], flag)
alist.extend(scores)
temp1 = sorted(alist, key=lambda k: k[1], reverse=True)
if temp1:
self.result[temp1[0][2]] = [temp1[0][0]]
# 計(jì)算詞語(yǔ)和字典中的詞的相似度
def simCal(self, word, entities, flag):
"""
計(jì)算詞語(yǔ)和字典中的詞的相似度
相同字符的個(gè)數(shù)/min(|A|,|B|) + 余弦相似度
:param word: str
:param entities:List
:return:
"""
a = len(word)
scores = []
for entity in entities:
sim_num = 0
b = len(entity)
c = len(set(entity+word))
temp = []
for w in word:
if w in entity:
sim_num += 1
if sim_num != 0:
score1 = sim_num / c # overlap score
temp.append(score1)
try:
score2 = self.model.similarity(word, entity) # 余弦相似度分?jǐn)?shù)
temp.append(score2)
except:
pass
score3 = 1 - self.editDistanceDP(word, entity) / (a + b) # 編輯距離分?jǐn)?shù)
if score3:
temp.append(score3)
score = sum(temp) / len(temp)
if score >= 0.7:
scores.append((entity, score, flag))
scores.sort(key=lambda k: k[1], reverse=True)
return scores
意圖識(shí)別任務(wù)實(shí)踐
意圖識(shí)別整體思路介紹
- step 1:利用TF-IDF表征文本特征,同時(shí)構(gòu)建一些人工特征(每一類意圖常見詞在句子中出現(xiàn)的個(gè)數(shù));
- step 2:訓(xùn)練樸素貝葉斯模型進(jìn)行意圖識(shí)別任務(wù);
-
step 3:使用實(shí)體信息進(jìn)行意圖的糾正和補(bǔ)充。
該項(xiàng)目通過(guò)手工標(biāo)記210條意圖分類訓(xùn)練數(shù)據(jù),并采用樸素貝葉斯算法訓(xùn)練得到意圖分類模型。其最佳測(cè)試效果的F1值達(dá)到了96.68%。
意圖識(shí)別整體步驟介紹
特征構(gòu)建
- TF-IDF特征
# 提取問題的TF-IDF特征
def tfidf_features(self, text, vectorizer):
"""
提取問題的TF-IDF特征
:param text:
:param vectorizer:
:return:
"""
jieba.load_userdict(self.vocab_path)
words = [w.strip() for w in jieba.cut(text) if w.strip() and w.strip() not in self.stopwords]
sents = [' '.join(words)]
tfidf = vectorizer.transform(sents).toarray()
return tfidf
- 人工特征
self.symptom_qwds = ['什么癥狀', '哪些癥狀', '癥狀有哪些', '癥狀是什么', '什么表征', '哪些表征', '表征是什么',
'什么現(xiàn)象', '哪些現(xiàn)象', '現(xiàn)象有哪些', '癥候', '什么表現(xiàn)', '哪些表現(xiàn)', '表現(xiàn)有哪些',
'什么行為', '哪些行為', '行為有哪些', '什么狀況', '哪些狀況', '狀況有哪些', '現(xiàn)象是什么',
'表現(xiàn)是什么', '行為是什么'] # 詢問癥狀
self.cureway_qwds = ['藥', '藥品', '用藥', '膠囊', '口服液', '炎片', '吃什么藥', '用什么藥', '怎么辦',
'買什么藥', '怎么治療', '如何醫(yī)治', '怎么醫(yī)治', '怎么治', '怎么醫(yī)', '如何治',
'醫(yī)治方式', '療法', '咋治', '咋辦', '咋治', '治療方法'] # 詢問治療方法
self.lasttime_qwds = ['周期', '多久', '多長(zhǎng)時(shí)間', '多少時(shí)間', '幾天', '幾年', '多少天', '多少小時(shí)',
'幾個(gè)小時(shí)', '多少年', '多久能好', '痊愈', '康復(fù)'] # 詢問治療周期
self.cureprob_qwds = ['多大概率能治好', '多大幾率能治好', '治好希望大么', '幾率', '幾成', '比例',
'可能性', '能治', '可治', '可以治', '可以醫(yī)', '能治好嗎', '可以治好嗎', '會(huì)好嗎',
'能好嗎', '治愈嗎'] # 詢問治愈率
self.check_qwds = ['檢查什么', '檢查項(xiàng)目', '哪些檢查', '什么檢查', '檢查哪些', '項(xiàng)目', '檢測(cè)什么',
'哪些檢測(cè)', '檢測(cè)哪些', '化驗(yàn)什么', '哪些化驗(yàn)', '化驗(yàn)?zāi)男?, '哪些體檢', '怎么查找',
'如何查找', '怎么檢查', '如何檢查', '怎么檢測(cè)', '如何檢測(cè)'] # 詢問檢查項(xiàng)目
self.belong_qwds = ['屬于什么科', '什么科', '科室', '掛什么', '掛哪個(gè)', '哪個(gè)科', '哪些科'] # 詢問科室
self.disase_qwds = ['什么病', '啥病', '得了什么', '得了哪種', '怎么回事', '咋回事', '回事',
'什么情況', '什么問題', '什么毛病', '啥毛病', '哪種病'] # 詢問疾病
def other_features(self, text):
"""
提取問題的關(guān)鍵詞特征
:param text:
:return:
"""
features = [0] * 7
for d in self.disase_qwds:
if d in text:
features[0] += 1
for s in self.symptom_qwds:
if s in text:
features[1] += 1
for c in self.cureway_qwds:
if c in text:
features[2] += 1
for c in self.check_qwds:
if c in text:
features[3] += 1
for p in self.lasttime_qwds:
if p in text:
features[4] += 1
for r in self.cureprob_qwds:
if r in text:
features[5] += 1
for d in self.belong_qwds:
if d in text:
features[6] += 1
m = max(features)
n = min(features)
normed_features = []
if m == n:
normed_features = features
else:
for i in features:
j = (i - n) / (m - n)
normed_features.append(j)
return np.array(normed_features)
使用樸素貝葉斯進(jìn)行文本分類
- 項(xiàng)目沒有給出訓(xùn)練過(guò)程,可參考下面sklearn的例子
# 項(xiàng)目沒有給出訓(xùn)練過(guò)程,可參考下面sklearn的例子
from sklearn.naive_bayes import MultinomialNB
mnb = MultinomialNB()
mnb.fit(X_train,y_train)
y_predict = mnb.predict(X_test)
# 意圖分類模型文件
self.tfidf_path = os.path.join(cur_dir, 'model/tfidf_model.m')
self.nb_path = os.path.join(cur_dir, 'model/intent_reg_model.m') #樸素貝葉斯模型
self.tfidf_model = joblib.load(self.tfidf_path)
self.nb_model = joblib.load(self.nb_path)
# 意圖預(yù)測(cè)
tfidf_feature = self.tfidf_features(question, self.tfidf_model)
other_feature = self.other_features(question)
m = other_feature.shape
other_feature = np.reshape(other_feature, (1, m[0]))
feature = np.concatenate((tfidf_feature, other_feature), axis=1)
predicted = self.model_predict(feature, self.nb_model)
intentions.append(predicted[0])
- 根據(jù)所識(shí)別的實(shí)體進(jìn)行補(bǔ)充和糾正意圖
# 已知疾病,查詢癥狀
if self.check_words(self.symptom_qwds, question) and ('Disease' in types or 'Alia' in types):
intention = "query_symptom"
if intention not in intentions:
intentions.append(intention)
# 已知疾病或癥狀,查詢治療方法
if self.check_words(self.cureway_qwds, question) and \
('Disease' in types or 'Symptom' in types or 'Alias' in types or 'Complication' in types):
intention = "query_cureway"
if intention not in intentions:
intentions.append(intention)
# 已知疾病或癥狀,查詢治療周期
if self.check_words(self.lasttime_qwds, question) and ('Disease' in types or 'Alia' in types):
intention = "query_period"
if intention not in intentions:
intentions.append(intention)
# 已知疾病,查詢治愈率
if self.check_words(self.cureprob_qwds, question) and ('Disease' in types or 'Alias' in types):
intention = "query_rate"
if intention not in intentions:
intentions.append(intention)
# 已知疾病,查詢檢查項(xiàng)目
if self.check_words(self.check_qwds, question) and ('Disease' in types or 'Alias' in types):
intention = "query_checklist"
if intention not in intentions:
intentions.append(intention)
# 查詢科室
if self.check_words(self.belong_qwds, question) and \
('Disease' in types or 'Symptom' in types or 'Alias' in types or 'Complication' in types):
intention = "query_department"
if intention not in intentions:
intentions.append(intention)
# 已知癥狀,查詢疾病
if self.check_words(self.disase_qwds, question) and ("Symptom" in types or "Complication" in types):
intention = "query_disease"
if intention not in intentions:
intentions.append(intention)
# 若沒有檢測(cè)到意圖,且已知疾病,則返回疾病的描述
if not intentions and ('Disease' in types or 'Alias' in types):
intention = "disease_describe"
if intention not in intentions:
intentions.append(intention)
# 若是疾病和癥狀同時(shí)出現(xiàn),且出現(xiàn)了查詢疾病的特征詞,則意圖為查詢疾病
if self.check_words(self.disase_qwds, question) and ('Disease' in types or 'Alias' in types) \
and ("Symptom" in types or "Complication" in types):
intention = "query_disease"
if intention not in intentions:
intentions.append(intention)
# 若沒有識(shí)別出實(shí)體或意圖則調(diào)用其它方法
if not intentions or not types:
intention = "QA_matching"
if intention not in intentions:
intentions.append(intention)
self.result["intentions"] = intentions
后續(xù)就是通過(guò)上述得到的意圖信息和實(shí)體信息選擇對(duì)應(yīng)的模版,并將實(shí)體信息填充入組成查詢語(yǔ)句進(jìn)行數(shù)據(jù)庫(kù)查詢。
參考資料:
https://github.com/datawhalechina/team-learning-nlp/blob/master/KnowledgeGraph_Basic/task04.md#%E7%9B%AE%E5%BD%95
https://zhuanlan.zhihu.com/p/136313695
https://zh.wikipedia.org/wiki/%E5%95%8F%E7%AD%94%E7%B3%BB%E7%B5%B1



