Tensorflow进行POS词性标注NER实体识别 - 构建LSTM网络进行序列化标注

http://blog.csdn.net/rockingdingo/article/details/55653279

 Github下载完整代码

https://github.com/rockingdingo/deepnlp/tree/master/deepnlp/pos

简介

这篇文章中我们将基于Tensorflow的LSTM模型来实现序列化标注的任务,以NLP中的POS词性标注为例实现一个深度学习的POS Tagger。文中具体介绍如何基于Tensorflow的LSTM cell单元来构建多层LSTM、双向Bi-LSTM模型,以及模型的训练和预测过程。对LSTM模型的基本结构和算法不熟悉的可以参考拓展阅读里的一些资料。 完整版代码可以在Github上找到:https://github.com/rockingdingo/deepnlp/tree/master/deepnlp/pos

数据和预处理

我们使用的词性标注POS的训练集来源是url [人民日报1998年的新闻语料],格式为”充满/v 希望/n 的/u 新/a 世纪/n ——/w 一九九八年/t”。具体的预处理过程包含以下步骤:

  • 读取训练集数据:得到两个列表word和tag,其中word保存分词,Tag保存对应的标签;
  • 构建词典:对词进行Count并且按照出现频率倒叙排列,建立字典表:word_to_id和tag_to_id 保存词和标签的id,未知词的标签即为UNKNOWN = "*";
  • 分别读取训练集train, dev和test数据集,将数据集的word列表和tag列表分别转化为其对应的id列表。
  • 构建一个迭代器iterator, 每次返回读取batch_size个词和标签的Pair对 (x,y)作为LSTM模型的输入。 x代表词ID的矩阵,y代表标签ID的矩阵,形状均为[batch_size, num_steps],代表batch_size组长度为num_steps的序列;矩阵中元素代表第x[i,j] 代表第i个batch下第j个词的ID,如“132”(面条),y[i,j] 为其对应标签的ID,如”3 ”(NN-名词)。

    模型

    图1 LSTM链式展开

    图2 LSTM内部结构

    LSTM前向传播公式

    input 门

    forget 门

    cell 状态更新

    output 门

    ht 隐藏层更新

     

实现

      1. 定义类和初始化函数init

我们首先定义一个POSTagger类,通过初始化函数init,根据超参数构建tensorflow的一个graph模型。 所有LSTM模型的超参数保存在config这个类中,传入init函数。

    代码1

[python] view plain copy

  1. class LargeConfigChinese(object):
  2. """Large config."""
  3. init_scale = 0.04
  4. learning_rate = 0.5
  5. max_grad_norm = 10
  6. num_layers = 2
  7. num_steps = 30
  8. hidden_size = 128
  9. max_epoch = 5
  10. max_max_epoch = 55
  11. keep_prob = 1.0
  12. lr_decay = 1 / 1.15
  13. batch_size = 1 # single sample batch
  14. vocab_size = 50000
  15. target_num = 44  # POS tagging for Chinese
  16. # 定义类和初始化函数init
  17. class POSTagger(object):
  18. """The pos tagger model"""
  19. def __init__(self, is_training, config):
  20. self.batch_size = batch_size = config.batch_size
  21. self.num_steps = num_steps = config.num_steps
  22. size = config.hidden_size
  23. vocab_size = config.vocab_size
  24. target_num = config.target_num # target output number
  25. # define model
  26. # To Do
    Init函数的输入参数config为一个包含超参数的类,具体输入包括:

  • init_scale: 初始化参数的范围[-init_scale, init_scale]
  • num_layers: LSTM模型的层数,默认为2层;
  • hidden_size: LSTM模型每层节点数,默认128;
  • num_steps: LSTM模型的步长T,代表共计算T个timestep,默认30;
  • keep_prob: Dropout层留存的概率,为避免过拟合设置, 当数据集小的时候可以设置为1;
  • vocab_size: 词典的单词个数,默认值50000;
  • target_num:标签的个数,默认值 44,针对人民日报的标签体系下的44个词性标签;
  • 其他

