经过本文你将了解如何训练一我的名、地址、组织、公司、产品、时间,共6个实体的命名实体识别模型。html
下面的连接中提供了已经用brat标注好的数据文件以及brat的配置文件,由于标注内容较多放到brat里加载会比较慢,因此拆分红了10份,每份包括3000多条样本数据,将这10份文件和相应的配置文件放到brat目录/data/project路径下,而后就能够从浏览器访问文件内容以及相应的标注状况了。python
提取码:99z3程序员
若是你还不知道什么是brat,或还不清楚如何使用brat,强烈建议先阅读前两篇文章《用深度学习作命名实体识别(二):文本标注工具brat》、《用深度学习作命名实体识别(三):文本数据标注过程》。github
标注数据虽然有了,可是还不能知足咱们的训练要求,由于咱们须要根据ann和txt,将其转成训练所需的数据格式,格式以下:
算法
能够看到,每一行一个字符,字符后面跟上空格,而后跟上该字符的标注, 每一个样本之间用空行分隔。
另外,也能够看到这里采用的是BIO的标注方式:json
转换代码以下:api
# -*- 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
笔者基于该项目作了一些代码修改,修改的目的以下:
修改后的项目地址:
提取码:sibq
修改后的项目下载下来解压后,须要作3件事情:
接下来,执行如下命令进行训练:
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 &
让咱们对命令中的参数作一些解释:
开始训练后,经过如下命令查看训练过程的日志信息:
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