BISTML-CRF项目源码

BILSTM-CRF项目源码

结构

data

boson数据集分析

oragindata

{{product_name:浙江在线杭州}}{{time:4月25日}}讯(记者{{person_name: 施宇翔}} 通讯员 {{person_name:方英}})毒贩很“时髦”,用{{product_name:微信}}交易毒品。没料想警方也很“潮”,将计就计,一举将其擒获。记者从{{org_name:杭州江干区**}}了解到,经过一个多月的侦查工作,{{org_name:江干区禁毒专案组}}抓获吸贩毒人员5名,缴获“冰毒”400余克,毒资30000余元,扣押汽车一辆。{{location:黑龙江}}籍男子{{person_name:钱某}}长期落脚于宾馆、单身公寓,经常变换住址。他有一辆车,经常半夜驾车来往于{{location:杭州主城区}}的各大宾馆和单身公寓,并且常要活动到{{time:凌晨6、7点钟}},{{time:白天}}则在家里呼呼大睡。{{person_name:钱某}}不寻常的特征,引起了警方注意。禁毒大队通过侦查,发现{{person_name:钱某}}实际上是在向落脚于宾馆和单身公寓的吸毒人员贩送“冰毒”。

第一层处理:实现对单个字打标签,具体实现:按照括号配对," : "左边的是tag,右边的是word。再将标签分为开始,中间,结束,分别给word中的每个字打tag。给没有“{{ }}”的每个字打上/o。将处理后的文件输入wordtag中。

def origin2tag():
    input_data = codecs.open('./origindata.txt','r','utf-8')
    output_data = codecs.open('./wordtag.txt','w','utf-8')
    # 一个line是一个待分析的实例
    for line in input_data.readlines():
        # 将line中的词组分出来
        line=line.strip()
        i=0 #读取词的时候计数
        while i <len(line):
            if line[i] == '{':
                i+=2
                temp=""
                while line[i]!='}':
                    temp+=line[i]
                    i+=1
                i+=2
                # ":"左边是标签,右边是实体
                word=temp.split(':')
                sen = word[1]
                # 给词组打开始,中间和结束
                output_data.write(sen[0]+"/B_"+word[0]+" ")
                for j in sen[1:len(sen)-1]:
                    output_data.write(j+"/M_"+word[0]+" ")
                output_data.write(sen[-1]+"/E_"+word[0]+" ")
            else:
                output_data.write(line[i]+"/O ")
                i+=1
        output_data.write('\n')
    input_data.close()
    output_data.close()

wordtag

