【百度LIC2020事件抽取賽道】賽後小結(小白篇,大佬略過)

講在前面

這次比賽對我來說是首次參加百度舉辦的比賽,也是第一個事件抽取方向的比賽,整體來說熟悉事件抽取的模型,以及相關的操作爲主,最高得分F1,0.796分,算是低分,在這裏對整個比賽過程,以及自己的一個情況做一個小結梳理,爲下次比賽做好準備工作,還是比較小白的,但是在這次比賽中也收穫了很多實戰的經驗,奈何各位大神雲集,競爭激烈,對我個人而言,熟悉模型,熟悉比賽模式,收穫經驗爲主!大佬請關掉網頁2333
本次賽題屬於一個多分類,多標籤的問題,文本先要進行事件分類,之後進行事件抽取,將論元和內容進行抽取,一個文本可能屬於多個事件,在同一個時間下,也會有多個論元。

一,baseline入坑:熟悉模型

感謝蘇劍林大佬的baseline,他代碼簡單明確的風格深深吸引了我,立志通讀大佬的代碼,以大佬爲明燈。主要是用Bert4keras實現,事件類型和論元同時預測,針對overlap的情況暫不考慮,執行直接覆蓋的政策,模型本身的話使用Bert預訓練模型+fineturn+全聯接層+CRF輸出層,損失是使用的交叉熵稀疏損失,優化器是Adam優化器,評價指標是每個標籤粗測的訓練效果。
之前的我對於fine turn也是一知半解,其實很簡單,就是使用別人訓練好的模型,加上自己的數據集再次進行訓練,更好的擬合自己的數據集。
網址如下:

加入的一些小的改進

1⃣️可以使用比較大的詞嵌入模型進行訓練,word_embedding_len=1024
2⃣️可以使得句子的最長長度增加,增大的訓練時間
3⃣️可以增加CRF的學習率,效果有提升,但是不知道作用在何處
4⃣️可以使用變化的學習率進行訓練,效果提升一般
5⃣️動態學習率,控制每一層網絡的學習率,這個我還沒有實現
6⃣️使用凍結層的操作,trainable=false 效果變差

對於具體事件抽取的一些改進

1⃣️可以針對overlap的情況進行數據的拆分,感覺會有一些用
2⃣️在文本的前部加入事件類型,給模型指示,這個需要提前使用事件分類的模型
3⃣️在合併的時候進行查找,相同的事件類型進行合併操作
4⃣️可以使用問答模型進行對模型提問,這個論元是不是屬於該事件類型下的論元,本次比賽還未實現,據同事介紹效果有提升。
5⃣️針對每一個事件類型可以單獨訓練模型進行預測,效果會變好

二,嘗試新模型:2019_ner命名實體模型

這個模型看起來非常厲害的樣子,但是我跑起來真的感覺沒有發揮這個模型最大的效果,這個模型有這麼多的輸入的參數我省事直接把默認的參數更改,這樣直接運行就可以出結果,這個模型是基於靜態圖的實體識別模型,最厲害的地方就是可以處理overlap的問題,在實際的模型中,是安裝token級別垂直輸入的,看了他的代碼,使用factor這個結構將文本,詞幹,詞性,標籤統一處理,統一進行已經處理好的詞嵌入中,直接接上RNN或者LSTM進行訓練,最後一層可以選擇時CRF還是Seq2Seq模型,整體而言比較複雜,花了蠻久的時間研究學習的,下面介紹一下我的研究學習的“成le果se”

非常多的介入的參數數據

在這裏插入圖片描述

tqdm 進度條程序,可以得到好看的進度條

放個不是很好看的報錯的結果,可以看一下效果

  # Train
        for epochs, learning_rate in args.epochs:
            for epoch in tqdm(range(epochs)):
                network.train_epoch(train, learning_rate, args)
                dev_score = 0
                if args.dev_data:
                    dev_score =network.evaluate("dev",dev,args)
                    print("{}".format(dev_score))
        # Save network
        network.saver.save(network.session, "{}/model".format(args.logdir), write_meta_graph=False)
        # Test
        test_score = network.evaluate("test", test, args)
        network.predict_to_file(args)

在這裏插入圖片描述

輸入的數據預處理

比較難受的就是需要單獨生成詞嵌入到向量,
這個部分比較重要,直接關係到後面的數據存儲部分,看了好幾遍纔看懂一些,貼一小部分,方便以後找到位置就好,這個太長了,之後和同事討論了好久就是關於這個的編碼的問題。
在這裏插入圖片描述

  # Load the sentences 加載句子。
        with open(filename, "r", encoding="utf-8") as file:
            in_sentence = False
            for line in file:
                line = line.rstrip("\r\n")
                if line:
                    columns = line.split("\t")
                    for f in range(self.FACTORS):  #爲什麼在——FACTORS 尋找.一共有4個元素
                        factor = self._factors[f]  #一個句子的factor
                        if not in_sentence:  #對於一個句子進行處理
                            factor.word_ids.append([])
                            factor.charseq_ids.append([])
                            factor.strings.append([])
                        column = columns[f] if f < len(columns) else '<pad>'  #f<4
                        words = []
                        if f == self.TAGS and seq2seq:  #怎麼處理 | 情況
                            words = column.split("|")
                            words.append("<eow>")
                        else:
                            words = [column]
                        for word in words:  #每一個元素
                            factor.strings[-1].append(word)

