1. 程式人生 > >用深度學習做命名實體識別(四)——模型訓練

用深度學習做命名實體識別(四)——模型訓練

通過本文你將瞭解如何訓練一個人名、地址、組織、公司、產品、時間,共6個實體的命名實體識別模型。

準備訓練樣本

下面的連結中提供了已經用brat標註好的資料檔案以及brat的配置檔案,因為標註內容較多放到brat里加載會比較慢,所以拆分成了10份,每份包括3000多條樣本資料,將這10份檔案和相應的配置檔案放到brat目錄/data/project路徑下,然後就可以從瀏覽器訪問檔案內容以及相應的標註情況了。

  • 連結:https://pan.baidu.com/s/1-wjQnvCSrbhor9x3GD6WSA 

  • 提取碼:99z3

如果你還不知道什麼是brat,或還不清楚如何使用brat,強烈建議先閱讀前兩篇文章《用深度學習做命名實體識別(二):文字標註工具brat》、《用深度學習做命名實體識別(三):文字資料標註過程》。

標註資料雖然有了,但是還不能滿足我們的訓練要求,因為我們需要根據ann和txt,將其轉成訓練所需的資料格式,格式如下:

可以看到,每一行一個字元,字元後面跟上空格,然後跟上該字元的標註, 每個樣本之間用空行分隔。
另外,也可以看到這裡採用的是BIO的標註方式:

  • B,即Begin,表示開始
  • I,即Intermediate,表示中間
  • O,即Other,表示其他,用於標記無關字元

轉換程式碼如下:

# -*- 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

筆者基於該專案做了一些程式碼修改,修改的目的如下:

  • 原來的專案是採用install的方式直接將專案安裝到你的python虛擬環境下,然後通過命令列執行訓練,筆者直接調整了原始碼,為了可以基於原始碼執行一些除錯;
  • 原來的專案訓練的時候幾乎沒有日誌資訊,修改後的專案可以看到訓練日誌;
  • 原來的專案只能在訓練結束後輸出評估結果,修改後的專案可以讓評估脫離訓練過程獨立進行。

修改後的專案地址:

  • 連結:https://pan.baidu.com/s/1Bht_-K9i-7WUbXdvG4sdpg

  • 提取碼:sibq

修改後的專案下載下來解壓後,需要做3件事情:

  1. 將之前下載的bert預訓練模型chinese_L-12_H-768_A-12目錄以及目錄中的檔案放到專案的models目錄下。
  2. 將之前準備的train、dev、test三個檔案放到person_data目錄下。
  3. 為該專案新建一個python的虛擬環境,然後安裝所需要的依賴包,關於需要哪些依賴包,專案中的requirement.txt是這麼描述的:

    tensorflow的安裝,因為我們是在GPU上訓練,所以只需要安裝tensorflow-gpu,筆者安裝的是tensorflow1.13.1版本,因為筆者的CUDA版本是10.0。

接下來,執行以下命令進行訓練:
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 &

讓我們對命令中的引數做一些解釋:

  • nohup
    使用nohup命令,可以保證在命令視窗被關閉,或遠端連結中斷的情況下,不影響遠端python程式的執行。python程式執行過程中的日誌資訊會儲存在當前資料夾下的log.out檔案中。
  • max_seq_length
    每個樣本的最大長度,不能超過512。如果你的某些樣本超過了這個長度,需要截斷。截斷程式碼可以使用專案根路徑下的data_process.py檔案。
  • batch_size
    每次送到模型進行訓練的樣本數量。一般是2冪次方。如果你的GPU視訊記憶體夠大,可以嘗試增大batch_size。
  • learning_rate
    初始學習率,用於調整模型的學習速度,過大過小都不好。剛開始訓練時:學習率以 0.01 ~ 0.001 為宜。接近訓練結束:學習速率的衰減應該在100倍以上。這裡因為我們採用的是遷移學習,由於預模型本身已經在原始資料集上收斂,此時學習率應該設定的較小,所以這裡設定成0.00002。
  • num_train_epochs
    每次用完所有樣本後,記為一個epoch。這裡是指設定多少個epoch後訓練結束。
  • filter_adam_var
    儲存訓練模型的時候是否過濾掉Adam的引數,預設為False。設定為True可以減小模型的大小。
  • verbose
    加上該引數就會開啟tensorflow的日誌。
  • data_dir
    train、dev、test資料所在的目錄。
  • output_dir
    模型輸出目錄。
  • init_checkpoint
    預訓練模型的路徑,這裡我們使用了bert的中文預訓練模型。
  • bert_config_file
    bert模型的配置檔案所在路徑。
  • vocab_file
    bert的詞彙表文件路徑。