浙/B_product_name 江/M_product_name 在/M_product_name 线/M_product_name 杭/M_product_name 州/E_product_name 4/B_time 月/M_time 2/M_time 5/M_time 日/E_time 讯/O (/O 记/O 者/O  /B_person_name 施/M_person_name 宇/M_person_name 翔/E_person_name  /O 通/O 讯/O 员/O  /O 方/B_person_name 英/E_person_name )/O 毒/O 贩/O 很/O “/O 时/O 髦/O ”/O ,/O 用/O 微/B_product_name 信/E_product_name 交/O 易/O 毒/O 品/O 。/O 没/O 料/O 想/O 警/O 方/O 也/O 很/O “/O 潮/O ”/O ,/O 将/O 计/O 就/O 计/O ,/O 一/O 举/O 将/O 其/O 擒/O 获/O 。/O 记/O 者/O 从/O 杭/B_org_name 州/M_org_name 江/M_org_name 干/M_org_name 区/M_org_name 公/M_org_name 安/M_org_name 分/M_org_name 局/E_org_name 了/O 解/O 到/O ,/O 经/O 过/O 一/O 个/O 多/O 月/O 的/O 侦/O 查/O 工/O 作/O ,/O 江/B_org_name 干/M_org_name 区/M_org_name 禁/M_org_name 毒/M_org_name 专/M_org_name 案/M_org_name 组/E_org_name 抓/O 获/O 吸/O 贩/O 毒/O 人/O 员/O 5/O 名/O ,/O 缴/O 获/O “/O 冰/O 毒/O ”/O 4/O 0/O 0/O 余/O 克/O ,/O 毒/O 资/O 3/O 0/O 0/O 0/O 0/O 余/O 元/O ,/O 扣/O 押/O 汽/O 车/O 一/O 辆/O 。/O 黑/B_location 龙/M_location 江/E_location 籍/O 男/O 子/O 钱/B_person_name 某/E_person_name 长/O 期/O 落/O 脚/O 于/O 宾/O 馆/O 、/O 单/O 身/O 公/O 寓/O ,/O 经/O 常/O 变/O 换/O 住/O 址/O 。/O 他/O 有/O 一/O 辆/O 车/O ,/O 经/O 常/O 半/O 夜/O 驾/O 车/O 来/O 往/O 于/O 杭/B_location 州/M_location 主/M_location 城/M_location 区/E_location 的/O 各/O 大/O 宾/O 馆/O 和/O 单/O 身/O 公/O 寓/O ,/O 并/O 且/O 常/O 要/O 活/O 动/O 到/O 凌/B_time 晨/M_time 6/M_time 、/M_time 7/M_time 点/M_time 钟/E_time ,/O 白/B_time 天/E_time 则/O 在/O 家/O 里/O 呼/O 呼/O 大/O 睡/O 。/O 钱/B_person_name 某/E_person_name 不/O 寻/O 常/O 的/O 特/O 征/O ,/O 引/O 起/O 了/O 警/O 方/O 注/O 意/O 。/O 禁/O 毒/O 大/O 队/O 通/O 过/O 侦/O 查/O ,/O 发/O 现/O 钱/B_person_name 某/E_person_name 实/O 际/O 上/O 是/O 在/O 向/O 落/O 脚/O 于/O 宾/O 馆/O 和/O 单/O 身/O 公/O 寓/O 的/O 吸/O 毒/O 人/O 员/O 贩/O 送/O “/O 冰/O 毒/O ”/O 。/O 

第二层处理:# 按照标点符号进行分段

# 文本处理阶段二
# 逻辑分段(标点符号)
def tagsplit():
    with open('./wordtag.txt','rb') as inp:
        texts = inp.read().decode('utf-8')
    sentences = re.split('[,。!?、‘’“”()]/[O]', texts)
    output_data = codecs.open('./wordtagsplit.txt','w','utf-8')
    for sentence in sentences:
        if sentence != " ":
            output_data.write(sentence.strip()+'\n')
    output_data.close()

浙/B_product_name 江/M_product_name 在/M_product_name 线/M_product_name 杭/M_product_name 州/E_product_name 4/B_time 月/M_time 2/M_time 5/M_time 日/E_time 讯/O

第三层处理:制作对应数据,单个字对应的id,tag对应的id,tag对应的字,训练用的数据集

def data2pkl():
    datas = list()
    labels = list()
    linedata=list()
    linelabel=list()
    tags = set()

    # 将标注的文本进行数据处理(获取实体文本以及标签,分别存入两个list datas labels,且一一对应,用集合tag统计存在多少种类型的标签)
    input_data = codecs.open('./wordtagsplit.txt','r','utf-8')  #处理已经打好标签的文本
    for line in input_data.readlines():
        line = line.split()
        linedata=[]
        linelabel=[]
        numNotO=0
        for word in line:
            word = word.split('/') #数据中每个词对用空格隔开,每个词和对应的标签使用“/”分隔符隔开的
            linedata.append(word[0])
            linelabel.append(word[1])
            tags.add(word[1])
            if word[1]!='O':  #o不是我们所需要识别的内容的标签
                numNotO+=1
        if numNotO!=0:
            datas.append(linedata)
            labels.append(linelabel)
    input_data.close()

    print(len(datas),tags)
    print(len(labels))

    # 实体打id