下面都是我生成的詞嵌入的向量文件,大的一個就要3個G左右,
在這裏插入圖片描述

生成詞嵌入的main文件

import json
import numpy as np
from bert4keras.tokenizers import Tokenizer
from bert4keras.models import build_transformer_model
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "7"
config_path = 'bert-utils-master/chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/bert_config.json'
checkpoint_path = 'bert-utils-master/chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/bert_model.ckpt'
dict_path = 'bert-utils-master/chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/vocab.txt'
maxlen = 128
epochs = 20
batch_size = 8
learning_rate = 2e-5
crf_lr_multiplier = 100  # 必要時擴大CRF層的學習率
model = build_transformer_model(config_path, checkpoint_path)  # 建立模型,加載權重
tokenizer = Tokenizer(dict_path, do_lower_case=True)

def load_data_test(filename,name):
    word_em=open("bert-utils-master/data/"+name+"_word_embedding_l.txt",'w', encoding='utf-8')
    with open(filename) as f:
        for l in f:
            l = json.loads(l)
            arguments = {}
            text=l['text']
            token_ids, segment_ids = tokenizer.encode(text,max_length=maxlen)
            tokens = tokenizer.tokenize(text)  # 轉化爲tokens
            we=model.predict([np.array([token_ids]), np.array([segment_ids])])
            #token_ids = token_ids[1:-1]
            #tokens = tokens[1:-1]
            for i in range(1,len(token_ids)-1):
                word_em.write(tokens[i])
                for j in range(1024):
                    word_em.write(" " + str(we[0][i][j]))
                word_em.write("\n")
            word_em.write("\n")
load_data_test("bert-utils-master/data/train.json", "train")
load_data_test("bert-utils-master/data/dev.json","dev")
load_data_test("bert-utils-master/data/test1.json","test")

三,自己編寫腳本前後處理

生成垂直數據的主要文件

class data_generator(DataGenerator):
    """數據生成器
    """
    def __iter__(self, random=False):   # 迭代器 : batch id segment_id label
        for is_end, (text, arguments) in self.sample(random):
            token_ids, segment_ids = tokenizer.encode(text,max_length=maxlen)
            tokens = tokenizer.tokenize(text)  #轉化爲tokens
            token_ids=token_ids[1:-2]
            tokens=tokens[1:-2]
            print(tokens)
            label = ["O"] * len(tokens)
            for argument in arguments.items():
                a_token_ids = tokenizer.encode(argument[0])[0][1:-1]
                start_index = search(a_token_ids, token_ids)
                mapping = tokenizer.rematch(text, tokens)    # 進行文本和token的匹配
                token_ids = tokenizer.tokens_to_ids(tokens)  # 找到tokens的ID
                #print(list(map(lambda x:text[x],mapping[start_index])))
                if start_index != -1:
                    if len(a_token_ids)==1:
                        if label[start_index]=="O" : 
                            print(arguments[argument[0]][0]+"-"+arguments[argument[0]][1] )
                            label[start_index]="U-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1] 
                        else: 
                            label[start_index]=label[start_index]+"|"+"U-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                    else :
                        if label[start_index]=="O" : 
                            label[start_index]="B-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                        else :  
                            label[start_index]=label[start_index]+"|"+"B-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                        for i in range(1, len(a_token_ids)-1):
                                if label[start_index + i]=="O" :  
                                    label[start_index + i] = "I-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                                else:
                                    label[start_index + i] = label[start_index + i]+"|"+"I-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                        if label[start_index + len(a_token_ids)-1]=="O" : 
                            label[start_index + len(a_token_ids)-1] = "L-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
                        else :    
                            label[start_index + len(a_token_ids)-1] = label[start_index + len(a_token_ids)-1]+"|"+"L-"+arguments[argument[0]][0]+"-"+arguments[argument[0]][1]
            with open('dev_data_label.txt','a') as f: 
                for i in range(len(tokens)):
                    f.write("{}\t{}\t{}\t{}\n".format(tokens[i],"_",list(map(lambda x:x.flag,pseg.cut(tokens[i])))[0],label[i]))
                f.write("\n")
train_generator = data_generator(train_data, len(train_data))
for i in train_generator:
    pass

將預測的結果進行合併的文件

