Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用深度学习做命名实体识别(四)——模型训练

用深度学习做命名实体识别(四)——模型训练

原创
作者头像
程序员一一涤生
修改于 2019-12-12 06:55:15
修改于 2019-12-12 06:55:15
2.6K2
举报

通过本文你将了解如何训练一个人名、地址、组织、公司、产品、时间,共6个实体的命名实体识别模型。 训练建议在GPU上进行,如果你没有GPU训练环境,或者你想要一个训练好的模型,可以加作者微信(jiabao512859468),有任何相关技术问题,都欢迎和作者探讨O(∩_∩)O~

ok,下面开始我们的模型训练。

准备训练样本

下面的链接中提供了已经用brat标注好的数据文件以及brat的配置文件,因为标注内容较多放到brat里加载会比较慢,所以拆分成了10份,每份包括3000多条样本数据,将这10份文件和相应的配置文件放到brat目录/data/project路径下,然后就可以从浏览器访问文件内容以及相应的标注情况了。

如果你还不知道什么是brat,或还不清楚如何使用brat,强烈建议先阅读前两篇文章《用深度学习做命名实体识别(二):文本标注工具brat》《用深度学习做命名实体识别(三):文本数据标注过程》

标注数据虽然有了,但是还不能满足我们的训练要求,因为我们需要根据ann和txt,将其转成训练所需的数据格式,格式如下:

可以看到,每一行一个字符,字符后面跟上空格,然后跟上该字符的标注, 每个样本之间用空行分隔。

另外,也可以看到这里采用的是BIO的标注方式:

  • B,即Begin,表示开始
  • I,即Intermediate,表示中间
  • O,即Other,表示其他,用于标记无关字符

转换代码如下:

代码语言:txt
AI代码解释
复制
# -*- 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

笔者基于该项目做了一些代码修改,修改的目的如下:

  • 原来的项目是采用install的方式直接将项目安装到你的python虚拟环境下,然后通过命令行执行训练,笔者直接调整了源代码,为了可以基于源代码执行一些调试;
  • 原来的项目训练的时候几乎没有日志信息,修改后的项目可以看到训练日志;
  • 原来的项目只能在训练结束后输出评估结果,修改后的项目可以让评估脱离训练过程独立进行。

修改后的项目地址:

修改后的项目下载下来解压后,需要做3件事情:

  1. 将之前下载的bert预训练模型chinese_L-12_H-768_A-12目录以及目录中的文件放到项目的models目录下。
  2. 将之前准备的train、dev、test三个文件放到person_data目录下。
  3. 为该项目新建一个python的虚拟环境,然后安装所需要的依赖包,关于需要哪些依赖包,项目中的requirement.txt是这么描述的:

tensorflow的安装,因为我们是在GPU上训练,所以只需要安装tensorflow-gpu,笔者安装的是tensorflow1.13.1版本,因为笔者的CUDA版本是10.0。

接下来,执行以下命令进行训练:

代码语言:txt
AI代码解释
复制
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 &

让我们对命令中的参数做一些解释:

  • nohup 使用nohup命令,可以保证在命令窗口被关闭,或远程链接中断的情况下,不影响远端python程序的执行。python程序执行过程中的日志信息会保存在当前文件夹下的log.out文件中。
  • max_seq_length 每个样本的最大长度,不能超过512。如果你的某些样本超过了这个长度,需要截断。截断代码可以使用项目根路径下的data_process.py文件。
  • batch_size 每次送到模型进行训练的样本数量。一般是2幂次方。如果你的GPU显存够大,可以尝试增大batch_size。
  • learning_rate 初始学习率,用于调整模型的学习速度,过大过小都不好。刚开始训练时:学习率以 0.01 ~ 0.001 为宜。接近训练结束:学习速率的衰减应该在100倍以上。这里因为我们采用的是迁移学习,由于预模型本身已经在原始数据集上收敛,此时学习率应该设置的较小,所以这里设置成0.00002。
  • num_train_epochs 每次用完所有样本后,记为一个epoch。这里是指设置多少个epoch后训练结束。
  • filter_adam_var 保存训练模型的时候是否过滤掉Adam的参数,默认为False。设置为True可以减小模型的大小。
  • verbose 加上该参数就会打开tensorflow的日志。
  • data_dir train、dev、test数据所在的目录。
  • output_dir 模型输出目录。
  • init_checkpoint 预训练模型的路径,这里我们使用了bert的中文预训练模型。
  • bert_config_file bert模型的配置文件所在路径。
  • vocab_file bert的词汇表文件路径。

开始训练后,通过以下命令查看训练过程的日志信息:

代码语言:txt
AI代码解释
复制
tail -f log.out

下图截取自训练结束后的部分输出日志:

可以看到评估损失值降到了0.04862。

训练会持续3个多小时(在一块Nvidia Geforce RTX2060 GPU上),结束后,会看到对test.txt样本进行测试的结果:

测试

每训练500步,程序会在output目录下保存一个模型文件,我们可以通过修改output目录下的checkpoint文件来指定要用来测试的模型文件。