#    from compiler.ast import flatten
    all_words = flatten(datas)
    sr_allwords = pd.Series(all_words) #给words打对应的id
    sr_allwords = sr_allwords.value_counts() #统计各word出现的次数,且按照由高到低排列
    set_words = sr_allwords.index
    set_ids = list(range(1, len(set_words)+1))

    # 标签打id
    tags = [i for i in tags]
    tag_ids = list(range(len(tags)))

    # 四种二元关系
    word2id = pd.Series(set_ids, index=set_words)
    id2word = pd.Series(set_words, index=set_ids)
    tag2id = pd.Series(tag_ids, index=tags)
    id2tag = pd.Series(tags, index=tag_ids)

    # 给找不到的实体单独打一个id
    word2id["unknow"] = len(word2id)+1
    print(word2id)

    # 每60个词打包在一起,不足的就截断到60个词
    max_len = 60
    def X_padding(words):
        ids = list(word2id[words])
        if len(ids) >= max_len:  
            return ids[:max_len]
        ids.extend([0]*(max_len-len(ids))) 
        return ids

    def y_padding(tags):
        ids = list(tag2id[tags])
        if len(ids) >= max_len: 
            return ids[:max_len]
        ids.extend([0]*(max_len-len(ids))) 
        return ids

    #建立word和tag的一一对应的二维表,行名是顺序数字
    df_data = pd.DataFrame({'words': datas, 'tags': labels}, index=list(range(len(datas))))
    # 填充数据
    df_data['x'] = df_data['words'].apply(X_padding)
    df_data['y'] = df_data['tags'].apply(y_padding)
    # 数据结构化
    x = np.asarray(list(df_data['x'].values))
    y = np.asarray(list(df_data['y'].values))

    # 划分数据集(训练:验证:测试 =0.66 : 0.16 : 0.2)
    from sklearn.model_selection import train_test_split
    x_train,x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=43)
    x_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train,  test_size=0.2, random_state=43)


    # 将结构化后的数据送入pkl中存储
    import pickle
    import os
    with open('../Bosondata.pkl', 'wb') as outp:
        pickle.dump(word2id, outp)
        pickle.dump(id2word, outp)
        pickle.dump(tag2id, outp)
        pickle.dump(id2tag, outp)
        pickle.dump(x_train, outp)
        pickle.dump(y_train, outp)
        pickle.dump(x_test, outp)
        pickle.dump(y_test, outp)
        pickle.dump(x_valid, outp)
        pickle.dump(y_valid, outp)
    print('** Finished saving the data.')
import collections
# 数据拉成一维的
def flatten(x):
    result = []
    for el in x:
        if isinstance(x, collections.Iterable) and not isinstance(el, str):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

BILSTM-CRF

具体解析
数据在BISTM-CRF层中的整个流程:

Created with Raphaël 2.2.0 数据进入 lstm训练得到每个词的标签分数 crf层对lstm层输出的标签序列进行路程打分

实现:

  • 初始化模型
  • 前馈
  • 损失函数
  • 反馈
    (具体解析详见链接)

train

加载处理过的数据

with open('../data/Bosondata.pkl', 'rb') as inp:
	word2id = pickle.load(inp)
	id2word = pickle.load(inp)
	tag2id = pickle.load(inp)
	id2tag = pickle.load(inp)
	x_train = pickle.load(inp)
	y_train = pickle.load(inp)
	x_test = pickle.load(inp)
	y_test = pickle.load(inp)
	x_valid = pickle.load(inp)
	y_valid = pickle.load(inp)

设置初始值

START_TAG = "<START>"
STOP_TAG = "<STOP>"
EMBEDDING_DIM = 100
HIDDEN_DIM = 200
EPOCHS = 5

tag2id[START_TAG]=len(tag2id)
tag2id[STOP_TAG]=len(tag2id)

模型

