基於百度開源項目LAC實現文本分詞、詞性標註和命名實體識別

         文本分詞、詞性標註和命名實體識別都是自然語言處理領域裏面很基礎的任務,他們的精度決定了下游任務的精度,今天在查資料的時候無意間發現了一個很好玩的開源項目,具體查了一下才知道這是百度開源的一個主要用於詞性標註和命名實體識別的項目,決定拿來嘗試一下。

      首先是項目環境的配置安裝,當前已經支持一鍵式安裝了,具體命令如下所示:

python -m pip install LAC

      簡單進行一下安裝驗證,成功截圖如下所示:

    接下來就可以進行使用了,官方GitHub地址在這裏。官方給出來的一些講解如下所示:

工具介紹
LAC全稱Lexical Analysis of Chinese,是百度自然語言處理部研發的一款聯合的詞法分析工具,實現中文分詞、詞性標註、專名識別等功能。該工具具有以下特點與優勢:

效果好:通過深度學習模型聯合學習分詞、詞性標註、專名識別任務,整體效果F1值超過0.91,詞性標註F1值超過0.94,專名識別F1值超過0.85,效果業內領先。
效率高:精簡模型參數,結合Paddle預測庫的性能優化,CPU單線程性能達800QPS,效率業內領先。
可定製:實現簡單可控的干預機制,精準匹配用戶詞典對模型進行干預。詞典支持長片段形式,使得干預更爲精準。
調用便捷:支持一鍵安裝,同時提供了Python、Java和C++調用接口與調用示例,實現快速調用和集成。
支持移動端: 定製超輕量級模型,體積僅爲2M,主流千元手機單線程性能達200QPS,滿足大多數移動端應用的需求,同等體積量級效果業內領先。
安裝與使用
在此我們主要介紹Python安裝與使用,其他語言使用:

C++
JAVA
Android
安裝說明
代碼兼容Python2/3

全自動安裝: pip install lac

半自動下載:先下載http://pypi.python.org/pypi/lac/,解壓後運行 python setup.py install

安裝完成後可在命令行輸入lac或lac --segonly啓動服務,進行快速體驗

國內網絡可使用百度源安裝,安裝速率更快:pip install lac -i https://mirror.baidu.com/pypi/simple

功能與使用
分詞
代碼示例:
from LAC import LAC

# 裝載分詞模型
lac = LAC(mode='seg')

# 單個樣本輸入,輸入爲Unicode編碼的字符串
text = u"LAC是個優秀的分詞工具"
seg_result = lac.run(text)

# 批量樣本輸入, 輸入爲多個句子組成的list,平均速率會更快
texts = [u"LAC是個優秀的分詞工具", u"百度是一家高科技公司"]
seg_result = lac.run(texts)
輸出:
【單樣本】:seg_result = [LAC, 是, 個, 優秀, 的, 分詞, 工具]
【批量樣本】:seg_result = [[LAC, 是, 個, 優秀, 的, 分詞, 工具], [百度, 是, 一家, 高科技, 公司]]
詞性標註與實體識別
代碼示例:
from LAC import LAC

# 裝載LAC模型
lac = LAC(mode='lac')

# 單個樣本輸入,輸入爲Unicode編碼的字符串
text = u"LAC是個優秀的分詞工具"
lac_result = lac.run(text)

# 批量樣本輸入, 輸入爲多個句子組成的list,平均速率更快
texts = [u"LAC是個優秀的分詞工具", u"百度是一家高科技公司"]
lac_result = lac.run(texts)
輸出:
每個句子的輸出其切詞結果word_list以及對每個單詞的標註tags_list,其格式爲(word_list, tags_list)

【單樣本】: lac_result = ([百度, 是, 一家, 高科技, 公司], [ORG, v, m, n, n])
【批量樣本】:lac_result = [
                    ([百度, 是, 一家, 高科技, 公司], [ORG, v, m, n, n]),
                    ([LAC, 是, 個, 優秀, 的, 分詞, 工具], [nz, v, q, a, u, n, n])
                ]
詞性和專名類別標籤集合如下表,其中我們將最常用的4個專名類別標記爲大寫的形式:

標籤	含義	標籤	含義	標籤	含義	標籤	含義
n	普通名詞	f	方位名詞	s	處所名詞	nw	作品名
nz	其他專名	v	普通動詞	vd	動副詞	vn	名動詞
a	形容詞	ad	副形詞	an	名形詞	d	副詞
m	數量詞	q	量詞	r	代詞	p	介詞
c	連詞	u	助詞	xc	其他虛詞	w	標點符號
PER	人名	LOC	地名	ORG	機構名	TIME	時間
定製化功能
在模型輸出的基礎上,LAC還支持用戶配置定製化的切分結果和專名類型輸出。當模型預測匹配到詞典的中的item時,會用定製化的結果替代原有結果。爲了實現更加精確的匹配,我們支持以由多個單詞組成的長片段作爲一個item。

我們通過裝載詞典文件的形式實現該功能,詞典文件每行表示一個定製化的item,由一個單詞或多個連續的單詞組成,每個單詞後使用'/'表示標籤,如果沒有'/'標籤則會使用模型默認的標籤。每個item單詞數越多,干預效果會越精準。

詞典文件示例

這裏僅作爲示例,展現各種需求情況下的結果。後續還將開放以通配符配置詞典的模式,敬請期待。

春天/SEASON
花/n 開/v
秋天的風
落 陽
代碼示例
from LAC import LAC
lac = LAC()

# 裝載干預詞典
lac.load_customization('custom.txt')

# 干預後結果
custom_result = lac.run(u"春天的花開秋天的風以及冬天的落陽")
以輸入“春天的花開秋天的風以及冬天的落陽”爲例,原本輸出結果爲:
春天/TIME 的/u 花開/v 秋天/TIME 的/u 風/n 以及/c 冬天/TIME 的/u 落陽/n
添加示例中的詞典文件後的結果爲:
春天/SEASON 的/u 花/n 開/v 秋天的風/n 以及/c 冬天/TIME 的/u 落/n 陽/n
增量訓練
我們也提供了增量訓練的接口,用戶可以使用自己的數據,進行增量訓練,首先需要將數據轉換爲模型輸入的格式,並且所有數據文件均爲"UTF-8"編碼:

1. 分詞訓練
數據樣例

與大多數開源分詞數據集格式一致,使用空格作爲單詞切分標記,如下所示:

LAC 是 個 優秀 的 分詞 工具 。
百度 是 一家 高科技 公司 。
春天 的 花開 秋天 的 風 以及 冬天 的 落陽 。
代碼示例
from LAC import LAC

# 選擇使用分詞模型
lac = LAC(mode = 'seg')

# 訓練和測試數據集,格式一致
train_file = "./data/seg_train.tsv"
test_file = "./data/seg_test.tsv"
lac.train(model_save_dir='./my_seg_model/',train_data=train_file, test_data=test_file)

# 使用自己訓練好的模型
my_lac = LAC(model_path='my_seg_model')
2. 詞法分析訓練
樣例數據

在分詞數據的基礎上,每個單詞以“/type”的形式標記其詞性或實體類別。值得注意的是,詞法分析的訓練目前僅支持標籤體系與我們一致的數據。後續也會開放支持新的標籤體系,敬請期待。

LAC/nz 是/v 個/q 優秀/a 的/u 分詞/n 工具/n 。/w
百度/ORG 是/v 一家/m 高科技/n 公司/n 。/w
春天/TIME 的/u 花開/v 秋天/TIME 的/u 風/n 以及/c 冬天/TIME 的/u 落陽/n 。/w
代碼示例
from LAC import LAC

# 選擇使用默認的詞法分析模型
lac = LAC()

# 訓練和測試數據集,格式一致
train_file = "./data/lac_train.tsv"
test_file = "./data/lac_test.tsv"
lac.train(model_save_dir='./my_lac_model/',train_data=train_file, test_data=test_file)

# 使用自己訓練好的模型
my_lac = LAC(model_path='my_lac_model')

      接下來,我們基於自己的數據集來編寫一些有意思的功能,其實在做命名實體識別的時候我突然想到,這個模塊可以代替人工自動生成有標註的數據集用於NER模型的訓練,當然了這裏自動生成的數據集可能是不準確的,大致是準確的,主要是想節省人工標註的成本。

     首先是加載本地數據集,代碼實現如下所示:

def loadData(data='toutiao_news_dataset.txt'):
    '''
    加載本地數據集
    '''
    with open(data,encoding='utf-8') as f:
        data_list=[one.strip().split('#') for one in f.readlines() if one.strip()]
    print('data_list_length: ',len(data_list))
    for i in range(10):
        print('index: '+str(i)+', content: '+data_list[i][-1])
    return data_list

        數據集樣例數據如下所示:

房產#。。。。。。。。。。。。。。。。。。。。。
房產#。。。。。。。。。。。。。。。。。。。。。
房產#。。。。。。。。。。。。。。。。。。。。。
汽車#。。。。。。。。。。。。。。。。。。。。。
汽車#。。。。。。。。。。。。。。。。。。。。。
汽車#。。。。。。。。。。。。。。。。。。。。。

       由於官方審覈限制,這裏的數據集內容替換成了省略號。 

       接下來我們隨機挑選數據集進行命名實體識別,代碼實現如下所示:

def NERModel(data_list):
    '''
    詞性標註與命名實體識別
    結果實例:
             [['京城', '最', '值得', '你', '來', '場', '文化', '之', '旅', '的', '博物館'], ['nz', 'd', 'v', 'r', 'v', 'q', 'n', 'u', 'n', 'u', 'n']]
    '''
    lac = LAC(mode='lac')
    texts = [one[-1] for one in data_list]
    lac_result = lac.run(texts)
    for i in range(10):
        print('index: ',str(i),', lac_result: ',lac_result[i])
        words, tags = lac_result[i]
        print(u" ".join(u"%s/%s" % (word, tag) for word, tag in zip(words, tags)))
        print('====================================================================')

     結果如下所示:

data_list_length:  382688
index: 0, content: 京城最值得你來場文化之旅的博物館
index: 1, content: 發酵牀的墊料種類有哪些?哪種更好?
index: 2, content: 上聯:黃山黃河黃皮膚黃土高原。怎麼對下聯?
index: 3, content: 林徽因什麼理由拒絕了徐志摩而選擇梁思成爲終身伴侶?
index: 4, content: 黃楊木是什麼樹?
index: 5, content: 上聯:草根登上星光道,怎麼對下聯?
index: 6, content: 什麼是超寫實繪畫?
index: 7, content: 松濤聽雨鶯婉轉,下聯?
index: 8, content: 上聯:老子騎牛讀書,下聯怎麼對?
index: 9, content: 上聯:山水醉人何須酒。如何對下聯?
index:  0 , lac_result:  [['京城', '最', '值得', '你', '來', '場', '文化', '之',
 '旅', '的', '博物館'], ['nz', 'd', 'v', 'r', 'v', 'q', 'n', 'u', 'n', 'u', 'n']
]
京城/nz 最/d 值得/v 你/r 來/v 場/q 文化/n 之/u 旅/n 的/u 博物館/n
====================================================================
index:  1 , lac_result:  [['發酵牀', '的', '墊料', '種類', '有', '哪些', '?', '
哪', '種', '更好', '?'], ['nz', 'u', 'n', 'n', 'v', 'r', 'w', 'r', 'q', 'a', 'w
']]
發酵牀/nz 的/u 墊料/n 種類/n 有/v 哪些/r ?/w 哪/r 種/q 更好/a ?/w
====================================================================
index:  2 , lac_result:  [['上聯', ':', '黃山', '黃河', '黃', '皮膚', '黃土高原
', '。', '怎麼', '對', '下聯', '?'], ['n', 'w', 'LOC', 'LOC', 'a', 'n', 'LOC',
'w', 'r', 'p', 'n', 'w']]
上聯/n :/w 黃山/LOC 黃河/LOC 黃/a 皮膚/n 黃土高原/LOC 。/w 怎麼/r 對/p 下聯/n
?/w
====================================================================
index:  3 , lac_result:  [['林徽因', '什麼', '理由', '拒絕', '了', '徐志摩', '而
', '選擇', '梁思', '成爲', '終身伴侶', '?'], ['PER', 'r', 'n', 'v', 'u', 'PER',
 'c', 'v', 'PER', 'v', 'n', 'w']]
林徽因/PER 什麼/r 理由/n 拒絕/v 了/u 徐志摩/PER 而/c 選擇/v 梁思/PER 成爲/v 終身
伴侶/n ?/w
====================================================================
index:  4 , lac_result:  [['黃楊木', '是', '什麼', '樹', '?'], ['n', 'v', 'r',
'n', 'w']]
黃楊木/n 是/v 什麼/r 樹/n ?/w
====================================================================
index:  5 , lac_result:  [['上聯', ':', '草根', '登上', '星光', '道', ',', '怎
麼', '對', '下聯', '?'], ['n', 'w', 'n', 'v', 'n', 'v', 'w', 'r', 'p', 'n', 'w'
]]
上聯/n :/w 草根/n 登上/v 星光/n 道/v ,/w 怎麼/r 對/p 下聯/n ?/w
====================================================================
index:  6 , lac_result:  [['什麼', '是', '超', '寫實', '繪畫', '?'], ['r', 'v',
 'vd', 'v', 'n', 'w']]
什麼/r 是/v 超/vd 寫實/v 繪畫/n ?/w
====================================================================
index:  7 , lac_result:  [['松濤聽雨鶯', '婉轉', ',', '下聯', '?'], ['nz', 'a'
, 'w', 'n', 'w']]
松濤聽雨鶯/nz 婉轉/a ,/w 下聯/n ?/w
====================================================================
index:  8 , lac_result:  [['上聯', ':', '老子', '騎', '牛', '讀書', ',', '下聯
', '怎麼', '對', '?'], ['n', 'w', 'n', 'v', 'n', 'v', 'w', 'n', 'r', 'p', 'w']]

上聯/n :/w 老子/n 騎/v 牛/n 讀書/v ,/w 下聯/n 怎麼/r 對/p ?/w
====================================================================
index:  9 , lac_result:  [['上聯', ':', '山水醉人', '何須', '酒', '。', '如何',
 '對', '下聯', '?'], ['n', 'w', 'n', 'v', 'n', 'w', 'r', 'p', 'n', 'w']]
上聯/n :/w 山水醉人/n 何須/v 酒/n 。/w 如何/r 對/p 下聯/n ?/w
====================================================================

    這裏我們發現了一處很明顯的錯誤:

    梁思成,我們都知道是一個人名,這裏被拆分開了,也就是識別錯誤了,我們可以自定義實體字典來解決這二個問題,代碼實現如下所示:

def NERModel(data_list):
    '''
    詞性標註與命名實體識別
    結果實例:
             [['京城', '最', '值得', '你', '來', '場', '文化', '之', '旅', '的', '博物館'], ['nz', 'd', 'v', 'r', 'v', 'q', 'n', 'u', 'n', 'u', 'n']]
    '''
    lac = LAC(mode='lac')
    lac.load_customization('mydict.txt')
    texts = [one[-1] for one in data_list]
    lac_result = lac.run(texts)
    for i in range(10):
        print('index: ',str(i),', lac_result: ',lac_result[i])
        words, tags = lac_result[i]
        print(u" ".join(u"%s/%s" % (word, tag) for word, tag in zip(words, tags)))
        print('====================================================================')

      這裏mydict.txt內容如下所示:

   接下來我們再來看下執行結果:

    可以看到:梁思成這個名字已經正確識別出來了。

     接下來我們實踐一下分詞功能,代碼實現如下所示:

def cutModel(data_list):
    '''
    分詞
    結果實例:
            ['京城', '最', '值得', '你', '來', '場', '文化', '之','旅', '的', '博物館']
    '''
    lac = LAC(mode = 'seg')
    texts = [one[-1] for one in data_list]
    lac_result = lac.run(texts)
    for i in range(10):
        print('index: ',str(i),', lac_result: ',lac_result[i])

    測試結果輸出如下所示:

       整體來說,安裝配置和使用實踐都很簡單,後面會結合命名實體識別任務來構建自動數據集。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章