import json
def load_data():
    filename="event_predict_lr_scheduler.jsonl"
    filename1="ner_pred_bert_v7.json"
    D = []
    fw = open("ner_pred_final_v4.json", 'w', encoding='utf-8')
    arguments={}
    with open(filename) as f:
        for x in f:
            x = json.loads(x)
            arguments["text"]=x["text"]
            arguments["id"]=x["id"]
            D.append(arguments)
            arguments={}
    i=0
    with open(filename1) as ner:
         for l in ner:
            l = json.loads(l)
            arguments = {}
            event_list=[]
            for event in l['event_list']:
                for argument in event['arguments']:
                    key = argument['argument']
                    value = argument['role']  #事件類型+論元角色
                    arguments[key] = value
                    event_list.append({
                                'event_type': "-".join(key.split("-")[0:-1]),
                                'arguments': [{
                                    'role': key.split("-")[-1].replace("##", ""),
                                    'argument': value.replace("##", "")
                                }]
                            })
            l = {}
            l['text'] = D[i]['text']
            l['id']=D[i]['id']
            l['event_list']=event_list
            i=i+1
            print(l)
            l = json.dumps(l, ensure_ascii=False)
            fw.write(l + '\n')
load_data()

四,跑服務器相關的一些知識

1⃣️學習SHH協議連接服務器
2⃣️使用iterm2 進行窗口化
3⃣️學習類Git相關知識
4⃣️使用pychram 連接服務器

五,關於詞嵌入相關的知識

不同的詞嵌入模型的效果可能相差還是比較大的,在這次比賽中我主要使用了一下預訓練的詞嵌入模型
chinese_L-12_H-768_A-12
chinese_roberta_L-6_H-384_A-12
chinese_roberta_wwm_large_ext_L-24_H-1024_A-16
其中層數越好訓練效果會變好,但是訓練時間增加。
1⃣️非常深的模型可以顯著提升nlp任務的訓練精確度,模型可以從無標記數據中訓練得到。
2⃣️基於transformer 時候encoder-decoder結構,利用transform的encoder進行訓練。
3⃣️BERT則選擇了兩個看起來更簡單的任務:完形填空和句對預測
4⃣️MaskLM的方式來訓練語言模型
最後,BERT也打開了一個思路:可以繼續在無標註數據上挖潛,而不僅僅限於語言模型。

[1]基於全詞遮罩(Whole Word Masking)技術的中文預訓練模型BERT-wwm https://github.com/ymcui/Chinese-BERT-wwm/
[2]NLP歷史突破!快速解讀Google BERT模型 + Word Embedding https://www.youtube.com/watch?v=Po38Dl-XDd4
[3]基於keras的BiLstm與CRF實現命名實體標註 https://www.cnblogs.com/vipyoumay/p/ner-chinese-keras.html

六,關於訓練模型知識

1⃣️快速試錯,快速嘗試
2⃣️使用融合模型進行訓練效果更好
3⃣️在小模型驗證方法正確,處理報錯,大模型上服務器進行訓練。

七,一些常用的終端命令行

[1]conda install tensorflow==1.14.0 -i https://pypi.tuna.tsinghua.edu.cn/simple/
[2]pip install \
  -i https://pypi.tuna.tsinghua.edu.cn/simple/ \
https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/gpu/tensorflow_gpu-1.4.0-cp27-none-linux_x86_64.whl
[4] pip install git+https://www.github.com/keras-team/keras-contrib.git
[5] pip install keras==2.1.4
[6]import tensorflow.compat.v1 as tf.               tf.disable_v2_behavior()
[7] pip install tensorflow==1.14.0 -I https://pypi.tuna.tsinghua.edu.cn/simple/
[8]pip install git+https://github.com/danielfrg/word2vec
[9]針對MATADATA 問題只要複製一個文件,到目錄下即可
[10] 設置LINUX conda 環境變量    -I 插入. ZZ 保存
vim ~/profile
export PATH=/data/lisong/anaconda3/bin:$PATH.   
source ~/profile
[11]安裝對映清華鏡像下tensorflow 
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes
conda install tensorflow==1.14.0
[12]限制一塊卡進行運行程序.    import os.  os.environ["CUDA_VISIBLE_DEVICE"] = 6
[13] git clone https://github.com/Determined22/zh-NER-TF.git  進行克隆
[14] 
1.    詞幹提取(Stemming):詞幹提取是一個初級的、基於規則的脫去後綴(如“ing”,“ly”,“es”,“s”等等)的過程
2.    詞元化(Lemmatization):另一方面,詞元化,是一個組織好的、一步一步的獲取詞根的過程。
並使用了詞彙表(單詞在字典裏的重要性)和形態學分析(單詞結構與語法關係)
[15]ImportError: cannot import name 'run_classifier' from 'bert' (/opt/anaconda3/envs/tensorflow/lib/python3.7/site-packages/bert/__init__.py)
[16] chmod u+x *.sh  python. 爲sh文件增加可執行權限
[17] 
conda env list
conda activate tensorflow
pip list | grep bert
[18]ps aux | grep 75457
[19] nohup python my.py >> /usr/local/python/xxf/my.log 2>&1 &
[20]tensorboard --logdir=/Users/tobeace/PycharmProjects/acl2019_nested_ner的副本/logs/
[21]Not a TBLoader or TBPlugin subclass: <class 'tensorboard_plugin_wit.wit_plugin_loader.WhatIfToolPluginLoader'>
需要在tensor flow環境下啓用
tensorflow可視化tensorboard “No graph definition files were found.” 錯誤
需要 返回一層文件夾即可;
[22]replace(##”,””)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章