NLP(十五)讓模型來告訴你文字中的時間
背景介紹
在文章NLP入門(十一)從文字中提取時間 中,筆者演示瞭如何利用分詞、詞性標註的方法從文字中獲取時間。當時的想法比較簡單快捷,只是利用了詞性標註這個功能而已,因此,在某些地方,時間的識別效果並不太好。比如以下的兩個例子:
原文1:
蘇北大量農村住房建於上世紀80年代之前。去年9月,江蘇省決定全面改善蘇北農民住房條件,計劃3年內改善30萬戶,作為決勝全面建成小康社會補短板的重要舉措。
用筆者之前的程式碼,提取的時間結果為:
提取時間: ['去年9月']
但實際上,我們提取的時間應該是:
上世紀80年代之前, 去年9月,3年內
原文2:
南宋紹興十年,金分兵兩路向陝西和河南大舉進攻,在很快奪回了河南、陝西之後,又率大軍向淮南大舉進攻。
用筆者之前的程式碼,提取的時間結果為:
提取時間: ['南宋']
但實際上,我們提取的時間應該是:
南宋紹興十年
因此,利用簡單的詞性標註功能來提取文字中的時間會存在漏提、錯提的情況,鑑於此,筆者想到能否用深度學習模型來實現文字中的時間提取呢?
該功能類似於命名實體識別(NER)功能,只不過NER是識別文字中的人名、地名、組織機構名,而我們這次需要識別文字中的時間。但是,它們背後的演算法原理都是一樣的,即採用序列標註模型來解決。
專案
在文章NLP(十四)自制序列標註平臺中,筆者提出了一種自制的序列標註平臺,利用該標註平臺,筆者從新聞網站中標註了大約2000份語料,標註出文本中的時間,其中75%作為訓練集(time.train檔案),10%作為驗證集(time.dev檔案),15%作為測試集(time.test檔案)。
筆者自己花了幾天的時間來標註資料,目前已累計標註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), 歡迎大家關注~