講在前面
這次比賽對我來說是首次參加百度舉辦的比賽,也是第一個事件抽取方向的比賽,整體來說熟悉事件抽取的模型,以及相關的操作爲主,最高得分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(“##”,””)