1. 程式人生 > >python命名實體抽取學習記錄(1)

python命名實體抽取學習記錄(1)

一、命名實體識別基本概念    

      資訊有多種表現形式,一個重要的形式就是結構化資料:即實體和關係的規範和可預測的組織。而現實生活中大多數自然語言句子是非結構化資料,為從文字獲得其意義,我們首先需要將自然語言資料轉化為結構化資料,然後利用強大的查詢工具,如sql。這種從文字獲取意義的方法被稱為資訊提取。

    文字資訊提取處理的順序是:首先,使用句子分割器將該文件的原始文字分割成句,然後使用分詞器將每個句子進一步細分為詞,之後對每個句子進行詞性標註,對每個標註過的句子進行命名實體識別,最後使用關係識別搜尋文字中不同實體間可能的關係。所以在對文件進行命名實體識別之前必須對文件進行分句,分詞和詞性標註。在命名實體識別中,我們需要分割和標註可能組成具有某種關係的實體,通常是名詞短語。

    命名實體識別(Named Entity Recognition)主要是識別出文本中出現的專有名稱和有意義的數量短語並進行分類。命名實體(Named Entity )主要包括實體(組織名,人名,地名)、時間表達式(日期、時間)和數字表達式(貨幣值、百分數等)。其中,對時間表達式和數字表達式的識別相對於對組織名,人名和地名這些實體的識別來說要簡單些,因為時間表達式和數字表達式在形式上有規律可循,而針對組織名,人名和地名,由於其具有開放性和發展性的特點,識別難度比較大。

    目前已有的命名實體識別的方法主要分為兩大類:基於規則的方法和基於統計的方法。

    基於規則的方法主要是根據待識別的命名實體的語言學上的表現形式,人為設定一些規則來識別命名實體的方法。這類方法實現的效果很大程度上依賴於規則的設定且需要大量的專業知識,而且因為不同領域內的實體具有不同的規則,所以對每個新領域的文字處理都要重新設定規則。使用基於規則的方法來進行命名實體識別比較消耗時間和消耗人力。  

      基於統計的方法主要利用原始的或經過加工的(人工標註的)語料進行訓練,其語料的加工(標註)不需要非常多的語言學的知識,而且小規模的語料可以在可接受的時間和人力代價下完成,且基於統計的方法實現的命名實體識別在新的領域使用時可以不作改動或者做較少的改動,只需要利用新領域的語料進行訓練即可。但是由於基於統計的方法獲取的概率知識不如基於規則的方法所具有的專家的語言學知識的可靠性,所以基於統計的命名實體識別系統的效能要比基於規則的命名實體識別的效能要低。用於命名實體識別的基於統計的方法主要有:N元模型、隱馬爾剋夫模型(HMM)、最大熵模型(ME)、決策樹(Decision Tree)等等。目前評價效能最好的是隱馬爾剋夫模型。

二、命名實體識別實現

       目前國內有多個開源的中文語言處理工具可供直接呼叫實現命名實體識別,比如復旦大學研發的fudanNLP,中科院的NLPIR分詞系統(又名ICTCLAS2013)和哈工大的LTP。

        因為從一開始接觸自然語言處理就是用Python進行基本的操作,恰好哈工大的LTP提供python介面,所以就直接用python呼叫其封裝成的pyltp的模組實現命名實體識別。

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

import codecs
from pyltp import SentenceSplitter
from pyltp import Segmentor
from pyltp import Postagger
from pyltp import NamedEntityRecognizer

#讀取待處理文字
'''
    讀取的文字格式是encoding引數值,codecs函式將其轉化為unicode格式。
'''
news_files = codecs.open('news_content.txt','r',encoding='utf8')
news_list = news_files.readlines()
#type(news_list[1].encode('utf-8'))


#分句
'''
此函式引數輸入的格式必須為str格式,所以直接獲取的list裡的引數值需要
通過encode('utf-8'),從Unicode轉換為str
'''
def sentence_splitter(sentence):
    sents = SentenceSplitter.split(sentence)
    print '\n'.join(sents)
    sents_list = list(sents)
    return sents_list
    

