經過本文你將瞭解如何訓練一我的名、地址、組織、公司、產品、時間,共6個實體的命名實體識別模型。html
訓練建議在GPU上進行,若是你沒有GPU訓練環境,或者你想要一個訓練好的模型,能夠加做者微信(jiabao512859468),有任何相關技術問題,都歡迎和做者探討O(∩_∩)O~python
ok,下面開始咱們的模型訓練。git
下面的連接中提供了已經用brat標註好的數據文件以及brat的配置文件,由於標註內容較多放到brat里加載會比較慢,因此拆分紅了10份,每份包括3000多條樣本數據,將這10份文件和相應的配置文件放到brat目錄/data/project路徑下,而後就能夠從瀏覽器訪問文件內容以及相應的標註狀況了。程序員
提取碼:99z3算法
若是你還不知道什麼是brat,或還不清楚如何使用brat,強烈建議先閱讀前兩篇文章《用深度學習作命名實體識別(二):文本標註工具brat》、《用深度學習作命名實體識別(三):文本數據標註過程》。json
標註數據雖然有了,可是還不能知足咱們的訓練要求,由於咱們須要根據ann和txt,將其轉成訓練所需的數據格式,格式以下: api
能夠看到,每一行一個字符,字符後面跟上空格,而後跟上該字符的標註, 每一個樣本之間用空行分隔。 另外,也能夠看到這裏採用的是BIO的標註方式:瀏覽器
轉換代碼以下:服務器
# -*- coding: utf-8 -*- """ 數據格式轉化 """ import codecs import os __author__ = '程序員一一滌生' tag_dic = {"時間": "TIME", "地點": "LOCATION", "人名": "PERSON_NAME", "組織名": "ORG_NAME", "公司名": "COMPANY_NAME", "產品名": "PRODUCT_NAME"} # 轉換成可訓練的格式,最後以"END O"結尾 def from_ann2dic(r_ann_path, r_txt_path, w_path): q_dic = {} print("開始讀取文件:%s" % r_ann_path) with codecs.open(r_ann_path, "r", encoding="utf-8") as f: line = f.readline() line = line.strip("\n\r") while line != "": line_arr = line.split() print(line_arr) cls = tag_dic[line_arr[1]] start_index = int(line_arr[2]) end_index = int(line_arr[3]) length = end_index - start_index for r in range(length): if r == 0: q_dic[start_index] = ("B-%s" % cls) else: q_dic[start_index + r] = ("I-%s" % cls) line = f.readline() line = line.strip("\n\r") print("開始讀取文件:%s" % r_txt_path) with codecs.open(r_txt_path, "r", encoding="utf-8") as f: content_str = f.read() # content_str = content_str.replace("\n", "").replace("\r", "").replace("//////", "\n") print("開始寫入文本%s" % w_path) with codecs.open(w_path, "w", encoding="utf-8") as w: for i, str in enumerate(content_str): if str is " " or str == "" or str == "\n" or str == "\r": print("===============") elif str == "/": if i == len(content_str) - len("//////") + 1: # 表示到達末尾 # w.write("\n") break # 連續六個字符首尾都是/,則表示換一行 elif content_str[i + len("//////") - 1] == "/" and content_str[i + len("//////") - 2] == "/" and \ content_str[i + len("//////") - 3] == "/" and content_str[i + len("//////") - 4] == "/" and \ content_str[i + len("//////") - 5] == "/": w.write("\n") i += len("//////") else: if i in q_dic: tag = q_dic[i] else: tag = "O" # 大寫字母O w.write('%s %s\n' % (str, tag)) w.write('%s\n' % "END O") # 去除空行 def drop_null_row(r_path, w_path): q_list = [] with codecs.open(r_path, "r", encoding="utf-8") as f: line = f.readline() line = line.strip("\n\r") while line != "END O": if line != "": q_list.append(line) line = f.readline() line = line.strip("\n\r") with codecs.open(w_path, "w", encoding="utf-8") as w: for i, line in enumerate(q_list): w.write('%s\n' % line) # 生成train.txt、dev.txt、test.txt # 除8,9-new.txt分別用於dev和test外,剩下的合併成train.txt def rw0(data_root_dir, w_path): if os.path.exists(w_path): os.remove(w_path) for file in os.listdir(data_root_dir): path = os.path.join(data_root_dir, file) if file.endswith("8-new.txt"): # 重命名爲dev.txt os.rename(path, os.path.join(data_root_dir, "dev.txt")) continue if file.endswith("9-new.txt"): # 重命名爲test.txt os.rename(path, os.path.join(data_root_dir, "test.txt")) continue q_list = [] print("開始讀取文件:%s" % file) with codecs.open(path, "r", encoding="utf-8") as f: line = f.readline() line = line.strip("\n\r") while line != "END O": q_list.append(line) line = f.readline() line = line.strip("\n\r") print("開始寫入文本%s" % w_path) with codecs.open(w_path, "a", encoding="utf-8") as f: for item in q_list: if item.__contains__('\ufeff1'): print("===============") f.write('%s\n' % item) if __name__ == '__main__': data_dir = "datas" for file in os.listdir(data_dir): if file.find(".") == -1: continue file_name = file[0:file.find(".")] r_ann_path = os.path.join(data_dir, "%s.ann" % file_name) r_txt_path = os.path.join(data_dir, "%s.txt" % file_name) w_path = "%s/new/%s-new.txt" % (data_dir, file_name) from_ann2dic(r_ann_path, r_txt_path, w_path) # 生成train.txt、dev.txt、test.txt rw0("%s/new" % data_dir, "%s/new/train.txt" % data_dir)
注意把該代碼文件和datas目錄放在一級,而後把從雲盤下載的10個標註數據文件放在datas目錄下,而後再執行上面的代碼,執行完成後,會在datas/new目錄下生成一些文件,咱們要的是其中的train、dev、test三個txt文件。
ok,到此咱們的訓練數據就準備好了,接下來咱們須要準備預訓練模型。
使用預訓練模型作微調的訓練方式稱爲遷移學習,不太明白什麼意思也不要緊,只要知道這樣作可讓咱們的訓練收斂的更快,而且可使得在較少的訓練樣本上訓練也能獲得不錯的效果。這裏咱們將使用目前最好的天然語言表徵模型之一的bert的中文預訓練模型。若是你還不清楚bert,也不要緊,這裏你只要知道使用bert能夠獲得比word2vec(詞向量)更好的表徵便可。
bert在中文維基百科上預訓練的模型下載地址:
https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip
下載下來,解壓後會看到以下幾個文件:
這裏咱們已經將bert_model.ckpt.data-00000-of-00001文件複製一份,命名爲bert_model.ckpt,因此多了一個bert_model.ckpt文件。由於不這樣作的話,後續的訓練會報錯,找不到ckpt。
以上工做都完成後,就能夠進入訓練環節了。
強烈建議使用GPU來訓練,不然你會瘋的。關於GPU環境的搭建能夠參考這篇文章《如何在阿里雲租一臺GPU服務器作深度學習?》。
本文的模型訓練參考的是github上一個開源的項目,該項目是基於bert+crf算法來訓練命名實體模型的,比基於lstm+crf的項目的效果要好,下面是該項目的地址:
https://github.com/macanv/BERT-BiLSTM-CRF-NER
筆者基於該項目作了一些代碼修改,修改的目的以下:
修改後的項目地址:
提取碼:sibq
修改後的項目下載下來解壓後,須要作3件事情:
接下來,執行如下命令進行訓練: nohup python bert_lstm_ner.py -max_seq_length 500 -batch_size 2 -learning_rate 2e-5 -num_train_epochs 3.0 -filter_adam_var True -verbose -data_dir person_data -output_dir output -init_checkpoint models/chinese_L-12_H-768_A-12/bert_model.ckpt -bert_config_file models/chinese_L-12_H-768_A-12/bert_config.json -vocab_file models/chinese_L-12_H-768_A-12/vocab.txt >log.out 2>&1 &
讓咱們對命令中的參數作一些解釋:
開始訓練後,經過如下命令查看訓練過程的日誌信息: tail -f log.out
下圖截取自訓練結束後的部分輸出日誌: 能夠看到評估損失值降到了0.04862。
訓練會持續3個多小時(在一塊Nvidia Geforce RTX2060 GPU上),結束後,會看到對test.txt樣本進行測試的結果:
每訓練500步,程序會在output目錄下保存一個模型文件,咱們能夠經過修改output目錄下的checkpoint文件來指定要用來測試的模型文件。
而後執行以下命令來對test.txt中的內容進行測試(注意bert_lstm_ner-test.py中的配置要和訓練時指定的參數配置一致): python bert_lstm_ner-test.py
測試輸出的結果和上面訓練完成後輸出的結果的格式是同樣的。若是你按照本文的步驟,完整的走到這裏了,那麼你已經有了一個能夠識別 人名、地址、組織、公司、產品、時間,共6個實體的命名實體識別模型,下一篇文章《用深度學習作命名實體識別(五):模型使用》將介紹如何使用這個模型來提供一個rest風格的實體識別接口,對該接口傳入一個句子參數,接口會返回句子中的人名、地址、組織、公司、產品、時間信息。
ok,本篇就這麼多內容啦~,感謝閱讀O(∩_∩)O,88~
<span style="display:block;text-align:center;color:red;">名句分享</span>
<span style="display:block;text-align:left;color:#576B95;">勿以小惡棄人大美,勿以小怨忘人大恩。——曾國藩</span>
<span style="display:block;text-align:center;color:red;">爲您推薦</span>
pycharm2019和idea2019版永久激活 手把手教你用深度學習作物體檢測(一): 快速感覺物體檢測的酷炫
本博客內容來自公衆號「程序員一一滌生」,歡迎掃碼關注 o(∩_∩)o