精华内容
下载资源
问答
  • 先立个FLAG写一个比较完善的中文序列标注系列 因为最近接触的序列标注比较多,主要是做命名实体识别,区别于之前的主要是类别是多种,不仅限于人名地名机构;模型主要是用了经典的BiLSTM+CRF,BERT,以及二者结合的...

    先立个FLAG写一个比较完善的中文序列标注系列

    因为最近接触的序列标注比较多,主要是做命名实体识别,区别于之前的主要是类别是多种,不仅限于人名地名机构;模型主要是用了经典的BiLSTM+CRF,BERT,以及二者结合的模型。

    博主渣渣,代码主要参考git上大佬的分享代码,此系列主要记录序列标注的原理,代码的注释,以及自己阅读的相关论文笔记。

    关于序列标注的统计学习方法主要是HMM,MEMM,CRF,在前边的博文有讲过,相关代码实现也都放在了git上,想要学习的同学学起来啊~~~

    关于中英文数据集,开源有很多,找不到的也可以联系我我告诉你地方哈哈哈,或者之后我整理下资源统一放到git上

    渣渣如我,希望我的记录能够给入门同学带来一丝丝帮助,也对得起敲过的这些字啦嘻嘻

    展开全文
  • 中文序列标注Flat Lattice代码解读与使用1. 原文与项目地址1.1 原文1.2 项目2. 环境搭配3. 总体结构与文件4. 参数介绍4.1 数据加载参数:4.2 bert相关参数(V1):4.3 模型参数:4.4 训练参数5. 模型与细节6. 模型...

    好久没有更新了,有好几个NLP的实践项目一直没有整理,以后会尽量加大更新频率,给大家带来更多的深度学习应用方面的分享。

    关于之前几篇博客的评论和私信,一般我都会回复,如果没有回的话可能是我也不懂的,或者当时回不了的,后来又忘记了。我目前主要是做NLP相关的研究,CV上的很多东西没有跟进,平时CV的项目接触的也少了很多,所以很多问题我也不明白,或者没有时间去仔细查代码。对于我没有及时回复的问题和解答不了的问题,在这里道个歉。

    进入正题之前,先声明一下,这篇文章是我在阅读原文和项目代码的基础上自己理解和修改的,可能存在不正确不恰当的地方,不能代表原项目本意,包括一些参数的解释,未必完全正确。如果要深入了解这个项目,请仔细阅读原文与代码。

    1. 原文与项目地址

    1.1 原文

    原文是《FLAT:Chinese NER Using Flat-Lattice Transformer》,解决的是中文命名实体识别的任务,在这里不过多的介绍原理。如果想了解原理上的细节或者Lattice在NER上的应用,可以直接在站内或者某乎搜索关键词,已经有很多人详细介绍过了。文章提出的方法在多个数据集上达到了SOTA结果,目前是中文NER的一个主流的方法。

    这篇博客虽然叫代码解读,但是我不会把每一个类、每一个方法都解释一遍,因为项目还是比较复杂的,我只会大体介绍其中相对重要的内容,把代码的运行方法讲清楚。如果有细节上的问题,可以留言或私信与我讨论。如果是原理方面的问题,建议先去多看看别人的介绍,把原理搞清楚,不清楚原理的话,想看懂代码是很难的。另外,不能完全以论文为准,有些处理细节在论文原文中没有详细的介绍。

    如果我写的有不正确的地方,还请大家帮忙指正。

    1.2 项目

    项目地址:https://github.com/LeeSureman/Flat-Lattice-Transformer

    项目分为V0和V1两个版本,其中V0是没有Bert的版本,V1是有Bert的版本。由于我个人的疏忽,一开始看的项目是原来的旧版本,只有V0,所以这次代码解读以V0为主,V1在代码结构上与V0比较相似,我在尝试复现论文结果时,在MSRA数据集上f1只达到了91,没有到论文的96,回去看git才发现原来还有个V1版本,后来在加入bert编码之后,复现了论文中96的结果。

    原项目中并没有给出模型如何进行预测等使用,在这篇博客中将会给出简单的预测方法。

    2. 运行环境

    代码是使用pytorch实现的,依赖的模块如下:

    Python: 3.7.3
    PyTorch: 1.2.0
    FastNLP: 0.5.0
    Numpy: 1.16.4
    

    (1)关于pytorch
    我使用的pytorch 1.5.0,只要是1.0以上的版本应该都没有问题。

    (2)关于FastNLP
    FastNLP是作者团队自己做的一个NLP工具包,跟这个项目比较贴合,用起来还挺好用的,FLAT的代码中很多类都是定义在FastNLP中的。
    与这个项目匹配的是0.5.0的版本,建议安装这个版本,严格按照作者的指导,会比较省事儿。如果你跟我一样安装了0.5.5的新版的FastNLP,在之后的代码执行过程中可能会遇到些问题。不过没有关系,都是比较容易解决的,我在下面也会进行说明。

    3. 项目结构

    项目的整体目录结构如下:
    项目结构

    这个目录并不是原作者git项目的结构,而是我整理之后的,直接下载我百度云上传的文件,解压之后就是这样的。
    链接:https://pan.baidu.com/s/1TGLb44HNQ2ypotxIYbsVJw
    提取码:2c2y

    所做的整理主要如下:

    (1)下载了相关的词向量文件数据集
    需要下载几个词向量文件和数据集如下

    文件 默认值 解释
    yangjie_rich_pretrain_unigram_path ‘./gigaword_chn.all.a2b.uni.ite50.vec’ 单字符的预训练vec编码
    yangjie_rich_pretrain_bigram_path ‘./gigaword_chn.all.a2b.bi.ite50.vec’ 双字符的预训练vec编码
    yangjie_rich_pretrain_word_path ‘./ctb.50d.vec’ 词语的预训练vec编码
    yangjie_rich_pretrain_char_and_word_path ‘./yangjie_word_char_mix.txt’ 词语和字符混合的编码,由prepocess生成
    msra_ner_cn_path ‘./MSRANER’ msra数据集位置

    (2)把V0和V1两个文件夹里边的main搬出来了,防止import的问题

    (3)load_data和fastNLP_model两个脚本是有V0和V1的区别的。如果执行的是flat_main.py,则会调用不带v1的脚本,如果是flat_main_v1.py,则会调用带v1的脚本。

    (4)MSRA是MSRA的序列标注数据集

    (5)cn-wwm是一个空文件夹,需要下载中文whole-word-mask的bert预训练权重文件的pytorch版,要求以bin为后缀名,该文件夹下还需要有json格式的bert config文件和词表。
    bert预训练
    方便起见我也都传百度云了。链接如下:
    链接:https://pan.baidu.com/s/1LdSYaFvgKdhLMXuSK7Fx-w
    提取码:thct

    4. 参数介绍

    项目中出现了大量的可调参数,而且,多数参数并没有给出含义的介绍,这让我在阅读代码时非常困扰,我在代码中挨个去找了这些参数,推断出参数的含义如下总结。

    4.1 数据加载参数:

    参数 解释 默认值
    dataset 数据集名称 ‘msra’
    bigram_min_freq bigram编码时考虑的最小词频 1
    char_min_freq 单个汉字编码时考虑的最小词频 1
    word_min_freq 词语编码时考虑的最小词频 1
    lattice_min_freq 添加lattice编码时考虑的最小词频 1
    train_clip 是否将训练集裁剪到200以下 False
    only_train_min_freq 仅对train中的词语使用min_freq筛选 True
    only_lexicon_in_train 只加载在trian中出现过的词汇 False
    number_normalized 0:不norm;1:char;2:char&bi;3:char&bi&lattice 0
    load_dataset_seed 随机种子 100

    4.2 bert相关参数(V1):

    bert相关的参数只有在V1版本中才会用到。

    参数 解释 默认值
    use_bert 是否使用bert编码 1
    only_bert 是否只使用bert编码 0
    fix_bert_epoch 多少轮之后开始训练bert 20
    after_bert 如果只使用bert,bert之后的层 mlp

    4.3 模型参数:

    参数 解释 默认值
    ff feed-forward中间层的节点个数 3,修正为hidden * ff
    hidden SE位置编码和三角函数编码共用的编码维度 会修正为head_dim * head
    layer Transformer中Encoder_Layer的数量 1
    head multi-head-attn中head的个数 8
    head_dim multi-head-attn中每个head的编码维度 20
    scaled multi-head-attn中是否对attn标准化(attn_raw/sqrt(per_head_size)) False
    attn_ff 是否在self-attn layer最后加一个linear层 False
    ff_activate feed-forward中的激活函数 ‘relu’
    use_bigram 是否使用双字符编码 1
    use_abs_pos 是否使用绝对位置编码 False
    use_rel_pos 是否使用相对位置编码 True
    rel_pos_shared 是否共享相对位置,无效参数 True
    add_pos 是否在transformer_layer中通过concat加入位置信息,无效参数 False
    learn_pos 绝对和相对位置编码中编码是否可学习(是否计算梯度) False
    pos_norm 是否对位置编码进行norm(pe/pe_sum) False
    rel_pos_init 相对位置编码初始化编码方向,0: 左向右;1:右向左。无效参数 1,但是实际调用的类中写死为0
    four_pos_shared 4个位置编码是不是共享权重 True
    four_pos_fusion 4个位置编码融合方法’ff’, ‘attn’, ‘gate’, ‘ff_two’, ‘ff_linear’ ff_two
    four_pos_fusion_shared 要不要共享4个位置融合之后形成的pos True
    k_proj attn中是否将key经过linear层 False
    q_proj attn中是否将query经过linear层 True
    v_proj attn中是否将value经过linear层 True
    r_proj attn中是否将相对位置编码经过linear层 True
    embed_dropout embedding中的dropout 0.5
    ff_dropout ff层中的dropout 0.15
    ff_dropout_2 第二个ff层中的dropout 0.15
    attn_dropout attention中的dropout 0

    4.4 训练参数

    训练参数相对容易理解,就不过多解释了(偷懒),把默认值列出来如下。

    epoch = 10
    batch = 1
    optim = 'sgd'   # sgd|adam
    lr = 1e-3
    warmup = 0.1
    embed_lr_rate = 1
    momentum = 0.9
    update_every = 1
    init = 'uniform'   # 'norm|uniform'
    self_supervised = False
    weight_decay = 0  
    norm_embed = True
    norm_lattice_embed = True
    test_batch = batch // 2
    

    5. 模型结构

    模型分为V0和V1两个版本。主要区别就是有没有使用Bert,所调用的类的名称也有所不同,但main脚本的流程没有太大的区别,都是按照如下的过程进行的:
    流程
    首先加载数据,创建fastNLP中Dataset类型的数据集,可以通过Dataset[‘train’]的方式去索引训练集、验证集和测试集。然后每一个数据集里边,又包含几个字段。

    字段名 含义
    char 原文本,以字为分割
    target 标签
    bigram 两两连续分割的字符

    equip lexicon是给数据集添加lattice的过程,就是去词库里边匹配词汇,然后放在原来的token embedding后边。
    接下来会读入数据的长度,给dataset添加要给seq_len的字段。然后对所有的embedding进行normalization。
    上述准备工作完成之后,就会建立模型,建立哪一种模型会根据用户输入的args进行判断。简单画了两个图,如下。

    V0版本的模型结构如下:
    V0结构
    V1版本的模型结构如下:
    V1结构

    6. 模型训练

    以MSRA数据集为例,训练只需要在项目目录下执行

    python flat_main.py --dataset msra
    

    可以根据训练参数去传参。
    在这里总结一下可能遇到的问题。如果你是直接执行我上传在百度云中的代码,可能不会遇到这些问题,因为我都已经修改过了。
    (1)import 报错。无法import _get_file_name_base_on_postfix函数。
    这个错误很明显,函数名以下短线开头,不能被import。只需要把这个函数复制到报错的脚本load_data.py中就可以了。
    (2)ncoding_type 不对。
    在代码里改一下就好了,例如MSRA数据集,改成bioes,CLUE数据集,改成bio,就可以了。
    (3)缺少数据类型‘chain’。
    这个来自于\JetBrains\PyCharm\python_stubs-1902731831\itertools.py这个脚本,把这个脚本复制到项目根路径,然后import它就可以了。在报错的脚本中添加:

    from itertools import chain
    

    (4)Bert相关的问题
    如果是用的V1版本代码,可以设置使用Bert编码,如果使用0.5.5的FastNLP则会遇到若干问题,问题和解决方法如下。
    BertModel的名称不对:
    这是因为FastNLP中这个类的名字变了,原本是_WordBertModel,改成了_BertWordModel,直接复制这个类,改一下名字,丢在fastNLP_Module_v1.py里边就可以了。
    然后会报缺少bert tokenizer,也是直接把fastNLP里边的\modules\tokenizer\bert_tokenizer.py里边相关的代码复制到fastNLP_Module_v1.py就可以解决了。
    (5)tqdm引发的报错
    如果是在jupyter中执行训练,可能会由于tqdm版本的问题引发报错,遇到这种情况只需要把训练参数中的use tqdm给关掉就可以了。

    7. 模型保存、加载与预测

    由于作者提供的只包含了训练,没有设置模型的保存、加载与预测相关的功能,所以我在这补充一下这几方面。

    7.1 模型保存

    模型在训练的时候,利用了fastNLP中的一个名为Trainer的类,通过查看这个类的代码可以发现,这个类是写了保存方法的。
    只需要在flat_main.py中,生成Trainer的位置加一个参数save_path就可以了。

        trainer = Trainer(datasets['train'], model, optimizer, loss, args.batch,
                          n_epochs=args.epoch,
                          dev_data=datasets['dev'],
                          metrics=metrics,
                          device=device, callbacks=callbacks, dev_batch_size=args.test_batch,
                          test_use_tqdm=False, check_code_level=-1,
                          update_every=args.update_every,
                          save_path='your_path/flat_lattice/{}/'.format(args.dataset))
    

    但是我这样修改了之后,在保存的时候还是报错了,所以我又把Trainer的save和load方法修改了一下。修改后的代码如下:

    def _save_model(self, model, model_name):
    	if self.save_path is not None:
    		model_path = os.path.join(self.save_path, model_name)
    		if not os.path.exists(self.save_path):
    			os.makedirs(self.save_path, exist_ok=True)
    		if _model_contains_inner_module(model):
    			model = model.module
    		torch.save(model.state_dict(), model_path)  # 只改了这一行
    
    def _save_model(self, model, model_name):
    	if self.save_path is not None:
    		model_path = os.path.join(self.save_path, model_name)
    		model.load_state_dict(torch.load(model_path))
    	elif hasattr(self, "_best_model_states"):
    		model.load_state_dict(self._best_model_states)
    	else:
    		return False
    	return True
    

    这样改好了之后,执行flat_main.py脚本进行训练之后,就会在save_path路径下保存一个模型权重文件。

    7.2 模型加载

    模型加载很简单,只需要在flat_main.py中,实例化model之后,load之前保存的权重文件就可以了。

    model_path = '/msra/best_Lattice_Transformer_SeqLabel_f_2021-03-03-14-55-31-899501'
    states = torch.load(model_path).state_dict()
    model.load_state_dict(states)
    

    7.3 生成预测

    作者并没有给出如何预测,但是在fastNLP中实际上是定义了用于预测的类的,名为predictor,去看一下代码的话,这个类其实写的很简单,但是很实用。使用方法如下:

    from fastNLP.core.predictor import Predictor
    predictor = Predictor(model)   # 这里的model是加载权重之后的model
    
    test_label_list = predictor.predict(datasets['test'][:1])['pred'][0]  # 预测结果
    test_raw_char = datasets['test'][:1]['raw_chars'][0]     # 原始文字
    

    上面代码中的test_label_list就在test上预测出来的label,label对应的BIO可以通过以下代码查看:

    for d in vocabs['label']:
        print(d)
    

    然后我写了一个简单的方法把label转换成实体(仅适用于MSRA数据集),如下所示:

    def recognize(label_list, raw_chars):
        """
        根据模型预测的label_list,找出其中的实体
        label_lsit: array
        raw_chars: list of raw_char
        return: entity_list: list of tuple(ent_text, ent_type)
        -------------
        ver: 20210303
        by: changhongyu
        """
        if len(label_list.shape) == 2:
            label_list = label_list[0]
        elif len(label_list) > 2:
            raise ValueError('please check the shape of input')
            
        assert len(label_list.shape) == 1
        assert len(label_list) == len(raw_chars)
        
        # 其实没有必要写这个
        # 但是为了将来可能适应bio的标注模式还是把它放在这里了
        starting_per = False
        starting_loc = False
        starting_org = False
        ent_type = None
        ent_text = ''
        entity_list = []
        
        for i, label in enumerate(label_list):
            if label in [0, 1, 2]:
                ent_text = ''
                ent_type = None
                continue
            # begin
            elif label == 10:
                ent_type = 'PER'
                starting_per = True
                ent_text += raw_chars[i]
            elif label == 4:
                ent_type = 'LOC'
                starting_loc = True
                ent_text += raw_chars[i]
            elif label == 6:
                ent_type = 'ORG'
                starting_org = True
                ent_text += raw_chars[i]
            # inside
            elif label == 9:
                if starting_per:
                    ent_text += raw_chars[i]
            elif label == 8:
                if starting_loc:
                    ent_text += raw_chars[i]
            elif label == 3:
                if starting_org:
                    ent_text += raw_chars[i]
            # end
            elif label == 11:
                if starting_per:
                    ent_text += raw_chars[i]
                    starting_per = False
            elif label == 5:
                if starting_loc:
                    ent_text += raw_chars[i]
                    starting_loc = False
            elif label == 7:
                if starting_org:
                    ent_text += raw_chars[i]
                    starting_org = False
            elif label == 13:
                ent_type = 'PER'
                ent_text = raw_chars[i]
            elif label == 12:
                ent_type = 'LOC'
                ent_text = raw_chars[i]
            elif label == 14:
                ent_type = 'PER'
                ent_text = raw_chars[i]
            else:
                ent_text = ''
                ent_type = None
                continue
            
            if not (starting_per or starting_loc or starting_org) and len(ent_text):
                # 判断实体已经结束,并且提取到的实体有内容
                entity_list.append((ent_text, ent_type))
            
        return entity_list    
    
    recognize(test_label_list, test_raw_char)
    # Out:
    # [('中共中央', 'ORG'),
    #  ('中国致公党', 'ORG'),
    #  ('中国致公党', 'ORG'),
    #  ('中国共产党中央委员会', 'ORG'),
    #  ('致公党', 'ORG')]
    

    8. 应用在自己的数据集

    在这里我以中文命名实体识别数据集CLUE NER 2020为例,介绍怎样将FLAT用在自己的数据集上。
    (1)数据格式转换。
    首先,不管你的数据格式原来是什么样子,都要转成一行一个字符的格式,例如:

    浙 B-CMP
    商 I-CMP
    银 I-CMP
    行 I-CMP
    企 O
    业 O
    信 O
    贷 O
    部 O
    

    并且命名为train.char.bmes,然后创建一个名为CLUE2020的文件夹,把train,dev和test都放进去。
    (2)设置路径
    打开paths.py,增加一行:

    clue_2020_ner_path = './CLUE2020'
    

    (3)写加载数据方法
    打开load_data_v1.py,仿照load_ontonotes4ner,写一个新的方法load_clue_2020,几乎就是复制粘贴,改一下加载数据的路径和缓存路径就好了。

    (4)在main中修改加载的数据集
    打开flat_main_v1.py,修改dataset为clue,以及读取数据的时候,加一个判断,如果dataset为clue,调用(3)中刚写的load_clue的方法。

    完成了这几步之后,训练时就可以用自己的数据集啦。

    python flat_main_v1.py --dataset clue
    

    然后就可以根据7中的步骤去进行预测了,记得自己写一下recognize的方法,这个识别方法是由数据集的标注格式决定的。

    注意一点,缓存的名字是跟着dataset的名字走的,如果你换了数据集的实际内容,而数据集的名称没有改的话,记得去cache里边把缓存清理掉,不然模型运行的时候会有限去找缓存,而缓存存的还是原来的数据集。

    这篇文章到这里就结束了,如果有任何关于Flat命名实体识别相关的问题,无论是原理还是代码,可以在评论区留言交流,如果我能解答的话会回复的。如果没有回复,可以给我发私信。

    如果对你有所帮助的话,记得一键三连支持一下博主。我们下期再见。

    展开全文
  • 序列标注 | (1) 序列标注问题概述

    千次阅读 2020-04-02 20:44:43
     序列标注问题是自然语言中最常见的问题,在深度学习火起来之前,常见的序列标注问题的解决方案都是借助于HMM模型,最大熵模型,CRF模型。尤其是CRF,是解决序列标注问题的主流方法。随着深度学习的发展,RNN在序列...

    原文地址
     序列标注问题是自然语言中最常见的问题,在深度学习火起来之前,常见的序列标注问题的解决方案都是借助于HMM模型,最大熵模型,CRF模型。尤其是CRF,是解决序列标注问题的主流方法。随着深度学习的发展,RNN在序列标注问题中取得了巨大的成果。而且深度学习中的end-to-end,也让序列标注问题变得更简单了。

    序列标注问题包括自然语言处理中的分词,词性标注,命名实体识别(实体抽取),关键词抽取,词义角色标注等等。我们只要在做序列标注时给定特定的标签集合,就可以进行序列标注。

    序列标注问题是NLP中最常见的问题,因为绝大多数NLP问题都可以转化为序列标注问题,虽然很多NLP任务看上去大不相同,但是如果转化为序列标注问题后其实面临的都是同一个问题。所谓“序列标注”,就是说对于一个一维线性输入序列:
     在这里插入图片描述
      给线性序列中的每个元素打上标签集合中的某个标签:
      在这里插入图片描述
      所以,其本质上是对线性序列中每个元素根据上下文内容进行分类的问题。一般情况下,对于NLP任务来说,线性序列就是输入的文本,往往可以把一个汉字看做线性序列的一个元素,而不同任务其标签集合代表的含义可能不太相同,但是相同的问题都是:如何根据汉字的上下文给汉字打上一个合适的标签(无论是分词,还是词性标注,或者是命名实体识别,道理都是相通的)。

    序列标注问题之中文分词

    以中文分词任务来说明序列标注的过程。假设现在输入句子“跟着TFboys学左手右手一个慢动作”,我们的任务是正确地把这个句子进行分词。首先,把句子看做是一系列单字组成的线性输入序列,即:
    在这里插入图片描述
      序列标注的任务就是给每个汉字(序列中的每个元素)打上一个标签,对于分词任务来说,我们可以定义标签集合为(jieba分词中的标签集合也是这样的):
      在这里插入图片描述
      其中B代表这个汉字是词汇的开始字符,M代表这个汉字是词汇的中间字符,E代表这个汉字是词汇的结束字符,而S代表单字词。
      在这里插入图片描述
      有了这四个标签就可以对中文进行分词了。这时你看到了,中文分词转换为对汉字的序列标注问题,假设我们已经训练好了序列标注模型,那么分别给每个汉字打上标签集合中的某个标签,这就算是分词结束了,因为这种形式不方便人来查看,所以可以增加一个后处理步骤,把B开头,后面跟着M的汉字拼接在一起,直到碰见E标签为止,这样就等于分出了一个单词,而打上S标签的汉字就可以看做是一个单字词。于是我们的例子就通过序列标注,被分词成如下形式:
    在这里插入图片描述
    在这里我们可以采用双向LSTM来处理该类问题,双向会关注上下文的信息。
    在NLP中最直观的处理问题的方式就是要把问题转换为序列标注问题,思考问题的思维方式也就转换为序列标注思维,这个思维很重要,决定你能否真的处理好NLP问题。

    序列标注之命名实体识别(NER,实体抽取)

    我们再来看看命名实体识别问题中的序列标注,命名实体识别任务是识别句子中出现的实体,通常识别人名、地名、机构名这三类实体。现在的问题是:假设输入中文句子
    在这里插入图片描述
    我们要识别出里面包含的人名、地名和机构名。如果以序列标注的角度看这个问题,我们首先得把输入序列看成一个个汉字组成的线性序列,然后我们要定义标签集合,标签集合如下(在这里的标签用什么代表不重要,重要的是它代表的含义):  在这里插入图片描述
     其中,BA代表这个汉字是地址首字,MA代表这个汉字是地址中间字,EA代表这个汉字是地址的尾字;BO代表这个汉字是机构名的首字,MO代表这个汉字是机构名称的中间字,EO代表这个汉字是机构名的尾字;BP代表这个汉字是人名首字,MP代表这个汉字是人名中间字,EP代表这个汉字是人名尾字,而O代表这个汉字不属于命名实体。
     在这里插入图片描述
     有了输入汉字序列,也有了标签集合,那么剩下的问题是训练出一个序列标注ML系统,能够对每一个汉字(每个元素)进行分类(打标签),假设我们已经学好了这个系统,那么就给输入句子中每个汉字打上标签集合中的标签,于是命名实体就被识别出来了,为了便于人查看,增加一个后处理步骤,把人名、地名、机构名都明确标识出来即可。
      除了上面的分词和命名实体标注,很多其他的NLP问题同样可以转换为序列标注问题,比如词性标注、CHUNK识别、句法分析、语义角色识别、关键词抽取等。

    传统解决序列标注问题的方法包括HMM/MaxEnt/CRF等,很明显RNN很快会取代CRF的主流地位,成为解决序列标注问题的标准解决方案,那么如果使用RNN来解决各种NLP基础及应用问题,我们又该如何处理呢,下面我们就归纳一下使用RNN解决序列标注问题的一般优化思路。

    对于分词、词性标注(POS)、命名实体识别(NER)这种前后依赖不会太远的问题,可以用RNN或者BiRNN处理就可以了。而对于具有长依赖的问题,可以使用LSTM、RLSTM、GRU等来处理。关于GRU和LSTM两者的性能差不多,不过对于样本数量较少时,有限考虑使用GRU(模型结构较LSTM更简单)。此外神经网络在训练的过程中容易过拟合,可以在训练过程中加入Dropout或者L1/L2正则来避免过拟合。

    CRF和LSTM在序列标注上的优劣

    **LSTM:**像RNN、LSTM、BILSTM这些模型,它们在序列建模上很强大,它们能够capture长远的上下文信息,此外还具备神经网络拟合非线性的能力,这些都是crf无法超越的地方,对于t时刻来说,输出层yt受到隐层ht(包含上下文信息)和输入层xt(当前的输入)的影响,但是yt和其他时刻的yt是相互独立的,感觉像是一种point wise,对当前t时刻来说,我们希望找到一个概率最大的yt,但其他时刻的yt对当前yt没有影响,如果yt之间存在较强的依赖关系的话(例如,形容词后面一般接名词,存在一定的约束),LSTM无法对这些约束进行建模,LSTM模型的性能将受到限制。

    **CRF:**它不像LSTM等模型,能够考虑长远的上下文信息,它更多考虑的是整个句子的局部特征的线性加权组合(通过特征模版去扫描整个句子)。关键的一点是,CRF的模型为p(y | x, w),注意这里y和x都是序列,它有点像list wise,优化的是一个序列y = (y1, y2, …, yn),而不是某个时刻的yt,即找到一个概率最高的序列y = (y1, y2, …, yn)使得p(y1, y2, …, yn| x, w)最高,它计算的是一种联合概率,优化的是整个序列(最终目标),而不是将每个时刻的最优拼接起来,在这一点上CRF要优于LSTM。

    **HMM:**CRF不管是在实践还是理论上都要优于HMM,HMM模型的参数主要是“初始的状态分布”,“状态之间的概率转移矩阵”,“状态到观测的概率转移矩阵”,这些信息在CRF中都可以有,例如:在特征模版中考虑h(y1), f(yi-1, yi), g(yi, xi)等特征。

    **CRF与LSTM:**从数据规模来说,在数据规模较小时,CRF的试验效果要略优于BILSTM,当数据规模较大时,BILSTM的效果应该会超过CRF。从场景来说,如果需要识别的任务不需要太依赖长久的信息,此时RNN等模型只会增加额外的复杂度,此时可以考虑类似科大讯飞FSMN(一种基于窗口考虑上下文信息的“前馈”网络)。

    **CNN+BILSTM+CRF:**这是目前学术界比较流行的做法,BILSTM+CRF是为了结合以上两个模型的优点,CNN主要是处理英文的情况,英文单词是由更细粒度的字母组成,这些字母潜藏着一些特征(例如:前缀后缀特征),通过CNN的卷积操作提取这些特征,在中文中可能并不适用(中文单字无法分解,除非是基于分词后的结果),这里简单举一个例子,例如词性标注场景,单词football与basketball被标为名词的概率较高, 这里后缀ball就是类似这种特征。

    展开全文
  • 序列标注

    2017-07-19 09:21:18
    序列标注问题应该说是自然语言处理中最常见的问题,而且很可能是最而没有之一。在深度学习没有广泛渗透到各个应用领域之前,传统的最常用的解决序列标注问题的方案是最大熵、CRF等模型,尤其是CRF,基本是最主流的...

    序列标注问题应该说是自然语言处理中最常见的问题,而且很可能是最而没有之一。在深度学习没有广泛渗透到各个应用领域之前,传统的最常用的解决序列标注问题的方案是最大熵、CRF等模型,尤其是CRF,基本是最主流的方法。随着深度学习的不断探索和发展,很可能RNN模型会取代CRF的传统霸主地位,会成为解决序列标注问题的标配解决方案。


    本文主要抽象出利用RNN解决序列标注问题的通用优化思路。这个RNN优化思路应该说是使用场景非常广泛,目前NLP中绝大多数问题都可以套用这套优化思路来统一解决问题。它有个独特的好处,就是说只要你构建好完整的这一套优化模型,绝大多数NLP的问题,包括分词、词性标注、命名实体识别、关键词抽取、语义角色标注等等,等于说问题已经被解决了,只需要你使用不同问题对应的训练数据跑一遍,就等于说上面问题的实际解决工具你就拥有了,而不用像传统模型一样还需要不断定义问题特征模板等,这就是End-to-End的好处。


    再啰嗦两句,为什么说RNN一定会取代CRF?当然这不代表CRF完全会被抛弃,以后一定有人去研究怎么结合RNNCRF,这个估计也没有疑问。但是RNN取代CRF获得主流地位是一定的,原因就是上面说的DLEnd-to-End的优势。意思是,即使RNN只能取得和CRF方法类似的效果,但是由于End-to-End的优势,你不需要对研究领域有任何领域知识也能把它干好,因为有用的领域特征DL自己会去学,这个好处是巨大的(但是对于传统依靠NLP经验混饭吃的NLP老手们来说,这是个坏消息),况且目前看RNN在不少领域已经获得了最好的结果。结合这些特点,这就是为何说RNN会取代CRF成为新的主流模型的原因。


    如果看将来的技术发展趋势,从目前情况看,如果再把Encoder-Decoder框架引进来,加上这篇文章讲的RNN解决序列标注问题,以及后面可能会写的用RNN解决句子分类问题的通用思路,我相信NLP90%以上的问题都可以被这两个模型覆盖(当然Encoder-Decoder实际上和RNN不是一个层级的模型,这个我们暂且放一边,但且把它们并列来提)。所谓:RNN在手,全都我有,此言诚不我欺也。


    我觉得还在坚韧不拔地坚持用传统方法搞NLP的同志应该好好读一读我上面这几段话,里面有深意,自己去体会。


    |序列标注问题


    如上文所述,序列标注问题是NLP中最常见的问题,因为绝大多数NLP问题都可以转化为序列标注问题,虽然很多NLP任务看上去大不相同,但是如果转化为序列标注问题后其实面临的都是同一个问题。所谓“序列标注”,就是说对于一个一维线性输入序列:


    给线性序列中的每个元素打上标签集合中的某个标签


    所以,其本质上是对线性序列中每个元素根据上下文内容进行分类的问题。一般情况下,对于NLP任务来说,线性序列就是输入的文本,往往可以把一个汉字看做线性序列的一个元素,而不同任务其标签集合代表的含义可能不太相同,但是相同的问题都是:如何根据汉字的上下文给汉字打上一个合适的标签。


    我们以中文分词任务来说明序列标注的过程。图1给出了个例子,假设现在输入句子“跟着TFboys学左手右手一个慢动作”,我们的任务是正确地把这个句子进行分词。首先,把句子看做是一系列单字组成的线性输入序列,即:


              “跟  Tfboys                    作”


    序列标注的任务就是给每个汉字打上一个标签,对于分词任务来说,我们可以定义标签集合为:


      

    其中B代表这个汉字是词汇的开始字符,M代表这个汉字是词汇的中间字符,E代表这个汉字是词汇的结束字符,而S代表单字词。



                                               1. 中文分词序列标注过程


    有了这四个标签就可以对中文进行分词了。这时你看到了,中文分词转换为对汉字的序列标注问题,假设我们已经训练好了序列标注模型,那么分别给每个汉字打上标签集合中的某个标签,这就算是分词结束了,因为这种形式不方便人来查看,所以可以增加一个后处理步骤,把B开头,后面跟着M的汉字拼接在一起,直到碰见E标签为止,这样就等于分出了一个单词,而打上S标签的汉字就可以看做是一个单字词。于是我们的例子就通过序列标注,被分词成如下形式:

           {跟着  Tfboys 左手 右手 一个 慢动作}


    要学习NLP,首先要把面对问题时的思维意识转换一下,就是如上所示,把你直观感觉的问题转换为序列标注思维,这个思维意识转换过程很重要,如果你没有这种意识,那很明显属于业余选手,专业选手看NLP问题肯定首先是把它们看做一个序列标注问题的。


    为了促进意识转换,下面再用命名实体识别(NER)问题举个序列标注解决问题的例子,命名实体识别任务是识别句子中出现的实体,通常识别人名、地名、机构名这三类实体。现在的问题是:假设输入中文句子


      {花园北路的北医三院里,昏迷三年的我听杨幂的爱的供养时起身关了收音机。}


    我们要识别出里面包含的人名、地名和机构名。如果以序列标注的角度看这个问题,应该怎么解决呢?图2给出了整个流程的说明。


    首先,我们把输入序列看成一个个汉字组成的线性序列,这是基础。


    然后,我们定义标签集合如下(这个标签用什么代表不重要,重要的是它们代表的含义):



    其中,BA代表这个汉字是地址首字,MA代表这个汉字是地址中间字,EA代表这个汉字是地址的尾字;BO代表这个汉字是机构名的首字,MO代表这个汉字是机构名称的中间字,EO代表这个汉字是机构名的尾字;BP代表这个汉字是人名首字,MP代表这个汉字是人名中间字,EP代表这个汉字是人名尾字,而O代表这个汉字不属于命名实体。


                                    2. 命名实体(NER)序列标注过程


    有了输入汉字序列,也有了标签集合,那么剩下的问题是训练出一个序列标注ML系统,能够对每一个汉字进行分类,假设我们已经学好了这个系统,那么就给输入句子中每个汉字打上标签集合中的标签,于是命名实体就被识别出来了,为了便于人查看,增加一个后处理步骤,把人名、地名、机构名都明确标识出来即可。


    很多NLP中的其它任务同样可以转换为序列标注问题,比如词性标注、CHUNK识别、句法分析、语义角色识别,甚至包括关键词抽取等很多应用问题也是如此。


    传统解决序列标注问题的方法包括HMM/MaxEnt/CRF等,很明显RNN很快会取代CRF的主流地位,成为解决序列标注问题的标准解决方案,那么如果使用RNN来解决各种NLP基础及应用问题,有什么优化轨迹可循吗?有的,下面我们就归纳一下使用RNN解决序列标注问题的一般优化思路。


    |使用RNN解决序列标注问题的一般优化思路


    首先,我们可以说,对于序列标注问题,都可以用RNN来替代传统解决方案,这个目前看没什么问题,也是大势所趋。


    然后,如果归纳的话,一般的优化流程是:首先用RNN来解决序列标注问题,然后可以上LSTM或者GRU来替代RNN中的隐层单元,因为LSTMGRU对于解决长距离依赖的场景明显要优于RNN本身,接着可以上BLSTM,即双向LSTM,因为双向RNN或者双向LSTM相对RNN来说,能够看到下文特征,所以明显引入了更多的特征,如果你觉得效果还是不够好,那么可以增加BLSTM的深度,不断叠加网络形成深度BLSTM网络,当然,这里要注意,随着优化的进行,模型越来越复杂,如果训练数据规模不够的话,很可能会出现过拟合现象,所以使用LSTM的时候,记得用上DropOut以及L1/L2正则来避免过拟合,但是如果模型相对训练数据规模确实复杂,你加上这些可能也没用。至于GRULSTM两者相比,到底哪个更好目前并无定论,倾向于认为两者性能差不多,但是GRULSTM的简化模型,所以同样结构的神经网络其参数要少,在训练数据不太大情况下,优先考虑使用GRU


    当然,根据NLP的不同任务特点,是先上BiRNN好还是先上LSTM好这个可能不同任务情况不一样,比如对于分词、POSNER这种依赖不会太远的任务,RNN应该就够了,但是BiRNN肯定是要上的,因为下文特征有重要参考意义,但是对于语义级别的任务比如指代消解、语义角色标注等,估计肯定是要上LSTM的。所以要根据具体情况具体分析,不过通用的优化手段也不外乎以上几种。


    下面给出深层双向LSTM来做NER的神经网络结构示意图:


                                           图3. 深层双向LSTMNER


    图比较直观,就不解释了,其它的NLP序列标注任务其实都可以套用这个模型来解决。

    展开全文
  • 序列标注简介

    千次阅读 2018-11-09 15:05:17
    1. 序列标注 序列标注:简单的来说序列标注就是:给定一个序列,对序列中的每一个元素做一个标记,或者说给每一个元素打一个标签。一般来说,一个序列指的是一个句子,而一个元素指的是句子中的一个词。 2. 标签 ...
  • 提出了一种基于序列标注模型的中文依存句法分析方法。 该方法将依存句法分析转化成序列标注问题, 利用条件随机 场 CRF ( Cond itionalR andom F ie ld)建立序列标注模型。 在宾州中文树库的测试中, 达得了 76. 59% ...
  • 序列标注中的BIO标注介绍

    万次阅读 多人点赞 2018-03-27 18:35:22
    一、序列标注 序列标注(Sequence labeling)是我们在解决NLP问题时经常遇到的基本问题之一。在序列标注中,我们想对一个序列的每一个元素标注一个标签。一般来说,一个序列指的是一个句子,而一个元素指的是句子中...
  • 序列标注模型

    千次阅读 2015-11-26 16:08:34
    序列标注模型   (2015-01-30 09:29:34) 转载▼     背景知识 序列标注模型被广泛应用于文本处理相关领域,例如分词、词性标注、命名实体识别等方面。现有的序列标注模型主要有HMM,MEMM 以及 CRF...
  • 序列标注中文命名实体识别(NER)
  • 中文NLP序列标注工具。利用CRF进行命名实体识别NER,自动标注数据集产生语料库,可以选择BIO或者BMES标注体系。
  • NER-序列标注

    千次阅读 2019-10-08 18:01:49
    命名实体识别-序列标注 标签类型 进行命名实体识别时,通常对每个字进行标注。中文为单个字,英文为单词,空格分割。 标签类型一般如下: 类型 说明 B Begin,代表实体片段的开始 I Internediate,代表...
  • 序列标注模型综述

    千次阅读 2018-11-09 17:55:45
    命名实体识别是序列标注的子问题,需要将元素进行定位和分类,如人名、组织名、地点、时间、质量等。命名实体识别的任务就是识别出待处理文本中三大类(实体类、时间类和数字类)、七小类(人名、机构名、地名、时间...
  • 2014人民日报 1998人民日报 国家语委 treebank msra微软亚洲研究院
  • 所谓序列标注: 就是有一个线性序列:X = x_1 , x_2, x_3,......,x_n 给每一个元素打上标签:Y = y_1, y_2, y_3,......, y_n 序列标注任务 1.分词 [B, M, E, S] B: 代表开始字符 M:代表中间字符 E:代表...
  • 1.深度学习下序列标注如何理解? 有篇文章说序列标注如分词,命名实体识别,其实都是对每一个token(每个字?)进行一次多分类,如dense层对每个字进行了一次分类,最后组装起来。 2.问题・_・? 这是否可以看成一个...
  • 具体说来,只要将每个汉字组词时所处的位置(首尾等)作为标签,则中文分词就转化为给定汉字序列找出标签序列的问题。一般而言,由字构词是序列标注模型的一种应用。 在所有“序列标注”模型中,隐马尔可夫模型是最...
  •   在文章NLP(三十四)使用keras-bert实现序列标注任务中,我们已经用keras-bert模块实现了中文序列标注任务,其中对BERT进行微调。当前,我们也可以顺便实现下英语序列标注任务。   本文将介绍如何使用keras-...
  • 序列标注相关方案

    千次阅读 2018-06-14 16:13:52
    序列标注是一个比较广泛的任务,包括分词,词性标注,命名实体识别,关系抽取等等,甚至你也可以用来做抽取式QA,直接在文章中标注出答案。 这里跟大家提一下分词,很基础也是很重要的一个任务,我说重要指的是我们...
  • 在NLP中,序列标注算法是常见的深度学习模型,但是,对于序列标注算法的评估,我们真的熟悉吗?在本文中,笔者将会序列标注算法的模型效果评估方法和seqeval的使用。序列标注算法的模型效果...
  • 序列标注问题是自然语言中最常见的问题,在深度学习火起来之前,常见的序列标注问题的解决方案都是借助于HMM模型,最大熵模型,CRF模型。尤其是CRF,是解决序列标注问题的主流方法。随着深度学习的发展,RNN在序列...
  • 大多数方法把中文分词转换为一个基于字符的序列标注问题,其中输入句子中的每个字符被标注为一个标签用以表示它在目标词中的位置。 多标准中文分词 尽管一些基于神经网络的中文分词方法取得了很大的进步,但是这些...
  • 当前主流的中文分词方法是基于字标注的传统机器学习方法,但传统机器学习方法需要人为地从中文文本中配置并提取特征,存在词库维度高且...LSTM网络模型的方法也更容易推广并应用到其他自然语言处理中序列标注的任务。
  • 使用深度学习进行中文自然语言处理之序列标注
  • 第4章 隐马尔可夫模型与序列标注 4.1 序列标注问题 4.2 隐马尔可夫模型 4.3 隐马尔可夫模型的样本生成 4.4 隐马尔可夫模型的训练 4.5 隐马尔可夫模型的预测 4.6 隐马尔可夫模型应用于中文分词 4.7 性能评测 ...
  • Bi-LSTM原理及TensorFlow实现序列标注

    千次阅读 2019-04-02 16:58:21
    本文整理了Bi-LSTM的原理,并在静觅博客静觅:TensorFlow Bi-LSTM实现序列标注 的基础上对TensorFlow 搭建一个Bi-LSTM来处理序列标注问题的代码进行了详细的注释。 Bi-LSTM理解 RNN的意思是,为了预测最后的结果,我...
  • 序列标注任务是中文自然语言处理(NLP)领域在句子层面中的主要任务,在给定的文本序列上预测序列中需要作出标注的标签。常见的子任务有命名实体识别(NER)、Chunk提取以及词性标注(POS)等。序列标注任务在e成的...
  • ​ 从文本翻译到文字、语音识别,再到命名实体识别,词性标注,序列标注问题已然渗透到社会生活中的方方面面。不少自然语言处理的问题都可以想方设法转化为序列标注问题:对邮件等”大块“的信息做命名实体识别,...
  • 隐马尔可夫模型与序列标注 1、序列标注问题 序列标注问题指的是给定一个序列x=x1x2……xn,找出...1.1序列标注中文分词 {B,M,E,S}法——B(Begin)、E(End)分别表示词语首尾;M(Middle)表示词中;S(S...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,517
精华内容 7,406
关键字:

中文序列标注