開始訓練後,通過以下命令檢視訓練過程的日誌資訊:
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~

名句分享


勿以小惡棄人大美,勿以小怨忘人大恩。——曾國藩

為您推薦


pycharm2019和idea2019版永久啟用
手把手教你用深度學習做物體檢測(一): 快速感受物體檢測的酷炫

本部落格內容來自公眾號“程式設計師一一滌生”,歡迎掃碼關注 o(∩_∩)o

相關推薦

深度學習命名實體識別()——模型訓練

通過本文你將瞭解如何訓練一個人名、地址、組織、公司、產品、時間,共6個實體的命名實體識別模型。 準備訓練樣本 下面的連結中提供了已經用brat標註好的資料檔案以及brat的配置檔案,因為標註內容較多放到brat里加載會比較慢,所以拆分成了10份,每份包括3000多條樣本資料,將這10份檔案和相應的配置檔案

深度學習命名實體識別(一):文字資料標註

“ 本文是用深度學習做命名實體識別系列的第一篇,通過本文,你將瞭解如何用brat做文字資料標註。” 一、 什麼是命名實體識別?  從一句話中識別出人名,地名,組織名,日期時間,這就是命名實體識別的一個例子,而人名,地名等這些被識別的目標就是命名實體。當然命名實體還可以是很多其它有

深度學習命名實體識別(三):文字資料標註過程

上一篇文章,我們介紹了brat的安裝和配置,當成功安裝和配置好了brat,我們就可以進行文字標註了。 首先,在brat專案的data目錄下新建一個project目錄,然後在brat專案的主目錄下找到以下檔案,複製到project目錄: 主目錄:/var/www/html/brat project目

深度學習命名實體識別(六)-BERT介紹

什麼是BERT? BERT,全稱是Bidirectional Encoder Representations from Transformers。可以理解為一種以Transformers為主要框架的雙向編碼表徵模型。所以要想理解BERT的原理,還需要先理解什麼是Transformers。 Transforme

深度學習命名實體識別(七)-CRF介紹

還記得之前介紹過的命名實體識別系列文章嗎,可以從句子中提取出人名、地址、公司等實體欄位,當時只是簡單提到了BERT+CRF模型,BERT已經在上一篇文章中介紹過了,本文將對CRF做一個基本的介紹。本文儘可能不涉及複雜晦澀的數學公式,目的只是快速瞭解CRF的基本概念以及其在命名實體識別等自然語言處理領域的作用

基於深度學習命名實體識別

note 深度學習 以及 效果 數據集 pre 之前 得到 高達 基於CRF做命名實體識別系列 用CRF做命名實體識別(一) 用CRF做命名實體識別(二) 用CRF做命名實體識別(三) 摘要 1. 之前用CRF做了命名實體識別,效果還可以,最高達到0.9293,當然這是自己

NLP入門(五)深度學習實現命名實體識別(NER)

前言   在文章:NLP入門(四)命名實體識別(NER)中,筆者介紹了兩個實現命名實體識別的工具——NLTK和Stanford NLP。在本文中,我們將會學習到如何使用深度學習工具來自己一步步地實現NER,只要你堅持看完,就一定會很有收穫的。   OK,話不多說,讓我們進入正題。   幾乎所有的NLP都依賴一

手把手教你深度學習物體檢測(三):模型訓練

本篇文章旨在快速試驗使用yolov3演算法訓練出自己的物體檢測模型,所以會重過程而輕原理,當然,原理是非常重要的,只是原理會安排在後續文章中專門進行介紹。所以如果本文中有些地方你有原理方面的疑惑,也沒關係,可以自行網上搜索相關資料,也可以先留著問題,相信你會在後續文章中找到答案。    

一文詳解深度學習命名實體識別(NER)中的應用

