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
  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
  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
  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
  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
  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
  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
  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

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

  1. 吴裕雄--天生自然神经网络与深度学习实战Python+Keras+TensorFlow:使用TensorFlow和Keras开发高级自然语言处理系统——LSTM网络原理以及使用LSTM实现人机问答系统

    !mkdir '/content/gdrive/My Drive/conversation' ''' 将文本句子分解成单词,并构建词库 ''' path = '/content/gdrive/My D ...

  2. 基于tensorflow的bilstm_crf的命名实体识别(数据集是msra命名实体识别数据集)

    github地址:https://github.com/taishan1994/tensorflow-bilstm-crf 1.熟悉数据 msra数据集总共有三个文件: train.txt:部分数据 ...

  3. Caffe2:使用Caffe构建LSTM网络

    前言: 一般所称的LSTM网络全叫全了应该是使用LSTM单元的RNN网络. 原文:(Caffe)LSTM层分析 入门篇:理解LSTM网络 LSTM的官方简介: http://deeplearning. ...

  4. 【NER】对命名实体识别(槽位填充)的一些认识

    命名实体识别 1. 问题定义 广义的命名实体识别是指识别出待处理文本中三大类(实体类.时间类和数字类).七小类(人名.机构名.地名.日期.货币和百分比)命名实体.但实际应用中不只是识别上述所说的实体类 ...

  5. pytorch 文本情感分类和命名实体识别NER中LSTM输出的区别

    文本情感分类: 文本情感分类采用LSTM的最后一层输出 比如双层的LSTM,使用正向的最后一层和反向的最后一层进行拼接 def forward(self,input): ''' :param inpu ...

  6. 利用pyltp进行实体识别

    一.实体识别作为信息抽取中基础的也是重要的一步,其技术可以分为三类,分别是其于规则的方法.其于统计模型的方法以及基于深度学习的方法. 基于规则的方法,主要依靠构建大量的实体抽取规则,一般由具有一定领域 ...

  7. 『深度应用』NLP命名实体识别(NER)开源实战教程

    近几年来,基于神经网络的深度学习方法在计算机视觉.语音识别等领域取得了巨大成功,另外在自然语言处理领域也取得了不少进展.在NLP的关键性基础任务—命名实体识别(Named Entity Recogni ...

  8. 哈工大LTP基本使用-分词、词性标注、依存句法分析、命名实体识别、角色标注

    代码 import os from pprint import pprint from pyltp import Segmentor, Postagger, Parser, NamedEntityRe ...

  9. NLP入门(五)用深度学习实现命名实体识别(NER)

    前言   在文章:NLP入门(四)命名实体识别(NER)中,笔者介绍了两个实现命名实体识别的工具--NLTK和Stanford NLP.在本文中,我们将会学习到如何使用深度学习工具来自己一步步地实现N ...

随机推荐

  1. Code Forces 698A Vacations

    题目描述 Vasya has nn days of vacations! So he decided to improve his IT skills and do sport. Vasya know ...

  2. 数据库事务的属性-ACID和隔离级别

    1.数据库事务的属性-ACID(四个英文单词的首写字母): 1)原子性(Atomicity) 所谓原子性就是将一组操作作为一个操作单元,是原子操作,即要么全部执行,要么全部不执行. 2)一致性(Con ...

  3. word如何插入和删除脚注,尾注

    在我们日常使用word时,经常会使用到脚注/尾注来对文中某些文字或内容进行注释或是重点说明,效果挺好,挺直观和明显.但是很多人并不会使用,特别是当需要删除脚注事,就特别苦恼了,那根小横线怎么也删除不掉 ...

  4. LPC-LINK 2 Board IO TABLE

  5. LINUX 内核守护进程

    http://alfred-sun.github.io/blog/2015/06/18/daemon-implementation/

  6. 如何调整word中表格某一列占半分比

    1.可以拖动,但是不准确 2.

  7. java容器类---概述

    1.容器类关系图 虚线框表示接口. 实线框表示实体类. 粗线框表示最经常使用的实体类. 点线的箭头表示实现了这个接口. 实线箭头表示类能够制造箭头所指的那个类的对象. Java集合工具包位于Java. ...

  8. 使用 Android 的日志工具LogCat

    Android 中的日志工具类是 Log(android.util.Log),这个类中提供了如下几个方法来供我们打印日志. 1.    Log.v() 这个方法用于打印那些最为琐碎的,意义最小的日志信 ...

  9. PPT幻灯片放映不显示备注,只让备注显示在自己屏幕上-投影机 设置

    无论是老师或是讲师还是即将要演讲的人,在讲课之前一定会做好课件,到哪一页该讲哪些内容,到哪里该如何去讲等等.那么一般的讲师会将这些课件存放到哪里呢?是用个书本记载下来呢,还是直接存放到电脑上呢?其实本 ...

  10. iOS开发25:使用SOAP访问Web服务

    SOAP是简单对象访问协议,它可看成是HTTP与XML的结合,其中XML部分是作为HTTP报文的实体主体部分.具体信息可以参考百度百科. 在iOS中使用SOAP,需要我们自己组装XML格式的字符串,当 ...