基於知識庫的自動問答:seq2seq模型實踐


向AI轉型的程序員都關注了這個號👇👇👇python

機器學習AI算法工程   公衆號:datayx程序員



問題描述

基於知識圖譜的自動問答(Question Answering over Knowledge Base, 即 KBQA)問題的大概形式是,預先給定一個知識庫(好比Freebase),知識庫中包含着大量的先驗知識數據,而後利用這些知識資源自動回答天然語言形態的問題(好比「肉夾饃是江蘇的美食嗎」,「虵今年多大了」等人民羣衆喜聞樂見的問題)。web


什麼是知識庫面試

知識庫(Knowledge Base),或者叫的比較爛俗點,知識圖譜(Knowledge Graph),是以知識爲主要單位,實體爲主要載體,包含着現實生活中人們對萬千事物的認知與各種事實的龐大數據庫。通常來講,知識(或者事實)主要以三元組形式呈現:<頭實體,關係,尾實體>,其中實體即人、地點、或特定概念等萬物。舉例來講,<虵,改變了,中國> 就是一條簡單的三元組示例,其中頭尾皆爲知識庫中固有的實體單元。算法


方法框架數據庫

先放個圖flask



對着圖說:假設要回答「Where was Leslie Cheung born」這個問題,主要分四步:微信

  1. 實體識別(Named Entity Recognition),即把問題中的主要實體的名字從問題中抽出來,這樣才知道應該去知識庫中搜取哪一個實體的信息來解決問題,即圖中把「Leslie Cheung」這我的名抽出來;網絡


  2. 實體連接(Entity Linking),把抽取出來的實體名和知識庫中具體的實體對應起來,作這一步是由於,因爲同名實體的存在,名字不是實體的惟一標識,實體獨一無二的編號(id)纔是,找到了實體名沒卵用,必需要對應到知識庫中具體的實體id,才能在知識庫中把具體實體找到,獲取相關信息。即圖中將「Leslie Cheung」映射到「m.sdjk1s」這個 id 上(Freebase 的實體 id 是這個格式的)。這一步會存在一些問題,好比直接搜「姓名」叫「Leslie Cheung」的實體是搜不到的,由於「Leslie Cheung」實際上是某個實體的「外文名」,他的「姓名」叫「張國榮」,以及有時候還會有多個叫「Leslie Cheung」的人。具體解決方式後面再說。app


  3. 關係預測(Relation Prediction),根據原問句中除去實體名之外的其餘詞語預測出應該從知識庫中哪一個關係去解答這個問題,是整個問題中最主要的一步。即圖中從「Where was <e> born」預測出「people.person.place_of_birth」(Freebase 的關係名格式,翻譯過來就是「出生地」)這個關係應該鏈接着問題的主要實體「Leslie Cheung」與這個問題的答案。


  4. 找到了實體與關係,直接在知識庫中把對應的三元組檢索出來,即 「<m.sdjk1s,
    people.person.place_of_birth, m.s1kjds>」,那麼這條三元組的尾實體,即「m.s1kjds」就是問題的答案,查詢其名字,就是「Hong Kong」。

數據集與工具

源代碼與數據下載:

其中 data/origin 目錄下是問答數據集的原始數據,鑑於實體識別與連接作起來比較麻煩,因此直接給出中間數據,data/seq2seq 目錄下是已經通過前兩步,能夠直接用於訓練 seq2seq 模型的數據。


本文項目代碼 方式:

關注微信公衆號 datayx  而後回覆  問答  便可獲取。

AI項目體驗地址 https://loveai.tech


數據集:

SimpleQuestions & WebQuestions 學術界問答領域比較喜聞樂見的兩個數據集了,至關權威,固然都是英文,別問爲何不是中文
另外,知識庫用的是  Freebase ,業界最權威的知識庫之一了,固然也是英文的,別問爲何不是中文

工具:

Pytorch

依照慣例,仍是先上結論

  • 從 tensorflow 轉過來發現,pytorch 真好用

  • 問答問題目前的解決方法,框架基本都是上面說那四步,可是具體作法五花八門,模型各式各樣,文中所用的 seq2seq 也只是一個簡單實踐,效果上比較以下圖(out-of-state now 是業界目前最好結果,APVA-TURBO 是我最近在作的一篇論文)