#分詞   
def segmentor(sentence):
    segmentor =  Segmentor()
    segmentor.load('E:\\ltp-data-v3.3.1\\ltp_data\\cws.model')#載入模型
    words = segmentor.segment(sentence) #分詞
    #預設可以這樣輸出
    #print '\t'.join(words)
    #可以轉化成List輸出
    word_list = list(words)
    segmentor.release()#釋放模型
    return word_list 

#詞性標註
def posttagger(words):
    postagger = Postagger()
    postagger.load('E:\\ltp-data-v3.3.1\\ltp_data\\pos.model')
    posttags=postagger.postag(words)  #詞性標註
    postags = list(posttags)
    postagger.release() #釋放模型
    #print type(postags)
    return postags

#命名實體識別
def ner(words,postags):
    print '命名實體開始!'
    recognizer = NamedEntityRecognizer()
    recognizer.load('E:\\ltp-data-v3.3.1\\ltp_data\\ner.model') #載入模型
    netags = recognizer.recognize(words,postags) #命名實體識別
    for word,ntag in zip(words,netags):
        print word+'/'+ ntag
    recognizer.release()   #釋放模型
    nerttags = list(netags)
    return nerttags

#新建一個txt檔案儲存命名實體識別的結果    
out_file = codecs.open('out_nerfile.txt','w',encoding='utf8')
sents = sentence_splitter(news_list[1].encode('utf-8'))
for sent in sents:
    words=segmentor(sent)
    tags = posttagger(words)
    nertags = ner(words,tags)
    for word,nertag in zip(words,nertags):
        out_file.write(word.decode('utf-8')+'/'+nertag.decode('utf-8')+' ')
            
out_file.close()

out_file.txt的結果是:

三、命名實體提取

       在成功進行命名實體識別後,要把文字中的命名實體識別給提取出來,就需要把組成命名實體的詞給單個提取出來且根據標記連線短語片語。在這裡,主要是提取命名實體三大類(組織、人名和地名)中的組織名。我主要利用正則表示式把命名實體的每個詞(詞的形式為:詞語/命名實體標註)給提取出來,再利用().join函式把單個詞給連線起來。直接上程式碼。

# -*- coding: utf-8 -*-
"""
提取的命名實體型別是組織
"""

import codecs
import re
#讀取命名實體識別的結果
file=codecs.open('out_nerfile.txt','r',encoding='utf8')
file_content = file.read()
#以空格為切割標誌,使每個命名實體標註片語為列表的一個元素
file_list = file_content.split()
#建立兩個空列表,分別儲存命名實體和構成命名實體短語片語的各個詞
ner_list=[]
phrase_list=[]
#使用正則表示式,判斷單個詞是否是命名實體
for word in file_list:
    if(re.search('Ni$',word)):
        print '提取出的命名實體為:',word
        word_list=word.split('/')
        #判斷是否單個詞是否是命名實體
        if re.search(r'^S',word_list[1]):
            ner_list.append(word_list[0])
        #判斷多個詞構成的命名實體,並對其進行連詞處理
        elif re.search(r'^B',word_list[1]):
            phrase_list.append(word_list[0])
        elif re.search(r'^I',word_list[1]):
            phrase_list.append(word_list[0])
        else:
            phrase_list.append(word_list[0])
            #把list轉換為字串.
            ner_phrase=''.join(phrase_list)
            ner_list.append(ner_phrase) 
            phrase_list=[]
#輸出命名實體識別的結果
for ner in ner_list:
    print ner           
      輸出的結果如下圖所示:

    

四、結語

      雖然初步經歷一系列的關於自然語言處理的學習、藉助各種工具、搜尋相關部落格勉勉強強把命名實體提取給成功抽取出來了,但是仍然不清楚其內在的機理,在後續的學習和實踐中會注重對其本質的理解分析,還要繼續啃《python自然語言處理》這本小紅書哪~另外,在實踐操作過程中,遇到很多bug,後續再慢慢整理吧。