定义占位符Placeholder

        在Tensorflow的图模型的Placeholder占位符,每次接受一组训练数据的字典表feed_dict,字典表中包含了我们在上一章节预处理中构建的迭代器iterator,每次返回(x,y)的Pair会被Feed进入占位符中: _input_data 和_targets的形状均为 [batch_size, num_steps],每个元素为词或者是标签的ID,保存为整数形tf.int32。

            代码2

[python] view plain copy

  1. self._input_data = tf.placeholder(tf.int32, [batch_size, num_steps])
  2. self._targets = tf.placeholder(tf.int32, [batch_size, num_steps])

词向量Word Embedding层

现在我们将输入到占位符_input_data和_targets的ID数据,转化为对应的词向量。 在Tensorflow定义了简单方法:首先随机生成一个embedding矩阵,形状为[vocab_size, size],即词典大小vocab_size 乘以定义的词向量的维度 size。 然后利用tf.nn.embedding_lookup() 方法来查找每个ID对应的向量。这个过程就是将长度为vocab_size的One-Hot输入向量Xi转化为一个固定长度size的词向量。 在后向传播过程,词向量也同时得到训练。

              代码3

[python] view plain copy

  1. embedding = tf.get_variable("embedding", [vocab_size, size], dtype=data_type())
  2. inputs = tf.nn.embedding_lookup(embedding, self._input_data)
              LSTM模型

基本LSTM单元:通过tf.nn.rnn_cell.BasicLSTMCell() 函数构建,size为LSTM的每层节点个数,forget_bias为偏移量,state_is_tuple=True为内部实现的一种结构,在tensorflow 0.10.0 后的版本为了提升计算速度已经建议均设置为TRUE,FALSE版本会被去除掉。

Dropout Wrapper层: 通过tf.nn.rnn_cell.DropoutWrapper() 函数可以在LSTM层加上Dropout避免训练过程的过拟合,通过设置 output_keep_prob 的概率来调节;

多层LSTM的cell单元:通过函数tf.nn.rnn_cell.MultiRNNCell() 构建,层数的参数是config.num_layers,第一层LSTM的输出会作为下一层LSTM的输入,将多层LSTM叠加在一起获得更好的模型Capacity;

              代码4-1

[python] view plain copy

  1. lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(size, forget_bias=0.0, state_is_tuple=True)
  2. if is_training and config.keep_prob < 1:
  3. lstm_cell = tf.nn.rnn_cell.DropoutWrapper(
  4. lstm_cell, output_keep_prob=config.keep_prob)
  5. cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * config.num_layers, state_is_tuple=True)
              双向LSTM模型 Bi-LSTM双向LSTM模型的传播过程与单向LSTM模型稍有区别:

              • 分别定义两个LSTM基本单元,一个前向的lstm_fw_cell,一个后向的lstm_bw_cell
              • 基于基本单元分别构建:多层前向LSTM单元cell_fw和多层后向LSTM的单元cell_bw:
              • 定义两个前向LSTM和后向LSTM的初始状态:initial_state_fw和initial_state_bw
              • 利用tf.nn.bidirectional_rnn() 函数完成传播过程:具体的两个LSTM分别得到隐含层的状态 hj_forward 和hj_backward,将[hj_forward,hj_backward]合并成为一个长度为2倍隐含层节点数的向量;
              • 最后返回的参数有:outputs, state_fw, state_bw

      图3 双向LSTM结构
    代码4-2