model = BiLSTM_CRF(len(word2id)+1, tag2id, EMBEDDING_DIM, HIDDEN_DIM)

优化器

optimizer = optim.SGD(model.parameters(), lr=0.005, weight_decay=1e-4)

训练

    for sentence, tags in zip(x_train,y_train):
        index+=1
        model.zero_grad()

	    # 数据处理的时候已将word转换成id进行存储
        # sentence = torch.tensor([word2id[w] for w in sentence], dtype=torch.long)
        # 数据处理的时候已将tag转换成id进行存储
        # tags = torch.tensor([tag2id[t] for t in tags], dtype=torch.long)
        sentence = torch.tensor(sentence)
        tags = torch.tensor(tags)

        loss = model.neg_log_likelihood(sentence, tags)

        loss.backward()
        optimizer.step()
        if index%30==0:
            print("epoch", epoch, "index", index)

测试

 entityres=[]
    entityall=[]
    for sentence, tags in zip(x_test,y_test):
        sentence=torch.tensor(sentence, dtype=torch.long)
        score,predict = model(sentence)
        sentence = sentence.numpy().tolist()
        tags = tags.tolist()
        entityres = calculate(sentence,predict,id2word,id2tag,entityres)
        entityall = calculate(sentence,tags,id2word,id2tag,entityall)
    jiaoji = [i for i in entityres if i in entityall]
    if len(jiaoji)!=0:
        zhun = float(len(jiaoji))/len(entityres)
        zhao = float(len(jiaoji))/len(entityall)
        print("test:")
        print("zhun:", zhun)
        print("zhao:", zhao)
        print("f:", (2 * zhun * zhao) / (zhun + zhao))
    else:
        print("zhun:",0)

保存模型

 path_name = "./model"+str(epoch)+".pkl"
    print(path_name)
    torch.save(model, path_name)
    print("model has been saved")

模型能力

epoch=1
train len: 1216
test len: 380
BISTML-CRF项目源码
BISTML-CRF项目源码

epoch=1
train len: 1600
test len: 500
BISTML-CRF项目源码

迁移MSRA数据集

数据处理

  • 分割数据集中的data和lable
    • 按行将单词提取到列表中
      • 按照bme格式,给单字打label
  • 按逻辑(’[,。!?、‘’“”()]/[O]’)分行
  • 自定义tag2id&&id2tag
  • 制作数据集(分单词,分单字,数据拉成一维)
  • 通过pd.Series()给word打id
  • 将word转成id,tag转成id,并进行数据规范(通过填充或删减将其长度固定)
  • 划分训练集,测试集,验证集

注意:生成 tag2id 时,应该将空设置成 id = 0,否则在进行padding时用 0填充会打上其他标签!!!!!影响整个模型的训练
数据集一定要仔细仔细再仔细!!!否则可能有test数据集无法验证,训练出的模型根本没有识别能力等等等问题!!!!,tag如果不多建议自定义,在msra数据集中,自动生成tag2id和id2tag存在bug

BILSTM-CRF&&resuleCal

直接用现成的

训练

  • 定model,loss,optimizer

  • START_TAG
    STOP_TAG
    EMBEDDING_DIM
    HIDDEN_DIM
    EPOCHS
  • 通过训练集进行训练
  • 通过测试集验证准确率
  • 保存模型

模型能力

train len: 1440
test len: 200
valid len 360
epoch = 1
BISTML-CRF项目源码
train len: 1440
test len: 200
valid len 360
epoch = 1
BISTML-CRF项目源码

处理brat标注后的数据

天池瑞金医院MMC人工智能辅助构建知识图谱大赛
代码数据集采用txt+ann类型,与brat相同,参考该代码进行分析

  • def clean_ann(): ann数据将其lable和对应txt中的位置提取出来存在csv中
  • **def word_level_tag2(filename)
上一篇:[白话解析]用水浒传为例学习最大熵马尔科夫模型


下一篇:算法题1:arguments和sort()方法的知识点考察