【百度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(##”,””)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章