實現spaCy實體標注模型


命名實體識別是指對現實世界中某個對象的名稱的識別。與詞性標注一樣,是自然語言處理的技術基礎之一。它的作用主要是通過模型識別出文本中需要的實體,也可以推導出實體之間的關系(實體消歧)。
本文介紹的是運用Python從頭訓練一個spaCy模型來識別中標公告中中標公司的名字,現通過爬蟲爬取了大約200篇中標公告(爬取過程省略),利用人工對其中的150篇訓練集公告進行標注中標公司,使用spaCy訓練一個實體抽取模型並進行本地保存,再調取訓練好的模型對剩余的50篇公告進行測試,檢驗該模型對中標公司提取的准確率。

1、獲取數據和數據清洗

首先,需要對爬取下來的中標公告文件數據進行清洗處理,分別對其進行去重和刪除網絡格式(比如&nbsp),清洗前后對比如下:
數據清洗對比

2、標注實體

對於清洗后的數據集,需要把標注后的結果以下例格式進行儲存(即文本+實體標注的索引+實體標注類別標簽):

TRAIN_DATA = [
    ('Who is Shaka Khan?', {
        'entities': [(7, 17, 'PERSON')]  
        ###實體標注的索引從0開始17是最后一字符的索引+1
    }),
    ('I like London and Berlin.', {
        'entities': [(7, 13, 'LOC'), (18, 24, 'LOC')]
    })
]

也就是說,需要找出需要被標注實體的開始索引和結束索引。由於有204篇公告,每篇公告都都需要人工標注,鑒於數據量和尋找索引工作量都很大,所以通過編寫Python程序且小組分工后每位成員人工標注中標公司名稱,並把結果儲存成上述格式。標注代碼如下:

import pandas as pd
import re

def find_company_name(name,text):
    if re.search(name,text):
        tup = list(re.search(name,text).span())
        tup.append("LOC")
        tup=tuple(tup)
        return (text,{'entities':tup})
    return False
from IPython.display import clear_output as clear

textall= []
data5 = pd.read_csv("home_work_clear.txt",encoding="utf-8",sep="\n",header=None)
start=int(input("請輸入第幾行開始"))
end=int(input("請輸入第幾行結束"))

for line in data5[0][start:end]:
    clear()#清除輸出
    print(line)
    while True:
        panduan="not break"
        company_name = input("請輸入公司名字")
        if find_company_name(company_name,line):
            print(find_company_name(company_name,line))
            textall.append(find_company_name(company_name,line))
            break
        elif company_name == "break":
            panduan='break'
            break
        else:
            print("輸入公司名字可能有誤,重新輸入")
        print
    if panduan=="break":
        break

f = open("result%d-%d.txt" % (start,end),"w+",encoding="gbk")
f.write(str(textall))
f.close()

(1)首先輸入需要標注的開始位置和結束位置:

輸入標注范圍

(2)然后輸入每份公告的中標公司名稱:

輸入中標公司名稱

(3)最后,把標注后的結果保存到txt中:

標注結果
上圖表示文本中索引121-133為中標公司。

3、划分訓練集和測試集

經標注后,一共有204個數據集,其中設定訓練集150篇公告,測試集為54篇公告:
數據集總數

4、spaCy模型訓練

對於處理好的訓練集,輸入到spaCy模型中進行訓練,並對訓練后的模型進行保存,代碼如下:

def main(model=None, output_dir = None, n_iter=100):   ##參數意義,model:是否存在現有的模型,output_dir:模型存儲位置,n_iter迭代次數
    """Load the model, set up the pipeline and train the entity recognizer."""
    if model is not None:
        nlp = spacy.load(model)  # load existing spaCy model  ###這里的作用是對現有的模型進行優化  *非常重要
        print("Loaded model '%s'" % model)
    else:
        nlp = spacy.blank('zh')  # create blank Language class
        print("Created blank 'zh' model")

        if 'ner' not in nlp.pipe_names:
        ner = nlp.create_pipe('ner')
        nlp.add_pipe('ner', last=True)
    # otherwise, get it so we can add labels
    else:
        ner = nlp.get_pipe('ner')

    # add labels
    for _, annotations in TRAIN_DATA:      ##添加train data的標簽
        for ent in annotations.get('entities'):
            ner.add_label(ent[2])

    other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
    with nlp.disable_pipes(*other_pipes):  # 僅訓練我們標注的標簽,假如沒有則會對所有的標簽訓練,
                                           #建議不要對下載的spacy的模型進行訓練可能導致下載的語言模型出錯,訓練一個空白語言模型就好
        optimizer = nlp.begin_training()   ##模型初始化
        for itn in range(n_iter):
            random.shuffle(TRAIN_DATA)     ##訓練數據每次迭代打亂順序
            losses = {}                    ##定義損失函數
            for text, annotations in TRAIN_DATA:
                example = Example.from_dict(nlp.make_doc(text), annotations)    ##對數據進行整理成新模型需要的數據
                print("example:",example)
                nlp.update(
                    [example],  # batch of annotations
                    drop=0.5,  # dropout - make it harder to memorise data
                    sgd=optimizer,  # 更新權重
                    losses=losses)
            print(losses)

    # 保存模型
    if output_dir is not None:
        output_dir = Path(output_dir)
        if not output_dir.exists():
            output_dir.mkdir()
        nlp.to_disk(output_dir)
        print("Saved model to", output_dir)

對上述訓練過程進行計時,由於代碼運行時間過久,測試得當訓練5個數據集需要花費差不多1分鍾的時間:
訓練5個數據集花費時間
通過檢查發現代碼中的迭代次數為100,為了提高速度把100改為了50,訓練150個訓練集,花費時間為(約47分鍾):
迭代50次的花費時間

並把訓練好的模型保存到本地,方便后續測試時可以直接加載已訓練好的模型得出預測結果。

5、測試集測試模型

訓練完spaCy模型后,導入測試數據進行測試,代碼如下:

import spacy

def load_model_test(path,text):
    nlp = spacy.load(path)
    print("Loading from", path)
    doc = nlp(text)
    for i in doc.ents:
        print(i.text,i.label_)

訓練結果如下:
測試集預測結果
根據上圖測試結果來看,總體預測結果良好,能准確找出中標公司名稱。但是,也存在少許預測失敗的數據,說明該模型還不是非常精確,后續可以在迭代次數和訓練數據量兩方面進行改進。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM