NLP(十五)讓模型來告訴你文字中的時間

jclian91發表於2019-08-13

背景介紹

  在文章NLP入門(十一)從文字中提取時間 中,筆者演示瞭如何利用分詞、詞性標註的方法從文字中獲取時間。當時的想法比較簡單快捷,只是利用了詞性標註這個功能而已,因此,在某些地方,時間的識別效果並不太好。比如以下的兩個例子:

原文1:

蘇北大量農村住房建於上世紀80年代之前。去年9月,江蘇省決定全面改善蘇北農民住房條件,計劃3年內改善30萬戶,作為決勝全面建成小康社會補短板的重要舉措。

用筆者之前的程式碼,提取的時間結果為:

提取時間: ['去年9月']

但實際上,我們提取的時間應該是:

上世紀80年代之前, 去年9月,3年內

原文2:

南宋紹興十年,金分兵兩路向陝西和河南大舉進攻,在很快奪回了河南、陝西之後,又率大軍向淮南大舉進攻。

用筆者之前的程式碼,提取的時間結果為:

提取時間: ['南宋']

但實際上,我們提取的時間應該是:

南宋紹興十年

  因此,利用簡單的詞性標註功能來提取文字中的時間會存在漏提、錯提的情況,鑑於此,筆者想到能否用深度學習模型來實現文字中的時間提取呢?
  該功能類似於命名實體識別(NER)功能,只不過NER是識別文字中的人名、地名、組織機構名,而我們這次需要識別文字中的時間。但是,它們背後的演算法原理都是一樣的,即採用序列標註模型來解決。

專案

  在文章NLP(十四)自制序列標註平臺中,筆者提出了一種自制的序列標註平臺,利用該標註平臺,筆者從新聞網站中標註了大約2000份語料,標註出文字中的時間,其中75%作為訓練集(time.train檔案),10%作為驗證集(time.dev檔案),15%作為測試集(time.test檔案)。
  雖然我們現在已經有了深度學習框架方便我們來訓練模型,比如TensorFlow, Keras, PyTorch等,但目前已有某大神開源了一個序列標註和文字分類的模組,名稱為kashgari-tf,它能夠方便快速地用幾行命令就可以訓練一個序列標註或文字分類的模型,容易上手,而且集中了多種模型(BiGRU,CNN, BiLSTM,CRF)以及多種預訓練模型(BERT,ERNIE,wwm-ext),對於使用者來說算是十分友好了。該模組的參考網址為:https://kashgari.bmio.net/
  筆者自己花了幾天的時間來標註資料,目前已累計標註2000+資料 ,後續將放到Github供大家參考。我們訓練的資料,比如time.train的前幾行如下:(每一行中間用空格隔開)

1 B-TIME
6 I-TIME
0 I-TIME
9 I-TIME
年 I-TIME
, O
日 O
本 O
薩 O
摩 O
藩 O
入 O
侵 O
琉 O
球 O
國 O
, O
並 O
在 O
一 O
個 O
時 O
期 O
內 O
控 O
制 O
琉 O
球 O
國 O
...

  接著是模型這塊,我們採用經典的BERT+Bi-LSTM+CRF模型,訓練1個epoch,batch_size為16,程式碼如下:

# -*- coding: utf-8 -*-
# time: 2019-08-09 16:47
# place: Zhichunlu Beijing

import kashgari
from kashgari.corpus import DataReader
from kashgari.embeddings import BERTEmbedding
from kashgari.tasks.labeling import BiLSTM_CRF_Model

train_x, train_y = DataReader().read_conll_format_file('./data/time.train')
valid_x, valid_y = DataReader().read_conll_format_file('./data/time.dev')
test_x, test_y = DataReader().read_conll_format_file('./data/time.test')

bert_embedding = BERTEmbedding('chinese_L-12_H-768_A-12',
                               task=kashgari.LABELING,
                               sequence_length=128)

model = BiLSTM_CRF_Model(bert_embedding)
model.fit(train_x, train_y, valid_x, valid_y, batch_size=16, epochs=1)

model.save('time_ner.h5')

model.evaluate(test_x, test_y)

模型訓練完後,得到的效果如下:

資料集 accuracy loss
訓練集 0.9814 6.7295
驗證集 0.6868 150.8513

在測試集上的結果如下:

資料集 precision recall f1
測試集 0.8547 0.8934 0.8736

  由於是小標註量,因此我們選擇了用BERT預訓練模型。如果不採用BERT預訓練模型,在同樣的資料集上,即使訓練100個epoch,雖然在訓練集上的準確率超過95%,但是在測試集上卻只有大約50%的準確率,效果不行,因此,需要採用預訓練模型。

測試效果

  在訓練完模型後,會在當前目錄下生成time_ner.h5模型檔案,接著我們需要該模型檔案來對新的檔案進行預測,提取出文字中的時間。模型預測的程式碼如下:

# Load saved model
import kashgari

loaded_model = kashgari.utils.load_model('time_ner.h5')

while True:
    text = input('sentence: ')
    t = loaded_model.predict([[char for char in text]])
    print(t)

  接著我們在幾條新的資料上進行預測,看看該模型的表現效果:

"原文": "繼香港市民10日到“亂港頭目”黎智英住所外抗議後,13日,“禍港四人幫”中的另一人李柱銘位於半山的住所外,也有香港市民自發組織前來抗議。",
"預測時間": [
"10日",
"13日"
]

"原文": "綠地控股2018年年度年報顯示,截至2018年12月31日,萬科金域中央專案的經營狀態為“住宅、辦公、商業”,專案用地面積18.90萬平方米,規劃計容建築面積79.38萬平方米,總建築面積為105.78萬平方米,已竣工面積32.90萬平方米,總投資額95億元,報告期實際投資額為10.18億元。",
"預測時間": [
"2018年年度",
"2018年12月31日"
]

"原文": "經過工作人員兩天的反覆驗證、嚴密測算,記者昨天從上海中心大廈得到確認:被譽為上海中心大廈“定樓神器”的阻尼器,在8月10日出現自2016年正式啟用以來的最大擺幅。",
"預測時間": [
"兩天",
"昨天",
"8月10日",
"2016年"
]

"原文": "不幸的是,在升任內史的同年九月,狄仁傑就在洛陽私宅離世。",
"預測時間": [
"同年九月"
]

"原文": "早上9點25分到達北京火車站,火車站在北京市區哦,地鐵很方便到達酒店,我們定了王府井大街的錦江之星,409元一晚,有點小貴。下午去了天壇公園,傍晚去了天安門廣場。",
"預測時間": [
"早上9點25分",
"下午",
"傍晚"
],

總結

  利用深度學習模型,在小標註量資料上,我們對時間識別取得了不錯的效果。後續如果我們想要提高時間識別的準確率,可以再多增加標註資料,目前還只有2000+資料~
  本專案已經開源,Github的地址為:https://github.com/percent4/Chinese_Time_Recogniztion

  另外,強烈推薦kashgari-tf模組,它能夠讓你在幾分鐘內搭建一個序列標註模型,而且方便載入各種預訓練模型。

注意:不妨瞭解下筆者的微信公眾號: Python爬蟲與演算法(微訊號為:easy_web_scrape), 歡迎大家關注~

相關文章