簡單的 seq2seq 效果還不錯,雖然跟學術界目前最好成績有很大差距,可是還不錯。後來我又加入了一些好比 KB embedding,turbo training 等一言難盡的補丁上去,變成如今的 APVA-TURBO 模型,在 WebQuestions 上已經快領先 8 個點了,可是文章裏不提了,太亂了,直接發一個論文連接,感興趣的能夠深刻研究


The APVA-TURBO Approach To Question Answering in Knowledge Base. Yue Wang, Richong Zhang, Cheng Xu and Yongyi Mao. Published 2018 in COLING


https://www.aclweb.org/anthology/C18-1170/


下面正式開編,詳細講一下用 seq2seq 模型作問答問題的過程以及 pytorch 的實現,我的淺見


1 數據處理(包括實體識別與連接)

先貼一下數據集的原始數據形態,拿 SimpleQuestions 的數據貼一下,WebQuestions 的數據要比這個醜陋一些,就不提了。數據量方面,SimpleQuestions 的 train/test 是 75910/21687 ,WebQuestions 是 3778/2032



SimpleQuestions 的原始數據中,每一行一個數據,分四列,中間用「\t」隔開,四列分別是頭實體id,關係,尾實體id與問句內容

1.1 實體識別

首先訓練實體識別模型,目標是給一個問題,能把問題中的實體名(entity mention)找到,方法就是喜聞樂見的 BIO 序列標註方法,模型用簡單 LSTM 能夠解決,或者再堆個 CRF 加強效果,序列標註在上一篇文章說過,「B」 即實體名的開始單詞,「I」 爲實體名的中間單詞(或結尾詞),「O」 爲不是實體名的單詞,輸入一串單詞序列,輸出一串長度相同的由 BIO 組成的字母序列


訓練數據有了,開始訓練模型,不是主要內容不細說了,放一個模型圖



這裏 char-BiGRU 是從字母維度上的的 word embedding,以及 CRF layer,都是爲了加強效果,簡單作能夠都省略

1.2 實體連接

找到了實體名,而後就是對應到 KB 中的具體實體。這一步作法比較簡單,可是對最終效果的影響仍是比較大的,包括在 KB 中能不能找到對應的實體,以及找到多個實體怎麼排序的問題。直接說方法,首先收集 KB 中全部實體的名稱(包括「name」「外文名」「別名」等等的),而後構建單詞到實體 id 的反向 map 表,舉個例子



這裏 Leslie 能夠連接到兩個實體,由於兩個實體的名字中都含有 Leslie 這個單詞。注意每個括號裏的數字,表明詞(或詞組)連接到這個實體的打分,計算方式就是這個詞組的單詞個數除以這個實體完整實體名的單詞個數。

這裏打分也能夠適當考慮實體的知名度進去,好比「Leslie Cheung Kwok-wing」這個實體知名度更高,「Uncle Leslie」沒怎麼據說過,因此用戶提這個問題更有多是問關於前者的,因此前者的打分也要適當提升一些。具體操做方式能夠很靈活,不細說了。

1.3 關係預測(seq2seq模型)

終於進入正題了。通過以前兩步的數據處理,如今的數據基本是這個樣子



上面是輸入下面是指望輸出,輸入中每條數據就是一個問句,由若干個單詞組成的序列,其中已經把實體名拿走,用「<e>」這個標記詞進行替換。輸出是一個關係名,雖然因爲 Freebase 的關係格式定義,一個關係名由三個用「.」拼接的單詞組成,可是這裏只把他當成一個完整的單詞看待。其實關係預測本質上就是一個文本分類問題,給定全部的關係列表,輸入一個文本,分類到一個最可能的關係上。

在這一步結束後,獲得了預測出的關係名,再加上上一步實體連接獲得的具體實體,就能從知識庫中找到三元組,找到答案,從而解決問題了。下面具體講關係預測的模型及實現代碼細節。

2 模型搭建

先上一個模型圖


最簡單的沒有任何添加劑的純自然的 seq2seq 模型,即 encoder-decoder 模型(固然也能夠再加 attention 什麼的上去,就不提了),左邊(綠色)是一個雙向 GRU(或 LSTM)(雙向即兩層,一層正向走一層反向走,而後把兩層的最後結果加到一塊兒,只用單向也能夠,區別不大)做爲 encoder,能把整個問題壓縮成一個向量 u,右邊是一個單向 GRU ,把向量 u 解壓縮成一個關係,或關係序列,_GO 是表示序列開始生成的標記詞,_EOS 是表示序列生成完畢的標記詞。下面詳細說一下爲何會是關係序列。這也是原本一個簡單的多分類任務爲何不用簡單的 RNN 分類模型而用 seq2seq 這種序列生成模型的緣由。