[python] view plain copy

  1. lstm_fw_cell = tf.nn.rnn_cell.BasicLSTMCell(size, forget_bias=0.0, state_is_tuple=True)
  2. lstm_bw_cell = tf.nn.rnn_cell.BasicLSTMCell(size, forget_bias=0.0, state_is_tuple=True)
  3. cell_fw = tf.nn.rnn_cell.MultiRNNCell([lstm_fw_cell] * num_layers, state_is_tuple=True)
  4. cell_bw = tf.nn.rnn_cell.MultiRNNCell([lstm_bw_cell] * num_layers, state_is_tuple=True)
  5. initial_state_fw = cell_fw.zero_state(batch_size, data_type())
  6. initial_state_bw = cell_bw.zero_state(batch_size, data_type())
  7. # Split to get a list of ‘n_steps‘ tensors of shape (batch_size, n_input)
  8. inputs_list = [tf.squeeze(s, squeeze_dims=[1]) for s in tf.split(1, num_steps, inputs)]
  9. with tf.variable_scope("pos_bilstm"):
  10. outputs, state_fw, state_bw = tf.nn.bidirectional_rnn(
  11. cell_fw, cell_bw, inputs_list, initial_state_fw = initial_state_fw,
  12. initial_state_bw = initial_state_bw)
    前向传播

LSTM模型每次读取当前步t的输入Xt 和上一步的隐含层的向量h(t-1),通过LSTM内部结构的一系列计算得到相应的输出。定义前向过程,通过for循环,每次输入一个步t对应的词向量 inputs[:, time_step, :],是一个3D的Tensor [batch_size, time_step, size] 。其中size为词向量的维度。之后会将每一步的结果添加到outputs这个list中。

最后的全连接层:将output这个向量乘以softmax_w再加上偏移softmax_b,得到输出部分的logits,最后利用tf.nn.sparse_softmax_cross_entropy_with_logits 比较真实值的向量_targets和预测值的向量 logits,计算交叉熵cross-entropy的损失函数loss;

    代码5

[python] view plain copy

  1. state = self._initial_state
  2. with tf.variable_scope("pos_lstm"):
  3. for time_step in range(num_steps):
  4. if time_step > 0: tf.get_variable_scope().reuse_variables()
  5. (cell_output, state) = cell(inputs[:, time_step, :], state)
  6. outputs.append(cell_output)
  7. output = tf.reshape(tf.concat(1, outputs), [-1, size])
  8. softmax_w = tf.get_variable(
  9. "softmax_w", [size, target_num], dtype=data_type())
  10. softmax_b = tf.get_variable("softmax_b", [target_num], dtype=data_type())
  11. logits = tf.matmul(output, softmax_w) + softmax_b
  12. loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits, tf.reshape(targets, [-1]))
    关于损失函数

Tensorflow中定义损失函数有:tf.nn.sparse_softmax_cross_entropy_with_logits() 和 tf.nn.softmax_cross_entropy_with_logits()。 另外还有一个函数tf.nn.seq2seq.sequence_loss_by_example()接收参数和sparse_softmax_cross_entropy_with_logits类似。 二者输出结果一致,区别在于接收的输入不同:

      • 函数tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name=None)

        "sparse_softmax"这个函数输入参数labels表示待拟合的标签,形状为 [batch_size] ,每个值为一个整形数值,int32或者int64, 代表了待预测标签的ID,即每个样本的标签有且仅有一个。

      • 函数tf.nn.softmax_cross_entropy_with_logits(logits, labels, dim=-1, name=None)

        "softmax" 这个函数输入参数labels形状为[batch_size, num_classes],每个元素类型为float32或者 float64。每个样本对应的标签向量可以是One-Hot表示,即每个样本只属于一个类别;同时也可以是对应多个标签soft softmax,即每个样本的label不仅限于一个,而是给出符合每个类别的概率分布。这个函数支持一个样本在多个类别下都有分布情况。

      模型训练过程 定义run_epoch函数:

      • fetches:定义需要评估和取出的数值,这里我们要计算取出model.cost, model.final_state 和 eval_op三个参数,其中eval_op 为前向过程中定义的SGD梯度下降的操作符:self._train_op = optimizer.apply_gradients(zip(grads, tvars))
      • feed_dict: 将每次迭代器返回的(x,y) Pair对的值,分别赋给input_data和target这两个占位符。

      To Do: State[i].c