近幾年來,基於神經網路的深度學習方法在計算機視覺、語音識別等領域取得了巨大成功,另外在自然語言處理領域也取得了不少進展。在NLP的關鍵性基礎任務—命名實體識別(Named Entity Recognition,NER)的研究中,深度學習也獲得了不錯的效果。最近,筆者閱讀了一系列基於深度學習的NE

CRF命名實體識別

裏的 以及 命名 語料庫 images AD 之前 .dll alt 摘要 本文主要講述了關於人民日報標註語料的預處理,利用CRF++工具包對模型進行訓練以及測試 目錄 明確我們的標註任務 語料和工具 數據預處理 1.數據說明 2.數據預處理 模型訓練及測試 1.流程 2

手把手教你深度學習物體檢測():模型使用

上一篇《手把手教你用深度學習做物體檢測(三):模型訓練》中介紹瞭如何使用yolov3訓練我們自己的物體檢測模型,本篇文章將重點介紹如何使用我們訓練好的模型來檢測圖片或視訊中的物體。   如果你看過了上一篇文章,那麼就知道我們用的是 AlexeyAB/darknet專案,該專案雖然提供了物體檢測的

深度學習球星顏值打分完整案例(一)

已經上傳了完整的程式碼和資料,資料比較少,大家可以幫忙補充。專案地址(記得給個start):https://github.com/jimenbian/face_rank先來說一下專案的背景,這次做的是一個最基礎的影象識別案例,通過訓練一個模型來給NBA球星的顏值打分,嗯,樓主

關於深度學習answer selection的論文

最近做question-answering系統,看了幾篇相關的論文,涉及到用CNN、LSTM、RNN、Attention等演算法,這裡做個記錄。 1 Attention-Based Convolutional Neural Network for Modeli

手把手教你深度學習物體檢測(二):資料標註

  “本篇文章將開始我們訓練自己的物體檢測模型之旅的第一步—— 資料標註。”   上篇文章介紹瞭如何基於訓練好的模型檢測圖片和視訊中的物體,若你也想先感受一下物體檢測,可以看看上篇文章:《手把手教你用深度學習做物體檢測(一):快速感受物體檢測的酷炫 》。   其實,網上關於資料標註的文章已有很多,但

手把手教你深度學習物體檢測(五):YOLOv1介紹

之前寫物體檢測系列文章的時候說過,關於YOLO演算法,會在後續的文章中介紹,然而,由於YOLO歷經3個版本,其論文也有3篇,想全面的講述清楚還是太難了,本週終於能夠抽出時間寫一些YOLO演算法相關的東西。本篇文章,我會先帶大家完整的過一遍YOLOv1的論文,理解了YOLOv1才能更好的理解它的後續版本,YO

手把手教你深度學習物體檢測(七):YOLOv3介紹

YOLOv3 論文:《 YOLOv3: An Incremental Improvement 》 地址: https://arxiv.org/pdf/1804.02767.pdfyolov3 相比之前版本的改進 網路的特徵提取部分 由 Darknet-19改成了&n

深度學習keras的cnn影象識別分類,準確率達97%

Keras是一個簡約,高度模組化的神經網路庫。可以很容易和快速實現原型(通過總模組化,極簡主義,和可擴充套件性)同時支援卷積網路(vision)和複發性的網路(序列資料)。以及兩者的組合。無縫地執行在CPU和GPU上。keras的資源庫網址為https://github.co

深度神經網路處理NER命名實體識別問題

本文結構: 什麼是命名實體識別(NER) 怎麼識別? cs224d Day 7: 專案2-用DNN處理NER問題 課程專案描述地址 什麼是NER? 命名實體識別(NER)是指識別文字中具有特定意義的實體,主要包括人名、地名、機構名、專有

【論文筆記】《基於深度學習的中文命名實體識別研究》閱讀筆記

作者及其單位:北京郵電大學,張俊遙,2019年6月,碩士論文 摘要 實驗資料:來源於網路公開的新聞文字資料;用隨機欠取樣和過取樣的方法解決分類不均衡問題;使用BIO格式的標籤識別5類命名實體,標註11種標籤。 學習模型:基於RNN-CRF框架,提出Bi-GRU-Attention模型;基於改進的ELMo可