有時候僅靠一個關係(一跳)並不能找到最終答案,好比「張國榮曾在哪一個國家留學」,爲了回答這個問題須要輸出兩個關係(兩跳),第一跳是從「張國榮」經過「畢業院校」這個關係找到「英國里茲大學」這個實體,第二跳是從「英國里茲大學」經過「所屬國家」這個關係找到「英國」這個最終答案。因此原來「張國榮出生在哪裏」這個問題對應的輸出序列是「出生地,_EOS」,而「張國榮曾在哪一個國家留學」對應的輸出序列就變成了「畢業院校,所屬國家,_EOS」,須要輸出的關係序列長度是不同的,這也是 seq2seq 模型解決問答問題的優點所在。


Encoder

好了,編完了,下面上代碼,首先是 Encoder


source_vocab_size 是全部數據中涉及到的單詞詞表大小,hidden_size 是單詞被壓縮成的詞向量維度,設 batch 的大小爲 B,batch 內每一個輸入序列長度爲 S,首先是形狀 S*B 的張量進來,而後通過 embedding 獲得 S*B*D 的張量,而後直接進 GRU ,獲得結果 outputs 以及 hidden,這裏由於使用了雙向 GRU 因此 outputs 出來是 S*B*2D 的,hidden 出來是 2*B*D 的,須要壓縮一下,outputs 在模型後面沒有用到能夠無所謂。這裏再細說一下這個 pack_padded_sequence 的做用,這個函數機制真的是讓我只想雙擊666

對於使用了 batch 的 GRU(或LSTM)來講,要求輸入的 batch 中的每個序列長度相同。可是一個 batch 裏的問題有長有短,怎麼可能都相同呢,因此就須要用一個沒有意義的標記詞(「_PAD」)把全部問題填充(Padding)到相同的長度,舉個例子


在這個大小爲 3 的 batch 裏 ,後兩個問題由於長度不足都被 padding 到了 5 個單詞,可是在推到 GRU 裏運行的時候,咱們只但願它們前面有效的單詞進去就能夠了,後面的 _PAD 填充過多時會嚴重影響最後出來的效果,bucket 機制或許能夠適當解決這個問題,可是 pytorch 提供的這個 pack_padded_sequence 很是完美,它能夠自動保證 _PAD 不會真正進入到 GRU 中影響效果,只須要你事先把 input_seqs 先按長度從大到小排列一下,而後把排序後每一個序列的真正長度 input_lengths 傳進來,好比這個例子裏 input_lengths 就是 [5,4,3],而後包裝好放進 GRU 裏, GRU 運行完了再用 pad_packed_sequence 這個函數解包一下,就 OK 了


Decoder


Decoder 也比較簡單,可是跟上面 Encoder 有個很大的區別就是這裏 Decoder 一次只處理一個單詞,假設指望輸出序列長度是 M,須要運行 M 次,而上面 Encoder 是一次就把長度爲 N 的序列都處理完。Decoder 不能這麼作的緣由是在它的下一次輸入是上一次輸出,只有先運行一遍獲得第一個單詞才能再去獲得第二個單詞,而不像 Encoder 一開始就知道整個輸入序列。


run_epoch

encoder 和 decoder 搭完了,下面就是怎麼把他們拼起來了,一個 S*D 的 batch 來了,先跑 encoder,獲得 1*S*D 的 encoder_hidden,就是模型圖中最重要的 u,而後設最長輸出序列長度爲 t,分 t 次運行 decoder 模型,一次輸入一個單詞,最初的輸入單詞爲標記詞「_GO」,並將 做爲初始隱層塞到 decoder 裏。


先定義好參數優化器 optimizer,這裏使用隨機梯度降低算法(SGD),而後每輸入一個 batch,運行一次 run_epoch 函數,計算一次 loss,更新一次參數,而後結束,返回此次 loss 的值;當 TRAIN=False,也就是測試的時候,不計算 loss 也不更新參數,直接對比真實輸出與指望輸出,返回準確度。

這裏計算 loss 用了 masked_cross_entropy 這個函數。