session.run() 函数每次将feed_dict的数据输入Graph模型,计算后返回fetches列表中定义的几个变量[cost, state, _ ]。_ 代表了评估的operator。

    代码6

[python] view plain copy

  1. def run_epoch(session, model, word_data, tag_data, eval_op, verbose=False):
  2. """Runs the model on the given data."""
  3. epoch_size = ((len(word_data) // model.batch_size) - 1) // model.num_steps
  4. start_time = time.time()
  5. costs = 0.0
  6. iters = 0
  7. state = session.run(model.initial_state)
  8. for step, (x, y) in enumerate(reader.iterator(word_data, tag_data, model.batch_size,
  9. model.num_steps)):
  10. fetches = [model.cost, model.final_state, eval_op]
  11. feed_dict = {}
  12. feed_dict[model.input_data] = x
  13. feed_dict[model.targets] = y
  14. for i, (c, h) in enumerate(model.initial_state):
  15. feed_dict[c] = state[i].c
  16. feed_dict[h] = state[i].h
  17. cost, state, _ = session.run(fetches, feed_dict)
  18. costs += cost
  19. iters += model.num_steps
  20. if verbose and step % (epoch_size // 10) == 10:
  21. print("%.3f perplexity: %.3f speed: %.0f wps" %
  22. (step * 1.0 / epoch_size, np.exp(costs / iters),
  23. iters * model.batch_size / (time.time() - start_time)))
  24. # Save Model to CheckPoint when is_training is True
  25. if model.is_training:
  26. if step % (epoch_size // 10) == 10:
  27. checkpoint_path = os.path.join(FLAGS.pos_train_dir, "pos.ckpt")
  28. model.saver.save(session, checkpoint_path)
  29. print("Model Saved... at time step " + str(step))
  30. return np.exp(costs / iters)

延伸阅读

深语人工智能-技术博客: 
http://www.deepnlp.org/blog/tensorflow-lstm-pos/
Python Package Index - deepnlp: Deep Learning NLP Pipeline implemented on Tensorflow
https://pypi.python.org/pypi/deepnlp

时间: 2024-12-22 16:24:31

Tensorflow进行POS词性标注NER实体识别 - 构建LSTM网络进行序列化标注的相关文章

CS224d 单隐层全连接网络处理英文命名实体识别tensorflow

什么是NER? 命名实体识别(NER)是指识别文本中具有特定意义的实体,主要包括人名.地名.机构名.专有名词等.命名实体识别是信息提取.问答系统.句法分析.机器翻译等应用领域的重要基础工具,作为结构化信息提取的重要步骤. NER具体任务 1.确定实体位置 2.确定实体类别 给一个单词,我们需要根据上下文判断,它属于下面四类的哪一个,如果都不属于,则类别为0,即不是实体,所以这是一个需要分成 5 类的问题: ? Person (PER) ? Organization (ORG) ? Locatio

神经网络结构在命名实体识别(NER)中的应用

近年来,基于神经网络的深度学习方法在自然语言处理领域已经取得了不少进展.作为NLP领域的基础任务-命名实体识别(Named Entity Recognition,NER)也不例外,神经网络结构在NER中也取得了不错的效果.最近,我也阅读学习了一系列使用神经网络结构进行NER的相关论文,在此进行一下总结,和大家一起分享学习. 1 引言 命名实体识别(Named Entity Recognition,NER)就是从一段自然语言文本中找出相关实体,并标注出其位置以及类型,如下图.它是NLP领域中一些复

NLP入门(八)使用CRF++实现命名实体识别(NER)

CRF与NER简介 ??CRF,英文全称为conditional random field, 中文名为条件随机场,是给定一组输入随机变量条件下另一组输出随机变量的条件概率分布模型,其特点是假设输出随机变量构成马尔可夫(Markov)随机场. ??较为简单的条件随机场是定义在线性链上的条件随机场,称为线性链条件随机场(linear chain conditional random field). 线性链条件随机场可以用于序列标注等问题,而本文需要解决的命名实体识别(NER)任务正好可通过序列标注方

关于bert+lstm+crf实体识别训练数据的构建(一)

一.在实体识别中,bert+lstm+crf也是近来常用的方法.这里的bert可以充当固定的embedding层,也可以用来和其它模型一起训练fine-tune.大家知道输入到bert中的数据需要一定的格式,如在单个句子的前后需要加入"[CLS]"和“[SEP]”,需要mask等.下面构造训练集并利用albert抽取句子的embedding. 1 import torch 2 from configs.base import config 3 from model.modeling_a

用IDCNN和CRF做端到端的中文实体识别

实体识别和关系抽取是例如构建知识图谱等上层自然语言处理应用的基础.实体识别可以简单理解为一个序列标注问题:给定一个句子,为句子序列中的每一个字做标注.因为同是序列标注问题,除去实体识别之外,相同的技术也可以去解决诸如分词.词性标注等不同的自然语言处理问题. 说到序列标注直觉是会想到RNN的结构.现在大部分表现最好的实体识别或者词性标注算法基本都是biLSTM的套路.就像Ruder在他的博客 Deep Learning for NLP Best Practices 里面说的,There has b

基于统计的中文实体识别方法简述

命名实体识别(NER)是自然语言处理的一个基础任务,其目的是识别出语料中的人名.地名.组织机构名等命名实体,一般包括三大类(实体类.时间类和数字类)和七小类(人名.地名.机构名.时间.日期.货币和百分比).NER是信息抽取.机器翻译.知识图谱等多种自然语言处理任务必不可少的组成部分. NER方法大致可分为两类:基于规则的方法和基于统计的方法.基于规则的方法多采用语言学家手工构造规则模板,这类系统大多依赖于知识库和词典,需要人工建立实体识别规则,成本高且可移植性差.基于统计的方法一般需要语料库来进

CRF++地名实体识别(特征为词性和词)

http://x-algo.cn/index.php/2016/02/29/crf-name-entity-recognition/ 类似使用CRF实现分词和词性标注,地域识别也是需要生成相应的tag进行标注.这里使用的语料库是1998年1月人民日报语料集.最终学习出来的模型,对复杂的地名识别准确率(F值)非常低,推测是预料中对地名的标注多处是前后矛盾.例如  [华南/ns 地区/n]ns  标为地名实体,但是 东北/f 地区/n 确分开标注,类似错误还有很多.将来有时间可以考虑使用微软的词库 

知识图谱文献综述(第三章 实体识别与链接)

第三章 实体识别与链接 1. 任务定义.目标和研究意义 实体是文本中承载信息的重要语言单位,也是知识图谱的核心单元. 命名实体识别是指识别文本中的命名性实体,并将其划分到指定类别的任 务[Chinchor & Robinson, 1997].常用实体类别包括人名.地名.机构名.日期等. 实体链接主要解决实体名的歧义性和多样性问题,是指将文本中实体名指向其 所代表的真实世界实体的任务,也通常被称为实体消歧.例如,给 一句话“苹果发布了最新产品 iPhone X”,实体链接系统需要将文本中的“苹果”

机器学习 - 命名实体识别之Hidden Markov Modelling

概述 命名实体识别在NLP的应用中也是非常广泛的,尤其是是information extraction的领域.Named Entity Recognition(NER) 的应用中,最常用的一种算法模型是隐式马可夫模型(Hidden Markov Modelling)- HMM.本节内容主要是通过介绍HMM的原理,以及应用HMM来做一个NER的实例演示. HMM原理解析 在解释HMM的原理之前,先引用几个HMM的基本概念,第一个是就是隐式状态,在本文中用H表示: 第二个就是显式状态,在本文中用大写