然后执行如下命令来对test.txt中的内容进行测试(注意bert_lstm_ner-test.py中的配置要和训练时指定的参数配置一致):

代码语言:txt
AI代码解释
复制
python bert\_lstm\_ner-test.py

测试输出的结果和上面训练完成后输出的结果的格式是一样的。如果你按照本文的步骤,完整的走到这里了,那么你已经有了一个可以识别 人名、地址、组织、公司、产品、时间,共6个实体的命名实体识别模型,下一篇文章《用深度学习做命名实体识别(五):模型使用》将介绍如何使用这个模型来提供一个rest风格的实体识别接口,对该接口传入一个句子参数,接口会返回句子中的人名、地址、组织、公司、产品、时间信息。

ok,本篇就这么多内容啦~,感谢阅读O(∩_∩)O,88~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
2 条评论
热度
最新
分享的修改后的模型链接失效了,可以重新分享一下吗
分享的修改后的模型链接失效了,可以重新分享一下吗
回复回复点赞举报
请问博主python的版本是多少呢
请问博主python的版本是多少呢
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
用深度学习做命名实体识别(五)-模型使用
注意,在cpu上使用模型的时间大概在2到3秒,而如果项目部署在搭载了支持深度学习的GPU的电脑上,接口的返回会快很多很多,当然不要忘记将tensorflow改为安装tensorflow-gpu。
程序员一一涤生
2019/09/23
8990
用深度学习做命名实体识别(五)-模型使用
基于模板的中文命名实体识别数据增强
本文将介绍一种基于模板的中文命名实体识别数据增强方法,自然语言处理中最常见的一个领域就是文本分类。文本分类是给定一段文本,模型需要输出该文本所属的类别。对文本分类进行数据增强较为简单的一种是对文本中的词进行同义词替换、随机删除、随机插入、打乱顺序等。命名实体识别不同于文本分类,但又和文本分类密切相关,因为实体识别是对每一个字或者词进行分类,我们要识别出的是一段字或词构成的短语,因此,上述文本分类中的数据增强可能会让实体进行切断而导致标签和实体不一致。这里,介绍一种基于模板得实体增强方法,能够解决上述得问题的同时,使得模型的性能进一步得到提升。
西西嘛呦
2022/09/23
7990
基于模板的中文命名实体识别数据增强
用深度学习做命名实体识别(五)-模型使用
注意,在cpu上使用模型的时间大概在2到3秒,而如果项目部署在搭载了支持深度学习的GPU的电脑上,接口的返回会快很多很多,当然不要忘记将tensorflow改为安装tensorflow-gpu。
程序员一一涤生
2019/09/29
1.3K0
用深度学习做命名实体识别(五)-模型使用
基于bert_bilstm_crf的命名实体识别
本文将介绍基于pytorch的bert_bilstm_crf进行命名实体识别,涵盖多个数据集。命名实体识别指的是从文本中提取出想要的实体,本文使用的标注方式是BIOES,例如,对于文本虞兔良先生:1963年12月出生,汉族,中国国籍,无境外永久居留权,浙江绍兴人,中共党员,MBA,经济师。,我们想要提取出里面的人名,那么虞兔良可以被标记为B-NAME,I-NAME,E-NAME。最终我们要做的就是对每一个字进行分类。
西西嘛呦
2022/09/23
7680
基于bert命名实体识别(一)数据处理
要使用官方的tensorflow版本的bert微调进行自己的命名实体识别,需要处理数据成bert相应的格式,主要是在run_classifier.py中,比如说:
西西嘛呦
2020/11/24
1.1K0
基于bert命名实体识别(一)数据处理
基于tensorflow的bilstm_crf的命名实体识别(数据集是msra命名实体识别数据集)
github地址:https://github.com/taishan1994/tensorflow-bilstm-crf
西西嘛呦
2020/11/24
1.3K0
基于tensorflow的bilstm_crf的命名实体识别(数据集是msra命名实体识别数据集)
用深度学习做命名实体识别(三):文本数据标注过程
首先,在brat项目的data目录下新建一个project目录,然后在brat项目的主目录下找到以下文件,复制到project目录:
程序员一一涤生
2019/09/06
1.5K0
用深度学习做命名实体识别(三):文本数据标注过程
Python人工智能 | 二十六.基于BiLSTM-CRF的医学命名实体识别研究(上)数据预处理
实体是知识图谱最重要的组成,命名实体识别(Named Entity Recognition,NER)对于知识图谱构建具有很重要意义。命名实体是一个词或短语,它可以在具有相似属性的一组事物中清楚地标识出某一个事物。命名实体识别(NER)则是指在文本中定位命名实体的边界并分类到预定义类型集合的过程。
Eastmount
2024/06/07
7300
Python人工智能 | 二十六.基于BiLSTM-CRF的医学命名实体识别研究(上)数据预处理
用BERT做命名实体识别任务
本质上NER是一个token classification任务, 需要把文本中的每一个token做一个分类。
lyhue1991
2023/09/05
7480
用BERT做命名实体识别任务
用深度学习做命名实体识别(一):文本数据标注
“ 本文是用深度学习做命名实体识别系列的第一篇,通过本文,你将了解如何用brat做文本数据标注。”
程序员一一涤生
2019/09/05
2.9K0
用深度学习做命名实体识别(一):文本数据标注
【命名实体识别】训练端到端的序列标注模型
导语 PaddlePaddle提供了丰富的运算单元,帮助大家以模块化的方式构建起千变万化的深度学习模型来解决不同的应用问题。这里,我们针对常见的机器学习任务,提供了不同的神经网络模型供大家学习和使用。本周推文目录如下: 3.12:【命名实体识别】 训练端到端的序列标注模型 3.13:【序列到序列学习】 无注意力机制的神经机器翻译 3.14:【序列到序列学习】 使用Scheduled Sampling改善翻译质量 3.15:【序列到序列学习】 带外部记忆机制的神经机器翻译 3.16:【序列到序列学习】 生成
用户1386409
2018/03/15
2.4K0
【命名实体识别】训练端到端的序列标注模型
如何在腾讯钛中训练基于bert预训练语言模型的文本分类模型
import codecs import os import keras import numpy as np import pandas as pd from keras.callbacks import ModelCheckpoint, EarlyStopping from keras.optimizers import Adam from keras_bert import load_trained_model_from_checkpoint, Tokenizer from keras_radam
用户1750490
2019/12/10
1.5K0
如何在腾讯钛中训练基于bert预训练语言模型的文本分类模型
tf25: 使用深度学习做阅读理解+完形填空
本文介绍了如何使用深度学习来做阅读理解+完形填空。首先介绍了TensorFlow的基本用法,然后详细讲解了如何使用TensorFlow来实现这个任务。主要包括两个部分:1. 使用深度学习来做阅读理解;2. 使用深度学习来做完形填空。最后还给出了一些实验结果和性能指标。
MachineLP
2018/01/09
2.3K0
tf25: 使用深度学习做阅读理解+完形填空
Qwen2大模型微调入门实战-命名实体识别(NER)任务
以Qwen2作为基座大模型,通过指令微调的方式做高精度的命名实体识别(NER),是学习入门LLM微调、建立大模型认知的非常好的任务。
zenRRan
2024/07/04
2K0
Qwen2大模型微调入门实战-命名实体识别(NER)任务
教程 | 如何用百度深度学习框架PaddlePaddle做数据预处理
机器之心经授权转载 作者:胡晓曼 本文主要介绍了百度的深度学习开源框架PaddlePaddle的数据预处理过程,创建一个reader读取数据,一行代码搞定数据的输入、混洗和批量读取。本文作者胡晓曼是一名高级算法工程师,热衷写通俗易懂的深度学习入门文章。 PaddlePaddle 的基本数据格式 根据官网的资料,总结出 PaddlePaddle 支持多种不同的数据格式,包括四种数据类型和三种序列格式: 四种数据类型: dense_vector:稠密的浮点数向量。 sparse_binary_vector:稀疏
机器之心
2018/05/10
8270
NLP命名实体识别开源实战教程 | 深度应用
近几年来,基于神经网络的深度学习方法在计算机视觉、语音识别等领域取得了巨大成功,另外在自然语言处理领域也取得了不少进展。在NLP的关键性基础任务—命名实体识别(Named Entity Recognition,NER)的研究中,深度学习也获得了不错的效果。
AI科技大本营
2019/08/20
1.7K0
NLP命名实体识别开源实战教程 | 深度应用
keras_bert文本多分类
#! -*- coding:utf-8 -*- import codecs import os import keras import pandas as pd from keras.callbacks import EarlyStopping from keras.layers import * from keras.models import Model from keras.optimizers import Adam from keras_bert import load_trained_mod
用户1750490
2019/12/17
1.4K0
keras_bert文本多分类
用机器学习打造聊天机器人(四) 代码篇
特征向量的构造有两种思想,一种是one-hot,一种是Dristributed Representation(这里用word2vec实现),一般来说后者能够更好的表示词的含义,但是有时候我们使用的句子来自特殊的领域,word2vec模型的预训练语料未必能够表示的很好,所以这个时候用one-hot就可能会表现的更好。
程序员一一涤生
2019/12/10
1.3K0
用机器学习打造聊天机器人(四) 代码篇
【深度学习系列】PaddlePaddle之数据预处理
本文介绍了如何使用PaddlePaddle进行图像分类,并通过一个具体的例子展示了如何划分训练集和测试集。首先介绍了使用PaddlePaddle进行图像分类的流程,然后提供了一个具体的例子,包括数据集、代码和模型。最后,总结了使用PaddlePaddle进行图像分类的优点和缺点。
Charlotte77
2018/01/09
9820
【深度学习系列】PaddlePaddle之数据预处理
[L1]实战语言模型~语料词典的生成
心宽一寸,受益三分。心宽路就宽,心窄路就窄。不争自然能得到人们的尊崇,能忍则忍,一忍百安。
触摸壹缕阳光
2020/06/01
1.4K0
推荐阅读
相关推荐
用深度学习做命名实体识别(五)-模型使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档