他這個 loss 計算有一個很大的好處是什麼呢,這就又涉及到 padding 的問題了,剛纔說輸入序列須要 padding,而且經過 pack_padded_sequence 避免了 _PAD 帶來的影響,而輸出序列也須要 padding,也須要一種措施避免影響,仍是舉個例子


在這個大小爲 3 的 batch 中,最長輸出序列 t=3,後兩條數據由於長度不足被加入了 _PAD 標記詞,可是計算 loss 並更新參數的時候,咱們只但願計算除 _PAD 之外的位置上的 loss,並不想關心 _PAD 上的 loss,由於沒有意義,且會給效果帶來影響。masked_cross_entropy 這個函數就經過一個 mask 矩陣把 _PAD 位置上的 loss 過濾掉了,很是流弊。具體再也不細說了,能夠看源碼。


3 訓練及測試

終於一切基礎都搭完能夠開始訓練了,也沒啥能夠說的,直接放代碼吧


一共訓練 num_epoch 輪,每輪經過 get_batch 這個函數製做一個 batch,運行一次 run_epoch 函數,更新一次模型,而後每隔 print_every 輪進行一次測試並打印結果,每隔 save_every 輪保存一次模型。get_batch 這個函數具體細節不寫了,能夠看源碼。


問答問題目前的解決方法,框架基本都是上面說那四步,可是具體作法五花八門,模型各式各樣,文中所用的 seq2seq 也只是一個簡單實踐,效果上比較以下圖。簡單的 seq2seq 效果還不錯,雖然跟學術界目前最好成績有很大差距,可是還不錯。

原文地址 https://zhuanlan.zhihu.com/p/34585912




閱讀過本文的人還看了如下文章:


《美團機器學習實踐》_美團算法團隊.pdf


2019最新《PyTorch天然語言處理》英、中文版PDF+源碼


《21個項目玩轉深度學習:基於TensorFlow的實踐詳解》完整版PDF+附書代碼


《深度學習之pytorch》pdf+附書源碼


PyTorch深度學習快速實戰入門《pytorch-handbook》


【下載】豆瓣評分8.1,《機器學習實戰:基於Scikit-Learn和TensorFlow》


《Python數據分析與挖掘實戰》PDF+完整源碼


汽車行業完整知識圖譜項目實戰視頻(全23課)


李沐大神開源《動手學深度學習》,加州伯克利深度學習(2019春)教材


筆記、代碼清晰易懂!李航《統計學習方法》最新資源全套!


《神經網絡與深度學習》最新2018版中英PDF+源碼


將機器學習模型部署爲REST API


FashionAI服裝屬性標籤圖像識別Top1-5方案分享


重要開源!CNN-RNN-CTC 實現手寫漢字識別


yolo3 檢測出圖像中的不規則漢字


一樣是機器學習算法工程師,你的面試爲何過不了?


前海徵信大數據算法:風險機率預測


【Keras】完整實現‘交通標誌’分類、‘票據’分類兩個項目,讓你掌握深度學習圖像分類


VGG16遷移學習,實現醫學圖像識別分類工程項目


特徵工程(一)


特徵工程(二) :文本數據的展開、過濾和分塊


特徵工程(三):特徵縮放,從詞袋到 TF-IDF


特徵工程(四): 類別特徵


特徵工程(五): PCA 降維


特徵工程(六): 非線性特徵提取和模型堆疊


特徵工程(七):圖像特徵提取和深度學習


如何利用全新的決策樹集成級聯結構gcForest作特徵工程並打分?


Machine Learning Yearning 中文翻譯稿


螞蟻金服2018秋招-算法工程師(共四面)經過


全球AI挑戰-場景分類的比賽源碼(多模型融合)


斯坦福CS230官方指南:CNN、RNN及使用技巧速查(打印收藏)


python+flask搭建CNN在線識別手寫中文網站


中科院Kaggle全球文本匹配競賽華人第1名團隊-深度學習與特徵工程



不斷更新資源

深度學習、機器學習、數據分析、python

 搜索公衆號添加: datayx  

長按圖片,識別二維碼,點關注



機器學習算法資源社羣

不斷上傳電子版PDF資料

技術問題求解

 QQ羣號: 333972581  

長按圖片,識別二維碼




海淘美妝

本文分享自微信公衆號 - 機器學習AI算法工程(datayx)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索